Skip to content

Commit e427b38

Browse files
author
Aleksander Grygier
committed
refactor: Componentization
1 parent ec2103b commit e427b38

File tree

7 files changed

+112
-135
lines changed

7 files changed

+112
-135
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Transcription } from '$lib/types';
2+
import { onMount, type Snippet } from 'svelte';
3+
4+
export interface RecordingTileProps {
5+
data: string;
6+
deleteRecording?: () => void;
7+
id: string;
8+
name: string;
9+
savedRecordings?: any[];
10+
titleSlot?: Snippet;
11+
transcription?: Transcription;
12+
}

apps/web/src/lib/components/RecordingTile.svelte renamed to apps/web/src/lib/components/RecordingTile/RecordingTile.svelte

Lines changed: 39 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,62 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
33
import WaveSurfer from 'wavesurfer.js';
4-
import Button from './Button.svelte';
5-
import { browser } from '$app/environment';
4+
import Button from '../Button.svelte';
65
import transcribeRecording from '$lib/methods/transcribe-recording';
7-
import LoadingSpinner from './LoadingSpinner.svelte';
8-
import type { Transcription } from '$lib/types';
9-
10-
interface RecordingTileProps {
11-
blob: Blob;
12-
deleteRecording: () => void;
13-
id: string;
14-
name: string;
15-
savedRecordings?: any[];
16-
transcription?: Transcription;
17-
}
6+
import LoadingSpinner from '../LoadingSpinner.svelte';
7+
import type { RecordingTileProps } from './RecordingTile';
8+
import RecordingWave from '../RecordingWave/RecordingWave.svelte';
9+
import base64ToBlob from '$lib/utils/base64-to-blob';
1810
1911
let {
20-
blob,
12+
data,
2113
deleteRecording,
2214
id,
2315
name,
2416
savedRecordings = $bindable([]),
17+
titleSlot,
2518
transcription
2619
}: RecordingTileProps = $props();
27-
let isPlaying = $state(false);
28-
let isTranscribing = $state(false);
29-
30-
const recordingUrl = URL.createObjectURL(blob);
3120
32-
let waveformContainer: HTMLElement;
33-
let wavesurfer: WaveSurfer;
34-
let progressColor = $state(
35-
browser && window.matchMedia('(prefers-color-scheme: dark)').matches
36-
? '#ffffff'
37-
: browser && window.matchMedia('(prefers-color-scheme: light)').matches
38-
? '#000000'
39-
: ''
40-
);
21+
let blob: Blob = $state(base64ToBlob(data));
4122
42-
function createWaveSurfer() {
43-
if (wavesurfer) {
44-
wavesurfer.destroy(); // Clean up any previous instance
45-
}
46-
47-
wavesurfer = WaveSurfer.create({
48-
container: waveformContainer,
49-
waveColor: '#ccc',
50-
progressColor,
51-
cursorColor: progressColor,
52-
height: 100,
53-
barWidth: 2,
54-
barRadius: 3,
55-
backend: 'MediaElement' // Ensures browser-native audio handling
56-
});
23+
let isPlaying = $state(false);
5724
58-
wavesurfer.load(recordingUrl);
25+
let isTranscribing = $state(false);
5926
60-
wavesurfer.on('play', () => {
61-
isPlaying = true;
62-
});
27+
let recordingUrl = $state(URL.createObjectURL(blob));
6328
64-
wavesurfer.on('pause', () => {
65-
isPlaying = false;
66-
});
29+
let wavesurfer: WaveSurfer | undefined = $state(undefined);
6730
68-
wavesurfer.on('finish', () => {
69-
isPlaying = false;
31+
onMount(() => {
32+
if (wavesurfer) {
33+
wavesurfer.on('play', () => {
34+
isPlaying = true;
35+
});
7036
71-
wavesurfer.load(recordingUrl);
72-
});
73-
}
37+
wavesurfer.on('pause', () => {
38+
isPlaying = false;
39+
});
7440
75-
onMount(() => {
76-
createWaveSurfer();
41+
wavesurfer.on('finish', () => {
42+
isPlaying = false;
43+
});
44+
}
7745
});
7846
</script>
7947

8048
<div class="recording-tile" {id}>
81-
<h3>{name}</h3>
49+
{#if titleSlot}
50+
<h3>
51+
{@render titleSlot()}
52+
</h3>
53+
{:else if name}
54+
<h3>
55+
{name}
56+
</h3>
57+
{/if}
8258

83-
<div bind:this={waveformContainer}></div>
59+
<RecordingWave {data} bind:wavesurfer />
8460

8561
{#if transcription?.text && transcription.text !== `Empty ${id}`}
8662
<div class="transcription">
@@ -127,22 +103,23 @@
127103
<div class="actions">
128104
<Button
129105
kind="primary"
130-
onclick={() => wavesurfer.playPause()}
106+
onclick={() => wavesurfer?.playPause()}
131107
label={isPlaying ? 'Pause' : 'Play'}
132108
/>
133109

134110
<Button
135111
kind="primary"
136112
onclick={() => {
137-
wavesurfer.stop();
138-
createWaveSurfer();
113+
wavesurfer?.stop();
139114
}}
140115
label="Stop"
141116
/>
142117

143118
<Button kind="secondary" download={name} href={recordingUrl} label="Download"></Button>
144119

145-
<Button kind="danger" onclick={() => deleteRecording()} label="Delete" />
120+
{#if deleteRecording}
121+
<Button kind="danger" onclick={() => deleteRecording()} label="Delete" />
122+
{/if}
146123
</div>
147124
</div>
148125

apps/web/src/lib/components/RecordingWave.svelte

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type WaveSurfer from 'wavesurfer.js';
2+
3+
export interface RecordingWaveProps {
4+
data: string;
5+
wavesurfer?: WaveSurfer | undefined;
6+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte';
3+
import WaveSurfer from 'wavesurfer.js';
4+
import { browser } from '$app/environment';
5+
import type { RecordingWaveProps } from './RecordingWave.d.ts';
6+
import base64ToBlob from '$lib/utils/base64-to-blob.js';
7+
8+
let { data, wavesurfer = $bindable() }: RecordingWaveProps = $props();
9+
10+
let progressColor = $state(
11+
browser && window.matchMedia('(prefers-color-scheme: dark)').matches
12+
? '#ffffff'
13+
: browser && window.matchMedia('(prefers-color-scheme: light)').matches
14+
? '#000000'
15+
: ''
16+
);
17+
18+
let recordingUrl = $state(URL.createObjectURL(base64ToBlob(data)));
19+
20+
let waveformContainer: HTMLElement;
21+
22+
onMount(() => {
23+
if (wavesurfer) {
24+
wavesurfer.destroy();
25+
}
26+
27+
wavesurfer = WaveSurfer.create({
28+
container: waveformContainer,
29+
waveColor: '#ccc',
30+
progressColor,
31+
cursorColor: progressColor,
32+
height: 100,
33+
barWidth: 2,
34+
barRadius: 3,
35+
backend: 'MediaElement'
36+
});
37+
38+
wavesurfer.load(recordingUrl);
39+
40+
wavesurfer.on('finish', () => {
41+
wavesurfer?.load(recordingUrl);
42+
});
43+
});
44+
</script>
45+
46+
<div class="recording-wave" bind:this={waveformContainer}></div>

apps/web/src/routes/+page.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<script lang="ts">
22
import Recorder from '$lib/components/Recorder.svelte';
3-
import RecordingTile from '$lib/components/RecordingTile.svelte';
3+
import RecordingTile from '$lib/components/RecordingTile/RecordingTile.svelte';
44
import { onMount } from 'svelte';
5-
import base64ToBlob from '$lib/utils/base64-to-blob';
65
import type { Transcription } from '$lib/types';
76
import createRecording from '$lib/methods/create-recording';
87
import { openDB } from 'idb';
@@ -94,8 +93,14 @@
9493
<ul>
9594
{#each savedRecordings as recording ((recording.id, recording.transcription))}
9695
<li>
96+
{#snippet titleSlot()}
97+
<a href="/recording/{recording.id}" style="text-decoration: none; color: inherit;">
98+
{recording.name}
99+
</a>
100+
{/snippet}
101+
97102
<RecordingTile
98-
blob={base64ToBlob(recording.data)}
103+
data={recording.data}
99104
deleteRecording={async () => {
100105
if (confirm('Are you sure you want to delete this recording?')) {
101106
savedRecordings = savedRecordings.filter((r) => r.id !== recording.id);
@@ -106,6 +111,7 @@
106111
id={recording.id}
107112
name={recording.name}
108113
transcription={recording.transcription}
114+
{titleSlot}
109115
bind:savedRecordings
110116
/>
111117
</li>

apps/web/src/routes/[account=account]/+page.svelte

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script>
22
import { page } from '$app/stores';
3-
import RecordingTile from '$lib/components/RecordingTile.svelte';
43
</script>
54

65
<svelte:head>
@@ -9,18 +8,6 @@
98

109
<main>
1110
<h1>Hello, {$page.data?.session?.user?.user_metadata.name}</h1>
12-
13-
<section>
14-
<h2>Your recordings</h2>
15-
16-
<ul>
17-
{#each $page.data.recordings as { id, name, transcription, url }}
18-
<li>
19-
<RecordingTile {id} {name} {transcription} {url} recordings={$page.data.recordings} />
20-
</li>
21-
{/each}
22-
</ul>
23-
</section>
2411
</main>
2512

2613
<style>

0 commit comments

Comments
 (0)