@momo-kits/foundation 0.169.5 → 0.169.6-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.
- package/Application/BottomSheet.hooks.ts +8 -8
- package/Application/BottomSheet.parts.tsx +34 -44
- package/Application/BottomSheet.tsx +26 -35
- package/Application/BottomTab/ActiveIndicator.tsx +14 -15
- package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
- package/Application/BottomTab/BottomTabBar.tsx +40 -63
- package/Application/BottomTab/BottomTabInset.tsx +4 -8
- package/Application/BottomTab/CurvedContainer.tsx +20 -16
- package/Application/BottomTab/CustomBottomTabItem.tsx +13 -16
- package/Application/BottomTab/DuoBarCompression.tsx +19 -13
- package/Application/BottomTab/DuoRailTabBar.tsx +13 -17
- package/Application/BottomTab/DuoTabBar.tsx +24 -12
- package/Application/BottomTab/TabBarIcon.tsx +8 -13
- package/Application/BottomTab/TabScreen.tsx +5 -1
- package/Application/BottomTab/index.tsx +30 -38
- package/Application/BottomTab/metrics.ts +46 -24
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +0 -4
- package/Application/Components/DuoGlassBackground.tsx +4 -5
- package/Application/Components/HeaderAnimated.tsx +0 -4
- package/Application/Components/HeaderBackground.tsx +0 -4
- package/Application/Components/HeaderExtendHeader.tsx +10 -11
- package/Application/Components/HeaderLeft.tsx +1 -2
- package/Application/Components/HeaderRight.tsx +1 -2
- package/Application/Components/HeaderTitle.shared.ts +9 -11
- package/Application/Components/HeaderTitle.tsx +3 -4
- package/Application/Components/HeaderToolkitAction.tsx +20 -17
- package/Application/Components/NavigationButton.tsx +1 -2
- package/Application/Components/SearchHeader.tsx +10 -17
- package/Application/Components/TitleJourney.tsx +2 -2
- package/Application/Components/TitleLocation.tsx +2 -2
- package/Application/Components/TitleUser.tsx +2 -2
- package/Application/Components/useToolkitActions.tsx +32 -17
- package/Application/Localize.ts +0 -7
- package/Application/ModalScreen.tsx +3 -9
- package/Application/Navigation.ts +16 -14
- package/Application/NavigationContainer.tsx +16 -19
- package/Application/Navigator.ts +48 -20
- package/Application/StackScreen.tracking.ts +26 -11
- package/Application/StackScreen.tsx +35 -8
- package/Application/TooltipPortal.tsx +5 -12
- package/Application/WidgetContainer.tsx +4 -10
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/modalLayout.ts +14 -13
- package/Application/navigationOptions.tsx +17 -21
- package/Application/types.tabs.ts +0 -1
- package/Badge/Badge.tsx +0 -5
- package/Badge/BadgeDot.tsx +0 -1
- package/Badge/BadgeDotAnimation.tsx +7 -15
- package/Badge/BadgeRibbon.tsx +38 -46
- package/Badge/Shape.tsx +0 -2
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +16 -5
- package/Button/utils.ts +15 -9
- package/CheckBox/index.tsx +0 -4
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +0 -1
- package/Divider/DashDivider.tsx +0 -1
- package/Divider/index.tsx +0 -4
- package/FoundationList/index.tsx +0 -4
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +12 -4
- package/Image/index.tsx +7 -9
- package/Input/Input.tsx +9 -8
- package/Input/InputDropDown.tsx +12 -10
- package/Input/InputMoney.tsx +8 -12
- package/Input/InputMoneyView.tsx +7 -14
- package/Input/InputOTP.tsx +33 -35
- package/Input/InputPhoneNumber.tsx +9 -8
- package/Input/InputSearch.tsx +6 -4
- package/Input/InputTextArea.tsx +0 -4
- package/Input/SearchTextTyping.tsx +0 -4
- package/Input/SystemTextInput.tsx +2 -7
- package/Input/TextTyping.tsx +0 -4
- package/Input/common.tsx +12 -26
- package/Input/index.tsx +20 -17
- package/Input/moneyUtils.ts +4 -10
- package/Input/styles.ts +5 -0
- package/Input/utils.ts +12 -14
- package/Layout/Card.tsx +4 -7
- package/Layout/FloatingButton.tsx +0 -4
- package/Layout/Item.tsx +2 -6
- package/Layout/ItemList.tsx +7 -9
- package/Layout/ItemSectionList.tsx +1 -2
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +58 -56
- package/Layout/Section.tsx +5 -8
- package/Layout/TrackingScope.tsx +0 -1
- package/Layout/screenContent.tsx +2 -4
- package/Layout/screenFooter.tsx +18 -28
- package/Layout/screenHeader.tsx +31 -34
- package/Layout/screenQuickReturn.tsx +20 -17
- package/Layout/screenScroll.ts +7 -4
- package/Layout/screenTypes.ts +2 -3
- package/Layout/types.ts +4 -2
- package/Layout/utils.ts +37 -31
- package/Loader/DotLoader.tsx +0 -4
- package/Loader/Loader.tsx +0 -4
- package/Loader/ProgressBar.tsx +0 -1
- package/Loader/Spinner.tsx +0 -4
- package/Loader/utils.ts +7 -11
- package/Pagination/Dot.tsx +0 -4
- package/Pagination/PaginationDot.tsx +0 -1
- package/Pagination/PaginationNumber.tsx +3 -5
- package/Pagination/PaginationScroll.tsx +0 -5
- package/Pagination/PaginationWhiteDot.tsx +0 -1
- package/Pagination/index.tsx +0 -4
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +48 -44
- package/Popup/PopupPromotion.tsx +7 -4
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +0 -4
- package/Radio/index.tsx +0 -4
- package/Skeleton/index.tsx +0 -8
- package/Switch/index.tsx +0 -1
- package/Tag/index.tsx +1 -5
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +3 -14
- package/Text/styles.ts +1 -1
- package/Text/types.ts +6 -1
- package/Text/utils.ts +0 -9
- package/Title/index.tsx +32 -41
- package/ViewBoundary/index.tsx +2 -4
- package/index.ts +6 -0
- package/package.json +1 -1
|
@@ -27,10 +27,6 @@ import { isIPhoneDuo } from './utils';
|
|
|
27
27
|
import { useModalLayout } from './modalLayout';
|
|
28
28
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* Screen of the `Modal` route: a bottom sheet when pushed by
|
|
32
|
-
* `showBottomSheet`, otherwise a centered popup.
|
|
33
|
-
*/
|
|
34
30
|
const ModalScreen: React.FC<any> = props => {
|
|
35
31
|
if (props.route?.params?.isBottomSheet) {
|
|
36
32
|
return <BottomSheet {...props} />;
|
|
@@ -38,10 +34,6 @@ const ModalScreen: React.FC<any> = props => {
|
|
|
38
34
|
return <Modal {...props} />;
|
|
39
35
|
};
|
|
40
36
|
|
|
41
|
-
/**
|
|
42
|
-
* Centered popup that fades and scales in over a scrim. On foldables it is
|
|
43
|
-
* centered in the modal placement area, at most a popup wide.
|
|
44
|
-
*/
|
|
45
37
|
const Modal: React.FC<ModalParams> = props => {
|
|
46
38
|
const { navigator } = useContext(ApplicationContext);
|
|
47
39
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -58,7 +50,9 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
58
50
|
const isDuo = isIPhoneDuo();
|
|
59
51
|
const { isFoldable, placement, popup, scrimOpacity } = useModalLayout();
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Fire auto_popup_displayed tracking event on mount
|
|
55
|
+
*/
|
|
62
56
|
useEffect(() => {
|
|
63
57
|
navigator?.maxApi?.getDataObserver?.('current_screen', (curScreen: any) => {
|
|
64
58
|
context.autoTracking({
|
|
@@ -11,10 +11,6 @@ export const WHITE_LIST = [
|
|
|
11
11
|
'vn.momo.ekyc',
|
|
12
12
|
];
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Screen-level navigation handle given to features; `setOptions` enforces the
|
|
16
|
-
* design system's header rules before applying React Navigation options.
|
|
17
|
-
*/
|
|
18
14
|
class Navigation {
|
|
19
15
|
instance: NavigationProp<any>;
|
|
20
16
|
context: any;
|
|
@@ -25,33 +21,39 @@ class Navigation {
|
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* navigation options filter by design system rule
|
|
25
|
+
* @param params
|
|
30
26
|
*/
|
|
31
27
|
private filterParams = (params: NavigationOptions) => {
|
|
32
|
-
const isWhiteListed = WHITE_LIST.includes(this.context?.appId);
|
|
33
|
-
const rightType = typeof params.headerRight;
|
|
34
|
-
const titleType = typeof params.headerTitle;
|
|
35
|
-
const isTitleRenderer = titleType !== 'string' && titleType !== 'object';
|
|
36
|
-
|
|
37
28
|
if (params.headerLeft) {
|
|
38
29
|
console.warn('headerLeft not allow override render by design system!');
|
|
39
30
|
delete params.headerLeft;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
|
-
if (
|
|
33
|
+
if (
|
|
34
|
+
params.headerRight &&
|
|
35
|
+
typeof params.headerRight !== 'object' &&
|
|
36
|
+
!WHITE_LIST.includes(this.context?.appId)
|
|
37
|
+
) {
|
|
43
38
|
console.warn('headerRight not allow type func render by design system!');
|
|
44
39
|
delete params.headerRight;
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
if (
|
|
42
|
+
if (
|
|
43
|
+
params.headerTitle &&
|
|
44
|
+
!['string', 'object'].includes(typeof params.headerTitle) &&
|
|
45
|
+
!WHITE_LIST.includes(this.context?.appId)
|
|
46
|
+
) {
|
|
48
47
|
console.warn('headerTitle not allow type func render by design system!');
|
|
49
48
|
delete params.headerTitle;
|
|
50
49
|
}
|
|
51
50
|
return params;
|
|
52
51
|
};
|
|
53
52
|
|
|
54
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* navigation setOptions support props NavigationOptions base on react-navigation options
|
|
55
|
+
* @param params
|
|
56
|
+
*/
|
|
55
57
|
setOptions = (params: NavigationOptions) => {
|
|
56
58
|
const options = getOptions(this.filterParams(params));
|
|
57
59
|
this.instance.setOptions(options as StackNavigationOptions);
|
|
@@ -33,10 +33,6 @@ import { ReservedRegionsProvider } from '../Layout/ReservedRegions';
|
|
|
33
33
|
|
|
34
34
|
const Stack = createStackNavigator();
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Root of a mini app: safe area, reserved regions and the merged mini app
|
|
38
|
-
* context around the kit's navigator.
|
|
39
|
-
*/
|
|
40
36
|
const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
41
37
|
screen,
|
|
42
38
|
theme = defaultTheme,
|
|
@@ -96,10 +92,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
96
92
|
);
|
|
97
93
|
};
|
|
98
94
|
|
|
99
|
-
/**
|
|
100
|
-
* Stack navigator with its `Stack`, `Dialog` and `Modal` routes, under an
|
|
101
|
-
* `ApplicationContext` carrying the navigator, themed fonts and `translate`.
|
|
102
|
-
*/
|
|
103
95
|
const Navigation: React.FC<any> = ({
|
|
104
96
|
screen,
|
|
105
97
|
theme = defaultTheme,
|
|
@@ -117,10 +109,9 @@ const Navigation: React.FC<any> = ({
|
|
|
117
109
|
const syncCanGoBack = useNativeCanGoBack();
|
|
118
110
|
const isWhiteList = isWhiteListDesignSystem(context);
|
|
119
111
|
const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
112
|
+
const navigationFontFamily = isWhiteList
|
|
113
|
+
? 'MomoTrustSans-Regular'
|
|
114
|
+
: 'SFProText';
|
|
124
115
|
let headerBackground = context?.designSystemConfig?.config?.headerBar;
|
|
125
116
|
let headerGradient = theme.colors?.gradient;
|
|
126
117
|
|
|
@@ -131,9 +122,15 @@ const Navigation: React.FC<any> = ({
|
|
|
131
122
|
headerGradient = context?.designSystemConfig?.config?.headerGradient;
|
|
132
123
|
}
|
|
133
124
|
|
|
125
|
+
/**
|
|
126
|
+
* inject data for navigator
|
|
127
|
+
*/
|
|
134
128
|
navigator.current.maxApi = maxApi;
|
|
135
129
|
|
|
136
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* translate context method
|
|
132
|
+
* @param data
|
|
133
|
+
*/
|
|
137
134
|
const translate = (data?: string | { vi: string; en: string }) => {
|
|
138
135
|
if (data && typeof data !== 'string') {
|
|
139
136
|
return localize?.translateData(data);
|
|
@@ -231,12 +228,12 @@ const Navigation: React.FC<any> = ({
|
|
|
231
228
|
backgroundColor: theme.colors.background.default,
|
|
232
229
|
},
|
|
233
230
|
headerLeft: (props: any) => <HeaderLeft {...props} floating />,
|
|
234
|
-
headerBackground: (props: any) =>
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
headerRight: (props: any) =>
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
headerBackground: (props: any) => (
|
|
232
|
+
<HeaderBackground {...props} />
|
|
233
|
+
),
|
|
234
|
+
headerRight: (props: any) => (
|
|
235
|
+
<HeaderRight {...props} floating />
|
|
236
|
+
),
|
|
240
237
|
headerTintColor: Colors.black_17,
|
|
241
238
|
...TransitionPresets.ModalTransition,
|
|
242
239
|
}}
|
package/Application/Navigator.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import {CommonActions, StackActions} from '@react-navigation/native';
|
|
2
2
|
import {BottomSheetParams, ModalParams, ScreenParams} from './types';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Imperative navigation API exposed to features through `ApplicationContext`.
|
|
6
|
-
* Every action is a no-op until the container reports ready.
|
|
7
|
-
*/
|
|
8
4
|
class Navigator {
|
|
9
5
|
ref?: any;
|
|
10
6
|
isReady?: any;
|
|
@@ -18,28 +14,40 @@ class Navigator {
|
|
|
18
14
|
this.isWidget = isWidget;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* push new stack screen
|
|
19
|
+
* @param params
|
|
20
|
+
*/
|
|
22
21
|
push = (params: ScreenParams) => {
|
|
23
22
|
if (this.isReady.current) {
|
|
24
23
|
this.ref.current?.dispatch?.(StackActions.push('Stack', params));
|
|
25
24
|
}
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* replace current screen with new screen
|
|
29
|
+
* @param params
|
|
30
|
+
*/
|
|
29
31
|
replace = (params: ScreenParams) => {
|
|
30
32
|
if (this.isReady.current) {
|
|
31
33
|
this.ref.current?.dispatch?.(StackActions.replace('Stack', params));
|
|
32
34
|
}
|
|
33
35
|
};
|
|
34
36
|
|
|
35
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* pop to dismiss a screen
|
|
39
|
+
* @param count
|
|
40
|
+
*/
|
|
36
41
|
pop = (count?: number) => {
|
|
37
42
|
if (this.isReady.current) {
|
|
38
43
|
this.ref.current?.dispatch?.(StackActions.pop(count ?? 1));
|
|
39
44
|
}
|
|
40
45
|
};
|
|
41
46
|
|
|
42
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* present a new screen, show from bottom iOS
|
|
49
|
+
* @param params
|
|
50
|
+
*/
|
|
43
51
|
present = (params: ScreenParams) => {
|
|
44
52
|
if (this.isReady.current) {
|
|
45
53
|
this.ref.current?.dispatch?.(StackActions.push('Dialog', params));
|
|
@@ -47,8 +55,9 @@ class Navigator {
|
|
|
47
55
|
};
|
|
48
56
|
|
|
49
57
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
58
|
+
* show a modal popup
|
|
59
|
+
* @param params
|
|
60
|
+
* @param onError
|
|
52
61
|
*/
|
|
53
62
|
showModal = (params: ModalParams, onError?: (error: string) => void) => {
|
|
54
63
|
try {
|
|
@@ -67,7 +76,11 @@ class Navigator {
|
|
|
67
76
|
}
|
|
68
77
|
};
|
|
69
78
|
|
|
70
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* show a bottom sheet
|
|
81
|
+
* @param params
|
|
82
|
+
* @param onError
|
|
83
|
+
*/
|
|
71
84
|
showBottomSheet = (
|
|
72
85
|
params: BottomSheetParams,
|
|
73
86
|
onError?: (error: string) => void
|
|
@@ -89,21 +102,31 @@ class Navigator {
|
|
|
89
102
|
}
|
|
90
103
|
};
|
|
91
104
|
|
|
92
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* pop all screen route
|
|
107
|
+
*/
|
|
93
108
|
popToTop = () => {
|
|
94
109
|
if (this.isReady.current) {
|
|
95
110
|
this.ref.current?.dispatch?.(StackActions.popToTop());
|
|
96
111
|
}
|
|
97
112
|
};
|
|
98
113
|
|
|
99
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* navigate route name bottom tab only
|
|
116
|
+
* @param name
|
|
117
|
+
*/
|
|
100
118
|
navigate = (name: string) => {
|
|
101
119
|
if (this.isReady.current) {
|
|
102
|
-
this.ref.current?.dispatch?.(
|
|
120
|
+
this.ref.current?.dispatch?.(
|
|
121
|
+
CommonActions.navigate(name)
|
|
122
|
+
);
|
|
103
123
|
}
|
|
104
124
|
};
|
|
105
125
|
|
|
106
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* reset a navigation flow with new screen
|
|
128
|
+
* @param params
|
|
129
|
+
*/
|
|
107
130
|
reset = (params: ScreenParams) => {
|
|
108
131
|
if (this.isReady.current) {
|
|
109
132
|
this.ref.current?.dispatch?.(
|
|
@@ -121,21 +144,26 @@ class Navigator {
|
|
|
121
144
|
}
|
|
122
145
|
};
|
|
123
146
|
|
|
124
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* dismiss a feature data
|
|
149
|
+
* @param data
|
|
150
|
+
*/
|
|
125
151
|
setDismissData = (data: any) => {
|
|
126
152
|
this.dismissData = data;
|
|
127
153
|
};
|
|
128
154
|
|
|
129
155
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
156
|
+
* set app context using for features of kit
|
|
157
|
+
* @param context
|
|
132
158
|
*/
|
|
133
|
-
setCurrentContext = (
|
|
159
|
+
setCurrentContext = (context: {
|
|
134
160
|
code?: string;
|
|
135
161
|
name?: {vi: string; en: string};
|
|
136
162
|
description?: {vi: string; en: string};
|
|
137
163
|
icon?: string;
|
|
138
|
-
}) => {
|
|
164
|
+
}) => {
|
|
165
|
+
console.log(context);
|
|
166
|
+
};
|
|
139
167
|
}
|
|
140
168
|
|
|
141
169
|
export default Navigator;
|
|
@@ -8,17 +8,15 @@ type ScreenTrackingDeps = {
|
|
|
8
8
|
screenName: string;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Auto-tracking reporters of a stack screen. Each event fires at most once per
|
|
13
|
-
* screen, guarded by the `release*` flags on `tracking.current`.
|
|
14
|
-
*/
|
|
15
11
|
export const createScreenTracking = ({
|
|
16
12
|
tracking,
|
|
17
13
|
context,
|
|
18
14
|
navigator,
|
|
19
15
|
screenName,
|
|
20
16
|
}: ScreenTrackingDeps) => {
|
|
21
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* tracking for screen load
|
|
19
|
+
*/
|
|
22
20
|
const onScreenLoad = () => {
|
|
23
21
|
if (!tracking.current?.releaseLoad) {
|
|
24
22
|
let timeLoad = tracking.current.timeLoad;
|
|
@@ -44,22 +42,29 @@ export const createScreenTracking = ({
|
|
|
44
42
|
);
|
|
45
43
|
tracking.current.releaseLoad = true;
|
|
46
44
|
|
|
45
|
+
/**
|
|
46
|
+
* debug
|
|
47
|
+
*/
|
|
47
48
|
navigator?.maxApi?.showToastDebug?.({
|
|
48
49
|
appId: context.appId,
|
|
49
50
|
message: `${screenName} screen_load_time ${timeLoad}`,
|
|
50
51
|
type: 'ERROR',
|
|
51
52
|
});
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (
|
|
54
|
+
__DEV__ &&
|
|
55
|
+
tracking.current.lastElement?.children?.current?.length > 0
|
|
56
|
+
) {
|
|
54
57
|
Alert.alert(
|
|
55
58
|
`${screenName}- load ${timeLoad}ms`,
|
|
56
|
-
JSON.stringify(
|
|
59
|
+
JSON.stringify(tracking.current.lastElement?.children?.current),
|
|
57
60
|
);
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
};
|
|
61
64
|
|
|
62
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* tracking for screen load
|
|
67
|
+
*/
|
|
63
68
|
const onScreenInteraction = () => {
|
|
64
69
|
if (!tracking.current?.releaseInteraction) {
|
|
65
70
|
let timeLoad = tracking.current.timeLoad;
|
|
@@ -87,6 +92,9 @@ export const createScreenTracking = ({
|
|
|
87
92
|
);
|
|
88
93
|
tracking.current.releaseInteraction = true;
|
|
89
94
|
|
|
95
|
+
/**
|
|
96
|
+
* debug toast
|
|
97
|
+
*/
|
|
90
98
|
navigator?.maxApi?.showToastDebug?.({
|
|
91
99
|
appId: context.appId,
|
|
92
100
|
message: `${screenName} screen_interaction_time ${tracking.current.timeInteraction}`,
|
|
@@ -95,7 +103,9 @@ export const createScreenTracking = ({
|
|
|
95
103
|
}
|
|
96
104
|
};
|
|
97
105
|
|
|
98
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* tracking for first interaction by user
|
|
108
|
+
*/
|
|
99
109
|
const onFirstInteraction = (action: string) => {
|
|
100
110
|
if (!tracking.current?.releaseUserInteraction) {
|
|
101
111
|
const timeLoad = tracking.current.endTime - tracking.current.startTime;
|
|
@@ -109,6 +119,9 @@ export const createScreenTracking = ({
|
|
|
109
119
|
});
|
|
110
120
|
tracking.current.releaseUserInteraction = true;
|
|
111
121
|
|
|
122
|
+
/**
|
|
123
|
+
* debug
|
|
124
|
+
*/
|
|
112
125
|
navigator?.maxApi?.showToastDebug?.({
|
|
113
126
|
appId: context.appId,
|
|
114
127
|
message: `${screenName} user_interaction ${timeLoad}`,
|
|
@@ -117,7 +130,9 @@ export const createScreenTracking = ({
|
|
|
117
130
|
}
|
|
118
131
|
};
|
|
119
132
|
|
|
120
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* tracking for loading screen
|
|
135
|
+
*/
|
|
121
136
|
const onScreenLoading = () => {
|
|
122
137
|
const start = tracking.current.timeStartLoading;
|
|
123
138
|
const end = tracking.current.timeEndLoading;
|
|
@@ -12,8 +12,9 @@ import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
|
|
|
12
12
|
const runAfterInteractions = InteractionManager.runAfterInteractions;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* container for stack screen
|
|
16
|
+
* @param props
|
|
17
|
+
* @constructor
|
|
17
18
|
*/
|
|
18
19
|
const StackScreen: React.FC<any> = props => {
|
|
19
20
|
const { navigator } = useContext(ApplicationContext);
|
|
@@ -63,12 +64,18 @@ const StackScreen: React.FC<any> = props => {
|
|
|
63
64
|
|
|
64
65
|
const screenName = Component?.name || Component?.type?.name || 'Invalid';
|
|
65
66
|
|
|
67
|
+
/**
|
|
68
|
+
* set options for screen
|
|
69
|
+
*/
|
|
66
70
|
useLayoutEffect(() => {
|
|
67
71
|
if (options) {
|
|
68
72
|
navigation.setOptions(options);
|
|
69
73
|
}
|
|
70
74
|
}, [navigation, options]);
|
|
71
75
|
|
|
76
|
+
/**
|
|
77
|
+
* tracking for screen
|
|
78
|
+
*/
|
|
72
79
|
useEffect(() => {
|
|
73
80
|
let focusScreen: any;
|
|
74
81
|
let onFocusApp: any;
|
|
@@ -142,7 +149,6 @@ const StackScreen: React.FC<any> = props => {
|
|
|
142
149
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
143
150
|
}, []);
|
|
144
151
|
|
|
145
|
-
/** Reports a navigation to this screen: `back` if it was shown before. */
|
|
146
152
|
const onScreenNavigated = (pre: string, current: string) => {
|
|
147
153
|
if (!isBackgroundToForeground) {
|
|
148
154
|
const item: any = {
|
|
@@ -160,6 +166,9 @@ const StackScreen: React.FC<any> = props => {
|
|
|
160
166
|
|
|
161
167
|
tracking.current.mounted = true;
|
|
162
168
|
|
|
169
|
+
/**
|
|
170
|
+
* debug toast
|
|
171
|
+
*/
|
|
163
172
|
navigator?.maxApi?.showToastDebug?.({
|
|
164
173
|
appId: context.appId,
|
|
165
174
|
message: `${screenName} screen_navigated`,
|
|
@@ -181,7 +190,9 @@ const StackScreen: React.FC<any> = props => {
|
|
|
181
190
|
screenName,
|
|
182
191
|
bottomTab: !!bottomTab,
|
|
183
192
|
onElementLoad: (item: any) => {
|
|
184
|
-
|
|
193
|
+
/**
|
|
194
|
+
* widget handle
|
|
195
|
+
*/
|
|
185
196
|
if (item?.componentName === 'Widget') {
|
|
186
197
|
const index = widgets.current.findIndex(
|
|
187
198
|
(element: any) => element.componentId === item.componentId,
|
|
@@ -205,25 +216,35 @@ const StackScreen: React.FC<any> = props => {
|
|
|
205
216
|
return;
|
|
206
217
|
}
|
|
207
218
|
|
|
219
|
+
/**
|
|
220
|
+
* tracking for element screen load
|
|
221
|
+
*/
|
|
208
222
|
clearTimeout(tracking.current.timeoutLoad);
|
|
209
223
|
tracking.current.endTime = Date.now();
|
|
210
224
|
tracking.current.timeoutInteraction?.cancel?.();
|
|
211
225
|
tracking.current.timeoutInteraction = runAfterInteractions(() => {
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
tracking.current.timeInteraction =
|
|
227
|
+
Date.now() - tracking.current.startTime;
|
|
214
228
|
});
|
|
215
229
|
|
|
230
|
+
/**
|
|
231
|
+
* support for debug last element
|
|
232
|
+
*/
|
|
216
233
|
if (item?.componentName) {
|
|
217
234
|
tracking.current.lastElement = item;
|
|
218
235
|
}
|
|
219
236
|
|
|
220
|
-
|
|
237
|
+
/**
|
|
238
|
+
* for stop tracking when user interaction
|
|
239
|
+
*/
|
|
221
240
|
if (item?.interaction) {
|
|
222
241
|
onScreenLoad();
|
|
223
242
|
onScreenInteraction();
|
|
224
243
|
onFirstInteraction(item?.action);
|
|
225
244
|
}
|
|
226
|
-
|
|
245
|
+
/**
|
|
246
|
+
* timeout for handle tracking screen
|
|
247
|
+
*/
|
|
227
248
|
tracking.current.timeoutLoad = setTimeout(() => {
|
|
228
249
|
const time = tracking.current.endTime - tracking.current.startTime;
|
|
229
250
|
tracking.current.widgets = widgets.current;
|
|
@@ -241,10 +262,16 @@ const StackScreen: React.FC<any> = props => {
|
|
|
241
262
|
tracking.current.timeStartLoading = Date.now();
|
|
242
263
|
}
|
|
243
264
|
|
|
265
|
+
/**
|
|
266
|
+
* tracking for loading screen
|
|
267
|
+
*/
|
|
244
268
|
if (!loading) {
|
|
245
269
|
tracking.current.timeEndLoading = Date.now();
|
|
246
270
|
}
|
|
247
271
|
|
|
272
|
+
/**
|
|
273
|
+
* timeout for handle tracking screen
|
|
274
|
+
*/
|
|
248
275
|
clearTimeout(tracking.current.timeoutLoading);
|
|
249
276
|
tracking.current.timeoutLoading = setTimeout(() => {
|
|
250
277
|
onScreenLoading();
|
|
@@ -21,14 +21,10 @@ const TooltipPortalContext = createContext<TooltipPortalContextValue | null>(
|
|
|
21
21
|
null,
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Registry of tooltip contents that `TooltipPortalHost` renders in one layer
|
|
26
|
-
* above the screen, so tooltips aren't clipped by their anchor's parents.
|
|
27
|
-
*/
|
|
28
24
|
export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
|
|
29
25
|
children,
|
|
30
26
|
}) => {
|
|
31
|
-
//
|
|
27
|
+
// Use ref for synchronous updates + reducer for manual re-renders
|
|
32
28
|
const portalsRef = useRef<Map<string, ReactNode>>(new Map());
|
|
33
29
|
const hostRef = useRef<View>(null);
|
|
34
30
|
const [updateCounter, forceUpdate] = useReducer((x: number) => x + 1, 0);
|
|
@@ -36,15 +32,16 @@ export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
|
|
|
36
32
|
|
|
37
33
|
const register = useCallback((id: string, content: ReactNode) => {
|
|
38
34
|
portalsRef.current.set(id, content);
|
|
39
|
-
forceUpdate();
|
|
35
|
+
forceUpdate(); // Trigger re-render to show new content
|
|
40
36
|
}, []);
|
|
41
37
|
|
|
42
38
|
const unregister = useCallback((id: string, immediate = false) => {
|
|
43
39
|
const hasItem = portalsRef.current.has(id);
|
|
44
|
-
if (!hasItem) return;
|
|
40
|
+
if (!hasItem) return; // Already removed
|
|
45
41
|
|
|
46
42
|
portalsRef.current.delete(id);
|
|
47
43
|
|
|
44
|
+
// Clear any pending updates
|
|
48
45
|
if (pendingUpdateRef.current) {
|
|
49
46
|
clearTimeout(pendingUpdateRef.current);
|
|
50
47
|
pendingUpdateRef.current = null;
|
|
@@ -75,7 +72,6 @@ export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
|
|
|
75
72
|
);
|
|
76
73
|
};
|
|
77
74
|
|
|
78
|
-
/** Full-screen, touch-through layer that renders every registered tooltip. */
|
|
79
75
|
export const TooltipPortalHost: React.FC = () => {
|
|
80
76
|
const context = useContext(TooltipPortalContext);
|
|
81
77
|
|
|
@@ -99,10 +95,6 @@ export const TooltipPortalHost: React.FC = () => {
|
|
|
99
95
|
);
|
|
100
96
|
};
|
|
101
97
|
|
|
102
|
-
/**
|
|
103
|
-
* Tooltip portal API. Outside a provider it returns no-ops (and warns in dev)
|
|
104
|
-
* so a tooltip degrades instead of crashing.
|
|
105
|
-
*/
|
|
106
98
|
export const useTooltipPortal = () => {
|
|
107
99
|
const context = useContext(TooltipPortalContext);
|
|
108
100
|
|
|
@@ -112,6 +104,7 @@ export const useTooltipPortal = () => {
|
|
|
112
104
|
'useTooltipPortal must be used within TooltipPortalProvider. Tooltips may not appear correctly.',
|
|
113
105
|
);
|
|
114
106
|
}
|
|
107
|
+
// Return no-op functions if context is missing
|
|
115
108
|
return {
|
|
116
109
|
register: () => { },
|
|
117
110
|
unregister: () => { },
|
|
@@ -18,7 +18,6 @@ import { StyleSheet, View } from 'react-native';
|
|
|
18
18
|
|
|
19
19
|
const Stack = createStackNavigator();
|
|
20
20
|
|
|
21
|
-
/** Widget screen that reports its laid-out height to size the container. */
|
|
22
21
|
const WidgetContent: React.FC = ({ route }: any) => {
|
|
23
22
|
const Component = route?.params?.screen;
|
|
24
23
|
return (
|
|
@@ -32,10 +31,6 @@ const WidgetContent: React.FC = ({ route }: any) => {
|
|
|
32
31
|
);
|
|
33
32
|
};
|
|
34
33
|
|
|
35
|
-
/**
|
|
36
|
-
* Root of an embedded widget: its own navigator with the widget screen and the
|
|
37
|
-
* `Modal` route, sized to the widget's content height.
|
|
38
|
-
*/
|
|
39
34
|
const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
40
35
|
widget,
|
|
41
36
|
theme = defaultTheme,
|
|
@@ -66,9 +61,11 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
66
61
|
headerGradient = context?.designConfig?.headerGradient;
|
|
67
62
|
}
|
|
68
63
|
|
|
64
|
+
/**
|
|
65
|
+
* inject data for navigator
|
|
66
|
+
*/
|
|
69
67
|
navigator.current.maxApi = maxApi;
|
|
70
68
|
|
|
71
|
-
/** Text for a localization key, or the current-language side of `{ vi, en }`. */
|
|
72
69
|
const translate = (data?: string | { vi: string; en: string }) => {
|
|
73
70
|
if (data && typeof data !== 'string') {
|
|
74
71
|
return localize?.translateData(data);
|
|
@@ -84,10 +81,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
84
81
|
};
|
|
85
82
|
const isWhiteList = isWhiteListDesignSystem(mergedContext);
|
|
86
83
|
const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
|
|
87
|
-
|
|
88
|
-
if (isWhiteList) {
|
|
89
|
-
navigationFontFamily = 'MomoTrustSans-Regular';
|
|
90
|
-
}
|
|
84
|
+
const navigationFontFamily = isWhiteList ? 'MomoTrustSans-Regular' : 'SFProText';
|
|
91
85
|
|
|
92
86
|
return (
|
|
93
87
|
<View style={[styles.container, { height: height ?? '100%' }]}>
|