feat: 章节软删除、对话左滑删除,移除已读状态
- 章节:详情页增加删除按钮,软删除(is_active=False),AI 不再修改但保留供参考 - 章节:get_chapter 增加 is_active 校验,已删除章节返回 404 - 章节:AI 生成时参考同类别已删除章节摘要 - 对话:左滑显示删除,调用 hard delete API,删除前二次确认 - 对话:根布局包裹 GestureHandlerRootView 以支持 Swipeable - 对话:移除已读/未读状态展示及相关 i18n
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { Image } from 'expo-image';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useLocalSearchParams } from 'expo-router';
|
||||
import { Settings, X } from 'lucide-react-native';
|
||||
import { router, useLocalSearchParams } from 'expo-router';
|
||||
import { Settings, Trash2, X } from 'lucide-react-native';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
Modal,
|
||||
Platform,
|
||||
Pressable,
|
||||
@@ -20,7 +21,7 @@ import { Icon } from '@/components/ui/icon';
|
||||
import { Text } from '@/components/ui/text';
|
||||
import { ScreenHeader } from '@/components/screen-header';
|
||||
import { ScreenGutter } from '@/constants/layout';
|
||||
import { useChapterDetail } from '@/features/memoir/hooks';
|
||||
import { useChapterDetail, useDeleteChapter } from '@/features/memoir/hooks';
|
||||
|
||||
// Life-Echo reading colors (from HTML reference)
|
||||
const READING_COLORS = {
|
||||
@@ -536,6 +537,7 @@ export default function ChapterScreen() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const { t } = useTranslation('memoir');
|
||||
const { data: chapter, isLoading } = useChapterDetail(id ?? '');
|
||||
const deleteChapter = useDeleteChapter();
|
||||
|
||||
const [settingsVisible, setSettingsVisible] = useState(false);
|
||||
const [fontSize, setFontSize] = useState<FontSize>('default');
|
||||
@@ -568,6 +570,7 @@ export default function ChapterScreen() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: bgColor,
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
@@ -576,6 +579,25 @@ export default function ChapterScreen() {
|
||||
>
|
||||
{t('chapterReading.chapterNotFound')}
|
||||
</Text>
|
||||
<Pressable
|
||||
onPress={() => router.back()}
|
||||
style={({ pressed }) => ({
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
backgroundColor: READING_COLORS.primary,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('chapterReading.back')}
|
||||
>
|
||||
<Text
|
||||
style={{ color: '#fff', fontSize: 16, fontWeight: '600' }}
|
||||
selectable={false}
|
||||
>
|
||||
{t('chapterReading.back')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -583,6 +605,25 @@ export default function ChapterScreen() {
|
||||
const sections = chapter.sections ?? [];
|
||||
const coverImageUrl = chapter.cover_image?.url ?? null;
|
||||
|
||||
const handleDeletePress = () => {
|
||||
Alert.alert(
|
||||
t('chapterReading.deleteChapter'),
|
||||
t('chapterReading.confirmDeleteMessage'),
|
||||
[
|
||||
{ text: t('chapterReading.cancel'), style: 'cancel' },
|
||||
{
|
||||
text: t('chapterReading.deleteChapterAction'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
deleteChapter.mutate(chapter.id, {
|
||||
onSuccess: () => router.back(),
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: bgColor }}>
|
||||
<ScreenHeader
|
||||
@@ -592,19 +633,38 @@ export default function ChapterScreen() {
|
||||
title={chapter.title}
|
||||
backAccessibilityLabel={t('chapterReading.back')}
|
||||
right={
|
||||
<Pressable
|
||||
onPress={() => setSettingsVisible(true)}
|
||||
style={({ pressed }) => ({
|
||||
padding: 8,
|
||||
marginRight: -8,
|
||||
borderRadius: 9999,
|
||||
opacity: pressed ? 0.7 : 1,
|
||||
})}
|
||||
accessibilityLabel={t('chapterReading.settings')}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<Icon as={Settings} size={24} color={READING_COLORS.primary} />
|
||||
</Pressable>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
|
||||
<Pressable
|
||||
onPress={handleDeletePress}
|
||||
disabled={deleteChapter.isPending}
|
||||
style={({ pressed }) => ({
|
||||
padding: 8,
|
||||
borderRadius: 9999,
|
||||
opacity: pressed ? 0.7 : 1,
|
||||
})}
|
||||
accessibilityLabel={t('chapterReading.deleteChapter')}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<Icon
|
||||
as={Trash2}
|
||||
size={22}
|
||||
color={READING_COLORS.onSurfaceVariant}
|
||||
/>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={() => setSettingsVisible(true)}
|
||||
style={({ pressed }) => ({
|
||||
padding: 8,
|
||||
marginRight: -8,
|
||||
borderRadius: 9999,
|
||||
opacity: pressed ? 0.7 : 1,
|
||||
})}
|
||||
accessibilityLabel={t('chapterReading.settings')}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<Icon as={Settings} size={24} color={READING_COLORS.primary} />
|
||||
</Pressable>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user