ignite-parse-auth-kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/CONTRIBUTING.md +0 -0
  2. package/LICENSE +21 -0
  3. package/README.md +492 -0
  4. package/app/app.tsx +116 -0
  5. package/app/components/AlertTongle.tsx +105 -0
  6. package/app/components/AutoImage.tsx +89 -0
  7. package/app/components/Button.tsx +248 -0
  8. package/app/components/Card.tsx +314 -0
  9. package/app/components/EmptyState.tsx +248 -0
  10. package/app/components/Header.tsx +332 -0
  11. package/app/components/Icon.tsx +140 -0
  12. package/app/components/ListItem.tsx +243 -0
  13. package/app/components/ListView.tsx +42 -0
  14. package/app/components/Screen.tsx +305 -0
  15. package/app/components/Text.test.tsx +23 -0
  16. package/app/components/Text.tsx +116 -0
  17. package/app/components/TextField.tsx +292 -0
  18. package/app/components/Toggle/Checkbox.tsx +123 -0
  19. package/app/components/Toggle/Radio.tsx +106 -0
  20. package/app/components/Toggle/Switch.tsx +264 -0
  21. package/app/components/Toggle/Toggle.tsx +285 -0
  22. package/app/components/index copy.ts +15 -0
  23. package/app/components/index.ts +18 -0
  24. package/app/config/config.base.ts +26 -0
  25. package/app/config/config.dev.ts +10 -0
  26. package/app/config/config.prod.ts +10 -0
  27. package/app/config/index.ts +28 -0
  28. package/app/context/AuthContext.tsx +14 -0
  29. package/app/context/EpisodeContext.tsx +136 -0
  30. package/app/context/auth/AuthProvider.tsx +340 -0
  31. package/app/context/auth/hooks.ts +29 -0
  32. package/app/context/auth/index.ts +38 -0
  33. package/app/context/auth/reducer.ts +68 -0
  34. package/app/context/auth/services.ts +394 -0
  35. package/app/context/auth/types.ts +99 -0
  36. package/app/context/auth/validation.ts +45 -0
  37. package/app/devtools/ReactotronClient.ts +9 -0
  38. package/app/devtools/ReactotronClient.web.ts +12 -0
  39. package/app/devtools/ReactotronConfig.ts +139 -0
  40. package/app/i18n/ar.ts +126 -0
  41. package/app/i18n/demo-ar.ts +464 -0
  42. package/app/i18n/demo-en.ts +462 -0
  43. package/app/i18n/demo-es.ts +469 -0
  44. package/app/i18n/demo-fr.ts +471 -0
  45. package/app/i18n/demo-hi.ts +468 -0
  46. package/app/i18n/demo-ja.ts +464 -0
  47. package/app/i18n/demo-ko.ts +457 -0
  48. package/app/i18n/en.ts +146 -0
  49. package/app/i18n/es.ts +132 -0
  50. package/app/i18n/fr.ts +132 -0
  51. package/app/i18n/hi.ts +131 -0
  52. package/app/i18n/index.ts +86 -0
  53. package/app/i18n/ja.ts +130 -0
  54. package/app/i18n/ko.ts +129 -0
  55. package/app/i18n/translate.ts +33 -0
  56. package/app/lib/Parse/index.ts +2 -0
  57. package/app/lib/Parse/parse.ts +62 -0
  58. package/app/navigators/AppNavigator.tsx +145 -0
  59. package/app/navigators/DemoNavigator.tsx +137 -0
  60. package/app/navigators/navigationUtilities.ts +208 -0
  61. package/app/screens/ChooseAuthScreen.tsx +224 -0
  62. package/app/screens/DemoCommunityScreen.tsx +141 -0
  63. package/app/screens/DemoDebugScreen.tsx +192 -0
  64. package/app/screens/DemoPodcastListScreen.tsx +387 -0
  65. package/app/screens/DemoShowroomScreen/DemoDivider.tsx +66 -0
  66. package/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx +313 -0
  67. package/app/screens/DemoShowroomScreen/DemoUseCase.tsx +52 -0
  68. package/app/screens/DemoShowroomScreen/DrawerIconButton.tsx +120 -0
  69. package/app/screens/DemoShowroomScreen/SectionListWithKeyboardAwareScrollView.tsx +59 -0
  70. package/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx +230 -0
  71. package/app/screens/DemoShowroomScreen/demos/DemoButton.tsx +234 -0
  72. package/app/screens/DemoShowroomScreen/demos/DemoCard.tsx +181 -0
  73. package/app/screens/DemoShowroomScreen/demos/DemoEmptyState.tsx +78 -0
  74. package/app/screens/DemoShowroomScreen/demos/DemoHeader.tsx +151 -0
  75. package/app/screens/DemoShowroomScreen/demos/DemoIcon.tsx +115 -0
  76. package/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx +218 -0
  77. package/app/screens/DemoShowroomScreen/demos/DemoText.tsx +144 -0
  78. package/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx +233 -0
  79. package/app/screens/DemoShowroomScreen/demos/DemoToggle.tsx +354 -0
  80. package/app/screens/DemoShowroomScreen/demos/index.ts +12 -0
  81. package/app/screens/ErrorScreen/ErrorBoundary.tsx +76 -0
  82. package/app/screens/ErrorScreen/ErrorDetails.tsx +98 -0
  83. package/app/screens/ForgetPasswordScreen.tsx +180 -0
  84. package/app/screens/LoginScreen.tsx +260 -0
  85. package/app/screens/RegisterScreen.tsx +395 -0
  86. package/app/screens/WelcomeScreen.tsx +114 -0
  87. package/app/services/api/apiProblem.test.ts +73 -0
  88. package/app/services/api/apiProblem.ts +74 -0
  89. package/app/services/api/index.ts +91 -0
  90. package/app/services/api/types.ts +50 -0
  91. package/app/theme/colors.ts +85 -0
  92. package/app/theme/colorsDark.ts +50 -0
  93. package/app/theme/context.tsx +145 -0
  94. package/app/theme/context.utils.ts +25 -0
  95. package/app/theme/spacing.ts +14 -0
  96. package/app/theme/spacingDark.ts +14 -0
  97. package/app/theme/styles.ts +24 -0
  98. package/app/theme/theme.ts +23 -0
  99. package/app/theme/timing.ts +6 -0
  100. package/app/theme/types.ts +64 -0
  101. package/app/theme/typography.ts +71 -0
  102. package/app/utils/crashReporting.ts +62 -0
  103. package/app/utils/delay.ts +6 -0
  104. package/app/utils/formatDate.ts +49 -0
  105. package/app/utils/gestureHandler.native.ts +3 -0
  106. package/app/utils/gestureHandler.ts +6 -0
  107. package/app/utils/hasValidStringProp.ts +11 -0
  108. package/app/utils/openLinkInBrowser.ts +8 -0
  109. package/app/utils/storage/index.ts +82 -0
  110. package/app/utils/storage/storage.test.ts +61 -0
  111. package/app/utils/useHeader.tsx +37 -0
  112. package/app/utils/useIsMounted.ts +18 -0
  113. package/app/utils/useSafeAreaInsetsStyle.ts +46 -0
  114. package/app.config.ts +39 -0
  115. package/app.json +67 -0
  116. package/assets/icons/back.png +0 -0
  117. package/assets/icons/back@2x.png +0 -0
  118. package/assets/icons/back@3x.png +0 -0
  119. package/assets/icons/bell.png +0 -0
  120. package/assets/icons/bell@2x.png +0 -0
  121. package/assets/icons/bell@3x.png +0 -0
  122. package/assets/icons/caretLeft.png +0 -0
  123. package/assets/icons/caretLeft@2x.png +0 -0
  124. package/assets/icons/caretLeft@3x.png +0 -0
  125. package/assets/icons/caretRight.png +0 -0
  126. package/assets/icons/caretRight@2x.png +0 -0
  127. package/assets/icons/caretRight@3x.png +0 -0
  128. package/assets/icons/check.png +0 -0
  129. package/assets/icons/check@2x.png +0 -0
  130. package/assets/icons/check@3x.png +0 -0
  131. package/assets/icons/demo/clap.png +0 -0
  132. package/assets/icons/demo/clap@2x.png +0 -0
  133. package/assets/icons/demo/clap@3x.png +0 -0
  134. package/assets/icons/demo/community.png +0 -0
  135. package/assets/icons/demo/community@2x.png +0 -0
  136. package/assets/icons/demo/community@3x.png +0 -0
  137. package/assets/icons/demo/components.png +0 -0
  138. package/assets/icons/demo/components@2x.png +0 -0
  139. package/assets/icons/demo/components@3x.png +0 -0
  140. package/assets/icons/demo/debug.png +0 -0
  141. package/assets/icons/demo/debug@2x.png +0 -0
  142. package/assets/icons/demo/debug@3x.png +0 -0
  143. package/assets/icons/demo/github.png +0 -0
  144. package/assets/icons/demo/github@2x.png +0 -0
  145. package/assets/icons/demo/github@3x.png +0 -0
  146. package/assets/icons/demo/heart.png +0 -0
  147. package/assets/icons/demo/heart@2x.png +0 -0
  148. package/assets/icons/demo/heart@3x.png +0 -0
  149. package/assets/icons/demo/pin.png +0 -0
  150. package/assets/icons/demo/pin@2x.png +0 -0
  151. package/assets/icons/demo/pin@3x.png +0 -0
  152. package/assets/icons/demo/podcast.png +0 -0
  153. package/assets/icons/demo/podcast@2x.png +0 -0
  154. package/assets/icons/demo/podcast@3x.png +0 -0
  155. package/assets/icons/demo/slack.png +0 -0
  156. package/assets/icons/demo/slack@2x.png +0 -0
  157. package/assets/icons/demo/slack@3x.png +0 -0
  158. package/assets/icons/google.png +0 -0
  159. package/assets/icons/hidden.png +0 -0
  160. package/assets/icons/hidden@2x.png +0 -0
  161. package/assets/icons/hidden@3x.png +0 -0
  162. package/assets/icons/ladybug.png +0 -0
  163. package/assets/icons/ladybug@2x.png +0 -0
  164. package/assets/icons/ladybug@3x.png +0 -0
  165. package/assets/icons/lock.png +0 -0
  166. package/assets/icons/lock@2x.png +0 -0
  167. package/assets/icons/lock@3x.png +0 -0
  168. package/assets/icons/menu.png +0 -0
  169. package/assets/icons/menu@2x.png +0 -0
  170. package/assets/icons/menu@3x.png +0 -0
  171. package/assets/icons/more.png +0 -0
  172. package/assets/icons/more@2x.png +0 -0
  173. package/assets/icons/more@3x.png +0 -0
  174. package/assets/icons/settings.png +0 -0
  175. package/assets/icons/settings@2x.png +0 -0
  176. package/assets/icons/settings@3x.png +0 -0
  177. package/assets/icons/view.png +0 -0
  178. package/assets/icons/view@2x.png +0 -0
  179. package/assets/icons/view@3x.png +0 -0
  180. package/assets/icons/x.png +0 -0
  181. package/assets/icons/x@2x.png +0 -0
  182. package/assets/icons/x@3x.png +0 -0
  183. package/assets/images/app-icon-all.png +0 -0
  184. package/assets/images/app-icon-android-adaptive-background.png +0 -0
  185. package/assets/images/app-icon-android-adaptive-foreground.png +0 -0
  186. package/assets/images/app-icon-android-legacy.png +0 -0
  187. package/assets/images/app-icon-ios.png +0 -0
  188. package/assets/images/app-icon-web-favicon.png +0 -0
  189. package/assets/images/demo/cr-logo.png +0 -0
  190. package/assets/images/demo/cr-logo@2x.png +0 -0
  191. package/assets/images/demo/cr-logo@3x.png +0 -0
  192. package/assets/images/demo/rnl-logo.png +0 -0
  193. package/assets/images/demo/rnl-logo@2x.png +0 -0
  194. package/assets/images/demo/rnl-logo@3x.png +0 -0
  195. package/assets/images/demo/rnn-logo.png +0 -0
  196. package/assets/images/demo/rnn-logo@2x.png +0 -0
  197. package/assets/images/demo/rnn-logo@3x.png +0 -0
  198. package/assets/images/demo/rnr-image-1.png +0 -0
  199. package/assets/images/demo/rnr-image-1@2x.png +0 -0
  200. package/assets/images/demo/rnr-image-1@3x.png +0 -0
  201. package/assets/images/demo/rnr-image-2.png +0 -0
  202. package/assets/images/demo/rnr-image-2@2x.png +0 -0
  203. package/assets/images/demo/rnr-image-2@3x.png +0 -0
  204. package/assets/images/demo/rnr-image-3.png +0 -0
  205. package/assets/images/demo/rnr-image-3@2x.png +0 -0
  206. package/assets/images/demo/rnr-image-3@3x.png +0 -0
  207. package/assets/images/demo/rnr-logo.png +0 -0
  208. package/assets/images/demo/rnr-logo@2x.png +0 -0
  209. package/assets/images/demo/rnr-logo@3x.png +0 -0
  210. package/assets/images/logo.png +0 -0
  211. package/assets/images/logo@2x.png +0 -0
  212. package/assets/images/logo@3x.png +0 -0
  213. package/assets/images/sad-face.png +0 -0
  214. package/assets/images/sad-face@2x.png +0 -0
  215. package/assets/images/sad-face@3x.png +0 -0
  216. package/assets/images/welcome-face.png +0 -0
  217. package/assets/images/welcome-face@2x.png +0 -0
  218. package/assets/images/welcome-face@3x.png +0 -0
  219. package/babel.config.js +7 -0
  220. package/bin/cli.js +196 -0
  221. package/ignite/templates/app-icon/android-adaptive-background.png +0 -0
  222. package/ignite/templates/app-icon/android-adaptive-foreground.png +0 -0
  223. package/ignite/templates/app-icon/android-legacy.png +0 -0
  224. package/ignite/templates/app-icon/ios-universal.png +0 -0
  225. package/ignite/templates/component/NAME.tsx.ejs +39 -0
  226. package/ignite/templates/navigator/NAMENavigator.tsx.ejs +18 -0
  227. package/ignite/templates/screen/NAMEScreen.tsx.ejs +29 -0
  228. package/ignite/templates/splash-screen/logo.png +0 -0
  229. package/index.tsx +9 -0
  230. package/jest.config.js +5 -0
  231. package/metro.config.js +31 -0
  232. package/package.json +166 -0
  233. package/plugins/withSplashScreen.ts +69 -0
  234. package/src/app/_layout.tsx +58 -0
  235. package/src/app/index.tsx +5 -0
  236. package/test/i18n.test.ts +75 -0
  237. package/test/mockFile.ts +6 -0
  238. package/test/setup.ts +58 -0
  239. package/test/test-tsconfig.json +8 -0
  240. package/tsconfig.json +52 -0
  241. package/types/lib.es5.d.ts +25 -0
@@ -0,0 +1,208 @@
1
+ import { useState, useEffect, useRef } from "react"
2
+ import { BackHandler, Linking, Platform } from "react-native"
3
+ import {
4
+ NavigationState,
5
+ PartialState,
6
+ createNavigationContainerRef,
7
+ } from "@react-navigation/native"
8
+
9
+ import Config from "@/config"
10
+ import type { PersistNavigationConfig } from "@/config/config.base"
11
+ import * as storage from "@/utils/storage"
12
+ import { useIsMounted } from "@/utils/useIsMounted"
13
+
14
+ import type { AppStackParamList, NavigationProps } from "./AppNavigator"
15
+
16
+ type Storage = typeof storage
17
+
18
+ /**
19
+ * Reference to the root App Navigator.
20
+ *
21
+ * If needed, you can use this to access the navigation object outside of a
22
+ * `NavigationContainer` context. However, it's recommended to use the `useNavigation` hook whenever possible.
23
+ * @see [Navigating Without Navigation Prop]{@link https://reactnavigation.org/docs/navigating-without-navigation-prop/}
24
+ *
25
+ * The types on this reference will only let you reference top level navigators. If you have
26
+ * nested navigators, you'll need to use the `useNavigation` with the stack navigator's ParamList type.
27
+ */
28
+ export const navigationRef = createNavigationContainerRef<AppStackParamList>()
29
+
30
+ /**
31
+ * Gets the current screen from any navigation state.
32
+ * @param {NavigationState | PartialState<NavigationState>} state - The navigation state to traverse.
33
+ * @returns {string} - The name of the current screen.
34
+ */
35
+ export function getActiveRouteName(state: NavigationState | PartialState<NavigationState>): string {
36
+ const route = state.routes[state.index ?? 0]
37
+
38
+ // Found the active route -- return the name
39
+ if (!route.state) return route.name as keyof AppStackParamList
40
+
41
+ // Recursive call to deal with nested routers
42
+ return getActiveRouteName(route.state as NavigationState<AppStackParamList>)
43
+ }
44
+
45
+ const iosExit = () => false
46
+
47
+ /**
48
+ * Hook that handles Android back button presses and forwards those on to
49
+ * the navigation or allows exiting the app.
50
+ * @see [BackHandler]{@link https://reactnative.dev/docs/backhandler}
51
+ * @param {(routeName: string) => boolean} canExit - Function that returns whether we can exit the app.
52
+ * @returns {void}
53
+ */
54
+ export function useBackButtonHandler(canExit: (routeName: string) => boolean) {
55
+ // The reason we're using a ref here is because we need to be able
56
+ // to update the canExit function without re-setting up all the listeners
57
+ const canExitRef = useRef(Platform.OS !== "android" ? iosExit : canExit)
58
+
59
+ useEffect(() => {
60
+ canExitRef.current = canExit
61
+ }, [canExit])
62
+
63
+ useEffect(() => {
64
+ // We'll fire this when the back button is pressed on Android.
65
+ const onBackPress = () => {
66
+ if (!navigationRef.isReady()) {
67
+ return false
68
+ }
69
+
70
+ // grab the current route
71
+ const routeName = getActiveRouteName(navigationRef.getRootState())
72
+
73
+ // are we allowed to exit?
74
+ if (canExitRef.current(routeName)) {
75
+ // exit and let the system know we've handled the event
76
+ BackHandler.exitApp()
77
+ return true
78
+ }
79
+
80
+ // we can't exit, so let's turn this into a back action
81
+ if (navigationRef.canGoBack()) {
82
+ navigationRef.goBack()
83
+ return true
84
+ }
85
+
86
+ return false
87
+ }
88
+
89
+ // Subscribe when we come to life
90
+ const subscription = BackHandler.addEventListener("hardwareBackPress", onBackPress)
91
+
92
+ // Unsubscribe when we're done
93
+ return () => subscription.remove()
94
+ }, [])
95
+ }
96
+
97
+ /**
98
+ * This helper function will determine whether we should enable navigation persistence
99
+ * based on a config setting and the __DEV__ environment (dev or prod).
100
+ * @param {PersistNavigationConfig} persistNavigation - The config setting for navigation persistence.
101
+ * @returns {boolean} - Whether to restore navigation state by default.
102
+ */
103
+ function navigationRestoredDefaultState(persistNavigation: PersistNavigationConfig) {
104
+ if (persistNavigation === "always") return false
105
+ if (persistNavigation === "dev" && __DEV__) return false
106
+ if (persistNavigation === "prod" && !__DEV__) return false
107
+
108
+ // all other cases, disable restoration by returning true
109
+ return true
110
+ }
111
+
112
+ /**
113
+ * Custom hook for persisting navigation state.
114
+ * @param {Storage} storage - The storage utility to use.
115
+ * @param {string} persistenceKey - The key to use for storing the navigation state.
116
+ * @returns {object} - The navigation state and persistence functions.
117
+ */
118
+ export function useNavigationPersistence(storage: Storage, persistenceKey: string) {
119
+ const [initialNavigationState, setInitialNavigationState] =
120
+ useState<NavigationProps["initialState"]>()
121
+ const isMounted = useIsMounted()
122
+
123
+ const initNavState = navigationRestoredDefaultState(Config.persistNavigation)
124
+ const [isRestored, setIsRestored] = useState(initNavState)
125
+
126
+ const routeNameRef = useRef<keyof AppStackParamList | undefined>(undefined)
127
+
128
+ const onNavigationStateChange = (state: NavigationState | undefined) => {
129
+ const previousRouteName = routeNameRef.current
130
+ if (state !== undefined) {
131
+ const currentRouteName = getActiveRouteName(state)
132
+
133
+ if (previousRouteName !== currentRouteName) {
134
+ // track screens.
135
+ if (__DEV__) {
136
+ console.log(currentRouteName)
137
+ }
138
+ }
139
+
140
+ // Save the current route name for later comparison
141
+ routeNameRef.current = currentRouteName as keyof AppStackParamList
142
+
143
+ // Persist state to storage
144
+ storage.save(persistenceKey, state)
145
+ }
146
+ }
147
+
148
+ const restoreState = async () => {
149
+ try {
150
+ const initialUrl = await Linking.getInitialURL()
151
+
152
+ // Only restore the state if app has not started from a deep link
153
+ if (!initialUrl) {
154
+ const state = (await storage.load(persistenceKey)) as NavigationProps["initialState"] | null
155
+ if (state) setInitialNavigationState(state)
156
+ }
157
+ } finally {
158
+ if (isMounted()) setIsRestored(true)
159
+ }
160
+ }
161
+
162
+ useEffect(() => {
163
+ if (!isRestored) restoreState()
164
+ // runs once on mount
165
+ // eslint-disable-next-line react-hooks/exhaustive-deps
166
+ }, [])
167
+
168
+ return { onNavigationStateChange, restoreState, isRestored, initialNavigationState }
169
+ }
170
+
171
+ /**
172
+ * use this to navigate without the navigation
173
+ * prop. If you have access to the navigation prop, do not use this.
174
+ * @see {@link https://reactnavigation.org/docs/navigating-without-navigation-prop/}
175
+ * @param {unknown} name - The name of the route to navigate to.
176
+ * @param {unknown} params - The params to pass to the route.
177
+ */
178
+ export function navigate(name: unknown, params?: unknown) {
179
+ if (navigationRef.isReady()) {
180
+ // @ts-expect-error
181
+ navigationRef.navigate(name as never, params as never)
182
+ }
183
+ }
184
+
185
+ /**
186
+ * This function is used to go back in a navigation stack, if it's possible to go back.
187
+ * If the navigation stack can't go back, nothing happens.
188
+ * The navigationRef variable is a React ref that references a navigation object.
189
+ * The navigationRef variable is set in the App component.
190
+ */
191
+ export function goBack() {
192
+ if (navigationRef.isReady() && navigationRef.canGoBack()) {
193
+ navigationRef.goBack()
194
+ }
195
+ }
196
+
197
+ /**
198
+ * resetRoot will reset the root navigation state to the given params.
199
+ * @param {Parameters<typeof navigationRef.resetRoot>[0]} state - The state to reset the root to.
200
+ * @returns {void}
201
+ */
202
+ export function resetRoot(
203
+ state: Parameters<typeof navigationRef.resetRoot>[0] = { index: 0, routes: [] },
204
+ ) {
205
+ if (navigationRef.isReady()) {
206
+ navigationRef.resetRoot(state)
207
+ }
208
+ }
@@ -0,0 +1,224 @@
1
+ // // fix this one
2
+
3
+ // import { FC } from "react"
4
+ // import { ViewStyle } from "react-native"
5
+ // import type { AppStackScreenProps } from "@/navigators/AppNavigator"
6
+ // import { Screen } from "@/components/Screen"
7
+ // import { Text } from "@/components/Text"
8
+ // // import { useNavigation } from "@react-navigation/native"
9
+
10
+ // interface ChooseAuthScreenProps extends AppStackScreenProps<"ChooseAuth"> {}
11
+
12
+ // export const ChooseAuthScreen: FC<ChooseAuthScreenProps> = () => {
13
+ // // Pull in navigation via hook
14
+ // // const navigation = useNavigation()
15
+ // return (
16
+ // <Screen style={$root} preset="scroll">
17
+ // <Text text="chooseAuth" />
18
+ // </Screen>
19
+ // )
20
+ // }
21
+
22
+ // const $root: ViewStyle = {
23
+ // flex: 1,
24
+ // }
25
+
26
+ import { FC, useEffect } from "react";
27
+ import { ViewStyle, View, Image, Alert } from "react-native";
28
+ import type { AppStackScreenProps } from "@/navigators/AppNavigator";
29
+
30
+ import { Button, Icon, Screen, Text } from "@/components";
31
+ import { useNavigation } from "@react-navigation/native";
32
+ import { useHeader } from "@/utils/useHeader";
33
+
34
+ import { useAppTheme } from "@/theme/context";
35
+ import type { ThemedStyle } from "@/theme/types";
36
+
37
+ import * as WebBrowser from "expo-web-browser";
38
+ import * as Google from "expo-auth-session/providers/google";
39
+ import { useAuth } from "@/context/AuthContext";
40
+
41
+ WebBrowser.maybeCompleteAuthSession();
42
+
43
+ interface ChooseAuthScreenProps extends AppStackScreenProps<"ChooseAuth"> {}
44
+
45
+ const logo = require("../../assets/images/app-icon-android-adaptive-foreground.png");
46
+
47
+ export const ChooseAuthScreen: FC<ChooseAuthScreenProps> = function ChooseAuthScreen(
48
+ _props
49
+ ) {
50
+ // Use auth context instead of MobX store
51
+ const { isAuthenticated, isLoading, error, googleSignIn } = useAuth();
52
+
53
+ const navigation = useNavigation<any>();
54
+
55
+ const [request, response, promptAsync] = Google.useAuthRequest({
56
+ iosClientId: process.env.EXPO_PUBLIC_IOS_GOOGLE_CLIENT_ID,
57
+ androidClientId: process.env.EXPO_PUBLIC_ANDROID_GOOGLE_CLIENT_ID,
58
+ webClientId: process.env.EXPO_PUBLIC_GOOGLE_CLIENT_ID, // optional if using web
59
+ scopes: ["profile", "email"],
60
+ });
61
+
62
+ // Handle Google authentication response
63
+ useEffect(() => {
64
+ const handleGoogleResponse = async () => {
65
+ if (!response) return;
66
+
67
+ // Log the full response for debugging
68
+ console.log("Google auth response:", response);
69
+
70
+ // Handle different response types
71
+ switch (response.type) {
72
+ case "opened":
73
+ case "locked":
74
+ console.log(`Auth session ${response.type}`);
75
+ return;
76
+
77
+ case "error":
78
+ console.error(
79
+ "Google auth error:",
80
+ response.errorCode,
81
+ response.error
82
+ );
83
+ Alert.alert(
84
+ "Authentication Error",
85
+ response.error?.message ||
86
+ "Failed to sign in with Google. Please try again."
87
+ );
88
+ return;
89
+
90
+ case "cancel":
91
+ case "dismiss":
92
+ console.log("User cancelled the sign in");
93
+ return;
94
+
95
+ case "success":
96
+ try {
97
+ console.log("Processing successful Google auth response");
98
+ const result = await googleSignIn(response as any);
99
+
100
+ if (result.success) {
101
+ console.log("Google sign-in successful!");
102
+ navigation.navigate("Demo", { screen: "DemoCommunity" });
103
+ } else {
104
+ console.log("Google sign-in failed:", result.error);
105
+ Alert.alert(
106
+ "Login Failed",
107
+ result.error || "An unexpected error occurred during sign-in."
108
+ );
109
+ }
110
+ } catch (error) {
111
+ console.error("Error during Google sign-in:", error);
112
+ Alert.alert(
113
+ "Error",
114
+ error instanceof Error
115
+ ? error.message
116
+ : "An unexpected error occurred. Please try again."
117
+ );
118
+ }
119
+ return;
120
+
121
+ default:
122
+ console.warn("Unhandled auth response type:", (response as any).type);
123
+ return;
124
+ }
125
+ };
126
+
127
+ handleGoogleResponse();
128
+ }, [response, googleSignIn, navigation]);
129
+
130
+ // Navigate to main app if already authenticated
131
+ useEffect(() => {
132
+ if (isAuthenticated) {
133
+ navigation.navigate("Welcome");
134
+ }
135
+ }, [isAuthenticated, navigation]);
136
+
137
+ const handleGoogleSignIn = async () => {
138
+ try {
139
+ await promptAsync();
140
+ } catch (error) {
141
+ console.error("Error starting Google sign-in:", error);
142
+ Alert.alert("Error", "Failed to start Google sign-in");
143
+ }
144
+ };
145
+
146
+ useHeader(
147
+ {
148
+ leftIcon: "back",
149
+ title: "Welcome to Dooit",
150
+ onLeftPress: () => navigation.navigate("Welcome"),
151
+ },
152
+ [navigation]
153
+ );
154
+
155
+ const {
156
+ themed,
157
+ theme: { colors },
158
+ } = useAppTheme();
159
+
160
+ return (
161
+ <Screen
162
+ style={$root}
163
+ contentContainerStyle={themed($screenContentContainer)}
164
+ safeAreaEdges={["bottom"]}
165
+ preset="scroll"
166
+ >
167
+ <Image
168
+ source={logo}
169
+ resizeMode="contain"
170
+ style={{ height: 200, width: 200, alignSelf: "center" }}
171
+ />
172
+ <View style={themed($buttonContainer)}>
173
+ <Button
174
+ text="Continue with Email"
175
+ onPress={() => navigation.navigate("Login")}
176
+ preset="reversed"
177
+ />
178
+ <Button
179
+ text="Sign in with Google"
180
+ style={{ gap: 8 }}
181
+ onPress={handleGoogleSignIn}
182
+ preset="reversed"
183
+ disabled={!request || isLoading}
184
+ LeftAccessory={(props) => (
185
+ <Icon icon="google" {...props} size={20} color={colors.border} />
186
+ )}
187
+ />
188
+
189
+ {/* Optional: Show loading state */}
190
+ {isLoading && (
191
+ <Text style={{ textAlign: "center", marginTop: 10 }}>
192
+ Signing in...
193
+ </Text>
194
+ )}
195
+
196
+ {/* Optional: Show error state */}
197
+ {error && (
198
+ <Text
199
+ style={{ textAlign: "center", marginTop: 10, color: colors.error }}
200
+ >
201
+ {error}
202
+ </Text>
203
+ )}
204
+ </View>
205
+ </Screen>
206
+ );
207
+ };
208
+
209
+ const $root: ViewStyle = {
210
+ flex: 1,
211
+ };
212
+
213
+ const $screenContentContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
214
+ paddingHorizontal: spacing.lg,
215
+ });
216
+
217
+ const $buttonContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
218
+ marginTop: spacing.xs,
219
+ flex: 1,
220
+ display: "flex",
221
+ flexDirection: "column",
222
+ justifyContent: "space-around",
223
+ gap: spacing.md,
224
+ });
@@ -0,0 +1,141 @@
1
+ import { FC } from "react"
2
+ import { Image, ImageStyle, TextStyle, View, ViewStyle } from "react-native"
3
+
4
+ import { ListItem } from "@/components/ListItem"
5
+ import { Screen } from "@/components/Screen"
6
+ import { Text } from "@/components/Text"
7
+ import { isRTL } from "@/i18n"
8
+ import { DemoTabScreenProps } from "@/navigators/DemoNavigator"
9
+ import type { ThemedStyle } from "@/theme/types"
10
+ import { useAppTheme } from "@/theme/context"
11
+ import { $styles } from "@/theme/styles"
12
+ import { openLinkInBrowser } from "@/utils/openLinkInBrowser"
13
+
14
+ const chainReactLogo = require("@assets/images/demo/cr-logo.png")
15
+ const reactNativeLiveLogo = require("@assets/images/demo/rnl-logo.png")
16
+ const reactNativeNewsletterLogo = require("@assets/images/demo/rnn-logo.png")
17
+ const reactNativeRadioLogo = require("@assets/images/demo/rnr-logo.png")
18
+
19
+ export const DemoCommunityScreen: FC<DemoTabScreenProps<"DemoCommunity">> =
20
+ function DemoCommunityScreen(_props) {
21
+ const { themed } = useAppTheme()
22
+ return (
23
+ <Screen preset="scroll" contentContainerStyle={$styles.container} safeAreaEdges={["top"]}>
24
+ <Text preset="heading" tx="demoCommunityScreen:title" style={themed($title)} />
25
+ <Text tx="demoCommunityScreen:tagLine" style={themed($tagline)} />
26
+
27
+ <Text preset="subheading" tx="demoCommunityScreen:joinUsOnSlackTitle" />
28
+ <Text tx="demoCommunityScreen:joinUsOnSlack" style={themed($description)} />
29
+ <ListItem
30
+ tx="demoCommunityScreen:joinSlackLink"
31
+ leftIcon="slack"
32
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
33
+ onPress={() => openLinkInBrowser("https://community.infinite.red/")}
34
+ />
35
+ <Text
36
+ preset="subheading"
37
+ tx="demoCommunityScreen:makeIgniteEvenBetterTitle"
38
+ style={themed($sectionTitle)}
39
+ />
40
+ <Text tx="demoCommunityScreen:makeIgniteEvenBetter" style={themed($description)} />
41
+ <ListItem
42
+ tx="demoCommunityScreen:contributeToIgniteLink"
43
+ leftIcon="github"
44
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
45
+ onPress={() => openLinkInBrowser("https://github.com/infinitered/ignite")}
46
+ />
47
+
48
+ <Text
49
+ preset="subheading"
50
+ tx="demoCommunityScreen:theLatestInReactNativeTitle"
51
+ style={themed($sectionTitle)}
52
+ />
53
+ <Text tx="demoCommunityScreen:theLatestInReactNative" style={themed($description)} />
54
+ <ListItem
55
+ tx="demoCommunityScreen:reactNativeRadioLink"
56
+ bottomSeparator
57
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
58
+ LeftComponent={
59
+ <View style={[$styles.row, themed($logoContainer)]}>
60
+ <Image source={reactNativeRadioLogo} style={$logo} />
61
+ </View>
62
+ }
63
+ onPress={() => openLinkInBrowser("https://reactnativeradio.com/")}
64
+ />
65
+ <ListItem
66
+ tx="demoCommunityScreen:reactNativeNewsletterLink"
67
+ bottomSeparator
68
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
69
+ LeftComponent={
70
+ <View style={[$styles.row, themed($logoContainer)]}>
71
+ <Image source={reactNativeNewsletterLogo} style={$logo} />
72
+ </View>
73
+ }
74
+ onPress={() => openLinkInBrowser("https://reactnativenewsletter.com/")}
75
+ />
76
+ <ListItem
77
+ tx="demoCommunityScreen:reactNativeLiveLink"
78
+ bottomSeparator
79
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
80
+ LeftComponent={
81
+ <View style={[$styles.row, themed($logoContainer)]}>
82
+ <Image source={reactNativeLiveLogo} style={$logo} />
83
+ </View>
84
+ }
85
+ onPress={() => openLinkInBrowser("https://rn.live/")}
86
+ />
87
+ <ListItem
88
+ tx="demoCommunityScreen:chainReactConferenceLink"
89
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
90
+ LeftComponent={
91
+ <View style={[$styles.row, themed($logoContainer)]}>
92
+ <Image source={chainReactLogo} style={$logo} />
93
+ </View>
94
+ }
95
+ onPress={() => openLinkInBrowser("https://cr.infinite.red/")}
96
+ />
97
+ <Text
98
+ preset="subheading"
99
+ tx="demoCommunityScreen:hireUsTitle"
100
+ style={themed($sectionTitle)}
101
+ />
102
+ <Text tx="demoCommunityScreen:hireUs" style={themed($description)} />
103
+ <ListItem
104
+ tx="demoCommunityScreen:hireUsLink"
105
+ leftIcon="clap"
106
+ rightIcon={isRTL ? "caretLeft" : "caretRight"}
107
+ onPress={() => openLinkInBrowser("https://infinite.red/contact")}
108
+ />
109
+ </Screen>
110
+ )
111
+ }
112
+
113
+ const $title: ThemedStyle<TextStyle> = ({ spacing }) => ({
114
+ marginBottom: spacing.sm,
115
+ })
116
+
117
+ const $tagline: ThemedStyle<TextStyle> = ({ spacing }) => ({
118
+ marginBottom: spacing.xxl,
119
+ })
120
+
121
+ const $description: ThemedStyle<TextStyle> = ({ spacing }) => ({
122
+ marginBottom: spacing.lg,
123
+ })
124
+
125
+ const $sectionTitle: ThemedStyle<TextStyle> = ({ spacing }) => ({
126
+ marginTop: spacing.xxl,
127
+ })
128
+
129
+ const $logoContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
130
+ marginEnd: spacing.md,
131
+ flexWrap: "wrap",
132
+ alignContent: "center",
133
+ alignSelf: "stretch",
134
+ })
135
+
136
+ const $logo: ImageStyle = {
137
+ height: 38,
138
+ width: 38,
139
+ }
140
+
141
+ // @demo remove-file