feat/调整tts音色,调整封面图prompt,修复对话页输入框显示逻辑,待验证封面图生成功能
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
Animated,
|
||||
FlatList,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
@@ -578,8 +579,20 @@ export default function ConversationScreen() {
|
||||
|
||||
const [input, setInput] = useState('');
|
||||
const [inputMode, setInputMode] = useState<InputMode>('text');
|
||||
const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
|
||||
const listRef = useRef<FlatList>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const onShow = () => setIsKeyboardVisible(true);
|
||||
const onHide = () => setIsKeyboardVisible(false);
|
||||
const subShow = Keyboard.addListener('keyboardDidShow', onShow);
|
||||
const subHide = Keyboard.addListener('keyboardDidHide', onHide);
|
||||
return () => {
|
||||
subShow.remove();
|
||||
subHide.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const flattenedData = flattenMessagesForList(messages ?? []);
|
||||
|
||||
const isRecording = recorderStatus === 'recording';
|
||||
@@ -607,11 +620,12 @@ export default function ConversationScreen() {
|
||||
: t('connectionDisconnected');
|
||||
|
||||
const keyboardOffset = Platform.OS === 'ios' ? insets.top + 56 : 0;
|
||||
const kavEnabled = inputMode === 'text' && isKeyboardVisible;
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior="padding"
|
||||
behavior={kavEnabled ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={keyboardOffset}
|
||||
>
|
||||
<View style={styles.column}>
|
||||
@@ -694,9 +708,14 @@ export default function ConversationScreen() {
|
||||
onChangeText={setInput}
|
||||
onSend={handleSend}
|
||||
inputMode={inputMode}
|
||||
onInputModeToggle={() =>
|
||||
setInputMode((m) => (m === 'text' ? 'voice' : 'text'))
|
||||
}
|
||||
onInputModeToggle={() => {
|
||||
setInputMode((m) => {
|
||||
if (m === 'text') {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
return m === 'text' ? 'voice' : 'text';
|
||||
});
|
||||
}}
|
||||
onAddPress={() => {}}
|
||||
onStartRecording={handleStartRecording}
|
||||
onStopRecording={() => void stopRecording()}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import { Image } from 'expo-image';
|
||||
import { router } from 'expo-router';
|
||||
import React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
Pressable,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
@@ -17,7 +19,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Text } from '@/components/ui/text';
|
||||
import { ScreenGutter } from '@/constants/layout';
|
||||
import { useCreateConversation } from '@/features/conversation/hooks';
|
||||
import { useChapters } from '@/features/memoir/hooks';
|
||||
import { useChapters, useCheckCoverGeneration } from '@/features/memoir/hooks';
|
||||
import type { ChapterViewModel } from '@/features/memoir/types';
|
||||
|
||||
type ChapterVariant = 'completed' | 'drafting' | 'locked-left' | 'locked-large';
|
||||
@@ -393,8 +395,26 @@ function EmptyState({
|
||||
|
||||
export default function MemoirScreen() {
|
||||
const { t } = useTranslation('memoir');
|
||||
const { viewModels: chapters, isLoading } = useChapters();
|
||||
const { viewModels: chapters, isLoading, refetch } = useChapters();
|
||||
const createConversation = useCreateConversation();
|
||||
const checkCover = useCheckCoverGeneration();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
checkCover.mutate(undefined);
|
||||
}, [checkCover.mutate]),
|
||||
);
|
||||
|
||||
const handleRefresh = useCallback(async () => {
|
||||
setRefreshing(true);
|
||||
try {
|
||||
await checkCover.mutateAsync(undefined);
|
||||
await refetch();
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
}, [checkCover.mutateAsync, refetch]);
|
||||
|
||||
const handleStartChapter = () => {
|
||||
createConversation.mutate(undefined, {
|
||||
@@ -414,6 +434,9 @@ export default function MemoirScreen() {
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
className="flex-1"
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||
}
|
||||
contentContainerStyle={{
|
||||
paddingHorizontal: ScreenGutter,
|
||||
paddingTop: 24,
|
||||
|
||||
@@ -48,6 +48,12 @@ export const memoirApi = {
|
||||
);
|
||||
},
|
||||
|
||||
checkCoverGeneration() {
|
||||
return api.post<{ triggered: string[] }>(
|
||||
'/api/chapters/check-cover-generation',
|
||||
);
|
||||
},
|
||||
|
||||
regenerateChapter(chapterId: string) {
|
||||
return api.post<{ status: string; message: string }>(
|
||||
`/api/chapters/${chapterId}/regenerate`,
|
||||
|
||||
@@ -69,6 +69,19 @@ export function useDeleteChapter() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useCheckCoverGeneration() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: () => memoirApi.checkCoverGeneration(),
|
||||
onSuccess: (data) => {
|
||||
if (data.triggered.length > 0) {
|
||||
queryClient.invalidateQueries({ queryKey: memoirKeys.chapters() });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Memoir state ───
|
||||
|
||||
export function useMemoirState() {
|
||||
|
||||
Reference in New Issue
Block a user