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,264 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useCallback } from "react"
|
|
2
|
+
import { Animated, Image, ImageStyle, Platform, StyleProp, View, ViewStyle } from "react-native"
|
|
3
|
+
|
|
4
|
+
import { iconRegistry } from "@/components/Icon"
|
|
5
|
+
import { isRTL } from "@/i18n"
|
|
6
|
+
import type { ThemedStyle } from "@/theme/types"
|
|
7
|
+
import { useAppTheme } from "@/theme/context"
|
|
8
|
+
import { $styles } from "@/theme/styles"
|
|
9
|
+
|
|
10
|
+
import { $inputOuterBase, BaseToggleInputProps, Toggle, ToggleProps } from "./Toggle"
|
|
11
|
+
|
|
12
|
+
export interface SwitchToggleProps extends Omit<ToggleProps<SwitchInputProps>, "ToggleInput"> {
|
|
13
|
+
/**
|
|
14
|
+
* Switch-only prop that adds a text/icon label for on/off states.
|
|
15
|
+
*/
|
|
16
|
+
accessibilityMode?: "text" | "icon"
|
|
17
|
+
/**
|
|
18
|
+
* Optional style prop that affects the knob View.
|
|
19
|
+
* Note: `width` and `height` rules should be points (numbers), not percentages.
|
|
20
|
+
*/
|
|
21
|
+
inputDetailStyle?: Omit<ViewStyle, "width" | "height"> & { width?: number; height?: number }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface SwitchInputProps extends BaseToggleInputProps<SwitchToggleProps> {
|
|
25
|
+
accessibilityMode?: SwitchToggleProps["accessibilityMode"]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {SwitchToggleProps} props - The props for the `Switch` component.
|
|
30
|
+
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Switch}
|
|
31
|
+
* @returns {JSX.Element} The rendered `Switch` component.
|
|
32
|
+
*/
|
|
33
|
+
export function Switch(props: SwitchToggleProps) {
|
|
34
|
+
const { accessibilityMode, ...rest } = props
|
|
35
|
+
const switchInput = useCallback(
|
|
36
|
+
(toggleProps: SwitchInputProps) => (
|
|
37
|
+
<SwitchInput {...toggleProps} accessibilityMode={accessibilityMode} />
|
|
38
|
+
),
|
|
39
|
+
[accessibilityMode],
|
|
40
|
+
)
|
|
41
|
+
return <Toggle accessibilityRole="switch" {...rest} ToggleInput={switchInput} />
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function SwitchInput(props: SwitchInputProps) {
|
|
45
|
+
const {
|
|
46
|
+
on,
|
|
47
|
+
status,
|
|
48
|
+
disabled,
|
|
49
|
+
outerStyle: $outerStyleOverride,
|
|
50
|
+
innerStyle: $innerStyleOverride,
|
|
51
|
+
detailStyle: $detailStyleOverride,
|
|
52
|
+
} = props
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
theme: { colors },
|
|
56
|
+
themed,
|
|
57
|
+
} = useAppTheme()
|
|
58
|
+
|
|
59
|
+
const animate = useRef(new Animated.Value(on ? 1 : 0)) // Initial value is set based on isActive
|
|
60
|
+
const opacity = useRef(new Animated.Value(0))
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
Animated.timing(animate.current, {
|
|
64
|
+
toValue: on ? 1 : 0,
|
|
65
|
+
duration: 300,
|
|
66
|
+
useNativeDriver: true, // Enable native driver for smoother animations
|
|
67
|
+
}).start()
|
|
68
|
+
}, [on])
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
Animated.timing(opacity.current, {
|
|
72
|
+
toValue: on ? 1 : 0,
|
|
73
|
+
duration: 300,
|
|
74
|
+
useNativeDriver: true,
|
|
75
|
+
}).start()
|
|
76
|
+
}, [on])
|
|
77
|
+
|
|
78
|
+
const knobSizeFallback = 2
|
|
79
|
+
|
|
80
|
+
const knobWidth = [$detailStyleOverride?.width, $switchDetail?.width, knobSizeFallback].find(
|
|
81
|
+
(v) => typeof v === "number",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
const knobHeight = [$detailStyleOverride?.height, $switchDetail?.height, knobSizeFallback].find(
|
|
85
|
+
(v) => typeof v === "number",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const offBackgroundColor = [
|
|
89
|
+
disabled && colors.palette.neutral400,
|
|
90
|
+
status === "error" && colors.errorBackground,
|
|
91
|
+
colors.palette.neutral300,
|
|
92
|
+
].filter(Boolean)[0]
|
|
93
|
+
|
|
94
|
+
const onBackgroundColor = [
|
|
95
|
+
disabled && colors.transparent,
|
|
96
|
+
status === "error" && colors.errorBackground,
|
|
97
|
+
colors.palette.secondary500,
|
|
98
|
+
].filter(Boolean)[0]
|
|
99
|
+
|
|
100
|
+
const knobBackgroundColor = (function () {
|
|
101
|
+
if (on) {
|
|
102
|
+
return [
|
|
103
|
+
$detailStyleOverride?.backgroundColor,
|
|
104
|
+
status === "error" && colors.error,
|
|
105
|
+
disabled && colors.palette.neutral600,
|
|
106
|
+
colors.palette.neutral100,
|
|
107
|
+
].filter(Boolean)[0]
|
|
108
|
+
} else {
|
|
109
|
+
return [
|
|
110
|
+
$innerStyleOverride?.backgroundColor,
|
|
111
|
+
disabled && colors.palette.neutral600,
|
|
112
|
+
status === "error" && colors.error,
|
|
113
|
+
colors.palette.neutral200,
|
|
114
|
+
].filter(Boolean)[0]
|
|
115
|
+
}
|
|
116
|
+
})()
|
|
117
|
+
|
|
118
|
+
const rtlAdjustment = isRTL ? -1 : 1
|
|
119
|
+
const $themedSwitchInner = useMemo(() => themed([$styles.toggleInner, $switchInner]), [themed])
|
|
120
|
+
|
|
121
|
+
const offsetLeft = ($innerStyleOverride?.paddingStart ||
|
|
122
|
+
$innerStyleOverride?.paddingLeft ||
|
|
123
|
+
$themedSwitchInner?.paddingStart ||
|
|
124
|
+
$themedSwitchInner?.paddingLeft ||
|
|
125
|
+
0) as number
|
|
126
|
+
|
|
127
|
+
const offsetRight = ($innerStyleOverride?.paddingEnd ||
|
|
128
|
+
$innerStyleOverride?.paddingRight ||
|
|
129
|
+
$themedSwitchInner?.paddingEnd ||
|
|
130
|
+
$themedSwitchInner?.paddingRight ||
|
|
131
|
+
0) as number
|
|
132
|
+
|
|
133
|
+
const outputRange =
|
|
134
|
+
Platform.OS === "web"
|
|
135
|
+
? isRTL
|
|
136
|
+
? [+(knobWidth || 0) + offsetRight, offsetLeft]
|
|
137
|
+
: [offsetLeft, +(knobWidth || 0) + offsetRight]
|
|
138
|
+
: [rtlAdjustment * offsetLeft, rtlAdjustment * (+(knobWidth || 0) + offsetRight)]
|
|
139
|
+
|
|
140
|
+
const $animatedSwitchKnob = animate.current.interpolate({
|
|
141
|
+
inputRange: [0, 1],
|
|
142
|
+
outputRange,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<View style={[$inputOuter, { backgroundColor: offBackgroundColor }, $outerStyleOverride]}>
|
|
147
|
+
<Animated.View
|
|
148
|
+
style={[
|
|
149
|
+
$themedSwitchInner,
|
|
150
|
+
{ backgroundColor: onBackgroundColor },
|
|
151
|
+
$innerStyleOverride,
|
|
152
|
+
{ opacity: opacity.current },
|
|
153
|
+
]}
|
|
154
|
+
/>
|
|
155
|
+
|
|
156
|
+
<SwitchAccessibilityLabel {...props} role="on" />
|
|
157
|
+
<SwitchAccessibilityLabel {...props} role="off" />
|
|
158
|
+
|
|
159
|
+
<Animated.View
|
|
160
|
+
style={[
|
|
161
|
+
$switchDetail,
|
|
162
|
+
$detailStyleOverride,
|
|
163
|
+
{ transform: [{ translateX: $animatedSwitchKnob }] },
|
|
164
|
+
{ width: knobWidth, height: knobHeight },
|
|
165
|
+
{ backgroundColor: knobBackgroundColor },
|
|
166
|
+
]}
|
|
167
|
+
/>
|
|
168
|
+
</View>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {ToggleInputProps & { role: "on" | "off" }} props - The props for the `SwitchAccessibilityLabel` component.
|
|
174
|
+
* @returns {JSX.Element} The rendered `SwitchAccessibilityLabel` component.
|
|
175
|
+
*/
|
|
176
|
+
function SwitchAccessibilityLabel(props: SwitchInputProps & { role: "on" | "off" }) {
|
|
177
|
+
const { on, disabled, status, accessibilityMode, role, innerStyle, detailStyle } = props
|
|
178
|
+
|
|
179
|
+
const {
|
|
180
|
+
theme: { colors },
|
|
181
|
+
} = useAppTheme()
|
|
182
|
+
|
|
183
|
+
if (!accessibilityMode) return null
|
|
184
|
+
|
|
185
|
+
const shouldLabelBeVisible = (on && role === "on") || (!on && role === "off")
|
|
186
|
+
|
|
187
|
+
const $switchAccessibilityStyle: StyleProp<ViewStyle> = [
|
|
188
|
+
$switchAccessibility,
|
|
189
|
+
role === "off" && { end: "5%" },
|
|
190
|
+
role === "on" && { left: "5%" },
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
const color = (function () {
|
|
194
|
+
if (disabled) return colors.palette.neutral600
|
|
195
|
+
if (status === "error") return colors.error
|
|
196
|
+
if (!on) return innerStyle?.backgroundColor || colors.palette.secondary500
|
|
197
|
+
return detailStyle?.backgroundColor || colors.palette.neutral100
|
|
198
|
+
})()
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<View style={$switchAccessibilityStyle}>
|
|
202
|
+
{accessibilityMode === "text" && shouldLabelBeVisible && (
|
|
203
|
+
<View
|
|
204
|
+
style={[
|
|
205
|
+
role === "on" && $switchAccessibilityLine,
|
|
206
|
+
role === "on" && { backgroundColor: color },
|
|
207
|
+
role === "off" && $switchAccessibilityCircle,
|
|
208
|
+
role === "off" && { borderColor: color },
|
|
209
|
+
]}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
|
|
213
|
+
{accessibilityMode === "icon" && shouldLabelBeVisible && (
|
|
214
|
+
<Image
|
|
215
|
+
style={[$switchAccessibilityIcon, { tintColor: color }]}
|
|
216
|
+
source={role === "off" ? iconRegistry.hidden : iconRegistry.view}
|
|
217
|
+
/>
|
|
218
|
+
)}
|
|
219
|
+
</View>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const $inputOuter: StyleProp<ViewStyle> = [
|
|
224
|
+
$inputOuterBase,
|
|
225
|
+
{ height: 32, width: 56, borderRadius: 16, borderWidth: 0 },
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
const $switchInner: ThemedStyle<ViewStyle> = ({ colors }) => ({
|
|
229
|
+
borderColor: colors.transparent,
|
|
230
|
+
position: "absolute",
|
|
231
|
+
paddingStart: 4,
|
|
232
|
+
paddingEnd: 4,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const $switchDetail: SwitchToggleProps["inputDetailStyle"] = {
|
|
236
|
+
borderRadius: 12,
|
|
237
|
+
position: "absolute",
|
|
238
|
+
width: 24,
|
|
239
|
+
height: 24,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const $switchAccessibility: ViewStyle = {
|
|
243
|
+
width: "40%",
|
|
244
|
+
justifyContent: "center",
|
|
245
|
+
alignItems: "center",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const $switchAccessibilityIcon: ImageStyle = {
|
|
249
|
+
width: 14,
|
|
250
|
+
height: 14,
|
|
251
|
+
resizeMode: "contain",
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const $switchAccessibilityLine: ViewStyle = {
|
|
255
|
+
width: 2,
|
|
256
|
+
height: 12,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const $switchAccessibilityCircle: ViewStyle = {
|
|
260
|
+
borderWidth: 2,
|
|
261
|
+
width: 12,
|
|
262
|
+
height: 12,
|
|
263
|
+
borderRadius: 6,
|
|
264
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { ComponentType, FC, useMemo } from "react"
|
|
2
|
+
import {
|
|
3
|
+
GestureResponderEvent,
|
|
4
|
+
ImageStyle,
|
|
5
|
+
StyleProp,
|
|
6
|
+
SwitchProps,
|
|
7
|
+
TextInputProps,
|
|
8
|
+
TextStyle,
|
|
9
|
+
TouchableOpacity,
|
|
10
|
+
TouchableOpacityProps,
|
|
11
|
+
View,
|
|
12
|
+
ViewProps,
|
|
13
|
+
ViewStyle,
|
|
14
|
+
} from "react-native"
|
|
15
|
+
|
|
16
|
+
import type { ThemedStyle } from "@/theme/types"
|
|
17
|
+
import { useAppTheme } from "@/theme/context"
|
|
18
|
+
import { $styles } from "@/theme/styles"
|
|
19
|
+
|
|
20
|
+
import { Text, TextProps } from "../Text"
|
|
21
|
+
|
|
22
|
+
export interface ToggleProps<T> extends Omit<TouchableOpacityProps, "style"> {
|
|
23
|
+
/**
|
|
24
|
+
* A style modifier for different input states.
|
|
25
|
+
*/
|
|
26
|
+
status?: "error" | "disabled"
|
|
27
|
+
/**
|
|
28
|
+
* If false, input is not editable. The default value is true.
|
|
29
|
+
*/
|
|
30
|
+
editable?: TextInputProps["editable"]
|
|
31
|
+
/**
|
|
32
|
+
* The value of the field. If true the component will be turned on.
|
|
33
|
+
*/
|
|
34
|
+
value?: boolean
|
|
35
|
+
/**
|
|
36
|
+
* Invoked with the new value when the value changes.
|
|
37
|
+
*/
|
|
38
|
+
onValueChange?: SwitchProps["onValueChange"]
|
|
39
|
+
/**
|
|
40
|
+
* Style overrides for the container
|
|
41
|
+
*/
|
|
42
|
+
containerStyle?: StyleProp<ViewStyle>
|
|
43
|
+
/**
|
|
44
|
+
* Style overrides for the input wrapper
|
|
45
|
+
*/
|
|
46
|
+
inputWrapperStyle?: StyleProp<ViewStyle>
|
|
47
|
+
/**
|
|
48
|
+
* Optional input wrapper style override.
|
|
49
|
+
* This gives the inputs their size, shape, "off" background-color, and outer border.
|
|
50
|
+
*/
|
|
51
|
+
inputOuterStyle?: ViewStyle
|
|
52
|
+
/**
|
|
53
|
+
* Optional input style override.
|
|
54
|
+
* This gives the inputs their inner characteristics and "on" background-color.
|
|
55
|
+
*/
|
|
56
|
+
inputInnerStyle?: ViewStyle
|
|
57
|
+
/**
|
|
58
|
+
* Optional detail style override.
|
|
59
|
+
* See Checkbox, Radio, and Switch for more details
|
|
60
|
+
*/
|
|
61
|
+
inputDetailStyle?: ViewStyle
|
|
62
|
+
/**
|
|
63
|
+
* The position of the label relative to the action component.
|
|
64
|
+
* Default: right
|
|
65
|
+
*/
|
|
66
|
+
labelPosition?: "left" | "right"
|
|
67
|
+
/**
|
|
68
|
+
* The label text to display if not using `labelTx`.
|
|
69
|
+
*/
|
|
70
|
+
label?: TextProps["text"]
|
|
71
|
+
/**
|
|
72
|
+
* Label text which is looked up via i18n.
|
|
73
|
+
*/
|
|
74
|
+
labelTx?: TextProps["tx"]
|
|
75
|
+
/**
|
|
76
|
+
* Optional label options to pass to i18n. Useful for interpolation
|
|
77
|
+
* as well as explicitly setting locale or translation fallbacks.
|
|
78
|
+
*/
|
|
79
|
+
labelTxOptions?: TextProps["txOptions"]
|
|
80
|
+
/**
|
|
81
|
+
* Style overrides for label text.
|
|
82
|
+
*/
|
|
83
|
+
labelStyle?: StyleProp<TextStyle>
|
|
84
|
+
/**
|
|
85
|
+
* Pass any additional props directly to the label Text component.
|
|
86
|
+
*/
|
|
87
|
+
LabelTextProps?: TextProps
|
|
88
|
+
/**
|
|
89
|
+
* The helper text to display if not using `helperTx`.
|
|
90
|
+
*/
|
|
91
|
+
helper?: TextProps["text"]
|
|
92
|
+
/**
|
|
93
|
+
* Helper text which is looked up via i18n.
|
|
94
|
+
*/
|
|
95
|
+
helperTx?: TextProps["tx"]
|
|
96
|
+
/**
|
|
97
|
+
* Optional helper options to pass to i18n. Useful for interpolation
|
|
98
|
+
* as well as explicitly setting locale or translation fallbacks.
|
|
99
|
+
*/
|
|
100
|
+
helperTxOptions?: TextProps["txOptions"]
|
|
101
|
+
/**
|
|
102
|
+
* Pass any additional props directly to the helper Text component.
|
|
103
|
+
*/
|
|
104
|
+
HelperTextProps?: TextProps
|
|
105
|
+
/**
|
|
106
|
+
* The input control for the type of toggle component
|
|
107
|
+
*/
|
|
108
|
+
ToggleInput: FC<BaseToggleInputProps<T>>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface BaseToggleInputProps<T> {
|
|
112
|
+
on: boolean
|
|
113
|
+
status: ToggleProps<T>["status"]
|
|
114
|
+
disabled: boolean
|
|
115
|
+
outerStyle: ViewStyle
|
|
116
|
+
innerStyle: ViewStyle
|
|
117
|
+
detailStyle: ViewStyle | ImageStyle
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Renders a boolean input.
|
|
122
|
+
* This is a controlled component that requires an onValueChange callback that updates the value prop in order for the component to reflect user actions. If the value prop is not updated, the component will continue to render the supplied value prop instead of the expected result of any user actions.
|
|
123
|
+
* @param {ToggleProps} props - The props for the `Toggle` component.
|
|
124
|
+
* @returns {JSX.Element} The rendered `Toggle` component.
|
|
125
|
+
*/
|
|
126
|
+
export function Toggle<T>(props: ToggleProps<T>) {
|
|
127
|
+
const {
|
|
128
|
+
editable = true,
|
|
129
|
+
status,
|
|
130
|
+
value,
|
|
131
|
+
onPress,
|
|
132
|
+
onValueChange,
|
|
133
|
+
labelPosition = "right",
|
|
134
|
+
helper,
|
|
135
|
+
helperTx,
|
|
136
|
+
helperTxOptions,
|
|
137
|
+
HelperTextProps,
|
|
138
|
+
containerStyle: $containerStyleOverride,
|
|
139
|
+
inputWrapperStyle: $inputWrapperStyleOverride,
|
|
140
|
+
ToggleInput,
|
|
141
|
+
accessibilityRole,
|
|
142
|
+
...WrapperProps
|
|
143
|
+
} = props
|
|
144
|
+
|
|
145
|
+
const {
|
|
146
|
+
theme: { colors },
|
|
147
|
+
themed,
|
|
148
|
+
} = useAppTheme()
|
|
149
|
+
|
|
150
|
+
const disabled = editable === false || status === "disabled" || props.disabled
|
|
151
|
+
|
|
152
|
+
const Wrapper = useMemo(
|
|
153
|
+
() => (disabled ? View : TouchableOpacity) as ComponentType<TouchableOpacityProps | ViewProps>,
|
|
154
|
+
[disabled],
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const $containerStyles = [$containerStyleOverride]
|
|
158
|
+
const $inputWrapperStyles = [$styles.row, $inputWrapper, $inputWrapperStyleOverride]
|
|
159
|
+
const $helperStyles = themed([
|
|
160
|
+
$helper,
|
|
161
|
+
status === "error" && { color: colors.error },
|
|
162
|
+
HelperTextProps?.style,
|
|
163
|
+
])
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param {GestureResponderEvent} e - The event object.
|
|
167
|
+
*/
|
|
168
|
+
function handlePress(e: GestureResponderEvent) {
|
|
169
|
+
if (disabled) return
|
|
170
|
+
onValueChange?.(!value)
|
|
171
|
+
onPress?.(e)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<Wrapper
|
|
176
|
+
activeOpacity={1}
|
|
177
|
+
accessibilityRole={accessibilityRole}
|
|
178
|
+
accessibilityState={{ checked: value, disabled }}
|
|
179
|
+
{...WrapperProps}
|
|
180
|
+
style={$containerStyles}
|
|
181
|
+
onPress={handlePress}
|
|
182
|
+
>
|
|
183
|
+
<View style={$inputWrapperStyles}>
|
|
184
|
+
{labelPosition === "left" && <FieldLabel<T> {...props} labelPosition={labelPosition} />}
|
|
185
|
+
|
|
186
|
+
<ToggleInput
|
|
187
|
+
on={!!value}
|
|
188
|
+
disabled={!!disabled}
|
|
189
|
+
status={status}
|
|
190
|
+
outerStyle={props.inputOuterStyle ?? {}}
|
|
191
|
+
innerStyle={props.inputInnerStyle ?? {}}
|
|
192
|
+
detailStyle={props.inputDetailStyle ?? {}}
|
|
193
|
+
/>
|
|
194
|
+
|
|
195
|
+
{labelPosition === "right" && <FieldLabel<T> {...props} labelPosition={labelPosition} />}
|
|
196
|
+
</View>
|
|
197
|
+
|
|
198
|
+
{!!(helper || helperTx) && (
|
|
199
|
+
<Text
|
|
200
|
+
preset="formHelper"
|
|
201
|
+
text={helper}
|
|
202
|
+
tx={helperTx}
|
|
203
|
+
txOptions={helperTxOptions}
|
|
204
|
+
{...HelperTextProps}
|
|
205
|
+
style={$helperStyles}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
208
|
+
</Wrapper>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {ToggleProps} props - The props for the `FieldLabel` component.
|
|
214
|
+
* @returns {JSX.Element} The rendered `FieldLabel` component.
|
|
215
|
+
*/
|
|
216
|
+
function FieldLabel<T>(props: ToggleProps<T>) {
|
|
217
|
+
const {
|
|
218
|
+
status,
|
|
219
|
+
label,
|
|
220
|
+
labelTx,
|
|
221
|
+
labelTxOptions,
|
|
222
|
+
LabelTextProps,
|
|
223
|
+
labelPosition,
|
|
224
|
+
labelStyle: $labelStyleOverride,
|
|
225
|
+
} = props
|
|
226
|
+
|
|
227
|
+
const {
|
|
228
|
+
theme: { colors },
|
|
229
|
+
themed,
|
|
230
|
+
} = useAppTheme()
|
|
231
|
+
|
|
232
|
+
if (!label && !labelTx && !LabelTextProps?.children) return null
|
|
233
|
+
|
|
234
|
+
const $labelStyle = themed([
|
|
235
|
+
$label,
|
|
236
|
+
status === "error" && { color: colors.error },
|
|
237
|
+
labelPosition === "right" && $labelRight,
|
|
238
|
+
labelPosition === "left" && $labelLeft,
|
|
239
|
+
$labelStyleOverride,
|
|
240
|
+
LabelTextProps?.style,
|
|
241
|
+
])
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<Text
|
|
245
|
+
preset="formLabel"
|
|
246
|
+
text={label}
|
|
247
|
+
tx={labelTx}
|
|
248
|
+
txOptions={labelTxOptions}
|
|
249
|
+
{...LabelTextProps}
|
|
250
|
+
style={$labelStyle}
|
|
251
|
+
/>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const $inputWrapper: ViewStyle = {
|
|
256
|
+
alignItems: "center",
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export const $inputOuterBase: ViewStyle = {
|
|
260
|
+
height: 24,
|
|
261
|
+
width: 24,
|
|
262
|
+
borderWidth: 2,
|
|
263
|
+
alignItems: "center",
|
|
264
|
+
overflow: "hidden",
|
|
265
|
+
flexGrow: 0,
|
|
266
|
+
flexShrink: 0,
|
|
267
|
+
justifyContent: "space-between",
|
|
268
|
+
flexDirection: "row",
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const $helper: ThemedStyle<TextStyle> = ({ spacing }) => ({
|
|
272
|
+
marginTop: spacing.xs,
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
const $label: TextStyle = {
|
|
276
|
+
flex: 1,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const $labelRight: ThemedStyle<TextStyle> = ({ spacing }) => ({
|
|
280
|
+
marginStart: spacing.md,
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
const $labelLeft: ThemedStyle<TextStyle> = ({ spacing }) => ({
|
|
284
|
+
marginEnd: spacing.md,
|
|
285
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from "./AutoImage"
|
|
2
|
+
export * from "./Button"
|
|
3
|
+
export * from "./Card"
|
|
4
|
+
export * from "./Header"
|
|
5
|
+
export * from "./Icon"
|
|
6
|
+
export * from "./ListItem"
|
|
7
|
+
export * from "./ListView"
|
|
8
|
+
export * from "./Screen"
|
|
9
|
+
export * from "./Text"
|
|
10
|
+
export * from "./TextField"
|
|
11
|
+
export * from "./Toggle"
|
|
12
|
+
export * from "./EmptyState"
|
|
13
|
+
export * from "./Calendar"
|
|
14
|
+
export * from "./TaskModal"
|
|
15
|
+
export * from "./AlertTongle"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./Button"
|
|
2
|
+
export * from "./Icon"
|
|
3
|
+
export * from "./Screen"
|
|
4
|
+
export * from "./Text"
|
|
5
|
+
export * from "./AutoImage"
|
|
6
|
+
export * from "./Button"
|
|
7
|
+
export * from "./Card"
|
|
8
|
+
export * from "./Header"
|
|
9
|
+
export * from "./Icon"
|
|
10
|
+
export * from "./ListItem"
|
|
11
|
+
export * from "./ListView"
|
|
12
|
+
export * from "./Screen"
|
|
13
|
+
export * from "./Text"
|
|
14
|
+
export * from "./TextField"
|
|
15
|
+
|
|
16
|
+
export * from "./EmptyState"
|
|
17
|
+
|
|
18
|
+
export * from "./AlertTongle"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ConfigBaseProps {
|
|
2
|
+
persistNavigation: "always" | "dev" | "prod" | "never"
|
|
3
|
+
catchErrors: "always" | "dev" | "prod" | "never"
|
|
4
|
+
exitRoutes: string[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type PersistNavigationConfig = ConfigBaseProps["persistNavigation"]
|
|
8
|
+
|
|
9
|
+
const BaseConfig: ConfigBaseProps = {
|
|
10
|
+
// This feature is particularly useful in development mode, but
|
|
11
|
+
// can be used in production as well if you prefer.
|
|
12
|
+
persistNavigation: "dev",
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Only enable if we're catching errors in the right environment
|
|
16
|
+
*/
|
|
17
|
+
catchErrors: "always",
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This is a list of all the route names that will exit the app if the back button
|
|
21
|
+
* is pressed while in that screen. Only affects Android.
|
|
22
|
+
*/
|
|
23
|
+
exitRoutes: ["Welcome"],
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default BaseConfig
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* These are configuration settings for the dev environment.
|
|
3
|
+
*
|
|
4
|
+
* Do not include API secrets in this file or anywhere in your JS.
|
|
5
|
+
*
|
|
6
|
+
* https://reactnative.dev/docs/security#storing-sensitive-info
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
API_URL: "https://api.rss2json.com/v1/",
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* These are configuration settings for the production environment.
|
|
3
|
+
*
|
|
4
|
+
* Do not include API secrets in this file or anywhere in your JS.
|
|
5
|
+
*
|
|
6
|
+
* https://reactnative.dev/docs/security#storing-sensitive-info
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
API_URL: "https://api.rss2json.com/v1/",
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file imports configuration objects from either the config.dev.js file
|
|
3
|
+
* or the config.prod.js file depending on whether we are in __DEV__ or not.
|
|
4
|
+
*
|
|
5
|
+
* Note that we do not gitignore these files. Unlike on web servers, just because
|
|
6
|
+
* these are not checked into your repo doesn't mean that they are secure.
|
|
7
|
+
* In fact, you're shipping a JavaScript bundle with every
|
|
8
|
+
* config variable in plain text. Anyone who downloads your app can easily
|
|
9
|
+
* extract them.
|
|
10
|
+
*
|
|
11
|
+
* If you doubt this, just bundle your app, and then go look at the bundle and
|
|
12
|
+
* search it for one of your config variable values. You'll find it there.
|
|
13
|
+
*
|
|
14
|
+
* Read more here: https://reactnative.dev/docs/security#storing-sensitive-info
|
|
15
|
+
*/
|
|
16
|
+
import BaseConfig from "./config.base"
|
|
17
|
+
import DevConfig from "./config.dev"
|
|
18
|
+
import ProdConfig from "./config.prod"
|
|
19
|
+
|
|
20
|
+
let ExtraConfig = ProdConfig
|
|
21
|
+
|
|
22
|
+
if (__DEV__) {
|
|
23
|
+
ExtraConfig = DevConfig
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Config = { ...BaseConfig, ...ExtraConfig }
|
|
27
|
+
|
|
28
|
+
export default Config
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AuthContext - Legacy re-export
|
|
3
|
+
* This file maintains backward compatibility by re-exporting from the new modular structure.
|
|
4
|
+
* All authentication logic has been refactored into dedicated modules in the ./auth folder:
|
|
5
|
+
* - types.ts: Type definitions
|
|
6
|
+
* - validation.ts: Validation utilities
|
|
7
|
+
* - services.ts: Parse API calls and auth operations
|
|
8
|
+
* - reducer.ts: State management reducer
|
|
9
|
+
* - AuthProvider.tsx: Provider component
|
|
10
|
+
* - hooks.ts: Custom hooks
|
|
11
|
+
* - index.ts: Main export point
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export * from "./auth";
|