feat(push): integrate Honor and Xiaomi push plugins with related updates

This commit is contained in:
lafay
2026-06-18 00:03:37 +08:00
parent b2979311bb
commit 96e8de18bf
20 changed files with 1088 additions and 265 deletions

View File

@@ -1,8 +1,10 @@
import { StatusBar } from 'expo-status-bar';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
ActivityIndicator,
Alert,
KeyboardAvoidingView,
Platform,
ScrollView,
StyleSheet,
TextInput,
@@ -13,7 +15,7 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import { authService } from '../../services';
import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../theme';
import { spacing, fontSizes, useAppColors, type AppColors } from '../../theme';
import { Text, SimpleHeader } from '../../components/common';
import { useResponsive, useResponsiveSpacing } from '../../hooks';
import { useAuthStore } from '../../stores';
@@ -33,6 +35,9 @@ function createAccountDeletionStyles(colors: AppColors) {
alignItems: 'center',
justifyContent: 'center',
},
keyboardView: {
flex: 1,
},
scrollContent: {
paddingVertical: spacing.lg,
},
@@ -41,53 +46,153 @@ function createAccountDeletionStyles(colors: AppColors) {
alignSelf: 'center',
width: '100%',
},
section: {
marginBottom: spacing['2xl'],
// 顶部叙述区:左对齐、有呼吸感
heroSection: {
paddingHorizontal: spacing['2xl'],
paddingTop: spacing.md,
paddingBottom: spacing.xl,
},
// 警告卡片 - 扁平化风格
warningCard: {
backgroundColor: colors.error.light + '20',
borderRadius: 14,
padding: spacing.lg,
marginHorizontal: spacing['2xl'],
marginBottom: spacing.xl,
},
warningTitle: {
fontWeight: '700',
fontSize: fontSizes.md,
heroEyebrow: {
fontSize: 13,
color: colors.text.hint,
letterSpacing: 1,
marginBottom: spacing.sm,
color: colors.error.main,
},
warningText: {
color: colors.error.dark,
marginBottom: spacing.xs,
fontSize: fontSizes.sm,
},
// 状态卡片 - 扁平化风格
statusCard: {
backgroundColor: colors.warning.light + '30',
borderRadius: 14,
padding: spacing.lg,
marginHorizontal: spacing['2xl'],
marginBottom: spacing.xl,
},
statusTitle: {
heroTitle: {
fontSize: 24,
fontWeight: '700',
fontSize: fontSizes.md,
color: colors.text.primary,
lineHeight: 32,
marginBottom: spacing.sm,
color: colors.warning.dark,
},
daysText: {
heroDesc: {
fontSize: 15,
color: colors.text.secondary,
lineHeight: 22,
},
// 倒计时区:圆环 + 数字 + 文案,替代"大数字 + 标签"卡片
countdownSection: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing['2xl'],
paddingVertical: spacing.lg,
},
countdownRing: {
width: 88,
height: 88,
borderRadius: 44,
borderWidth: 4,
borderColor: colors.warning.main,
alignItems: 'center',
justifyContent: 'center',
marginRight: spacing.lg,
},
countdownNumber: {
fontSize: 32,
fontWeight: '700',
color: colors.warning.dark,
textAlign: 'center',
marginVertical: spacing.md,
lineHeight: 36,
},
// 内容区域
sectionContent: {
marginHorizontal: spacing['2xl'],
marginBottom: spacing.xl,
countdownUnit: {
fontSize: 11,
color: colors.text.secondary,
marginTop: -2,
},
countdownTextWrap: {
flex: 1,
},
countdownTitle: {
fontSize: 16,
fontWeight: '600',
color: colors.text.primary,
marginBottom: 4,
},
countdownDesc: {
fontSize: 13,
color: colors.text.secondary,
lineHeight: 19,
},
// 引导文 + 列表(无背景、无圆角,自然排版)
guideSection: {
paddingHorizontal: spacing['2xl'],
paddingTop: spacing.lg,
},
guideLabel: {
fontSize: 13,
color: colors.text.secondary,
fontWeight: '500',
marginBottom: spacing.md,
},
guideItem: {
flexDirection: 'row',
alignItems: 'flex-start',
paddingVertical: 10,
},
guideIcon: {
marginRight: spacing.md,
marginTop: 2,
},
guideTextWrap: {
flex: 1,
},
guideTitle: {
fontSize: 15,
fontWeight: '500',
color: colors.text.primary,
marginBottom: 2,
},
guideDesc: {
fontSize: 13,
color: colors.text.secondary,
lineHeight: 19,
},
guideDivider: {
height: StyleSheet.hairlineWidth,
backgroundColor: colors.divider,
marginLeft: spacing['2xl'] + 22 + spacing.md,
},
// 注意事项(无填色,仅左侧色条)
noticeSection: {
flexDirection: 'row',
alignItems: 'flex-start',
paddingHorizontal: spacing['2xl'],
paddingTop: spacing.lg,
paddingBottom: spacing.md,
},
noticeBar: {
width: 3,
alignSelf: 'stretch',
backgroundColor: colors.error.main,
borderRadius: 2,
marginRight: spacing.md,
},
noticeTextWrap: {
flex: 1,
},
noticeTitle: {
fontSize: 14,
fontWeight: '600',
color: colors.error.main,
marginBottom: 4,
},
noticeDesc: {
fontSize: 13,
color: colors.text.secondary,
lineHeight: 20,
},
// 表单区(与 AccountSecurity 风格一致:分节标题 + 输入框)
formSection: {
marginTop: spacing.lg,
},
sectionHeader: {
paddingHorizontal: spacing['2xl'],
marginBottom: spacing.sm,
marginTop: spacing.sm,
},
sectionTitle: {
fontWeight: '600',
@@ -95,47 +200,40 @@ function createAccountDeletionStyles(colors: AppColors) {
color: colors.text.secondary,
textTransform: 'uppercase',
letterSpacing: 0.5,
marginBottom: spacing.md,
},
listItem: {
// 输入框
inputWrapper: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: spacing.sm,
},
listBullet: {
marginRight: spacing.sm,
marginTop: 2,
},
// 输入框 - 扁平化风格
input: {
alignItems: 'center',
backgroundColor: colors.background.default,
borderRadius: 14,
padding: spacing.md,
fontSize: 16,
paddingHorizontal: spacing.lg,
height: 56,
marginHorizontal: spacing['2xl'],
marginBottom: spacing.md,
},
inputIcon: {
marginRight: spacing.sm,
},
input: {
flex: 1,
color: colors.text.primary,
fontSize: fontSizes.md,
height: 56,
},
// 按钮行
eyeButton: {
padding: 4,
marginLeft: 4,
},
// 按钮行:次按钮在左、危险按钮在右,与全站保持一致
buttonRow: {
flexDirection: 'row',
gap: spacing.md,
marginTop: spacing.lg,
marginHorizontal: spacing['2xl'],
},
// 扁平化按钮
primaryButton: {
flex: 1,
height: 56,
borderRadius: 14,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
},
primaryButtonText: {
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '600',
},
secondaryButton: {
flex: 1,
height: 56,
@@ -165,11 +263,38 @@ function createAccountDeletionStyles(colors: AppColors) {
fontWeight: '600',
},
buttonDisabled: {
opacity: 0.6,
opacity: 0.5,
},
cancelButton: {
marginTop: spacing.lg,
primaryButton: {
height: 56,
borderRadius: 14,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
marginHorizontal: spacing['2xl'],
marginTop: spacing.md,
},
primaryButtonText: {
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '600',
},
// 页脚:联系客服
footer: {
alignItems: 'center',
marginTop: spacing.xl,
paddingHorizontal: spacing['2xl'],
},
footerText: {
fontSize: 13,
color: colors.text.hint,
lineHeight: 20,
textAlign: 'center',
},
footerLink: {
color: colors.primary.main,
fontWeight: '500',
},
});
}
@@ -185,9 +310,9 @@ export const AccountDeletionScreen: React.FC = () => {
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 });
const loadStatus = useCallback(async () => {
@@ -284,121 +409,244 @@ export const AccountDeletionScreen: React.FC = () => {
);
}
// 待注销状态
if (status?.is_pending_deletion) {
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<StatusBar style="auto" />
<SimpleHeader title="注销账号" onBack={() => router.back()} />
<ScrollView
contentContainerStyle={[
styles.scrollContent,
{ paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding },
]}
showsVerticalScrollIndicator={false}
>
<View style={styles.content}>
{/* 顶部叙述 */}
<View style={styles.heroSection}>
<Text style={styles.heroEyebrow}>ACCOUNT · </Text>
<Text style={styles.heroTitle}></Text>
<Text style={styles.heroDesc}>
使
</Text>
</View>
{/* 倒计时 */}
<View style={styles.countdownSection}>
<View style={styles.countdownRing}>
<Text style={styles.countdownNumber}>{status.cool_down_days || 90}</Text>
<Text style={styles.countdownUnit}></Text>
</View>
<View style={styles.countdownTextWrap}>
<Text style={styles.countdownTitle}></Text>
<Text style={styles.countdownDesc}>
</Text>
</View>
</View>
{/* 取消按钮 */}
<TouchableOpacity
style={[
styles.primaryButton,
submitting && styles.buttonDisabled,
]}
onPress={handleCancelDeletion}
disabled={submitting}
activeOpacity={0.9}
>
{submitting ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.primaryButtonText}></Text>
)}
</TouchableOpacity>
{/* 页脚 */}
<View style={styles.footer}>
<Text style={styles.footerText}>
<Text style={styles.footerLink}> </Text>
</Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
// 正常状态:申请注销
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<StatusBar style="auto" />
<SimpleHeader title="注销账号" onBack={() => router.back()} />
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding }]}>
<View style={styles.content}>
{status?.is_pending_deletion ? (
<View style={styles.section}>
<View style={styles.statusCard}>
<Text variant="body" style={styles.statusTitle}>
</Text>
<Text variant="body" color={colors.text.secondary}>
</Text>
<Text variant="body" style={styles.daysText}>
{status.cool_down_days || 90}
</Text>
<Text variant="caption" color={colors.text.secondary}>
</Text>
<TouchableOpacity
style={[styles.secondaryButton, styles.cancelButton, submitting && styles.buttonDisabled]}
onPress={handleCancelDeletion}
disabled={submitting}
>
{submitting ? (
<ActivityIndicator size="small" color={colors.text.primary} />
) : (
<Text style={styles.secondaryButtonText}></Text>
)}
</TouchableOpacity>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.keyboardView}
>
<ScrollView
contentContainerStyle={[
styles.scrollContent,
{ paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding },
]}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
>
<View style={styles.content}>
{/* 顶部叙述 */}
<View style={styles.heroSection}>
<Text style={styles.heroEyebrow}>ACCOUNT · </Text>
<Text style={styles.heroTitle}></Text>
<Text style={styles.heroDesc}>
90
</Text>
</View>
{/* 注销影响 - 列表式(无背景) */}
<View style={styles.guideSection}>
<Text style={styles.guideLabel}></Text>
<View style={styles.guideItem}>
<MaterialCommunityIcons
name="account-remove-outline"
size={22}
color={colors.error.main}
style={styles.guideIcon}
/>
<View style={styles.guideTextWrap}>
<Text style={styles.guideTitle}></Text>
<Text style={styles.guideDesc}></Text>
</View>
</View>
<View style={styles.guideDivider} />
<View style={styles.guideItem}>
<MaterialCommunityIcons
name="message-text-outline"
size={22}
color={colors.warning.main}
style={styles.guideIcon}
/>
<View style={styles.guideTextWrap}>
<Text style={styles.guideTitle}></Text>
<Text style={styles.guideDesc}></Text>
</View>
</View>
<View style={styles.guideDivider} />
<View style={styles.guideItem}>
<MaterialCommunityIcons
name="heart-broken-outline"
size={22}
color={colors.error.main}
style={styles.guideIcon}
/>
<View style={styles.guideTextWrap}>
<Text style={styles.guideTitle}></Text>
<Text style={styles.guideDesc}></Text>
</View>
</View>
<View style={styles.guideDivider} />
<View style={styles.guideItem}>
<MaterialCommunityIcons
name="clock-time-four-outline"
size={22}
color={colors.text.secondary}
style={styles.guideIcon}
/>
<View style={styles.guideTextWrap}>
<Text style={styles.guideTitle}>90 </Text>
<Text style={styles.guideDesc}></Text>
</View>
</View>
</View>
) : (
<View style={styles.section}>
{/* 警告卡片 */}
<View style={styles.warningCard}>
<Text variant="body" style={styles.warningTitle}>
{/* 红色提示 - 左侧细线代替大色块 */}
<View style={styles.noticeSection}>
<View style={styles.noticeBar} />
<View style={styles.noticeTextWrap}>
<Text style={styles.noticeTitle}></Text>
<Text style={styles.noticeDesc}>
90
</Text>
<Text variant="body" style={styles.warningText}>
90
</Text>
<Text variant="body" style={styles.warningText}>
</View>
</View>
{/* 密码确认 */}
<View style={styles.formSection}>
<View style={styles.sectionHeader}>
<Text variant="caption" style={styles.sectionTitle}>
</Text>
</View>
{/* 注销说明 */}
<View style={styles.sectionContent}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.listItem}>
<MaterialCommunityIcons name="close-circle" size={16} color={colors.error.main} style={styles.listBullet} />
<Text variant="body" color={colors.text.secondary}>
</Text>
</View>
<View style={styles.listItem}>
<MaterialCommunityIcons name="information" size={16} color={colors.warning.main} style={styles.listBullet} />
<Text variant="body" color={colors.text.secondary}>
</Text>
</View>
<View style={styles.listItem}>
<MaterialCommunityIcons name="close-circle" size={16} color={colors.error.main} style={styles.listBullet} />
<Text variant="body" color={colors.text.secondary}>
</Text>
</View>
<View style={styles.listItem}>
<MaterialCommunityIcons name="close-circle" size={16} color={colors.error.main} style={styles.listBullet} />
<Text variant="body" color={colors.text.secondary}>
</Text>
</View>
</View>
{/* 密码确认 */}
<View style={styles.sectionContent}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.inputWrapper}>
<MaterialCommunityIcons
name="lock-outline"
size={20}
color={colors.text.secondary}
style={styles.inputIcon}
/>
<TextInput
style={styles.input}
placeholder="请输入密码"
placeholder="请输入登录密码以确认"
placeholderTextColor={colors.text.hint}
secureTextEntry
secureTextEntry={!showPassword}
value={password}
onChangeText={setPassword}
autoCapitalize="none"
returnKeyType="done"
/>
</View>
{/* 按钮行 */}
<View style={styles.buttonRow}>
<TouchableOpacity
style={styles.secondaryButton}
onPress={() => router.back()}
onPress={() => setShowPassword(!showPassword)}
style={styles.eyeButton}
>
<Text style={styles.secondaryButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.dangerButton, submitting && styles.buttonDisabled]}
onPress={handleRequestDeletion}
disabled={submitting || !password.trim()}
>
{submitting ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.dangerButtonText}></Text>
)}
<MaterialCommunityIcons
name={showPassword ? 'eye-off-outline' : 'eye-outline'}
size={20}
color={colors.text.hint}
/>
</TouchableOpacity>
</View>
</View>
)}
</View>
</ScrollView>
{/* 按钮行 */}
<View style={styles.buttonRow}>
<TouchableOpacity
style={styles.secondaryButton}
onPress={() => router.back()}
activeOpacity={0.8}
>
<Text style={styles.secondaryButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.dangerButton,
(submitting || !password.trim()) && styles.buttonDisabled,
]}
onPress={handleRequestDeletion}
disabled={submitting || !password.trim()}
activeOpacity={0.9}
>
{submitting ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.dangerButtonText}></Text>
)}
</TouchableOpacity>
</View>
{/* 页脚 */}
<View style={styles.footer}>
<Text style={styles.footerText}>
<Text style={styles.footerLink}> </Text>
</Text>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
};

View File

@@ -175,37 +175,6 @@ export const NotificationSettingsScreen: React.FC = () => {
}
};
const handleAutoStartModeChange = async (mode: AutoStartMode) => {
if (mode === AutoStartMode.BACKGROUND) {
Alert.alert(
'后台消息接收',
getAutoStartDescription() + '\n\n开启后应用可在后台接收消息推送。',
[
{ text: '取消', style: 'cancel' },
{
text: '同意并开启',
onPress: async () => {
await setAutoStartMode(AutoStartMode.BACKGROUND);
setAutoStartModeState(AutoStartMode.BACKGROUND);
setAutoStartConsented(true);
// 重新初始化后台服务以应用新设置
const { reinitBackgroundService } = await import('@/services/background');
await reinitBackgroundService();
Alert.alert('已开启', '后台消息接收已开启。您可随时在设置中关闭。');
},
},
]
);
} else {
await setAutoStartMode(AutoStartMode.SILENT);
setAutoStartModeState(AutoStartMode.SILENT);
setAutoStartConsented(false);
// 重新初始化后台服务以应用新设置
const { reinitBackgroundService } = await import('@/services/background');
await reinitBackgroundService();
}
};
const syncModeOptions: { mode: BackgroundSyncMode; title: string; subtitle: string; icon: string }[] = [
{
mode: BackgroundSyncMode.DISABLED,