@momo-kits/foundation 0.81.1 → 0.81.2-beta.2

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,14 +1,13 @@
1
1
  import React, {Fragment, useContext, useEffect, useState} from 'react';
2
2
  import {
3
3
  Animated,
4
+ BackHandler,
4
5
  DeviceEventEmitter,
5
- Platform,
6
6
  StatusBar,
7
7
  StyleSheet,
8
8
  TouchableOpacity,
9
9
  View,
10
10
  } from 'react-native';
11
- import {TouchableOpacity as TouchableGesture} from 'react-native-gesture-handler';
12
11
  import {ApplicationContext} from './index';
13
12
  import {Colors, Radius, Spacing, Styles} from '../Consts';
14
13
  import {Image} from '../Image';
@@ -24,8 +23,7 @@ import {scaleSize, Text} from '../Text';
24
23
  import {Icon} from '../Icon';
25
24
  import {PopupNotify} from '../Popup';
26
25
 
27
- const Touchable =
28
- Platform.OS === 'android' ? TouchableGesture : TouchableOpacity;
26
+ const TRIGGER_ID = "'111154000000000000";
29
27
 
30
28
  const styles = StyleSheet.create({
31
29
  navigationButton: {
@@ -106,7 +104,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
106
104
  }
107
105
 
108
106
  return (
109
- <Touchable
107
+ <TouchableOpacity
110
108
  accessibilityLabel={`NavigationButton/${accessibilityLabel}`}
111
109
  style={[
112
110
  styles.navigationButton,
@@ -121,7 +119,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
121
119
  color={tintColor ?? theme.colors.text.default}
122
120
  size={20}
123
121
  />
124
- </Touchable>
122
+ </TouchableOpacity>
125
123
  );
126
124
  };
127
125
 
@@ -153,9 +151,30 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
153
151
  backgroundColor,
154
152
  accessibilityLabel = '',
155
153
  }) => {
156
- const {navigator} = useContext(ApplicationContext);
154
+ const {navigator, appInfo} = useContext(ApplicationContext);
155
+ const screen_name = navigator?.ref?.current?.getCurrentRoute().name;
156
+
157
+ useEffect(() => {
158
+ const backHandler = BackHandler.addEventListener(
159
+ 'hardwareBackPress',
160
+ goBack,
161
+ );
162
+
163
+ return () => backHandler.remove();
164
+ }, []);
165
+
157
166
  const goBack = () => {
158
167
  const canGoBack = navigator?.ref.current?.canGoBack();
168
+
169
+ if (appInfo) {
170
+ navigator?.maxApi?.trackEvent('service_icon_clicked', {
171
+ ...appInfo,
172
+ screen_name,
173
+ trigger_id: TRIGGER_ID,
174
+ icon_name: 'back',
175
+ });
176
+ }
177
+
159
178
  if (canGoBack) {
160
179
  if (preventBack) {
161
180
  navigator?.showModal({
@@ -181,6 +200,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
181
200
  DeviceEventEmitter.emit('dismiss', {requestId});
182
201
  }
183
202
  onPressLeftHeader?.();
203
+ return true;
184
204
  };
185
205
 
186
206
  return (
@@ -397,17 +417,21 @@ const HeaderToolkitAction: React.FC<any> = ({tintColor}) => {
397
417
  borderWidth: backgroundColor ? 0.5 : 0,
398
418
  },
399
419
  ]}>
400
- <Touchable accessibilityLabel={'Toolkit/More'} onPress={onMore}>
420
+ <TouchableOpacity
421
+ accessibilityLabel={'Toolkit/More'}
422
+ onPress={onMore}>
401
423
  <Icon color={tintColor} source="navigation_more_horiz" size={20} />
402
- </Touchable>
424
+ </TouchableOpacity>
403
425
  <View style={[styles.divider, {backgroundColor: tintColor}]} />
404
- <Touchable accessibilityLabel={`Toolkit/Close`} onPress={onClose}>
426
+ <TouchableOpacity
427
+ accessibilityLabel={`Toolkit/Close`}
428
+ onPress={onClose}>
405
429
  <Icon
406
430
  color={tintColor}
407
431
  source="16_navigation_close_circle"
408
432
  size={20}
409
433
  />
410
- </Touchable>
434
+ </TouchableOpacity>
411
435
  </View>
412
436
  </View>
413
437
  );
@@ -441,7 +465,7 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
441
465
  <TouchableOpacity onPress={onPress}>
442
466
  <Animated.View
443
467
  style={{
444
- aspectRatio: 1.75,
468
+ height: 210,
445
469
  opacity: opacity,
446
470
  transform: [{scale}, {translateY}],
447
471
  }}>
@@ -39,8 +39,10 @@ const ModalScreen: React.FC<any> = props => {
39
39
  };
40
40
 
41
41
  const Modal: React.FC<ModalParams> = props => {
42
- const {theme, navigator} = useContext(ApplicationContext);
43
- const {screen, barrierDismissible}: ModalParams = props.route.params;
42
+ const TRIGGER_ID = '111012100000000000';
43
+ const {theme, navigator, appInfo} = useContext(ApplicationContext);
44
+ const {screen, barrierDismissible, modalName}: ModalParams =
45
+ props.route.params;
44
46
  const Component = useRef(screen).current;
45
47
  const navigation = new Navigation(props.navigation, theme);
46
48
  const params = {
@@ -50,6 +52,37 @@ const Modal: React.FC<ModalParams> = props => {
50
52
 
51
53
  delete params.screen;
52
54
 
55
+ const navigationRoutes = props.navigation.getState()?.routes || [];
56
+ const routesLength = navigationRoutes.length;
57
+ let screen_name = navigationRoutes?.[0]?.params?.screen?.name;
58
+ if (routesLength > 1) {
59
+ screen_name = navigationRoutes[routesLength - 2]?.params?.screen?.name;
60
+ }
61
+ const error_code = (screen as Function)?.()?.props?.information;
62
+
63
+ useEffect(() => {
64
+ navigator?.maxApi?.trackEvent('service_component_displayed', {
65
+ ...appInfo,
66
+ screen_name,
67
+ trigger_id: TRIGGER_ID,
68
+ component_type: 'popup',
69
+ component_name: modalName,
70
+ error_code,
71
+ });
72
+
73
+ return () => {
74
+ navigator?.maxApi?.trackEvent('service_icon_clicked', {
75
+ ...appInfo,
76
+ screen_name,
77
+ trigger_id: '111154100000000001',
78
+ component_type: 'popup',
79
+ component_name: modalName,
80
+ error_code,
81
+ icon_name: 'close',
82
+ });
83
+ };
84
+ }, []);
85
+
53
86
  const onDismiss = () => {
54
87
  if (barrierDismissible) {
55
88
  return;
@@ -90,7 +123,7 @@ const Modal: React.FC<ModalParams> = props => {
90
123
 
91
124
  const BottomSheet: React.FC<BottomSheetParams> = props => {
92
125
  const insets = useSafeAreaInsets();
93
- const {theme, navigator} = useContext(ApplicationContext);
126
+ const {theme, navigator, appInfo} = useContext(ApplicationContext);
94
127
  const [keyboardSnap, setKeyboardSnap] = useState(0);
95
128
  const {screen, options, snapPoint}: BottomSheetParams = props.route.params;
96
129
  const Component = useRef(screen).current;
@@ -160,7 +193,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
160
193
  navigator?.pop();
161
194
  }
162
195
  }}
163
- bottomInset={bottomInset}
196
+ bottomInset={Platform.OS === 'ios' ? bottomInset : 0}
164
197
  handleComponent={null}
165
198
  backdropComponent={backdropComponent}>
166
199
  <BottomSheetView accessibilityLabel={'BottomSheet'}>
@@ -181,6 +214,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
181
214
  />
182
215
  </View>
183
216
  <NavigationContainer
217
+ appInfo={appInfo}
218
+ maxApi={navigator?.maxApi}
184
219
  theme={{
185
220
  ...theme,
186
221
  assets: {...theme.assets, headerBackground: undefined},
@@ -23,6 +23,7 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
23
23
  options,
24
24
  maxApi,
25
25
  localization,
26
+ appInfo,
26
27
  }) => {
27
28
  const navigationRef = React.useRef<NavigationContainerRef>(null);
28
29
  const navigator = useRef(new Navigator(navigationRef));
@@ -73,6 +74,7 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
73
74
  showGrid,
74
75
  language,
75
76
  translate,
77
+ appInfo,
76
78
  }}>
77
79
  <ReactNavigationContainer
78
80
  theme={{
@@ -1,12 +1,14 @@
1
- import React, {useContext, useLayoutEffect} from 'react';
1
+ import React, {useContext, useEffect, useLayoutEffect} from 'react';
2
2
  import {ScreenParams} from './types';
3
3
  import Navigation from './Navigation';
4
4
  import {ApplicationContext} from './index';
5
5
  import {GridSystem} from '../Layout';
6
6
 
7
+ const TRIGGER_ID = '111378000000000000';
8
+
7
9
  const StackScreen: React.FC<ScreenParams> = props => {
8
10
  const {screen: Component, options}: ScreenParams = props.route.params;
9
- const {theme, showGrid} = useContext(ApplicationContext);
11
+ const {theme, showGrid, navigator, appInfo} = useContext(ApplicationContext);
10
12
  const navigation = new Navigation(props.navigation, theme);
11
13
 
12
14
  const params = {
@@ -16,6 +18,24 @@ const StackScreen: React.FC<ScreenParams> = props => {
16
18
 
17
19
  delete params.screen;
18
20
 
21
+ const navigationRoutes = props.navigation.getState()?.routes || [];
22
+ const routesLength = navigationRoutes.length;
23
+ let pre_screen_name = navigationRoutes?.[0]?.params?.screen?.name;
24
+ if (routesLength > 1) {
25
+ pre_screen_name = navigationRoutes[routesLength - 2]?.params?.screen?.name;
26
+ }
27
+
28
+ useEffect(() => {
29
+ if (appInfo) {
30
+ navigator?.maxApi?.trackEvent('service_frame_displayed', {
31
+ ...appInfo,
32
+ screen_name: Component.name,
33
+ pre_screen_name,
34
+ trigger_id: TRIGGER_ID,
35
+ });
36
+ }
37
+ }, []);
38
+
19
39
  useLayoutEffect(() => {
20
40
  if (options) {
21
41
  navigation.setOptions(options);
@@ -68,6 +68,7 @@ export type Context = {
68
68
  showGrid?: boolean;
69
69
  language?: 'vi' | 'en';
70
70
  translate?: (key: string) => string;
71
+ appInfo: MiniAppInfo;
71
72
  };
72
73
 
73
74
  export type LocalizationObject = {
@@ -79,12 +80,20 @@ export type LocalizationObject = {
79
80
  };
80
81
  };
81
82
 
83
+ export type MiniAppInfo = {
84
+ feature_code: string;
85
+ app_id: string;
86
+ build_number: number;
87
+ flow_partner_id?: string;
88
+ };
89
+
82
90
  export type NavigationContainerProps = {
83
91
  screen: Screen;
84
92
  options?: NavigationOptions;
85
93
  theme: Theme;
86
- maxApi?: any;
94
+ maxApi: any;
87
95
  localization?: LocalizationObject;
96
+ appInfo: MiniAppInfo;
88
97
  };
89
98
 
90
99
  export type NavigationScreenProps = {
@@ -97,10 +106,12 @@ export type ScreenParams = {
97
106
  [key: string]: any;
98
107
  screen: Screen;
99
108
  options?: NavigationOptions;
109
+ tracking?: any;
100
110
  };
101
111
 
102
112
  export interface ModalParams extends ScreenParams {
103
113
  [key: string]: any;
114
+ modalName?: string;
104
115
  onDismiss?: () => void;
105
116
  barrierDismissible?: boolean;
106
117
  }
@@ -125,9 +136,7 @@ export interface NavigationOptions extends StackNavigationOptions {
125
136
  onPressLeftHeader?: () => void;
126
137
  surface?: boolean;
127
138
  hiddenBack?: boolean;
128
- headerTitleAlign?: 'left' | 'center';
129
139
  customTitle?: TitleCustomProps;
130
- animatedValue?: Animated.Value;
131
140
  }
132
141
 
133
142
  export interface HeaderBackProps extends NavigationButtonProps {
@@ -152,6 +152,10 @@ const Spacing = {
152
152
  M: 12,
153
153
  L: 16,
154
154
  XL: 24,
155
+ XXL: 32,
156
+ Size48: 48,
157
+ Size56: 56,
158
+ Size64: 64,
155
159
  };
156
160
 
157
161
  const Radius = {
@@ -103,7 +103,7 @@ const InputOTP: FC<InputOTPProps> = ({
103
103
  };
104
104
 
105
105
  const renderInputs = (length: number) => {
106
- const TextInputs = [];
106
+ const TextInputs: React.ReactNode[] = [];
107
107
  for (let i = 0; i < length; i++) {
108
108
  TextInputs.push(
109
109
  <View>
package/Layout/Card.tsx CHANGED
@@ -8,6 +8,7 @@ import {Image} from '../Image';
8
8
  import {ApplicationContext} from '../Application';
9
9
  import styles from './styles';
10
10
  import {Colors, Shadow, Spacing} from '../Consts';
11
+ import ItemList from './ItemList';
11
12
 
12
13
  export interface CardProps extends ViewProps {
13
14
  /**
@@ -15,6 +16,9 @@ export interface CardProps extends ViewProps {
15
16
  */
16
17
  backgroundImage?: string;
17
18
 
19
+ /**
20
+ * Optional. Use shadow for the card.
21
+ */
18
22
  useShadow?: boolean;
19
23
  }
20
24
 
@@ -155,7 +159,7 @@ const Card: React.FC<CardProps> = ({
155
159
  style={styles.imageBackground}
156
160
  />
157
161
  )}
158
- {renderView(validateChildren(children, [Item]))}
162
+ {renderView(validateChildren(children, [Item, ItemList]))}
159
163
  {showGrid && renderOverlay()}
160
164
  </View>
161
165
  </GridContext.Provider>
package/Layout/Item.tsx CHANGED
@@ -5,7 +5,7 @@ import {GridContext} from './index';
5
5
  import {ApplicationContext} from '../Application';
6
6
  import styles from './styles';
7
7
 
8
- type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
8
+ export type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
9
9
 
10
10
  export interface ItemProps extends ViewProps {
11
11
  /**
@@ -0,0 +1,56 @@
1
+ import React, {FC, useContext} from 'react';
2
+ import {FlatList, FlatListProps, View} from 'react-native';
3
+ import Item, {SpanNumber} from './Item';
4
+ import {GridContext} from './index';
5
+ import styles from './styles';
6
+
7
+ export interface ItemListProps extends FlatListProps<any> {
8
+ /**
9
+ * Require key support number columns change grid system
10
+ */
11
+
12
+ key: string;
13
+ /**
14
+ * Represents the width of the Card component in terms of span numbers (1-12).
15
+ */
16
+ widthSpan?: SpanNumber;
17
+ }
18
+
19
+ const ItemList: FC<ItemListProps> = ({
20
+ key,
21
+ renderItem,
22
+ widthSpan,
23
+ style,
24
+ contentContainerStyle,
25
+ ...props
26
+ }) => {
27
+ const {gutterSize, numberOfColumns} = useContext(GridContext);
28
+ const widthItem = (widthSpan ?? numberOfColumns) as SpanNumber;
29
+ const numColumns = Math.floor(numberOfColumns / widthItem);
30
+ const indexBegin = props?.data?.map((_i, index) => index * numColumns);
31
+ const _renderItem = (item: any) => {
32
+ if (indexBegin?.includes(item?.index)) {
33
+ return <Item widthSpan={widthItem}>{renderItem?.(item)}</Item>;
34
+ }
35
+ return (
36
+ <View style={{paddingLeft: gutterSize}}>
37
+ <Item widthSpan={widthItem}>{renderItem?.(item)}</Item>
38
+ </View>
39
+ );
40
+ };
41
+
42
+ return (
43
+ <FlatList
44
+ {...props}
45
+ key={`${key ?? 'ItemList'}${numColumns}}`}
46
+ numColumns={numColumns}
47
+ renderItem={_renderItem}
48
+ horizontal={false}
49
+ ItemSeparatorComponent={() => <View style={{height: gutterSize}} />}
50
+ style={[style, styles.protectedStyle]}
51
+ contentContainerStyle={[contentContainerStyle, styles.protectedStyle]}
52
+ />
53
+ );
54
+ };
55
+
56
+ export default ItemList;
package/Layout/Screen.tsx CHANGED
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  Animated,
3
- Image,
4
3
  KeyboardAvoidingView,
5
4
  NativeScrollEvent,
6
5
  NativeSyntheticEvent,
@@ -28,6 +27,7 @@ import {
28
27
  import Navigation from '../Application/Navigation';
29
28
  import styles from './styles';
30
29
  import {AnimatedHeader, NavigationOptions} from '../Application/types';
30
+ import {Image} from '../Image';
31
31
 
32
32
  export interface ScreenProps extends ViewProps {
33
33
  children: React.ReactNode;
@@ -259,6 +259,7 @@ const Screen = forwardRef(
259
259
  uri: headerBackground,
260
260
  }}
261
261
  style={styles.extendedHeader}
262
+ loading={false}
262
263
  />
263
264
  <View style={{height: heightHeader}} />
264
265
  </>
@@ -7,6 +7,7 @@ import Item from './Item';
7
7
  import {ApplicationContext} from '../Application';
8
8
  import styles from './styles';
9
9
  import {Image} from '../Image';
10
+ import ItemList from './ItemList';
10
11
 
11
12
  export interface SectionProps extends ViewProps {
12
13
  /**
@@ -168,7 +169,7 @@ const Section: React.FC<SectionProps> = ({
168
169
  style={styles.imageBackground}
169
170
  />
170
171
  )}
171
- {renderView(validateChildren(children, [Item]))}
172
+ {renderView(validateChildren(children, [Item, ItemList]))}
172
173
  {showGrid && renderOverlay()}
173
174
  </View>
174
175
  </GridContext.Provider>
package/Layout/index.ts CHANGED
@@ -5,6 +5,7 @@ import {validateChildren} from './utils';
5
5
  import {createContext} from 'react';
6
6
  import {GridContextProps} from './types';
7
7
  import Item from './Item';
8
+ import ItemList from './ItemList';
8
9
 
9
10
  const GridContext = createContext<GridContextProps>({
10
11
  numberOfColumns: 12,
@@ -14,4 +15,12 @@ const GridContext = createContext<GridContextProps>({
14
15
  getSizeSpan: () => 0,
15
16
  });
16
17
 
17
- export {GridContext, Card, Section, GridSystem, validateChildren, Item};
18
+ export {
19
+ GridContext,
20
+ Card,
21
+ Section,
22
+ GridSystem,
23
+ validateChildren,
24
+ Item,
25
+ ItemList,
26
+ };
package/Layout/styles.ts CHANGED
@@ -77,4 +77,20 @@ export default StyleSheet.create({
77
77
  right: 0,
78
78
  borderRadius: Spacing.M,
79
79
  },
80
+ protectedStyle: {
81
+ marginTop: undefined,
82
+ marginBottom: undefined,
83
+ marginLeft: undefined,
84
+ marginRight: undefined,
85
+ paddingTop: undefined,
86
+ paddingBottom: undefined,
87
+ paddingLeft: undefined,
88
+ paddingRight: undefined,
89
+ padding: undefined,
90
+ margin: undefined,
91
+ marginHorizontal: undefined,
92
+ marginVertical: undefined,
93
+ paddingHorizontal: undefined,
94
+ paddingVertical: undefined,
95
+ },
80
96
  });
package/Layout/utils.ts CHANGED
@@ -8,7 +8,7 @@ import React, {Fragment, ReactElement, useEffect, useState} from 'react';
8
8
  */
9
9
  const validateChildren = (
10
10
  children: React.ReactNode | React.ReactNode[],
11
- childrenTypes: Array<React.FC>,
11
+ childrenTypes: Array<React.ReactNode>,
12
12
  ) => {
13
13
  const validateType = (child: React.ReactElement) => {
14
14
  const valid = childrenTypes.some(
@@ -17,7 +17,7 @@ const validateChildren = (
17
17
  if (!valid) {
18
18
  console.warn(
19
19
  `Wrong children type for this component, expected are ${childrenTypes
20
- .map(type => type.displayName)
20
+ .map(item => item)
21
21
  .join(', ')}.`,
22
22
  );
23
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.81.1",
3
+ "version": "0.81.2-beta.2",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -10,7 +10,7 @@
10
10
  "@momo-kits/foundation"
11
11
  ],
12
12
  "dependencies": {
13
- "@gorhom/bottom-sheet": "2.4.1",
13
+ "@gorhom/bottom-sheet": "git+https://gitlab.com/dung.huynh1/bottom-sheet.git#main",
14
14
  "react-native-safe-area-context": "3.1.4",
15
15
  "react-native-linear-gradient": "2.5.6",
16
16
  "react-native-gesture-handler": "1.10.3",
@@ -38,6 +38,7 @@
38
38
  "metro-react-native-babel-preset": "^0.64.0",
39
39
  "react-test-renderer": "17.0.1",
40
40
  "typescript": "^4.0.3",
41
+ "@momo-platform/versions": "4.1.11",
41
42
  "react-scanner": "^1.1.0"
42
43
  },
43
44
  "author": "@momo-kits/foundation",
package/tsconfig.json CHANGED
@@ -20,6 +20,5 @@
20
20
  "skipLibCheck": true,
21
21
  "strict": true,
22
22
  "target": "esnext",
23
- "verbatimModuleSyntax": false
24
23
  }
25
24
  }