Skip to content

Commit b1222cb

Browse files
committed
Merge branch 'main' of github.com:Muxi-X/ccnubox_rn
2 parents df88b13 + 1a2a9d0 commit b1222cb

5 files changed

Lines changed: 121 additions & 5 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ export interface ScrollableViewProps {
351351
- 底部:有渐变、动画为滑入
352352
- 中部:无渐变、动画为放大
353353

354+
**API 方法:**
355+
356+
- `Modal.show(props)` - 显示Modal
357+
- `Modal.clear()` - 清除所有Modal
358+
354359
```tsx
355360
<ModalTrigger
356361
title={title}

src/app/auth/guide.tsx

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Icon, Toast } from '@ant-design/react-native';
22
import AsyncStorage from '@react-native-async-storage/async-storage';
33
import { LinearGradient } from 'expo-linear-gradient';
4-
import { router } from 'expo-router';
4+
import { router, useFocusEffect } from 'expo-router';
55
import * as React from 'react';
6-
import { FC, useEffect, useState } from 'react';
6+
import { FC, useCallback, useEffect, useState } from 'react';
77
import {
8+
BackHandler,
89
Dimensions,
910
Image,
11+
Platform,
1012
StyleSheet,
1113
Text,
1214
TouchableOpacity,
@@ -23,12 +25,13 @@ import Animated, {
2325
import AnimatedFade from '@/components/animatedView/AnimatedFade';
2426
import AnimatedOpacity from '@/components/animatedView/AnimatedOpacity';
2527
import Button from '@/components/button';
28+
import Modal from '@/components/modal';
2629
import Pagination from '@/components/pagination';
2730

2831
import useVisualScheme from '@/store/visualScheme';
2932

3033
import { preloginGuide } from '@/constants/prelogin';
31-
import { commonStyles } from '@/styles/common';
34+
import { commonColors, commonStyles } from '@/styles/common';
3235
import { percent2px } from '@/utils';
3336

3437
const PAGE_SWIPE_ANIMATION_DURATION = 450;
@@ -49,6 +52,41 @@ const GuidePage: FC = () => {
4952
};
5053
// 标题移动距离
5154
const titleShift = useSharedValue(0);
55+
56+
useFocusEffect(
57+
useCallback(() => {
58+
AsyncStorage.getItem('agreement').then(agreement => {
59+
if (agreement !== 'accepted') {
60+
Modal.show({
61+
children: <AgreementModal />,
62+
mode: 'middle',
63+
cancelText: '不同意',
64+
confirmText: '同意并接受',
65+
onConfirm: () => {
66+
AsyncStorage.setItem('agreement', 'accepted');
67+
},
68+
onCancel() {
69+
if (Platform.OS === 'android') {
70+
router.replace('/auth/guide');
71+
BackHandler.exitApp();
72+
} else if (Platform.OS === 'ios') {
73+
router.replace('/auth/guide');
74+
}
75+
},
76+
onClose() {
77+
if (Platform.OS === 'android') {
78+
router.replace('/auth/guide');
79+
} else if (Platform.OS === 'ios') {
80+
router.replace('/auth/guide');
81+
}
82+
},
83+
});
84+
}
85+
});
86+
return () => {};
87+
}, [])
88+
);
89+
5290
useEffect(() => {
5391
'worklet';
5492
// 渐变条每次移动多少
@@ -67,16 +105,20 @@ const GuidePage: FC = () => {
67105
}
68106
);
69107
}, [gradientValue, activeIndex, titleShift]);
108+
70109
const gradientStyle = useAnimatedStyle(() => ({
71110
transform: [{ translateX: gradientValue.value }],
72111
}));
112+
73113
const titleStyle = useAnimatedStyle(() => ({
74114
transform: [{ translateX: titleShift.value }],
75115
}));
116+
76117
const handleStart = () => {
77118
router.navigate('/auth/login');
78119
AsyncStorage.setItem('firstLaunch', 'true');
79120
};
121+
80122
// 跳转第几条内容
81123
const jump = (pageNum: number) => {
82124
if (pageNum > preloginGuide.length - 1 || pageNum < 0) {
@@ -94,6 +136,7 @@ const GuidePage: FC = () => {
94136
setActiveContentIndex(pageNum);
95137
}, PAGE_SWIPE_ANIMATION_DURATION + 200);
96138
};
139+
97140
const onSwipe = Gesture.Pan()
98141
.minDistance(30)
99142
.onEnd(event => {
@@ -105,6 +148,7 @@ const GuidePage: FC = () => {
105148
const handleChange = (current: number) => {
106149
jump(current);
107150
};
151+
108152
return (
109153
<View style={styles.guide_wrap}>
110154
<GestureDetector gesture={onSwipe}>
@@ -184,6 +228,36 @@ const GuidePage: FC = () => {
184228

185229
export default GuidePage;
186230

231+
export const AgreementModal: FC = () => {
232+
return (
233+
<Text
234+
style={[commonStyles.fontMedium, { textAlign: 'center', lineHeight: 22 }]}
235+
>
236+
请你务必审慎阅读、充分理解“用户协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。你可阅读
237+
<Text
238+
style={{ color: commonColors.purple, textDecorationLine: 'underline' }}
239+
onPress={() => {
240+
router.push('/(setting)/agreement');
241+
Modal.clear();
242+
}}
243+
>
244+
《用户协议》
245+
</Text>
246+
247+
<Text
248+
style={{ color: commonColors.purple, textDecorationLine: 'underline' }}
249+
onPress={() => {
250+
router.push('/(setting)/privacy');
251+
Modal.clear();
252+
}}
253+
>
254+
《隐私政策》
255+
</Text>
256+
了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。
257+
</Text>
258+
);
259+
};
260+
187261
const styles = StyleSheet.create({
188262
skip_container: {
189263
flexDirection: 'row',

src/components/modal/index.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import useVisualScheme from '@/store/visualScheme';
2020
import { commonColors, commonStyles } from '@/styles/common';
2121
import { percent2px } from '@/utils';
2222

23-
const Modal: React.FC<ModalProps> & { show: (props: ModalProps) => number } = ({
23+
const Modal: React.FC<ModalProps> & {
24+
show: (props: ModalProps) => number;
25+
clear: () => void;
26+
} = ({
2427
visible: initVisible = true,
2528
currentKey,
2629
onClose,
@@ -252,6 +255,32 @@ Modal.show = props => {
252255
const { appendChildren } = usePortalStore.getState();
253256
return appendChildren(<Modal {...props}></Modal>, 'modal');
254257
};
258+
259+
/**
260+
* 清除所有Modal
261+
* @example 示例
262+
* Modal.clear()
263+
*/
264+
Modal.clear = () => {
265+
const { elements, clearAll } = usePortalStore.getState();
266+
267+
// 先设置所有Modal为不可见,触发关闭动画
268+
Object.entries(elements).forEach(([key, element]) => {
269+
if (
270+
element &&
271+
element.props &&
272+
(element.props as any).portalType === 'modal'
273+
) {
274+
// 通过updateChildren立即设置visible为false
275+
usePortalStore.getState().updateChildren(Number(key), { visible: false });
276+
}
277+
});
278+
279+
// 延迟清空,等待动画完成
280+
setTimeout(() => {
281+
clearAll();
282+
}, 300); // 比Modal的200ms延迟稍长一些
283+
};
255284
/**
256285
* 带有触发按钮的 trigger
257286
* @param props

src/store/portal.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const usePortalStore = create<PortalStore>((set, get) => ({
3838
key,
3939
portalType,
4040
currentKey: key,
41-
});
41+
} as any);
4242
}
4343
set({
4444
elements: tmpMap,
@@ -52,4 +52,10 @@ export const usePortalStore = create<PortalStore>((set, get) => ({
5252

5353
updateFromElements();
5454
},
55+
clearAll: () => {
56+
set({
57+
elements: {},
58+
});
59+
get().updateFromElements();
60+
},
5561
}));

src/store/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export interface PortalStore {
6565
appendChildren: (_newChildren: ReactElement, _portalType?: string) => number;
6666
/* 通过 key 删除节点,每个 portal 下的组件都会接收到一个 currentKey 参数,代表当前 key 值 */
6767
deleteChildren: (_key: number) => void;
68+
/* 清除所有 portal 下的节点 */
69+
clearAll: () => void;
6870
/* 通过 elements 遍历得出 portal 下真正的节点结构 */
6971
updateFromElements: () => void;
7072
}

0 commit comments

Comments
 (0)