diff --git a/src/app/call/stores.ts b/src/app/call/stores.ts
index abe4084e..9b5fbac0 100644
--- a/src/app/call/stores.ts
+++ b/src/app/call/stores.ts
@@ -6,11 +6,13 @@ export type VoiceSession = {
url: string
h: string
room: LiveKitRoom
- muted: boolean
cameraOn: boolean
screenShareOn: boolean
}
+/** Mic mute state is separate so toggling it does not re-render video tiles. */
+export const voiceMicMuted = writable(true)
+
export type Pubkey = string
export type VoiceParticipant = {pubkey?: Pubkey; identity: string}
diff --git a/src/app/call/voice.ts b/src/app/call/voice.ts
index 07ab0648..050e2385 100644
--- a/src/app/call/voice.ts
+++ b/src/app/call/voice.ts
@@ -13,7 +13,7 @@ import {
type AudioCaptureOptions,
} from "livekit-client"
import {derived, get} from "svelte/store"
-import {map, removeUndefined, uniqBy} from "@welshman/lib"
+import {map, not, removeUndefined, uniqBy} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {makeHttpAuth, makeHttpAuthHeader, getTags} from "@welshman/util"
import {signer} from "@welshman/app"
@@ -22,6 +22,7 @@ import {AbortError, whenAborted, whenTimeout} from "$lib/util"
import {
currentVoiceRoom,
currentVoiceSession,
+ voiceMicMuted,
participantFromLiveKitIdentity,
participantKey,
participantPubkeyMap,
@@ -173,6 +174,7 @@ const setUpMicrophone = async (
const onRoomDisconnected = (reason?: DisconnectReason) => {
videoPrimaryTileKey.set(undefined)
+ voiceMicMuted.set(true)
currentVoiceSession.set(undefined)
resetVideoCallLayout()
if (reason !== undefined && reason !== DisconnectReason.CLIENT_INITIATED) {
@@ -295,11 +297,11 @@ export const joinVoiceRoom = async (
const muted = await setUpMicrophone(startMuted, preferredMicId, liveKitRoom.localParticipant)
+ voiceMicMuted.set(muted)
currentVoiceSession.set({
url,
h,
room: liveKitRoom,
- muted,
cameraOn: false,
screenShareOn: false,
})
@@ -339,6 +341,7 @@ export const leaveVoiceRoom = async () => {
voiceState.set(VoiceState.Disconnected)
videoPrimaryTileKey.set(undefined)
+ voiceMicMuted.set(true)
currentVoiceSession.set(undefined)
resetVideoCallLayout()
session.room.disconnect()
@@ -356,18 +359,17 @@ export const toggleMute = async () => {
const session = get(currentVoiceSession)
if (!session) return
- const muted = !session.muted
- if (muted) {
+ voiceMicMuted.update(not)
+ if (get(voiceMicMuted)) {
// Disable and re-enable microphone to trigger permission prompt
session.room.localParticipant.setMicrophoneEnabled(false)
- currentVoiceSession.set({...session, muted})
return
}
try {
await session.room.localParticipant.setMicrophoneEnabled(true)
- currentVoiceSession.set({...session, muted})
} catch (e) {
+ voiceMicMuted.set(true)
pushToast({theme: "error", message: "Could not access microphone"})
}
}
diff --git a/src/app/components/VideoCallContent.svelte b/src/app/components/VideoCallContent.svelte
index 6bd00ebf..08e20c7c 100644
--- a/src/app/components/VideoCallContent.svelte
+++ b/src/app/components/VideoCallContent.svelte
@@ -216,8 +216,8 @@
data-tip={pinned ? "Exit spotlight" : "Spotlight"}
aria-pressed={pinned}
class={cx(
- "absolute right-1 top-1 z-20 btn btn-xs btn-square btn-ghost",
- pinned ? "btn-active bg-primary/25 text-primary" : "bg-base-100/70",
+ "absolute right-1 top-1 z-20 btn btn-xs btn-square",
+ pinned ? "btn-primary" : "btn-ghost bg-base-100",
)}
onclick={spotlightHandlerFor(tileKey(tile))}>
diff --git a/src/app/components/VoiceWidget.svelte b/src/app/components/VoiceWidget.svelte
index c8601644..1882b3f8 100644
--- a/src/app/components/VoiceWidget.svelte
+++ b/src/app/components/VoiceWidget.svelte
@@ -41,6 +41,7 @@
VoiceState,
currentVoiceSession,
currentVoiceRoom,
+ voiceMicMuted,
voiceState,
isLocalSpeaking,
} from "@app/call/stores"
@@ -183,18 +184,17 @@
{:else if $voiceState === VoiceState.Connected && $currentVoiceSession}