@momo-kits/foundation 0.75.1-beta.9 → 0.75.2-beta.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.
Files changed (112) hide show
  1. package/Application/BottomTab/BottomTabBar.tsx +478 -0
  2. package/Application/BottomTab/index.tsx +134 -0
  3. package/Application/Components.tsx +552 -0
  4. package/Application/ModalScreen.tsx +244 -0
  5. package/Application/Navigation.ts +50 -0
  6. package/Application/NavigationContainer.tsx +128 -0
  7. package/Application/Navigator.ts +151 -0
  8. package/Application/StackScreen.tsx +146 -0
  9. package/Application/index.ts +37 -0
  10. package/{Navigation → Application}/types.ts +96 -27
  11. package/Application/utils.tsx +175 -0
  12. package/Assets/DotAnimation.json +256 -0
  13. package/Assets/SpinnerAnimation.json +1027 -0
  14. package/Assets/icon.json +89 -5
  15. package/Assets/icon_bank.json +506 -0
  16. package/Assets/language.json +202 -0
  17. package/Badge/Badge.tsx +56 -0
  18. package/Badge/BadgeDot.tsx +11 -0
  19. package/Badge/BadgeRibbon.tsx +125 -0
  20. package/Badge/Shape.tsx +29 -0
  21. package/Badge/index.tsx +7 -0
  22. package/Badge/styles.ts +58 -0
  23. package/Badge/types.ts +63 -0
  24. package/Button/index.tsx +90 -39
  25. package/Button/styles.ts +19 -0
  26. package/CheckBox/index.tsx +58 -43
  27. package/CheckBox/styles.ts +1 -1
  28. package/CheckBox/types.ts +6 -4
  29. package/Consts/colors+spacing+radius.ts +18 -0
  30. package/Consts/styles.ts +0 -26
  31. package/Consts/theme.ts +5 -1
  32. package/Divider/index.tsx +3 -1
  33. package/Icon/index.tsx +14 -10
  34. package/Icon/types.ts +4 -3
  35. package/IconButton/index.tsx +24 -15
  36. package/Image/index.tsx +63 -45
  37. package/Image/styles.ts +1 -9
  38. package/Image/types.ts +5 -1
  39. package/Input/Input.tsx +227 -115
  40. package/Input/InputDropDown.tsx +132 -0
  41. package/Input/InputMoney.tsx +174 -110
  42. package/Input/InputOTP.tsx +257 -0
  43. package/Input/InputSearch.tsx +160 -130
  44. package/Input/InputTextArea.tsx +149 -121
  45. package/Input/common.tsx +43 -18
  46. package/Input/index.tsx +205 -17
  47. package/Input/styles.ts +44 -7
  48. package/Input/utils.ts +2 -2
  49. package/Layout/Card.tsx +76 -37
  50. package/Layout/FloatingButton.tsx +128 -0
  51. package/Layout/GridSystem.tsx +35 -23
  52. package/Layout/Item.tsx +29 -33
  53. package/Layout/ItemList.tsx +55 -0
  54. package/Layout/ItemSectionList.tsx +39 -0
  55. package/Layout/Screen.tsx +378 -0
  56. package/Layout/Section.tsx +73 -35
  57. package/Layout/index.ts +13 -3
  58. package/Layout/styles.ts +101 -0
  59. package/Layout/types.ts +1 -37
  60. package/Layout/utils.ts +38 -43
  61. package/Loader/DotLoader.tsx +37 -0
  62. package/Loader/Loader.tsx +13 -0
  63. package/Loader/ProgressBar.tsx +44 -0
  64. package/Loader/Spinner.tsx +29 -0
  65. package/Loader/index.tsx +4 -0
  66. package/Loader/styles.ts +10 -0
  67. package/Loader/types.ts +35 -0
  68. package/Loader/utils.ts +25 -0
  69. package/Pagination/Dot.tsx +1 -1
  70. package/Pagination/PaginationDot.tsx +8 -3
  71. package/Pagination/PaginationNumber.tsx +8 -7
  72. package/Pagination/PaginationScroll.tsx +1 -1
  73. package/Pagination/PaginationWhiteDot.tsx +8 -5
  74. package/Pagination/styles.ts +3 -0
  75. package/Pagination/types.ts +2 -0
  76. package/Popup/PopupNotify.tsx +102 -44
  77. package/Popup/PopupPromotion.tsx +63 -22
  78. package/Popup/types.ts +15 -4
  79. package/Radio/index.tsx +35 -21
  80. package/Radio/types.ts +7 -5
  81. package/Skeleton/index.tsx +1 -1
  82. package/Skeleton/types.ts +3 -0
  83. package/Switch/index.tsx +35 -24
  84. package/Switch/types.ts +5 -3
  85. package/Tag/index.tsx +1 -1
  86. package/Text/index.tsx +32 -26
  87. package/Text/styles.ts +38 -0
  88. package/Text/types.ts +25 -13
  89. package/Text/utils.ts +14 -0
  90. package/Title/index.tsx +186 -0
  91. package/Title/styles.ts +79 -0
  92. package/Title/types.ts +20 -0
  93. package/code-scanner.js +143 -0
  94. package/index.ts +17 -3
  95. package/migrate_typo.js +56 -0
  96. package/package.json +44 -39
  97. package/publish.sh +2 -5
  98. package/tsconfig.json +24 -0
  99. package/verify.js +47 -0
  100. package/Assets/header-background.png +0 -0
  101. package/Layout/ScreenContainer.tsx +0 -121
  102. package/Navigation/BottomTab.tsx +0 -39
  103. package/Navigation/Components.tsx +0 -365
  104. package/Navigation/ModalScreen.tsx +0 -226
  105. package/Navigation/Navigation.ts +0 -44
  106. package/Navigation/NavigationButton.tsx +0 -54
  107. package/Navigation/NavigationContainer.tsx +0 -77
  108. package/Navigation/Navigator.ts +0 -82
  109. package/Navigation/Observer.ts +0 -79
  110. package/Navigation/StackScreen.tsx +0 -27
  111. package/Navigation/index.ts +0 -14
  112. package/Navigation/utils.tsx +0 -137
@@ -0,0 +1,244 @@
1
+ import React, {useContext, useEffect, useRef} from 'react';
2
+ import {
3
+ KeyboardAvoidingView,
4
+ Platform,
5
+ Pressable,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+ import {Modalize} from 'react-native-modalize';
11
+ import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
12
+
13
+ import {ApplicationContext} from './index';
14
+ import {Radius, Spacing, Styles} from '../Consts';
15
+ import {Icon} from '../Icon';
16
+ import {Text} from '../Text';
17
+ import Navigation from './Navigation';
18
+ import {BottomSheetParams, ModalParams} from './types';
19
+
20
+ const ModalScreen: React.FC<any> = props => {
21
+ /**
22
+ * dismiss callback when modal | bottom sheet dismissed
23
+ */
24
+ useEffect(() => {
25
+ return () => {
26
+ props.route.params?.onDismiss?.();
27
+ };
28
+ }, []);
29
+
30
+ if (props.route?.params?.isBottomSheet) {
31
+ return <BottomSheet {...props} />;
32
+ }
33
+ return <Modal {...props} />;
34
+ };
35
+
36
+ const Modal: React.FC<ModalParams> = props => {
37
+ const {theme, navigator} = useContext(ApplicationContext);
38
+ const {screen, barrierDismissible} = props.route.params;
39
+ const Component = useRef(screen).current;
40
+ const navigation = new Navigation(props.navigation, theme);
41
+ const params = {
42
+ ...props.route.params,
43
+ navigation,
44
+ };
45
+
46
+ delete params.screen;
47
+
48
+ const onDismiss = () => {
49
+ if (barrierDismissible) {
50
+ return;
51
+ }
52
+ navigator?.pop();
53
+ };
54
+
55
+ return (
56
+ <KeyboardAvoidingView
57
+ style={Styles.flex}
58
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
59
+ <View style={styles.container}>
60
+ <Pressable
61
+ style={styles.modalSpaceHorizontal}
62
+ onPress={() => onDismiss()}
63
+ />
64
+ <View style={styles.modalCenterStage}>
65
+ <Pressable
66
+ style={styles.modalSpaceVertical}
67
+ onPress={() => onDismiss()}
68
+ />
69
+ <View style={styles.modalContent}>
70
+ <Component {...params} />
71
+ </View>
72
+ <Pressable
73
+ style={styles.modalSpaceVertical}
74
+ onPress={() => onDismiss()}
75
+ />
76
+ </View>
77
+ <Pressable
78
+ style={styles.modalSpaceHorizontal}
79
+ onPress={() => onDismiss()}
80
+ />
81
+ </View>
82
+ </KeyboardAvoidingView>
83
+ );
84
+ };
85
+
86
+ const BottomSheet: React.FC<BottomSheetParams> = props => {
87
+ const {theme, navigator} = useContext(ApplicationContext);
88
+ const {
89
+ screen: Screen,
90
+ options,
91
+ footerComponent,
92
+ flatListProps,
93
+ }: BottomSheetParams = props.route.params;
94
+
95
+ const navigation = new Navigation(props.navigation, theme);
96
+
97
+ const params = {
98
+ ...props.route.params,
99
+ navigation,
100
+ };
101
+
102
+ delete params.screen;
103
+ const modalizeRef = useRef<Modalize>(null);
104
+
105
+ useEffect(() => {
106
+ modalizeRef.current?.open();
107
+ }, []);
108
+
109
+ const handleClose = () => {
110
+ modalizeRef?.current?.close();
111
+ };
112
+
113
+ return (
114
+ <SafeAreaView style={Styles.flex}>
115
+ <Modalize
116
+ keyboardAvoidingOffset={-20}
117
+ adjustToContentHeight
118
+ closeOnOverlayTap
119
+ closeSnapPointStraightEnabled
120
+ onClosed={() => {
121
+ navigator?.pop();
122
+ }}
123
+ ref={modalizeRef}
124
+ withHandle={false}
125
+ flatListProps={flatListProps}
126
+ HeaderComponent={headerDefault(
127
+ options?.title ?? options?.customTitle?.title ?? '',
128
+ handleClose,
129
+ )}
130
+ FooterComponent={footerDefault(footerComponent)}>
131
+ <View style={styles.bottomSheetContainer}>
132
+ <Screen {...props} {...params} />
133
+ </View>
134
+ </Modalize>
135
+ </SafeAreaView>
136
+ );
137
+ };
138
+
139
+ const headerDefault = (title: string, onClose: () => void) => {
140
+ return (
141
+ <View style={styles.header}>
142
+ <Text
143
+ style={styles.textOption}
144
+ numberOfLines={2}
145
+ typography="header_default_bold">
146
+ {title ?? ''}
147
+ </Text>
148
+ <TouchableOpacity style={styles.buttonClose} onPress={onClose}>
149
+ <Icon source="navigation_close" />
150
+ </TouchableOpacity>
151
+ </View>
152
+ );
153
+ };
154
+
155
+ const footerDefault = (footerComponent: React.ReactNode) => {
156
+ const insets = useSafeAreaInsets();
157
+ if (!footerComponent) return;
158
+ return (
159
+ <View
160
+ style={{
161
+ marginBottom: Math.min(insets.bottom, 21) + Spacing.S,
162
+ paddingHorizontal: 12,
163
+ }}>
164
+ {footerComponent}
165
+ </View>
166
+ );
167
+ };
168
+
169
+ const styles = StyleSheet.create({
170
+ container: {flex: 1, flexDirection: 'row'},
171
+ modalContent: {
172
+ maxWidth: '100%',
173
+ maxHeight: '80%',
174
+ },
175
+ modalCenterStage: {
176
+ maxWidth: '90%',
177
+ },
178
+ modalSpaceHorizontal: {
179
+ flex: 1,
180
+ minWidth: '5%',
181
+ },
182
+ modalSpaceVertical: {
183
+ flex: 1,
184
+ minHeight: '10%',
185
+ },
186
+ sheetContainer: {
187
+ height: '100%',
188
+ width: '100%',
189
+ borderTopLeftRadius: Radius.M,
190
+ borderTopRightRadius: Radius.M,
191
+ overflow: 'hidden',
192
+ },
193
+ indicatorContainer: {
194
+ width: '100%',
195
+ paddingTop: Spacing.S,
196
+ paddingBottom: Spacing.XS,
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ },
200
+ indicator: {
201
+ width: 40,
202
+ height: 4,
203
+ borderRadius: Radius.S,
204
+ },
205
+ bottomSheetContainer: {
206
+ width: '100%',
207
+ backgroundColor: 'white',
208
+ borderTopRightRadius: 16,
209
+ borderTopLeftRadius: 16,
210
+ },
211
+ header: {
212
+ height: 50,
213
+ width: '100%',
214
+ alignItems: 'center',
215
+ justifyContent: 'center',
216
+ },
217
+ textOption: {
218
+ fontSize: 16,
219
+ color: '#303233',
220
+ textAlign: 'center',
221
+ fontWeight: 'bold',
222
+ paddingHorizontal: 60,
223
+ },
224
+ imageOption: {
225
+ height: 1,
226
+ backgroundColor: '#ebebeb',
227
+ width: '100%',
228
+ position: 'absolute',
229
+ bottom: 1,
230
+ },
231
+ buttonClose: {
232
+ padding: 12,
233
+ position: 'absolute',
234
+ right: 0,
235
+ },
236
+ icon: {
237
+ width: 24,
238
+ height: 24,
239
+ tintColor: '#303233',
240
+ backgroundColor: 'red',
241
+ },
242
+ });
243
+
244
+ export default ModalScreen;
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import {NavigationProp} from '@react-navigation/native';
3
+ import {NavigationOptions, Theme} from './types';
4
+ import {HeaderRightAction} from './index';
5
+ import {getOptions} from './utils';
6
+ import {HeaderToolkitAction} from './Components';
7
+
8
+ class Navigation {
9
+ instance: NavigationProp<any>;
10
+ readonly theme: Theme;
11
+
12
+ constructor(instance: any, theme: Theme) {
13
+ this.instance = instance;
14
+ this.theme = theme;
15
+ }
16
+
17
+ /**
18
+ * navigation options filter by design system rule
19
+ * @param params
20
+ */
21
+ private filterParams = (params: NavigationOptions) => {
22
+ if (params.headerRight) {
23
+ const headerRight = params.headerRight?.({}) as React.ReactElement;
24
+ const type = headerRight?.type;
25
+ if (type !== HeaderRightAction && type !== HeaderToolkitAction) {
26
+ console.warn(
27
+ 'headerRight contains element type of HeaderRightAction | HeaderToolkitAction, Please check again.',
28
+ );
29
+ }
30
+ }
31
+
32
+ if (params.headerLeft) {
33
+ console.warn('headerLeft not allow override render by design system!');
34
+ delete params.headerLeft;
35
+ }
36
+
37
+ return params;
38
+ };
39
+
40
+ /**
41
+ * navigation setOptions support props NavigationOptions base on react-navigation options
42
+ * @param params
43
+ */
44
+ setOptions = (params: NavigationOptions) => {
45
+ params = getOptions(this.filterParams(params), this.theme);
46
+ this.instance.setOptions(params);
47
+ };
48
+ }
49
+
50
+ export default Navigation;
@@ -0,0 +1,128 @@
1
+ import React, {useEffect, useRef, useState} from 'react';
2
+ import {SafeAreaProvider} from 'react-native-safe-area-context';
3
+ import {
4
+ NavigationContainer as ReactNavigationContainer,
5
+ NavigationContainerRef,
6
+ } from '@react-navigation/native';
7
+ import {BottomSheetModalProvider} from '@gorhom/bottom-sheet';
8
+ import {createStackNavigator} from '@react-navigation/stack';
9
+ import {DeviceEventEmitter} from 'react-native';
10
+ import StackScreen from './StackScreen';
11
+ import ModalScreen from './ModalScreen';
12
+ import Navigator from './Navigator';
13
+ import {getDialogOptions, getModalOptions, getStackOptions} from './utils';
14
+ import {LocalizationObject, NavigationContainerProps} from './types';
15
+ import {ApplicationContext} from './index';
16
+ import kitLocalization from '../Assets/language.json';
17
+
18
+ /**
19
+ * link for deprecated
20
+ */
21
+
22
+ console.log(typeof BottomSheetModalProvider);
23
+
24
+ const Stack = createStackNavigator();
25
+
26
+ const NavigationContainer: React.FC<NavigationContainerProps> = ({
27
+ screen,
28
+ theme,
29
+ options,
30
+ maxApi,
31
+ localization,
32
+ initialParams,
33
+ }) => {
34
+ const navigationRef = useRef<NavigationContainerRef>(null);
35
+ const isReady = useRef(false);
36
+ const navigator = useRef(new Navigator(navigationRef, isReady));
37
+ const [showGrid, setShowGrid] = useState(false);
38
+ const config = useRef<any>();
39
+
40
+ /**
41
+ * handle mini language & listen change
42
+ * engine only shake to enable grid view
43
+ */
44
+ useEffect(() => {
45
+ const subscription = DeviceEventEmitter.addListener(
46
+ 'onChangeGrid',
47
+ enable => {
48
+ setShowGrid(!!enable);
49
+ },
50
+ );
51
+
52
+ return () => {
53
+ subscription?.remove?.();
54
+ };
55
+ }, []);
56
+
57
+ /**
58
+ * inject data for navigator
59
+ */
60
+ navigator.current.maxApi = maxApi;
61
+
62
+ /**
63
+ * translate
64
+ * @param key
65
+ */
66
+ const translate = (key: string) => {
67
+ const language: 'vi' | 'en' = 'vi';
68
+ const mergedLocalization: LocalizationObject = {
69
+ vi: {...localization?.vi, ...kitLocalization.vi},
70
+ en: {...localization?.en, ...kitLocalization.en},
71
+ };
72
+ return mergedLocalization?.[language]?.[key] || key;
73
+ };
74
+
75
+ return (
76
+ <SafeAreaProvider>
77
+ <ApplicationContext.Provider
78
+ value={{
79
+ navigator: navigator.current,
80
+ config: config.current,
81
+ theme,
82
+ showGrid,
83
+ translate,
84
+ }}>
85
+ <ReactNavigationContainer
86
+ theme={{
87
+ ...theme,
88
+ colors: {
89
+ ...theme.colors,
90
+ background: theme.colors.background.default,
91
+ card: theme.colors.background.surface,
92
+ text: theme.colors.text.default,
93
+ border: theme.colors.border.default,
94
+ notification: theme.colors.error.primary,
95
+ },
96
+ }}
97
+ ref={navigator.current?.ref}
98
+ onReady={() => {
99
+ isReady.current = true;
100
+ }}
101
+ independent={true}>
102
+ <Stack.Navigator initialRouteName="Stack" headerMode="screen">
103
+ <Stack.Screen
104
+ name="Stack"
105
+ component={StackScreen}
106
+ initialParams={{screen, initialParams}}
107
+ options={{...getStackOptions(), ...options}}
108
+ />
109
+ <Stack.Screen
110
+ name="Dialog"
111
+ component={StackScreen}
112
+ options={getDialogOptions()}
113
+ initialParams={{screen}}
114
+ />
115
+ <Stack.Screen
116
+ name="Modal"
117
+ component={ModalScreen}
118
+ options={getModalOptions()}
119
+ initialParams={{screen}}
120
+ />
121
+ </Stack.Navigator>
122
+ </ReactNavigationContainer>
123
+ </ApplicationContext.Provider>
124
+ </SafeAreaProvider>
125
+ );
126
+ };
127
+
128
+ export {ApplicationContext, NavigationContainer};
@@ -0,0 +1,151 @@
1
+ import {CommonActions, StackActions} from '@react-navigation/native';
2
+ import {
3
+ BottomSheetParams,
4
+ HeaderToolkitProps,
5
+ ModalParams,
6
+ ScreenParams,
7
+ } from './types';
8
+
9
+ class Navigator {
10
+ ref?: any;
11
+ isReady?: any;
12
+ toolkitConfig?: HeaderToolkitProps;
13
+ maxApi?: any;
14
+ dismissData?: any;
15
+ toolkitCallback?: (item: any) => void;
16
+
17
+ constructor(navigation: any, isReady: any) {
18
+ this.ref = navigation;
19
+ this.isReady = isReady;
20
+ }
21
+
22
+ /**
23
+ * push new stack screen
24
+ * @param params
25
+ */
26
+ push = (params: ScreenParams) => {
27
+ if (this.isReady.current) {
28
+ this.ref.current.dispatch(StackActions.push('Stack', params));
29
+ }
30
+ };
31
+
32
+ /**
33
+ * replace current screen with new screen
34
+ * @param params
35
+ */
36
+ replace = (params: ScreenParams) => {
37
+ if (this.isReady.current) {
38
+ this.ref.current.dispatch(StackActions.replace('Stack', params));
39
+ }
40
+ };
41
+ /**
42
+ * pop to dismiss a screen
43
+ * @param count
44
+ */
45
+ pop = (count?: number) => {
46
+ if (this.isReady.current) {
47
+ this.ref.current.dispatch(StackActions.pop(count ?? 1));
48
+ }
49
+ };
50
+
51
+ /**
52
+ * present a new screen, show from bottom iOS
53
+ * @param params
54
+ */
55
+ present = (params: ScreenParams) => {
56
+ if (this.isReady.current) {
57
+ this.ref.current.dispatch(StackActions.push('Dialog', params));
58
+ }
59
+ };
60
+
61
+ /**
62
+ * show a modal popup
63
+ * @param params
64
+ */
65
+ showModal = (params: ModalParams) => {
66
+ if (this.isReady.current) {
67
+ this.ref.current.dispatch(StackActions.push('Modal', params));
68
+ }
69
+ };
70
+
71
+ /**
72
+ * show a bottom sheet
73
+ * @param params
74
+ */
75
+ showBottomSheet = (params: BottomSheetParams) => {
76
+ if (this.isReady.current) {
77
+ this.ref.current.dispatch(
78
+ StackActions.push('Modal', {
79
+ ...params,
80
+ isBottomSheet: true,
81
+ }),
82
+ );
83
+ }
84
+ };
85
+
86
+ /**
87
+ * pop all screen route
88
+ */
89
+ popToTop = () => {
90
+ if (this.isReady.current) {
91
+ this.ref.current.dispatch(StackActions.popToTop());
92
+ }
93
+ };
94
+
95
+ /**
96
+ * navigate
97
+ * @param name
98
+ * @param params
99
+ */
100
+ navigate = (name: string, params: any) => {
101
+ if (this.isReady.current) {
102
+ this.ref.current.dispatch(
103
+ CommonActions.navigate({
104
+ name,
105
+ params,
106
+ }),
107
+ );
108
+ }
109
+ };
110
+
111
+ /**
112
+ * reset a navigation flow with new screen
113
+ * @param params
114
+ */
115
+ reset = (params: ScreenParams) => {
116
+ if (this.isReady.current) {
117
+ this.ref.current.dispatch(
118
+ CommonActions.reset({
119
+ index: 0,
120
+ routes: [
121
+ {
122
+ name: 'Stack',
123
+ key: `Stack_${new Date().getTime()}`,
124
+ params,
125
+ },
126
+ ],
127
+ }),
128
+ );
129
+ }
130
+ };
131
+
132
+ /**
133
+ * dismiss a feature data
134
+ * @param data
135
+ */
136
+ setDismissData = (data: any) => {
137
+ this.dismissData = data;
138
+ };
139
+
140
+ /**
141
+ * set callback function for toolkit
142
+ * @param callback
143
+ */
144
+ setToolkitCallback = (
145
+ callback: (item: {key: string; action: string}) => void,
146
+ ) => {
147
+ this.toolkitCallback = callback;
148
+ };
149
+ }
150
+
151
+ export default Navigator;
@@ -0,0 +1,146 @@
1
+ import React, {useContext, useEffect, useLayoutEffect, useRef} from 'react';
2
+ import {ScreenParams} from './types';
3
+ import Navigation from './Navigation';
4
+ import {ApplicationContext, MiniAppContext, ScreenContext} from './index';
5
+ import {GridSystem} from '../Layout';
6
+ import {useHeaderHeight} from '@react-navigation/stack';
7
+ import {Alert, InteractionManager} from 'react-native';
8
+
9
+ const StackScreen: React.FC<ScreenParams> = props => {
10
+ const {theme, showGrid, navigator} = useContext(ApplicationContext);
11
+ const startTime = useRef(Date.now());
12
+ const endTime = useRef(Date.now());
13
+ const timeLoad = useRef(0);
14
+ const timeInteraction = useRef(0);
15
+ const timeoutLoad = useRef<any>();
16
+ const tracked = useRef<any>({
17
+ releaseLoad: undefined,
18
+ releaseInteraction: undefined,
19
+ timeoutLoad: undefined,
20
+ timeoutInteraction: undefined,
21
+ });
22
+ const interaction = useRef<any>();
23
+ const context = useContext<any>(MiniAppContext);
24
+
25
+ const {screen: Component, options, initialParams} = props.route.params;
26
+ const navigation = new Navigation(props.navigation, theme);
27
+ const heightHeader = useHeaderHeight();
28
+
29
+ const data = {
30
+ ...initialParams,
31
+ ...props.route.params,
32
+ navigation,
33
+ };
34
+
35
+ delete data.screen;
36
+ delete data.initialParams;
37
+
38
+ const screenName = Component?.name || Component?.type?.name || 'Invalid';
39
+
40
+ /**
41
+ * set options for screen
42
+ */
43
+ useLayoutEffect(() => {
44
+ if (options) {
45
+ navigation.setOptions(options);
46
+ }
47
+ }, [options]);
48
+
49
+ /**
50
+ * tracking for screen
51
+ */
52
+ useEffect(() => {
53
+ if (__DEV__ && (!screenName || screenName == 'Invalid')) {
54
+ Alert.alert('Design system', `Unvalidated screen name "${screenName}" !`);
55
+ }
56
+
57
+ navigator?.maxApi?.of?.({screenName});
58
+
59
+ tracked.current.timeoutLoad = setTimeout(() => {
60
+ onScreenLoad();
61
+ }, 5000);
62
+ tracked.current.timeoutInteraction = setTimeout(() => {
63
+ onScreenInteraction();
64
+ }, 5000);
65
+
66
+ return () => {
67
+ onScreenLoad();
68
+ onScreenInteraction();
69
+ };
70
+ }, []);
71
+
72
+ /**
73
+ * tracking for screen load
74
+ */
75
+ const onScreenLoad = () => {
76
+ clearTimeout(tracked.current.timeoutLoad);
77
+ if (!tracked.current?.releaseLoad) {
78
+ context.autoTracking?.({
79
+ appId: context.appId,
80
+ code: context.code,
81
+ screenName,
82
+ componentName: 'Screen',
83
+ state: 'load',
84
+ duration: timeLoad.current,
85
+ });
86
+ navigator?.maxApi?.showToastDebug?.({
87
+ appId: `auto - ${context.appId}`,
88
+ message: `${screenName} screen_load_time ${timeLoad.current}`,
89
+ });
90
+ tracked.current.releaseLoad = true;
91
+ }
92
+ };
93
+
94
+ /**
95
+ * tracking for screen load
96
+ */
97
+ const onScreenInteraction = () => {
98
+ clearTimeout(tracked.current.timeoutInteraction);
99
+ if (!tracked.current?.releaseInteraction) {
100
+ context.autoTracking?.({
101
+ appId: context.appId,
102
+ code: context.code,
103
+ screenName,
104
+ componentName: 'Screen',
105
+ state: 'interaction',
106
+ duration: timeInteraction.current - timeLoad.current,
107
+ });
108
+
109
+ navigator?.maxApi?.showToastDebug?.({
110
+ appId: `auto - ${context.appId}`,
111
+ message: `${screenName} screen_interaction_time ${timeInteraction.current}`,
112
+ });
113
+ tracked.current.releaseInteraction = true;
114
+ }
115
+ };
116
+
117
+ return (
118
+ <ScreenContext.Provider
119
+ value={{
120
+ screenName,
121
+ onElementLoad: () => {
122
+ endTime.current = Date.now();
123
+ interaction.current?.cancel?.();
124
+ interaction.current = InteractionManager.runAfterInteractions(() => {
125
+ timeInteraction.current = Date.now() - startTime.current;
126
+ });
127
+ clearTimeout(timeoutLoad.current);
128
+ timeoutLoad.current = setTimeout(() => {
129
+ if (timeLoad.current === 0) {
130
+ timeLoad.current = endTime.current - startTime.current;
131
+ }
132
+ }, 2000);
133
+ },
134
+ }}>
135
+ <Component
136
+ heightHeader={heightHeader}
137
+ {...data}
138
+ onScreenLoad={onScreenLoad}
139
+ onScreenInteraction={onScreenInteraction}
140
+ />
141
+ {showGrid && <GridSystem />}
142
+ </ScreenContext.Provider>
143
+ );
144
+ };
145
+
146
+ export default StackScreen;