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.
- package/CONTRIBUTING.md +0 -0
- package/LICENSE +21 -0
- package/README.md +492 -0
- package/app/app.tsx +116 -0
- package/app/components/AlertTongle.tsx +105 -0
- package/app/components/AutoImage.tsx +89 -0
- package/app/components/Button.tsx +248 -0
- package/app/components/Card.tsx +314 -0
- package/app/components/EmptyState.tsx +248 -0
- package/app/components/Header.tsx +332 -0
- package/app/components/Icon.tsx +140 -0
- package/app/components/ListItem.tsx +243 -0
- package/app/components/ListView.tsx +42 -0
- package/app/components/Screen.tsx +305 -0
- package/app/components/Text.test.tsx +23 -0
- package/app/components/Text.tsx +116 -0
- package/app/components/TextField.tsx +292 -0
- package/app/components/Toggle/Checkbox.tsx +123 -0
- package/app/components/Toggle/Radio.tsx +106 -0
- package/app/components/Toggle/Switch.tsx +264 -0
- package/app/components/Toggle/Toggle.tsx +285 -0
- package/app/components/index copy.ts +15 -0
- package/app/components/index.ts +18 -0
- package/app/config/config.base.ts +26 -0
- package/app/config/config.dev.ts +10 -0
- package/app/config/config.prod.ts +10 -0
- package/app/config/index.ts +28 -0
- package/app/context/AuthContext.tsx +14 -0
- package/app/context/EpisodeContext.tsx +136 -0
- package/app/context/auth/AuthProvider.tsx +340 -0
- package/app/context/auth/hooks.ts +29 -0
- package/app/context/auth/index.ts +38 -0
- package/app/context/auth/reducer.ts +68 -0
- package/app/context/auth/services.ts +394 -0
- package/app/context/auth/types.ts +99 -0
- package/app/context/auth/validation.ts +45 -0
- package/app/devtools/ReactotronClient.ts +9 -0
- package/app/devtools/ReactotronClient.web.ts +12 -0
- package/app/devtools/ReactotronConfig.ts +139 -0
- package/app/i18n/ar.ts +126 -0
- package/app/i18n/demo-ar.ts +464 -0
- package/app/i18n/demo-en.ts +462 -0
- package/app/i18n/demo-es.ts +469 -0
- package/app/i18n/demo-fr.ts +471 -0
- package/app/i18n/demo-hi.ts +468 -0
- package/app/i18n/demo-ja.ts +464 -0
- package/app/i18n/demo-ko.ts +457 -0
- package/app/i18n/en.ts +146 -0
- package/app/i18n/es.ts +132 -0
- package/app/i18n/fr.ts +132 -0
- package/app/i18n/hi.ts +131 -0
- package/app/i18n/index.ts +86 -0
- package/app/i18n/ja.ts +130 -0
- package/app/i18n/ko.ts +129 -0
- package/app/i18n/translate.ts +33 -0
- package/app/lib/Parse/index.ts +2 -0
- package/app/lib/Parse/parse.ts +62 -0
- package/app/navigators/AppNavigator.tsx +145 -0
- package/app/navigators/DemoNavigator.tsx +137 -0
- package/app/navigators/navigationUtilities.ts +208 -0
- package/app/screens/ChooseAuthScreen.tsx +224 -0
- package/app/screens/DemoCommunityScreen.tsx +141 -0
- package/app/screens/DemoDebugScreen.tsx +192 -0
- package/app/screens/DemoPodcastListScreen.tsx +387 -0
- package/app/screens/DemoShowroomScreen/DemoDivider.tsx +66 -0
- package/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx +313 -0
- package/app/screens/DemoShowroomScreen/DemoUseCase.tsx +52 -0
- package/app/screens/DemoShowroomScreen/DrawerIconButton.tsx +120 -0
- package/app/screens/DemoShowroomScreen/SectionListWithKeyboardAwareScrollView.tsx +59 -0
- package/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx +230 -0
- package/app/screens/DemoShowroomScreen/demos/DemoButton.tsx +234 -0
- package/app/screens/DemoShowroomScreen/demos/DemoCard.tsx +181 -0
- package/app/screens/DemoShowroomScreen/demos/DemoEmptyState.tsx +78 -0
- package/app/screens/DemoShowroomScreen/demos/DemoHeader.tsx +151 -0
- package/app/screens/DemoShowroomScreen/demos/DemoIcon.tsx +115 -0
- package/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx +218 -0
- package/app/screens/DemoShowroomScreen/demos/DemoText.tsx +144 -0
- package/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx +233 -0
- package/app/screens/DemoShowroomScreen/demos/DemoToggle.tsx +354 -0
- package/app/screens/DemoShowroomScreen/demos/index.ts +12 -0
- package/app/screens/ErrorScreen/ErrorBoundary.tsx +76 -0
- package/app/screens/ErrorScreen/ErrorDetails.tsx +98 -0
- package/app/screens/ForgetPasswordScreen.tsx +180 -0
- package/app/screens/LoginScreen.tsx +260 -0
- package/app/screens/RegisterScreen.tsx +395 -0
- package/app/screens/WelcomeScreen.tsx +114 -0
- package/app/services/api/apiProblem.test.ts +73 -0
- package/app/services/api/apiProblem.ts +74 -0
- package/app/services/api/index.ts +91 -0
- package/app/services/api/types.ts +50 -0
- package/app/theme/colors.ts +85 -0
- package/app/theme/colorsDark.ts +50 -0
- package/app/theme/context.tsx +145 -0
- package/app/theme/context.utils.ts +25 -0
- package/app/theme/spacing.ts +14 -0
- package/app/theme/spacingDark.ts +14 -0
- package/app/theme/styles.ts +24 -0
- package/app/theme/theme.ts +23 -0
- package/app/theme/timing.ts +6 -0
- package/app/theme/types.ts +64 -0
- package/app/theme/typography.ts +71 -0
- package/app/utils/crashReporting.ts +62 -0
- package/app/utils/delay.ts +6 -0
- package/app/utils/formatDate.ts +49 -0
- package/app/utils/gestureHandler.native.ts +3 -0
- package/app/utils/gestureHandler.ts +6 -0
- package/app/utils/hasValidStringProp.ts +11 -0
- package/app/utils/openLinkInBrowser.ts +8 -0
- package/app/utils/storage/index.ts +82 -0
- package/app/utils/storage/storage.test.ts +61 -0
- package/app/utils/useHeader.tsx +37 -0
- package/app/utils/useIsMounted.ts +18 -0
- package/app/utils/useSafeAreaInsetsStyle.ts +46 -0
- package/app.config.ts +39 -0
- package/app.json +67 -0
- package/assets/icons/back.png +0 -0
- package/assets/icons/back@2x.png +0 -0
- package/assets/icons/back@3x.png +0 -0
- package/assets/icons/bell.png +0 -0
- package/assets/icons/bell@2x.png +0 -0
- package/assets/icons/bell@3x.png +0 -0
- package/assets/icons/caretLeft.png +0 -0
- package/assets/icons/caretLeft@2x.png +0 -0
- package/assets/icons/caretLeft@3x.png +0 -0
- package/assets/icons/caretRight.png +0 -0
- package/assets/icons/caretRight@2x.png +0 -0
- package/assets/icons/caretRight@3x.png +0 -0
- package/assets/icons/check.png +0 -0
- package/assets/icons/check@2x.png +0 -0
- package/assets/icons/check@3x.png +0 -0
- package/assets/icons/demo/clap.png +0 -0
- package/assets/icons/demo/clap@2x.png +0 -0
- package/assets/icons/demo/clap@3x.png +0 -0
- package/assets/icons/demo/community.png +0 -0
- package/assets/icons/demo/community@2x.png +0 -0
- package/assets/icons/demo/community@3x.png +0 -0
- package/assets/icons/demo/components.png +0 -0
- package/assets/icons/demo/components@2x.png +0 -0
- package/assets/icons/demo/components@3x.png +0 -0
- package/assets/icons/demo/debug.png +0 -0
- package/assets/icons/demo/debug@2x.png +0 -0
- package/assets/icons/demo/debug@3x.png +0 -0
- package/assets/icons/demo/github.png +0 -0
- package/assets/icons/demo/github@2x.png +0 -0
- package/assets/icons/demo/github@3x.png +0 -0
- package/assets/icons/demo/heart.png +0 -0
- package/assets/icons/demo/heart@2x.png +0 -0
- package/assets/icons/demo/heart@3x.png +0 -0
- package/assets/icons/demo/pin.png +0 -0
- package/assets/icons/demo/pin@2x.png +0 -0
- package/assets/icons/demo/pin@3x.png +0 -0
- package/assets/icons/demo/podcast.png +0 -0
- package/assets/icons/demo/podcast@2x.png +0 -0
- package/assets/icons/demo/podcast@3x.png +0 -0
- package/assets/icons/demo/slack.png +0 -0
- package/assets/icons/demo/slack@2x.png +0 -0
- package/assets/icons/demo/slack@3x.png +0 -0
- package/assets/icons/google.png +0 -0
- package/assets/icons/hidden.png +0 -0
- package/assets/icons/hidden@2x.png +0 -0
- package/assets/icons/hidden@3x.png +0 -0
- package/assets/icons/ladybug.png +0 -0
- package/assets/icons/ladybug@2x.png +0 -0
- package/assets/icons/ladybug@3x.png +0 -0
- package/assets/icons/lock.png +0 -0
- package/assets/icons/lock@2x.png +0 -0
- package/assets/icons/lock@3x.png +0 -0
- package/assets/icons/menu.png +0 -0
- package/assets/icons/menu@2x.png +0 -0
- package/assets/icons/menu@3x.png +0 -0
- package/assets/icons/more.png +0 -0
- package/assets/icons/more@2x.png +0 -0
- package/assets/icons/more@3x.png +0 -0
- package/assets/icons/settings.png +0 -0
- package/assets/icons/settings@2x.png +0 -0
- package/assets/icons/settings@3x.png +0 -0
- package/assets/icons/view.png +0 -0
- package/assets/icons/view@2x.png +0 -0
- package/assets/icons/view@3x.png +0 -0
- package/assets/icons/x.png +0 -0
- package/assets/icons/x@2x.png +0 -0
- package/assets/icons/x@3x.png +0 -0
- package/assets/images/app-icon-all.png +0 -0
- package/assets/images/app-icon-android-adaptive-background.png +0 -0
- package/assets/images/app-icon-android-adaptive-foreground.png +0 -0
- package/assets/images/app-icon-android-legacy.png +0 -0
- package/assets/images/app-icon-ios.png +0 -0
- package/assets/images/app-icon-web-favicon.png +0 -0
- package/assets/images/demo/cr-logo.png +0 -0
- package/assets/images/demo/cr-logo@2x.png +0 -0
- package/assets/images/demo/cr-logo@3x.png +0 -0
- package/assets/images/demo/rnl-logo.png +0 -0
- package/assets/images/demo/rnl-logo@2x.png +0 -0
- package/assets/images/demo/rnl-logo@3x.png +0 -0
- package/assets/images/demo/rnn-logo.png +0 -0
- package/assets/images/demo/rnn-logo@2x.png +0 -0
- package/assets/images/demo/rnn-logo@3x.png +0 -0
- package/assets/images/demo/rnr-image-1.png +0 -0
- package/assets/images/demo/rnr-image-1@2x.png +0 -0
- package/assets/images/demo/rnr-image-1@3x.png +0 -0
- package/assets/images/demo/rnr-image-2.png +0 -0
- package/assets/images/demo/rnr-image-2@2x.png +0 -0
- package/assets/images/demo/rnr-image-2@3x.png +0 -0
- package/assets/images/demo/rnr-image-3.png +0 -0
- package/assets/images/demo/rnr-image-3@2x.png +0 -0
- package/assets/images/demo/rnr-image-3@3x.png +0 -0
- package/assets/images/demo/rnr-logo.png +0 -0
- package/assets/images/demo/rnr-logo@2x.png +0 -0
- package/assets/images/demo/rnr-logo@3x.png +0 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/logo@2x.png +0 -0
- package/assets/images/logo@3x.png +0 -0
- package/assets/images/sad-face.png +0 -0
- package/assets/images/sad-face@2x.png +0 -0
- package/assets/images/sad-face@3x.png +0 -0
- package/assets/images/welcome-face.png +0 -0
- package/assets/images/welcome-face@2x.png +0 -0
- package/assets/images/welcome-face@3x.png +0 -0
- package/babel.config.js +7 -0
- package/bin/cli.js +196 -0
- package/ignite/templates/app-icon/android-adaptive-background.png +0 -0
- package/ignite/templates/app-icon/android-adaptive-foreground.png +0 -0
- package/ignite/templates/app-icon/android-legacy.png +0 -0
- package/ignite/templates/app-icon/ios-universal.png +0 -0
- package/ignite/templates/component/NAME.tsx.ejs +39 -0
- package/ignite/templates/navigator/NAMENavigator.tsx.ejs +18 -0
- package/ignite/templates/screen/NAMEScreen.tsx.ejs +29 -0
- package/ignite/templates/splash-screen/logo.png +0 -0
- package/index.tsx +9 -0
- package/jest.config.js +5 -0
- package/metro.config.js +31 -0
- package/package.json +166 -0
- package/plugins/withSplashScreen.ts +69 -0
- package/src/app/_layout.tsx +58 -0
- package/src/app/index.tsx +5 -0
- package/test/i18n.test.ts +75 -0
- package/test/mockFile.ts +6 -0
- package/test/setup.ts +58 -0
- package/test/test-tsconfig.json +8 -0
- package/tsconfig.json +52 -0
- 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
|