forked from coracle/flotilla
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc88030943 | |||
| 84acad4a20 | |||
| 64a62a72d1 | |||
| e0511edc4d |
@@ -1,15 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
urls: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const {urls}: Props = $props()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="column menu gap-2">
|
|
||||||
{#each urls as url (url)}
|
|
||||||
<MenuSpacesItem {url} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link replaceState href={path}>
|
<Link replaceState href={path}>
|
||||||
<CardButton class="btn-neutral shadow-md bg-alt">
|
<CardButton class="btn-neutral shadow-md bg-alt rounded-box border-none">
|
||||||
{#snippet icon()}
|
{#snippet icon()}
|
||||||
<RelayIcon {url} size={12} class="rounded-full" />
|
<RelayIcon {url} size={12} class="rounded-full" />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
import Divider from "@lib/components/Divider.svelte"
|
import Divider from "@lib/components/Divider.svelte"
|
||||||
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
||||||
import ChatEnable from "@app/components/ChatEnable.svelte"
|
import ChatEnable from "@app/components/ChatEnable.svelte"
|
||||||
import MenuOtherSpaces from "@app/components/MenuOtherSpaces.svelte"
|
|
||||||
import MenuSettings from "@app/components/MenuSettings.svelte"
|
import MenuSettings from "@app/components/MenuSettings.svelte"
|
||||||
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
||||||
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
|
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
|
||||||
@@ -28,8 +27,6 @@
|
|||||||
|
|
||||||
const {children}: Props = $props()
|
const {children}: Props = $props()
|
||||||
|
|
||||||
const showOtherSpacesMenu = () => pushModal(MenuOtherSpaces, {urls: secondarySpaceUrls})
|
|
||||||
|
|
||||||
const showSettingsMenu = () => pushModal(MenuSettings)
|
const showSettingsMenu = () => pushModal(MenuSettings)
|
||||||
|
|
||||||
const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
|
const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
|
||||||
@@ -60,15 +57,13 @@
|
|||||||
{#each primarySpaceUrls as url (url)}
|
{#each primarySpaceUrls as url (url)}
|
||||||
<PrimaryNavItemSpace {url} />
|
<PrimaryNavItemSpace {url} />
|
||||||
{/each}
|
{/each}
|
||||||
{#if secondarySpaceUrls.length > 0}
|
<PrimaryNavItem
|
||||||
<PrimaryNavItem
|
href="/spaces"
|
||||||
title="Other Spaces"
|
title="All Spaces"
|
||||||
class="tooltip-right"
|
class="tooltip-right"
|
||||||
onclick={showOtherSpacesMenu}
|
notification={otherSpaceNotifications}>
|
||||||
notification={otherSpaceNotifications}>
|
<ImageIcon alt="All Spaces" src={Widget} size={8} />
|
||||||
<ImageIcon alt="Other Spaces" src={Widget} size={8} />
|
</PrimaryNavItem>
|
||||||
</PrimaryNavItem>
|
|
||||||
{/if}
|
|
||||||
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
|
||||||
<ImageIcon alt="Add a Space" src={Compass} size={8} />
|
<ImageIcon alt="Add a Space" src={Compass} size={8} />
|
||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
makeList,
|
makeList,
|
||||||
addToListPublicly,
|
addToListPublicly,
|
||||||
removeFromListByPredicate,
|
removeFromListByPredicate,
|
||||||
|
updateList,
|
||||||
getTag,
|
getTag,
|
||||||
getListTags,
|
getListTags,
|
||||||
getRelayTagValues,
|
getRelayTagValues,
|
||||||
@@ -148,6 +149,20 @@ export const removeSpaceMembership = async (url: string) => {
|
|||||||
return publishThunk({event, relays})
|
return publishThunk({event, relays})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setSpaceMembershipOrder = async (urls: string[]) => {
|
||||||
|
const list = get(userGroupList) || makeList({kind: ROOMS})
|
||||||
|
const orderedUrls = uniq(urls.map(normalizeRelayUrl))
|
||||||
|
const relayTags = list.publicTags.filter(t => t[0] === "r")
|
||||||
|
const otherPublicTags = list.publicTags.filter(t => t[0] !== "r")
|
||||||
|
const relayTagByUrl = new Map(relayTags.map(t => [normalizeRelayUrl(t[1]), t]))
|
||||||
|
const orderedRelayTags = orderedUrls.map(url => relayTagByUrl.get(url) || ["r", url])
|
||||||
|
const publicTags = [...orderedRelayTags, ...otherPublicTags]
|
||||||
|
const event = await updateList(list, {publicTags}).reconcile(nip44EncryptToSelf)
|
||||||
|
const relays = uniq([...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
|
||||||
|
|
||||||
|
return publishThunk({event, relays})
|
||||||
|
}
|
||||||
|
|
||||||
export const addRoomMembership = async (url: string, h: string) => {
|
export const addRoomMembership = async (url: string, h: string) => {
|
||||||
const list = get(userGroupList) || makeList({kind: ROOMS})
|
const list = get(userGroupList) || makeList({kind: ROOMS})
|
||||||
const newTags = [
|
const newTags = [
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {insertAt, removeAt} from "@welshman/lib"
|
||||||
import SettingsMinimalistic from "@assets/icons/settings-minimalistic.svg?dataurl"
|
import SettingsMinimalistic from "@assets/icons/settings-minimalistic.svg?dataurl"
|
||||||
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
@@ -9,9 +10,88 @@
|
|||||||
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
|
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
|
||||||
import SpaceAdd from "@app/components/SpaceAdd.svelte"
|
import SpaceAdd from "@app/components/SpaceAdd.svelte"
|
||||||
import {userSpaceUrls, loadUserGroupList, PLATFORM_RELAYS} from "@app/core/state"
|
import {userSpaceUrls, loadUserGroupList, PLATFORM_RELAYS} from "@app/core/state"
|
||||||
|
import {setSpaceMembershipOrder} from "@app/core/commands"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
|
|
||||||
const addSpace = () => pushModal(SpaceAdd)
|
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 reorderSpaceUrls = (targetUrl: string) => {
|
||||||
|
if (!draggedUrl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceIndex = orderedSpaceUrls.indexOf(draggedUrl)
|
||||||
|
const targetIndex = orderedSpaceUrls.indexOf(targetUrl)
|
||||||
|
|
||||||
|
if (sourceIndex === -1 || targetIndex === -1 || sourceIndex === targetIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderedSpaceUrls = insertAt(
|
||||||
|
targetIndex,
|
||||||
|
orderedSpaceUrls[sourceIndex],
|
||||||
|
removeAt(sourceIndex, orderedSpaceUrls),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragStart = (e: DragEvent, url: string) => {
|
||||||
|
draggedUrl = url
|
||||||
|
dragStartOrder = [...orderedSpaceUrls]
|
||||||
|
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.effectAllowed = "move"
|
||||||
|
e.dataTransfer.setData("text/plain", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragOver = (e: DragEvent, targetUrl: string) => {
|
||||||
|
e.preventDefault()
|
||||||
|
reorderSpaceUrls(targetUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDrop = (e: DragEvent, targetUrl: string) => {
|
||||||
|
e.preventDefault()
|
||||||
|
reorderSpaceUrls(targetUrl)
|
||||||
|
draggedUrl = undefined
|
||||||
|
|
||||||
|
if (dragStartOrder && !isSameOrder(dragStartOrder, orderedSpaceUrls)) {
|
||||||
|
void setSpaceMembershipOrder(orderedSpaceUrls).catch(console.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
dragStartOrder = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragEnd = () => {
|
||||||
|
draggedUrl = undefined
|
||||||
|
dragStartOrder = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const nextUrls = reconcileUrls(orderedSpaceUrls, $userSpaceUrls)
|
||||||
|
|
||||||
|
if (!isSameOrder(nextUrls, orderedSpaceUrls)) {
|
||||||
|
orderedSpaceUrls = nextUrls
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let orderedSpaceUrls = $state<string[]>([])
|
||||||
|
let draggedUrl = $state<string | undefined>()
|
||||||
|
let dragStartOrder = $state<string[] | undefined>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Page class="cw-full">
|
<Page class="cw-full">
|
||||||
@@ -43,8 +123,17 @@
|
|||||||
Loading your spaces...
|
Loading your spaces...
|
||||||
</div>
|
</div>
|
||||||
{:then}
|
{:then}
|
||||||
{#each $userSpaceUrls as url (url)}
|
{#each orderedSpaceUrls as url (url)}
|
||||||
<MenuSpacesItem {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}
|
{:else}
|
||||||
<div class="flex flex-col gap-8 items-center py-20">
|
<div class="flex flex-col gap-8 items-center py-20">
|
||||||
<p>You haven't added any spaces yet!</p>
|
<p>You haven't added any spaces yet!</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user