feat(challenges): 添加自定义挑战功能和多语言支持

- 新增自定义挑战创建页面,支持设置挑战类型、时间范围、目标值等
- 实现挑战邀请码系统,支持通过邀请码加入自定义挑战
- 完善挑战详情页面的多语言翻译支持
- 优化用户认证状态检查逻辑,使用token作为主要判断依据
- 添加阿里字体文件支持,提升UI显示效果
- 改进确认弹窗组件,支持Liquid Glass效果和自定义内容
- 优化应用启动流程,直接读取onboarding状态而非预加载用户数据
This commit is contained in:
richarjiang
2025-11-26 16:39:01 +08:00
parent 3ad0e08d58
commit 39671ed70f
24 changed files with 3124 additions and 727 deletions

View File

@@ -215,11 +215,13 @@ const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: '700',
fontFamily: 'AliBold'
},
remaining: {
fontSize: 11,
fontWeight: '600',
alignSelf: 'flex-start',
fontFamily: 'AliRegular'
},
metaRow: {
marginTop: 12,
@@ -227,10 +229,12 @@ const styles = StyleSheet.create({
metaValue: {
fontSize: 14,
fontWeight: '700',
fontFamily: 'AliBold'
},
metaSuffix: {
fontSize: 13,
fontWeight: '500',
fontFamily: 'AliBold'
},
track: {
marginTop: 12,

View File

@@ -1,10 +1,13 @@
import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
import * as Haptics from 'expo-haptics';
import React, { useEffect, useRef, useState } from 'react';
import {
ActivityIndicator,
Animated,
Dimensions,
KeyboardAvoidingView,
Modal,
Platform,
StyleSheet,
Text,
TouchableOpacity,
@@ -24,6 +27,7 @@ interface ConfirmationSheetProps {
cancelText?: string;
destructive?: boolean;
loading?: boolean;
content?: React.ReactNode;
}
export function ConfirmationSheet({
@@ -36,11 +40,13 @@ export function ConfirmationSheet({
cancelText = '取消',
destructive = false,
loading = false,
content,
}: ConfirmationSheetProps) {
const insets = useSafeAreaInsets();
const translateY = useRef(new Animated.Value(screenHeight)).current;
const backdropOpacity = useRef(new Animated.Value(0)).current;
const [modalVisible, setModalVisible] = useState(visible);
const isGlassAvailable = isLiquidGlassAvailable();
useEffect(() => {
if (visible) {
@@ -116,7 +122,10 @@ export function ConfirmationSheet({
onRequestClose={onClose}
statusBarTranslucent
>
<View style={styles.overlay}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
style={styles.overlay}
>
<Animated.View
style={[
styles.backdrop,
@@ -140,35 +149,67 @@ export function ConfirmationSheet({
<View style={styles.handle} />
<Text style={styles.title}>{title}</Text>
{description ? <Text style={styles.description}>{description}</Text> : null}
{content}
<View style={styles.actions}>
<TouchableOpacity
style={styles.cancelButton}
style={[styles.buttonContainer, loading && styles.disabledButton]}
activeOpacity={0.85}
onPress={handleCancel}
disabled={loading}
>
<Text style={styles.cancelText}>{cancelText}</Text>
{isGlassAvailable ? (
<GlassView
style={styles.glassButton}
glassEffectStyle="regular"
tintColor="rgba(241, 245, 249, 0.6)"
isInteractive
>
<Text style={styles.cancelText}>{cancelText}</Text>
</GlassView>
) : (
<View style={styles.cancelButton}>
<Text style={styles.cancelText}>{cancelText}</Text>
</View>
)}
</TouchableOpacity>
<TouchableOpacity
style={[
styles.confirmButton,
destructive ? styles.destructiveButton : styles.primaryButton,
loading && styles.disabledButton,
]}
style={[styles.buttonContainer, loading && styles.disabledButton]}
activeOpacity={0.85}
onPress={handleConfirm}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
{isGlassAvailable ? (
<GlassView
style={styles.glassButton}
glassEffectStyle="regular"
tintColor={destructive ? 'rgba(239, 68, 68, 0.85)' : 'rgba(37, 99, 235, 0.85)'}
isInteractive
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.confirmText}>{confirmText}</Text>
)}
</GlassView>
) : (
<Text style={styles.confirmText}>{confirmText}</Text>
<View
style={[
styles.confirmButton,
destructive ? styles.destructiveButton : styles.primaryButton,
]}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.confirmText}>{confirmText}</Text>
)}
</View>
)}
</TouchableOpacity>
</View>
</Animated.View>
</View>
</KeyboardAvoidingView>
</Modal>
);
}
@@ -221,8 +262,17 @@ const styles = StyleSheet.create({
gap: 12,
marginTop: 8,
},
cancelButton: {
buttonContainer: {
flex: 1,
},
glassButton: {
height: 56,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
},
cancelButton: {
height: 56,
borderRadius: 18,
borderWidth: 1,
@@ -237,7 +287,6 @@ const styles = StyleSheet.create({
color: '#111827',
},
confirmButton: {
flex: 1,
height: 56,
borderRadius: 18,
alignItems: 'center',