Skip to content

Commit ff4fcd9

Browse files
myieyehahn-kev
andauthored
Make audio-input respect readonly (#1939)
* Make audio-input respect readonly * add a save as button to audio input * update locales * Remove unused import --------- Co-authored-by: Kevin Hahn <kevin_hahn@sil.org>
1 parent c20ed56 commit ff4fcd9

9 files changed

Lines changed: 86 additions & 16 deletions

File tree

frontend/viewer/src/lib/components/audio/audio-editor.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
if (!finalAudio) throw new Error('No audio to download');
6969
const url = URL.createObjectURL(finalAudio);
7070
try {
71+
//todo only works on desktop, not mobile
7172
const a = document.createElement('a');
7273
a.href = url;
7374
a.download = `${finalAudio.name}`;

frontend/viewer/src/lib/components/field-editors/audio-input.svelte

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@
5858
loader = defaultLoader,
5959
audioId = $bindable(),
6060
onchange = () => {},
61+
readonly = false,
6162
}: {
62-
loader?: (audioId: string) => Promise<ReadableStream | undefined | typeof handled>,
63+
loader?: (audioId: string) => Promise<{stream: ReadableStream, filename: string} | undefined | typeof handled>,
6364
audioId: string | undefined,
6465
onchange?: (audioId: string | undefined) => void;
66+
readonly?: boolean;
6567
} = $props();
6668
6769
const projectContext = useProjectContext();
@@ -87,23 +89,24 @@
8789
8890
return handled;
8991
}
90-
return await file.stream.stream();
92+
return {stream: await file.stream.stream(), filename: file.fileName ?? ''};
9193
}
9294
9395
async function load() {
9496
if (!audio || loadedAudioId === audioId || !audioId) return !!audioId;
9597
playerState = 'loading';
9698
try {
97-
const stream = await loader(audioId);
98-
if (stream === handled) return false;
99-
if (!stream) {
99+
const result = await loader(audioId);
100+
if (result === handled) return false;
101+
if (!result) {
100102
AppNotification.error(`Failed to load audio ${audioId}`);
101103
return;
102104
}
103-
let blob = await new Response(stream).blob();
105+
let blob = await new Response(result.stream).blob();
104106
if (audio.src) URL.revokeObjectURL(audio.src);
105107
loadedAudioId = undefined;
106108
audio.src = URL.createObjectURL(blob);
109+
filename = result.filename;
107110
loadedAudioId = audioId;
108111
return true;
109112
} finally {
@@ -182,6 +185,7 @@
182185
183186
184187
let loadedAudioId = $state<string>();
188+
let filename = $state('');
185189
let audio = $state<HTMLAudioElement>();
186190
let audioRuned = $derived(audio ? new AudioRuned(audio) : null);
187191
useEventListener(() => audio, 'ended', () => playerState = 'paused');
@@ -233,6 +237,16 @@
233237
}
234238
}
235239
240+
async function onSaveAs() {
241+
if (!audio) return;
242+
await load();
243+
//todo sadly this only works on desktop, not mobile, but it's the same with save as with the audio editor.
244+
const a = document.createElement('a');
245+
a.href = audio.src;
246+
a.download = filename;
247+
a.click();
248+
}
249+
236250
function onAudioError(event: Event) {
237251
if (audioHasKnownFlacSeekError()) {
238252
console.log('Ignoring known FLAC seek error. Will try to recover on next play.');
@@ -253,9 +267,15 @@
253267
</script>
254268
{#if supportsAudio}
255269
{#if !audioId}
256-
<Button variant="secondary" icon="i-mdi-microphone-plus" size="sm" iconProps={{class: 'size-5'}} onclick={onGetAudioClick}>
257-
{$t`Add audio`}
258-
</Button>
270+
{#if !readonly}
271+
<Button variant="secondary" icon="i-mdi-microphone-plus" size="sm" iconProps={{class: 'size-5'}} onclick={onGetAudioClick}>
272+
{$t`Add audio`}
273+
</Button>
274+
{:else}
275+
<div class="text-muted-foreground p-1">
276+
{$t`No audio`}
277+
</div>
278+
{/if}
259279
{:else if isNotFoundAudioId(audioId)}
260280
<div class="text-muted-foreground p-1">
261281
{$t`Audio file not included in Send & Receive`}
@@ -301,11 +321,16 @@
301321
{/snippet}
302322
</ResponsiveMenu.Trigger>
303323
<ResponsiveMenu.Content>
304-
<ResponsiveMenu.Item icon="i-mdi-microphone-plus" onSelect={onGetAudioClick}>
305-
{$t`Replace audio`}
306-
</ResponsiveMenu.Item>
307-
<ResponsiveMenu.Item icon="i-mdi-delete" onSelect={onRemoveAudio}>
308-
{$t`Remove audio`}
324+
{#if !readonly}
325+
<ResponsiveMenu.Item icon="i-mdi-microphone-plus" onSelect={onGetAudioClick}>
326+
{$t`Replace audio`}
327+
</ResponsiveMenu.Item>
328+
<ResponsiveMenu.Item icon="i-mdi-delete" onSelect={onRemoveAudio}>
329+
{$t`Remove audio`}
330+
</ResponsiveMenu.Item>
331+
{/if}
332+
<ResponsiveMenu.Item icon="i-mdi-download" onSelect={onSaveAs}>
333+
{$t`Save As`}
309334
</ResponsiveMenu.Item>
310335
</ResponsiveMenu.Content>
311336
</ResponsiveMenu.Root>

frontend/viewer/src/lib/components/field-editors/multi-ws-input.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
autocapitalize="off"
5151
onchange={() => onchange?.(ws.wsId, value[ws.wsId], value)} />
5252
{:else}
53-
<AudioInput bind:audioId={value[ws.wsId]} onchange={() => onchange?.(ws.wsId, value[ws.wsId], value)}/>
53+
<AudioInput
54+
bind:audioId={value[ws.wsId]} onchange={() => onchange?.(ws.wsId, value[ws.wsId], value)}
55+
{readonly} />
5456
{/if}
5557
</div>
5658
{/each}

frontend/viewer/src/lib/components/field-editors/rich-multi-ws-input.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@
7575
aria-label={ws.abbreviation}
7676
/>
7777
{:else}
78-
<AudioInput bind:audioId={() => getAudioId(value[ws.wsId]), audioId => setAudioId(audioId, ws.wsId)}/>
78+
<AudioInput
79+
bind:audioId={() => getAudioId(value[ws.wsId]), audioId => setAudioId(audioId, ws.wsId)}
80+
{readonly} />
7981
{/if}
8082
</div>
8183
{/each}

frontend/viewer/src/locales/en.po

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,10 @@ msgstr "New Word"
658658
msgid "No activity found"
659659
msgstr "No activity found"
660660

661+
#: src/lib/components/field-editors/audio-input.svelte
662+
msgid "No audio"
663+
msgstr "No audio"
664+
661665
#: src/lib/history/HistoryView.svelte
662666
msgid "No change name"
663667
msgstr "No change name"
@@ -811,6 +815,10 @@ msgstr "Reopen"
811815
msgid "Replace audio"
812816
msgstr "Replace audio"
813817

818+
#: src/lib/components/field-editors/audio-input.svelte
819+
msgid "Save As"
820+
msgstr "Save As"
821+
814822
#: src/lib/components/audio/AudioDialog.svelte
815823
msgid "Save audio"
816824
msgstr "Save audio"

frontend/viewer/src/locales/es.po

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ msgstr "Nueva palabra"
663663
msgid "No activity found"
664664
msgstr "No se ha encontrado actividad"
665665

666+
#: src/lib/components/field-editors/audio-input.svelte
667+
msgid "No audio"
668+
msgstr ""
669+
666670
#: src/lib/history/HistoryView.svelte
667671
msgid "No change name"
668672
msgstr "Sin cambio de nombre"
@@ -816,6 +820,10 @@ msgstr "Vuelva a abrir"
816820
msgid "Replace audio"
817821
msgstr "Sustituir audio"
818822

823+
#: src/lib/components/field-editors/audio-input.svelte
824+
msgid "Save As"
825+
msgstr ""
826+
819827
#: src/lib/components/audio/AudioDialog.svelte
820828
msgid "Save audio"
821829
msgstr "Guardar audio"

frontend/viewer/src/locales/fr.po

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ msgstr "Nouveau mot"
663663
msgid "No activity found"
664664
msgstr "Aucune activité trouvée"
665665

666+
#: src/lib/components/field-editors/audio-input.svelte
667+
msgid "No audio"
668+
msgstr ""
669+
666670
#: src/lib/history/HistoryView.svelte
667671
msgid "No change name"
668672
msgstr "Pas de changement de nom"
@@ -816,6 +820,10 @@ msgstr "Réouverture"
816820
msgid "Replace audio"
817821
msgstr "Remplacer l'audio"
818822

823+
#: src/lib/components/field-editors/audio-input.svelte
824+
msgid "Save As"
825+
msgstr ""
826+
819827
#: src/lib/components/audio/AudioDialog.svelte
820828
msgid "Save audio"
821829
msgstr "Sauvegarder l'audio"

frontend/viewer/src/locales/id.po

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ msgstr "Kata Baru"
663663
msgid "No activity found"
664664
msgstr "Tidak ada aktivitas yang ditemukan"
665665

666+
#: src/lib/components/field-editors/audio-input.svelte
667+
msgid "No audio"
668+
msgstr ""
669+
666670
#: src/lib/history/HistoryView.svelte
667671
msgid "No change name"
668672
msgstr "Tidak ada perubahan nama"
@@ -816,6 +820,10 @@ msgstr "Buka kembali"
816820
msgid "Replace audio"
817821
msgstr "Mengganti audio"
818822

823+
#: src/lib/components/field-editors/audio-input.svelte
824+
msgid "Save As"
825+
msgstr ""
826+
819827
#: src/lib/components/audio/AudioDialog.svelte
820828
msgid "Save audio"
821829
msgstr "Menyimpan audio"

frontend/viewer/src/locales/ko.po

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ msgstr "새 단어"
663663
msgid "No activity found"
664664
msgstr "활동을 찾을 수 없습니다."
665665

666+
#: src/lib/components/field-editors/audio-input.svelte
667+
msgid "No audio"
668+
msgstr ""
669+
666670
#: src/lib/history/HistoryView.svelte
667671
msgid "No change name"
668672
msgstr "이름 변경 안 함"
@@ -816,6 +820,10 @@ msgstr "다시 열기"
816820
msgid "Replace audio"
817821
msgstr "오디오 교체"
818822

823+
#: src/lib/components/field-editors/audio-input.svelte
824+
msgid "Save As"
825+
msgstr ""
826+
819827
#: src/lib/components/audio/AudioDialog.svelte
820828
msgid "Save audio"
821829
msgstr "오디오 저장"

0 commit comments

Comments
 (0)