Remove no-text rooms, highlight active room, fix custom voice room icons
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
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"
|
||||
import Volume from "@assets/icons/volume.svg?dataurl"
|
||||
import UploadMinimalistic from "@assets/icons/upload-minimalistic.svg?dataurl"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
@@ -16,15 +17,7 @@
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {uploadFile} from "@app/core/commands"
|
||||
import {deriveHasLivekit} from "@app/core/state"
|
||||
|
||||
type RoomMode = "text" | "voice" | "both"
|
||||
|
||||
const getRoomMode = (room: RoomMeta): RoomMode => {
|
||||
if (room.livekit && room.noText) return "voice"
|
||||
if (room.livekit) return "both"
|
||||
return "text"
|
||||
}
|
||||
import {deriveHasLivekit, getRoomType, RoomType} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
@@ -37,13 +30,13 @@
|
||||
const {url, header, footer, onsubmit, initialValues = makeRoomMeta()}: Props = $props()
|
||||
|
||||
const values = $state(initialValues)
|
||||
let roomMode = $state<RoomMode>(getRoomMode(initialValues))
|
||||
let roomType = $state<RoomType>(getRoomType(initialValues))
|
||||
const relayHasLivekit = deriveHasLivekit(url)
|
||||
|
||||
const submit = async () => {
|
||||
const room = $state.snapshot(values)
|
||||
|
||||
if ((roomMode === "voice" || roomMode === "both") && !get(relayHasLivekit)) {
|
||||
if (roomType === RoomType.Voice && !get(relayHasLivekit)) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "This relay does not support voice rooms.",
|
||||
@@ -71,8 +64,8 @@
|
||||
}
|
||||
|
||||
if (get(relayHasLivekit)) {
|
||||
room.livekit = roomMode === "both" || roomMode === "voice"
|
||||
room.noText = roomMode === "voice"
|
||||
room.livekit = roomType === RoomType.Voice
|
||||
room.noText = false
|
||||
}
|
||||
const editMessage = await waitForThunkError(editRoom(url, room))
|
||||
|
||||
@@ -171,7 +164,7 @@
|
||||
{#if imagePreview}
|
||||
<ImageIcon src={imagePreview} alt="" class="rounded-lg" />
|
||||
{:else}
|
||||
<Icon icon={Hashtag} />
|
||||
<Icon icon={roomType === RoomType.Voice ? Volume : Hashtag} />
|
||||
{/if}
|
||||
<input bind:value={values.name} class="grow" type="text" />
|
||||
</label>
|
||||
@@ -195,11 +188,10 @@
|
||||
{#snippet input()}
|
||||
<select
|
||||
class="select select-bordered w-full"
|
||||
bind:value={roomMode}
|
||||
bind:value={roomType}
|
||||
aria-label="Room type">
|
||||
<option value="text">Text only</option>
|
||||
<option value="both">Text and voice</option>
|
||||
<option value="voice">Voice only</option>
|
||||
<option value={RoomType.Text}>Text</option>
|
||||
<option value={RoomType.Voice}>Voice</option>
|
||||
</select>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Hashtag from "@assets/icons/hashtag.svg?dataurl"
|
||||
import Volume from "@assets/icons/volume.svg?dataurl"
|
||||
import VolumeLoud from "@assets/icons/volume-loud.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
||||
import {deriveRoom} from "@app/core/state"
|
||||
import {currentVoiceSession} from "@app/voice"
|
||||
|
||||
interface Props {
|
||||
h: string
|
||||
@@ -14,9 +17,25 @@
|
||||
const {url, h, size = 5, fallbackIcon = Hashtag}: Props = $props()
|
||||
|
||||
const room = deriveRoom(url, h)
|
||||
const isVoiceRoom = $derived($room.livekit)
|
||||
const isVoiceRoomActive = $derived(
|
||||
$currentVoiceSession?.url === url && $currentVoiceSession?.h === h,
|
||||
)
|
||||
const typeIconSrc = $derived(isVoiceRoomActive ? VolumeLoud : Volume)
|
||||
</script>
|
||||
|
||||
{#if $room.picture}
|
||||
{#if isVoiceRoom}
|
||||
<div class="flex items-center gap-1 shrink-0">
|
||||
<Icon
|
||||
icon={typeIconSrc}
|
||||
size={size + 1}
|
||||
class={isVoiceRoomActive ? "text-primary -translate-x-0.5" : ""} />
|
||||
{#if $room.picture}
|
||||
<span class="text-base">/</span>
|
||||
<ImageIcon src={$room.picture} {size} alt="" class="rounded-lg" />
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $room.picture}
|
||||
<ImageIcon src={$room.picture} {size} alt="" class="rounded-lg" />
|
||||
{:else}
|
||||
<Icon icon={fallbackIcon} {size} />
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
import SpaceReports from "@app/components/SpaceReports.svelte"
|
||||
import RoomCreate from "@app/components/RoomCreate.svelte"
|
||||
import SpaceMenuRoomItem from "@app/components/SpaceMenuRoomItem.svelte"
|
||||
import VoiceRoomItem from "@app/components/VoiceRoomItem.svelte"
|
||||
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
||||
import SocketStatusIndicator from "@app/components/SocketStatusIndicator.svelte"
|
||||
import {
|
||||
@@ -47,8 +46,6 @@
|
||||
deriveSpaceMembers,
|
||||
deriveUserRooms,
|
||||
deriveOtherRooms,
|
||||
deriveRoomsWithLivekit,
|
||||
deriveRoomsNoText,
|
||||
deriveOtherVoiceRooms,
|
||||
userSpaceUrls,
|
||||
hasNip29,
|
||||
@@ -73,8 +70,6 @@
|
||||
const calendarPath = makeSpacePath(url, "calendar")
|
||||
const userRooms = deriveUserRooms(url)
|
||||
const otherRooms = deriveOtherRooms(url)
|
||||
const roomsWithLivekit = deriveRoomsWithLivekit(url)
|
||||
const roomsNoText = deriveRoomsNoText(url)
|
||||
const otherVoiceRooms = deriveOtherVoiceRooms(url)
|
||||
const members = deriveSpaceMembers(url)
|
||||
const userIsAdmin = deriveUserIsSpaceAdmin(url)
|
||||
@@ -264,14 +259,7 @@
|
||||
<SecondaryNavHeader>Your Rooms</SecondaryNavHeader>
|
||||
{/if}
|
||||
{#each $userRooms as h (h)}
|
||||
{#if !$roomsNoText.has(h)}
|
||||
<SpaceMenuRoomItem notify {replaceState} {url} {h} />
|
||||
{/if}
|
||||
{#if $roomsWithLivekit.has(h)}
|
||||
<div class="hidden md:block">
|
||||
<VoiceRoomItem {url} {h} />
|
||||
</div>
|
||||
{/if}
|
||||
<SpaceMenuRoomItem notify {replaceState} {url} {h} />
|
||||
{/each}
|
||||
{#if $otherRooms.length > 0}
|
||||
<div class="h-2"></div>
|
||||
@@ -293,13 +281,11 @@
|
||||
<SpaceMenuRoomItem {replaceState} {url} {h} />
|
||||
{/each}
|
||||
{#if $otherVoiceRooms.length > 0}
|
||||
<div class="hidden md:block">
|
||||
<div class="h-2"></div>
|
||||
<SecondaryNavHeader>Voice Rooms</SecondaryNavHeader>
|
||||
{#each $otherVoiceRooms as h (h)}
|
||||
<VoiceRoomItem {url} {h} />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="h-2"></div>
|
||||
<SecondaryNavHeader>Voice Rooms</SecondaryNavHeader>
|
||||
{#each $otherVoiceRooms as h (h)}
|
||||
<SpaceMenuRoomItem {replaceState} {url} {h} />
|
||||
{/each}
|
||||
{/if}
|
||||
{#if $canCreateRoom}
|
||||
<SecondaryNavItem {replaceState} onclick={addRoom}>
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
||||
import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte"
|
||||
import {deriveShouldNotify} from "@app/core/state"
|
||||
import {deriveRoom, deriveShouldNotify, getRoomType, RoomType} from "@app/core/state"
|
||||
import {notifications} from "@app/util/notifications"
|
||||
import {makeRoomPath} from "@app/util/routes"
|
||||
import {joinVoiceRoom, currentVoiceSession} from "@app/voice"
|
||||
|
||||
interface Props {
|
||||
url: any
|
||||
@@ -17,15 +18,24 @@
|
||||
|
||||
const {url, h, notify = false, replaceState = false}: Props = $props()
|
||||
|
||||
const room = deriveRoom(url, h)
|
||||
const roomType = $derived(getRoomType($room))
|
||||
const path = makeRoomPath(url, h)
|
||||
const shouldNotifyForSpace = deriveShouldNotify(url)
|
||||
const shouldNotifyForRoom = deriveShouldNotify(url, h)
|
||||
const showDifferenceIcon = $derived($shouldNotifyForRoom !== $shouldNotifyForSpace)
|
||||
|
||||
const handleClick = () => {
|
||||
if (roomType !== RoomType.Voice) return
|
||||
if ($currentVoiceSession?.url === url && $currentVoiceSession?.h === h) return
|
||||
void joinVoiceRoom(url, h)
|
||||
}
|
||||
</script>
|
||||
|
||||
<SecondaryNavItem
|
||||
href={path}
|
||||
{replaceState}
|
||||
onclick={handleClick}
|
||||
notification={notify ? $notifications.has(path) : false}>
|
||||
<RoomNameWithImage {url} {h} />
|
||||
{#if showDifferenceIcon}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script lang="ts">
|
||||
import cx from "classnames"
|
||||
import {loadProfile, displayProfileByPubkey} from "@welshman/app"
|
||||
import Volume from "@assets/icons/volume.svg?dataurl"
|
||||
import VolumeLoud from "@assets/icons/volume-loud.svg?dataurl"
|
||||
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||
import RoomImage from "@app/components/RoomImage.svelte"
|
||||
@@ -62,7 +60,7 @@
|
||||
{#if joinAbortController}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{:else}
|
||||
<RoomImage {url} {h} size={4} fallbackIcon={isActive ? VolumeLoud : Volume} />
|
||||
<RoomImage {url} {h} size={4} />
|
||||
{/if}
|
||||
<RoomName {url} {h} />
|
||||
</div>
|
||||
|
||||
+20
-15
@@ -568,11 +568,19 @@ export const chatSearch = derived(throttled(800, chatsById), $chatsByPubkey => {
|
||||
|
||||
// Rooms
|
||||
|
||||
export enum RoomType {
|
||||
Text = "text",
|
||||
Voice = "voice",
|
||||
}
|
||||
|
||||
export type Room = PublishedRoomMeta & {
|
||||
id: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export const getRoomType = (room: {livekit?: boolean}): RoomType =>
|
||||
room.livekit ? RoomType.Voice : RoomType.Text
|
||||
|
||||
export const makeRoomId = (url: string, h: string) => `${url}'${h}`
|
||||
|
||||
export const splitRoomId = (id: string) => id.split("'")
|
||||
@@ -664,7 +672,7 @@ export const displayRoom = (url: string, h: string) => getRoom(makeRoomId(url, h
|
||||
|
||||
export const roomComparator = (url: string) => (h: string) => displayRoom(url, h).toLowerCase()
|
||||
|
||||
export const deriveRoomsWithLivekit = (url: string) =>
|
||||
export const deriveVoiceRooms = (url: string) =>
|
||||
derived(roomsById, $roomsById => {
|
||||
const set = new Set<string>()
|
||||
for (const room of $roomsById.values()) {
|
||||
@@ -687,20 +695,17 @@ export const deriveRoomsNoText = (url: string) =>
|
||||
})
|
||||
|
||||
export const deriveOtherVoiceRooms = (url: string) =>
|
||||
derived(
|
||||
[deriveRoomsWithLivekit(url), deriveUserRooms(url)],
|
||||
([$roomsWithLivekit, $userRooms]) => {
|
||||
const rooms: string[] = []
|
||||
derived([deriveVoiceRooms(url), deriveUserRooms(url)], ([$roomsWithLivekit, $userRooms]) => {
|
||||
const rooms: string[] = []
|
||||
|
||||
for (const h of $roomsWithLivekit) {
|
||||
if (!$userRooms.includes(h)) {
|
||||
rooms.push(h)
|
||||
}
|
||||
for (const h of $roomsWithLivekit) {
|
||||
if (!$userRooms.includes(h)) {
|
||||
rooms.push(h)
|
||||
}
|
||||
}
|
||||
|
||||
return sortBy(roomComparator(url), uniq(rooms))
|
||||
},
|
||||
)
|
||||
return sortBy(roomComparator(url), uniq(rooms))
|
||||
})
|
||||
|
||||
// User space/room lists
|
||||
|
||||
@@ -792,12 +797,12 @@ export const deriveUserRooms = (url: string) =>
|
||||
|
||||
export const deriveOtherRooms = (url: string) =>
|
||||
derived(
|
||||
[deriveUserRooms(url), deriveRoomsNoText(url), roomsByUrl],
|
||||
([$userRooms, $roomsNoText, $roomsByUrl]) => {
|
||||
[deriveUserRooms(url), deriveVoiceRooms(url), roomsByUrl],
|
||||
([$userRooms, voiceRooms, $roomsByUrl]) => {
|
||||
const rooms: string[] = []
|
||||
|
||||
for (const {h} of $roomsByUrl.get(url) || []) {
|
||||
if (!$userRooms.includes(h) && !$roomsNoText.has(h)) {
|
||||
if (!$userRooms.includes(h) && !voiceRooms.has(h)) {
|
||||
rooms.push(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,13 @@
|
||||
<div data-component="PageBar" class="cw top-sai fixed z-nav p-2 {props.class}">
|
||||
<div class="rounded-xl bg-base-100 p-4 shadow-md h-20 md:h-12 flex flex-col justify-center">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="ellipsize flex items-center gap-4 whitespace-nowrap">
|
||||
{@render props.icon?.()}
|
||||
{@render props.title?.()}
|
||||
<div class="flex min-w-0 flex-1 items-center gap-4 py-0.5 pl-1">
|
||||
<div class="shrink-0 flex items-center">
|
||||
{@render props.icon?.()}
|
||||
</div>
|
||||
<div class="ellipsize whitespace-nowrap">
|
||||
{@render props.title?.()}
|
||||
</div>
|
||||
</div>
|
||||
{@render props.action?.()}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user