@momo-kits/foundation 0.169.7-beta.3 → 0.169.7-beta.4
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 +37 -40
- package/Application/BottomTab/ActiveIndicator.tsx +14 -15
- package/Application/BottomTab/BottomTabBar.tsx +62 -49
- package/Application/BottomTab/BottomTabInset.tsx +4 -8
- package/Application/BottomTab/CurvedContainer.tsx +12 -14
- package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
- package/Application/BottomTab/DuoBarCompression.tsx +19 -13
- package/Application/BottomTab/DuoRailTabBar.tsx +24 -20
- package/Application/BottomTab/DuoTabBar.tsx +10 -8
- package/Application/BottomTab/TabBarIcon.tsx +29 -15
- package/Application/BottomTab/TabScreen.tsx +5 -1
- package/Application/BottomTab/index.tsx +30 -33
- package/Application/BottomTab/metrics.ts +23 -23
- 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/KitAdoption/AdoptionBadge.tsx +119 -0
- package/Application/KitAdoption/AdoptionProbe.tsx +166 -0
- package/Application/KitAdoption/AdoptionStore.ts +80 -0
- package/Application/KitAdoption/Authorship/utils.ts +137 -0
- package/Application/KitAdoption/Slots/utils.ts +68 -0
- package/Application/KitAdoption/constants.ts +120 -0
- package/Application/KitAdoption/index.ts +12 -0
- package/Application/KitAdoption/styles.ts +55 -0
- package/Application/KitAdoption/types.ts +61 -0
- package/Application/KitAdoption/utils.ts +376 -0
- package/Application/Localize.ts +0 -7
- package/Application/ModalScreen.tsx +10 -10
- package/Application/Navigation.ts +16 -14
- package/Application/NavigationContainer.tsx +18 -19
- package/Application/Navigator.ts +48 -20
- package/Application/ScaleSizeProvider.tsx +6 -13
- package/Application/StackScreen.tracking.ts +33 -11
- package/Application/StackScreen.tsx +54 -10
- package/Application/TooltipPortal.tsx +5 -12
- package/Application/WidgetContainer.tsx +4 -10
- package/Application/__tests__/KitAdoption.integration.test.tsx +253 -0
- package/Application/__tests__/KitAdoption.test.ts +283 -0
- package/Application/__tests__/KitAdoptionAuthorship.test.ts +132 -0
- package/Application/__tests__/KitAdoptionStore.test.ts +110 -0
- package/Application/__tests__/KitAdoptionStructural.test.ts +193 -0
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/modalLayout.ts +14 -16
- package/Application/navigationOptions.tsx +17 -21
- 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 +1 -5
- 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 +49 -45
- package/Popup/PopupPromotion.tsx +7 -4
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -11
- 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 +4 -20
- package/Title/index.tsx +32 -41
- package/ViewBoundary/index.tsx +2 -4
- package/index.ts +6 -0
- package/package.json +1 -1
|
@@ -23,10 +23,6 @@ const SCREEN_PADDING = 12;
|
|
|
23
23
|
const BACK_WIDTH = 28;
|
|
24
24
|
const TOOLKIT_WIDTH = 73;
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Extended header background: a gradient that collapses on scroll and, with
|
|
28
|
-
* `inputSearchProps`, a full-width search that slides into the bar beside back/toolkit.
|
|
29
|
-
*/
|
|
30
26
|
const HeaderExtendHeader: React.FC<{
|
|
31
27
|
headerType?: HeaderType;
|
|
32
28
|
animatedValue: AnimatedCompatValue;
|
|
@@ -68,15 +64,18 @@ const HeaderExtendHeader: React.FC<{
|
|
|
68
64
|
const contentLeft = layout.insetLeft + SCREEN_PADDING;
|
|
69
65
|
const contentRight = layout.insetLeft + layout.contentWidth;
|
|
70
66
|
// collapsed on the status bar row, the search stays between its clusters
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const collapsedRight = contentRight - clearanceRight - headerRightWidth;
|
|
67
|
+
const collapsedLeft =
|
|
68
|
+
layout.insetLeft + leftPosition + (layout.statusBarClearance?.left ?? 0);
|
|
69
|
+
const collapsedRight = contentRight - (layout.statusBarClearance?.right ?? 0);
|
|
75
70
|
const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
|
|
76
|
-
const collapsedSearchWidth =
|
|
71
|
+
const collapsedSearchWidth =
|
|
72
|
+
collapsedRight - collapsedLeft - SCREEN_PADDING - headerRightWidth;
|
|
77
73
|
// a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
|
|
78
|
-
const
|
|
79
|
-
|
|
74
|
+
const collapsedRaise =
|
|
75
|
+
(layout.headerHeight -
|
|
76
|
+
layout.headerStatusBarHeight -
|
|
77
|
+
HeaderMetrics.barHeight) /
|
|
78
|
+
2;
|
|
80
79
|
|
|
81
80
|
let useShadowHeader = useShadowHeaderProp;
|
|
82
81
|
if (inputSearchProps && Platform.OS === 'android') {
|
|
@@ -10,8 +10,7 @@ import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
|
|
|
10
10
|
import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
|
|
13
|
+
* default header left used for nav
|
|
15
14
|
*/
|
|
16
15
|
const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
17
16
|
tintColor,
|
|
@@ -9,8 +9,7 @@ import { HeaderToolkitAction } from './HeaderToolkitAction';
|
|
|
9
9
|
import { styles } from './HeaderRight.styles';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* button, or the toolkit (stacked vertically in the floating iPhone Duo side bar).
|
|
12
|
+
* main component for header right
|
|
14
13
|
*/
|
|
15
14
|
const HeaderRight: React.FC<any> = props => {
|
|
16
15
|
const { translate } = useContext(ApplicationContext);
|
|
@@ -4,10 +4,6 @@ import { Spacing } from '../../Consts';
|
|
|
4
4
|
import { isWhiteListDesignSystem } from '../utils';
|
|
5
5
|
import { useScreenLayout } from '../../Layout';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
|
|
9
|
-
* SFProText otherwise.
|
|
10
|
-
*/
|
|
11
7
|
const useHeaderFontFamily = (): string => {
|
|
12
8
|
const context = useContext<any>(MiniAppContext);
|
|
13
9
|
return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
|
|
@@ -24,22 +20,24 @@ const HEADER_ACTIONS_WIDTH = 172;
|
|
|
24
20
|
const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
|
-
*
|
|
28
|
-
* the bar, so the title gets the content width; otherwise it shares the bar with them.
|
|
23
|
+
* max title width, the iPhone Duo side bar takes back and toolkit out of the bar
|
|
29
24
|
*/
|
|
30
25
|
const useTitleMaxWidth = () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
26
|
+
const { width, contentWidth, sideBar, statusBarClearance } =
|
|
27
|
+
useScreenLayout();
|
|
33
28
|
let clearance = 0;
|
|
34
|
-
let rowActionsExtra = 0;
|
|
35
29
|
if (statusBarClearance) {
|
|
36
30
|
clearance = statusBarClearance.left + statusBarClearance.right;
|
|
37
|
-
rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
|
|
38
31
|
}
|
|
39
32
|
if (sideBar) {
|
|
40
33
|
return contentWidth - Spacing.M * 2 - clearance;
|
|
41
34
|
}
|
|
42
|
-
return
|
|
35
|
+
return (
|
|
36
|
+
width -
|
|
37
|
+
HEADER_ACTIONS_WIDTH -
|
|
38
|
+
(statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
|
|
39
|
+
clearance
|
|
40
|
+
);
|
|
43
41
|
};
|
|
44
42
|
|
|
45
43
|
export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
|
|
@@ -33,8 +33,7 @@ type HeaderTitleExtraProps = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* scroll through `interpolate` (0 to 1 over the first 200pt by default).
|
|
36
|
+
* default header title used for nav
|
|
38
37
|
*/
|
|
39
38
|
const HeaderTitle: React.FC<
|
|
40
39
|
HeaderTitleExtraProps & { [key: string]: any }
|
|
@@ -101,8 +100,8 @@ const HeaderTitle: React.FC<
|
|
|
101
100
|
};
|
|
102
101
|
|
|
103
102
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* Title custom for header
|
|
104
|
+
* @constructor
|
|
106
105
|
*/
|
|
107
106
|
const TitleCustom: React.FC<any> = ({ type, data, ...props }) => {
|
|
108
107
|
switch (type) {
|
|
@@ -14,8 +14,7 @@ const CLOSE_ICON = '16_navigation_close_circle';
|
|
|
14
14
|
const HOME_ICON = '16_basic_home';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header toolkit
|
|
18
|
-
* `vertical` (iPhone Duo side bar) it reports its height and may fold into one button.
|
|
17
|
+
* Header toolkit action
|
|
19
18
|
*/
|
|
20
19
|
const HeaderToolkitAction: React.FC<any> = ({
|
|
21
20
|
tintColor,
|
|
@@ -28,9 +27,8 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
28
27
|
vertical = false,
|
|
29
28
|
}) => {
|
|
30
29
|
const context = useContext<any>(MiniAppContext);
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const { expandToolbar, setToolbarHeight } = compression;
|
|
30
|
+
const { toolbarCompressed, toolbarHidden, expandToolbar, setToolbarHeight } =
|
|
31
|
+
useDuoBarCompression();
|
|
34
32
|
const { statusBarClearance } = useScreenLayout();
|
|
35
33
|
const {
|
|
36
34
|
isLoading,
|
|
@@ -58,22 +56,18 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
58
56
|
|
|
59
57
|
const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
|
|
60
58
|
const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
|
|
61
|
-
let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
|
|
62
|
-
if (useHelpCenter) {
|
|
63
|
-
toolkitWidthStyle = styles.toolkitWide;
|
|
64
|
-
}
|
|
65
59
|
|
|
66
60
|
const groupCount = useHelpCenter ? 2 : 1;
|
|
67
61
|
let capsuleHeight = DuoMetrics.controlSize;
|
|
68
62
|
if (groupCount > 1) {
|
|
69
63
|
capsuleHeight = groupCount * DuoMetrics.itemPitch;
|
|
70
64
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
// Back slot, the optional shortcut, then the help/home capsule
|
|
66
|
+
const fullToolbarHeight =
|
|
67
|
+
DuoMetrics.controlSize +
|
|
68
|
+
Spacing.M +
|
|
69
|
+
(useShortcut ? DuoMetrics.controlSize + Spacing.M : 0) +
|
|
70
|
+
capsuleHeight;
|
|
77
71
|
|
|
78
72
|
useEffect(() => {
|
|
79
73
|
if (vertical) {
|
|
@@ -86,7 +80,8 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
86
80
|
return null;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
// HIG "Toolbar compressed":
|
|
83
|
+
// HIG "Toolbar compressed": tools fold into one button; tapping it expands the
|
|
84
|
+
// toolbar and compresses the tab bar instead. Not the useMore icon, to tell them apart
|
|
90
85
|
if (vertical && toolbarCompressed) {
|
|
91
86
|
return (
|
|
92
87
|
<SideBarButton
|
|
@@ -175,7 +170,15 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
175
170
|
showBadge={showBadge}
|
|
176
171
|
/>
|
|
177
172
|
)}
|
|
178
|
-
<View
|
|
173
|
+
<View
|
|
174
|
+
style={[
|
|
175
|
+
styles.toolkitContainer,
|
|
176
|
+
buttonStyle,
|
|
177
|
+
context.appId !== 'vn.momo.helpcenter'
|
|
178
|
+
? styles.toolkitWide
|
|
179
|
+
: styles.toolkitNarrow,
|
|
180
|
+
]}
|
|
181
|
+
>
|
|
179
182
|
{useHelpCenter && (
|
|
180
183
|
<>
|
|
181
184
|
<TouchableOpacity
|
|
@@ -7,8 +7,7 @@ import { Icon } from '../../Icon';
|
|
|
7
7
|
import { BadgeDot } from '../../Badge';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* dot; switches to a dark fill under a white tint so the icon stays readable.
|
|
10
|
+
* default navigation button used header nav
|
|
12
11
|
*/
|
|
13
12
|
const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
14
13
|
icon,
|
|
@@ -10,10 +10,6 @@ import { useNormalizedSharedValue } from '../hooks';
|
|
|
10
10
|
import { useScreenLayout } from '../../Layout';
|
|
11
11
|
import { DuoMetrics } from '../../Layout/utils';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Header search bar whose fill turns from surface to default once scrolled, with
|
|
15
|
-
* a cancel button that goes back (or dismisses the mini app) unless `renderButtons`.
|
|
16
|
-
*/
|
|
17
13
|
const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
18
14
|
(
|
|
19
15
|
{
|
|
@@ -40,9 +36,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
40
36
|
if (layout.sideBar) {
|
|
41
37
|
searchMaxWidth = layout.contentWidth - Spacing.M * 2 - headerRightWidth;
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
if (layout.statusBarClearance) {
|
|
40
|
+
searchMaxWidth -=
|
|
41
|
+
layout.statusBarClearance.left + layout.statusBarClearance.right;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
const backgroundStyle = useAnimatedStyle(() => {
|
|
@@ -80,15 +76,6 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
80
76
|
|
|
81
77
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
82
78
|
|
|
83
|
-
let buttons: React.ReactNode = (
|
|
84
|
-
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
85
|
-
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
86
|
-
</TouchableOpacity>
|
|
87
|
-
);
|
|
88
|
-
if (typeof renderButtons === 'function') {
|
|
89
|
-
buttons = renderButtons();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
79
|
return (
|
|
93
80
|
<View
|
|
94
81
|
style={[styles.container, showBaseLineDebug && styles.debugBaseLine]}
|
|
@@ -110,7 +97,13 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
110
97
|
/>
|
|
111
98
|
</Animated.View>
|
|
112
99
|
</View>
|
|
113
|
-
{
|
|
100
|
+
{renderButtons && typeof renderButtons === 'function' ? (
|
|
101
|
+
renderButtons()
|
|
102
|
+
) : (
|
|
103
|
+
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
104
|
+
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
)}
|
|
114
107
|
</View>
|
|
115
108
|
);
|
|
116
109
|
},
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
} from './HeaderTitle.shared';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header title
|
|
18
|
-
*
|
|
17
|
+
* Header user title
|
|
18
|
+
* @constructor
|
|
19
19
|
*/
|
|
20
20
|
const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
21
21
|
start,
|
|
@@ -9,8 +9,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
9
9
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Header title
|
|
13
|
-
*
|
|
12
|
+
* Header user title
|
|
13
|
+
* @constructor
|
|
14
14
|
*/
|
|
15
15
|
const TitleLocation: React.FC<TitleLocationProps> = ({
|
|
16
16
|
tintColor,
|
|
@@ -10,8 +10,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
10
10
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Header
|
|
14
|
-
*
|
|
13
|
+
* Header user title
|
|
14
|
+
* @constructor
|
|
15
15
|
*/
|
|
16
16
|
const TitleUser: React.FC<TitleUserProps> = ({
|
|
17
17
|
title,
|
|
@@ -15,8 +15,7 @@ type ToolkitActionsParams = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* with the home), single tool or "more" shortcut, help center and close.
|
|
18
|
+
* state and handlers behind the header toolkit buttons
|
|
20
19
|
*/
|
|
21
20
|
const useToolkitActions = ({
|
|
22
21
|
preventClose,
|
|
@@ -32,15 +31,20 @@ const useToolkitActions = ({
|
|
|
32
31
|
const [isFavorite, setIsFavorite] = useState<boolean>(false);
|
|
33
32
|
const [isLoading, setIsLoading] = useState(false);
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
/**
|
|
35
|
+
* check app is favorite
|
|
36
|
+
*/
|
|
37
|
+
const checkAppIsFavorite = useCallback(
|
|
38
|
+
() =>
|
|
39
|
+
navigator?.maxApi?.dispatchFunction?.(
|
|
40
|
+
'isFavoriteApp',
|
|
41
|
+
{ code: context?.code },
|
|
42
|
+
(result: boolean) => {
|
|
43
|
+
setIsFavorite(result);
|
|
44
|
+
},
|
|
45
|
+
),
|
|
46
|
+
[context?.code, navigator?.maxApi],
|
|
47
|
+
);
|
|
44
48
|
|
|
45
49
|
useEffect(() => {
|
|
46
50
|
if (useShortcut) {
|
|
@@ -71,6 +75,9 @@ const useToolkitActions = ({
|
|
|
71
75
|
}
|
|
72
76
|
};
|
|
73
77
|
|
|
78
|
+
/**
|
|
79
|
+
* close navigation container
|
|
80
|
+
*/
|
|
74
81
|
const onClose = () => {
|
|
75
82
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
76
83
|
context?.autoTracking?.({
|
|
@@ -100,6 +107,9 @@ const useToolkitActions = ({
|
|
|
100
107
|
}
|
|
101
108
|
};
|
|
102
109
|
|
|
110
|
+
/**
|
|
111
|
+
* on press shortcut
|
|
112
|
+
*/
|
|
103
113
|
const onPressShortcut = () => {
|
|
104
114
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
105
115
|
const expected = !isFavorite;
|
|
@@ -129,6 +139,9 @@ const useToolkitActions = ({
|
|
|
129
139
|
}, 1000);
|
|
130
140
|
};
|
|
131
141
|
|
|
142
|
+
/**
|
|
143
|
+
* on press help center
|
|
144
|
+
*/
|
|
132
145
|
const onPressHelpCenter = () => {
|
|
133
146
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
134
147
|
context?.autoTracking?.({
|
|
@@ -141,6 +154,9 @@ const useToolkitActions = ({
|
|
|
141
154
|
navigator?.maxApi?.dispatchFunction?.('showHelpCenter', context);
|
|
142
155
|
};
|
|
143
156
|
|
|
157
|
+
/**
|
|
158
|
+
* on press icon more
|
|
159
|
+
*/
|
|
144
160
|
const onPressIconMore = () => {
|
|
145
161
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
146
162
|
context?.autoTracking?.({
|
|
@@ -184,10 +200,9 @@ const useToolkitActions = ({
|
|
|
184
200
|
iconShortcut = 'navigation_more_icon';
|
|
185
201
|
shortcutOnPress = onPressIconMore;
|
|
186
202
|
} else if (isHaveOneTool) {
|
|
187
|
-
const
|
|
203
|
+
const singleTool = tools.find(
|
|
188
204
|
(group: ToolGroup) => group?.items?.length === 1,
|
|
189
|
-
);
|
|
190
|
-
const singleTool = singleToolGroup?.items[0];
|
|
205
|
+
)?.items[0];
|
|
191
206
|
iconShortcut = singleTool?.icon;
|
|
192
207
|
shortcutOnPress = () => {
|
|
193
208
|
singleTool?.onPress?.();
|
|
@@ -201,9 +216,9 @@ const useToolkitActions = ({
|
|
|
201
216
|
};
|
|
202
217
|
}
|
|
203
218
|
|
|
204
|
-
const showBadge = tools.some((group: ToolGroup) =>
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
const showBadge = tools.some((group: ToolGroup) =>
|
|
220
|
+
group?.items?.some?.(tool => tool?.showBadge),
|
|
221
|
+
);
|
|
207
222
|
|
|
208
223
|
return {
|
|
209
224
|
isLoading,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React, { useState, useSyncExternalStore } from 'react';
|
|
2
|
+
import { Share, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
+
|
|
5
|
+
import { Colors, Spacing } from '../../Consts';
|
|
6
|
+
import { Text } from '../../Text';
|
|
7
|
+
import { adoptionStore } from './AdoptionStore';
|
|
8
|
+
import { styles } from './styles';
|
|
9
|
+
import type { ScreenSnapshot } from './types';
|
|
10
|
+
|
|
11
|
+
/** Subscribes a React listener to the adoption store. Stable identity for useSyncExternalStore. */
|
|
12
|
+
const subscribeToStore = (listener: () => void): (() => void) =>
|
|
13
|
+
adoptionStore.subscribe(listener);
|
|
14
|
+
|
|
15
|
+
const getLatestSnapshot = (): ScreenSnapshot | undefined =>
|
|
16
|
+
adoptionStore.getLatest();
|
|
17
|
+
|
|
18
|
+
const getScreenCount = (): number => adoptionStore.getAll().length;
|
|
19
|
+
|
|
20
|
+
/** Share of classified nodes that are app-authored kit: K / (K + R + O). */
|
|
21
|
+
const getAdoptionRate = (snapshot: ScreenSnapshot): number => {
|
|
22
|
+
const { K, R, O } = snapshot.counts;
|
|
23
|
+
const total = K + R + O;
|
|
24
|
+
return total === 0 ? 0 : K / total;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const formatSummary = (snapshot: ScreenSnapshot | undefined): string => {
|
|
28
|
+
if (!snapshot) return 'Kit adoption: chưa có dữ liệu';
|
|
29
|
+
const { K, R, O } = snapshot.counts;
|
|
30
|
+
const percent = Math.round(getAdoptionRate(snapshot) * 100);
|
|
31
|
+
return `Kit ${percent}% · K${K} R${R} O${O}`;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleRemeasure = (): void => {
|
|
35
|
+
adoptionStore.remeasureActive();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleShare = (): void => {
|
|
39
|
+
Share.share({ message: JSON.stringify(adoptionStore.getAll()) }).catch(
|
|
40
|
+
() => undefined,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleClear = (): void => {
|
|
45
|
+
adoptionStore.clear();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Dev-only floating badge showing the latest screen's kit adoption. Tap to
|
|
50
|
+
* expand actions (re-measure, share JSON, clear). Renders nothing in release builds.
|
|
51
|
+
*/
|
|
52
|
+
const AdoptionBadge: React.FC = () => {
|
|
53
|
+
const insets = useSafeAreaInsets();
|
|
54
|
+
const [isExpanded, setExpanded] = useState(false);
|
|
55
|
+
const latest = useSyncExternalStore(subscribeToStore, getLatestSnapshot);
|
|
56
|
+
const screenCount = useSyncExternalStore(subscribeToStore, getScreenCount);
|
|
57
|
+
|
|
58
|
+
if (!__DEV__) return null;
|
|
59
|
+
|
|
60
|
+
const handleToggle = (): void => setExpanded(prev => !prev);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<View
|
|
64
|
+
pointerEvents="box-none"
|
|
65
|
+
style={[styles.wrapper, { top: insets.top + Spacing.S }]}
|
|
66
|
+
>
|
|
67
|
+
<TouchableOpacity
|
|
68
|
+
accessibilityRole="button"
|
|
69
|
+
onPress={handleToggle}
|
|
70
|
+
style={styles.badge}
|
|
71
|
+
>
|
|
72
|
+
<Text typography="label_xs_medium" color={Colors.black_01}>
|
|
73
|
+
{formatSummary(latest)}
|
|
74
|
+
</Text>
|
|
75
|
+
{latest ? (
|
|
76
|
+
<Text typography="description_xs_regular" color={Colors.black_01}>
|
|
77
|
+
{latest.screen_name}
|
|
78
|
+
</Text>
|
|
79
|
+
) : null}
|
|
80
|
+
</TouchableOpacity>
|
|
81
|
+
{isExpanded ? (
|
|
82
|
+
<View style={styles.panel}>
|
|
83
|
+
<Text typography="label_xs_medium" color={Colors.black_17}>
|
|
84
|
+
{`Đã đo ${screenCount} màn hình`}
|
|
85
|
+
</Text>
|
|
86
|
+
<TouchableOpacity
|
|
87
|
+
accessibilityRole="button"
|
|
88
|
+
onPress={handleRemeasure}
|
|
89
|
+
style={styles.action}
|
|
90
|
+
>
|
|
91
|
+
<Text typography="label_xs_medium" color={Colors.blue_03}>
|
|
92
|
+
Đo lại
|
|
93
|
+
</Text>
|
|
94
|
+
</TouchableOpacity>
|
|
95
|
+
<TouchableOpacity
|
|
96
|
+
accessibilityRole="button"
|
|
97
|
+
onPress={handleShare}
|
|
98
|
+
style={styles.action}
|
|
99
|
+
>
|
|
100
|
+
<Text typography="label_xs_medium" color={Colors.blue_03}>
|
|
101
|
+
Share JSON
|
|
102
|
+
</Text>
|
|
103
|
+
</TouchableOpacity>
|
|
104
|
+
<TouchableOpacity
|
|
105
|
+
accessibilityRole="button"
|
|
106
|
+
onPress={handleClear}
|
|
107
|
+
style={styles.actionOutline}
|
|
108
|
+
>
|
|
109
|
+
<Text typography="label_xs_medium" color={Colors.black_17}>
|
|
110
|
+
Xoá
|
|
111
|
+
</Text>
|
|
112
|
+
</TouchableOpacity>
|
|
113
|
+
</View>
|
|
114
|
+
) : null}
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { AdoptionBadge };
|