diff --git a/src/app/components/VoiceRoomItem.svelte b/src/app/components/VoiceRoomItem.svelte index 5676c314..f17748e3 100644 --- a/src/app/components/VoiceRoomItem.svelte +++ b/src/app/components/VoiceRoomItem.svelte @@ -5,7 +5,6 @@ import ProfileCircle from "@app/components/ProfileCircle.svelte" import RoomImage from "@app/components/RoomImage.svelte" import RoomName from "@app/components/RoomName.svelte" - import {pushToast} from "@app/util/toast" import {makeRoomPath} from "@app/util/routes" import { deriveVoiceParticipants, @@ -42,12 +41,7 @@ return } - try { - await joinVoiceRoom(url, h) - } catch (e) { - console.error("Failed to join voice room", e) - pushToast({theme: "error", message: "Failed to join voice room"}) - } + await joinVoiceRoom(url, h) } $effect(() => { diff --git a/src/app/voice.ts b/src/app/voice.ts index e8222e99..8e13a098 100644 --- a/src/app/voice.ts +++ b/src/app/voice.ts @@ -9,7 +9,7 @@ import type {TrustedEvent} from "@welshman/util" import {makeHttpAuth, makeHttpAuthHeader, getTags} from "@welshman/util" import {signer} from "@welshman/app" import {getLivekitEndpoint} from "$lib/livekit" -import {AbortError, whenAborted, whenTimeout} from "$lib/util" +import {AbortError, TimeoutError, whenAborted, whenTimeout} from "$lib/util" import {deriveLatestEventForUrl} from "@app/core/state" import {pushToast} from "@app/util/toast" @@ -17,6 +17,24 @@ export const LIVEKIT_PARTICIPANTS = 39004 export {checkRelayHasLivekit} from "$lib/livekit" +export class VoiceJoinMembershipError extends Error { + constructor() { + super("Failed to join voice room: you must be a member.") + this.name = "VoiceJoinMembershipError" + } +} + +const handleJoinError = (e: unknown) => { + if (e instanceof AbortError) return + console.error("Failed to join voice room", e) + let message = "Failed to join voice room" + if (e instanceof VoiceJoinMembershipError) message = e.message + else if (e instanceof TimeoutError) + message = "Connection timed out. Please check your network and try again." + else if (e instanceof Error && e.message === "No signer available") message = e.message + pushToast({theme: "error", message}) +} + export type VoiceSession = { url: string h: string @@ -95,6 +113,7 @@ const fetchLivekitToken = async ( if (!response.ok) { const text = await response.text() + if (response.status === 403) throw new VoiceJoinMembershipError() throw new Error(`Token request failed (${response.status}): ${text}`) } @@ -243,8 +262,7 @@ export const joinVoiceRoom = async (url: string, h: string): Promise => { playJoinSound() } catch (e) { if (isActive()) voiceState.set("disconnected") - if (e instanceof AbortError) return - throw e + handleJoinError(e) } finally { if (isActive()) joinAbortController = undefined } @@ -266,7 +284,8 @@ export const leaveVoiceRoom = async () => { export const rejoinVoiceRoom = () => { const target = get(currentVoiceRoom) - if (target) joinVoiceRoom(target.url, target.h) + if (!target) return + void joinVoiceRoom(target.url, target.h) } export const toggleMute = async () => {