@momo-kits/foundation 0.121.3 → 0.121.4-rc.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 +1 -1
- package/Application/BottomTab/index.tsx +53 -1
- package/Application/Components/HeaderRight.tsx +7 -4
- package/Application/ModalScreen.tsx +1 -1
- package/Application/NavigationContainer.tsx +0 -95
- package/Application/StackScreen.tsx +139 -6
- package/Application/index.ts +4 -0
- package/Application/types.ts +1 -0
- package/Application/utils.tsx +3 -2
- package/Layout/TrackingScope.tsx +18 -0
- package/Layout/index.ts +2 -0
- package/Skeleton/index.tsx +7 -3
- package/package.json +6 -6
- package/publish.sh +4 -1
- package/Application/Components/BottomSheetHelpCenter.tsx +0 -192
|
@@ -50,7 +50,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
50
50
|
useKeyboardAvoidingView = true,
|
|
51
51
|
useScrollOverflow = false,
|
|
52
52
|
keyboardVerticalOffset,
|
|
53
|
-
useDivider,
|
|
53
|
+
useDivider = true,
|
|
54
54
|
}: BottomSheetParams = props.route.params;
|
|
55
55
|
|
|
56
56
|
const customEasingOpen = Easing.bezier(0.05, 0.7, 0.1, 1);
|
|
@@ -7,7 +7,7 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
|
7
7
|
import {Colors} from '../../Consts';
|
|
8
8
|
import {Icon} from '../../Icon';
|
|
9
9
|
import {exportFontFamily, Text} from '../../Text';
|
|
10
|
-
import {ApplicationContext} from '../index';
|
|
10
|
+
import {ApplicationContext, MiniAppContext} from '../index';
|
|
11
11
|
import StackScreen from '../StackScreen';
|
|
12
12
|
import {BottomTabProps} from '../types';
|
|
13
13
|
import {getOptions, getStackOptions} from '../utils';
|
|
@@ -17,6 +17,8 @@ const Tab = createBottomTabNavigator();
|
|
|
17
17
|
const Stack = createStackNavigator();
|
|
18
18
|
|
|
19
19
|
const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
20
|
+
const {navigator} = useContext(ApplicationContext);
|
|
21
|
+
const context = useContext<any>(MiniAppContext);
|
|
20
22
|
const {nested, options, screen, initialParams} = route?.params;
|
|
21
23
|
const useAnimation = route?.params?.useAnimation ?? true;
|
|
22
24
|
|
|
@@ -24,6 +26,7 @@ const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
|
24
26
|
const scaleValue = 0.97;
|
|
25
27
|
const opacity = new Animated.Value(opacityValue);
|
|
26
28
|
const scale = new Animated.Value(scaleValue);
|
|
29
|
+
const screenName = screen?.name || screen?.type?.name || 'Invalid';
|
|
27
30
|
|
|
28
31
|
useFocusEffect(
|
|
29
32
|
React.useCallback(() => {
|
|
@@ -39,6 +42,14 @@ const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
|
39
42
|
useNativeDriver: true,
|
|
40
43
|
}),
|
|
41
44
|
]).start();
|
|
45
|
+
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
|
|
48
|
+
onScreenNavigated(data?.screenName, screenName);
|
|
49
|
+
navigator?.maxApi?.setObserver?.('current_screen', {screenName});
|
|
50
|
+
});
|
|
51
|
+
}, 100);
|
|
52
|
+
|
|
42
53
|
return () => {
|
|
43
54
|
if (navigation.getState().index !== route?.params.index) {
|
|
44
55
|
Animated.parallel([
|
|
@@ -58,6 +69,45 @@ const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
|
58
69
|
}, [navigation])
|
|
59
70
|
);
|
|
60
71
|
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
|
|
74
|
+
if (navigation.isFocused()) {
|
|
75
|
+
navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
|
|
76
|
+
onScreenNavigated(data?.screenName, screenName);
|
|
77
|
+
navigator?.maxApi?.setObserver?.('current_screen', {screenName});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
onFocusApp?.();
|
|
84
|
+
};
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const onScreenNavigated = (preScreenName: string, screenName: string) => {
|
|
88
|
+
const data: any = {
|
|
89
|
+
preScreenName,
|
|
90
|
+
screenName,
|
|
91
|
+
componentName: 'Screen',
|
|
92
|
+
state: 'navigated',
|
|
93
|
+
action: 'push',
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
context?.autoTracking?.({
|
|
97
|
+
...context,
|
|
98
|
+
...data,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* debug toast
|
|
103
|
+
*/
|
|
104
|
+
navigator?.maxApi?.showToastDebug?.({
|
|
105
|
+
appId: context.appId,
|
|
106
|
+
message: `${screenName} screen_navigated`,
|
|
107
|
+
type: 'ERROR',
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
61
111
|
let opacityStyle: undefined | Animated.Value = undefined;
|
|
62
112
|
let scaleStyle: number | Animated.Value = 1;
|
|
63
113
|
if (useAnimation) {
|
|
@@ -86,6 +136,7 @@ const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
|
86
136
|
screen,
|
|
87
137
|
options: stackOptions,
|
|
88
138
|
initialParams,
|
|
139
|
+
bottomTab: 'nested',
|
|
89
140
|
}}
|
|
90
141
|
options={{...getStackOptions()}}
|
|
91
142
|
/>
|
|
@@ -110,6 +161,7 @@ const TabScreen: React.FC<any> = ({route, navigation}) => {
|
|
|
110
161
|
screen,
|
|
111
162
|
options: stackOptions,
|
|
112
163
|
initialParams,
|
|
164
|
+
bottomTab: 'default',
|
|
113
165
|
}}
|
|
114
166
|
options={{...getStackOptions(), ...options}}
|
|
115
167
|
/>
|
|
@@ -55,6 +55,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
55
55
|
useShortcut = false,
|
|
56
56
|
useMore = false,
|
|
57
57
|
tools = [],
|
|
58
|
+
useCloseIcon = false,
|
|
58
59
|
}) => {
|
|
59
60
|
const {navigator} = useContext(ApplicationContext);
|
|
60
61
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -104,14 +105,14 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
104
105
|
);
|
|
105
106
|
|
|
106
107
|
const onDismiss = () => {
|
|
107
|
-
if (isWhiteList) {
|
|
108
|
-
navigator?.maxApi?.dispatchFunction?.('dismissAll');
|
|
109
|
-
} else {
|
|
108
|
+
if (useCloseIcon && !isWhiteList) {
|
|
110
109
|
navigator?.maxApi?.dispatchFunction?.(
|
|
111
110
|
'dismiss',
|
|
112
111
|
navigator?.dismissData,
|
|
113
112
|
undefined
|
|
114
113
|
);
|
|
114
|
+
} else {
|
|
115
|
+
navigator?.maxApi?.dispatchFunction?.('dismissAll');
|
|
115
116
|
}
|
|
116
117
|
};
|
|
117
118
|
|
|
@@ -299,7 +300,9 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
299
300
|
<Icon
|
|
300
301
|
color={tintColor}
|
|
301
302
|
source={
|
|
302
|
-
|
|
303
|
+
useCloseIcon && !isWhiteList
|
|
304
|
+
? '16_navigation_close_circle'
|
|
305
|
+
: '16_basic_home'
|
|
303
306
|
}
|
|
304
307
|
size={20}
|
|
305
308
|
/>
|
|
@@ -96,7 +96,7 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
96
96
|
style={StyleSheet.absoluteFillObject}>
|
|
97
97
|
<KeyboardAvoidingView
|
|
98
98
|
style={Styles.flexCenter}
|
|
99
|
-
behavior={Platform.OS === 'ios' ? 'padding' :
|
|
99
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
|
|
100
100
|
<Pressable
|
|
101
101
|
style={StyleSheet.absoluteFillObject}
|
|
102
102
|
onPress={() => onDismiss(undefined, barrierDismissible)}>
|
|
@@ -30,7 +30,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
30
30
|
}) => {
|
|
31
31
|
const context = useContext<any>(MiniAppContext);
|
|
32
32
|
const navigationRef = useRef<NavigationContainerRef>(null);
|
|
33
|
-
const routes = useRef<any>();
|
|
34
33
|
const isReady = useRef(false);
|
|
35
34
|
const navigator = useRef(new Navigator(navigationRef, isReady));
|
|
36
35
|
const [showGrid, setShowGrid] = useState(false);
|
|
@@ -74,49 +73,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
74
73
|
return localize?.translate(data as string);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
/**
|
|
78
|
-
* navigation state route change handle
|
|
79
|
-
* @param preScreenName
|
|
80
|
-
* @param screenName
|
|
81
|
-
* @param action
|
|
82
|
-
* @param type
|
|
83
|
-
*/
|
|
84
|
-
const onScreenNavigated = (
|
|
85
|
-
preScreenName: string,
|
|
86
|
-
screenName: string,
|
|
87
|
-
action: string,
|
|
88
|
-
type: 'Screen' | 'Modal' | 'BottomSheet'
|
|
89
|
-
) => {
|
|
90
|
-
let data: any = {
|
|
91
|
-
preScreenName,
|
|
92
|
-
screenName,
|
|
93
|
-
componentName: type,
|
|
94
|
-
state: 'navigated',
|
|
95
|
-
};
|
|
96
|
-
if (type !== 'Screen') {
|
|
97
|
-
data = {
|
|
98
|
-
preScreenName,
|
|
99
|
-
screenName,
|
|
100
|
-
componentName: type,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
context?.autoTracking?.({
|
|
105
|
-
...context,
|
|
106
|
-
...data,
|
|
107
|
-
action,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* debug toast
|
|
112
|
-
*/
|
|
113
|
-
maxApi?.showToastDebug?.({
|
|
114
|
-
appId: context.appId,
|
|
115
|
-
message: `${screenName} screen_navigated`,
|
|
116
|
-
type: 'ERROR',
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
|
|
120
76
|
navigator.current.setCurrentContext = setCurrentContext;
|
|
121
77
|
|
|
122
78
|
return (
|
|
@@ -158,57 +114,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
158
114
|
ref={navigationRef}
|
|
159
115
|
onReady={() => {
|
|
160
116
|
isReady.current = true;
|
|
161
|
-
routes.current = navigationRef.current?.getRootState?.()?.routes;
|
|
162
|
-
maxApi?.getDataObserver('CURRENT_SCREEN', (data: any) => {
|
|
163
|
-
onScreenNavigated(
|
|
164
|
-
data?.screenName,
|
|
165
|
-
screen?.name,
|
|
166
|
-
'push',
|
|
167
|
-
'Screen'
|
|
168
|
-
);
|
|
169
|
-
maxApi?.setObserver('CURRENT_SCREEN', {
|
|
170
|
-
screenName: screen?.name,
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
}}
|
|
174
|
-
onStateChange={state => {
|
|
175
|
-
const lastedRoute: any = state?.routes?.[state?.index];
|
|
176
|
-
const oldRoute: any =
|
|
177
|
-
routes.current?.[routes.current?.length - 1];
|
|
178
|
-
const lasted = lastedRoute?.params?.screen;
|
|
179
|
-
const previous = oldRoute?.params?.screen;
|
|
180
|
-
const preScreenName = previous?.name ?? previous?.type?.name;
|
|
181
|
-
const screenName = lasted?.name ?? lasted?.type?.name;
|
|
182
|
-
|
|
183
|
-
let action: string;
|
|
184
|
-
let type: 'Screen' | 'Modal' | 'BottomSheet';
|
|
185
|
-
|
|
186
|
-
if (lastedRoute?.name === 'Modal') {
|
|
187
|
-
type = 'Modal';
|
|
188
|
-
action = 'open';
|
|
189
|
-
if (lastedRoute?.params?.isBottomSheet) {
|
|
190
|
-
type = 'BottomSheet';
|
|
191
|
-
}
|
|
192
|
-
} else if (oldRoute?.name === 'Modal') {
|
|
193
|
-
action = 'close';
|
|
194
|
-
type = 'Modal';
|
|
195
|
-
if (oldRoute?.params?.isBottomSheet) {
|
|
196
|
-
type = 'BottomSheet';
|
|
197
|
-
}
|
|
198
|
-
} else {
|
|
199
|
-
type = 'Screen';
|
|
200
|
-
if (routes.current?.length > (state?.routes?.length ?? 0)) {
|
|
201
|
-
action = 'back';
|
|
202
|
-
} else {
|
|
203
|
-
action = 'push';
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
onScreenNavigated(preScreenName, screenName, action, type);
|
|
208
|
-
|
|
209
|
-
routes.current = state?.routes;
|
|
210
|
-
maxApi?.of?.({screenName});
|
|
211
|
-
maxApi?.setObserver('CURRENT_SCREEN', {screenName});
|
|
212
117
|
}}
|
|
213
118
|
independent={true}>
|
|
214
119
|
<Stack.Navigator initialRouteName="Stack" headerMode="screen">
|
|
@@ -17,24 +17,36 @@ const runAfterInteractions = InteractionManager.runAfterInteractions;
|
|
|
17
17
|
const StackScreen: React.FC<ScreenParams> = props => {
|
|
18
18
|
const {showGrid, navigator} = useContext(ApplicationContext);
|
|
19
19
|
const tracking = useRef<any>({
|
|
20
|
+
mounted: false,
|
|
20
21
|
timeoutLoad: undefined,
|
|
21
22
|
timeoutInteraction: undefined,
|
|
22
23
|
timeoutTracking: undefined,
|
|
24
|
+
timeoutLoading: undefined,
|
|
23
25
|
startTime: Date.now(),
|
|
24
26
|
endTime: Date.now(),
|
|
25
27
|
traceIdLoad: undefined,
|
|
26
28
|
traceIdInteraction: undefined,
|
|
27
29
|
releaseLoad: undefined,
|
|
28
30
|
releaseInteraction: undefined,
|
|
31
|
+
releaseUserInteraction: undefined,
|
|
32
|
+
releaseLoading: false,
|
|
29
33
|
timeLoad: 0,
|
|
30
34
|
timeInteraction: 0,
|
|
35
|
+
timeStartLoading: 0,
|
|
36
|
+
timeEndLoading: 0,
|
|
31
37
|
widgets: [],
|
|
32
38
|
params: undefined,
|
|
39
|
+
lastElement: undefined,
|
|
33
40
|
});
|
|
34
41
|
const widgets = useRef<any>([]);
|
|
35
42
|
const context = useContext<any>(MiniAppContext);
|
|
36
|
-
|
|
37
|
-
const {
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
screen: Component,
|
|
46
|
+
options,
|
|
47
|
+
initialParams,
|
|
48
|
+
bottomTab,
|
|
49
|
+
} = props.route.params;
|
|
38
50
|
const navigation = new Navigation(props.navigation, context);
|
|
39
51
|
const heightHeader = useHeaderHeight();
|
|
40
52
|
|
|
@@ -62,6 +74,8 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
62
74
|
* tracking for screen
|
|
63
75
|
*/
|
|
64
76
|
useEffect(() => {
|
|
77
|
+
let focusScreen: any;
|
|
78
|
+
let onFocusApp: any;
|
|
65
79
|
if (['Invalid', 'screen'].includes(screenName)) {
|
|
66
80
|
navigator?.maxApi?.showPopup?.('notice', {
|
|
67
81
|
title: 'Invalid screen name',
|
|
@@ -70,6 +84,26 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
70
84
|
});
|
|
71
85
|
}
|
|
72
86
|
|
|
87
|
+
if (!bottomTab) {
|
|
88
|
+
focusScreen = props.navigation.addListener('focus', () => {
|
|
89
|
+
navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
|
|
90
|
+
onScreenNavigated(data?.screenName, screenName);
|
|
91
|
+
navigator?.maxApi?.setObserver?.('current_screen', {screenName});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
|
|
95
|
+
if (props.navigation.isFocused()) {
|
|
96
|
+
navigator?.maxApi?.getDataObserver?.(
|
|
97
|
+
'current_screen',
|
|
98
|
+
(data: any) => {
|
|
99
|
+
onScreenNavigated(data?.screenName, screenName);
|
|
100
|
+
navigator?.maxApi?.setObserver?.('current_screen', {screenName});
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
navigator?.maxApi?.startTraceScreenLoad?.(
|
|
74
108
|
screenName,
|
|
75
109
|
context,
|
|
@@ -93,10 +127,41 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
93
127
|
return () => {
|
|
94
128
|
onScreenLoad();
|
|
95
129
|
onScreenInteraction();
|
|
130
|
+
clearTimeout(tracking.current.timeoutLoad);
|
|
131
|
+
clearTimeout(tracking.current.timeoutInteraction);
|
|
96
132
|
clearTimeout(tracking.current.timeoutTracking);
|
|
133
|
+
clearTimeout(tracking.current.timeoutLoading);
|
|
134
|
+
focusScreen?.();
|
|
135
|
+
onFocusApp?.();
|
|
97
136
|
};
|
|
98
137
|
}, []);
|
|
99
138
|
|
|
139
|
+
const onScreenNavigated = (preScreenName: string, screenName: string) => {
|
|
140
|
+
const data: any = {
|
|
141
|
+
preScreenName,
|
|
142
|
+
screenName,
|
|
143
|
+
componentName: 'Screen',
|
|
144
|
+
state: 'navigated',
|
|
145
|
+
action: tracking.current?.mounted ? 'back' : 'push',
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
context?.autoTracking?.({
|
|
149
|
+
...context,
|
|
150
|
+
...data,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
tracking.current.mounted = true;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* debug toast
|
|
157
|
+
*/
|
|
158
|
+
navigator?.maxApi?.showToastDebug?.({
|
|
159
|
+
appId: context.appId,
|
|
160
|
+
message: `${screenName} screen_navigated`,
|
|
161
|
+
type: 'ERROR',
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
100
165
|
/**
|
|
101
166
|
* tracking for screen load
|
|
102
167
|
*/
|
|
@@ -116,7 +181,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
116
181
|
widgets: tracking.current.widgets,
|
|
117
182
|
params: tracking.current.params,
|
|
118
183
|
version: version,
|
|
119
|
-
lastElement:
|
|
184
|
+
lastElement: tracking.current.lastElement,
|
|
120
185
|
});
|
|
121
186
|
navigator?.maxApi?.stopTrace?.(
|
|
122
187
|
tracking.current.traceIdLoad,
|
|
@@ -133,10 +198,13 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
133
198
|
message: `${screenName} screen_load_time ${timeLoad}`,
|
|
134
199
|
type: 'ERROR',
|
|
135
200
|
});
|
|
136
|
-
if (
|
|
201
|
+
if (
|
|
202
|
+
__DEV__ &&
|
|
203
|
+
tracking.current.lastElement?.children?.current?.length > 0
|
|
204
|
+
) {
|
|
137
205
|
Alert.alert(
|
|
138
206
|
`${screenName}- load ${timeLoad}ms`,
|
|
139
|
-
JSON.stringify(
|
|
207
|
+
JSON.stringify(tracking.current.lastElement?.children?.current)
|
|
140
208
|
);
|
|
141
209
|
}
|
|
142
210
|
}
|
|
@@ -183,6 +251,49 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
183
251
|
}
|
|
184
252
|
};
|
|
185
253
|
|
|
254
|
+
/**
|
|
255
|
+
* tracking for first interaction by user
|
|
256
|
+
*/
|
|
257
|
+
const onFirstInteraction = (action: string) => {
|
|
258
|
+
if (!tracking.current?.releaseUserInteraction) {
|
|
259
|
+
const timeLoad = tracking.current.endTime - tracking.current.startTime;
|
|
260
|
+
context?.autoTracking?.({
|
|
261
|
+
...context,
|
|
262
|
+
screenName,
|
|
263
|
+
componentName: 'Screen',
|
|
264
|
+
state: 'interaction',
|
|
265
|
+
duration: timeLoad,
|
|
266
|
+
action,
|
|
267
|
+
});
|
|
268
|
+
tracking.current.releaseUserInteraction = true;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* debug
|
|
272
|
+
*/
|
|
273
|
+
navigator?.maxApi?.showToastDebug?.({
|
|
274
|
+
appId: context.appId,
|
|
275
|
+
message: `${screenName} user_interaction ${timeLoad}`,
|
|
276
|
+
type: 'ERROR',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const onScreenLoading = () => {
|
|
282
|
+
if (!tracking.current?.releaseLoading) {
|
|
283
|
+
const timeLoad =
|
|
284
|
+
tracking.current.timeEndLoading - tracking.current.timeStartLoading;
|
|
285
|
+
context?.autoTracking?.({
|
|
286
|
+
...context,
|
|
287
|
+
screenName,
|
|
288
|
+
componentName: 'Screen',
|
|
289
|
+
state: 'loading',
|
|
290
|
+
duration: timeLoad,
|
|
291
|
+
loadingType: 'skeleton',
|
|
292
|
+
});
|
|
293
|
+
tracking.current.releaseLoading = true;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
186
297
|
return (
|
|
187
298
|
<ScreenContext.Provider
|
|
188
299
|
value={{
|
|
@@ -213,6 +324,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
213
324
|
}
|
|
214
325
|
return;
|
|
215
326
|
}
|
|
327
|
+
|
|
216
328
|
/**
|
|
217
329
|
* tracking for element screen load
|
|
218
330
|
*/
|
|
@@ -228,7 +340,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
228
340
|
* support for debug last element
|
|
229
341
|
*/
|
|
230
342
|
if (data?.componentName) {
|
|
231
|
-
|
|
343
|
+
tracking.current.lastElement = data;
|
|
232
344
|
}
|
|
233
345
|
|
|
234
346
|
/**
|
|
@@ -237,6 +349,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
237
349
|
if (data?.interaction) {
|
|
238
350
|
onScreenLoad();
|
|
239
351
|
onScreenInteraction();
|
|
352
|
+
onFirstInteraction(data?.action);
|
|
240
353
|
}
|
|
241
354
|
/**
|
|
242
355
|
* timeout for handle tracking screen
|
|
@@ -253,6 +366,26 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
253
366
|
onScreenInteraction();
|
|
254
367
|
}, 2000);
|
|
255
368
|
},
|
|
369
|
+
onLoading: (loading: boolean) => {
|
|
370
|
+
if (loading && tracking.current.timeStartLoading === 0) {
|
|
371
|
+
tracking.current.timeStartLoading = Date.now();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* tracking for loading screen
|
|
376
|
+
*/
|
|
377
|
+
clearTimeout(tracking.current.timeoutLoading);
|
|
378
|
+
if (!loading) {
|
|
379
|
+
tracking.current.timeEndLoading = Date.now();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* timeout for handle tracking screen
|
|
384
|
+
*/
|
|
385
|
+
tracking.current.timeoutLoading = setTimeout(() => {
|
|
386
|
+
onScreenLoading();
|
|
387
|
+
}, 2000);
|
|
388
|
+
},
|
|
256
389
|
onSetParams: (data: ScreenTrackingParams) => {
|
|
257
390
|
tracking.current.params = data;
|
|
258
391
|
},
|
package/Application/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
|
|
|
21
21
|
const ScreenContext = (Platform as any).ScreenContext ?? Context;
|
|
22
22
|
const ComponentContext = (Platform as any).ComponentContext ?? Context;
|
|
23
23
|
const SkeletonContext = createContext({loading: false});
|
|
24
|
+
const TrackingScopeContext = createContext<{scopeName?: string}>({
|
|
25
|
+
scopeName: undefined,
|
|
26
|
+
});
|
|
24
27
|
|
|
25
28
|
export {
|
|
26
29
|
ApplicationContext,
|
|
@@ -39,4 +42,5 @@ export {
|
|
|
39
42
|
HeaderAnimated,
|
|
40
43
|
setAutomationID,
|
|
41
44
|
useComponentId,
|
|
45
|
+
TrackingScopeContext,
|
|
42
46
|
};
|
package/Application/types.ts
CHANGED
package/Application/utils.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {HeaderTitleProps, NavigationOptions} from './types';
|
|
14
14
|
import {Colors} from '../Consts';
|
|
15
15
|
import {Animated, Platform} from 'react-native';
|
|
16
|
-
import {MiniAppContext, ScreenContext} from './index';
|
|
16
|
+
import {MiniAppContext, ScreenContext, TrackingScopeContext} from './index';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* default options for stack screen
|
|
@@ -173,12 +173,13 @@ const setAutomationID = (accessibilityLabel = '') => {
|
|
|
173
173
|
const useComponentId = (componentName: string, accessibilityLabel?: string) => {
|
|
174
174
|
const app = useContext<any>(MiniAppContext);
|
|
175
175
|
const screen = useContext<any>(ScreenContext);
|
|
176
|
+
const {scopeName} = useContext<any>(TrackingScopeContext);
|
|
176
177
|
|
|
177
178
|
const componentId = useMemo(() => {
|
|
178
179
|
if (accessibilityLabel) {
|
|
179
180
|
return accessibilityLabel;
|
|
180
181
|
}
|
|
181
|
-
return `${app.appId}/${app.code}/${screen.screenName}/${componentName}`;
|
|
182
|
+
return `${app.appId}/${app.code}/${screen.screenName}/${scopeName}/${componentName}`;
|
|
182
183
|
}, [componentName, accessibilityLabel, app, screen]);
|
|
183
184
|
|
|
184
185
|
return {componentId};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {TrackingScopeContext} from '../Application';
|
|
3
|
+
|
|
4
|
+
const TrackingScope = ({
|
|
5
|
+
scopeName,
|
|
6
|
+
children,
|
|
7
|
+
}: {
|
|
8
|
+
scopeName: string;
|
|
9
|
+
children: any;
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<TrackingScopeContext.Provider value={{scopeName}}>
|
|
13
|
+
{children}
|
|
14
|
+
</TrackingScopeContext.Provider>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export {TrackingScope};
|
package/Layout/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {GridContextProps} from './types';
|
|
|
7
7
|
import Item from './Item';
|
|
8
8
|
import ItemList from './ItemList';
|
|
9
9
|
import ItemSectionList from './ItemSectionList';
|
|
10
|
+
import {TrackingScope} from './TrackingScope';
|
|
10
11
|
|
|
11
12
|
const GridContext = createContext<GridContextProps>({
|
|
12
13
|
numberOfColumns: 12,
|
|
@@ -24,4 +25,5 @@ export {
|
|
|
24
25
|
Item,
|
|
25
26
|
ItemList,
|
|
26
27
|
ItemSectionList,
|
|
28
|
+
TrackingScope,
|
|
27
29
|
};
|
package/Skeleton/index.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useEffect, useMemo, useRef, useState} from 'react';
|
|
1
|
+
import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Easing,
|
|
@@ -12,9 +12,10 @@ import LinearGradient from 'react-native-linear-gradient';
|
|
|
12
12
|
import {SkeletonTypes} from './types';
|
|
13
13
|
import {Colors, Styles} from '../Consts';
|
|
14
14
|
import styles from './styles';
|
|
15
|
-
import {SkeletonContext} from '../Application';
|
|
15
|
+
import {ScreenContext, SkeletonContext} from '../Application';
|
|
16
16
|
|
|
17
17
|
const Skeleton: React.FC<SkeletonTypes> = ({style}) => {
|
|
18
|
+
const screen = useContext<any>(ScreenContext);
|
|
18
19
|
const [width, setWidth] = useState(0);
|
|
19
20
|
const PRIMARY_COLOR = Colors.black_05;
|
|
20
21
|
const HIGHLIGHT_COLOR1 = Colors.black_05;
|
|
@@ -33,14 +34,16 @@ const Skeleton: React.FC<SkeletonTypes> = ({style}) => {
|
|
|
33
34
|
duration: 1000,
|
|
34
35
|
easing: Easing.linear,
|
|
35
36
|
useNativeDriver: Platform.OS !== 'web',
|
|
36
|
-
})
|
|
37
|
+
})
|
|
37
38
|
);
|
|
38
39
|
}, [beginShimmerPosition]);
|
|
39
40
|
|
|
40
41
|
useEffect(() => {
|
|
41
42
|
animatedValue.start();
|
|
43
|
+
screen?.onLoading?.(true);
|
|
42
44
|
return () => {
|
|
43
45
|
animatedValue.stop();
|
|
46
|
+
screen?.onLoading?.(false);
|
|
44
47
|
};
|
|
45
48
|
}, [animatedValue]);
|
|
46
49
|
|
|
@@ -49,6 +52,7 @@ const Skeleton: React.FC<SkeletonTypes> = ({style}) => {
|
|
|
49
52
|
setWidth(newWidth);
|
|
50
53
|
}
|
|
51
54
|
};
|
|
55
|
+
|
|
52
56
|
return (
|
|
53
57
|
<View style={[styles.container, style]}>
|
|
54
58
|
<View
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/foundation",
|
|
3
|
-
"version": "0.121.
|
|
3
|
+
"version": "0.121.4-rc.1",
|
|
4
4
|
"minimumDeployTarget": 32,
|
|
5
|
-
"deploymentTarget":
|
|
5
|
+
"deploymentTarget": 121,
|
|
6
6
|
"description": "React Native Component Kits",
|
|
7
7
|
"main": "index.ts",
|
|
8
8
|
"scripts": {},
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
"react-native-linear-gradient": "2.8.3",
|
|
17
17
|
"react-native-gesture-handler": "1.10.3",
|
|
18
18
|
"react-native-fast-image": "8.1.5",
|
|
19
|
+
"@react-navigation/bottom-tabs": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-bottom-tabs.git",
|
|
19
20
|
"@react-navigation/core": "5.16.1",
|
|
20
21
|
"@react-navigation/native": "5.9.8",
|
|
21
22
|
"@react-navigation/routers": "5.7.4",
|
|
22
|
-
"react-native-reanimated": "
|
|
23
|
-
"lottie-react-native": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/momo-lottie-react-native.git",
|
|
24
|
-
"@react-navigation/stack": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-navigation-stack.git"
|
|
25
|
-
"@react-navigation/bottom-tabs": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-bottom-tabs.git"
|
|
23
|
+
"react-native-reanimated": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-reanimated.git#v1.13.4_gradle_7",
|
|
24
|
+
"lottie-react-native": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/momo-lottie-react-native.git#test_lottie_ios",
|
|
25
|
+
"@react-navigation/stack": "https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-navigation-stack.git"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react-native": "*"
|
package/publish.sh
CHANGED
|
@@ -9,10 +9,13 @@ elif [ "$1" == "latest" ]; then
|
|
|
9
9
|
npm version $(npm view @momo-kits/foundation@latest version)
|
|
10
10
|
npm version prerelease --preid=rc
|
|
11
11
|
npm publish --tag latest --access=public
|
|
12
|
-
|
|
12
|
+
elif [ "$1" == "beta" ]; then
|
|
13
13
|
npm version $(npm view @momo-kits/foundation@beta version)
|
|
14
14
|
npm version prerelease --preid=beta
|
|
15
15
|
npm publish --tag beta --access=public
|
|
16
|
+
else
|
|
17
|
+
npm publish --tag alpha --access=public
|
|
16
18
|
fi
|
|
19
|
+
|
|
17
20
|
PACKAGE_NAME=$(npm pkg get name)
|
|
18
21
|
NEW_PACKAGE_VERSION=$(npm pkg get version)
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import React, {useContext} from 'react';
|
|
2
|
-
import {StyleSheet, TouchableOpacity, View} from 'react-native';
|
|
3
|
-
import {ApplicationContext, MiniAppContext} from '../index';
|
|
4
|
-
import {Colors, Radius, Spacing, Styles} from '../../Consts';
|
|
5
|
-
import {Text} from '../../Text';
|
|
6
|
-
import {Icon} from '../../Icon';
|
|
7
|
-
|
|
8
|
-
const ServiceItem: React.FC<any> = ({service}) => {
|
|
9
|
-
const {theme, translate} = useContext(ApplicationContext);
|
|
10
|
-
const {title, description, icon, onPress} = service;
|
|
11
|
-
const serviceTitle = translate?.(title);
|
|
12
|
-
const serviceDescription = translate?.(description);
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<TouchableOpacity onPress={onPress} style={Styles.row}>
|
|
16
|
-
<View style={styles.iconWrapper}>
|
|
17
|
-
<Icon color={theme.colors.text.hint} source={icon} size={28} />
|
|
18
|
-
</View>
|
|
19
|
-
<View>
|
|
20
|
-
<View style={Styles.row}>
|
|
21
|
-
<Text typography={'action_xs_bold'}>{serviceTitle}</Text>
|
|
22
|
-
<Icon source={'arrow_chevron_right_small'} size={16} />
|
|
23
|
-
</View>
|
|
24
|
-
<Text
|
|
25
|
-
typography={'description_xs_regular'}
|
|
26
|
-
color={theme.colors.text.hint}>
|
|
27
|
-
{serviceDescription}
|
|
28
|
-
</Text>
|
|
29
|
-
</View>
|
|
30
|
-
</TouchableOpacity>
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const BottomSheetHelpCenter: React.FC<any> = ({onRequestClose}) => {
|
|
35
|
-
const {theme, navigator} = useContext(ApplicationContext);
|
|
36
|
-
const context = useContext<any>(MiniAppContext);
|
|
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
|
-
|
|
53
|
-
onRequestClose?.(() => {
|
|
54
|
-
navigator?.maxApi?.startFeatureCode?.(
|
|
55
|
-
'helpcenter_problemlevel1',
|
|
56
|
-
context?.toolkitConfig?.faq
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
|
|
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
|
-
|
|
76
|
-
onRequestClose?.(() => {
|
|
77
|
-
navigator?.maxApi?.getDataObserver('CURRENT_SCREEN', (data: any) => {
|
|
78
|
-
let screenName = data?.screenName;
|
|
79
|
-
navigator?.maxApi?.startFeatureCode?.('chatbot', {
|
|
80
|
-
botId: 'botGptCs',
|
|
81
|
-
forwardParams: {
|
|
82
|
-
forService: 'navigation',
|
|
83
|
-
mini_app_id: context?.appId,
|
|
84
|
-
feature_code: context?.code,
|
|
85
|
-
screen_name: screenName,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
|
|
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
|
-
|
|
107
|
-
onRequestClose?.(() => {
|
|
108
|
-
navigator?.maxApi?.startFeatureCode?.('feedback', {
|
|
109
|
-
forService: 'navigation',
|
|
110
|
-
loggedStatus: true,
|
|
111
|
-
application: {
|
|
112
|
-
appId: context?.appId,
|
|
113
|
-
appCode: context?.code,
|
|
114
|
-
appName: context?.name?.['en'],
|
|
115
|
-
buildNumber: context?.buildNumber,
|
|
116
|
-
},
|
|
117
|
-
newUi: true,
|
|
118
|
-
stepFeedback: 1,
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const services = [
|
|
124
|
-
{
|
|
125
|
-
title: {vi: 'Câu hỏi thường gặp', en: 'Câu hỏi thường gặp'},
|
|
126
|
-
description: {
|
|
127
|
-
vi: 'Giải đáp các thắc mắc mọi người thường gặp',
|
|
128
|
-
en: 'Giải đáp các thắc mắc mọi người thường gặp',
|
|
129
|
-
},
|
|
130
|
-
icon: 'notifications_circle_question',
|
|
131
|
-
onPress: onPressFaq,
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
title: {vi: 'Hỗ trợ trực tuyến', en: 'Hỗ trợ trực tuyến'},
|
|
135
|
-
description: {
|
|
136
|
-
vi: 'Trả lời mọi câu hỏi của bạn 24/7',
|
|
137
|
-
en: 'Trả lời mọi câu hỏi của bạn 24/7',
|
|
138
|
-
},
|
|
139
|
-
icon: 'ic_support',
|
|
140
|
-
onPress: onPressChatbot,
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
title: {vi: 'Chia sẻ góp ý', en: 'Chia sẻ góp ý'},
|
|
144
|
-
description: {
|
|
145
|
-
vi: 'Đề xuất cải thiện hoặc báo lỗi sản phẩm/dịch vụ',
|
|
146
|
-
en: 'Đề xuất cải thiện hoặc báo lỗi sản phẩm/dịch vụ',
|
|
147
|
-
},
|
|
148
|
-
icon: 'file_mail',
|
|
149
|
-
onPress: onPressFeedback,
|
|
150
|
-
},
|
|
151
|
-
];
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<View
|
|
155
|
-
style={[
|
|
156
|
-
styles.container,
|
|
157
|
-
{
|
|
158
|
-
backgroundColor: theme.colors.background.surface,
|
|
159
|
-
},
|
|
160
|
-
]}>
|
|
161
|
-
{services.map((item, index) => {
|
|
162
|
-
return (
|
|
163
|
-
<>
|
|
164
|
-
<ServiceItem service={item} />
|
|
165
|
-
{index !== services.length - 1 && <View style={styles.divider} />}
|
|
166
|
-
</>
|
|
167
|
-
);
|
|
168
|
-
})}
|
|
169
|
-
</View>
|
|
170
|
-
);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const styles = StyleSheet.create({
|
|
174
|
-
container: {height: 300, width: '100%', padding: Spacing.M},
|
|
175
|
-
divider: {
|
|
176
|
-
marginVertical: Spacing.M,
|
|
177
|
-
backgroundColor: Colors.black_02,
|
|
178
|
-
height: 1,
|
|
179
|
-
width: '100%',
|
|
180
|
-
},
|
|
181
|
-
iconWrapper: {
|
|
182
|
-
width: 36,
|
|
183
|
-
height: 36,
|
|
184
|
-
backgroundColor: Colors.black_02,
|
|
185
|
-
borderRadius: Radius.M,
|
|
186
|
-
marginRight: Spacing.S,
|
|
187
|
-
alignItems: 'center',
|
|
188
|
-
justifyContent: 'center',
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
export {BottomSheetHelpCenter};
|