feat(challenges): 添加自定义挑战功能和多语言支持
- 新增自定义挑战创建页面,支持设置挑战类型、时间范围、目标值等 - 实现挑战邀请码系统,支持通过邀请码加入自定义挑战 - 完善挑战详情页面的多语言翻译支持 - 优化用户认证状态检查逻辑,使用token作为主要判断依据 - 添加阿里字体文件支持,提升UI显示效果 - 改进确认弹窗组件,支持Liquid Glass效果和自定义内容 - 优化应用启动流程,直接读取onboarding状态而非预加载用户数据
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user