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,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* If you're using Sentry
|
|
3
|
+
* Expo https://docs.expo.dev/guides/using-sentry/
|
|
4
|
+
*/
|
|
5
|
+
// import * as Sentry from "@sentry/react-native"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* If you're using Crashlytics: https://rnfirebase.io/crashlytics/usage
|
|
9
|
+
*/
|
|
10
|
+
// import crashlytics from "@react-native-firebase/crashlytics"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* If you're using Bugsnag:
|
|
14
|
+
* RN https://docs.bugsnag.com/platforms/react-native/)
|
|
15
|
+
* Expo https://docs.bugsnag.com/platforms/react-native/expo/
|
|
16
|
+
*/
|
|
17
|
+
// import Bugsnag from "@bugsnag/react-native"
|
|
18
|
+
// import Bugsnag from "@bugsnag/expo"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This is where you put your crash reporting service initialization code to call in `./app/app.tsx`
|
|
22
|
+
*/
|
|
23
|
+
export const initCrashReporting = () => {
|
|
24
|
+
// Sentry.init({
|
|
25
|
+
// dsn: "YOUR DSN HERE",
|
|
26
|
+
// debug: true, // If `true`, Sentry will try to print out useful debugging information if something goes wrong with sending the event. Set it to `false` in production
|
|
27
|
+
// })
|
|
28
|
+
// Bugsnag.start("YOUR API KEY")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Error classifications used to sort errors on error reporting services.
|
|
33
|
+
*/
|
|
34
|
+
export enum ErrorType {
|
|
35
|
+
/**
|
|
36
|
+
* An error that would normally cause a red screen in dev
|
|
37
|
+
* and force the user to sign out and restart.
|
|
38
|
+
*/
|
|
39
|
+
FATAL = "Fatal",
|
|
40
|
+
/**
|
|
41
|
+
* An error caught by try/catch where defined using Reactotron.tron.error.
|
|
42
|
+
*/
|
|
43
|
+
HANDLED = "Handled",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Manually report a handled error.
|
|
48
|
+
*/
|
|
49
|
+
export const reportCrash = (error: Error, type: ErrorType = ErrorType.FATAL) => {
|
|
50
|
+
if (__DEV__) {
|
|
51
|
+
// Log to console and Reactotron in development
|
|
52
|
+
const message = error.message || "Unknown"
|
|
53
|
+
console.error(error)
|
|
54
|
+
console.log(message, type)
|
|
55
|
+
} else {
|
|
56
|
+
// In production, utilize crash reporting service of choice below:
|
|
57
|
+
// RN
|
|
58
|
+
// Sentry.captureException(error)
|
|
59
|
+
// crashlytics().recordError(error)
|
|
60
|
+
// Bugsnag.notify(error)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Note the syntax of these imports from the date-fns library.
|
|
2
|
+
// If you import with the syntax: import { format } from "date-fns" the ENTIRE library
|
|
3
|
+
// will be included in your production bundle (even if you only use one function).
|
|
4
|
+
// This is because react-native does not support tree-shaking.
|
|
5
|
+
import { format } from "date-fns/format"
|
|
6
|
+
import type { Locale } from "date-fns/locale"
|
|
7
|
+
import { parseISO } from "date-fns/parseISO"
|
|
8
|
+
import i18n from "i18next"
|
|
9
|
+
|
|
10
|
+
type Options = Parameters<typeof format>[2]
|
|
11
|
+
|
|
12
|
+
let dateFnsLocale: Locale
|
|
13
|
+
export const loadDateFnsLocale = () => {
|
|
14
|
+
const primaryTag = i18n.language.split("-")[0]
|
|
15
|
+
switch (primaryTag) {
|
|
16
|
+
case "en":
|
|
17
|
+
dateFnsLocale = require("date-fns/locale/en-US").default
|
|
18
|
+
break
|
|
19
|
+
case "ar":
|
|
20
|
+
dateFnsLocale = require("date-fns/locale/ar").default
|
|
21
|
+
break
|
|
22
|
+
case "ko":
|
|
23
|
+
dateFnsLocale = require("date-fns/locale/ko").default
|
|
24
|
+
break
|
|
25
|
+
case "es":
|
|
26
|
+
dateFnsLocale = require("date-fns/locale/es").default
|
|
27
|
+
break
|
|
28
|
+
case "fr":
|
|
29
|
+
dateFnsLocale = require("date-fns/locale/fr").default
|
|
30
|
+
break
|
|
31
|
+
case "hi":
|
|
32
|
+
dateFnsLocale = require("date-fns/locale/hi").default
|
|
33
|
+
break
|
|
34
|
+
case "ja":
|
|
35
|
+
dateFnsLocale = require("date-fns/locale/ja").default
|
|
36
|
+
break
|
|
37
|
+
default:
|
|
38
|
+
dateFnsLocale = require("date-fns/locale/en-US").default
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const formatDate = (date: string, dateFormat?: string, options?: Options) => {
|
|
44
|
+
const dateOptions = {
|
|
45
|
+
...options,
|
|
46
|
+
locale: dateFnsLocale,
|
|
47
|
+
}
|
|
48
|
+
return format(parseISO(date), dateFormat ?? "MMM dd, yyyy", dateOptions)
|
|
49
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Don't import react-native-gesture-handler on web
|
|
2
|
+
// https://reactnavigation.org/docs/drawer-navigator/#installation
|
|
3
|
+
|
|
4
|
+
// This however is needed at the moment
|
|
5
|
+
// https://github.com/software-mansion/react-native-gesture-handler/issues/2402
|
|
6
|
+
import "setimmediate"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Type-safe utility to check if an unknown object has a valid string property.
|
|
2
|
+
// This is particularly useful in React 19 where props are typed as unknown by default.
|
|
3
|
+
// The function safely narrows down the type by checking both property existence and type.
|
|
4
|
+
export function hasValidStringProp(props: unknown, propName: string): boolean {
|
|
5
|
+
return (
|
|
6
|
+
props !== null &&
|
|
7
|
+
typeof props === "object" &&
|
|
8
|
+
propName in props &&
|
|
9
|
+
typeof (props as Record<string, unknown>)[propName] === "string"
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { MMKV } from "react-native-mmkv"
|
|
2
|
+
|
|
3
|
+
export const storage = new MMKV()
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Loads a string from storage.
|
|
7
|
+
*
|
|
8
|
+
* @param key The key to fetch.
|
|
9
|
+
*/
|
|
10
|
+
export function loadString(key: string): string | null {
|
|
11
|
+
try {
|
|
12
|
+
return storage.getString(key) ?? null
|
|
13
|
+
} catch {
|
|
14
|
+
// not sure why this would fail... even reading the RN docs I'm unclear
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Saves a string to storage.
|
|
21
|
+
*
|
|
22
|
+
* @param key The key to fetch.
|
|
23
|
+
* @param value The value to store.
|
|
24
|
+
*/
|
|
25
|
+
export function saveString(key: string, value: string): boolean {
|
|
26
|
+
try {
|
|
27
|
+
storage.set(key, value)
|
|
28
|
+
return true
|
|
29
|
+
} catch {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Loads something from storage and runs it thru JSON.parse.
|
|
36
|
+
*
|
|
37
|
+
* @param key The key to fetch.
|
|
38
|
+
*/
|
|
39
|
+
export function load<T>(key: string): T | null {
|
|
40
|
+
let almostThere: string | null = null
|
|
41
|
+
try {
|
|
42
|
+
almostThere = loadString(key)
|
|
43
|
+
return JSON.parse(almostThere ?? "") as T
|
|
44
|
+
} catch {
|
|
45
|
+
return (almostThere as T) ?? null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Saves an object to storage.
|
|
51
|
+
*
|
|
52
|
+
* @param key The key to fetch.
|
|
53
|
+
* @param value The value to store.
|
|
54
|
+
*/
|
|
55
|
+
export function save(key: string, value: unknown): boolean {
|
|
56
|
+
try {
|
|
57
|
+
saveString(key, JSON.stringify(value))
|
|
58
|
+
return true
|
|
59
|
+
} catch {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Removes something from storage.
|
|
66
|
+
*
|
|
67
|
+
* @param key The key to kill.
|
|
68
|
+
*/
|
|
69
|
+
export function remove(key: string): void {
|
|
70
|
+
try {
|
|
71
|
+
storage.delete(key)
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Burn it all to the ground.
|
|
77
|
+
*/
|
|
78
|
+
export function clear(): void {
|
|
79
|
+
try {
|
|
80
|
+
storage.clearAll()
|
|
81
|
+
} catch {}
|
|
82
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { load, loadString, save, saveString, clear, remove, storage } from "."
|
|
2
|
+
|
|
3
|
+
const VALUE_OBJECT = { x: 1 }
|
|
4
|
+
const VALUE_STRING = JSON.stringify(VALUE_OBJECT)
|
|
5
|
+
|
|
6
|
+
describe("MMKV Storage", () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
storage.clearAll()
|
|
9
|
+
storage.set("string", "string")
|
|
10
|
+
storage.set("object", JSON.stringify(VALUE_OBJECT))
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it("should be defined", () => {
|
|
14
|
+
expect(storage).toBeDefined()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("should have default keys", () => {
|
|
18
|
+
expect(storage.getAllKeys()).toEqual(["string", "object"])
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("should load data", () => {
|
|
22
|
+
expect(load<object>("object")).toEqual(VALUE_OBJECT)
|
|
23
|
+
expect(loadString("object")).toEqual(VALUE_STRING)
|
|
24
|
+
|
|
25
|
+
expect(load<string>("string")).toEqual("string")
|
|
26
|
+
expect(loadString("string")).toEqual("string")
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("should save strings", () => {
|
|
30
|
+
saveString("string", "new string")
|
|
31
|
+
expect(loadString("string")).toEqual("new string")
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("should save objects", () => {
|
|
35
|
+
save("object", { y: 2 })
|
|
36
|
+
expect(load<object>("object")).toEqual({ y: 2 })
|
|
37
|
+
save("object", { z: 3, also: true })
|
|
38
|
+
expect(load<object>("object")).toEqual({ z: 3, also: true })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("should save strings and objects", () => {
|
|
42
|
+
saveString("object", "new string")
|
|
43
|
+
expect(loadString("object")).toEqual("new string")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("should remove data", () => {
|
|
47
|
+
remove("object")
|
|
48
|
+
expect(load<object>("object")).toBeNull()
|
|
49
|
+
expect(storage.getAllKeys()).toEqual(["string"])
|
|
50
|
+
|
|
51
|
+
remove("string")
|
|
52
|
+
expect(load<string>("string")).toBeNull()
|
|
53
|
+
expect(storage.getAllKeys()).toEqual([])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("should clear all data", () => {
|
|
57
|
+
expect(storage.getAllKeys()).toEqual(["string", "object"])
|
|
58
|
+
clear()
|
|
59
|
+
expect(storage.getAllKeys()).toEqual([])
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect } from "react"
|
|
2
|
+
import { Platform } from "react-native"
|
|
3
|
+
import { useNavigation } from "@react-navigation/native"
|
|
4
|
+
|
|
5
|
+
import { Header, HeaderProps } from "@/components/Header"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A hook that can be used to easily set the Header of a react-navigation screen from within the screen's component.
|
|
9
|
+
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/utility/useHeader/}
|
|
10
|
+
* @param {HeaderProps} headerProps - The props for the `Header` component.
|
|
11
|
+
* @param {any[]} deps - The dependencies to watch for changes to update the header.
|
|
12
|
+
*/
|
|
13
|
+
export function useHeader(
|
|
14
|
+
headerProps: HeaderProps,
|
|
15
|
+
deps: Parameters<typeof useLayoutEffect>[1] = [],
|
|
16
|
+
) {
|
|
17
|
+
const navigation = useNavigation()
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* We need to have multiple implementations of this hook for web and mobile.
|
|
21
|
+
* Web needs to use useEffect to avoid a rendering loop.
|
|
22
|
+
* In mobile and also to avoid a visible header jump when navigating between screens, we use
|
|
23
|
+
* `useLayoutEffect`, which will apply the settings before the screen renders.
|
|
24
|
+
*/
|
|
25
|
+
const usePlatformEffect = Platform.OS === "web" ? useEffect : useLayoutEffect
|
|
26
|
+
|
|
27
|
+
// To avoid a visible header jump when navigating between screens, we use
|
|
28
|
+
// `useLayoutEffect`, which will apply the settings before the screen renders.
|
|
29
|
+
usePlatformEffect(() => {
|
|
30
|
+
navigation.setOptions({
|
|
31
|
+
headerShown: true,
|
|
32
|
+
header: () => <Header {...headerProps} />,
|
|
33
|
+
})
|
|
34
|
+
// intentionally created API to have user set when they want to update the header via `deps`
|
|
35
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
+
}, [...deps, navigation])
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useCallback, useRef } from "react"
|
|
2
|
+
/**
|
|
3
|
+
* A common react custom hook to check if the component is mounted.
|
|
4
|
+
* @returns {() => boolean} - A function that returns true if the component is mounted.
|
|
5
|
+
*/
|
|
6
|
+
export function useIsMounted() {
|
|
7
|
+
const isMounted = useRef(false)
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
isMounted.current = true
|
|
11
|
+
|
|
12
|
+
return () => {
|
|
13
|
+
isMounted.current = false
|
|
14
|
+
}
|
|
15
|
+
}, [])
|
|
16
|
+
|
|
17
|
+
return useCallback(() => isMounted.current, [])
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Edge, useSafeAreaInsets } from "react-native-safe-area-context"
|
|
2
|
+
|
|
3
|
+
export type ExtendedEdge = Edge | "start" | "end"
|
|
4
|
+
|
|
5
|
+
const propertySuffixMap = {
|
|
6
|
+
top: "Top",
|
|
7
|
+
bottom: "Bottom",
|
|
8
|
+
left: "Start",
|
|
9
|
+
right: "End",
|
|
10
|
+
start: "Start",
|
|
11
|
+
end: "End",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const edgeInsetMap: Record<string, Edge> = {
|
|
15
|
+
start: "left",
|
|
16
|
+
end: "right",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SafeAreaInsetsStyle<
|
|
20
|
+
Property extends "padding" | "margin" = "padding",
|
|
21
|
+
Edges extends Array<ExtendedEdge> = Array<ExtendedEdge>,
|
|
22
|
+
> = {
|
|
23
|
+
[K in Edges[number] as `${Property}${Capitalize<K>}`]: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A hook that can be used to create a safe-area-aware style object that can be passed directly to a View.
|
|
28
|
+
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/utils/useSafeAreaInsetsStyle.ts/}
|
|
29
|
+
* @param {ExtendedEdge[]} safeAreaEdges - The edges to apply the safe area insets to.
|
|
30
|
+
* @param {"padding" | "margin"} property - The property to apply the safe area insets to.
|
|
31
|
+
* @returns {SafeAreaInsetsStyle<Property, Edges>} - The style object with the safe area insets applied.
|
|
32
|
+
*/
|
|
33
|
+
export function useSafeAreaInsetsStyle<
|
|
34
|
+
Property extends "padding" | "margin" = "padding",
|
|
35
|
+
Edges extends Array<ExtendedEdge> = [],
|
|
36
|
+
>(
|
|
37
|
+
safeAreaEdges: Edges = [] as unknown as Edges,
|
|
38
|
+
property: Property = "padding" as Property,
|
|
39
|
+
): SafeAreaInsetsStyle<Property, Edges> {
|
|
40
|
+
const insets = useSafeAreaInsets()
|
|
41
|
+
|
|
42
|
+
return safeAreaEdges.reduce((acc, e) => {
|
|
43
|
+
const value = edgeInsetMap[e] ?? e
|
|
44
|
+
return { ...acc, [`${property}${propertySuffixMap[e]}`]: insets[value] }
|
|
45
|
+
}, {}) as SafeAreaInsetsStyle<Property, Edges>
|
|
46
|
+
}
|
package/app.config.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ExpoConfig, ConfigContext } from "@expo/config"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Use ts-node here so we can use TypeScript for our Config Plugins
|
|
5
|
+
* and not have to compile them to JavaScript
|
|
6
|
+
*/
|
|
7
|
+
require("ts-node/register")
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param config ExpoConfig coming from the static config app.json if it exists
|
|
11
|
+
*
|
|
12
|
+
* You can read more about Expo's Configuration Resolution Rules here:
|
|
13
|
+
* https://docs.expo.dev/workflow/configuration/#configuration-resolution-rules
|
|
14
|
+
*/
|
|
15
|
+
module.exports = ({ config }: ConfigContext): Partial<ExpoConfig> => {
|
|
16
|
+
const existingPlugins = config.plugins ?? []
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
...config,
|
|
20
|
+
ios: {
|
|
21
|
+
...config.ios,
|
|
22
|
+
// This privacyManifests is to get you started.
|
|
23
|
+
// See Expo's guide on apple privacy manifests here:
|
|
24
|
+
// https://docs.expo.dev/guides/apple-privacy/
|
|
25
|
+
// You may need to add more privacy manifests depending on your app's usage of APIs.
|
|
26
|
+
// More details and a list of "required reason" APIs can be found in the Apple Developer Documentation.
|
|
27
|
+
// https://developer.apple.com/documentation/bundleresources/privacy-manifest-files
|
|
28
|
+
privacyManifests: {
|
|
29
|
+
NSPrivacyAccessedAPITypes: [
|
|
30
|
+
{
|
|
31
|
+
NSPrivacyAccessedAPIType: "NSPrivacyAccessedAPICategoryUserDefaults",
|
|
32
|
+
NSPrivacyAccessedAPITypeReasons: ["CA92.1"], // CA92.1 = "Access info from same app, per documentation"
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
plugins: [...existingPlugins, require("./plugins/withSplashScreen").withSplashScreen],
|
|
38
|
+
}
|
|
39
|
+
}
|
package/app.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "AuthTemplate",
|
|
3
|
+
"slug": "AuthTemplate",
|
|
4
|
+
"scheme": "authtemplate",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"orientation": "portrait",
|
|
7
|
+
"userInterfaceStyle": "automatic",
|
|
8
|
+
"icon": "./assets/images/app-icon-all.png",
|
|
9
|
+
"updates": {
|
|
10
|
+
"fallbackToCacheTimeout": 0
|
|
11
|
+
},
|
|
12
|
+
"newArchEnabled": true,
|
|
13
|
+
"jsEngine": "hermes",
|
|
14
|
+
"assetBundlePatterns": [
|
|
15
|
+
"**/*"
|
|
16
|
+
],
|
|
17
|
+
"android": {
|
|
18
|
+
"icon": "./assets/images/app-icon-android-legacy.png",
|
|
19
|
+
"package": "com.authtemplate",
|
|
20
|
+
"adaptiveIcon": {
|
|
21
|
+
"foregroundImage": "./assets/images/app-icon-android-adaptive-foreground.png",
|
|
22
|
+
"backgroundImage": "./assets/images/app-icon-android-adaptive-background.png"
|
|
23
|
+
},
|
|
24
|
+
"allowBackup": false,
|
|
25
|
+
"edgeToEdgeEnabled": true
|
|
26
|
+
},
|
|
27
|
+
"ios": {
|
|
28
|
+
"icon": "./assets/images/app-icon-ios.png",
|
|
29
|
+
"supportsTablet": true,
|
|
30
|
+
"bundleIdentifier": "com.authtemplate"
|
|
31
|
+
},
|
|
32
|
+
"web": {
|
|
33
|
+
"favicon": "./assets/images/app-icon-web-favicon.png",
|
|
34
|
+
"bundler": "metro"
|
|
35
|
+
},
|
|
36
|
+
"plugins": [
|
|
37
|
+
"expo-localization",
|
|
38
|
+
"expo-web-browser",
|
|
39
|
+
"expo-font",
|
|
40
|
+
[
|
|
41
|
+
"expo-splash-screen",
|
|
42
|
+
{
|
|
43
|
+
"image": "./assets/images/app-icon-android-adaptive-foreground.png",
|
|
44
|
+
"imageWidth": 300,
|
|
45
|
+
"resizeMode": "contain",
|
|
46
|
+
"backgroundColor": "#191015"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
"react-native-edge-to-edge",
|
|
51
|
+
{
|
|
52
|
+
"android": {
|
|
53
|
+
"parentTheme": "Light",
|
|
54
|
+
"enforceNavigationBarContrast": false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
],
|
|
59
|
+
"experiments": {
|
|
60
|
+
"tsconfigPaths": true
|
|
61
|
+
},
|
|
62
|
+
"extra": {
|
|
63
|
+
"ignite": {
|
|
64
|
+
"version": "UNKNOWN"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|