Split chat component into smaller components
This commit is contained in:
@@ -120,6 +120,16 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
this.isMuted.set(false);
|
||||
this.volumePercent.set(storedVolume);
|
||||
this.lastNonZeroVolume.set(storedVolume);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const audio = this.audioRef?.nativeElement;
|
||||
|
||||
if (!audio)
|
||||
return;
|
||||
|
||||
audio.load();
|
||||
this.applyAudioVolume(storedVolume);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,6 +160,7 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
this.waveformExpanded.set(nextExpanded);
|
||||
|
||||
if (nextExpanded) {
|
||||
this.waveformUnavailable.set(false);
|
||||
requestAnimationFrame(() => {
|
||||
void this.ensureWaveformLoaded();
|
||||
});
|
||||
@@ -165,6 +176,12 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
this.applyAudioVolume(this.volumePercent());
|
||||
this.durationSeconds.set(Number.isFinite(audio.duration) ? audio.duration : 0);
|
||||
this.currentTimeSeconds.set(audio.currentTime || 0);
|
||||
|
||||
if (this.waveformExpanded() && !this.waveSurfer && !this.waveformLoading()) {
|
||||
requestAnimationFrame(() => {
|
||||
void this.ensureWaveformLoaded();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onTimeUpdate(): void {
|
||||
@@ -266,7 +283,7 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private async ensureWaveformLoaded(): Promise<void> {
|
||||
if (this.waveformLoading() || this.waveSurfer || this.waveformUnavailable())
|
||||
if (this.waveformLoading() || this.waveSurfer)
|
||||
return;
|
||||
|
||||
const source = this.src();
|
||||
@@ -277,8 +294,18 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
return;
|
||||
|
||||
this.waveformLoading.set(true);
|
||||
this.waveformUnavailable.set(false);
|
||||
|
||||
try {
|
||||
await this.ensureAudioMetadata(audio);
|
||||
|
||||
await this.waitForNextPaint();
|
||||
|
||||
if (!this.waveformExpanded()) {
|
||||
this.waveformLoading.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { default: WaveSurfer } = await import('wavesurfer.js');
|
||||
|
||||
this.waveSurfer = WaveSurfer.create({
|
||||
@@ -309,14 +336,41 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
});
|
||||
} catch {
|
||||
this.destroyWaveSurfer();
|
||||
this.waveformLoading.set(false);
|
||||
this.waveformUnavailable.set(true);
|
||||
} finally {
|
||||
if (this.waveformUnavailable()) {
|
||||
this.waveformLoading.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ensureAudioMetadata(audio: HTMLAudioElement): Promise<void> {
|
||||
if (audio.readyState >= HTMLMediaElement.HAVE_METADATA)
|
||||
return Promise.resolve();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const handleLoadedMetadata = (): void => {
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
const handleCanPlay = (): void => {
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
const handleError = (): void => {
|
||||
cleanup();
|
||||
reject(new Error('Failed to load audio metadata'));
|
||||
};
|
||||
const cleanup = (): void => {
|
||||
audio.removeEventListener('loadedmetadata', handleLoadedMetadata);
|
||||
audio.removeEventListener('canplay', handleCanPlay);
|
||||
audio.removeEventListener('error', handleError);
|
||||
};
|
||||
|
||||
audio.addEventListener('loadedmetadata', handleLoadedMetadata, { once: true });
|
||||
audio.addEventListener('canplay', handleCanPlay, { once: true });
|
||||
audio.addEventListener('error', handleError, { once: true });
|
||||
audio.load();
|
||||
});
|
||||
}
|
||||
|
||||
private destroyWaveSurfer(): void {
|
||||
if (!this.waveSurfer)
|
||||
return;
|
||||
@@ -363,6 +417,14 @@ export class ChatAudioPlayerComponent implements OnDestroy {
|
||||
].join('');
|
||||
}
|
||||
|
||||
private waitForNextPaint(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => resolve());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
formatTime(seconds: number): string {
|
||||
if (!Number.isFinite(seconds) || seconds < 0)
|
||||
return '0:00';
|
||||
|
||||
Reference in New Issue
Block a user