From 521a4d0a51aed1f7ffcf3a9f7e58b350e4fdb6d8 Mon Sep 17 00:00:00 2001 From: jinyaqiu Date: Thu, 17 Jul 2025 15:27:00 +0800 Subject: [PATCH] feat: userInfo --- app/(tabs)/owner.tsx | 92 ++++++++++++---------- components/copy.tsx | 38 +++++++++ components/login/signUp.tsx | 43 +++++++--- components/owner/qualification/privacy.tsx | 15 ++-- components/owner/userName.tsx | 59 ++++++++------ i18n/locales/en/login.json | 3 +- i18n/locales/zh/login.json | 3 +- package-lock.json | 47 +++-------- package.json | 4 +- 9 files changed, 176 insertions(+), 128 deletions(-) create mode 100644 components/copy.tsx diff --git a/app/(tabs)/owner.tsx b/app/(tabs)/owner.tsx index 1c6ec85..40f5d46 100644 --- a/app/(tabs)/owner.tsx +++ b/app/(tabs)/owner.tsx @@ -16,7 +16,7 @@ import { fetchApi } from '@/lib/server-api-util'; import { CountData, UserInfoDetails } from '@/types/user'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ScrollView, StyleSheet, View } from 'react-native'; +import { FlatList, ScrollView, StyleSheet, View } from 'react-native'; import { useSafeAreaInsets } from "react-native-safe-area-context"; export default function OwnerPage() { const insets = useSafeAreaInsets(); @@ -59,54 +59,57 @@ export default function OwnerPage() { return ( - - {/* 用户信息 */} - + ListHeaderComponent={ + + {/* 用户信息 */} + - {/* 设置栏 */} - + {/* 设置栏 */} + - {/* 资源数据 */} - - } style={{ flex: 1 }} isFormatBytes={true} /> - } style={{ flex: 1 }} /> - - {/* 数据统计 */} - + {/* 资源数据 */} + + } style={{ flex: 1 }} isFormatBytes={true} /> + } style={{ flex: 1 }} /> + + {/* 数据统计 */} + - {/* 分类 */} - - - {countData?.counter?.category_count && Object.entries(countData?.counter?.category_count).map(([key, value], index) => { - return ( - - ) - })} - - + {/* 分类 */} + + + {countData?.counter?.category_count && Object.entries(countData?.counter?.category_count).map(([key, value], index) => { + return ( + + ) + })} + + - {/* 作品数据 */} - - } number={userInfoDetails.stories_count} /> - } number={userInfoDetails.conversations_count} /> - - - {/* 排行榜 */} - - - + {/* 作品数据 */} + + } number={userInfoDetails.stories_count} /> + } number={userInfoDetails.conversations_count} /> + + {/* 排行榜 */} + + + } + /> {/* 设置弹窗 */} @@ -122,6 +125,9 @@ const styles = StyleSheet.create({ backgroundColor: 'white', paddingBottom: 86, }, + contentContainer: { + paddingHorizontal: 16, + }, resourceContainer: { flexDirection: 'row', gap: 16 diff --git a/components/copy.tsx b/components/copy.tsx new file mode 100644 index 0000000..476058f --- /dev/null +++ b/components/copy.tsx @@ -0,0 +1,38 @@ +import Ionicons from '@expo/vector-icons/Ionicons'; +import * as Clipboard from 'expo-clipboard'; +import React, { useState } from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; + +const CopyButton = ({ textToCopy }: { textToCopy: string }) => { + const [isCopied, setIsCopied] = useState(false); + + const handleCopy = async () => { + await Clipboard.setStringAsync(textToCopy); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 2000); + }; + + return ( + + {isCopied ? ( + + ) : ( + + )} + + ); +}; + +const styles = StyleSheet.create({ + button: { + flexDirection: 'row', + alignItems: 'center', + padding: 4, + }, + text: { + marginLeft: 8, + fontSize: 16, + }, +}); + +export default CopyButton; \ No newline at end of file diff --git a/components/login/signUp.tsx b/components/login/signUp.tsx index 0c3782b..6f5c441 100644 --- a/components/login/signUp.tsx +++ b/components/login/signUp.tsx @@ -7,6 +7,7 @@ import { useAuth } from "../../contexts/auth-context"; import { fetchApi } from "../../lib/server-api-util"; import { User } from "../../types/user"; import { ThemedText } from "../ThemedText"; +import PrivacyModal from "../owner/qualification/privacy"; interface LoginProps { updateUrlParam: (status: string, value: string) => void; @@ -25,7 +26,9 @@ const SignUp = ({ updateUrlParam, setError, setShowPassword, showPassword }: Log const [passwordsMatch, setPasswordsMatch] = useState(true); const [loading, setLoading] = useState(false); const [checked, setChecked] = useState(false); - + const [modalType, setModalType] = useState<'ai' | 'terms' | 'privacy' | 'user'>('ai'); + // 协议弹窗 + const [privacyModalVisible, setPrivacyModalVisible] = useState(false); // 从 URL 参数中获取 task_id 和 steps const params = useLocalSearchParams<{ task_id?: string; steps?: string }>(); const taskId = params.task_id; @@ -263,10 +266,10 @@ const SignUp = ({ updateUrlParam, setError, setShowPassword, showPassword }: Log {t("auth.telLogin.agree", { ns: 'login' })} - router.push({ - pathname: '/agreement', - params: { type: 'service' } - } as any)}> + { + setModalType('terms'); + setPrivacyModalVisible(true); + }}> {t("auth.telLogin.terms", { ns: 'login' })} @@ -274,10 +277,10 @@ const SignUp = ({ updateUrlParam, setError, setShowPassword, showPassword }: Log {t("auth.telLogin.and", { ns: 'login' })} - router.push({ - pathname: '/agreement', - params: { type: 'privacy' } - } as any)}> + { + setModalType('privacy'); + setPrivacyModalVisible(true); + }}> {t("auth.telLogin.privacyPolicy", { ns: 'login' })} @@ -285,14 +288,25 @@ const SignUp = ({ updateUrlParam, setError, setShowPassword, showPassword }: Log {t("auth.telLogin.and", { ns: 'login' })} - router.push({ - pathname: '/agreement', - params: { type: 'user' } - } as any)}> + { + setModalType('user'); + setPrivacyModalVisible(true); + }}> {t("auth.telLogin.userAgreement", { ns: 'login' })} + + {t("auth.telLogin.and", { ns: 'login' })} + + { + setModalType('ai'); + setPrivacyModalVisible(true); + }}> + + {t("auth.telLogin.aiAgreement", { ns: 'login' })} + + {t("auth.telLogin.agreement", { ns: 'login' })} @@ -319,6 +333,9 @@ const SignUp = ({ updateUrlParam, setError, setShowPassword, showPassword }: Log + + {/* 协议弹窗 */} + } diff --git a/components/owner/qualification/privacy.tsx b/components/owner/qualification/privacy.tsx index 75a6078..7af7259 100644 --- a/components/owner/qualification/privacy.tsx +++ b/components/owner/qualification/privacy.tsx @@ -1,7 +1,7 @@ import { fetchApi } from '@/lib/server-api-util'; import { Policy } from '@/types/personal-info'; import React, { useEffect, useState } from 'react'; -import { Modal, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import RenderHtml from 'react-native-render-html'; const PrivacyModal = (props: { modalVisible: boolean, setModalVisible: (visible: boolean) => void, type: string }) => { @@ -62,12 +62,8 @@ const PrivacyModal = (props: { modalVisible: boolean, setModalVisible: (visible: onRequestClose={() => { setModalVisible(!modalVisible); }}> - setModalVisible(false)}> - e.stopPropagation()}> + + Settings {type === 'ai' ? 'AI Policy' : type === 'terms' ? 'Terms of Service' : type === 'privacy' ? 'Privacy Policy' : 'User Agreement'} @@ -85,9 +81,8 @@ const PrivacyModal = (props: { modalVisible: boolean, setModalVisible: (visible: }} /> - - - + + ); }; diff --git a/components/owner/userName.tsx b/components/owner/userName.tsx index ab64c68..691c276 100644 --- a/components/owner/userName.tsx +++ b/components/owner/userName.tsx @@ -1,25 +1,26 @@ import UserSvg from '@/assets/icons/svg/ataver.svg'; import { ThemedText } from '@/components/ThemedText'; import { UserInfoDetails } from '@/types/user'; -// import { Image } from 'expo-image'; +import { useState } from 'react'; import { Image, ScrollView, View } from 'react-native'; export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) { - + // 添加状态来跟踪图片加载状态 + const [imageError, setImageError] = useState(false); return ( - + {/* 用户名 */} - + - + {userInfo?.user_info?.nickname} - - User ID:{userInfo?.user_info?.user_id} - + + + User ID: {userInfo?.user_info?.user_id} + + {/* */} + + {/* 头像 */} - - {userInfo?.user_info?.avatar_file_url - ? + + {userInfo?.user_info?.avatar_file_url && !imageError ? ( { + console.log('图片加载失败:', userInfo.user_info.avatar_file_url); + setImageError(true); + }} + onLoad={() => { + console.log('图片加载成功'); + }} /> - : + ) : ( - } + )} ); diff --git a/i18n/locales/en/login.json b/i18n/locales/en/login.json index ea568c7..bd71809 100644 --- a/i18n/locales/en/login.json +++ b/i18n/locales/en/login.json @@ -48,7 +48,8 @@ "codeVaild": "The code you entered is invalid", "sendAgain": "Did’nt receive a code?", "resend": "Resend", - "goBack": "Go Back" + "goBack": "Go Back", + "aiAgreement": "AI Function Usage Norms" }, "login": { "title": "Log in", diff --git a/i18n/locales/zh/login.json b/i18n/locales/zh/login.json index fa391b5..3daf77c 100644 --- a/i18n/locales/zh/login.json +++ b/i18n/locales/zh/login.json @@ -48,7 +48,8 @@ "codeValid": "您输入的验证码无效", "sendAgain": "没有收到验证码?", "resend": "重新发送", - "goBack": "返回" + "goBack": "返回", + "aiAgreement": "《AI功能使用规范》" }, "login": { "title": "登录", diff --git a/package-lock.json b/package-lock.json index 068ae76..6070e55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "expo-audio": "~0.4.7", "expo-background-fetch": "^13.1.6", "expo-blur": "~14.1.5", + "expo-clipboard": "~7.1.5", "expo-constants": "~17.1.6", "expo-dev-client": "~5.2.1", "expo-device": "~7.1.4", @@ -3360,18 +3361,6 @@ } } }, - "node_modules/@react-native-async-storage/async-storage": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", - "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", - "license": "MIT", - "dependencies": { - "merge-options": "^3.0.4" - }, - "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" - } - }, "node_modules/@react-native-picker/picker": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz", @@ -7835,6 +7824,17 @@ "react-native": "*" } }, + "node_modules/expo-clipboard": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-7.1.5.tgz", + "integrity": "sha512-TCANUGOxouoJXxKBW5ASJl2WlmQLGpuZGemDCL2fO5ZMl57DGTypUmagb0CVUFxDl0yAtFIcESd78UsF9o64aw==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-constants": { "version": "17.1.7", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.7.tgz", @@ -9743,15 +9743,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -10819,18 +10810,6 @@ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "license": "MIT" }, - "node_modules/merge-options": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", - "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -15896,4 +15875,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index bc50a88..268a95e 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "expo-file-system": "~18.1.10", "expo-font": "~13.3.1", "expo-haptics": "~14.1.4", - "expo-image": "~2.3.2", "expo-image-manipulator": "~13.1.7", "expo-image-picker": "~16.1.4", "expo-linking": "~7.1.5", @@ -69,7 +68,8 @@ "react-native-uuid": "^2.0.3", "react-native-web": "~0.20.0", "react-native-webview": "13.13.5", - "react-redux": "^9.2.0" + "react-redux": "^9.2.0", + "expo-clipboard": "~7.1.5" }, "devDependencies": { "@babel/core": "^7.25.2",