@momo-kits/foundation 0.150.2-test.1 → 0.151.1-beta.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.
@@ -1,22 +1,11 @@
1
- import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useMemo,
6
- useRef,
7
- useState,
8
- } from 'react';
1
+ import React, { useCallback, useContext, useEffect, useRef } from 'react';
9
2
  import {
10
3
  Dimensions,
11
- Keyboard,
12
4
  KeyboardAvoidingView,
13
- KeyboardEventName,
14
- LayoutAnimation,
15
5
  Modal,
16
6
  PanResponder,
17
7
  Platform,
18
8
  Pressable,
19
- ScrollView,
20
9
  StyleSheet,
21
10
  TouchableOpacity,
22
11
  View,
@@ -31,12 +20,12 @@ import { useHeaderHeight } from '@react-navigation/elements';
31
20
  import Animated, {
32
21
  Easing,
33
22
  Extrapolate,
34
- useSharedValue,
35
- useAnimatedStyle,
36
- withTiming,
37
- withSpring,
38
23
  interpolate,
39
24
  runOnJS,
25
+ useAnimatedStyle,
26
+ useSharedValue,
27
+ withSpring,
28
+ withTiming,
40
29
  } from 'react-native-reanimated';
41
30
  import layoutStyles from '../Layout/styles';
42
31
 
@@ -46,8 +35,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
46
35
  const action = useRef<undefined | string>(undefined);
47
36
  const insets = useSafeAreaInsets();
48
37
  const heightHeader = useHeaderHeight();
49
- const [keyboardHeight, setKeyboardHeight] = useState(0);
50
- const [contentHeight, setContentHeight] = useState(0);
51
38
  const keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
52
39
 
53
40
  const {
@@ -59,7 +46,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
59
46
  draggable = true,
60
47
  useBottomInset = true,
61
48
  useKeyboardAvoidingView = true,
62
- useScrollOverflow = false,
63
49
  keyboardVerticalOffset,
64
50
  useDivider = true,
65
51
  footerComponent,
@@ -116,73 +102,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
116
102
  ).current;
117
103
 
118
104
  let Container: any = View;
119
- let Content: any = View;
120
105
  if (useNativeModal) {
121
106
  Container = Modal;
122
107
  }
123
- if (useScrollOverflow) {
124
- Content = ScrollView;
125
- }
126
108
  let backgroundColor = theme.colors.background.default;
127
109
  if (surface) {
128
110
  backgroundColor = theme.colors.background.surface;
129
111
  }
130
112
 
131
- const headerHeight = 90;
132
-
133
- /**
134
- * If the content height exceeds the device height,
135
- * adjust bottomInset based on keyboard behavior so the BottomSheet content stays fixed
136
- * whether the keyboard is open or closed.
137
- */
138
-
139
- let bottomInset: number;
140
- if (keyboardHeight > 0) {
141
- bottomInset =
142
- Platform.select({
143
- ios: 0,
144
- android: 21,
145
- }) ?? 0;
146
- } else {
147
- bottomInset =
148
- Platform.select({
149
- ios: Math.min(insets.bottom, 21),
150
- android: -21,
151
- }) ?? 0;
152
- }
153
-
154
- const maxContentSize =
155
- heightDevice - headerHeight - keyboardHeight - bottomInset;
156
-
157
- useEffect(() => {
158
- let showEvent: KeyboardEventName;
159
- let hideEvent: KeyboardEventName;
160
-
161
- if (Platform.OS === 'ios') {
162
- showEvent = 'keyboardWillShow';
163
- hideEvent = 'keyboardWillHide';
164
- } else {
165
- showEvent = 'keyboardDidShow';
166
- hideEvent = 'keyboardDidHide';
167
- }
168
- const onKeyboardShow = (e: any) => {
169
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
170
- setKeyboardHeight(e.endCoordinates.height);
171
- };
172
- const onKeyboardHide = () => {
173
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
174
- setKeyboardHeight(0);
175
- };
176
-
177
- const showSub = Keyboard.addListener(showEvent, onKeyboardShow);
178
- const hideSub = Keyboard.addListener(hideEvent, onKeyboardHide);
179
-
180
- return () => {
181
- showSub.remove();
182
- hideSub.remove();
183
- };
184
- }, []);
185
-
186
113
  /**
187
114
  * emit dismiss event
188
115
  */
@@ -194,17 +121,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
194
121
  };
195
122
  }, [heightDevice, openAnimation, props.route.params, translateY]);
196
123
 
197
- /**
198
- * handle content bottom sheet change
199
- * @param width
200
- * @param height
201
- */
202
- const handleContentSizeChange = (width: number, height: number) => {
203
- if (contentHeight !== height) {
204
- setContentHeight(height);
205
- }
206
- };
207
-
208
124
  /**
209
125
  * handler dismiss
210
126
  */
@@ -214,8 +130,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
214
130
  return;
215
131
  }
216
132
  closeAnimation(() => {
133
+ navigator?.pop();
217
134
  runOnJS(() => {
218
- navigator?.pop();
219
135
  callback?.();
220
136
  })();
221
137
  });
@@ -238,14 +154,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
238
154
  */
239
155
  const renderHeader = useCallback(() => {
240
156
  return (
241
- <View
242
- style={{
243
- backgroundColor,
244
- borderTopLeftRadius: Radius.M,
245
- borderTopRightRadius: Radius.M,
246
- }}
247
- {...panResponder.panHandlers}
248
- >
157
+ <View {...panResponder.panHandlers}>
249
158
  <View style={{ height: 16 }}>
250
159
  <View style={styles.indicator} />
251
160
  </View>
@@ -285,7 +194,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
285
194
  </View>
286
195
  );
287
196
  }, [
288
- backgroundColor,
289
197
  onDismiss,
290
198
  options.header,
291
199
  options.title,
@@ -297,7 +205,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
297
205
  const animatedStyle = useAnimatedStyle(() => {
298
206
  return {
299
207
  transform: [{ translateY: translateY.value }],
300
- maxHeight: maxContentSize,
301
208
  };
302
209
  });
303
210
 
@@ -341,19 +248,23 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
341
248
  <Animated.View style={[styles.overlay, animatedOverlayStyle]} />
342
249
  </Pressable>
343
250
 
344
- <Animated.View style={animatedStyle}>
251
+ <Animated.View
252
+ style={[
253
+ animatedStyle,
254
+ {
255
+ backgroundColor,
256
+ borderTopLeftRadius: Radius.M,
257
+ borderTopRightRadius: Radius.M,
258
+ overflow: 'hidden',
259
+ },
260
+ ]}
261
+ >
345
262
  {renderHeader()}
346
- <Content
347
- scrollEnabled={contentHeight + 20 > maxContentSize}
348
- style={{ backgroundColor: backgroundColor }}
349
- onContentSizeChange={handleContentSizeChange}
350
- >
351
- <Screen
352
- {...props}
353
- {...props.route.params}
354
- onRequestClose={onRequestClose}
355
- />
356
- </Content>
263
+ <Screen
264
+ {...props}
265
+ {...props.route.params}
266
+ onRequestClose={onRequestClose}
267
+ />
357
268
  {footerComponent && (
358
269
  <View
359
270
  style={[
@@ -37,7 +37,6 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
37
37
  const { navigator } = useContext(ApplicationContext);
38
38
  const context = useContext<any>(MiniAppContext);
39
39
  const { nested, options, screen, initialParams } = route?.params;
40
- const useAnimation = route?.params?.useAnimation ?? true;
41
40
  const insets = useSafeAreaInsets();
42
41
 
43
42
  const opacityValue = 0.3;
@@ -47,10 +46,10 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
47
46
  const screenName = screen?.name || screen?.type?.name || 'Invalid';
48
47
 
49
48
  const onScreenNavigated = useCallback(
50
- (preScreenName: string, screenName: string) => {
49
+ (pre: string, current: string) => {
51
50
  const data: any = {
52
- preScreenName,
53
- screenName,
51
+ preScreenName: pre,
52
+ screenName: current,
54
53
  componentName: 'Screen',
55
54
  state: 'navigated',
56
55
  action: 'push',
@@ -70,79 +69,82 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
70
69
  type: 'ERROR',
71
70
  });
72
71
  },
73
- [context, navigator?.maxApi],
72
+ [context, navigator?.maxApi, screenName],
74
73
  );
75
74
 
76
75
  useFocusEffect(
77
- React.useCallback(() => {
78
- Animated.parallel([
79
- Animated.timing(opacity, {
80
- toValue: 1,
81
- duration: 200,
82
- useNativeDriver: true,
83
- }),
84
- Animated.timing(scale, {
85
- toValue: 1,
86
- duration: 200,
87
- useNativeDriver: true,
88
- }),
89
- ]).start();
76
+ React.useCallback(
77
+ () => {
78
+ Animated.parallel([
79
+ Animated.timing(opacity, {
80
+ toValue: 1,
81
+ duration: 200,
82
+ useNativeDriver: true,
83
+ }),
84
+ Animated.timing(scale, {
85
+ toValue: 1,
86
+ duration: 200,
87
+ useNativeDriver: true,
88
+ }),
89
+ ]).start();
90
90
 
91
- setTimeout(() => {
92
- navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
93
- onScreenNavigated(data?.screenName, screenName);
94
- navigator?.maxApi?.setObserver?.('current_screen', { screenName });
95
- });
96
- }, 100);
91
+ setTimeout(() => {
92
+ navigator?.maxApi?.getDataObserver?.(
93
+ 'current_screen',
94
+ (data: any) => {
95
+ onScreenNavigated(data?.screenName, screenName);
96
+ navigator?.maxApi?.setObserver?.('current_screen', {
97
+ screenName,
98
+ });
99
+ },
100
+ );
101
+ }, 100);
97
102
 
98
- return () => {
99
- if (navigation.getState().index !== route?.params.index) {
100
- Animated.parallel([
101
- Animated.timing(opacity, {
102
- toValue: opacityValue,
103
- duration: 200,
104
- useNativeDriver: true,
105
- }),
106
- Animated.timing(scale, {
107
- toValue: scaleValue,
108
- duration: 200,
109
- useNativeDriver: true,
110
- }),
111
- ]).start();
112
- }
113
- };
114
- }, [
115
- navigation,
116
- navigator?.maxApi,
117
- onScreenNavigated,
118
- opacity,
119
- route?.params.index,
120
- scale,
121
- screenName,
122
- ]),
103
+ return () => {
104
+ if (navigation.getState().index !== route?.params.index) {
105
+ Animated.parallel([
106
+ Animated.timing(opacity, {
107
+ toValue: opacityValue,
108
+ duration: 200,
109
+ useNativeDriver: true,
110
+ }),
111
+ Animated.timing(scale, {
112
+ toValue: scaleValue,
113
+ duration: 200,
114
+ useNativeDriver: true,
115
+ }),
116
+ ]).start();
117
+ }
118
+ };
119
+ },
120
+ // eslint-disable-next-line react-hooks/exhaustive-deps
121
+ [],
122
+ ),
123
123
  );
124
124
 
125
- useEffect(() => {
126
- const onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
127
- if (navigation.isFocused()) {
128
- navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
129
- onScreenNavigated(data?.screenName, screenName);
130
- navigator?.maxApi?.setObserver?.('current_screen', { screenName });
131
- });
132
- }
133
- });
134
-
135
- return () => {
136
- onFocusApp?.remove?.();
137
- };
138
- }, [navigation, navigator?.maxApi, onScreenNavigated, screenName]);
125
+ useEffect(
126
+ () => {
127
+ const onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
128
+ if (navigation.isFocused()) {
129
+ navigator?.maxApi?.getDataObserver?.(
130
+ 'current_screen',
131
+ (data: any) => {
132
+ onScreenNavigated(data?.screenName, screenName);
133
+ navigator?.maxApi?.setObserver?.('current_screen', {
134
+ screenName,
135
+ });
136
+ },
137
+ );
138
+ }
139
+ });
139
140
 
140
- let opacityStyle: undefined | Animated.Value;
141
- let scaleStyle: number | Animated.Value = 1;
142
- if (useAnimation) {
143
- opacityStyle = opacity;
144
- scaleStyle = scale;
145
- }
141
+ return () => {
142
+ onFocusApp?.remove?.();
143
+ };
144
+ },
145
+ // eslint-disable-next-line react-hooks/exhaustive-deps
146
+ [],
147
+ );
146
148
 
147
149
  let stackOptions = {};
148
150
  if (options) {
@@ -155,8 +157,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
155
157
  style={[
156
158
  styles.container,
157
159
  {
158
- opacity: opacityStyle,
159
- transform: [{ scale: scaleStyle }],
160
+ opacity,
161
+ transform: [{ scale }],
160
162
  },
161
163
  ]}
162
164
  >
@@ -188,8 +190,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
188
190
  style={[
189
191
  styles.container,
190
192
  {
191
- opacity: opacityStyle,
192
- transform: [{ scale: scaleStyle }],
193
+ opacity,
194
+ transform: [{ scale }],
193
195
  },
194
196
  ]}
195
197
  >
@@ -227,7 +229,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
227
229
  navigation,
228
230
  initialRouteName,
229
231
  floatingButton,
230
- useAnimation = true,
231
232
  }) => {
232
233
  const { theme, navigator } = useContext(ApplicationContext);
233
234
  const dimensions = useWindowDimensions();
@@ -340,7 +341,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
340
341
  key={`BottomTab-${item.name}`}
341
342
  name={item.name}
342
343
  component={TabScreen}
343
- initialParams={{ ...item, nested, index, useAnimation }}
344
+ initialParams={{ ...item, nested, index }}
344
345
  listeners={handler}
345
346
  options={{
346
347
  tabBarLabel: ({ focused, color }) => {
@@ -86,7 +86,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
86
86
 
87
87
  const styles = StyleSheet.create({
88
88
  headerLeft: {
89
- marginLeft: 12,
89
+ marginLeft: 8,
90
90
  },
91
91
  });
92
92
 
@@ -10,7 +10,7 @@ import {
10
10
  Modal as ModalRN,
11
11
  } from 'react-native';
12
12
 
13
- import { ApplicationContext } from './index';
13
+ import { ApplicationContext, MiniAppContext } from './index';
14
14
  import { Styles } from '../Consts';
15
15
  import Navigation from './Navigation';
16
16
  import { ModalParams } from './types';
@@ -18,6 +18,18 @@ import BottomSheet from './BottomSheet';
18
18
  import { runOnJS } from 'react-native-reanimated';
19
19
 
20
20
  const ModalScreen: React.FC<any> = props => {
21
+ const context: any = useContext(MiniAppContext);
22
+ const { navigator } = useContext(ApplicationContext);
23
+
24
+ useEffect(
25
+ () => {
26
+ if (context?.enableHapticDialog) {
27
+ navigator?.maxApi?.triggerEventVibration?.('light');
28
+ }
29
+ }, // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ [],
31
+ );
32
+
21
33
  if (props.route?.params?.isBottomSheet) {
22
34
  return <BottomSheet {...props} />;
23
35
  }
@@ -37,6 +37,12 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
37
37
  maxApi,
38
38
  initialParams,
39
39
  localize = new Localize({ vi: {}, en: {} }),
40
+ features = {
41
+ enableBottomTabAnimation: true,
42
+ enableHapticDialog: true,
43
+ enableForceFoundationList: false,
44
+ enableHapticBottomTab: true,
45
+ },
40
46
  }) => {
41
47
  const context = useContext<any>(MiniAppContext);
42
48
  const [currentContext, setCurrentContext] = useState({});
@@ -46,6 +52,7 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
46
52
  value={{
47
53
  ...context,
48
54
  ...currentContext,
55
+ features,
49
56
  }}
50
57
  >
51
58
  <Navigation
@@ -1,10 +1,4 @@
1
- import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useLayoutEffect,
6
- useRef,
7
- } from 'react';
1
+ import React, { useContext, useEffect, useLayoutEffect, useRef } from 'react';
8
2
  import { Alert, InteractionManager } from 'react-native';
9
3
  import { useHeaderHeight } from '@react-navigation/elements';
10
4
  import { ScreenTrackingParams } from './types';
@@ -75,39 +69,106 @@ const StackScreen: React.FC<any> = props => {
75
69
  }
76
70
  }, [navigation, options]);
77
71
 
78
- const onScreenNavigated = useCallback(
79
- (preScreenName: string, screenName: string) => {
80
- const data: any = {
81
- preScreenName,
82
- screenName,
83
- componentName: 'Screen',
84
- state: 'navigated',
85
- action: tracking.current?.mounted ? 'back' : 'push',
86
- };
72
+ /**
73
+ * tracking for screen
74
+ */
75
+ useEffect(() => {
76
+ let focusScreen: any;
77
+ let onFocusApp: any;
78
+ if (['Invalid', 'screen'].includes(screenName)) {
79
+ navigator?.maxApi?.showPopup?.('notice', {
80
+ title: 'Invalid screen name',
81
+ message:
82
+ 'Your screen has not been rendered because Platform has not detected the screen name. Please migrate to support this feature.',
83
+ });
84
+ }
87
85
 
88
- context?.autoTracking?.({
89
- ...context,
90
- ...data,
86
+ if (!bottomTab) {
87
+ focusScreen = props.navigation.addListener('focus', () => {
88
+ navigator?.maxApi?.getDataObserver?.('current_screen', (item: any) => {
89
+ onScreenNavigated(item?.screenName, screenName);
90
+ navigator?.maxApi?.setObserver?.('current_screen', { screenName });
91
+ });
91
92
  });
93
+ onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
94
+ if (props.navigation.isFocused()) {
95
+ navigator?.maxApi?.getDataObserver?.(
96
+ 'current_screen',
97
+ (item: any) => {
98
+ onScreenNavigated(item?.screenName, screenName);
99
+ navigator?.maxApi?.setObserver?.('current_screen', {
100
+ screenName,
101
+ });
102
+ },
103
+ );
104
+ }
105
+ });
106
+ }
107
+
108
+ navigator?.maxApi?.startTraceScreenLoad?.(
109
+ screenName,
110
+ context,
111
+ (item: any) => {
112
+ tracking.current.traceIdLoad = item?.traceId;
113
+ },
114
+ );
115
+ navigator?.maxApi?.startTraceScreenInteraction?.(
116
+ screenName,
117
+ context,
118
+ (item: any) => {
119
+ tracking.current.traceIdInteraction = item?.traceId;
120
+ },
121
+ );
92
122
 
93
- tracking.current.mounted = true;
123
+ tracking.current.timeoutTracking = setTimeout(() => {
124
+ onScreenLoad();
125
+ onScreenInteraction();
126
+ }, 5000);
94
127
 
95
- /**
96
- * debug toast
97
- */
98
- navigator?.maxApi?.showToastDebug?.({
99
- appId: context.appId,
100
- message: `${screenName} screen_navigated`,
101
- type: 'ERROR',
102
- });
103
- },
104
- [context, navigator?.maxApi],
105
- );
128
+ return () => {
129
+ onScreenLoad();
130
+ onScreenInteraction();
131
+ clearTimeout(tracking.current.timeoutLoad);
132
+ clearTimeout(tracking.current.timeoutInteraction);
133
+ clearTimeout(tracking.current.timeoutTracking);
134
+ // eslint-disable-next-line react-hooks/exhaustive-deps
135
+ clearTimeout(tracking.current.timeoutLoading);
136
+ focusScreen?.();
137
+ onFocusApp?.remove?.();
138
+ };
139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
140
+ }, []);
141
+
142
+ const onScreenNavigated = (pre: string, current: string) => {
143
+ const item: any = {
144
+ preScreenName: pre,
145
+ screenName: current,
146
+ componentName: 'Screen',
147
+ state: 'navigated',
148
+ action: tracking.current?.mounted ? 'back' : 'push',
149
+ };
150
+
151
+ context?.autoTracking?.({
152
+ ...context,
153
+ ...item,
154
+ });
155
+
156
+ tracking.current.mounted = true;
157
+
158
+ /**
159
+ * debug toast
160
+ */
161
+ navigator?.maxApi?.showToastDebug?.({
162
+ appId: context.appId,
163
+ message: `${screenName} screen_navigated`,
164
+ type: 'ERROR',
165
+ });
166
+ };
106
167
 
107
168
  /**
108
169
  * tracking for screen load
109
170
  */
110
- const onScreenLoad = useCallback(() => {
171
+ const onScreenLoad = () => {
111
172
  if (!tracking.current?.releaseLoad) {
112
173
  let timeLoad = tracking.current.timeLoad;
113
174
  if (timeLoad === 0) {
@@ -150,12 +211,12 @@ const StackScreen: React.FC<any> = props => {
150
211
  );
151
212
  }
152
213
  }
153
- }, [context, navigator?.maxApi, screenName]);
214
+ };
154
215
 
155
216
  /**
156
217
  * tracking for screen load
157
218
  */
158
- const onScreenInteraction = useCallback(() => {
219
+ const onScreenInteraction = () => {
159
220
  if (!tracking.current?.releaseInteraction) {
160
221
  let timeLoad = tracking.current.timeLoad;
161
222
  if (timeLoad === 0) {
@@ -191,130 +252,7 @@ const StackScreen: React.FC<any> = props => {
191
252
  type: 'ERROR',
192
253
  });
193
254
  }
194
- }, [context, navigator?.maxApi, screenName]);
195
-
196
- /**
197
- * tracking for screen
198
- */
199
- useEffect(() => {
200
- let focusScreen: any;
201
- let onFocusApp: any;
202
- if (['Invalid', 'screen'].includes(screenName)) {
203
- navigator?.maxApi?.showPopup?.('notice', {
204
- title: 'Invalid screen name',
205
- message:
206
- 'Your screen has not been rendered because Platform has not detected the screen name. Please migrate to support this feature.',
207
- });
208
- }
209
-
210
- if (!bottomTab) {
211
- focusScreen = props.navigation.addListener('focus', () => {
212
- navigator?.maxApi?.getDataObserver?.('current_screen', (data: any) => {
213
- onScreenNavigated(data?.screenName, screenName);
214
- navigator?.maxApi?.setObserver?.('current_screen', { screenName });
215
- });
216
- });
217
- onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
218
- if (props.navigation.isFocused()) {
219
- navigator?.maxApi?.getDataObserver?.(
220
- 'current_screen',
221
- (data: any) => {
222
- onScreenNavigated(data?.screenName, screenName);
223
- navigator?.maxApi?.setObserver?.('current_screen', {
224
- screenName,
225
- });
226
- },
227
- );
228
- }
229
- });
230
- }
231
-
232
- navigator?.maxApi?.startTraceScreenLoad?.(
233
- screenName,
234
- context,
235
- (data: any) => {
236
- tracking.current.traceIdLoad = data?.traceId;
237
- },
238
- );
239
- navigator?.maxApi?.startTraceScreenInteraction?.(
240
- screenName,
241
- context,
242
- (data: any) => {
243
- tracking.current.traceIdInteraction = data?.traceId;
244
- },
245
- );
246
-
247
- tracking.current.timeoutTracking = setTimeout(() => {
248
- onScreenLoad();
249
- onScreenInteraction();
250
- }, 5000);
251
-
252
- return () => {
253
- onScreenLoad();
254
- onScreenInteraction();
255
- clearTimeout(tracking.current.timeoutLoad);
256
- clearTimeout(tracking.current.timeoutInteraction);
257
- clearTimeout(tracking.current.timeoutTracking);
258
- // eslint-disable-next-line react-hooks/exhaustive-deps
259
- clearTimeout(tracking.current.timeoutLoading);
260
- focusScreen?.();
261
- onFocusApp?.remove?.();
262
- };
263
- }, [
264
- bottomTab,
265
- context,
266
- navigator?.maxApi,
267
- onScreenInteraction,
268
- onScreenLoad,
269
- onScreenNavigated,
270
- props.navigation,
271
- screenName,
272
- ]);
273
-
274
- /**
275
- * tracking for screen
276
- */
277
- useEffect(() => {
278
- if (['Invalid', 'screen'].includes(screenName)) {
279
- navigator?.maxApi?.showPopup?.('notice', {
280
- title: 'Invalid screen name',
281
- message:
282
- 'Your screen has not been rendered because Platform has not detected the screen name. Please migrate to support this feature.',
283
- });
284
- }
285
-
286
- navigator?.maxApi?.startTraceScreenLoad?.(
287
- screenName,
288
- context,
289
- (item: any) => {
290
- tracking.current.traceIdLoad = item?.traceId;
291
- },
292
- );
293
- navigator?.maxApi?.startTraceScreenInteraction?.(
294
- screenName,
295
- context,
296
- (item: any) => {
297
- tracking.current.traceIdInteraction = item?.traceId;
298
- },
299
- );
300
-
301
- tracking.current.timeoutTracking = setTimeout(() => {
302
- onScreenLoad();
303
- onScreenInteraction();
304
- }, 5000);
305
-
306
- return () => {
307
- onScreenLoad();
308
- onScreenInteraction();
309
- };
310
- }, [
311
- context,
312
- navigator?.maxApi,
313
- onScreenInteraction,
314
- onScreenLoad,
315
- props.navigation,
316
- screenName,
317
- ]);
255
+ };
318
256
 
319
257
  /**
320
258
  * tracking for first interaction by user
@@ -343,16 +281,19 @@ const StackScreen: React.FC<any> = props => {
343
281
  }
344
282
  };
345
283
 
284
+ /**
285
+ * tracking for loading screen
286
+ */
346
287
  const onScreenLoading = () => {
347
- if (!tracking.current?.releaseLoading) {
348
- const timeLoad =
349
- tracking.current.timeEndLoading - tracking.current.timeStartLoading;
288
+ const start = tracking.current.timeStartLoading;
289
+ const end = tracking.current.timeEndLoading;
290
+ if (!tracking.current?.releaseLoading && start && end && end > start) {
350
291
  context?.autoTracking?.({
351
292
  ...context,
352
293
  screenName,
353
294
  componentName: 'Screen',
354
295
  state: 'loading',
355
- duration: timeLoad,
296
+ duration: end - start,
356
297
  loadingType: 'skeleton',
357
298
  });
358
299
  tracking.current.releaseLoading = true;
@@ -369,7 +310,7 @@ const StackScreen: React.FC<any> = props => {
369
310
  */
370
311
  if (item?.componentName === 'Widget') {
371
312
  const index = widgets.current.findIndex(
372
- (e: any) => e.componentId === item.componentId,
313
+ (element: any) => element.componentId === item.componentId,
373
314
  );
374
315
  const time = Date.now() - tracking.current.startTime;
375
316
  if (index === -1) {
@@ -404,8 +345,8 @@ const StackScreen: React.FC<any> = props => {
404
345
  /**
405
346
  * support for debug last element
406
347
  */
407
- if (data?.componentName) {
408
- tracking.current.lastElement = data;
348
+ if (item?.componentName) {
349
+ tracking.current.lastElement = item;
409
350
  }
410
351
 
411
352
  /**
@@ -414,7 +355,7 @@ const StackScreen: React.FC<any> = props => {
414
355
  if (item?.interaction) {
415
356
  onScreenLoad();
416
357
  onScreenInteraction();
417
- onFirstInteraction(data?.action);
358
+ onFirstInteraction(item?.action);
418
359
  }
419
360
  /**
420
361
  * timeout for handle tracking screen
@@ -439,7 +380,6 @@ const StackScreen: React.FC<any> = props => {
439
380
  /**
440
381
  * tracking for loading screen
441
382
  */
442
- clearTimeout(tracking.current.timeoutLoading);
443
383
  if (!loading) {
444
384
  tracking.current.timeEndLoading = Date.now();
445
385
  }
@@ -447,12 +387,13 @@ const StackScreen: React.FC<any> = props => {
447
387
  /**
448
388
  * timeout for handle tracking screen
449
389
  */
390
+ clearTimeout(tracking.current.timeoutLoading);
450
391
  tracking.current.timeoutLoading = setTimeout(() => {
451
392
  onScreenLoading();
452
393
  }, 2000);
453
394
  },
454
- onSetParams: (data: ScreenTrackingParams) => {
455
- tracking.current.params = data;
395
+ onSetParams: (item: ScreenTrackingParams) => {
396
+ tracking.current.params = item;
456
397
  },
457
398
  }}
458
399
  >
@@ -1,17 +1,17 @@
1
- import {EventArg} from '@react-navigation/core';
2
- import {StackNavigationOptions} from '@react-navigation/stack';
3
- import React, {ReactNode} from 'react';
1
+ import { EventArg } from '@react-navigation/core';
2
+ import { StackNavigationOptions } from '@react-navigation/stack';
3
+ import React, { ReactNode } from 'react';
4
4
  import {
5
5
  Animated,
6
6
  TouchableOpacityProps,
7
7
  ViewProps,
8
8
  ViewStyle,
9
9
  } from 'react-native';
10
- import {PopupNotifyProps} from '../Popup/types';
10
+ import { PopupNotifyProps } from '../Popup/types';
11
11
  import Localize from './Localize';
12
12
  import Navigation from './Navigation';
13
13
  import Navigator from './Navigator';
14
- import {InputRef, InputSearchProps} from '../Input';
14
+ import { InputRef, InputSearchProps } from '../Input';
15
15
 
16
16
  export type Theme = {
17
17
  dark: boolean;
@@ -74,7 +74,7 @@ export type Context = {
74
74
  theme: Theme;
75
75
  navigator?: Navigator;
76
76
  showGrid?: boolean;
77
- translate?: (data: string | {vi: string; en: string}) => string;
77
+ translate?: (data: string | { vi: string; en: string }) => string;
78
78
  };
79
79
 
80
80
  export type LocalizationObject = {
@@ -87,19 +87,26 @@ export type LocalizationObject = {
87
87
  };
88
88
 
89
89
  export type ToolGroup = {
90
- title: {vi: string; en: string};
90
+ title: { vi: string; en: string };
91
91
  items: Tool[];
92
92
  };
93
93
 
94
94
  export type Tool = {
95
95
  icon: string;
96
- name: {vi: string; en: string};
96
+ name: { vi: string; en: string };
97
97
  key: string;
98
98
  showBadge?: boolean;
99
99
  showRightIcon?: boolean;
100
100
  onPress: () => void;
101
101
  };
102
102
 
103
+ export type FeaturesFlag = {
104
+ enableForceFoundationList?: boolean;
105
+ enableBottomTabAnimation?: boolean;
106
+ enableHapticBottomTab?: boolean;
107
+ enableHapticDialog?: boolean;
108
+ };
109
+
103
110
  export type NavigationContainerProps = {
104
111
  screen: React.ComponentType<NavigationScreenProps>;
105
112
  options?: NavigationOptions;
@@ -107,6 +114,7 @@ export type NavigationContainerProps = {
107
114
  maxApi: any;
108
115
  initialParams?: any;
109
116
  localize: Localize;
117
+ features: FeaturesFlag;
110
118
  };
111
119
 
112
120
  export type WidgetContainerProps = {
@@ -157,7 +165,6 @@ export type BottomSheetParams = {
157
165
  useBottomInset?: boolean;
158
166
  useKeyboardAvoidingView?: boolean;
159
167
  keyboardVerticalOffset?: number;
160
- useScrollOverflow?: boolean;
161
168
  useDivider?: boolean;
162
169
  footerComponent?: React.ReactNode;
163
170
  };
@@ -270,7 +277,6 @@ export type BottomTabProps = {
270
277
  blur?: (e: EventArg<'blur', false, unknown>) => void;
271
278
  };
272
279
  floatingButton?: FloatingButtonProps;
273
- useAnimation?: boolean;
274
280
  };
275
281
 
276
282
  export type FloatingButtonProps = {
package/Image/index.tsx CHANGED
@@ -66,7 +66,7 @@ const Image: React.FC<ImageProps> = ({
66
66
  {...rest}
67
67
  {...{ accessibilityLabel: rest.accessibilityLabel ?? accessibilityLabel }}
68
68
  source={source}
69
- style={style}
69
+ style={[styles.container, style]}
70
70
  onProgress={() => {
71
71
  error.current = false;
72
72
  if (status !== 'loading' && loading) {
@@ -90,7 +90,7 @@ const Image: React.FC<ImageProps> = ({
90
90
  error.current = true;
91
91
  }}
92
92
  >
93
- <View style={styles.container}>{renderContent()}</View>
93
+ {renderContent()}
94
94
  </FastImage>
95
95
  );
96
96
  };
package/Image/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { StyleSheet } from 'react-native';
1
+ import {StyleSheet} from 'react-native';
2
2
 
3
3
  export default StyleSheet.create({
4
- container: { zIndex: -1, flex: 1 },
4
+ container: {},
5
5
  });
@@ -202,6 +202,7 @@ const InputOTP = forwardRef(
202
202
  const renderInputView = () => {
203
203
  return (
204
204
  <TouchableOpacity
205
+ activeOpacity={1}
205
206
  onPress={() => inputRef.current?.focus()}
206
207
  style={[
207
208
  styles.otpInput,
@@ -238,6 +239,7 @@ const InputOTP = forwardRef(
238
239
  {...props}
239
240
  ref={inputRef}
240
241
  value={value}
242
+ textAlign={'center'}
241
243
  onChangeText={_onChangeText}
242
244
  keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
243
245
  allowFontScaling={false}
package/Input/styles.ts CHANGED
@@ -1,6 +1,6 @@
1
- import {Platform, StyleSheet} from 'react-native';
2
- import {Colors, Radius, Spacing} from '../Consts';
3
- import {scaleSize} from '../Text';
1
+ import { Platform, StyleSheet } from 'react-native';
2
+ import { Colors, Radius, Spacing } from '../Consts';
3
+ import { scaleSize } from '../Text';
4
4
 
5
5
  export default StyleSheet.create({
6
6
  //input style
@@ -31,8 +31,8 @@ export default StyleSheet.create({
31
31
  flexDirection: 'row',
32
32
  alignItems: 'center',
33
33
  },
34
- floatingIcon: {marginLeft: Spacing.XS},
35
- errorIcon: {marginRight: Spacing.XS, marginTop: Spacing.XXS},
34
+ floatingIcon: { marginLeft: Spacing.XS },
35
+ errorIcon: { marginRight: Spacing.XS, marginTop: Spacing.XXS },
36
36
  errorView: {
37
37
  flexDirection: 'row',
38
38
  marginTop: Spacing.XS,
@@ -119,8 +119,8 @@ export default StyleSheet.create({
119
119
  iconSearchInput: {
120
120
  marginLeft: Spacing.S,
121
121
  },
122
- searchInputContainer: {flexDirection: 'row', alignItems: 'center'},
123
- textButton: {marginLeft: Spacing.L},
122
+ searchInputContainer: { flexDirection: 'row', alignItems: 'center' },
123
+ textButton: { marginLeft: Spacing.L },
124
124
  divider: {
125
125
  width: 1,
126
126
  height: 20,
@@ -178,16 +178,17 @@ export default StyleSheet.create({
178
178
  position: 'absolute',
179
179
  width: '100%',
180
180
  height: '100%',
181
- opacity: 0.01,
181
+ opacity: 0.02,
182
182
  backgroundColor: 'transparent',
183
+ zIndex: 2,
183
184
  },
184
185
  otpRealInput: {
185
- opacity: 0.01,
186
186
  width: '100%',
187
187
  height: '100%',
188
- zIndex: 1,
188
+ zIndex: 2,
189
+ opacity: 0.02,
190
+ letterSpacing: Spacing.XXL,
189
191
  },
190
-
191
192
  //DropDown
192
193
  inputDropDownWrapper: {
193
194
  flexDirection: 'row',
@@ -216,5 +217,5 @@ export default StyleSheet.create({
216
217
  borderRadius: Radius.XS,
217
218
  overflow: 'hidden',
218
219
  },
219
- currency: {fontSize: scaleSize(20), fontWeight: 'bold'},
220
+ currency: { fontSize: scaleSize(20), fontWeight: 'bold' },
220
221
  });
package/Layout/Card.tsx CHANGED
@@ -33,10 +33,10 @@ const Card: React.FC<CardProps> = ({
33
33
  const gutterSize = 8;
34
34
  const margin = 12;
35
35
  const widthSection = Dimensions.get('window').width - margin * 2;
36
- const sizePerSpan = Math.round(
36
+ let sizePerSpan =
37
37
  (widthSection - margin * 2 - gutterSize * (numberOfColumns - 1)) /
38
- numberOfColumns,
39
- );
38
+ numberOfColumns -
39
+ 1 / numberOfColumns;
40
40
 
41
41
  const gridContext: GridContextProps = {
42
42
  numberOfColumns,
package/Text/styles.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export default {
2
- headline_default_bold: {fontSize: 24, lineHeight: 34, fontWeight: '700'},
3
- header_m_bold: {fontSize: 18, lineHeight: 26, fontWeight: '700'},
4
- header_default_bold: {fontSize: 16, lineHeight: 22, fontWeight: '700'},
5
- header_s_semibold: {fontSize: 14, lineHeight: 20, fontWeight: '600'},
6
- header_xs_semibold: {fontSize: 12, lineHeight: 18, fontWeight: '600'},
7
- body_default_regular: {fontSize: 14, lineHeight: 20, fontWeight: '400'},
2
+ headline_l_bold: { fontSize: 28, lineHeight: 40, fontWeight: '700' },
3
+ headline_default_bold: { fontSize: 24, lineHeight: 34, fontWeight: '700' },
4
+ header_m_bold: { fontSize: 18, lineHeight: 26, fontWeight: '700' },
5
+ header_default_bold: { fontSize: 16, lineHeight: 22, fontWeight: '700' },
6
+ header_s_semibold: { fontSize: 14, lineHeight: 20, fontWeight: '600' },
7
+ header_xs_semibold: { fontSize: 12, lineHeight: 18, fontWeight: '600' },
8
+ body_default_regular: { fontSize: 14, lineHeight: 20, fontWeight: '400' },
8
9
  body_default_regularstrikethrought: {
9
10
  fontSize: 14,
10
11
  lineHeight: 20,
@@ -22,52 +23,52 @@ export default {
22
23
  fontWeight: '400',
23
24
  textDecorationLine: 'line-through',
24
25
  },
25
- description_xs_regular: {fontSize: 10, lineHeight: 14, fontWeight: '400'},
26
+ description_xs_regular: { fontSize: 10, lineHeight: 14, fontWeight: '400' },
26
27
  description_xs_regularstrikethrought: {
27
28
  fontSize: 10,
28
29
  lineHeight: 14,
29
30
  fontWeight: '400',
30
31
  textDecorationLine: 'line-through',
31
32
  },
32
- label_default_medium: {fontSize: 14, lineHeight: 20, fontWeight: '500'},
33
- label_s_medium: {fontSize: 12, lineHeight: 18, fontWeight: '500'},
34
- label_xs_medium: {fontSize: 10, lineHeight: 14, fontWeight: '500'},
35
- action_default_bold: {fontSize: 16, lineHeight: 22, fontWeight: '700'},
36
- action_s_bold: {fontSize: 14, lineHeight: 20, fontWeight: '700'},
37
- action_xs_bold: {fontSize: 12, lineHeight: 18, fontWeight: '700'},
38
- action_xxs_bold: {fontSize: 10, lineHeight: 14, fontWeight: '700'},
33
+ label_default_medium: { fontSize: 14, lineHeight: 20, fontWeight: '500' },
34
+ label_s_medium: { fontSize: 12, lineHeight: 18, fontWeight: '500' },
35
+ label_xs_medium: { fontSize: 10, lineHeight: 14, fontWeight: '500' },
36
+ action_default_bold: { fontSize: 16, lineHeight: 22, fontWeight: '700' },
37
+ action_s_bold: { fontSize: 14, lineHeight: 20, fontWeight: '700' },
38
+ action_xs_bold: { fontSize: 12, lineHeight: 18, fontWeight: '700' },
39
+ action_xxs_bold: { fontSize: 10, lineHeight: 14, fontWeight: '700' },
39
40
  /**non-support**/
40
41
  headline_default: {
41
42
  fontSize: 28,
42
43
  lineHeight: 34,
43
44
  fontWeight: '600',
44
45
  },
45
- headline_s: {fontSize: 24, lineHeight: 32, fontWeight: '600'},
46
- headline_l: {fontSize: 32, lineHeight: 36, fontWeight: '600'},
47
- headline_xl: {fontSize: 36, lineHeight: 44, fontWeight: '600'},
48
- title_default: {fontSize: 20, lineHeight: 28, fontWeight: '700'},
49
- title_xs: {fontSize: 16, lineHeight: 22, fontWeight: '700'},
50
- title_s: {fontSize: 18, lineHeight: 28, fontWeight: '700'},
51
- header_default: {fontSize: 15, lineHeight: 22, fontWeight: '600'},
52
- header_xs: {fontSize: 12, lineHeight: 16, fontWeight: '600'},
53
- header_s: {fontSize: 14, lineHeight: 20, fontWeight: '600'},
54
- paragraph_default: {fontSize: 15, lineHeight: 22, fontWeight: '400'},
55
- paragraph_bold: {fontSize: 15, lineHeight: 22, fontWeight: '700'},
46
+ headline_s: { fontSize: 24, lineHeight: 32, fontWeight: '600' },
47
+ headline_l: { fontSize: 32, lineHeight: 36, fontWeight: '600' },
48
+ headline_xl: { fontSize: 36, lineHeight: 44, fontWeight: '600' },
49
+ title_default: { fontSize: 20, lineHeight: 28, fontWeight: '700' },
50
+ title_xs: { fontSize: 16, lineHeight: 22, fontWeight: '700' },
51
+ title_s: { fontSize: 18, lineHeight: 28, fontWeight: '700' },
52
+ header_default: { fontSize: 15, lineHeight: 22, fontWeight: '600' },
53
+ header_xs: { fontSize: 12, lineHeight: 16, fontWeight: '600' },
54
+ header_s: { fontSize: 14, lineHeight: 20, fontWeight: '600' },
55
+ paragraph_default: { fontSize: 15, lineHeight: 22, fontWeight: '400' },
56
+ paragraph_bold: { fontSize: 15, lineHeight: 22, fontWeight: '700' },
56
57
  paragraph_italic: {
57
58
  fontSize: 15,
58
59
  lineHeight: 22,
59
60
  fontWeight: '400',
60
61
  fontStyle: 'italic',
61
62
  },
62
- description_default: {fontSize: 14, lineHeight: 20, fontWeight: '400'},
63
- description_xs: {fontSize: 10, lineHeight: 14, fontWeight: '400'},
64
- description_s: {fontSize: 12, lineHeight: 16, fontWeight: '400'},
65
- label_default: {fontSize: 14, lineHeight: 20, fontWeight: '500'},
66
- label_xxs: {fontSize: 8, lineHeight: 12, fontWeight: '500'},
67
- label_xs: {fontSize: 10, lineHeight: 14, fontWeight: '500'},
68
- label_s: {fontSize: 12, lineHeight: 16, fontWeight: '500'},
69
- action_default: {fontSize: 16, lineHeight: 22, fontWeight: '700'},
70
- action_xxs: {fontSize: 10, lineHeight: 14, fontWeight: '700'},
71
- action_xs: {fontSize: 12, lineHeight: 16, fontWeight: '700'},
72
- action_s: {fontSize: 15, lineHeight: 22, fontWeight: '700'},
63
+ description_default: { fontSize: 14, lineHeight: 20, fontWeight: '400' },
64
+ description_xs: { fontSize: 10, lineHeight: 14, fontWeight: '400' },
65
+ description_s: { fontSize: 12, lineHeight: 16, fontWeight: '400' },
66
+ label_default: { fontSize: 14, lineHeight: 20, fontWeight: '500' },
67
+ label_xxs: { fontSize: 8, lineHeight: 12, fontWeight: '500' },
68
+ label_xs: { fontSize: 10, lineHeight: 14, fontWeight: '500' },
69
+ label_s: { fontSize: 12, lineHeight: 16, fontWeight: '500' },
70
+ action_default: { fontSize: 16, lineHeight: 22, fontWeight: '700' },
71
+ action_xxs: { fontSize: 10, lineHeight: 14, fontWeight: '700' },
72
+ action_xs: { fontSize: 12, lineHeight: 16, fontWeight: '700' },
73
+ action_s: { fontSize: 15, lineHeight: 22, fontWeight: '700' },
73
74
  };
package/Text/types.ts CHANGED
@@ -32,6 +32,7 @@ export type Typography =
32
32
  /**
33
33
  * end
34
34
  **/
35
+ | 'headline_l_bold'
35
36
  | 'headline_default_bold'
36
37
  | 'header_m_bold'
37
38
  | 'header_default_bold'
package/package.json CHANGED
@@ -1,35 +1,35 @@
1
1
  {
2
- "name": "@momo-kits/foundation",
3
- "version": "0.150.2-test.1",
4
- "description": "React Native Component Kits",
5
- "main": "index.ts",
6
- "scripts": {},
7
- "keywords": [
8
- "@momo-kits/foundation"
9
- ],
10
- "dependencies": {
11
- "react-native-fast-image": "git+https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-fast-image.git#v8.11.0",
12
- "@react-navigation/bottom-tabs": "7.4.2",
13
- "@react-navigation/core": "7.12.1",
14
- "@react-navigation/elements": "2.5.2",
15
- "@react-navigation/native": "7.1.14",
16
- "@react-navigation/routers": "7.4.1",
17
- "@react-navigation/stack": "7.4.2",
18
- "react-native-gesture-handler": "2.27.1",
19
- "react-native-linear-gradient": "git+https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-linear-gradient#v3.0.0",
20
- "react-native-reanimated": "4.1.0",
21
- "react-native-safe-area-context": "5.5.2",
22
- "@shopify/flash-list": "2.1.0"
23
- },
24
- "peerDependencies": {
25
- "react-native": "*"
26
- },
27
- "devDependencies": {
28
- "@momo-platform/versions": "4.1.11",
29
- "@types/color": "3.0.6"
30
- },
31
- "publishConfig": {
32
- "registry": "https://registry.npmjs.org/"
33
- },
34
- "license": "MoMo"
35
- }
2
+ "name": "@momo-kits/foundation",
3
+ "version": "0.151.1-beta.1",
4
+ "description": "React Native Component Kits",
5
+ "main": "index.ts",
6
+ "scripts": {},
7
+ "keywords": [
8
+ "@momo-kits/foundation"
9
+ ],
10
+ "dependencies": {
11
+ "react-native-fast-image": "git+https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-fast-image.git#v8.11.0",
12
+ "@react-navigation/bottom-tabs": "7.4.2",
13
+ "@react-navigation/core": "7.12.1",
14
+ "@react-navigation/elements": "2.5.2",
15
+ "@react-navigation/native": "7.1.14",
16
+ "@react-navigation/routers": "7.4.1",
17
+ "@react-navigation/stack": "7.4.2",
18
+ "react-native-gesture-handler": "2.27.1",
19
+ "react-native-linear-gradient": "git+https://oauth2:TGi6oBj-LdzsKpLXQSoH@gitlab.mservice.com.vn/momo-platform/public/react-native-linear-gradient#v3.0.0",
20
+ "react-native-reanimated": "4.1.0",
21
+ "react-native-safe-area-context": "5.5.2",
22
+ "@shopify/flash-list": "2.1.0"
23
+ },
24
+ "peerDependencies": {
25
+ "react-native": "*"
26
+ },
27
+ "devDependencies": {
28
+ "@momo-platform/versions": "4.1.11",
29
+ "@types/color": "3.0.6"
30
+ },
31
+ "publishConfig": {
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "license": "MoMo"
35
+ }