147 lines
4.2 KiB
Svelte
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>
|