Files
flotilla/src/routes/spaces/+page.svelte
T

147 lines
4.2 KiB
Svelte

<script lang="ts">
import SettingsMinimalistic from "@assets/icons/settings-minimalistic.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Page from "@lib/components/Page.svelte"
import PageBar from "@lib/components/PageBar.svelte"
import PageContent from "@lib/components/PageContent.svelte"
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
import SpaceAdd from "@app/components/SpaceAdd.svelte"
import {userSpaceUrls, loadUserGroupList, PLATFORM_RELAYS} from "@app/core/state"
import {pushModal} from "@app/util/modal"
const addSpace = () => pushModal(SpaceAdd)
const reconcileUrls = (currentUrls: string[], nextUrls: string[]) => {
const mergedUrls = currentUrls.filter(url => nextUrls.includes(url))
for (const url of nextUrls) {
if (!mergedUrls.includes(url)) {
mergedUrls.push(url)
}
}
return mergedUrls
}
const isSameOrder = (a: string[], b: string[]) =>
a.length === b.length && a.every((url, index) => url === b[index])
const reorderUrls = (sourceUrl: string, targetUrl: string, currentUrls: string[]) => {
if (sourceUrl === targetUrl) {
return currentUrls
}
const sourceIndex = currentUrls.indexOf(sourceUrl)
const targetIndex = currentUrls.indexOf(targetUrl)
if (sourceIndex === -1 || targetIndex === -1) {
return currentUrls
}
const nextUrls = currentUrls.filter(url => url !== sourceUrl)
nextUrls.splice(targetIndex, 0, sourceUrl)
return nextUrls
}
const moveDraggedUrl = (targetUrl: string) => {
if (!draggedUrl) {
return
}
orderedSpaceUrls = reorderUrls(draggedUrl, targetUrl, orderedSpaceUrls)
}
const onDragStart = (e: DragEvent, url: string) => {
draggedUrl = url
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = "move"
e.dataTransfer.setData("text/plain", url)
}
}
const onDragOver = (e: DragEvent, targetUrl: string) => {
e.preventDefault()
moveDraggedUrl(targetUrl)
}
const onDrop = (e: DragEvent, targetUrl: string) => {
e.preventDefault()
moveDraggedUrl(targetUrl)
draggedUrl = undefined
}
const onDragEnd = () => {
draggedUrl = undefined
}
$effect(() => {
const nextUrls = reconcileUrls(orderedSpaceUrls, $userSpaceUrls)
if (!isSameOrder(nextUrls, orderedSpaceUrls)) {
orderedSpaceUrls = nextUrls
}
})
let orderedSpaceUrls = $state<string[]>([])
let draggedUrl = $state<string | undefined>()
</script>
<Page class="cw-full">
<PageBar class="cw-full">
{#snippet icon()}
<div class="center">
<Icon icon={SettingsMinimalistic} />
</div>
{/snippet}
{#snippet title()}
<strong>Your Spaces</strong>
{/snippet}
{#snippet action()}
{#if $userSpaceUrls.length > 0 && PLATFORM_RELAYS.length === 0}
<Button class="btn btn-primary btn-sm" onclick={addSpace}>
<Icon icon={AddCircle} />
Add Space
</Button>
{/if}
{/snippet}
</PageBar>
<PageContent class="cw-full flex flex-col gap-2 p-4 pt-6">
{#each PLATFORM_RELAYS as url (url)}
<MenuSpacesItem {url} />
{:else}
{#await loadUserGroupList()}
<div class="flex justify-center items-center py-20">
<span class="loading loading-spinner mr-3"></span>
Loading your spaces...
</div>
{:then}
{#each orderedSpaceUrls as url (url)}
<div
class:opacity-60={draggedUrl === url}
draggable="true"
role="listitem"
ondragstart={e => onDragStart(e, url)}
ondragover={e => onDragOver(e, url)}
ondrop={e => onDrop(e, url)}
ondragend={onDragEnd}>
<MenuSpacesItem {url} />
</div>
{:else}
<div class="flex flex-col gap-8 items-center py-20">
<p>You haven't added any spaces yet!</p>
<Button class="btn btn-primary" onclick={addSpace}>
<Icon icon={AddCircle} />
Add a Space
</Button>
</div>
{/each}
{/await}
{/each}
</PageContent>
</Page>