@momo-kits/foundation 0.110.1-beta.7 → 0.110.1-optimize.1
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/Application/BottomSheet.tsx +14 -4
- package/Application/Components/BottomSheetHelpCenter.tsx +43 -0
- package/Application/Components/HeaderBackground.tsx +11 -4
- package/Application/Components/HeaderExtendHeader.tsx +20 -4
- package/Application/Components/HeaderLeft.tsx +11 -12
- package/Application/Components/HeaderRight.tsx +58 -12
- package/Application/Components/HeaderTitle.tsx +291 -29
- package/Application/Components/SearchHeader.tsx +5 -10
- package/Application/NavigationContainer.tsx +58 -13
- package/Application/StackScreen.tsx +26 -40
- package/Application/types.ts +7 -1
- package/Application/utils.tsx +2 -0
- package/Button/index.tsx +0 -2
- package/Input/Input.tsx +0 -2
- package/Input/InputDropDown.tsx +0 -2
- package/Input/InputMoney.tsx +0 -2
- package/Input/InputOTP.tsx +0 -2
- package/Input/InputSearch.tsx +0 -2
- package/Title/index.tsx +18 -4
- package/Title/types.ts +6 -0
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ import {Icon} from '../Icon';
|
|
|
20
20
|
const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
21
21
|
const {theme, navigator} = useContext(ApplicationContext);
|
|
22
22
|
const heightDevice = Dimensions.get('screen').height;
|
|
23
|
+
const action = useRef<undefined | string>();
|
|
23
24
|
const insets = useSafeAreaInsets();
|
|
24
25
|
const {
|
|
25
26
|
screen: Screen,
|
|
@@ -53,6 +54,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
53
54
|
},
|
|
54
55
|
onPanResponderRelease: (_, gestureState) => {
|
|
55
56
|
if (gestureState.dy > 100) {
|
|
57
|
+
action.current = 'gesture';
|
|
56
58
|
onDismiss();
|
|
57
59
|
} else {
|
|
58
60
|
Animated.spring(pan, {
|
|
@@ -82,7 +84,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
82
84
|
duration: 150,
|
|
83
85
|
}).start();
|
|
84
86
|
return () => {
|
|
85
|
-
props.route.params?.onDismiss?.();
|
|
87
|
+
props.route.params?.onDismiss?.(action.current);
|
|
86
88
|
};
|
|
87
89
|
}, []);
|
|
88
90
|
|
|
@@ -150,7 +152,10 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
150
152
|
)}
|
|
151
153
|
<TouchableOpacity
|
|
152
154
|
style={styles.iconButton}
|
|
153
|
-
onPress={() =>
|
|
155
|
+
onPress={() => {
|
|
156
|
+
action.current = 'icon_close';
|
|
157
|
+
onDismiss(true);
|
|
158
|
+
}}>
|
|
154
159
|
<Icon source="navigation_close" />
|
|
155
160
|
</TouchableOpacity>
|
|
156
161
|
</View>
|
|
@@ -162,7 +167,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
162
167
|
<Container
|
|
163
168
|
transparent
|
|
164
169
|
visible={true}
|
|
165
|
-
onRequestClose={() =>
|
|
170
|
+
onRequestClose={() => {
|
|
171
|
+
onDismiss();
|
|
172
|
+
}}
|
|
166
173
|
style={styles.overlay}>
|
|
167
174
|
<KeyboardAvoidingView
|
|
168
175
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
@@ -171,7 +178,10 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
171
178
|
<TouchableOpacity
|
|
172
179
|
style={Styles.flex}
|
|
173
180
|
activeOpacity={1}
|
|
174
|
-
onPress={() =>
|
|
181
|
+
onPress={() => {
|
|
182
|
+
action.current = 'touch';
|
|
183
|
+
onDismiss();
|
|
184
|
+
}}
|
|
175
185
|
/>
|
|
176
186
|
<Animated.View style={{transform: pan.getTranslateTransform()}}>
|
|
177
187
|
{renderHeader()}
|
|
@@ -10,6 +10,7 @@ const ServiceItem: React.FC<any> = ({service}) => {
|
|
|
10
10
|
const {title, description, icon, onPress} = service;
|
|
11
11
|
const serviceTitle = translate?.(title);
|
|
12
12
|
const serviceDescription = translate?.(description);
|
|
13
|
+
|
|
13
14
|
return (
|
|
14
15
|
<TouchableOpacity onPress={onPress} style={Styles.row}>
|
|
15
16
|
<View style={styles.iconWrapper}>
|
|
@@ -35,6 +36,20 @@ const BottomSheetHelpCenter: React.FC<any> = ({onRequestClose}) => {
|
|
|
35
36
|
const context = useContext<any>(MiniAppContext);
|
|
36
37
|
|
|
37
38
|
const onPressFaq = () => {
|
|
39
|
+
const routes = navigator?.ref.current?.getRootState()?.routes || [];
|
|
40
|
+
const routesLength = routes.length;
|
|
41
|
+
let screenName = routes?.[0]?.params?.screen?.name;
|
|
42
|
+
if (routesLength > 1) {
|
|
43
|
+
screenName = routes[routesLength - 2]?.params?.screen?.name;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
context?.autoTracking?.({
|
|
47
|
+
...context,
|
|
48
|
+
componentName: 'TouchableOpacity',
|
|
49
|
+
componentId: 'bottomsheet_faq_item',
|
|
50
|
+
screenName: screenName,
|
|
51
|
+
});
|
|
52
|
+
|
|
38
53
|
onRequestClose?.(() => {
|
|
39
54
|
navigator?.maxApi?.startFeatureCode?.(
|
|
40
55
|
'helpcenter_problemlevel1',
|
|
@@ -44,6 +59,20 @@ const BottomSheetHelpCenter: React.FC<any> = ({onRequestClose}) => {
|
|
|
44
59
|
};
|
|
45
60
|
|
|
46
61
|
const onPressChatbot = () => {
|
|
62
|
+
const routes = navigator?.ref.current?.getRootState()?.routes || [];
|
|
63
|
+
const routesLength = routes.length;
|
|
64
|
+
let screenName = routes?.[0]?.params?.screen?.name;
|
|
65
|
+
if (routesLength > 1) {
|
|
66
|
+
screenName = routes[routesLength - 2]?.params?.screen?.name;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
context?.autoTracking?.({
|
|
70
|
+
...context,
|
|
71
|
+
componentName: 'TouchableOpacity',
|
|
72
|
+
componentId: 'bottomsheet_chatbot_item',
|
|
73
|
+
screenName: screenName,
|
|
74
|
+
});
|
|
75
|
+
|
|
47
76
|
onRequestClose?.(() => {
|
|
48
77
|
navigator?.maxApi?.getDataObserver('CURRENT_SCREEN', (data: any) => {
|
|
49
78
|
let screenName = data?.screenName;
|
|
@@ -61,6 +90,20 @@ const BottomSheetHelpCenter: React.FC<any> = ({onRequestClose}) => {
|
|
|
61
90
|
};
|
|
62
91
|
|
|
63
92
|
const onPressFeedback = () => {
|
|
93
|
+
const routes = navigator?.ref.current?.getRootState()?.routes || [];
|
|
94
|
+
const routesLength = routes.length;
|
|
95
|
+
let screenName = routes?.[0]?.params?.screen?.name;
|
|
96
|
+
if (routesLength > 1) {
|
|
97
|
+
screenName = routes[routesLength - 2]?.params?.screen?.name;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
context?.autoTracking?.({
|
|
101
|
+
...context,
|
|
102
|
+
componentName: 'TouchableOpacity',
|
|
103
|
+
componentId: 'bottomsheet_feedback_item',
|
|
104
|
+
screenName: screenName,
|
|
105
|
+
});
|
|
106
|
+
|
|
64
107
|
onRequestClose?.(() => {
|
|
65
108
|
navigator?.maxApi?.startFeatureCode?.('feedback', {
|
|
66
109
|
forService: 'navigation',
|
|
@@ -48,9 +48,17 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
|
48
48
|
{
|
|
49
49
|
backgroundColor: theme.colors.background.surface,
|
|
50
50
|
opacity: opacityBackground,
|
|
51
|
+
overflow: 'hidden',
|
|
51
52
|
},
|
|
52
|
-
]}
|
|
53
|
-
|
|
53
|
+
]}>
|
|
54
|
+
{theme?.assets?.headerBackground && (
|
|
55
|
+
<Image
|
|
56
|
+
style={styles.headerBackground}
|
|
57
|
+
source={{uri: theme?.assets?.headerBackground}}
|
|
58
|
+
loading={false}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
</Animated.View>
|
|
54
62
|
<View style={styles.gradientContainer}>
|
|
55
63
|
{useGradient && (
|
|
56
64
|
<LinearGradientAnimated
|
|
@@ -107,9 +115,8 @@ const styles = StyleSheet.create({
|
|
|
107
115
|
},
|
|
108
116
|
headerBackground: {
|
|
109
117
|
width: '100%',
|
|
110
|
-
height: undefined,
|
|
111
118
|
position: 'absolute',
|
|
112
|
-
aspectRatio: 375 /
|
|
119
|
+
aspectRatio: 375 / 152,
|
|
113
120
|
},
|
|
114
121
|
dividerHeader: {
|
|
115
122
|
borderBottomWidth: 1,
|
|
@@ -105,9 +105,17 @@ const HeaderExtendHeader: React.FC<{
|
|
|
105
105
|
width: '100%',
|
|
106
106
|
height: heightHeader,
|
|
107
107
|
zIndex: 1,
|
|
108
|
+
overflow: 'hidden',
|
|
108
109
|
},
|
|
109
|
-
]}
|
|
110
|
-
|
|
110
|
+
]}>
|
|
111
|
+
{headerBackground && (
|
|
112
|
+
<Image
|
|
113
|
+
style={styles.headerBackground}
|
|
114
|
+
source={{uri: headerBackground}}
|
|
115
|
+
loading={false}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
118
|
+
</Animated.View>
|
|
111
119
|
|
|
112
120
|
<Animated.View
|
|
113
121
|
style={[
|
|
@@ -175,9 +183,17 @@ const HeaderExtendHeader: React.FC<{
|
|
|
175
183
|
height: heightHeader,
|
|
176
184
|
backgroundColor: theme.colors.background.surface,
|
|
177
185
|
opacity: opacityBackground,
|
|
186
|
+
overflow: 'hidden',
|
|
178
187
|
},
|
|
179
|
-
]}
|
|
180
|
-
|
|
188
|
+
]}>
|
|
189
|
+
{headerBackground && (
|
|
190
|
+
<Image
|
|
191
|
+
style={styles.headerBackground}
|
|
192
|
+
source={{uri: headerBackground}}
|
|
193
|
+
loading={false}
|
|
194
|
+
/>
|
|
195
|
+
)}
|
|
196
|
+
</Animated.View>
|
|
181
197
|
<LinearGradientAnimated
|
|
182
198
|
colors={[gradientColor, gradientColor + '00']}
|
|
183
199
|
style={[styles.extendedHeader, {opacity: opacityGradient}]}>
|
|
@@ -12,6 +12,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
12
12
|
tintColor,
|
|
13
13
|
preventBack,
|
|
14
14
|
onPressLeftHeader,
|
|
15
|
+
onBackHandler,
|
|
15
16
|
}) => {
|
|
16
17
|
const context = useContext<any>(MiniAppContext);
|
|
17
18
|
const {navigator} = useContext(ApplicationContext);
|
|
@@ -29,18 +30,12 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
29
30
|
const goBack = () => {
|
|
30
31
|
const canGoBack = navigator?.ref?.current?.canGoBack?.();
|
|
31
32
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
32
|
-
|
|
33
|
+
context?.autoTracking?.({
|
|
33
34
|
...context,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
navigator?.maxApi?.showToastDebug?.({
|
|
39
|
-
appId: `auto - ${context.appId}`,
|
|
40
|
-
message: 'service_icon_clicked',
|
|
41
|
-
params,
|
|
35
|
+
componentName: 'IconButton',
|
|
36
|
+
componentId: 'navigation_back',
|
|
37
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
42
38
|
});
|
|
43
|
-
navigator?.maxApi?.trackEvent?.('service_icon_clicked', params);
|
|
44
39
|
|
|
45
40
|
if (canGoBack) {
|
|
46
41
|
navigator?.ref?.current?.goBack?.();
|
|
@@ -49,7 +44,6 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
49
44
|
} else {
|
|
50
45
|
(global as any)?.miniAppApi?.dispatch?.('dismiss', context);
|
|
51
46
|
}
|
|
52
|
-
onPressLeftHeader?.();
|
|
53
47
|
};
|
|
54
48
|
|
|
55
49
|
if (preventBack) {
|
|
@@ -68,7 +62,12 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
68
62
|
),
|
|
69
63
|
});
|
|
70
64
|
} else {
|
|
71
|
-
|
|
65
|
+
onPressLeftHeader?.();
|
|
66
|
+
if (typeof onBackHandler === 'function') {
|
|
67
|
+
onBackHandler(goBack);
|
|
68
|
+
} else {
|
|
69
|
+
goBack();
|
|
70
|
+
}
|
|
72
71
|
}
|
|
73
72
|
return true;
|
|
74
73
|
};
|
|
@@ -15,6 +15,7 @@ import {scaleSize, Text} from '../../Text';
|
|
|
15
15
|
import {BottomSheetHelpCenter} from './BottomSheetHelpCenter';
|
|
16
16
|
import {Image} from '../../Image';
|
|
17
17
|
|
|
18
|
+
const DID_SYNC_NEW_HOME = 'did_sync_new_home';
|
|
18
19
|
/**
|
|
19
20
|
* main component for header right
|
|
20
21
|
*/
|
|
@@ -64,11 +65,22 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
64
65
|
useEffect(() => {
|
|
65
66
|
if (useShortcut) {
|
|
66
67
|
checkAppIsFavorite();
|
|
68
|
+
|
|
69
|
+
const favoriteObserver = navigator?.maxApi?.observer?.(
|
|
70
|
+
DID_SYNC_NEW_HOME,
|
|
71
|
+
(updatedData: any) => {
|
|
72
|
+
const status = updatedData?.items?.includes(context?.code);
|
|
73
|
+
setIsFavorite(status);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
return () => {
|
|
77
|
+
favoriteObserver?.remove?.();
|
|
78
|
+
};
|
|
67
79
|
}
|
|
68
|
-
}, [useShortcut,
|
|
80
|
+
}, [useShortcut, context]);
|
|
69
81
|
|
|
70
82
|
let buttonStyle: ViewStyle = {};
|
|
71
|
-
if (tintColor === Colors.black_01) {
|
|
83
|
+
if (tintColor === Colors.black_01 || tintColor === 'white') {
|
|
72
84
|
buttonStyle = {
|
|
73
85
|
backgroundColor: '#00000099',
|
|
74
86
|
borderColor: '#FFFFFF33',
|
|
@@ -78,22 +90,27 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
78
90
|
/**
|
|
79
91
|
* check app is favorite
|
|
80
92
|
*/
|
|
81
|
-
const checkAppIsFavorite = () =>
|
|
93
|
+
const checkAppIsFavorite = () =>
|
|
82
94
|
navigator?.maxApi?.dispatchFunction?.(
|
|
83
95
|
'isFavoriteApp',
|
|
84
|
-
context?.
|
|
96
|
+
context?.code,
|
|
85
97
|
(result: boolean) => {
|
|
86
|
-
|
|
87
|
-
setIsFavorite(result);
|
|
88
|
-
}
|
|
98
|
+
setIsFavorite(result);
|
|
89
99
|
}
|
|
90
100
|
);
|
|
91
|
-
};
|
|
92
101
|
|
|
93
102
|
/**
|
|
94
103
|
* close navigation container
|
|
95
104
|
*/
|
|
96
105
|
const onClose = () => {
|
|
106
|
+
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
107
|
+
context?.autoTracking?.({
|
|
108
|
+
...context,
|
|
109
|
+
componentName: 'IconButton',
|
|
110
|
+
componentId: 'navigation_close',
|
|
111
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
112
|
+
});
|
|
113
|
+
|
|
97
114
|
if (preventClose) {
|
|
98
115
|
navigator?.showModal({
|
|
99
116
|
screen: () => (
|
|
@@ -122,6 +139,13 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
122
139
|
* on press shortcut
|
|
123
140
|
*/
|
|
124
141
|
const onPressShortcut = () => {
|
|
142
|
+
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
143
|
+
context?.autoTracking?.({
|
|
144
|
+
...context,
|
|
145
|
+
componentName: 'IconButton',
|
|
146
|
+
componentId: 'navigation_shortcut',
|
|
147
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
148
|
+
});
|
|
125
149
|
setIsLoading(true);
|
|
126
150
|
navigator?.maxApi?.dispatchFunction?.(
|
|
127
151
|
'onToolAction',
|
|
@@ -147,8 +171,13 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
147
171
|
const onPressHelpCenter = () => {
|
|
148
172
|
const appName = translate?.(context?.name);
|
|
149
173
|
const appDescription = translate?.(context?.description);
|
|
150
|
-
const
|
|
151
|
-
|
|
174
|
+
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
175
|
+
context?.autoTracking?.({
|
|
176
|
+
...context,
|
|
177
|
+
componentName: 'IconButton',
|
|
178
|
+
componentId: 'navigation_helpcenter',
|
|
179
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
180
|
+
});
|
|
152
181
|
|
|
153
182
|
navigator?.showBottomSheet({
|
|
154
183
|
options: {
|
|
@@ -160,7 +189,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
160
189
|
/>
|
|
161
190
|
<View>
|
|
162
191
|
<Text typography={'label_default_medium'}>{appName}</Text>
|
|
163
|
-
{
|
|
192
|
+
{appDescription !== '{}' && (
|
|
164
193
|
<Text
|
|
165
194
|
typography={'description_default_regular'}
|
|
166
195
|
color={Colors.black_12}>
|
|
@@ -180,6 +209,14 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
180
209
|
* on press icon more
|
|
181
210
|
*/
|
|
182
211
|
const onPressIconMore = () => {
|
|
212
|
+
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
213
|
+
context?.autoTracking?.({
|
|
214
|
+
...context,
|
|
215
|
+
componentName: 'IconButton',
|
|
216
|
+
componentId: 'navigation_more',
|
|
217
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
218
|
+
});
|
|
219
|
+
|
|
183
220
|
navigator?.maxApi?.dispatchFunction?.('showTools', tools, (key: string) => {
|
|
184
221
|
for (const group of tools) {
|
|
185
222
|
const pressedTool = group?.items?.find?.(
|
|
@@ -210,7 +247,16 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
210
247
|
(group: ToolGroup) => group?.items?.length === 1
|
|
211
248
|
)?.items[0];
|
|
212
249
|
iconShortcut = singleTool?.icon;
|
|
213
|
-
shortcutOnPress =
|
|
250
|
+
shortcutOnPress = () => {
|
|
251
|
+
singleTool?.onPress?.();
|
|
252
|
+
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
253
|
+
context?.autoTracking?.({
|
|
254
|
+
...context,
|
|
255
|
+
componentName: 'IconButton',
|
|
256
|
+
componentId: 'navigation_shortcut',
|
|
257
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
258
|
+
});
|
|
259
|
+
};
|
|
214
260
|
}
|
|
215
261
|
|
|
216
262
|
const showBadge = tools.some((group: ToolGroup) =>
|
|
@@ -8,10 +8,11 @@ import {
|
|
|
8
8
|
} from 'react-native';
|
|
9
9
|
import {ApplicationContext, MiniAppContext} from '../index';
|
|
10
10
|
import {exportFontFamily, scaleSize, Text} from '../../Text';
|
|
11
|
-
import {Colors, Spacing, Styles} from '../../Consts';
|
|
11
|
+
import {Colors, Spacing, Radius, Styles} from '../../Consts';
|
|
12
12
|
import {TitleJourneyProps, TitleLocationProps, TitleUserProps} from '../types';
|
|
13
13
|
import {Image} from '../../Image';
|
|
14
14
|
import {Icon} from '../../Icon';
|
|
15
|
+
import {Skeleton} from '../../Skeleton';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* default header title used for nav
|
|
@@ -69,13 +70,16 @@ const TitleCustom: React.FC<any> = ({type, data, ...props}) => {
|
|
|
69
70
|
const TitleUser: React.FC<TitleUserProps> = ({
|
|
70
71
|
title,
|
|
71
72
|
subTitle,
|
|
72
|
-
image,
|
|
73
|
+
image: image,
|
|
73
74
|
tintColor,
|
|
74
75
|
dotColor,
|
|
75
|
-
verify,
|
|
76
76
|
onPress,
|
|
77
|
+
verify,
|
|
78
|
+
icons,
|
|
79
|
+
isLoading,
|
|
77
80
|
}) => {
|
|
78
81
|
const {theme} = useContext(ApplicationContext);
|
|
82
|
+
const maxWidth = icons?.length === 2 ? '73%' : '100%';
|
|
79
83
|
const getShortName = (name: string) => {
|
|
80
84
|
const words = name.split(' ');
|
|
81
85
|
const lastTwoWords = words.slice(-2);
|
|
@@ -83,17 +87,214 @@ const TitleUser: React.FC<TitleUserProps> = ({
|
|
|
83
87
|
};
|
|
84
88
|
|
|
85
89
|
const renderImage = () => {
|
|
86
|
-
|
|
90
|
+
const avatarWidth = Spacing.XXL;
|
|
91
|
+
if (Array.isArray(image) && image.length > 0) {
|
|
92
|
+
switch (image.length) {
|
|
93
|
+
case 1: {
|
|
94
|
+
return image.map((i: string) => (
|
|
95
|
+
<Image source={{uri: i}} style={styles.circle} />
|
|
96
|
+
));
|
|
97
|
+
}
|
|
98
|
+
case 2: {
|
|
99
|
+
return (
|
|
100
|
+
<View style={[Styles.flex, styles.groupAvtContainer]}>
|
|
101
|
+
<Image
|
|
102
|
+
source={{uri: image[0]}}
|
|
103
|
+
style={[
|
|
104
|
+
styles.circleMedium,
|
|
105
|
+
{position: 'absolute', top: 0, right: 0, zIndex: 0},
|
|
106
|
+
]}
|
|
107
|
+
/>
|
|
108
|
+
<Image
|
|
109
|
+
source={{uri: image[1]}}
|
|
110
|
+
style={[
|
|
111
|
+
styles.circleMedium,
|
|
112
|
+
{position: 'absolute', left: 0, bottom: 0, zIndex: 1},
|
|
113
|
+
]}
|
|
114
|
+
/>
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
case 3: {
|
|
119
|
+
return (
|
|
120
|
+
<View style={[Styles.flex, styles.groupAvtContainer]}>
|
|
121
|
+
<Image
|
|
122
|
+
source={{uri: image[0]}}
|
|
123
|
+
style={[
|
|
124
|
+
styles.circleSmall,
|
|
125
|
+
{position: 'absolute', bottom: 2, right: 2, zIndex: 0},
|
|
126
|
+
]}
|
|
127
|
+
/>
|
|
128
|
+
<Image
|
|
129
|
+
source={{uri: image[1]}}
|
|
130
|
+
style={[
|
|
131
|
+
styles.circleSmall,
|
|
132
|
+
{
|
|
133
|
+
position: 'absolute',
|
|
134
|
+
top: 2,
|
|
135
|
+
left: avatarWidth / 2 - 16 / 2,
|
|
136
|
+
zIndex: 1,
|
|
137
|
+
},
|
|
138
|
+
]}
|
|
139
|
+
/>
|
|
140
|
+
<Image
|
|
141
|
+
source={{uri: image[2]}}
|
|
142
|
+
style={[
|
|
143
|
+
styles.circleSmall,
|
|
144
|
+
{position: 'absolute', left: 2, bottom: 2, zIndex: 2},
|
|
145
|
+
]}
|
|
146
|
+
/>
|
|
147
|
+
</View>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
case 4: {
|
|
151
|
+
return (
|
|
152
|
+
<View style={[Styles.flex, styles.groupAvtContainer]}>
|
|
153
|
+
<Image
|
|
154
|
+
source={{uri: image[0]}}
|
|
155
|
+
style={[
|
|
156
|
+
styles.circleSmall,
|
|
157
|
+
{position: 'absolute', bottom: 2, left: 2, zIndex: 0},
|
|
158
|
+
]}
|
|
159
|
+
/>
|
|
160
|
+
<Image
|
|
161
|
+
source={{uri: image[1]}}
|
|
162
|
+
style={[
|
|
163
|
+
styles.circleSmall,
|
|
164
|
+
{position: 'absolute', bottom: 2, right: 2, zIndex: 1},
|
|
165
|
+
]}
|
|
166
|
+
/>
|
|
167
|
+
<Image
|
|
168
|
+
source={{uri: image[2]}}
|
|
169
|
+
style={[
|
|
170
|
+
styles.circleSmall,
|
|
171
|
+
{position: 'absolute', top: 2, right: 2, zIndex: 2},
|
|
172
|
+
]}
|
|
173
|
+
/>
|
|
174
|
+
<Image
|
|
175
|
+
source={{uri: image[2]}}
|
|
176
|
+
style={[
|
|
177
|
+
styles.circleSmall,
|
|
178
|
+
{position: 'absolute', top: 2, left: 2, zIndex: 3},
|
|
179
|
+
]}
|
|
180
|
+
/>
|
|
181
|
+
</View>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
default: {
|
|
185
|
+
return (
|
|
186
|
+
<View style={[Styles.flex, styles.groupAvtContainer]}>
|
|
187
|
+
<Image
|
|
188
|
+
source={{uri: image[0]}}
|
|
189
|
+
style={[
|
|
190
|
+
styles.circleSmall,
|
|
191
|
+
{position: 'absolute', left: 2, bottom: 2, zIndex: 0},
|
|
192
|
+
]}
|
|
193
|
+
/>
|
|
194
|
+
<Image
|
|
195
|
+
source={{uri: image[1]}}
|
|
196
|
+
style={[
|
|
197
|
+
styles.circleSmall,
|
|
198
|
+
{position: 'absolute', top: 2, right: 2, zIndex: 2},
|
|
199
|
+
]}
|
|
200
|
+
/>
|
|
201
|
+
<Image
|
|
202
|
+
source={{uri: image[2]}}
|
|
203
|
+
style={[
|
|
204
|
+
styles.circleSmall,
|
|
205
|
+
{position: 'absolute', left: 2, top: 2, zIndex: 3},
|
|
206
|
+
]}
|
|
207
|
+
/>
|
|
208
|
+
<View
|
|
209
|
+
style={[
|
|
210
|
+
styles.circleSmall,
|
|
211
|
+
{
|
|
212
|
+
position: 'absolute',
|
|
213
|
+
bottom: 2,
|
|
214
|
+
right: 2,
|
|
215
|
+
zIndex: 1,
|
|
216
|
+
backgroundColor: Colors.pink_09,
|
|
217
|
+
},
|
|
218
|
+
]}>
|
|
219
|
+
<Text
|
|
220
|
+
color={Colors.pink_03}
|
|
221
|
+
typography={'description_xs_regular'}>
|
|
222
|
+
{`+${image.length - 3}`}
|
|
223
|
+
</Text>
|
|
224
|
+
</View>
|
|
225
|
+
</View>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (typeof image === 'string') {
|
|
87
232
|
return <Image source={{uri: image}} style={styles.circle} />;
|
|
88
|
-
}
|
|
89
|
-
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<View
|
|
237
|
+
style={[
|
|
238
|
+
styles.imageContainer,
|
|
239
|
+
{borderColor: theme.colors.border.default},
|
|
240
|
+
]}>
|
|
90
241
|
<Text color={Colors.pink_03} typography={'description_xs_regular'}>
|
|
91
242
|
{getShortName(title)}
|
|
92
243
|
</Text>
|
|
93
|
-
|
|
94
|
-
|
|
244
|
+
</View>
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const renderVerifyIcon = () => {
|
|
249
|
+
const renderIcons = () => {
|
|
250
|
+
if (icons && icons.length > 0) {
|
|
251
|
+
return icons.map(icon => (
|
|
252
|
+
<Image
|
|
253
|
+
source={{
|
|
254
|
+
uri: icon,
|
|
255
|
+
}}
|
|
256
|
+
style={styles.verifiedIcon}
|
|
257
|
+
/>
|
|
258
|
+
));
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return <View style={Styles.row}>{renderIcons()}</View>;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const renderShimmer = () => {
|
|
266
|
+
return (
|
|
267
|
+
<View style={Styles.row}>
|
|
268
|
+
<View
|
|
269
|
+
style={[
|
|
270
|
+
styles.imageContainer,
|
|
271
|
+
{
|
|
272
|
+
borderColor: theme.colors.border.default,
|
|
273
|
+
marginRight: Spacing.M / 2,
|
|
274
|
+
},
|
|
275
|
+
]}>
|
|
276
|
+
<Skeleton />
|
|
277
|
+
</View>
|
|
278
|
+
<View style={Styles.flex}>
|
|
279
|
+
<View style={styles.shimmerTitle}>
|
|
280
|
+
<Skeleton />
|
|
281
|
+
</View>
|
|
282
|
+
<View style={{height: Spacing.XXS}} />
|
|
283
|
+
<View style={styles.shimmerDescription}>
|
|
284
|
+
<Skeleton />
|
|
285
|
+
</View>
|
|
286
|
+
</View>
|
|
287
|
+
</View>
|
|
288
|
+
);
|
|
95
289
|
};
|
|
96
290
|
|
|
291
|
+
if (verify) {
|
|
292
|
+
console.warn(`Warning: The verify value "${verify}" is deprecated.`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (isLoading) {
|
|
296
|
+
return renderShimmer();
|
|
297
|
+
}
|
|
97
298
|
return (
|
|
98
299
|
<TouchableOpacity
|
|
99
300
|
style={styles.headerTitleContainer}
|
|
@@ -101,18 +302,12 @@ const TitleUser: React.FC<TitleUserProps> = ({
|
|
|
101
302
|
disabled={onPress === undefined}>
|
|
102
303
|
<View style={[Styles.row, Styles.flex]}>
|
|
103
304
|
<View>
|
|
104
|
-
|
|
105
|
-
style={[
|
|
106
|
-
styles.imageContainer,
|
|
107
|
-
{borderColor: theme.colors.border.default},
|
|
108
|
-
]}>
|
|
109
|
-
{renderImage()}
|
|
110
|
-
</View>
|
|
305
|
+
{renderImage()}
|
|
111
306
|
{!!dotColor && (
|
|
112
307
|
<View style={[styles.dotAvatar, {backgroundColor: dotColor}]} />
|
|
113
308
|
)}
|
|
114
309
|
</View>
|
|
115
|
-
<View style={[Styles.flex, {marginLeft: 6}]}>
|
|
310
|
+
<View style={[Styles.flex, {marginLeft: 6, maxWidth: maxWidth}]}>
|
|
116
311
|
<View style={Styles.row}>
|
|
117
312
|
<Text
|
|
118
313
|
typography="action_xs_bold"
|
|
@@ -120,14 +315,7 @@ const TitleUser: React.FC<TitleUserProps> = ({
|
|
|
120
315
|
numberOfLines={1}>
|
|
121
316
|
{title}
|
|
122
317
|
</Text>
|
|
123
|
-
{
|
|
124
|
-
<Image
|
|
125
|
-
source={{
|
|
126
|
-
uri: 'https://static.momocdn.net/app/img/kits/verified.png',
|
|
127
|
-
}}
|
|
128
|
-
style={styles.verifiedIcon}
|
|
129
|
-
/>
|
|
130
|
-
)}
|
|
318
|
+
{renderVerifyIcon()}
|
|
131
319
|
</View>
|
|
132
320
|
{!!subTitle && (
|
|
133
321
|
<Text
|
|
@@ -153,7 +341,24 @@ const TitleLocation: React.FC<TitleLocationProps> = ({
|
|
|
153
341
|
location,
|
|
154
342
|
description,
|
|
155
343
|
onPress,
|
|
344
|
+
isLoading,
|
|
156
345
|
}) => {
|
|
346
|
+
const renderShimmer = () => {
|
|
347
|
+
return (
|
|
348
|
+
<View style={Styles.flex}>
|
|
349
|
+
<View style={styles.shimmerDescription}>
|
|
350
|
+
<Skeleton />
|
|
351
|
+
</View>
|
|
352
|
+
<View style={{height: Spacing.XXS}} />
|
|
353
|
+
<View style={styles.shimmerTitle}>
|
|
354
|
+
<Skeleton />
|
|
355
|
+
</View>
|
|
356
|
+
</View>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
if (isLoading) {
|
|
360
|
+
return renderShimmer();
|
|
361
|
+
}
|
|
157
362
|
return (
|
|
158
363
|
<TouchableOpacity
|
|
159
364
|
style={[styles.headerTitleContainer, {alignItems: 'flex-start'}]}
|
|
@@ -193,14 +398,36 @@ const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
|
193
398
|
iconColor,
|
|
194
399
|
tintColor,
|
|
195
400
|
onPress,
|
|
401
|
+
isLoading,
|
|
196
402
|
}) => {
|
|
403
|
+
const {width} = Dimensions.get('screen');
|
|
404
|
+
const startWidth = width * 0.25;
|
|
405
|
+
const endWidth = width * 0.75 - 212;
|
|
406
|
+
const renderShimmer = () => {
|
|
407
|
+
return (
|
|
408
|
+
<View style={Styles.flex}>
|
|
409
|
+
<View style={styles.shimmerTitle}>
|
|
410
|
+
<Skeleton />
|
|
411
|
+
</View>
|
|
412
|
+
<View style={{height: Spacing.XXS}} />
|
|
413
|
+
<View style={styles.shimmerDescription}>
|
|
414
|
+
<Skeleton />
|
|
415
|
+
</View>
|
|
416
|
+
</View>
|
|
417
|
+
);
|
|
418
|
+
};
|
|
419
|
+
if (isLoading) {
|
|
420
|
+
return renderShimmer();
|
|
421
|
+
}
|
|
197
422
|
return (
|
|
198
|
-
<TouchableOpacity
|
|
199
|
-
style={[styles.headerTitleContainer, {overflow: 'hidden'}]}
|
|
200
|
-
onPress={onPress}>
|
|
423
|
+
<TouchableOpacity style={styles.headerTitleContainer} onPress={onPress}>
|
|
201
424
|
<View style={styles.rowJourney}>
|
|
202
425
|
<View style={Styles.row}>
|
|
203
|
-
<Text
|
|
426
|
+
<Text
|
|
427
|
+
typography="action_xs_bold"
|
|
428
|
+
style={{maxWidth: startWidth}}
|
|
429
|
+
color={tintColor}
|
|
430
|
+
numberOfLines={1}>
|
|
204
431
|
{start}
|
|
205
432
|
</Text>
|
|
206
433
|
<Icon
|
|
@@ -211,6 +438,7 @@ const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
|
211
438
|
/>
|
|
212
439
|
{!!end && (
|
|
213
440
|
<Text
|
|
441
|
+
style={{maxWidth: endWidth}}
|
|
214
442
|
typography="action_xs_bold"
|
|
215
443
|
color={tintColor}
|
|
216
444
|
numberOfLines={1}>
|
|
@@ -270,6 +498,28 @@ const styles = StyleSheet.create({
|
|
|
270
498
|
circle: {
|
|
271
499
|
width: 32,
|
|
272
500
|
height: 32,
|
|
501
|
+
borderRadius: 16,
|
|
502
|
+
borderWidth: 0.5,
|
|
503
|
+
borderColor: Colors.black_04,
|
|
504
|
+
},
|
|
505
|
+
circleMedium: {
|
|
506
|
+
width: 24,
|
|
507
|
+
height: 24,
|
|
508
|
+
borderRadius: 16,
|
|
509
|
+
borderWidth: 0.5,
|
|
510
|
+
borderColor: Colors.black_04,
|
|
511
|
+
},
|
|
512
|
+
circleSmall: {
|
|
513
|
+
width: 16,
|
|
514
|
+
height: 16,
|
|
515
|
+
borderRadius: 16,
|
|
516
|
+
borderWidth: 0.5,
|
|
517
|
+
borderColor: Colors.black_04,
|
|
518
|
+
},
|
|
519
|
+
groupAvtContainer: {
|
|
520
|
+
width: Spacing.XXL,
|
|
521
|
+
height: Spacing.XXL,
|
|
522
|
+
position: 'relative',
|
|
273
523
|
},
|
|
274
524
|
imageContainer: {
|
|
275
525
|
width: 32,
|
|
@@ -291,7 +541,19 @@ const styles = StyleSheet.create({
|
|
|
291
541
|
borderWidth: 2,
|
|
292
542
|
borderColor: Colors.black_01,
|
|
293
543
|
},
|
|
294
|
-
verifiedIcon: {width:
|
|
544
|
+
verifiedIcon: {width: 16, height: 16, marginLeft: Spacing.XS},
|
|
545
|
+
shimmerTitle: {
|
|
546
|
+
height: 18,
|
|
547
|
+
width: 120,
|
|
548
|
+
borderRadius: Radius.XS,
|
|
549
|
+
overflow: 'hidden',
|
|
550
|
+
},
|
|
551
|
+
shimmerDescription: {
|
|
552
|
+
height: 12,
|
|
553
|
+
width: 120,
|
|
554
|
+
borderRadius: Radius.XS,
|
|
555
|
+
overflow: 'hidden',
|
|
556
|
+
},
|
|
295
557
|
});
|
|
296
558
|
|
|
297
559
|
export {HeaderTitle, TitleCustom};
|
|
@@ -45,18 +45,13 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
45
45
|
const goBack = () => {
|
|
46
46
|
const canGoBack = navigator?.ref?.current?.canGoBack?.();
|
|
47
47
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
48
|
-
|
|
48
|
+
context?.autoTracking?.({
|
|
49
49
|
...context,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
54
|
-
navigator?.maxApi?.showToastDebug?.({
|
|
55
|
-
appId: `auto - ${context.appId}`,
|
|
56
|
-
message: 'service_icon_clicked',
|
|
57
|
-
params,
|
|
50
|
+
componentName: 'SearchHeader',
|
|
51
|
+
componentId: 'search_back',
|
|
52
|
+
screenName: currentRoute?.params?.screen?.name ?? currentRoute?.name,
|
|
58
53
|
});
|
|
59
|
-
|
|
54
|
+
|
|
60
55
|
if (canGoBack) {
|
|
61
56
|
navigator?.ref?.current?.goBack?.();
|
|
62
57
|
} else if (navigator?.maxApi) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useEffect, useRef, useState} from 'react';
|
|
1
|
+
import React, {useContext, useEffect, useRef, useState} from 'react';
|
|
2
2
|
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
|
3
3
|
import {
|
|
4
4
|
NavigationContainer as ReactNavigationContainer,
|
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
createStackNavigator,
|
|
9
9
|
StackNavigationOptions,
|
|
10
10
|
} from '@react-navigation/stack';
|
|
11
|
-
import {DeviceEventEmitter} from 'react-native';
|
|
11
|
+
import {DeviceEventEmitter, NativeModules} from 'react-native';
|
|
12
12
|
import StackScreen from './StackScreen';
|
|
13
13
|
import ModalScreen from './ModalScreen';
|
|
14
14
|
import Navigator from './Navigator';
|
|
15
15
|
import {getDialogOptions, getModalOptions, getStackOptions} from './utils';
|
|
16
16
|
import {NavigationContainerProps} from './types';
|
|
17
|
-
import {ApplicationContext} from './index';
|
|
17
|
+
import {ApplicationContext, MiniAppContext} from './index';
|
|
18
18
|
import Localize from './Localize';
|
|
19
19
|
import {defaultTheme} from '../Consts';
|
|
20
20
|
|
|
@@ -28,11 +28,20 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
28
28
|
initialParams,
|
|
29
29
|
localize = new Localize({vi: {}, en: {}}),
|
|
30
30
|
}) => {
|
|
31
|
+
const context = useContext<any>(MiniAppContext);
|
|
31
32
|
const navigationRef = useRef<NavigationContainerRef>(null);
|
|
33
|
+
const routes = useRef<any>();
|
|
32
34
|
const isReady = useRef(false);
|
|
33
35
|
const navigator = useRef(new Navigator(navigationRef, isReady));
|
|
34
36
|
const [showGrid, setShowGrid] = useState(false);
|
|
35
|
-
|
|
37
|
+
let config = null;
|
|
38
|
+
try {
|
|
39
|
+
config = JSON.parse(
|
|
40
|
+
NativeModules?.ConfigsModule?.getConfigSync?.('DESIGN_SYSTEM') || ''
|
|
41
|
+
);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log(e);
|
|
44
|
+
}
|
|
36
45
|
|
|
37
46
|
/**
|
|
38
47
|
* inject data for navigator
|
|
@@ -63,15 +72,29 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
63
72
|
return localize?.translate(data as string);
|
|
64
73
|
};
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
const onScreenNavigated = (
|
|
76
|
+
preScreenName: string,
|
|
77
|
+
screenName: string,
|
|
78
|
+
action: string
|
|
79
|
+
) => {
|
|
80
|
+
context?.autoTracking?.({
|
|
81
|
+
...context,
|
|
82
|
+
preScreenName,
|
|
83
|
+
screenName,
|
|
84
|
+
componentName: 'Screen',
|
|
85
|
+
state: 'navigated',
|
|
86
|
+
action,
|
|
73
87
|
});
|
|
74
|
-
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* debug toast
|
|
91
|
+
*/
|
|
92
|
+
maxApi?.showToastDebug?.({
|
|
93
|
+
appId: context.appId,
|
|
94
|
+
message: `${screenName} screen_navigated`,
|
|
95
|
+
type: 'ERROR',
|
|
96
|
+
});
|
|
97
|
+
};
|
|
75
98
|
|
|
76
99
|
return (
|
|
77
100
|
<SafeAreaProvider>
|
|
@@ -81,7 +104,8 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
81
104
|
theme: {
|
|
82
105
|
...theme,
|
|
83
106
|
assets: {
|
|
84
|
-
headerBackground:
|
|
107
|
+
headerBackground:
|
|
108
|
+
theme.assets?.headerBackground || config?.headerBar,
|
|
85
109
|
},
|
|
86
110
|
},
|
|
87
111
|
showGrid,
|
|
@@ -102,6 +126,27 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
102
126
|
ref={navigationRef}
|
|
103
127
|
onReady={() => {
|
|
104
128
|
isReady.current = true;
|
|
129
|
+
routes.current = navigationRef.current?.getRootState?.()?.routes;
|
|
130
|
+
}}
|
|
131
|
+
onStateChange={state => {
|
|
132
|
+
const lastedRoute: any = state?.routes?.[state?.routes?.length - 1];
|
|
133
|
+
const oldRoute: any = routes.current?.[routes.current?.length - 1];
|
|
134
|
+
const lasted = lastedRoute?.params?.screen;
|
|
135
|
+
const previous = oldRoute?.params?.screen;
|
|
136
|
+
if (routes.current?.length < (state?.routes?.length ?? 0)) {
|
|
137
|
+
onScreenNavigated(
|
|
138
|
+
previous?.name ?? previous?.type?.name,
|
|
139
|
+
lasted?.name ?? lasted?.type?.name,
|
|
140
|
+
'push'
|
|
141
|
+
);
|
|
142
|
+
} else if (routes.current?.length > (state?.routes?.length ?? 0)) {
|
|
143
|
+
onScreenNavigated(
|
|
144
|
+
previous?.name ?? previous?.type?.name,
|
|
145
|
+
lasted?.name ?? lasted?.type?.name,
|
|
146
|
+
'back'
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
routes.current = state?.routes;
|
|
105
150
|
}}
|
|
106
151
|
independent={true}>
|
|
107
152
|
<Stack.Navigator initialRouteName="Stack" headerMode="screen">
|
|
@@ -6,6 +6,8 @@ import Navigation from './Navigation';
|
|
|
6
6
|
import {ApplicationContext, MiniAppContext, ScreenContext} from './index';
|
|
7
7
|
import {GridSystem} from '../Layout';
|
|
8
8
|
|
|
9
|
+
const runAfterInteractions = InteractionManager.runAfterInteractions;
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* container for stack screen
|
|
11
13
|
* @param props
|
|
@@ -42,7 +44,6 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
42
44
|
delete data.initialParams;
|
|
43
45
|
|
|
44
46
|
const screenName = Component?.name || Component?.type?.name || 'Invalid';
|
|
45
|
-
const routes = props.navigation.getState()?.routes || [];
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* set options for screen
|
|
@@ -67,15 +68,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
67
68
|
|
|
68
69
|
const subscription = props.navigation?.addListener?.('focus', () => {
|
|
69
70
|
navigator?.maxApi?.of?.({screenName});
|
|
70
|
-
navigator?.maxApi?.
|
|
71
|
-
let preScreenName = data?.screenName;
|
|
72
|
-
if (routes?.length > 1) {
|
|
73
|
-
const screen = routes?.[routes?.length - 2]?.params?.screen;
|
|
74
|
-
preScreenName = screen?.name || screen?.type?.name || 'Invalid';
|
|
75
|
-
}
|
|
76
|
-
onScreenNavigated(preScreenName);
|
|
77
|
-
navigator?.maxApi?.setObserver('CURRENT_SCREEN', {screenName});
|
|
78
|
-
});
|
|
71
|
+
navigator?.maxApi?.setObserver('CURRENT_SCREEN', {screenName});
|
|
79
72
|
});
|
|
80
73
|
navigator?.maxApi?.startTraceScreenLoad?.(
|
|
81
74
|
screenName,
|
|
@@ -115,7 +108,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
115
108
|
timeLoad = tracking.current.endTime - tracking.current.startTime;
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
context
|
|
111
|
+
context?.autoTracking?.({
|
|
119
112
|
...context,
|
|
120
113
|
screenName,
|
|
121
114
|
componentName: 'Screen',
|
|
@@ -162,7 +155,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
162
155
|
tracking.current.timeInteraction = timeLoad;
|
|
163
156
|
}
|
|
164
157
|
|
|
165
|
-
context
|
|
158
|
+
context?.autoTracking?.({
|
|
166
159
|
...context,
|
|
167
160
|
screenName,
|
|
168
161
|
componentName: 'Screen',
|
|
@@ -188,46 +181,39 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
188
181
|
}
|
|
189
182
|
};
|
|
190
183
|
|
|
191
|
-
/**
|
|
192
|
-
* tracking for screen navigated
|
|
193
|
-
*/
|
|
194
|
-
const onScreenNavigated = (preScreenName: string) => {
|
|
195
|
-
context.autoTracking?.({
|
|
196
|
-
...context,
|
|
197
|
-
preScreenName,
|
|
198
|
-
screenName,
|
|
199
|
-
componentName: 'Screen',
|
|
200
|
-
state: 'navigated',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* debug toast
|
|
205
|
-
*/
|
|
206
|
-
navigator?.maxApi?.showToastDebug?.({
|
|
207
|
-
appId: context.appId,
|
|
208
|
-
message: `${screenName} screen_navigated`,
|
|
209
|
-
type: 'ERROR',
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
|
-
|
|
213
184
|
return (
|
|
214
185
|
<ScreenContext.Provider
|
|
215
186
|
value={{
|
|
216
187
|
screenName,
|
|
217
188
|
onElementLoad: (data: any) => {
|
|
189
|
+
/**
|
|
190
|
+
* tracking for element screen load
|
|
191
|
+
*/
|
|
218
192
|
clearTimeout(tracking.current.timeoutLoad);
|
|
219
193
|
tracking.current.endTime = Date.now();
|
|
220
194
|
tracking.current.timeoutInteraction?.cancel?.();
|
|
221
|
-
tracking.current.timeoutInteraction =
|
|
222
|
-
|
|
223
|
-
tracking.current.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
195
|
+
tracking.current.timeoutInteraction = runAfterInteractions(() => {
|
|
196
|
+
tracking.current.timeInteraction =
|
|
197
|
+
Date.now() - tracking.current.startTime;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* support for debug last element
|
|
202
|
+
*/
|
|
227
203
|
if (data?.componentName) {
|
|
228
204
|
lastElement.current = data;
|
|
229
205
|
}
|
|
230
206
|
|
|
207
|
+
/**
|
|
208
|
+
* for stop tracking when user interaction
|
|
209
|
+
*/
|
|
210
|
+
if (data.interaction) {
|
|
211
|
+
onScreenLoad();
|
|
212
|
+
onScreenInteraction();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* timeout for handle tracking screen
|
|
216
|
+
*/
|
|
231
217
|
tracking.current.timeoutLoad = setTimeout(() => {
|
|
232
218
|
const time = tracking.current.endTime - tracking.current.startTime;
|
|
233
219
|
if (tracking.current.timeLoad === 0) {
|
package/Application/types.ts
CHANGED
|
@@ -158,6 +158,7 @@ export interface NavigationOptions
|
|
|
158
158
|
extends Omit<StackNavigationOptions, 'headerRight' | 'headerTitle'> {
|
|
159
159
|
preventBack?: PopupNotifyProps;
|
|
160
160
|
onPressLeftHeader?: () => void;
|
|
161
|
+
onBackHandler?: (goBack: () => void) => void;
|
|
161
162
|
hiddenBack?: boolean;
|
|
162
163
|
headerTitle?: HeaderTitleProps | string;
|
|
163
164
|
headerRight?: OnBoarding | HeaderRightToolkit | any;
|
|
@@ -178,6 +179,7 @@ export type HeaderRightToolkit = {
|
|
|
178
179
|
export interface HeaderBackProps extends NavigationButtonProps {
|
|
179
180
|
preventBack?: PopupNotifyProps;
|
|
180
181
|
onPressLeftHeader?: () => void;
|
|
182
|
+
onBackHandler?: (goBack: () => void) => void;
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
export type HeaderBackgroundProps = {
|
|
@@ -193,11 +195,13 @@ export type HeaderBackgroundProps = {
|
|
|
193
195
|
export type TitleUserProps = {
|
|
194
196
|
title: string;
|
|
195
197
|
subTitle?: string;
|
|
196
|
-
image?: string;
|
|
198
|
+
image?: string[] | string;
|
|
197
199
|
dotColor?: string;
|
|
198
200
|
verify?: boolean;
|
|
199
201
|
tintColor?: string;
|
|
200
202
|
onPress?: () => void;
|
|
203
|
+
icons?: string[];
|
|
204
|
+
isLoading?: boolean;
|
|
201
205
|
};
|
|
202
206
|
|
|
203
207
|
export type TitleLocationProps = {
|
|
@@ -205,6 +209,7 @@ export type TitleLocationProps = {
|
|
|
205
209
|
location: string;
|
|
206
210
|
tintColor?: string;
|
|
207
211
|
onPress?: () => void;
|
|
212
|
+
isLoading?: boolean;
|
|
208
213
|
};
|
|
209
214
|
|
|
210
215
|
export type TitleJourneyProps = {
|
|
@@ -215,6 +220,7 @@ export type TitleJourneyProps = {
|
|
|
215
220
|
iconColor?: string;
|
|
216
221
|
tintColor?: string;
|
|
217
222
|
onPress?: () => void;
|
|
223
|
+
isLoading?: boolean;
|
|
218
224
|
};
|
|
219
225
|
|
|
220
226
|
export type HeaderToolkitProps = {
|
package/Application/utils.tsx
CHANGED
|
@@ -87,6 +87,7 @@ const getOptions = (
|
|
|
87
87
|
*/
|
|
88
88
|
if (
|
|
89
89
|
typeof params.onPressLeftHeader === 'function' ||
|
|
90
|
+
typeof params.onBackHandler === 'function' ||
|
|
90
91
|
params.preventBack !== undefined ||
|
|
91
92
|
typeof params.hiddenBack === 'boolean'
|
|
92
93
|
) {
|
|
@@ -95,6 +96,7 @@ const getOptions = (
|
|
|
95
96
|
{...props}
|
|
96
97
|
preventBack={params.preventBack}
|
|
97
98
|
onPressLeftHeader={params.onPressLeftHeader}
|
|
99
|
+
onBackHandler={params.onBackHandler}
|
|
98
100
|
/>
|
|
99
101
|
);
|
|
100
102
|
if (params.hiddenBack) {
|
package/Button/index.tsx
CHANGED
|
@@ -88,7 +88,6 @@ const Button: FC<ButtonProps> = ({
|
|
|
88
88
|
...rest
|
|
89
89
|
}) => {
|
|
90
90
|
const {theme, config} = useContext(ApplicationContext);
|
|
91
|
-
const component = useContext<any>(ComponentContext);
|
|
92
91
|
const {gradient, color} = config?.navigationBar?.buttonColors ?? {};
|
|
93
92
|
let gradientPros;
|
|
94
93
|
let state = 'enabled';
|
|
@@ -293,7 +292,6 @@ const Button: FC<ButtonProps> = ({
|
|
|
293
292
|
return (
|
|
294
293
|
<ComponentContext.Provider
|
|
295
294
|
value={{
|
|
296
|
-
...component,
|
|
297
295
|
componentName: 'Button',
|
|
298
296
|
params,
|
|
299
297
|
state: state,
|
package/Input/Input.tsx
CHANGED
|
@@ -67,7 +67,6 @@ const Input = forwardRef(
|
|
|
67
67
|
ref
|
|
68
68
|
) => {
|
|
69
69
|
const {theme} = useContext(ApplicationContext);
|
|
70
|
-
const component = useContext<any>(ComponentContext);
|
|
71
70
|
const [focused, setFocused] = useState(false);
|
|
72
71
|
const [haveValue, setHaveValue] = useState(!!value || !!props.defaultValue);
|
|
73
72
|
const [secureTextInput, setSecureTextInput] = useState(secureTextEntry);
|
|
@@ -270,7 +269,6 @@ const Input = forwardRef(
|
|
|
270
269
|
return (
|
|
271
270
|
<ComponentContext.Provider
|
|
272
271
|
value={{
|
|
273
|
-
...component,
|
|
274
272
|
componentName: 'Input',
|
|
275
273
|
params,
|
|
276
274
|
state: inputState,
|
package/Input/InputDropDown.tsx
CHANGED
|
@@ -30,7 +30,6 @@ const InputDropDown = ({
|
|
|
30
30
|
multiline,
|
|
31
31
|
}: InputDropDownProps) => {
|
|
32
32
|
const {theme} = useContext(ApplicationContext);
|
|
33
|
-
const component = useContext<any>(ComponentContext);
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
35
|
* Render the input view
|
|
@@ -106,7 +105,6 @@ const InputDropDown = ({
|
|
|
106
105
|
return (
|
|
107
106
|
<ComponentContext.Provider
|
|
108
107
|
value={{
|
|
109
|
-
...component,
|
|
110
108
|
componentName: 'InputDropDown',
|
|
111
109
|
params,
|
|
112
110
|
state: 'enabled',
|
package/Input/InputMoney.tsx
CHANGED
|
@@ -62,7 +62,6 @@ const InputMoney = forwardRef(
|
|
|
62
62
|
ref
|
|
63
63
|
) => {
|
|
64
64
|
const {theme} = useContext(ApplicationContext);
|
|
65
|
-
const component = useContext<any>(ComponentContext);
|
|
66
65
|
|
|
67
66
|
const [focused, setFocused] = useState(false);
|
|
68
67
|
const inputRef = useRef<TextInput>(null);
|
|
@@ -207,7 +206,6 @@ const InputMoney = forwardRef(
|
|
|
207
206
|
return (
|
|
208
207
|
<ComponentContext.Provider
|
|
209
208
|
value={{
|
|
210
|
-
...component,
|
|
211
209
|
componentName: 'InputMoney',
|
|
212
210
|
params,
|
|
213
211
|
state: inputState,
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -88,7 +88,6 @@ const InputOTP = forwardRef(
|
|
|
88
88
|
const [focused, setFocused] = useState(false);
|
|
89
89
|
const inputRef = useRef<TextInput | null>(null);
|
|
90
90
|
const {theme} = useContext(ApplicationContext);
|
|
91
|
-
const component = useContext<any>(ComponentContext);
|
|
92
91
|
|
|
93
92
|
useImperativeHandle(ref, () => ({
|
|
94
93
|
onChangeText: (text: string) => {
|
|
@@ -230,7 +229,6 @@ const InputOTP = forwardRef(
|
|
|
230
229
|
return (
|
|
231
230
|
<ComponentContext.Provider
|
|
232
231
|
value={{
|
|
233
|
-
...component,
|
|
234
232
|
componentName: 'InputOTP',
|
|
235
233
|
params,
|
|
236
234
|
state: 'enabled',
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -139,7 +139,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
139
139
|
ref
|
|
140
140
|
) => {
|
|
141
141
|
const {theme} = useContext(ApplicationContext);
|
|
142
|
-
const component = useContext<any>(ComponentContext);
|
|
143
142
|
|
|
144
143
|
const [focused, setFocused] = useState(false);
|
|
145
144
|
const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
|
|
@@ -272,7 +271,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
272
271
|
return (
|
|
273
272
|
<ComponentContext.Provider
|
|
274
273
|
value={{
|
|
275
|
-
...component,
|
|
276
274
|
componentName: 'InputSearch',
|
|
277
275
|
params,
|
|
278
276
|
state: inputState,
|
package/Title/index.tsx
CHANGED
|
@@ -10,6 +10,12 @@ import {TitleProps} from './types';
|
|
|
10
10
|
import {Typography} from '../Text/types';
|
|
11
11
|
|
|
12
12
|
const Title: FC<TitleProps> = ({
|
|
13
|
+
accessibilityLabelIcon,
|
|
14
|
+
accessibilityLabelTitle,
|
|
15
|
+
accessibilityLabelRightAction,
|
|
16
|
+
accessibilityLabelDescription,
|
|
17
|
+
accessibilityLabelBadge,
|
|
18
|
+
accessibilityLabelTrailingAction,
|
|
13
19
|
title = 'Title',
|
|
14
20
|
type = 'section',
|
|
15
21
|
size = 'medium',
|
|
@@ -56,7 +62,7 @@ const Title: FC<TitleProps> = ({
|
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
return (
|
|
59
|
-
<View style={[styles.iconView, iconStyle]}>
|
|
65
|
+
<View accessibilityLabel={accessibilityLabelIcon} style={[styles.iconView, iconStyle]}>
|
|
60
66
|
<Icon color={iconColor} source={icon} />
|
|
61
67
|
</View>
|
|
62
68
|
);
|
|
@@ -73,6 +79,7 @@ const Title: FC<TitleProps> = ({
|
|
|
73
79
|
}}
|
|
74
80
|
style={[styles.iconLeftView, flexStyle]}>
|
|
75
81
|
<RNText
|
|
82
|
+
accessibilityLabel={accessibilityLabelTitle}
|
|
76
83
|
numberOfLines={numberOfLines}
|
|
77
84
|
style={[
|
|
78
85
|
styleSheet[typography],
|
|
@@ -93,7 +100,9 @@ const Title: FC<TitleProps> = ({
|
|
|
93
100
|
}}
|
|
94
101
|
style={{
|
|
95
102
|
alignItems: 'center',
|
|
96
|
-
}}
|
|
103
|
+
}}
|
|
104
|
+
accessibilityLabel={accessibilityLabelBadge}
|
|
105
|
+
>
|
|
97
106
|
<Badge style={styles.badge} label={badgeLabel} />
|
|
98
107
|
</View>
|
|
99
108
|
)}
|
|
@@ -101,6 +110,7 @@ const Title: FC<TitleProps> = ({
|
|
|
101
110
|
</View>
|
|
102
111
|
{description && (
|
|
103
112
|
<Text
|
|
113
|
+
accessibilityLabel={accessibilityLabelDescription}
|
|
104
114
|
style={styles.description}
|
|
105
115
|
color={theme.colors.text.secondary}
|
|
106
116
|
typography={'description_default_regular'}>
|
|
@@ -114,6 +124,7 @@ const Title: FC<TitleProps> = ({
|
|
|
114
124
|
const renderActionLeft = () => {
|
|
115
125
|
return (
|
|
116
126
|
<TouchableOpacity
|
|
127
|
+
accessibilityLabel={accessibilityLabelTrailingAction}
|
|
117
128
|
onPress={onPressTrailingAction}
|
|
118
129
|
style={styles.iconLeftView}
|
|
119
130
|
hitSlop={{top: 10, bottom: 10, left: 50, right: 10}}>
|
|
@@ -157,7 +168,9 @@ const Title: FC<TitleProps> = ({
|
|
|
157
168
|
{
|
|
158
169
|
backgroundColor: theme.colors.primary + '0F',
|
|
159
170
|
},
|
|
160
|
-
]}
|
|
171
|
+
]}
|
|
172
|
+
accessibilityLabel={accessibilityLabelRightAction}
|
|
173
|
+
>
|
|
161
174
|
<Icon
|
|
162
175
|
source={'arrow_chevron_right_small'}
|
|
163
176
|
size={scaleSize(22)}
|
|
@@ -165,7 +178,7 @@ const Title: FC<TitleProps> = ({
|
|
|
165
178
|
/>
|
|
166
179
|
</TouchableOpacity>
|
|
167
180
|
) : (
|
|
168
|
-
<TouchableOpacity onPress={onPressRightAction}>
|
|
181
|
+
<TouchableOpacity onPress={onPressRightAction} accessibilityLabel={accessibilityLabelRightAction}>
|
|
169
182
|
<Text color={theme.colors.primary} typography={buttonTypo}>
|
|
170
183
|
{buttonTitle}
|
|
171
184
|
</Text>
|
|
@@ -179,6 +192,7 @@ const Title: FC<TitleProps> = ({
|
|
|
179
192
|
return (
|
|
180
193
|
<View style={isSection && styles.margin}>
|
|
181
194
|
<RNText
|
|
195
|
+
accessibilityLabel={accessibilityLabelTitle}
|
|
182
196
|
numberOfLines={numberOfLines}
|
|
183
197
|
style={[styleSheet[typography], styles.title]}>
|
|
184
198
|
{title}
|
package/Title/types.ts
CHANGED
|
@@ -17,4 +17,10 @@ export type TitleProps = {
|
|
|
17
17
|
onPressTrailingAction?: () => void;
|
|
18
18
|
textOnly?: boolean;
|
|
19
19
|
style?: ViewStyle;
|
|
20
|
+
accessibilityLabelTitle?: string;
|
|
21
|
+
accessibilityLabelRightAction?: string;
|
|
22
|
+
accessibilityLabelDescription?: string;
|
|
23
|
+
accessibilityLabelIcon?: string;
|
|
24
|
+
accessibilityLabelBadge?: string;
|
|
25
|
+
accessibilityLabelTrailingAction?: string;
|
|
20
26
|
};
|