expo-duooomi-app/app/settings.tsx

158 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Ionicons } from '@expo/vector-icons'
import { Block, ConfirmModal, Img, Text, Toast } from '@share/components'
import { router, Stack } from 'expo-router'
import { observer } from 'mobx-react-lite'
import React from 'react'
import { ScrollView } from 'react-native'
import { userStore } from '@/stores'
type MenuItem = {
id: string
label: string
icon: keyof typeof Ionicons.glyphMap
onPress: () => void
}
export default observer(function SettingsPage() {
const { user, signOut } = userStore
const handleLogout = () => {
Toast.showModal(
<ConfirmModal
title="退出登录"
content="确定要退出登录吗?"
onCancel={Toast.hideModal}
onConfirm={async () => {
// 1. 立即关闭 Modal避免在动画期间操作路由
Toast.hideModal()
// 2. 等待 Modal 关闭动画完成(关键:避免 View 空指针)
setTimeout(() => {
// 3. 清空导航栈
router.dismissAll()
// 4. 执行登出
signOut()
// 5. 跳转到登录页
router.replace('/auth')
// 6. 显示提示
setTimeout(() => {
Toast.show({ title: '已退出登录' })
}, 100)
}, 400)
}}
/>,
)
}
const menuItems: MenuItem[] = [
{
id: 'service',
label: '服务条款',
icon: 'document-text-outline',
onPress: () => {
router.push({
pathname: '/service',
params: { type: 'terms', title: '服务条款' },
})
},
},
{
id: 'privacy',
label: '隐私协议',
icon: 'shield-outline',
onPress: () => {
router.push({
pathname: '/privacy',
params: { type: 'privacy', title: '隐私协议' },
})
},
},
// {
// id: 'about',
// label: '关于我们',
// icon: 'information-circle-outline',
// onPress: () => {
// // TODO: 替换为实际的关于我们URL或创建关于页面
// router.push({
// pathname: '/webview',
// params: { url: 'https://example.com/about', title: '关于我们' },
// })
// },
// },
]
const handleUserProfileClick = () => {
router.push('/profile')
}
const renderHeader = () => (
<Block className="flex-row items-center justify-between px-[16px] py-[10px]">
<Block className="ml-[-8px] size-[40px] items-center justify-center" opacity={0.7} onClick={() => router.back()}>
<Ionicons color="#323232" name="chevron-back" size={24} />
</Block>
<Text className="text-[16px] font-[700] text-[#323232]"></Text>
<Block className="w-[32px]" />
</Block>
)
const renderUserSection = () => (
<Block className="flex-row items-center gap-[12px] px-[16px] py-[10px]" onClick={handleUserProfileClick}>
<Block className="size-[68px] items-center justify-center overflow-hidden rounded-full bg-gray-200">
{user?.image ? (
<Img src={user.image} style={{ width: 68, height: 68, borderRadius: 30 }} width={68} />
) : (
<Ionicons color="#9CA3AF" name="person" size={32} />
)}
</Block>
<Block className="flex-1">
<Text className="text-[16px] font-[700] text-[#323232]">{user?.name || user?.phoneNumber || '未登录'}</Text>
{(user?.phoneNumber || (user?.email && user?.emailVerified)) && user?.name && (
<Text className="mt-[4px] text-[12px] text-[#9D9D9D]">{user.phoneNumber || user.email}</Text>
)}
</Block>
<Ionicons color="#323232" name="chevron-forward" size={20} />
</Block>
)
const renderMenuSection = () => (
<Block className="mt-[8px]">
<Text className="px-[20px] py-[8px] text-[12px] text-[#9D9D9D]">使</Text>
<Block className="mx-[16px] rounded-xl bg-white">
{menuItems.map((item) => (
<Block key={item.id}>
<Block className="flex-row items-center gap-[12px] p-[16px]" onClick={item.onPress}>
<Ionicons color="#323232" name={item.icon} size={22} />
<Text className="flex-1 text-[14px] text-[#323232]">{item.label}</Text>
<Ionicons color="#9D9D9D" name="chevron-forward" size={20} />
</Block>
</Block>
))}
</Block>
</Block>
)
const renderLogoutButton = () => (
<Block className="mt-[40px] items-center px-[16px]">
<Block className="w-full items-center justify-center rounded-xl bg-white py-[14px]" onClick={handleLogout}>
<Text className="text-[14px] font-[600] text-[#323232]">退</Text>
</Block>
</Block>
)
return (
<Block className="h-full flex-1 bg-[#fafafa]">
<Stack.Screen options={{ headerShown: false }} />
{renderHeader()}
<ScrollView contentContainerStyle={{ flexGrow: 1 }} showsVerticalScrollIndicator={false}>
{renderUserSection()}
{renderMenuSection()}
{renderLogoutButton()}
</ScrollView>
</Block>
)
})