Skip to content

Commit 9b05178

Browse files
committed
refactor(transcription): Improve UI reactivity and remove notifications
- Remove all toast notifications and centralize everything in the player UI - Make UI immediately respond to transcription start and show progress - Make transcription button disappear and show progress UI immediately - Fix local state handling in EpisodePlayer for immediate UI updates - Add smoother transitions between transcription states - Show errors and success messages directly in the player UI
1 parent 82e503c commit 9b05178

File tree

2 files changed

+82
-69
lines changed

2 files changed

+82
-69
lines changed

src/services/TranscriptionService.ts

Lines changed: 56 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,14 @@ export class TranscriptionService {
116116
*/
117117
cancelTranscription(): void {
118118
if (!this.isTranscribing) {
119-
new Notice("No transcription is currently in progress.");
120119
return;
121120
}
122121

123122
this.cancelRequested = true;
124123
this.processingStatus = "Cancelling...";
125124

126-
if (this.activeNotice) {
127-
this.activeNotice.update("Cancelling transcription. Please wait...");
128-
}
129-
130125
// The cancellation will be handled in the transcription process
131-
new Notice("Transcription cancellation requested. This may take a moment...");
126+
// No notifications, everything will be shown in the UI
132127
}
133128

134129
/**
@@ -223,7 +218,10 @@ export class TranscriptionService {
223218
// Get current episode first
224219
const currentEpisode = this.plugin.api.podcast;
225220
if (!currentEpisode) {
226-
new Notice("No episode is currently playing.");
221+
this.processingStatus = "Error: No episode is playing";
222+
setTimeout(() => {
223+
this.isTranscribing = false;
224+
}, 2000);
227225
return;
228226
}
229227

@@ -242,7 +240,10 @@ export class TranscriptionService {
242240
// Validate API key
243241
if (!this.initializeClient()) {
244242
// Reset state if validation fails
245-
this.isTranscribing = false;
243+
this.processingStatus = "Error: Invalid API key";
244+
setTimeout(() => {
245+
this.isTranscribing = false;
246+
}, 2000);
246247
return;
247248
}
248249

@@ -254,24 +255,24 @@ export class TranscriptionService {
254255
);
255256
const existingFile = this.plugin.app.vault.getAbstractFileByPath(transcriptPath);
256257
if (existingFile instanceof TFile) {
257-
new Notice(`You've already transcribed this episode - found ${transcriptPath}.`);
258-
this.isTranscribing = false;
258+
this.processingStatus = `Already transcribed: ${transcriptPath}`;
259+
setTimeout(() => {
260+
this.isTranscribing = false;
261+
}, 2000);
259262
return;
260263
}
261264
}
262265

263266
// Reset cancellation flag
264267
this.cancelRequested = false;
265-
266-
// Create a notice for traditional display (we'll also show in the UI)
267-
const notice = TimerNotice("Transcription", "Preparing to transcribe...");
268-
this.activeNotice = notice;
269268

270269
try {
271270
// Use setTimeout to allow UI to update before heavy processing starts
272271
await new Promise(resolve => setTimeout(resolve, 50));
273272

274-
notice.update("Downloading episode...");
273+
// Update UI status
274+
this.processingStatus = "Downloading episode...";
275+
275276
const downloadPath = await downloadEpisode(
276277
currentEpisode,
277278
this.plugin.settings.download.path,
@@ -290,7 +291,9 @@ export class TranscriptionService {
290291
const formattedSize = this.formatFileSize(fileSize);
291292
const estimatedTime = this.estimateTranscriptionTime(fileSize);
292293

293-
notice.update(`Preparing audio (${formattedSize}) for transcription...\nEstimated time: ${estimatedTime}`);
294+
// Update UI status
295+
this.processingStatus = `Preparing audio (${formattedSize})...`;
296+
this.timeRemaining = `Estimated time: ${estimatedTime}`;
294297
this.progressSize = formattedSize;
295298

296299
// Read the audio file in chunks to reduce memory pressure
@@ -310,70 +313,53 @@ export class TranscriptionService {
310313
// @ts-ignore - using a workaround to help release memory
311314
const tempFileBuffer = null;
312315

313-
notice.update(`Starting transcription of ${formattedSize} audio...\nEstimated time: ${estimatedTime}\n\nYou can cancel using the "Cancel Transcription" button below the player or via command palette.`);
316+
// Update UI status
317+
this.processingStatus = `Starting transcription (${formattedSize})...`;
314318

315-
// Create commands for consistent control
319+
// Create command for cancellation via command palette
316320
this.plugin.addCommand({
317321
id: 'cancel-transcription',
318322
name: 'Cancel Current Transcription',
319323
callback: () => this.cancelTranscription()
320324
});
321325

322-
// Create a notice with a cancel button for better visibility
323-
const cancelNotice = new Notice(
324-
"Transcription in progress. Click to cancel.",
325-
0, // Duration 0 = until manually closed
326-
[{
327-
text: "Cancel",
328-
onClick: () => {
329-
this.cancelTranscription();
330-
cancelNotice.hide();
331-
}
332-
}]
333-
);
326+
// Create a no-op update function since we're not using notices anymore
327+
const updateStatus = (message: string) => {
328+
// We're not doing anything with this message since we update UI directly
329+
};
334330

335331
// Process transcription with concurrent chunks
336332
const transcription = await this.transcribeChunks(
337333
files,
338-
notice.update,
334+
updateStatus,
339335
currentEpisode.id,
340336
fileSize,
341337
resume ? this.resumeData : null
342338
);
343339

344340
// If cancelled, stop here
345341
if (this.cancelRequested) {
346-
notice.update("Transcription cancelled by user.");
347-
notice.stop();
342+
this.processingStatus = "Transcription cancelled";
343+
344+
// Keep the UI visible for a moment before removing
345+
setTimeout(() => {
346+
this.isTranscribing = false;
347+
}, 2000);
348348
return;
349349
}
350350

351351
// Schedule processing in the next event loop iteration to avoid UI blocking
352352
await new Promise(resolve => setTimeout(resolve, 50));
353353

354-
notice.update("Processing timestamps...");
354+
// Update UI status
355355
this.processingStatus = "Formatting timestamps...";
356356
const formattedTranscription = await this.formatTranscriptionWithTimestamps(transcription);
357357

358-
notice.update("Saving transcription...");
358+
// Update UI status
359359
this.processingStatus = "Saving...";
360-
await this.saveTranscription(currentEpisode, formattedTranscription);
361-
362-
// Clean up notices
363-
try {
364-
this.plugin.app.workspace.iterateAllLeaves(leaf => {
365-
const notices = document.querySelectorAll('.notice');
366-
notices.forEach(noticeEl => {
367-
if (noticeEl.textContent.includes('Transcription in progress')) {
368-
noticeEl.querySelector('button')?.click();
369-
}
370-
});
371-
});
372-
} catch (e) {
373-
console.log("Error cleaning up notices:", e);
374-
}
360+
const transcriptPath = await this.saveTranscription(currentEpisode, formattedTranscription);
375361

376-
// Remove the cancel command
362+
// Remove command for cancellation
377363
try {
378364
this.plugin.app.commands.removeCommand(`${this.plugin.manifest.id}:cancel-transcription`);
379365
} catch (e) {
@@ -383,29 +369,32 @@ export class TranscriptionService {
383369
// Clear resume data since we've completed successfully
384370
this.clearResumeState();
385371

386-
notice.stop();
387-
notice.update("Transcription completed and saved successfully.");
372+
// Show completion status in the UI
373+
this.processingStatus = `Saved: ${transcriptPath}`;
374+
this.progressPercent = 100;
375+
376+
// Keep the UI visible for a moment before removing
377+
setTimeout(() => {
378+
this.isTranscribing = false;
379+
}, 2000);
388380

389-
// Show a success notice with a link to the file
390-
new Notice(
391-
`Transcription complete!\nFile saved to: ${transcriptPath}`,
392-
10000 // Show for 10 seconds
393-
);
394381
} catch (error) {
395382
console.error("Transcription error:", error);
396-
notice.update(`Transcription failed: ${error.message}`);
397-
new Notice(`Transcription failed: ${error.message}`, 5000);
398-
} finally {
399-
this.isTranscribing = false;
400-
this.activeNotice = null;
401383

384+
// Show error in UI
385+
this.processingStatus = `Error: ${error.message}`;
386+
387+
// Keep the error visible for a moment before removing
388+
setTimeout(() => {
389+
this.isTranscribing = false;
390+
}, 3000);
391+
} finally {
392+
// Remove the command in case it wasn't removed earlier
402393
try {
403394
this.plugin.app.commands.removeCommand(`${this.plugin.manifest.id}:cancel-transcription`);
404395
} catch (e) {
405396
// Command may have already been removed, ignore
406397
}
407-
408-
setTimeout(() => notice.hide(), 5000);
409398
}
410399
}
411400

@@ -917,7 +906,7 @@ export class TranscriptionService {
917906
private async saveTranscription(
918907
episode: Episode,
919908
transcription: string,
920-
): Promise<void> {
909+
): Promise<string> {
921910
const transcriptPath = FilePathTemplateEngine(
922911
this.plugin.settings.transcript.path,
923912
episode,
@@ -945,6 +934,7 @@ export class TranscriptionService {
945934
if (!file) {
946935
const newFile = await vault.create(transcriptPath, transcriptContent);
947936
await this.plugin.app.workspace.getLeaf().openFile(newFile);
937+
return transcriptPath;
948938
} else {
949939
throw new Error("Expected a file but got a folder");
950940
}

src/ui/PodcastView/EpisodePlayer.svelte

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,13 @@
178178
}
179179
180180
function transcribeEpisode() {
181-
$plugin.api.transcribeCurrentEpisode();
181+
// Set local state immediately for UI update
182+
_isTranscribing = true;
183+
184+
// Then call the API
185+
setTimeout(() => {
186+
$plugin.api.transcribeCurrentEpisode();
187+
}, 0);
182188
}
183189
184190
function cancelTranscription() {
@@ -188,11 +194,28 @@
188194
}
189195
190196
function resumeTranscription() {
191-
$plugin.api.resumeTranscription();
197+
// Set local state immediately for UI update
198+
_isTranscribing = true;
199+
200+
// Then call the API
201+
setTimeout(() => {
202+
$plugin.api.resumeTranscription();
203+
}, 0);
192204
}
193205
206+
// Create a reactive variable to track transcription status
207+
let _isTranscribing = false;
208+
194209
// Track if transcription is currently in progress
195-
$: isTranscribing = $plugin.transcriptionService && $plugin.transcriptionService.isTranscribing;
210+
$: {
211+
// Force immediate UI update when transcription service changes
212+
if ($plugin.transcriptionService) {
213+
_isTranscribing = $plugin.transcriptionService.isTranscribing;
214+
}
215+
}
216+
217+
// Make isTranscribing reactive, but set it locally first for immediate UI updates
218+
$: isTranscribing = _isTranscribing;
196219
197220
// Check if there's a resumable transcription for the current episode
198221
$: hasResumableTranscription = $currentEpisode &&

0 commit comments

Comments
 (0)