From 7d45a92b9a85afa9f2ad75971f6a7cf117c91ab7 Mon Sep 17 00:00:00 2001 From: Trevor Coleman Date: Wed, 27 Mar 2024 17:55:56 -0400 Subject: [PATCH] fixes and typos --- docs/recipes/LocalFirstDataWithPowerSync.md | 1763 ++++++++++--------- 1 file changed, 916 insertions(+), 847 deletions(-) diff --git a/docs/recipes/LocalFirstDataWithPowerSync.md b/docs/recipes/LocalFirstDataWithPowerSync.md index 423260e1..61098178 100644 --- a/docs/recipes/LocalFirstDataWithPowerSync.md +++ b/docs/recipes/LocalFirstDataWithPowerSync.md @@ -55,35 +55,26 @@ Check the [PowerSync documentation](https://docs.powersync.com/) for more inform ## Prerequisites +To complete this recipe you'll need: + 1. **An Ignite app using `Expo CNG` or `Bare` workflow** -- PowerSync requires native modules, so you cannot use Expo Go + PowerSync requires native modules, so you cannot use Expo Go + You can create a new Ignite app using the Ignite CLI:: ```bash npx ignite-cli@latest new PowerSyncIgnire --remove-demo --workflow=cng --yes ``` -2. **A Postgres SQL instance set up and connected to a PowerSync** - -- Instruction for connecting to a number of platforms are available in the [PowerSync documentation](https://docs.powersync.com/) -- If you don't have a database, you can follow the [PowerSync + Supabase Integration Guide](https://docs.powersync.com/integration-guides/supabase-+-powersync) to - get one set up -- both PowerSync and Supabase have free tiers that you can use to get started. - -3. **Your PowerSync URL** +2. **A Supabase Project set up and connected to a PowerSync** -- Found in your PowerSync dashboard. Click on the "Edit Instance" button for your instance and copy the URL from the "Instance URL" field in the dialog that appears. + - Follow the [PowerSync + Supabase Integration Guide](https://docs.powersync.com/integration-guides/supabase-+-powersync) to get this set up -- both PowerSync and Supabase have free tiers that you can use to get started. -4. **Supabase project details:** +3. **Configure or Disable Supabase Email Verification** -- **supabaseUrl**: Found through your Supabase dashboard under: **Project Settings** > **API** > **Project URL**. -- **supabaseAnonKey**: Found through your Supabase dashboard under: **Project Settings** > **API** > **Project API - keys**. - -5. Configure or Disable Supabase Email Verification - -- By default, Supabase requires email verification for new users. This should be configured for any production apps. -- For the purposes of this recipe, you can disable this in the Supabase dashboard under: - **Authentication** > **Providers** > **Email** >> **Confirm Email** + - By default, Supabase requires email verification for new users. This should be configured for any production apps. + - For the purposes of this recipe, you can disable this in the Supabase dashboard under: + - **Authentication** > **Providers** > **Email** > **Confirm Email** ## Installing SDK and Dependencies @@ -173,23 +164,29 @@ PowerSync requires a valid session token to connect to the Supabase backend, so ### Add Supabase Config Variables to Your App Config -First add your Supabase config to your app's configuration. In ignite apps, config is kept in `app/config/config.base.ts`. +First add your Supabase config to your app's configuration. In ignite apps, config is kept in `app/config/config.base.ts`. + +You'll need: + +- **supabaseUrl**: Found through your Supabase dashboard under: **Project Settings** > **API** > **Project URL**. +- **supabaseAnonKey**: Found through your Supabase dashboard under: **Project Settings** > **API** > **Project API + keys**. ```ts // `app/config/config.base.ts`: // update the interface to include the new properties export interface ConfigBaseProps { - // Existing config properties - supabaseUrl: string; - supabaseAnonKey: string; + // Existing config properties + supabaseUrl: string; + supabaseAnonKey: string; } // Add the new properties to the config object const BaseConfig: ConfigBaseProps = { - // Existing config values - supabaseUrl: '<>', - supabaseAnonKey: '<>', + // Existing config values + supabaseUrl: '<>', + supabaseAnonKey: '<>', }; ``` @@ -206,9 +203,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import {createClient} from "@supabase/supabase-js" export const supabase = createClient(Config.supabaseUrl, Config.supabaseAnonKey, { - auth: { - persistSession: true, storage: AsyncStorage, - }, + auth: { + persistSession: true, storage: AsyncStorage, + }, }) ``` @@ -233,13 +230,13 @@ import {supabase} from "app/services/database/supabase" import {createContext, PropsWithChildren, useCallback, useContext, useMemo, useState} from "react" type AuthContext = { - signIn: (email: string, password: string) => void - signUp: (email: string, password: string) => void - signOut: () => void - signedIn: boolean - loading: boolean - error: string - user: User | null + signIn: (email: string, password: string) => void + signUp: (email: string, password: string) => void + signOut: () => void + signedIn: boolean + loading: boolean + error: string + user: User | null } // We initialize the context with null to ensure that it is not used outside of the provider @@ -249,90 +246,90 @@ const AuthContext = createContext(null) * AuthProvider manages the authentication state and provides the necessary methods to sign in, sign up and sign out. */ export const AuthProvider = ({children}: PropsWithChildren) => { - const [signedIn, setSignedIn] = useState(false) - const [loading, setLoading] = useState(false) - const [error, setError] = useState("") - const [user, setUser] = useState(null) - - - // Sign in with provided email and password - const signIn = useCallback(async (email: string, password: string) => { - setLoading(true) - setError("") - setUser(null) - try { - const {data: {session, user}, error} = await supabase.auth.signInWithPassword({email, password}) - if (error) { - setSignedIn(false) - setError(error.message) - } else if (session && user) { - setSignedIn(true) - setUser(user) - } - } catch (error: any) { - setError(error?.message ?? "Unknown error") - } finally { - setLoading(false) - } - }, [ - setSignedIn, setLoading, setError, setUser, supabase - ]) - - // Create a new account with provided email and password - const signUp = useCallback(async (email: string, password: string) => { - setLoading(true) - setError("") - setUser(null) - try { - const {data, error} = await supabase.auth.signUp({email, password}) - if (error) { - setSignedIn(false) - setError(error.message) - } else if (data.session) { - await supabase.auth.setSession(data.session) - setSignedIn(true) - setUser(data.user) - } - } catch (error: any) { - setUser(null) - setSignedIn(false) - setError(error?.message ?? "Unknown error") - } finally { - setLoading(false) - } - }, [ - setSignedIn, setLoading, setError, setUser, supabase - ]) - - // Sign out the current user - const signOut = useCallback(async () => { - setLoading(true) - await supabase.auth.signOut() - setError("") + const [signedIn, setSignedIn] = useState(false) + const [loading, setLoading] = useState(false) + const [error, setError] = useState("") + const [user, setUser] = useState(null) + + + // Sign in with provided email and password + const signIn = useCallback(async (email: string, password: string) => { + setLoading(true) + setError("") + setUser(null) + try { + const {data: {session, user}, error} = await supabase.auth.signInWithPassword({email, password}) + if (error) { setSignedIn(false) - setLoading(false) - setUser(null) - }, [ - setSignedIn, setLoading, setError, setUser, supabase - ]) - - // Always memoize context values as they can cause unnecessary re-renders if they aren't stable! - const value = useMemo(() => ({ - signIn, signOut, signUp, signedIn, loading, error, user - }), [ - signIn, signOut, signUp, signedIn, loading, error, user - ]) - return {children} + setError(error.message) + } else if (session && user) { + setSignedIn(true) + setUser(user) + } + } catch (error: any) { + setError(error?.message ?? "Unknown error") + } finally { + setLoading(false) + } + }, [ + setSignedIn, setLoading, setError, setUser, supabase + ]) + + // Create a new account with provided email and password + const signUp = useCallback(async (email: string, password: string) => { + setLoading(true) + setError("") + setUser(null) + try { + const {data, error} = await supabase.auth.signUp({email, password}) + if (error) { + setSignedIn(false) + setError(error.message) + } else if (data.session) { + await supabase.auth.setSession(data.session) + setSignedIn(true) + setUser(data.user) + } + } catch (error: any) { + setUser(null) + setSignedIn(false) + setError(error?.message ?? "Unknown error") + } finally { + setLoading(false) + } + }, [ + setSignedIn, setLoading, setError, setUser, supabase + ]) + + // Sign out the current user + const signOut = useCallback(async () => { + setLoading(true) + await supabase.auth.signOut() + setError("") + setSignedIn(false) + setLoading(false) + setUser(null) + }, [ + setSignedIn, setLoading, setError, setUser, supabase + ]) + + // Always memoize context values as they can cause unnecessary re-renders if they aren't stable! + const value = useMemo(() => ({ + signIn, signOut, signUp, signedIn, loading, error, user + }), [ + signIn, signOut, signUp, signedIn, loading, error, user + ]) + return {children} } export const useAuth = () => { - const context = useContext(AuthContext) + const context = useContext(AuthContext) - // It's a good idea to throw an error if the context is null, as it means the hook is being used outside of the provider - if (context === null) { - throw new Error('useAuthContext must be used within a AuthProvider') - } - return context + // It's a good idea to throw an error if the context is null, as it means the hook is being used outside of the provider + if (context === null) { + throw new Error('useAuthContext must be used within a AuthProvider') + } + return context } ``` @@ -353,16 +350,16 @@ import {AuthProvider} from "app/services/database/use-auth" // ... function App(props: AppProps) { - // ... - return ( - // success-line - - - {/* ... */} - - // success-line - - ); + // ... + return ( + // success-line + + + {/* ... */} + + // success-line + + ); } @@ -380,7 +377,7 @@ npx ignite-cli generate screen Auth This will: * create a new screen in `app/screens/AuthScreen.tsx`, -* add that screen to the `AppNavigator` in `app/navigators/app-navigator.tsx`, and +* add that screen to the `AppNavigator` in `app/navigators/AppNavigator.tsx`, and * update the `Params` and `ScreenProps` types ::: @@ -399,107 +396,107 @@ import {colors, spacing} from "../theme" interface AuthScreenProps extends AppStackScreenProps<"Auth"> {} export const AuthScreen: React.FC = ({navigation}) => { - const {signUp, signIn, loading, error, user} = useAuth() - const [email, setEmail] = useState("") - const [password, setPassword] = useState("") + const {signUp, signIn, loading, error, user} = useAuth() + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") - const handleSignIn = async () => { - signIn(email, password) - } + const handleSignIn = async () => { + signIn(email, password) + } - const handleSignUp = async () => { - signUp(email, password) + const handleSignUp = async () => { + signUp(email, password) + } + + useEffect(() => { + if (user) { + navigation.navigate("Welcome") } + }, [user]) + + return ( + + Sign in or Create Account + + + + +