Skip to content

Commit d7109d4

Browse files
committed
feat: 修改同步方式
1 parent 3ef25b4 commit d7109d4

1 file changed

Lines changed: 59 additions & 87 deletions

File tree

apps/mobile/src/screens/TTSScreen.tsx

Lines changed: 59 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -94,23 +94,13 @@ export function TTSScreen() {
9494
const theme = getTheme(actualTheme === "dark");
9595
const ttsStore = useTTSStore();
9696

97-
// ── Sync state from store when expanding from mini player ──────────
98-
useEffect(() => {
99-
if (ttsStore.currentBookId === book.id && ttsStore.paragraphs.length > 0) {
100-
setParagraphs(ttsStore.paragraphs);
101-
setChapterTitle(ttsStore.chapterTitle);
102-
setChapterIndex(ttsStore.chapterIndex);
103-
setCurrentParagraph(ttsStore.currentParagraph);
104-
if (ttsStore.selectedProvider) setProvider(ttsStore.selectedProvider);
105-
if (ttsStore.selectedVoice?.id) setVoiceId(ttsStore.selectedVoice.id);
106-
setLoading(false);
107-
}
108-
}, []);
97+
// Derived state from store (single source of truth)
98+
const paragraphs = ttsStore.paragraphs;
99+
const chapterTitle = ttsStore.chapterTitle;
100+
const chapterIndex = ttsStore.chapterIndex;
101+
const currentParagraph = ttsStore.currentParagraph;
109102

110-
// ── Book + chapter state ─────────────────────────────────────────────
111-
const [paragraphs, setParagraphs] = useState<Paragraph[]>([]);
112-
const [chapterTitle, setChapterTitle] = useState("");
113-
const [chapterIndex, setChapterIndex] = useState(0);
103+
// ── Local UI state ──────────────────────────────────────────────────
114104
const [chapters, setChapters] = useState<{ title: string; index: number }[]>(
115105
[],
116106
);
@@ -146,7 +136,6 @@ export function TTSScreen() {
146136
const isPaused = ttsStore.state === 'paused';
147137
const isLoadingAudio = ttsStore.state === 'loading' || playbackState === State.Connecting || playbackState === State.Buffering;
148138

149-
const [currentParagraph, setCurrentParagraph] = useState(0);
150139
const [paragraphProgress, setParagraphProgress] = useState(0);
151140
const [resumeOffsetMs, setResumeOffsetMs] = useState(0);
152141

@@ -185,8 +174,7 @@ export function TTSScreen() {
185174
progressUpdateEventInterval: 1,
186175
});
187176
// Only reset if nothing is playing (avoid interrupting playback from mini player)
188-
const state = await TrackPlayer.getPlaybackState();
189-
if (state.state !== State.Playing && state.state !== State.Buffering) {
177+
if (ttsStore.state !== 'playing') {
190178
await TrackPlayer.reset();
191179
}
192180
} catch (e) {
@@ -345,47 +333,6 @@ export function TTSScreen() {
345333
return () => sub.remove();
346334
}, [currentParagraph, paragraphs.length, chapterIndex, voiceId, provider, ttsStore]);
347335

348-
// ── TrackPlayer event: active track changed → update current paragraph ─
349-
useEffect(() => {
350-
const sub = TrackPlayer.addEventListener(Event.PlaybackActiveTrackChanged, async (event) => {
351-
if (event.index === undefined) return;
352-
353-
// Get current track to determine which paragraph we're on
354-
const track = await TrackPlayer.getTrack(event.index);
355-
if (!track?.id) return;
356-
357-
// Extract paragraph index from track ID (format: "paraId-chunkIdx")
358-
const paraId = track.id.split('-')[0];
359-
const paraIdx = paragraphs.findIndex(p => p.id === paraId);
360-
361-
if (paraIdx >= 0 && paraIdx !== currentParagraph) {
362-
setCurrentParagraph(paraIdx);
363-
ttsStore.setCurrentParagraph(paraIdx);
364-
365-
// Add next paragraph to queue for seamless playback
366-
try {
367-
const nextIdx = paraIdx + 1;
368-
if (nextIdx < paragraphs.length) {
369-
const nextUris = await synthesizeParagraph(nextIdx);
370-
if (nextUris.length > 0) {
371-
const nextTracks = nextUris.map((uri, i) => ({
372-
id: `${paragraphs[nextIdx].id}-${i}`,
373-
url: uri,
374-
title: `${book.title || '未知书籍'} - ${chapterTitle}`,
375-
artist: book.author || "未知作者",
376-
artwork: undefined,
377-
duration: 0,
378-
}));
379-
await TrackPlayer.add(nextTracks);
380-
}
381-
}
382-
} catch {
383-
// Ignore prefetch errors
384-
}
385-
}
386-
});
387-
return () => sub.remove();
388-
}, [ttsStore, paragraphs, currentParagraph, book, chapterTitle, synthesizeParagraph]);
389336

390337
// Auto-scroll to current paragraph in content view
391338
useEffect(() => {
@@ -405,7 +352,7 @@ export function TTSScreen() {
405352

406353
const loadChapter = async (ci: number, vid: string, prov: string) => {
407354
console.log('[TTSScreen] loadChapter called, ci:', ci, 'vid:', vid, 'prov:', prov);
408-
setChapterIndex(ci);
355+
ttsStore.setChapterIndex(ci);
409356
try {
410357
const apiClient = getApiClient();
411358
console.log('[TTSScreen] Fetching paragraphs for book:', book.id, 'chapter:', ci);
@@ -430,16 +377,14 @@ export function TTSScreen() {
430377
}
431378
}
432379

433-
setChapterTitle(pRes.data.title);
434-
setParagraphs(pRes.data.paragraphs);
435-
setCurrentParagraph(0);
436380
setParagraphProgress(0);
437381
setResumeOffsetMs(0);
438382
prefetchedRef.current.clear();
439383
ttsStore.setParagraphs(pRes.data.paragraphs);
440384
ttsStore.setTotalParagraphs(pRes.data.paragraphs.length);
441385
ttsStore.setChapterTitle(pRes.data.title);
442386
ttsStore.setChapterIndex(ci);
387+
ttsStore.setCurrentParagraph(0);
443388

444389
// Resume from saved cloud progress
445390
try {
@@ -452,7 +397,7 @@ export function TTSScreen() {
452397
}
453398
if (rec.voice) setVoiceId(rec.voice);
454399
setResumeOffsetMs(Math.max(0, rec.audioOffsetMs || 0));
455-
setCurrentParagraph(
400+
ttsStore.setCurrentParagraph(
456401
Math.min(rec.paragraphIndex, pRes.data.paragraphs.length - 1),
457402
);
458403
}
@@ -506,27 +451,21 @@ export function TTSScreen() {
506451
// ── Persist latest position when user leaves the screen ─────────────
507452
useEffect(() => {
508453
const unsubscribe = navigation.addListener("beforeRemove", async () => {
509-
await persistProgress(currentParagraph, Math.round(progress.position * 1000));
510-
// Sync state to store
511-
ttsStore.setParagraphs(paragraphs);
512-
ttsStore.setTotalParagraphs(paragraphs.length);
513-
ttsStore.setChapterTitle(chapterTitle);
514-
ttsStore.setChapterIndex(chapterIndex);
515-
ttsStore.setCurrentParagraph(currentParagraph);
454+
await persistProgress(ttsStore.currentParagraph, Math.round(progress.position * 1000));
516455
ttsStore.setMiniPlayerVisible(true);
517456
});
518457
return unsubscribe;
519-
}, [currentParagraph, navigation, persistProgress, progress.position, ttsStore, paragraphs, chapterTitle, chapterIndex]);
458+
}, [navigation, persistProgress, progress.position, ttsStore]);
520459

521460
// ── Persist progress when app goes to background ────────────────────
522461
useEffect(() => {
523462
const subscription = AppState.addEventListener("change", (nextAppState) => {
524463
if (nextAppState === "background" || nextAppState === "inactive") {
525-
void persistProgress(currentParagraph, Math.round(progress.position * 1000));
464+
void persistProgress(ttsStore.currentParagraph, Math.round(progress.position * 1000));
526465
}
527466
});
528467
return () => subscription.remove();
529-
}, [currentParagraph, persistProgress, progress.position]);
468+
}, [persistProgress, progress.position, ttsStore]);
530469

531470
const synthesizeParagraph = useCallback(
532471
async (idx: number): Promise<string[]> => {
@@ -559,17 +498,57 @@ export function TTSScreen() {
559498
[book.id, paragraphs, provider, voiceId],
560499
);
561500

501+
// ── TrackPlayer event: active track changed → update current paragraph ─
502+
useEffect(() => {
503+
const sub = TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (event: any) => {
504+
if (event.nextTrack === undefined) return;
505+
506+
// Get current track to determine which paragraph we're on
507+
const track = await TrackPlayer.getTrack(event.nextTrack);
508+
if (!track?.id) return;
509+
510+
// Extract paragraph index from track ID (format: "paraId-chunkIdx")
511+
const paraId = track.id.split('-')[0];
512+
const paraIdx = paragraphs.findIndex(p => p.id === paraId);
513+
514+
if (paraIdx >= 0 && paraIdx !== ttsStore.currentParagraph) {
515+
ttsStore.setCurrentParagraph(paraIdx);
516+
517+
// Add next paragraph to queue for seamless playback
518+
try {
519+
const nextIdx = paraIdx + 1;
520+
if (nextIdx < paragraphs.length) {
521+
const nextUris = await synthesizeParagraph(nextIdx);
522+
if (nextUris.length > 0) {
523+
const nextTracks = nextUris.map((uri, i) => ({
524+
id: `${paragraphs[nextIdx].id}-${i}`,
525+
url: uri,
526+
title: `${book.title || '未知书籍'} - ${chapterTitle}`,
527+
artist: book.author || "未知作者",
528+
artwork: undefined,
529+
duration: 0,
530+
}));
531+
await TrackPlayer.add(nextTracks);
532+
}
533+
}
534+
} catch {
535+
// Ignore prefetch errors
536+
}
537+
}
538+
});
539+
return () => sub.remove();
540+
}, [ttsStore, paragraphs, book, chapterTitle, synthesizeParagraph]);
541+
562542
const playParagraph = useCallback(
563543
async (idx: number) => {
564544
if (idx >= paragraphs.length) {
565-
setCurrentParagraph(0);
545+
ttsStore.setCurrentParagraph(0);
566546
setParagraphProgress(0);
567547
ttsStore.setState("idle");
568548
return;
569549
}
570-
setCurrentParagraph(idx);
571-
setParagraphProgress(0);
572550
ttsStore.setCurrentParagraph(idx);
551+
setParagraphProgress(0);
573552
const startOffsetMs = resumeOffsetMs;
574553
setResumeOffsetMs(0);
575554

@@ -658,7 +637,7 @@ export function TTSScreen() {
658637
const handleStop = useCallback(async () => {
659638
await TrackPlayer.reset();
660639
ttsStore.setState("idle");
661-
setCurrentParagraph(0);
640+
ttsStore.setCurrentParagraph(0);
662641
setParagraphProgress(0);
663642
}, [ttsStore]);
664643

@@ -668,7 +647,6 @@ export function TTSScreen() {
668647
if (isPlaying || isPaused) {
669648
await playParagraph(idx);
670649
} else {
671-
setCurrentParagraph(idx);
672650
ttsStore.setCurrentParagraph(idx);
673651
void persistProgress(idx);
674652
}
@@ -740,15 +718,9 @@ export function TTSScreen() {
740718
}, []);
741719

742720
const handleMinimize = useCallback(() => {
743-
// Sync current state to store before minimizing
744-
ttsStore.setParagraphs(paragraphs);
745-
ttsStore.setTotalParagraphs(paragraphs.length);
746-
ttsStore.setChapterTitle(chapterTitle);
747-
ttsStore.setChapterIndex(chapterIndex);
748-
ttsStore.setCurrentParagraph(currentParagraph);
749721
ttsStore.setMiniPlayerVisible(true);
750722
navigation.goBack();
751-
}, [ttsStore, navigation, paragraphs, chapterTitle, chapterIndex, currentParagraph]);
723+
}, [ttsStore, navigation]);
752724

753725
// ── Progress calculation ─────────────────────────────────────────────
754726
const overallProgress = paragraphs.length

0 commit comments

Comments
 (0)