feat: 适配 headerbar ios26
This commit is contained in:
@@ -3,6 +3,7 @@ import { HeaderBar } from '@/components/ui/HeaderBar';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
||||
import { completeTask, skipTask } from '@/store/tasksSlice';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
@@ -19,17 +20,18 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
export default function TaskDetailScreen() {
|
||||
const safeAreaTop = useSafeAreaTop()
|
||||
const { taskId } = useLocalSearchParams<{ taskId: string }>();
|
||||
const router = useRouter();
|
||||
const theme = useColorScheme() ?? 'light';
|
||||
const colorTokens = Colors[theme];
|
||||
const dispatch = useAppDispatch();
|
||||
const { showConfirm } = useGlobalDialog();
|
||||
|
||||
|
||||
// 从Redux中获取任务数据
|
||||
const { tasks, tasksLoading } = useAppSelector(state => state.tasks);
|
||||
const task = tasks.find(t => t.id === taskId) || null;
|
||||
|
||||
|
||||
const [comment, setComment] = useState('');
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
@@ -98,10 +100,10 @@ export default function TaskDetailScreen() {
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
};
|
||||
return `创建于 ${date.toLocaleDateString('zh-CN', options)}`;
|
||||
};
|
||||
@@ -112,14 +114,14 @@ export default function TaskDetailScreen() {
|
||||
}
|
||||
|
||||
try {
|
||||
await dispatch(completeTask({
|
||||
taskId: task.id,
|
||||
completionData: {
|
||||
await dispatch(completeTask({
|
||||
taskId: task.id,
|
||||
completionData: {
|
||||
count: 1,
|
||||
notes: '通过任务详情页面完成'
|
||||
}
|
||||
}
|
||||
})).unwrap();
|
||||
|
||||
|
||||
// 检查任务是否真正完成(当前完成次数是否达到目标次数)
|
||||
const updatedTask = tasks.find(t => t.id === task.id);
|
||||
if (updatedTask && updatedTask.currentCount >= updatedTask.targetCount) {
|
||||
@@ -150,13 +152,13 @@ export default function TaskDetailScreen() {
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
await dispatch(skipTask({
|
||||
taskId: task.id,
|
||||
skipData: {
|
||||
await dispatch(skipTask({
|
||||
taskId: task.id,
|
||||
skipData: {
|
||||
reason: '用户主动跳过'
|
||||
}
|
||||
}
|
||||
})).unwrap();
|
||||
|
||||
|
||||
Alert.alert('成功', '任务已跳过!');
|
||||
router.back();
|
||||
} catch (error) {
|
||||
@@ -178,10 +180,13 @@ export default function TaskDetailScreen() {
|
||||
if (tasksLoading) {
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colorTokens.background }]}>
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
onBack={() => router.back()}
|
||||
/>
|
||||
<View style={{
|
||||
paddingTop: safeAreaTop
|
||||
}} />
|
||||
<View style={styles.loadingContainer}>
|
||||
<Text style={[styles.loadingText, { color: colorTokens.text }]}>加载中...</Text>
|
||||
</View>
|
||||
@@ -192,10 +197,13 @@ export default function TaskDetailScreen() {
|
||||
if (!task) {
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colorTokens.background }]}>
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
onBack={() => router.back()}
|
||||
/>
|
||||
<View style={{
|
||||
paddingTop: safeAreaTop
|
||||
}} />
|
||||
<View style={styles.errorContainer}>
|
||||
<Text style={[styles.errorText, { color: colorTokens.text }]}>任务不存在</Text>
|
||||
</View>
|
||||
@@ -206,8 +214,8 @@ export default function TaskDetailScreen() {
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colorTokens.background }]}>
|
||||
{/* 使用HeaderBar组件 */}
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
<HeaderBar
|
||||
title="任务详情"
|
||||
onBack={() => router.back()}
|
||||
right={
|
||||
task.status !== 'completed' && task.status !== 'skipped' && task.currentCount < task.targetCount ? (
|
||||
@@ -222,7 +230,9 @@ export default function TaskDetailScreen() {
|
||||
}
|
||||
/>
|
||||
|
||||
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
|
||||
<ScrollView style={styles.scrollView} contentContainerStyle={{
|
||||
paddingTop: safeAreaTop
|
||||
}} showsVerticalScrollIndicator={false}>
|
||||
{/* 任务标题和创建时间 */}
|
||||
<View style={styles.titleSection}>
|
||||
<Text style={[styles.taskTitle, { color: colorTokens.text }]}>{task.title}</Text>
|
||||
@@ -255,7 +265,7 @@ export default function TaskDetailScreen() {
|
||||
<Text style={styles.priorityTagText}>高</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={styles.infoItem}>
|
||||
<Text style={[styles.infoLabel, { color: colorTokens.text }]}>难度</Text>
|
||||
<View style={[styles.difficultyTag, { backgroundColor: getDifficultyColor('very_easy') }]}>
|
||||
@@ -268,7 +278,7 @@ export default function TaskDetailScreen() {
|
||||
{/* 任务进度信息 */}
|
||||
<View style={styles.progressSection}>
|
||||
<Text style={[styles.sectionTitle, { color: colorTokens.text }]}>进度</Text>
|
||||
|
||||
|
||||
{/* 进度条 */}
|
||||
<View style={styles.progressBar}>
|
||||
<View
|
||||
@@ -276,13 +286,13 @@ export default function TaskDetailScreen() {
|
||||
styles.progressFill,
|
||||
{
|
||||
width: task.progressPercentage > 0 ? `${Math.min(task.progressPercentage, 100)}%` : '2%',
|
||||
backgroundColor: task.progressPercentage >= 100
|
||||
? '#10B981'
|
||||
: task.progressPercentage >= 50
|
||||
? '#F59E0B'
|
||||
: task.progressPercentage > 0
|
||||
? colorTokens.primary
|
||||
: '#E5E7EB',
|
||||
backgroundColor: task.progressPercentage >= 100
|
||||
? '#10B981'
|
||||
: task.progressPercentage >= 50
|
||||
? '#F59E0B'
|
||||
: task.progressPercentage > 0
|
||||
? colorTokens.primary
|
||||
: '#E5E7EB',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -315,8 +325,8 @@ export default function TaskDetailScreen() {
|
||||
{/* 底部操作按钮 */}
|
||||
{task.status !== 'completed' && task.status !== 'skipped' && task.currentCount < task.targetCount && (
|
||||
<View style={styles.actionButtons}>
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, styles.skipButton]}
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, styles.skipButton]}
|
||||
onPress={handleSkipTask}
|
||||
>
|
||||
<MaterialIcons name="skip-next" size={20} color="#6B7280" />
|
||||
@@ -338,7 +348,7 @@ export default function TaskDetailScreen() {
|
||||
</View>
|
||||
<View style={styles.commentInputWrapper}>
|
||||
<TextInput
|
||||
style={[styles.commentInput, {
|
||||
style={[styles.commentInput, {
|
||||
color: colorTokens.text,
|
||||
backgroundColor: '#F3F4F6'
|
||||
}]}
|
||||
@@ -349,9 +359,9 @@ export default function TaskDetailScreen() {
|
||||
multiline
|
||||
maxLength={500}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[styles.sendButton, {
|
||||
backgroundColor: comment.trim() ? '#6B7280' : '#D1D5DB'
|
||||
<TouchableOpacity
|
||||
style={[styles.sendButton, {
|
||||
backgroundColor: comment.trim() ? '#6B7280' : '#D1D5DB'
|
||||
}]}
|
||||
onPress={handleSendComment}
|
||||
disabled={!comment.trim()}
|
||||
|
||||
Reference in New Issue
Block a user