@modhamanish/rn-mm-template 1.0.4 → 1.1.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/MMTemplate/.eslintrc.js +95 -1
- package/MMTemplate/.husky/pre-commit +2 -0
- package/MMTemplate/.prettierrc.js +2 -0
- package/MMTemplate/App.tsx +27 -21
- package/MMTemplate/README.md +76 -35
- package/MMTemplate/__tests__/App.test.tsx +5 -3
- package/MMTemplate/babel.config.js +24 -1
- package/MMTemplate/index.js +1 -0
- package/MMTemplate/lint_errors.txt +7 -0
- package/MMTemplate/package.json +24 -4
- package/MMTemplate/src/components/AnimationView.tsx +54 -69
- package/MMTemplate/src/components/AppText.tsx +7 -4
- package/MMTemplate/src/components/CustomAlert.tsx +203 -0
- package/MMTemplate/src/components/CustomToast.tsx +2 -1
- package/MMTemplate/src/components/ErrorBoundaryFallback.tsx +127 -0
- package/MMTemplate/src/components/FeatureItem.tsx +8 -6
- package/MMTemplate/src/components/FullScreenContainer.tsx +9 -7
- package/MMTemplate/src/components/InfoCard.tsx +8 -6
- package/MMTemplate/src/components/LanguageSwitcher.tsx +8 -6
- package/MMTemplate/src/components/TextInput.tsx +25 -15
- package/MMTemplate/src/components/ThemeSwitcher.tsx +4 -3
- package/MMTemplate/src/context/AuthContext.tsx +5 -4
- package/MMTemplate/src/context/ThemeContext.tsx +10 -19
- package/MMTemplate/src/locales/en.json +4 -1
- package/MMTemplate/src/locales/hi.json +4 -1
- package/MMTemplate/src/navigation/AppNavigator.tsx +7 -8
- package/MMTemplate/src/navigation/AppStack.tsx +6 -6
- package/MMTemplate/src/navigation/AuthCheck.tsx +11 -10
- package/MMTemplate/src/navigation/AuthStack.tsx +6 -4
- package/MMTemplate/src/navigation/MainTab.tsx +19 -13
- package/MMTemplate/src/screens/AddNoteScreen.tsx +20 -14
- package/MMTemplate/src/screens/HomeScreen.tsx +21 -15
- package/MMTemplate/src/screens/LoginScreen.tsx +21 -18
- package/MMTemplate/src/screens/NoteScreen.tsx +18 -16
- package/MMTemplate/src/screens/ProfileScreen.tsx +16 -13
- package/MMTemplate/src/screens/SettingsScreen.tsx +14 -11
- package/MMTemplate/src/screens/WelcomeScreen.tsx +19 -13
- package/MMTemplate/src/services/axiosInstance.ts +7 -5
- package/MMTemplate/src/services/note.query.ts +7 -5
- package/MMTemplate/src/theme/{Colors.ts → colors.ts} +6 -0
- package/MMTemplate/src/types/components.types.ts +38 -0
- package/MMTemplate/src/types/navigation.types.ts +1 -1
- package/MMTemplate/src/utils/i18n.ts +8 -6
- package/MMTemplate/src/utils/navigationUtils.ts +2 -1
- package/MMTemplate/src/utils/storageHelper.ts +8 -8
- package/MMTemplate/src/utils/utilsHelper.ts +1 -0
- package/MMTemplate/tsconfig.json +27 -0
- package/README.md +3 -1
- package/package.json +4 -1
- package/release.js +75 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { memo, useEffect } from 'react';
|
|
1
|
+
import React, { FC, memo, useEffect } from 'react';
|
|
2
|
+
|
|
2
3
|
import Animated, {
|
|
3
4
|
Easing,
|
|
4
5
|
useAnimatedStyle,
|
|
@@ -7,62 +8,49 @@ import Animated, {
|
|
|
7
8
|
withTiming,
|
|
8
9
|
} from 'react-native-reanimated';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
import { AnimationViewProps } from '@src/types/components.types';
|
|
12
|
+
|
|
13
|
+
const AnimationView: FC<AnimationViewProps> = ({
|
|
11
14
|
children,
|
|
12
15
|
animType,
|
|
13
16
|
duration = 500,
|
|
14
17
|
delay = 0,
|
|
15
18
|
rotateValue = 360,
|
|
16
19
|
style,
|
|
17
|
-
}: {
|
|
18
|
-
children: React.ReactNode;
|
|
19
|
-
animType:
|
|
20
|
-
| 'FadeIn'
|
|
21
|
-
| 'FadeOut'
|
|
22
|
-
| 'ZoomIn'
|
|
23
|
-
| 'ZoomOut'
|
|
24
|
-
| 'RotateIn'
|
|
25
|
-
| 'RotateOut'
|
|
26
|
-
| 'SlideInDown';
|
|
27
|
-
duration?: number;
|
|
28
|
-
delay?: number;
|
|
29
|
-
rotateValue?: number;
|
|
30
|
-
style?: any;
|
|
31
20
|
}) => {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
const getInitialValue = () => {
|
|
22
|
+
switch (animType) {
|
|
23
|
+
case 'SlideInDown':
|
|
24
|
+
return -100;
|
|
25
|
+
case 'FadeOut':
|
|
26
|
+
case 'ZoomOut':
|
|
27
|
+
return 1;
|
|
28
|
+
default:
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const animValue = useSharedValue(getInitialValue());
|
|
36
33
|
|
|
37
34
|
const animStyle = useAnimatedStyle(() => {
|
|
38
35
|
switch (animType) {
|
|
39
36
|
case 'FadeIn':
|
|
40
|
-
return {
|
|
41
|
-
opacity: fadeAnim.value,
|
|
42
|
-
};
|
|
43
37
|
case 'FadeOut':
|
|
44
38
|
return {
|
|
45
|
-
opacity:
|
|
39
|
+
opacity: animValue.value,
|
|
46
40
|
};
|
|
47
41
|
case 'ZoomIn':
|
|
48
|
-
return {
|
|
49
|
-
transform: [{ scale: zoomAnim.value }],
|
|
50
|
-
};
|
|
51
42
|
case 'ZoomOut':
|
|
52
43
|
return {
|
|
53
|
-
transform: [{ scale:
|
|
44
|
+
transform: [{ scale: animValue.value }],
|
|
54
45
|
};
|
|
55
46
|
case 'RotateIn':
|
|
56
|
-
return {
|
|
57
|
-
transform: [{ rotate: rotateAnim.value + 'deg' }],
|
|
58
|
-
};
|
|
59
47
|
case 'RotateOut':
|
|
60
48
|
return {
|
|
61
|
-
transform: [{ rotate:
|
|
49
|
+
transform: [{ rotate: animValue.value + 'deg' }],
|
|
62
50
|
};
|
|
63
51
|
case 'SlideInDown':
|
|
64
52
|
return {
|
|
65
|
-
transform: [{ translateY:
|
|
53
|
+
transform: [{ translateY: animValue.value }],
|
|
66
54
|
};
|
|
67
55
|
default:
|
|
68
56
|
return {};
|
|
@@ -70,43 +58,40 @@ const AnimationView = ({
|
|
|
70
58
|
});
|
|
71
59
|
|
|
72
60
|
useEffect(() => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
easing: Easing.elastic(1),
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
} else if (animType === 'SlideInDown') {
|
|
104
|
-
translateAnim.value = withDelay(
|
|
105
|
-
delay,
|
|
106
|
-
withTiming(0, { duration: duration, easing: Easing.elastic(1) }),
|
|
107
|
-
);
|
|
61
|
+
let targetValue = 0;
|
|
62
|
+
let easing = Easing.inOut(Easing.ease);
|
|
63
|
+
|
|
64
|
+
switch (animType) {
|
|
65
|
+
case 'FadeIn':
|
|
66
|
+
targetValue = 1;
|
|
67
|
+
break;
|
|
68
|
+
case 'FadeOut':
|
|
69
|
+
targetValue = 0;
|
|
70
|
+
break;
|
|
71
|
+
case 'ZoomIn':
|
|
72
|
+
targetValue = 1;
|
|
73
|
+
easing = Easing.elastic(1);
|
|
74
|
+
break;
|
|
75
|
+
case 'ZoomOut':
|
|
76
|
+
targetValue = 0;
|
|
77
|
+
easing = Easing.elastic(1);
|
|
78
|
+
break;
|
|
79
|
+
case 'RotateIn':
|
|
80
|
+
case 'RotateOut':
|
|
81
|
+
targetValue = rotateValue;
|
|
82
|
+
easing = Easing.elastic(1);
|
|
83
|
+
break;
|
|
84
|
+
case 'SlideInDown':
|
|
85
|
+
targetValue = 0;
|
|
86
|
+
easing = Easing.elastic(1);
|
|
87
|
+
break;
|
|
108
88
|
}
|
|
109
|
-
|
|
89
|
+
|
|
90
|
+
animValue.value = withDelay(
|
|
91
|
+
delay,
|
|
92
|
+
withTiming(targetValue, { duration, easing }),
|
|
93
|
+
);
|
|
94
|
+
}, [animType, duration, delay, rotateValue, animValue]);
|
|
110
95
|
return <Animated.View style={[animStyle, style]}>{children}</Animated.View>;
|
|
111
96
|
};
|
|
112
97
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { FC, memo } from 'react';
|
|
2
2
|
import { Text, TextStyle } from 'react-native';
|
|
3
|
-
|
|
4
|
-
import { AppTextProps, AppTextSize } from '
|
|
3
|
+
|
|
4
|
+
import { AppTextProps, AppTextSize } from '@app-types/components.types';
|
|
5
|
+
import { useTheme } from '@context/ThemeContext';
|
|
5
6
|
|
|
6
7
|
const FONTS: Record<string, string> | undefined = {
|
|
7
8
|
// Define custom font families here. If empty or undefined, the component will use default system fonts with font weights.
|
|
@@ -41,7 +42,9 @@ const AppText: FC<AppTextProps> = ({
|
|
|
41
42
|
return SIZES[s as string] || SIZES.normal;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
const getTransform = (
|
|
45
|
+
const getTransform = (
|
|
46
|
+
t?: string,
|
|
47
|
+
): 'uppercase' | 'lowercase' | 'capitalize' | undefined => {
|
|
45
48
|
if (!t) return undefined;
|
|
46
49
|
switch (t) {
|
|
47
50
|
case 'capital':
|
|
@@ -54,7 +57,7 @@ const AppText: FC<AppTextProps> = ({
|
|
|
54
57
|
case 'capitalize':
|
|
55
58
|
return 'capitalize';
|
|
56
59
|
default:
|
|
57
|
-
return
|
|
60
|
+
return undefined;
|
|
58
61
|
}
|
|
59
62
|
};
|
|
60
63
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, { FC, memo, useState, useMemo } from 'react';
|
|
2
|
+
import { Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import AppText from '@components/AppText';
|
|
7
|
+
import { useTheme } from '@src/context/ThemeContext';
|
|
8
|
+
import { ThemeType } from '@src/theme/colors';
|
|
9
|
+
import { AlertOptionsProps } from '@src/types/components.types';
|
|
10
|
+
import { hexWithOpacity } from '@src/utils/utilsHelper';
|
|
11
|
+
|
|
12
|
+
let externalShow: ((opts: AlertOptionsProps) => void) | null = null;
|
|
13
|
+
|
|
14
|
+
const CustomAlert: FC = () => {
|
|
15
|
+
const [stack, setStack] = useState<AlertOptionsProps[]>([]);
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
const { colors } = theme;
|
|
19
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
20
|
+
|
|
21
|
+
const show = (opts: AlertOptionsProps) => {
|
|
22
|
+
const item: AlertOptionsProps = {
|
|
23
|
+
title: opts.title || '',
|
|
24
|
+
message: opts.message || '',
|
|
25
|
+
onConfirm: opts.onConfirm,
|
|
26
|
+
onCancel: opts.onCancel,
|
|
27
|
+
onClose: opts.onClose,
|
|
28
|
+
confirmText: opts.confirmText || t('common.ok'),
|
|
29
|
+
cancelText: opts.cancelText || t('common.cancel'),
|
|
30
|
+
boxStyle: opts.boxStyle,
|
|
31
|
+
titleStyle: opts.titleStyle,
|
|
32
|
+
messageStyle: opts.messageStyle,
|
|
33
|
+
confirmButtonStyle: opts.confirmButtonStyle,
|
|
34
|
+
cancelButtonStyle: opts.cancelButtonStyle,
|
|
35
|
+
confirmTextStyle: opts.confirmTextStyle,
|
|
36
|
+
cancelTextStyle: opts.cancelTextStyle,
|
|
37
|
+
isCancellable: opts.isCancellable || false,
|
|
38
|
+
isError: opts.isError || false,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
setStack(prev => [...prev, item]);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const hideTop = () => {
|
|
45
|
+
setStack(prev => prev.slice(0, -1));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
externalShow = show;
|
|
49
|
+
|
|
50
|
+
if (stack.length === 0) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const item = stack[stack.length - 1];
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Modal visible transparent animationType="fade">
|
|
58
|
+
<TouchableOpacity
|
|
59
|
+
style={styles.overlay}
|
|
60
|
+
activeOpacity={1}
|
|
61
|
+
onPress={() => {
|
|
62
|
+
if (item.isCancellable || (!item.onCancel && !item.onConfirm)) {
|
|
63
|
+
hideTop();
|
|
64
|
+
item.onClose && item.onClose();
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<TouchableOpacity
|
|
69
|
+
activeOpacity={1}
|
|
70
|
+
onPress={() => {}}
|
|
71
|
+
style={[styles.box, item.boxStyle]}
|
|
72
|
+
>
|
|
73
|
+
{!!item.title && (
|
|
74
|
+
<AppText
|
|
75
|
+
variant="semiBold"
|
|
76
|
+
size="xlarge"
|
|
77
|
+
style={[
|
|
78
|
+
styles.title,
|
|
79
|
+
{ color: item.isError ? colors.error : colors.primary },
|
|
80
|
+
item.titleStyle,
|
|
81
|
+
]}
|
|
82
|
+
>
|
|
83
|
+
{item.title}
|
|
84
|
+
</AppText>
|
|
85
|
+
)}
|
|
86
|
+
{!!item.message && (
|
|
87
|
+
<AppText
|
|
88
|
+
variant="regular"
|
|
89
|
+
size="normal"
|
|
90
|
+
color={colors.textColor}
|
|
91
|
+
style={[styles.message, item.messageStyle]}
|
|
92
|
+
>
|
|
93
|
+
{item.message}
|
|
94
|
+
</AppText>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
{item.onCancel || item.onConfirm ? (
|
|
98
|
+
<View style={styles.btnRow}>
|
|
99
|
+
{Boolean(item.onCancel || item.isCancellable) && (
|
|
100
|
+
<TouchableOpacity
|
|
101
|
+
style={[
|
|
102
|
+
styles.btn,
|
|
103
|
+
{ backgroundColor: colors.backgroundColor },
|
|
104
|
+
item.cancelButtonStyle,
|
|
105
|
+
]}
|
|
106
|
+
onPress={() => {
|
|
107
|
+
hideTop();
|
|
108
|
+
item.onCancel && item.onCancel();
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<AppText
|
|
112
|
+
variant="medium"
|
|
113
|
+
color={colors.textColor}
|
|
114
|
+
style={item.cancelTextStyle}
|
|
115
|
+
>
|
|
116
|
+
{item.cancelText}
|
|
117
|
+
</AppText>
|
|
118
|
+
</TouchableOpacity>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{item.onConfirm && (
|
|
122
|
+
<TouchableOpacity
|
|
123
|
+
style={[
|
|
124
|
+
styles.btn,
|
|
125
|
+
{
|
|
126
|
+
backgroundColor: item.isError
|
|
127
|
+
? colors.error
|
|
128
|
+
: colors.primary,
|
|
129
|
+
},
|
|
130
|
+
item.confirmButtonStyle,
|
|
131
|
+
]}
|
|
132
|
+
onPress={() => {
|
|
133
|
+
hideTop();
|
|
134
|
+
item.onConfirm && item.onConfirm();
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<AppText
|
|
138
|
+
variant="medium"
|
|
139
|
+
color={colors.white}
|
|
140
|
+
style={item.confirmTextStyle}
|
|
141
|
+
>
|
|
142
|
+
{item.confirmText}
|
|
143
|
+
</AppText>
|
|
144
|
+
</TouchableOpacity>
|
|
145
|
+
)}
|
|
146
|
+
</View>
|
|
147
|
+
) : null}
|
|
148
|
+
</TouchableOpacity>
|
|
149
|
+
</TouchableOpacity>
|
|
150
|
+
</Modal>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const MemoAlert = memo(CustomAlert);
|
|
155
|
+
|
|
156
|
+
(
|
|
157
|
+
MemoAlert as typeof MemoAlert & { show: (opts: AlertOptionsProps) => void }
|
|
158
|
+
).show = (opts: AlertOptionsProps) => externalShow && externalShow(opts);
|
|
159
|
+
|
|
160
|
+
export default MemoAlert as typeof MemoAlert & {
|
|
161
|
+
show: (opts: AlertOptionsProps) => void;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const getStyles = ({ colors }: ThemeType) =>
|
|
165
|
+
StyleSheet.create({
|
|
166
|
+
overlay: {
|
|
167
|
+
flex: 1,
|
|
168
|
+
backgroundColor: hexWithOpacity(colors.black, 50),
|
|
169
|
+
justifyContent: 'center',
|
|
170
|
+
},
|
|
171
|
+
box: {
|
|
172
|
+
margin: 24,
|
|
173
|
+
padding: 24,
|
|
174
|
+
borderRadius: 16,
|
|
175
|
+
borderWidth: 1,
|
|
176
|
+
backgroundColor: colors.backgroundColor,
|
|
177
|
+
borderColor: colors.borderColor,
|
|
178
|
+
shadowColor: colors.textColor,
|
|
179
|
+
shadowOffset: { width: 0, height: 4 },
|
|
180
|
+
shadowOpacity: 0.1,
|
|
181
|
+
shadowRadius: 12,
|
|
182
|
+
elevation: 5,
|
|
183
|
+
},
|
|
184
|
+
title: {
|
|
185
|
+
marginBottom: 12,
|
|
186
|
+
},
|
|
187
|
+
message: {
|
|
188
|
+
lineHeight: 20,
|
|
189
|
+
},
|
|
190
|
+
btnRow: {
|
|
191
|
+
marginTop: 24,
|
|
192
|
+
flexDirection: 'row',
|
|
193
|
+
justifyContent: 'flex-end',
|
|
194
|
+
},
|
|
195
|
+
btn: {
|
|
196
|
+
paddingVertical: 10,
|
|
197
|
+
paddingHorizontal: 20,
|
|
198
|
+
marginLeft: 12,
|
|
199
|
+
borderRadius: 12,
|
|
200
|
+
minWidth: 80,
|
|
201
|
+
alignItems: 'center',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { StyleSheet, View, TouchableOpacity, ScrollView } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import AppText from '@components/AppText';
|
|
7
|
+
import FullScreenContainer from '@components/FullScreenContainer';
|
|
8
|
+
import { useTheme } from '@context/ThemeContext';
|
|
9
|
+
import { ThemeType } from '@src/theme/colors';
|
|
10
|
+
import { hexWithOpacity } from '@utils/utilsHelper';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
error: Error;
|
|
14
|
+
resetError: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ErrorBoundaryFallback = (props: Props) => {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
const theme = useTheme();
|
|
20
|
+
const { colors } = theme;
|
|
21
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<FullScreenContainer style={styles.container}>
|
|
25
|
+
<View style={styles.content}>
|
|
26
|
+
<View style={styles.iconContainer}>
|
|
27
|
+
<AppText style={styles.icon}>⚠️</AppText>
|
|
28
|
+
</View>
|
|
29
|
+
|
|
30
|
+
<AppText variant="h1" style={styles.title}>
|
|
31
|
+
{t('common.errorOccurred')}
|
|
32
|
+
</AppText>
|
|
33
|
+
|
|
34
|
+
<AppText variant="regular" style={styles.subtitle}>
|
|
35
|
+
{t('common.errorDescription')}
|
|
36
|
+
</AppText>
|
|
37
|
+
|
|
38
|
+
<View style={styles.errorBox}>
|
|
39
|
+
<ScrollView bounces={false}>
|
|
40
|
+
<AppText
|
|
41
|
+
size="small"
|
|
42
|
+
color={colors.primary}
|
|
43
|
+
style={styles.errorText}
|
|
44
|
+
>
|
|
45
|
+
{props.error.toString()}
|
|
46
|
+
</AppText>
|
|
47
|
+
</ScrollView>
|
|
48
|
+
</View>
|
|
49
|
+
|
|
50
|
+
<TouchableOpacity
|
|
51
|
+
style={styles.button}
|
|
52
|
+
onPress={props.resetError}
|
|
53
|
+
activeOpacity={0.8}
|
|
54
|
+
>
|
|
55
|
+
<AppText variant="semiBold" color={colors.white}>
|
|
56
|
+
{t('common.tryAgain')}
|
|
57
|
+
</AppText>
|
|
58
|
+
</TouchableOpacity>
|
|
59
|
+
</View>
|
|
60
|
+
</FullScreenContainer>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const getStyles = (theme: ThemeType) => {
|
|
65
|
+
const { colors } = theme;
|
|
66
|
+
return StyleSheet.create({
|
|
67
|
+
container: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
justifyContent: 'center',
|
|
70
|
+
padding: 24,
|
|
71
|
+
},
|
|
72
|
+
content: {
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
},
|
|
75
|
+
iconContainer: {
|
|
76
|
+
width: 80,
|
|
77
|
+
height: 80,
|
|
78
|
+
borderRadius: 40,
|
|
79
|
+
justifyContent: 'center',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
marginBottom: 24,
|
|
82
|
+
backgroundColor: hexWithOpacity(colors.primary, 15),
|
|
83
|
+
},
|
|
84
|
+
icon: {
|
|
85
|
+
fontSize: 40,
|
|
86
|
+
},
|
|
87
|
+
title: {
|
|
88
|
+
textAlign: 'center',
|
|
89
|
+
marginBottom: 12,
|
|
90
|
+
},
|
|
91
|
+
subtitle: {
|
|
92
|
+
textAlign: 'center',
|
|
93
|
+
marginBottom: 32,
|
|
94
|
+
lineHeight: 22,
|
|
95
|
+
color: hexWithOpacity(colors.textColor, 80),
|
|
96
|
+
},
|
|
97
|
+
errorBox: {
|
|
98
|
+
width: '100%',
|
|
99
|
+
maxHeight: 200,
|
|
100
|
+
borderRadius: 12,
|
|
101
|
+
borderWidth: 1,
|
|
102
|
+
padding: 16,
|
|
103
|
+
marginBottom: 32,
|
|
104
|
+
backgroundColor: colors.backgroundColor,
|
|
105
|
+
borderColor: colors.borderColor,
|
|
106
|
+
},
|
|
107
|
+
errorText: {
|
|
108
|
+
lineHeight: 18,
|
|
109
|
+
fontFamily: 'monospace',
|
|
110
|
+
},
|
|
111
|
+
button: {
|
|
112
|
+
width: '100%',
|
|
113
|
+
height: 56,
|
|
114
|
+
borderRadius: 16,
|
|
115
|
+
justifyContent: 'center',
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
shadowColor: colors.black,
|
|
118
|
+
shadowOffset: { width: 0, height: 4 },
|
|
119
|
+
shadowOpacity: 0.1,
|
|
120
|
+
shadowRadius: 8,
|
|
121
|
+
elevation: 4,
|
|
122
|
+
backgroundColor: colors.primary,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export default ErrorBoundaryFallback;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
|
|
4
|
+
import AppText from '@components/AppText';
|
|
5
|
+
import { useTheme } from '@context/ThemeContext';
|
|
6
|
+
import { ThemeType } from '@src/theme/colors';
|
|
7
|
+
import { hexWithOpacity } from '@utils/utilsHelper';
|
|
6
8
|
|
|
7
9
|
interface FeatureItemProps {
|
|
8
10
|
icon: string;
|
|
@@ -16,7 +18,7 @@ const FeatureItem: React.FC<FeatureItemProps> = ({
|
|
|
16
18
|
description,
|
|
17
19
|
}) => {
|
|
18
20
|
const theme = useTheme();
|
|
19
|
-
const styles = getStyles(theme);
|
|
21
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
20
22
|
|
|
21
23
|
return (
|
|
22
24
|
<View style={styles.container}>
|
|
@@ -56,7 +58,7 @@ const getStyles = ({ colors }: ThemeType) =>
|
|
|
56
58
|
marginBottom: 2,
|
|
57
59
|
},
|
|
58
60
|
description: {
|
|
59
|
-
color: colors.textColor
|
|
61
|
+
color: hexWithOpacity(colors.textColor, 80),
|
|
60
62
|
lineHeight: 18,
|
|
61
63
|
},
|
|
62
64
|
});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import React, { FC, memo, useMemo } from 'react';
|
|
1
2
|
import { StatusBar, StyleSheet } from 'react-native';
|
|
2
|
-
|
|
3
|
-
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
3
|
+
|
|
4
4
|
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import {
|
|
5
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
6
|
+
|
|
7
|
+
import { FullScreenContainerProps } from '@app-types/components.types';
|
|
8
|
+
import { useTheme } from '@context/ThemeContext';
|
|
9
|
+
import { ThemeType } from '@src/theme/colors';
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
const FullScreenContainer: FC<FullScreenContainerProps> = ({
|
|
10
12
|
children,
|
|
11
13
|
style,
|
|
12
14
|
edges = ['top'],
|
|
@@ -16,7 +18,7 @@ export const FullScreenContainer: FC<FullScreenContainerProps> = ({
|
|
|
16
18
|
isKeyboardAvoidingView = false,
|
|
17
19
|
}) => {
|
|
18
20
|
const theme = useTheme();
|
|
19
|
-
const styles = getStyles(theme);
|
|
21
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
20
22
|
|
|
21
23
|
const appBarStyle =
|
|
22
24
|
barStyle ??
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
|
|
4
|
+
import AppText from '@components/AppText';
|
|
5
|
+
import { useTheme } from '@context/ThemeContext';
|
|
6
|
+
import { ThemeType } from '@src/theme/colors';
|
|
7
|
+
import { hexWithOpacity } from '@utils/utilsHelper';
|
|
6
8
|
|
|
7
9
|
interface InfoCardProps {
|
|
8
10
|
title: string;
|
|
@@ -12,7 +14,7 @@ interface InfoCardProps {
|
|
|
12
14
|
|
|
13
15
|
const InfoCard: React.FC<InfoCardProps> = ({ title, children, icon }) => {
|
|
14
16
|
const theme = useTheme();
|
|
15
|
-
const styles = getStyles(theme);
|
|
17
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
20
|
<View style={styles.card}>
|
|
@@ -41,7 +43,7 @@ const getStyles = ({ colors }: ThemeType) =>
|
|
|
41
43
|
padding: 16,
|
|
42
44
|
marginBottom: 16,
|
|
43
45
|
borderWidth: 1,
|
|
44
|
-
borderColor: colors.primary
|
|
46
|
+
borderColor: hexWithOpacity(colors.primary, 12),
|
|
45
47
|
shadowColor: colors.textColor,
|
|
46
48
|
shadowOffset: { width: 0, height: 2 },
|
|
47
49
|
shadowOpacity: 0.1,
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
|
|
6
|
+
import AppText from '@components/AppText';
|
|
7
|
+
import { useTheme } from '@context/ThemeContext';
|
|
8
|
+
import { ThemeType } from '@src/theme/colors';
|
|
9
|
+
import StorageHelper from '@utils/storageHelper';
|
|
8
10
|
|
|
9
11
|
const LanguageSwitcher = () => {
|
|
10
12
|
const { i18n } = useTranslation();
|
|
11
13
|
const theme = useTheme();
|
|
12
|
-
const styles = getStyles(theme);
|
|
14
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
13
15
|
|
|
14
16
|
const changeLanguage = (lng: string) => {
|
|
15
17
|
i18n.changeLanguage(lng);
|