Files
flotilla/src/app/components/MenuSpace.svelte
2025-06-09 13:48:46 -07:00

175 lines
5.7 KiB
Svelte

<script lang="ts">
import {onMount} from "svelte"
import {displayRelayUrl} from "@welshman/util"
import {deriveRelay} from "@welshman/app"
import {fly} from "@lib/transition"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Popover from "@lib/components/Popover.svelte"
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
import SecondaryNavHeader from "@lib/components/SecondaryNavHeader.svelte"
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
import SpaceInvite from "@app/components/SpaceInvite.svelte"
import SpaceExit from "@app/components/SpaceExit.svelte"
import SpaceJoin from "@app/components/SpaceJoin.svelte"
import ProfileList from "@app/components/ProfileList.svelte"
import RoomCreate from "@app/components/RoomCreate.svelte"
import MenuSpaceRoomItem from "@app/components/MenuSpaceRoomItem.svelte"
import InfoMissingRooms from "@app/components/InfoMissingRooms.svelte"
import {
userRoomsByUrl,
hasMembershipUrl,
memberships,
deriveUserRooms,
deriveOtherRooms,
hasNip29,
} from "@app/state"
import {notifications} from "@app/notifications"
import {pushModal} from "@app/modal"
import {makeSpacePath} from "@app/routes"
const {url} = $props()
const relay = deriveRelay(url)
const chatPath = makeSpacePath(url, "chat")
const threadsPath = makeSpacePath(url, "threads")
const calendarPath = makeSpacePath(url, "calendar")
const userRooms = deriveUserRooms(url)
const otherRooms = deriveOtherRooms(url)
const openMenu = () => {
showMenu = true
}
const toggleMenu = () => {
showMenu = !showMenu
}
const showMissingRooms = () => pushModal(InfoMissingRooms)
const showMembers = () =>
pushModal(
ProfileList,
{url, pubkeys: members, title: `Members of`, subtitle: displayRelayUrl(url)},
{replaceState},
)
const createInvite = () => pushModal(SpaceInvite, {url}, {replaceState})
const leaveSpace = () => pushModal(SpaceExit, {url}, {replaceState})
const joinSpace = () => pushModal(SpaceJoin, {url}, {replaceState})
const addRoom = () => pushModal(RoomCreate, {url}, {replaceState})
let showMenu = $state(false)
let replaceState = $state(false)
let element: Element | undefined = $state()
const members = $derived(
$memberships.filter(l => hasMembershipUrl(l, url)).map(l => l.event.pubkey),
)
onMount(() => {
replaceState = Boolean(element?.closest(".drawer"))
})
</script>
<div bind:this={element}>
<SecondaryNavSection class="max-h-screen">
<div>
<SecondaryNavItem class="w-full !justify-between" onclick={openMenu}>
<strong class="ellipsize">{displayRelayUrl(url)}</strong>
<Icon icon="alt-arrow-down" />
</SecondaryNavItem>
{#if showMenu}
<Popover hideOnClick onClose={toggleMenu}>
<ul
transition:fly
class="menu absolute z-popover mt-2 w-full rounded-box bg-base-100 p-2 shadow-xl">
<li>
<Button onclick={showMembers}>
<Icon icon="user-rounded" />
View Members ({members.length})
</Button>
</li>
<li>
<Button onclick={createInvite}>
<Icon icon="link-round" />
Create Invite
</Button>
</li>
<li>
{#if $userRoomsByUrl.has(url)}
<Button onclick={leaveSpace} class="text-error">
<Icon icon="exit" />
Leave Space
</Button>
{:else}
<Button onclick={joinSpace} class="bg-primary text-primary-content">
<Icon icon="login-2" />
Join Space
</Button>
{/if}
</li>
</ul>
</Popover>
{/if}
</div>
<div class="flex min-h-0 flex-col gap-1 overflow-auto">
<SecondaryNavItem {replaceState} href={makeSpacePath(url)}>
<Icon icon="home-smile" /> Home
</SecondaryNavItem>
<SecondaryNavItem
{replaceState}
href={threadsPath}
notification={$notifications.has(threadsPath)}>
<Icon icon="notes-minimalistic" /> Threads
</SecondaryNavItem>
<SecondaryNavItem
{replaceState}
href={calendarPath}
notification={$notifications.has(calendarPath)}>
<Icon icon="calendar-minimalistic" /> Calendar
</SecondaryNavItem>
{#if hasNip29($relay)}
{#if $userRooms.length > 0}
<div class="h-2"></div>
<SecondaryNavHeader>Your Rooms</SecondaryNavHeader>
{/if}
{#each $userRooms as room, i (room)}
<MenuSpaceRoomItem {replaceState} notify {url} {room} />
{/each}
{#if $otherRooms.length > 0}
<div class="h-2"></div>
<SecondaryNavHeader>
{#if $userRooms.length > 0}
Other Rooms
{:else}
Rooms
{/if}
</SecondaryNavHeader>
{/if}
{#each $otherRooms as room, i (room)}
<MenuSpaceRoomItem {replaceState} {url} {room} />
{/each}
<SecondaryNavItem {replaceState} onclick={addRoom}>
<Icon icon="add-circle" />
Create room
</SecondaryNavItem>
{:else}
<SecondaryNavItem
{replaceState}
href={chatPath}
notification={$notifications.has(chatPath)}>
<Icon icon="chat-round" /> Chat
</SecondaryNavItem>
<Button class="link flex items-center gap-2 py-2 pl-4 text-sm" onclick={showMissingRooms}>
<Icon icon="info-circle" size={4} />
Where did my rooms go?
</Button>
{/if}
</div>
</SecondaryNavSection>
</div>