Improve recordings list sorting and live show-page status updates.
Add sortable columns with a fixed status width, and keep the show page in sync via Echo, polling, and Alpine hydrate while transcription runs.
This commit is contained in:
@@ -66,12 +66,16 @@ function subscribeToRecording(recordingId, handler) {
|
||||
channel.listen('.RecordingTranscriptionUpdated', handler);
|
||||
|
||||
return () => {
|
||||
window.Echo.leave(channelName);
|
||||
// Prefer stopListening over leave() so a remount does not drop other subscribers.
|
||||
if (typeof channel.stopListening === 'function') {
|
||||
channel.stopListening('.RecordingTranscriptionUpdated');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show-page Alpine component: Echo push + local elapsed tick + optional status hydrate.
|
||||
* Livewire also listens on the user recordings channel and polls while active.
|
||||
*/
|
||||
export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
return {
|
||||
@@ -82,6 +86,7 @@ export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
},
|
||||
pollError: null,
|
||||
tickTimer: null,
|
||||
hydrateTimer: null,
|
||||
leaveChannel: null,
|
||||
|
||||
get badgeColor() {
|
||||
@@ -108,11 +113,13 @@ export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
if (this.status.is_active) {
|
||||
this.beginTick();
|
||||
this.hydrateOnce();
|
||||
this.beginHydratePoll();
|
||||
}
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.stopTick();
|
||||
this.stopHydratePoll();
|
||||
|
||||
if (this.leaveChannel) {
|
||||
this.leaveChannel();
|
||||
@@ -131,12 +138,14 @@ export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
|
||||
if (this.status.is_active) {
|
||||
this.beginTick();
|
||||
this.beginHydratePoll();
|
||||
} else {
|
||||
this.stopTick();
|
||||
this.stopHydratePoll();
|
||||
}
|
||||
|
||||
if (wasActive && !this.status.is_active
|
||||
&& !this.status.has_transcript
|
||||
if (wasActive && ! this.status.is_active
|
||||
&& ! this.status.has_transcript
|
||||
&& this.status.status !== 'failed'
|
||||
&& this.status.status !== 'cancelled') {
|
||||
window.location.reload();
|
||||
@@ -158,26 +167,47 @@ export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
}
|
||||
},
|
||||
|
||||
tickElapsed() {
|
||||
if (!this.status.is_active || this.status.elapsed_seconds == null) {
|
||||
beginHydratePoll() {
|
||||
if (this.hydrateTimer || ! this.statusUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.status.elapsed_seconds += 1;
|
||||
this.status.elapsed_human = formatElapsed(this.status.elapsed_seconds);
|
||||
this.hydrateTimer = setInterval(() => this.hydrateOnce(), 2000);
|
||||
},
|
||||
|
||||
stopHydratePoll() {
|
||||
if (this.hydrateTimer) {
|
||||
clearInterval(this.hydrateTimer);
|
||||
this.hydrateTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
tickElapsed() {
|
||||
if (! this.status.is_active || this.status.elapsed_seconds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const next = this.status.elapsed_seconds + 1;
|
||||
|
||||
this.status = {
|
||||
...this.status,
|
||||
elapsed_seconds: next,
|
||||
elapsed_human: formatElapsed(next),
|
||||
};
|
||||
},
|
||||
|
||||
async hydrateOnce() {
|
||||
if (!this.statusUrl) {
|
||||
if (! this.statusUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(this.statusUrl, {
|
||||
headers: { Accept: 'application/json' },
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (! response.ok) {
|
||||
throw new Error('Status request failed (' + response.status + ')');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user