From 2859eb99b391461f8d307ae80bc296c2c1758baa Mon Sep 17 00:00:00 2001 From: DanDaManTran Date: Tue, 8 Apr 2025 16:16:00 -0500 Subject: [PATCH 1/5] syncing demo with the academy --- .../FormTextField/FormTextField.test.tsx | 16 +++++++++++----- .../RestaurantHeader/RestaurantHeader.tsx | 3 +++ tsconfig.json | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/shared/components/FormTextField/FormTextField.test.tsx b/src/shared/components/FormTextField/FormTextField.test.tsx index 55809d0..fb1a0d4 100644 --- a/src/shared/components/FormTextField/FormTextField.test.tsx +++ b/src/shared/components/FormTextField/FormTextField.test.tsx @@ -1,9 +1,9 @@ -import { fireEvent, render, screen } from "@testing-library/react-native" +import { fireEvent, render, screen, userEvent } from "@testing-library/react-native" import FormTextField from "./FormTextField" describe("FormTextField component", () => { - it("renders label", () => { + it("renders label", async () => { const handleChangeMock = jest.fn() render( @@ -14,9 +14,15 @@ describe("FormTextField component", () => { >, ) - expect(screen.getByText(/Hello/)).toBeOnTheScreen() + const user = userEvent.setup() + expect(screen.getByText(/Hello/)).toBeTruthy() - fireEvent.changeText(screen.getByLabelText(/Hello/i), "test") - expect(handleChangeMock).toHaveBeenCalledWith("test") + await user.type(screen.getByLabelText(/Hello/i), "test") + + expect(handleChangeMock).toHaveBeenCalledTimes(4) + expect(handleChangeMock).toHaveBeenNthCalledWith(1, "responset") + expect(handleChangeMock).toHaveBeenNthCalledWith(2, "responsee") + expect(handleChangeMock).toHaveBeenNthCalledWith(3, "responses") + expect(handleChangeMock).toHaveBeenNthCalledWith(4, "responset") }) }) diff --git a/src/shared/components/RestaurantHeader/RestaurantHeader.tsx b/src/shared/components/RestaurantHeader/RestaurantHeader.tsx index c33723f..d154df7 100644 --- a/src/shared/components/RestaurantHeader/RestaurantHeader.tsx +++ b/src/shared/components/RestaurantHeader/RestaurantHeader.tsx @@ -55,7 +55,10 @@ function getStyles(theme: Theme): { } { return StyleSheet.create({ heroBackground: { + width: "100%", + maxWidth: 768, height: 180, + margin: "auto", justifyContent: "flex-end", alignItems: "flex-start", }, diff --git a/tsconfig.json b/tsconfig.json index 8487fbf..23d909e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "@shared/*": ["./src/shared/*"], "@screens/*": ["./src/screens/*"] }, + "types": ["react-native", "jest", "node"], "module": "ESNext", "strict": true, From a12d14f468e7bcde07c7e5497d9be28975726b00 Mon Sep 17 00:00:00 2001 From: DanDaManTran Date: Tue, 8 Apr 2025 16:30:34 -0500 Subject: [PATCH 2/5] typescript error fix --- src/shared/components/FormTextField/FormTextField.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/FormTextField/FormTextField.test.tsx b/src/shared/components/FormTextField/FormTextField.test.tsx index fb1a0d4..74b9caf 100644 --- a/src/shared/components/FormTextField/FormTextField.test.tsx +++ b/src/shared/components/FormTextField/FormTextField.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, userEvent } from "@testing-library/react-native" +import { render, screen, userEvent } from "@testing-library/react-native" import FormTextField from "./FormTextField" From 42006b6963fc1a0ae07689e0e605d4361953255d Mon Sep 17 00:00:00 2001 From: DanDaManTran Date: Mon, 14 Apr 2025 14:16:03 -0500 Subject: [PATCH 3/5] Syncing up with Academy --- .../components/FormSwitch/FormSwitch.tsx | 3 -- .../FormTextField/FormTextField.tsx | 6 --- src/shared/design/Box/Box.tsx | 49 +++++-------------- src/shared/design/Button/Button.tsx | 42 +++++++++++----- src/shared/design/Card/Card.tsx | 1 + src/shared/design/Typography/Typography.tsx | 2 +- src/shared/design/theme/theme.ts | 6 +-- 7 files changed, 43 insertions(+), 66 deletions(-) diff --git a/src/shared/components/FormSwitch/FormSwitch.tsx b/src/shared/components/FormSwitch/FormSwitch.tsx index 5418e32..845e971 100644 --- a/src/shared/components/FormSwitch/FormSwitch.tsx +++ b/src/shared/components/FormSwitch/FormSwitch.tsx @@ -7,14 +7,12 @@ import Typography from "../../design/Typography" export interface FormSwitchProps { label: string - hint?: string value: boolean onChange: (value: boolean) => void } const FormSwitch: React.FC = ({ label, - hint, value, onChange, }) => { @@ -37,7 +35,6 @@ const FormSwitch: React.FC = ({ void } const FormTextField: React.FC = ({ label, - hint, - placeholder, value, onChange, }) => { @@ -32,10 +28,8 @@ const FormTextField: React.FC = ({ = ( - { variant = "primary", disabled, children, ...props }, - ref, +const Button: React.ForwardRefRenderFunction = ({ + variant = "primary", + margin, + padding, + fontSize = 20, + fontWeight = "400", + disabled, + children, + ...props + }, + ref, ) => { const theme = useTheme() const styles = getStyles(theme, variant) return ( - + {children} - + ) } diff --git a/src/shared/design/Card/Card.tsx b/src/shared/design/Card/Card.tsx index 13d34fc..3017884 100644 --- a/src/shared/design/Card/Card.tsx +++ b/src/shared/design/Card/Card.tsx @@ -33,6 +33,7 @@ function getStyles(theme: Theme): { } { return StyleSheet.create({ container: { + width: "100%", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, diff --git a/src/shared/design/Typography/Typography.tsx b/src/shared/design/Typography/Typography.tsx index 5815566..45ba6c3 100644 --- a/src/shared/design/Typography/Typography.tsx +++ b/src/shared/design/Typography/Typography.tsx @@ -16,7 +16,7 @@ const Typography: React.FC = ({ const styles = getStyles(theme, variant) return ( - + {children} ) diff --git a/src/shared/design/theme/theme.ts b/src/shared/design/theme/theme.ts index 797a0c8..13566fd 100644 --- a/src/shared/design/theme/theme.ts +++ b/src/shared/design/theme/theme.ts @@ -32,15 +32,11 @@ export interface Theme { export type ThemeMargin = | keyof Theme["spacing"] - | [keyof Theme["spacing"]] | [keyof Theme["spacing"], keyof Theme["spacing"]] - | [keyof Theme["spacing"], keyof Theme["spacing"], keyof Theme["spacing"]] export type ThemePadding = | keyof Theme["spacing"] - | [keyof Theme["spacing"]] | [keyof Theme["spacing"], keyof Theme["spacing"]] - | [keyof Theme["spacing"], keyof Theme["spacing"], keyof Theme["spacing"]] - + const light: Theme = { palette: { screen: { From 83cae334f987892041bf3c2f735da7f8d72d7a89 Mon Sep 17 00:00:00 2001 From: DanDaManTran Date: Mon, 14 Apr 2025 14:22:25 -0500 Subject: [PATCH 4/5] prettier --- .../components/FormSwitch/FormSwitch.tsx | 6 +---- src/shared/design/Box/Box.tsx | 2 +- src/shared/design/Button/Button.tsx | 25 ++++++++++--------- src/shared/design/theme/theme.ts | 2 +- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/shared/components/FormSwitch/FormSwitch.tsx b/src/shared/components/FormSwitch/FormSwitch.tsx index 845e971..1db2cca 100644 --- a/src/shared/components/FormSwitch/FormSwitch.tsx +++ b/src/shared/components/FormSwitch/FormSwitch.tsx @@ -11,11 +11,7 @@ export interface FormSwitchProps { onChange: (value: boolean) => void } -const FormSwitch: React.FC = ({ - label, - value, - onChange, -}) => { +const FormSwitch: React.FC = ({ label, value, onChange }) => { const theme = useTheme() const id = useId() diff --git a/src/shared/design/Box/Box.tsx b/src/shared/design/Box/Box.tsx index d526d5d..e8a1da6 100644 --- a/src/shared/design/Box/Box.tsx +++ b/src/shared/design/Box/Box.tsx @@ -65,4 +65,4 @@ function getStyles( }, ]), }) -} \ No newline at end of file +} diff --git a/src/shared/design/Button/Button.tsx b/src/shared/design/Button/Button.tsx index d7ae980..155c4f4 100644 --- a/src/shared/design/Button/Button.tsx +++ b/src/shared/design/Button/Button.tsx @@ -6,7 +6,7 @@ import { StyleSheet, Pressable, View, - Text + Text, } from "react-native" import { Theme, useTheme } from "../theme" @@ -23,17 +23,18 @@ export interface ButtonProps extends PressableProps { children: string } -const Button: React.ForwardRefRenderFunction = ({ - variant = "primary", - margin, - padding, - fontSize = 20, - fontWeight = "400", - disabled, - children, - ...props - }, - ref, +const Button: React.ForwardRefRenderFunction = ( + { + variant = "primary", + margin, + padding, + fontSize = 20, + fontWeight = "400", + disabled, + children, + ...props + }, + ref, ) => { const theme = useTheme() const styles = getStyles(theme, variant) diff --git a/src/shared/design/theme/theme.ts b/src/shared/design/theme/theme.ts index 13566fd..b3bea30 100644 --- a/src/shared/design/theme/theme.ts +++ b/src/shared/design/theme/theme.ts @@ -36,7 +36,7 @@ export type ThemeMargin = export type ThemePadding = | keyof Theme["spacing"] | [keyof Theme["spacing"], keyof Theme["spacing"]] - + const light: Theme = { palette: { screen: { From 9f580ecebb7908db9cc4cbb2e962d327cabfc86d Mon Sep 17 00:00:00 2001 From: DanDaManTran Date: Mon, 14 Apr 2025 14:38:21 -0500 Subject: [PATCH 5/5] syncing services --- src/shared/services/auth/AuthProvider.tsx | 36 +++++++++++++++++++++-- src/shared/services/auth/context.ts | 4 +++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/shared/services/auth/AuthProvider.tsx b/src/shared/services/auth/AuthProvider.tsx index b3bc272..f6552c1 100644 --- a/src/shared/services/auth/AuthProvider.tsx +++ b/src/shared/services/auth/AuthProvider.tsx @@ -19,35 +19,65 @@ GoogleSignin.configure({ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { + const [error, setError] = useState() + const [isPending, setIsPending] = useState(true) const [userInfo, setUserInfo] = useState() const signIn = useCallback(async () => { try { + setError(undefined) + setIsPending(true) const userInfo = await GoogleSignin.signIn() + setIsPending(false) setUserInfo(userInfo) return userInfo.user } catch (error) { setUserInfo(null) console.error("GoogleSignin.signIn() error", error) + setError(error as Error) + setIsPending(false) + setUserInfo(null) return false } }, []) const signOut = useCallback(async () => { try { + setError(undefined) + setIsPending(true) + await GoogleSignin.signOut() + setIsPending(false) setUserInfo(null) + return true } catch (error) { console.error("GoogleSignin.signOut() error", error) + setError(error as Error) + return false } }, []) useEffect(() => { async function run() { - const userInfo = await GoogleSignin.getCurrentUser() - setUserInfo(userInfo) + try { + setError(undefined) + setIsPending(true) + + const userInfo = await GoogleSignin.getCurrentUser() + + setIsPending(false) + setUserInfo(userInfo || undefined) + } catch (error) { + console.error( + "Call to GoogleSignin.getCurrentUser() failed with error:", + error, + ) + + setError(error as Error) + setIsPending(false) + } } run() @@ -57,7 +87,9 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ () => ({ signIn, signOut, + error, isAuthenticated: userInfo ? true : userInfo === null ? false : undefined, + isPending, user: userInfo?.user, scopes: userInfo?.scopes, idToken: userInfo?.idToken, diff --git a/src/shared/services/auth/context.ts b/src/shared/services/auth/context.ts index 5fa2f09..4a7e5cf 100644 --- a/src/shared/services/auth/context.ts +++ b/src/shared/services/auth/context.ts @@ -6,8 +6,12 @@ export interface AuthContext { signIn: () => Promise /** Log the user out from Google Auth. Return boolean success. */ signOut: () => Promise + /** Error if any Google API returns an error. Undefined if no errors in the last API call. */ + error?: Error | undefined /** Boolean if the user is authenticated or not. Undefined if unknown. */ isAuthenticated?: boolean + /** Boolean if a Google API call is being made. */ + isPending: boolean /** Google Auth User object. Undefined if not signed in. */ user?: UserInfo["user"] /** List of Google Auth scopes. Undefined if not signed in. */