Add a dialog before joining voice rooms #109

Merged
hodlbod merged 5 commits from feature/join-voice-room-dialog into dev 2026-03-27 19:02:57 +00:00
Collaborator

After using the voice rooms more since we removed the option for voice-only rooms I think you were right to suggest a dialog box before joining rooms. It felt far to clunky to have to join the voice call any time you just wanted to try to view room members, edit room settings, or just view the recent text chat.

This adds a dialog that allows the user to decline to join the call but still access the text part of the room along with associated settings and controls. It also acts as another confirmation step before turning on the user's microphone, and allows them to choose an audio input so they don't have to mess with the (generally terrible) browser controls for doing so. We should probably have controls to change your audio input and output from within the call as well, but I think this is enough for an MVP.

Screenshot 2026-03-27 at 11.10.53 AM.png

After using the voice rooms more since we removed the option for voice-only rooms I think you were right to suggest a dialog box before joining rooms. It felt far to clunky to have to join the voice call any time you just wanted to try to view room members, edit room settings, or just view the recent text chat. This adds a dialog that allows the user to decline to join the call but still access the text part of the room along with associated settings and controls. It also acts as another confirmation step before turning on the user's microphone, and allows them to choose an audio input so they don't have to mess with the (generally terrible) browser controls for doing so. We should probably have controls to change your audio input and output from within the call as well, but I think this is enough for an MVP. ![Screenshot 2026-03-27 at 11.10.53 AM.png](/attachments/3ac271a6-5d17-4063-9ac6-3e5bdef10ccf)
mplorentz reviewed 2026-03-27 15:18:07 +00:00
@@ -21,3 +26,2 @@
const roomName = $derived(
$currentVoiceRoom ? displayRoom($currentVoiceRoom.url, $currentVoiceRoom.h) : "",
const targetRoom = derived(
Author
Collaborator

This logic got more complex. Basically we need incorporate the room the user is currently viewing now.

Detailed explanation: now that you can join the text room without joining the voice call I needed to handle the case where you join voice room A, then view the text chat for voice room B, then disconnect from the call. In this case the VoiceWidget should show a button to join the call for voice room B since that's the room you are currently viewing. It required some changes to state.ts which I'm not sure I'm happy with. More on that below.

This logic got more complex. Basically we need incorporate the room the user is currently viewing now. Detailed explanation: now that you can join the text room without joining the voice call I needed to handle the case where you join voice room A, then view the text chat for voice room B, then disconnect from the call. In this case the `VoiceWidget` should show a button to join the call for voice room B since that's the room you are currently viewing. It required some changes to state.ts which I'm not sure I'm happy with. More on that below.
mplorentz reviewed 2026-03-27 15:20:07 +00:00
@@ -805,6 +809,25 @@ export const deriveOtherRooms = (url: string) =>
},
)
export const parseDisplayedRoomParams = (params: Page["params"]): RoomRef | undefined => {
Author
Collaborator

I need a way to derive the room the user is currently viewing to handle the UX for VoiceWidget displayed above. Parsing it out of the page params seems safe enough but idk it feels like there should be a better way to do this. What do you think?

I need a way to derive the room the user is currently viewing to handle the UX for `VoiceWidget` displayed above. Parsing it out of the page params seems safe enough but idk it feels like there should be a better way to do this. What do you think?
mplorentz reviewed 2026-03-27 15:21:49 +00:00
src/app/voice.ts Outdated
@@ -29,3 +36,3 @@
export type VoiceParticipant = {pubkey?: Pubkey; identity: string}
export type VoiceState = "joining" | "connected" | "disconnected"
export enum VoiceState {
Author
Collaborator

idk why I didn't make this an enum before.

idk why I didn't make this an enum before.
hodlbod marked this conversation as resolved
hodlbod reviewed 2026-03-27 16:14:39 +00:00
@@ -26,0 +48,4 @@
const openJoinDialog = async () => {
const target = get(targetRoom)
if (!target) return
Owner

Just use $targetRoom here, as long as you're in a svelte file you can avoid using get, even if you're not in a template

Just use `$targetRoom` here, as long as you're in a svelte file you can avoid using get, even if you're not in a template
@@ -808,0 +826,4 @@
const inner = deriveRoom($p.url, $p.h)
return inner.subscribe(set)
},
) as Readable<(RoomMeta & {url: string; id: string}) | undefined>
Owner

I think parsing page parameters is fine, there's no way around it really. We could do this pretty simply in the component, though. Howabout just doing the same thing we do in h/page in VoiceWidget:

const {h, relay} = $derived($page.params)
const url = $derived(relay ? decodeRelay(relay) : undefined)
const displayedRoom = $derived(url && h ? deriveRoom(url, h) : readable(undefined))

Nesting $derived/derived like that is gross but I think it makes sense here

I think parsing page parameters is fine, there's no way around it really. We could do this pretty simply in the component, though. Howabout just doing the same thing we do in h/page in VoiceWidget: ``` const {h, relay} = $derived($page.params) const url = $derived(relay ? decodeRelay(relay) : undefined) const displayedRoom = $derived(url && h ? deriveRoom(url, h) : readable(undefined)) ``` Nesting $derived/derived like that is gross but I think it makes sense here
src/app/voice.ts Outdated
@@ -36,2 +46,3 @@
export const voiceState = writable<VoiceState>(VoiceState.Disconnected)
export const currentVoiceRoom = writable<{url: string; h: string} | undefined>(undefined)
export const currentVoiceRoom = writable<RoomRef | undefined>(undefined)
Owner

Any reason we can't just use Room from core/state? Or maybe deriveRoom doesn't return that type.

Any reason we can't just use `Room` from core/state? Or maybe deriveRoom doesn't return that type.
Author
Collaborator

Yeah, I was able to do that.

Yeah, I was able to do that.
mplorentz force-pushed feature/join-voice-room-dialog from 67a6dc3feb to 0b04076baa 2026-03-27 18:59:47 +00:00 Compare
mplorentz requested review from hodlbod 2026-03-27 19:00:07 +00:00
hodlbod merged commit 16a73f27c9 into dev 2026-03-27 19:02:57 +00:00
hodlbod deleted branch feature/join-voice-room-dialog 2026-03-27 19:02:57 +00:00
Sign in to join this conversation.