fix(conversation): 优化对话列表滚动与播放队列并发
- 使用 InteractionManager.runAfterInteractions 包裹 scrollToEnd,避免滚动卡顿 - 提取 flattenedData 变量,减少重复计算 - 输入框增加 minHeight:22,空内容时保持一行高度 - use-player: 用 isPlayNextInProgressRef 防止 playNext 并发执行 - hooks: 格式化 useEffect 依赖数组
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
Alert,
|
||||
Animated,
|
||||
FlatList,
|
||||
InteractionManager,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
@@ -579,6 +580,8 @@ export default function ConversationScreen() {
|
||||
const [inputMode, setInputMode] = useState<InputMode>('text');
|
||||
const listRef = useRef<FlatList>(null);
|
||||
|
||||
const flattenedData = flattenMessagesForList(messages ?? []);
|
||||
|
||||
const isRecording = recorderStatus === 'recording';
|
||||
const recordingDuration = Math.floor(recordingDurationMs / 1000);
|
||||
|
||||
@@ -608,7 +611,7 @@ export default function ConversationScreen() {
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
behavior="padding"
|
||||
keyboardVerticalOffset={keyboardOffset}
|
||||
>
|
||||
<View style={styles.column}>
|
||||
@@ -652,7 +655,7 @@ export default function ConversationScreen() {
|
||||
ref={listRef}
|
||||
style={styles.list}
|
||||
contentContainerStyle={styles.listContent}
|
||||
data={flattenMessagesForList(messages ?? [])}
|
||||
data={flattenedData}
|
||||
keyExtractor={(item) => item.listKey}
|
||||
renderItem={({ item }) => (
|
||||
<MessageBubble
|
||||
@@ -662,7 +665,9 @@ export default function ConversationScreen() {
|
||||
/>
|
||||
)}
|
||||
onContentSizeChange={() =>
|
||||
listRef.current?.scrollToEnd({ animated: true })
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
listRef.current?.scrollToEnd({ animated: true });
|
||||
})
|
||||
}
|
||||
ListFooterComponent={
|
||||
streamingMessage ? (
|
||||
@@ -963,6 +968,7 @@ const styles = StyleSheet.create({
|
||||
lineHeight: 22,
|
||||
color: CHAT_COLORS.onSurface,
|
||||
padding: 0,
|
||||
minHeight: 22, // 保持空内容时至少一行高度
|
||||
maxHeight: 88, // 4 lines * 22 lineHeight
|
||||
},
|
||||
sendButton: {
|
||||
|
||||
@@ -184,7 +184,14 @@ export function useRealtimeSession({
|
||||
setConnectionState('disconnected');
|
||||
setStreamingMessage(null);
|
||||
};
|
||||
}, [conversationId, enabled, queryClient, handleStreamingText, handleError, onTtsAudio]);
|
||||
}, [
|
||||
conversationId,
|
||||
enabled,
|
||||
queryClient,
|
||||
handleStreamingText,
|
||||
handleError,
|
||||
onTtsAudio,
|
||||
]);
|
||||
|
||||
const sendText = useCallback(
|
||||
(text: string) => {
|
||||
|
||||
@@ -28,6 +28,7 @@ export function usePlayer(): UsePlayerResult {
|
||||
const [currentSource, setCurrentSource] = useState<string | null>(null);
|
||||
const isPlayingRef = useRef(false);
|
||||
const wasBlockedByRecorderRef = useRef(false);
|
||||
const isPlayNextInProgressRef = useRef(false);
|
||||
|
||||
const player = useAudioPlayer(currentSource);
|
||||
const playerStatus = useAudioPlayerStatus(player);
|
||||
@@ -41,24 +42,32 @@ export function usePlayer(): UsePlayerResult {
|
||||
}, [currentSource, player]);
|
||||
|
||||
const playNext = useCallback(async () => {
|
||||
if (queueRef.current.length === 0) {
|
||||
setCurrentSource(null);
|
||||
setStatus('idle');
|
||||
setQueueLength(0);
|
||||
await audioFocus.release();
|
||||
return;
|
||||
}
|
||||
if (isPlayNextInProgressRef.current) return;
|
||||
isPlayNextInProgressRef.current = true;
|
||||
try {
|
||||
if (queueRef.current.length === 0) {
|
||||
setCurrentSource(null);
|
||||
setStatus('idle');
|
||||
setQueueLength(0);
|
||||
await audioFocus.release();
|
||||
return;
|
||||
}
|
||||
|
||||
const acquired = await audioFocus.acquireForPlayback();
|
||||
if (!acquired) {
|
||||
setStatus('idle');
|
||||
return;
|
||||
}
|
||||
const acquired = await audioFocus.acquireForPlayback();
|
||||
if (!acquired) {
|
||||
setStatus('idle');
|
||||
return;
|
||||
}
|
||||
|
||||
const next = queueRef.current.shift()!;
|
||||
setQueueLength(queueRef.current.length);
|
||||
setStatus('playing');
|
||||
setCurrentSource(next.uri);
|
||||
if (queueRef.current.length === 0) return;
|
||||
|
||||
const next = queueRef.current.shift()!;
|
||||
setQueueLength(queueRef.current.length);
|
||||
setStatus('playing');
|
||||
setCurrentSource(next.uri);
|
||||
} finally {
|
||||
isPlayNextInProgressRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Detect playback completion → advance queue
|
||||
|
||||
Reference in New Issue
Block a user