修复:CI 部署环境与 ref 错配、迁移碎片化、图片意图 source_span、章节物化脏版式、会话历史与本地语音不一致
新增:TTS 上传 COS 与分片、章节 reading_segments 物化与快照、markdown 清洗、会话消息 repository、语音 store 重构与相关测试
This commit is contained in:
@@ -5,17 +5,20 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { WsConnectionState } from '@/core/ws/types';
|
||||
|
||||
import { conversationApi } from './api';
|
||||
import { conversationMessagesRepository } from './conversation-messages-repository';
|
||||
import { conversationKeys } from './query-keys';
|
||||
import {
|
||||
RealtimeSession,
|
||||
type ErrorCallback,
|
||||
type StreamingTextCallback,
|
||||
type TtsSegmentPayload,
|
||||
} from './realtime-session';
|
||||
import type {
|
||||
ConversationListItem,
|
||||
MessageItem,
|
||||
StreamingAgentMessage,
|
||||
import {
|
||||
type ConversationListItem,
|
||||
type MessageItem,
|
||||
type StreamingAgentMessage,
|
||||
} from './types';
|
||||
import { voiceSegmentStore } from '@/features/voice/voice-segment-store';
|
||||
|
||||
// ─── Query hooks ───
|
||||
|
||||
@@ -38,7 +41,7 @@ export function useConversationDetail(conversationId: string) {
|
||||
export function useMessages(conversationId: string) {
|
||||
return useQuery({
|
||||
queryKey: conversationKeys.messages(conversationId),
|
||||
queryFn: () => conversationApi.messages(conversationId),
|
||||
queryFn: () => conversationMessagesRepository.loadMessages(conversationId),
|
||||
enabled: !!conversationId,
|
||||
});
|
||||
}
|
||||
@@ -76,7 +79,8 @@ export function useDeleteConversation() {
|
||||
return useMutation({
|
||||
mutationFn: (conversationId: string) =>
|
||||
conversationApi.delete(conversationId),
|
||||
onSuccess: (_, conversationId) => {
|
||||
onSuccess: async (_, conversationId) => {
|
||||
await voiceSegmentStore.clearConversation(conversationId);
|
||||
queryClient.setQueryData<ConversationListItem[]>(
|
||||
conversationKeys.lists(),
|
||||
(old) => old?.filter((item) => item.id !== conversationId),
|
||||
@@ -112,7 +116,7 @@ export function useEndConversation() {
|
||||
interface UseRealtimeSessionOptions {
|
||||
conversationId: string;
|
||||
enabled?: boolean;
|
||||
onTtsAudio?: (audioBase64: string) => void;
|
||||
onTtsSegment?: (payload: TtsSegmentPayload) => void;
|
||||
}
|
||||
|
||||
const MIN_RECORDING_DURATION_SEC = 1;
|
||||
@@ -137,7 +141,7 @@ interface RealtimeSessionState {
|
||||
export function useRealtimeSession({
|
||||
conversationId,
|
||||
enabled = true,
|
||||
onTtsAudio,
|
||||
onTtsSegment,
|
||||
}: UseRealtimeSessionOptions): RealtimeSessionState {
|
||||
const queryClient = useQueryClient();
|
||||
const sessionRef = useRef<RealtimeSession | null>(null);
|
||||
@@ -170,7 +174,7 @@ export function useRealtimeSession({
|
||||
conversationId,
|
||||
queryClient,
|
||||
onStreamingText: handleStreamingText,
|
||||
onTtsAudio,
|
||||
onTtsSegment,
|
||||
onError: handleError,
|
||||
onStateChange: setConnectionState,
|
||||
});
|
||||
@@ -190,7 +194,7 @@ export function useRealtimeSession({
|
||||
queryClient,
|
||||
handleStreamingText,
|
||||
handleError,
|
||||
onTtsAudio,
|
||||
onTtsSegment,
|
||||
]);
|
||||
|
||||
const sendText = useCallback(
|
||||
@@ -249,6 +253,12 @@ export function useRealtimeSession({
|
||||
}
|
||||
|
||||
const localId = `pending_voice_${Date.now()}`;
|
||||
await voiceSegmentStore.recordSentSegment({
|
||||
voiceSessionId,
|
||||
conversationId,
|
||||
fileUri: uri,
|
||||
durationMs,
|
||||
});
|
||||
queryClient.setQueryData<MessageItem[]>(
|
||||
conversationKeys.messages(conversationId),
|
||||
(old) => {
|
||||
@@ -259,6 +269,7 @@ export function useRealtimeSession({
|
||||
senderType: 'user',
|
||||
timestamp: Date.now(),
|
||||
messageType: 'voice',
|
||||
voiceSessionId,
|
||||
durationSeconds: durationSec,
|
||||
audioUri: uri,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user