feature/23-voice-room/poc #93

Merged
hodlbod merged 68 commits from feature/23-voice-room/poc into dev 2026-03-16 20:38:06 +00:00
2 changed files with 11 additions and 35 deletions
Showing only changes of commit a691f7b80a - Show all commits
+9 -22
View File
@@ -1,9 +1,8 @@
<script lang="ts">
import {get} from "svelte/store"
import type {Snippet} from "svelte"
import {assoc, append, nth, uniqBy} from "@welshman/lib"
import type {RoomMeta, TrustedEvent} from "@welshman/util"
import {getTag, makeRoomMeta} from "@welshman/util"
import type {RoomMeta} from "@welshman/util"
import {makeRoomMeta} from "@welshman/util"
import {waitForThunkError, createRoom, editRoom, joinRoom} from "@welshman/app"
import StickerSmileSquare from "@assets/icons/sticker-smile-square.svg?dataurl"
import Hashtag from "@assets/icons/hashtag.svg?dataurl"
@@ -21,23 +20,12 @@
type RoomMode = "text" | "voice" | "both"
const getRoomModeFromEvent = (event?: TrustedEvent): RoomMode => {
const tags = event?.tags ?? []
const hasLivekit = !!getTag("livekit", tags)
const hasNoText = !!getTag("no-text", tags)
if (hasLivekit && hasNoText) return "voice"
if (hasLivekit) return "both"
const getRoomMode = (room: RoomMeta): RoomMode => {
if (room.livekit && room.noText) return "voice"
if (room.livekit) return "both"
return "text"
}
hodlbod marked this conversation as resolved Outdated
Outdated
Review

[nit] A useful util is getTagValue("livekit", tags) just for clarity. Also, beef up the type signature, either pass maybe or pass a TrustedEvent

[nit] A useful util is `getTagValue("livekit", tags)` just for clarity. Also, beef up the type signature, either pass maybe<tags> or pass a TrustedEvent
Outdated
Review

Ok cool. Can you explain why you prefer Maybe over typescripts built-in optional type? Is that a pretty universal preference for you?

Ok cool. Can you explain why you prefer Maybe over typescripts built-in optional type? Is that a pretty universal preference for you?
Outdated
Review

The optional type means scattering around a bunch of ad-hoc types that would need to be updated if the model changes (not that it will here, but it's still redundant). (event?: TrustedEvent) is stronger and allows more assumptions about what's actually happening. If you just want to accept tags so that (for example) callers can call this without having an event, you could do (tags: string[][] = []). But this function should always be able to assume that an event exists

The optional type means scattering around a bunch of ad-hoc types that would need to be updated if the model changes (not that it will here, but it's still redundant). `(event?: TrustedEvent)` is stronger and allows more assumptions about what's actually happening. If you just want to accept tags so that (for example) callers can call this without having an event, you could do `(tags: string[][] = [])`. But this function should always be able to assume that an event exists
Outdated
Review

Sorry, I'm asking why you suggested (event: Maybe<TrustedEvent>) over (event?: TrustedEvent)?

Sorry, I'm asking why you suggested `(event: Maybe<TrustedEvent>)` over `(event?: TrustedEvent)`?
Outdated
Review

Oh, I see, no optional parameters are better, I was just being vague

Oh, I see, no optional parameters are better, I was just being vague
const buildTagsWithRoomMode = (existingTags: string[][], roomMode: RoomMode): string[][] => {
const filtered = existingTags.filter(t => t[0] !== "livekit" && t[0] !== "no-text")
if (roomMode === "both") return uniqBy(nth(0), append(["livekit"], filtered))
if (roomMode === "voice")
return uniqBy(nth(0), append(["no-text"], append(["livekit"], filtered)))
return filtered
}
type Props = {
url: string
header: Snippet
1
@@ -49,7 +37,7 @@
const {url, header, footer, onsubmit, initialValues = makeRoomMeta()}: Props = $props()
const values = $state(initialValues)
let roomMode = $state<RoomMode>(getRoomModeFromEvent(initialValues.event))
let roomMode = $state<RoomMode>(getRoomMode(initialValues))
const relayHasLivekit = deriveHasLivekit(url)
const submit = async () => {
1
@@ -82,10 +70,9 @@
return pushToast({theme: "error", message: createMessage})
}
hodlbod marked this conversation as resolved Outdated
Outdated
Review

This should never run if we make the form change below.

This should never run if we make the form change below.
if (room.event && get(relayHasLivekit)) {
const existingTags = room.event.tags ?? []
const tags = buildTagsWithRoomMode(existingTags, roomMode)
room.event = assoc("tags", tags)(room.event) as RoomMeta["event"]
if (get(relayHasLivekit)) {
room.livekit = roomMode === "both" || roomMode === "voice"
room.noText = roomMode === "voice"
}
const editMessage = await waitForThunkError(editRoom(url, room))
7
+2 -13
View File
@@ -103,7 +103,6 @@ import {
getListTags,
getPubkeyTagValues,
getRelayTagValues,
getTag,
getTagValues,
isRelayUrl,
normalizeRelayUrl,
4
@@ -669,7 +668,7 @@ export const deriveRoomsWithLivekit = (url: string) =>
derived(roomsById, $roomsById => {
const set = new Set<string>()
for (const room of $roomsById.values()) {
if (room.url === url && getTag("livekit", room.event?.tags ?? [])) {
if (room.url === url && room.livekit) {
set.add(room.h)
}
}
@@ -680,23 +679,13 @@ export const deriveRoomsNoText = (url: string) =>
derived(roomsById, $roomsById => {
const set = new Set<string>()
for (const room of $roomsById.values()) {
if (room.url === url && getTag("no-text", room.event?.tags ?? [])) {
if (room.url === url && room.noText) {
set.add(room.h)
}
}
return set
})
export const roomHasLivekit = (url: string, h: string) => {
const room = getRoom(makeRoomId(url, h))
return !!getTag("livekit", room?.event?.tags ?? [])
}
export const roomIsNoText = (url: string, h: string) => {
const room = getRoom(makeRoomId(url, h))
return !!getTag("no-text", room?.event?.tags ?? [])
}
// User space/room lists
export const groupListsByPubkey = deriveItemsByKey({