forked from coracle/flotilla
Spruce up join button on spaces page
This commit is contained in:
+2
-34
@@ -37,37 +37,5 @@ export const updateList = async (kind: number, modifyTags: ModifyTags) => {
|
|||||||
await publish({event, relays})
|
await publish({event, relays})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const joinGroup = async (id: string) => {
|
export const updateGroupMemberships = (newTags: string[][]) =>
|
||||||
const [url, nom] = splitGroupId(id)
|
updateList(GROUPS, (tags: string[][]) => uniqBy(t => t.join(''), [...tags, ...newTags]))
|
||||||
const relay = await loadRelay(url)
|
|
||||||
|
|
||||||
if (!relay) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, we weren't able to find that relay."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relay.supported_nips?.includes(29)) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, it looks like that relay doesn't support nostr spaces."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = await loadGroup(nom, [url])
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, we weren't able to find that space."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateList(GROUPS, (tags: string[][]) => uniqBy(t => t.join(''), append(["group", nom, url], tags)))
|
|
||||||
|
|
||||||
goto(`/spaces/${nom}`)
|
|
||||||
pushToast({
|
|
||||||
message: "Welcome to the space!"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,17 +11,52 @@
|
|||||||
import {pushModal} from '@app/modal'
|
import {pushModal} from '@app/modal'
|
||||||
import {pushToast} from '@app/toast'
|
import {pushToast} from '@app/toast'
|
||||||
import {GROUP_DELIMITER, splitGroupId, loadRelay, loadGroup} from '@app/state'
|
import {GROUP_DELIMITER, splitGroupId, loadRelay, loadGroup} from '@app/state'
|
||||||
import {joinGroup} from '@app/commands'
|
import {updateGroupMemberships} from '@app/commands'
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
const browse = () => goto("/browse", {state: {}})
|
const browse = () => goto("/spaces")
|
||||||
|
|
||||||
|
const joinQualifiedGroup = async (id: string) => {
|
||||||
|
const [url, nom] = splitGroupId(id)
|
||||||
|
const relay = await loadRelay(url)
|
||||||
|
|
||||||
|
if (!relay) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, we weren't able to find that relay."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!relay.supported_nips?.includes(29)) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, it looks like that relay doesn't support nostr spaces."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = await loadGroup(nom, [url])
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, we weren't able to find that space."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateGroupMemberships([["group", nom, url]])
|
||||||
|
|
||||||
|
goto(`/spaces/${nom}`)
|
||||||
|
pushToast({
|
||||||
|
message: "Welcome to the space!"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const join = async () => {
|
const join = async () => {
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await joinGroup(id)
|
await joinQualifiedGroup(id)
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type {Page} from '@sveltejs/kit'
|
import type {Page} from '@sveltejs/kit'
|
||||||
import {userGroupsByNom} from '@app/state'
|
import {userGroupsByNom} from '@app/state'
|
||||||
|
|
||||||
|
export const makeSpacePath = (nom: string) => `/spaces/${nom}`
|
||||||
|
|
||||||
export const getPrimaryNavItem = ($page: Page) => {
|
export const getPrimaryNavItem = ($page: Page) => {
|
||||||
if ($page.route?.id?.match('^/(spaces|themes)$')) return 'discover'
|
if ($page.route?.id?.match('^/(spaces|themes)$')) return 'discover'
|
||||||
if ($page.route?.id?.startsWith('/spaces')) return 'space'
|
if ($page.route?.id?.startsWith('/spaces')) return 'space'
|
||||||
|
|||||||
+84
-29
@@ -1,6 +1,6 @@
|
|||||||
import type {Readable} from "svelte/store"
|
import type {Readable} from "svelte/store"
|
||||||
import type {FuseResult} from 'fuse.js'
|
import type {FuseResult} from 'fuse.js'
|
||||||
import {writable, readable, derived} from "svelte/store"
|
import {get, writable, readable, derived} from "svelte/store"
|
||||||
import type {Maybe} from "@welshman/lib"
|
import type {Maybe} from "@welshman/lib"
|
||||||
import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib"
|
import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib"
|
||||||
import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util"
|
import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util"
|
||||||
@@ -138,7 +138,6 @@ export const {
|
|||||||
getIndex: getRelaysByUrl,
|
getIndex: getRelaysByUrl,
|
||||||
deriveItem: deriveRelay,
|
deriveItem: deriveRelay,
|
||||||
loadItem: loadRelay,
|
loadItem: loadRelay,
|
||||||
// getItem: getRelay,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'relays',
|
name: 'relays',
|
||||||
store: relays,
|
store: relays,
|
||||||
@@ -168,7 +167,6 @@ export const {
|
|||||||
getIndex: getHandlesByNip05,
|
getIndex: getHandlesByNip05,
|
||||||
deriveItem: deriveHandle,
|
deriveItem: deriveHandle,
|
||||||
loadItem: loadHandle,
|
loadItem: loadHandle,
|
||||||
// getItem: getHandle,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'handles',
|
name: 'handles',
|
||||||
store: handles,
|
store: handles,
|
||||||
@@ -191,8 +189,7 @@ export const {
|
|||||||
|
|
||||||
// Profiles
|
// Profiles
|
||||||
|
|
||||||
export const profiles = deriveEventsMapped<PublishedProfile>({
|
export const profiles = deriveEventsMapped<PublishedProfile>(repository, {
|
||||||
repository,
|
|
||||||
filters: [{kinds: [PROFILE]}],
|
filters: [{kinds: [PROFILE]}],
|
||||||
eventToItem: readProfile,
|
eventToItem: readProfile,
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
@@ -203,7 +200,6 @@ export const {
|
|||||||
getIndex: getProfilesByPubkey,
|
getIndex: getProfilesByPubkey,
|
||||||
deriveItem: deriveProfile,
|
deriveItem: deriveProfile,
|
||||||
loadItem: loadProfile,
|
loadItem: loadProfile,
|
||||||
// getItem: getProfile,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'profiles',
|
name: 'profiles',
|
||||||
store: profiles,
|
store: profiles,
|
||||||
@@ -224,14 +220,13 @@ export const getReadRelayUrls = (event?: CustomEvent): string[] =>
|
|||||||
export const getWriteRelayUrls = (event?: CustomEvent): string[] =>
|
export const getWriteRelayUrls = (event?: CustomEvent): string[] =>
|
||||||
getRelayTags(event?.tags || []).filter((t: string[]) => !t[2] || t[2] === 'write').map((t: string[]) => normalizeRelayUrl(t[1]))
|
getRelayTags(event?.tags || []).filter((t: string[]) => !t[2] || t[2] === 'write').map((t: string[]) => normalizeRelayUrl(t[1]))
|
||||||
|
|
||||||
export const relaySelections = deriveEvents({repository, filters: [{kinds: [RELAYS]}]})
|
export const relaySelections = deriveEvents(repository, {filters: [{kinds: [RELAYS]}]})
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
indexStore: relaySelectionsByPubkey,
|
indexStore: relaySelectionsByPubkey,
|
||||||
getIndex: getRelaySelectionsByPubkey,
|
getIndex: getRelaySelectionsByPubkey,
|
||||||
deriveItem: deriveRelaySelections,
|
deriveItem: deriveRelaySelections,
|
||||||
loadItem: loadRelaySelections,
|
loadItem: loadRelaySelections,
|
||||||
// getItem: getRelaySelections,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'relaySelections',
|
name: 'relaySelections',
|
||||||
store: relaySelections,
|
store: relaySelections,
|
||||||
@@ -246,8 +241,7 @@ export const {
|
|||||||
|
|
||||||
// Follows
|
// Follows
|
||||||
|
|
||||||
export const follows = deriveEventsMapped<PublishedList>({
|
export const follows = deriveEventsMapped<PublishedList>(repository, {
|
||||||
repository,
|
|
||||||
filters: [{kinds: [FOLLOWS]}],
|
filters: [{kinds: [FOLLOWS]}],
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
eventToItem: async (event: CustomEvent) =>
|
eventToItem: async (event: CustomEvent) =>
|
||||||
@@ -263,7 +257,6 @@ export const {
|
|||||||
getIndex: getFollowsByPubkey,
|
getIndex: getFollowsByPubkey,
|
||||||
deriveItem: deriveFollows,
|
deriveItem: deriveFollows,
|
||||||
loadItem: loadFollows,
|
loadItem: loadFollows,
|
||||||
// getItem: getFollows,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'follows',
|
name: 'follows',
|
||||||
store: follows,
|
store: follows,
|
||||||
@@ -278,8 +271,7 @@ export const {
|
|||||||
|
|
||||||
// Mutes
|
// Mutes
|
||||||
|
|
||||||
export const mutes = deriveEventsMapped<PublishedList>({
|
export const mutes = deriveEventsMapped<PublishedList>(repository, {
|
||||||
repository,
|
|
||||||
filters: [{kinds: [MUTES]}],
|
filters: [{kinds: [MUTES]}],
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
eventToItem: async (event: CustomEvent) =>
|
eventToItem: async (event: CustomEvent) =>
|
||||||
@@ -295,7 +287,6 @@ export const {
|
|||||||
getIndex: getMutesByPubkey,
|
getIndex: getMutesByPubkey,
|
||||||
deriveItem: deriveMutes,
|
deriveItem: deriveMutes,
|
||||||
loadItem: loadMutes,
|
loadItem: loadMutes,
|
||||||
// getItem: getMutes,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'mutes',
|
name: 'mutes',
|
||||||
store: mutes,
|
store: mutes,
|
||||||
@@ -350,8 +341,7 @@ export const readGroup = (event: CustomEvent) => {
|
|||||||
return {nom, name, about, picture, event}
|
return {nom, name, about, picture, event}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const groups = deriveEventsMapped<PublishedGroup>({
|
export const groups = deriveEventsMapped<PublishedGroup>(repository, {
|
||||||
repository,
|
|
||||||
filters: [{kinds: [GROUP_META]}],
|
filters: [{kinds: [GROUP_META]}],
|
||||||
eventToItem: readGroup,
|
eventToItem: readGroup,
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
@@ -362,7 +352,6 @@ export const {
|
|||||||
getIndex: getGroupsByNom,
|
getIndex: getGroupsByNom,
|
||||||
deriveItem: deriveGroup,
|
deriveItem: deriveGroup,
|
||||||
loadItem: loadGroup,
|
loadItem: loadGroup,
|
||||||
// getItem: getGroup,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'groups',
|
name: 'groups',
|
||||||
store: groups,
|
store: groups,
|
||||||
@@ -412,6 +401,18 @@ export const qualifiedGroups = derived([relaysByPubkey, groups], ([$relaysByPubk
|
|||||||
|
|
||||||
export const qualifiedGroupsById = derived(qualifiedGroups, $qualifiedGroups => indexBy($qg => $qg.id, $qualifiedGroups))
|
export const qualifiedGroupsById = derived(qualifiedGroups, $qualifiedGroups => indexBy($qg => $qg.id, $qualifiedGroups))
|
||||||
|
|
||||||
|
export const qualifiedGroupsByNom = derived(qualifiedGroups, $qualifiedGroups => groupBy($qg => $qg.group.nom, $qualifiedGroups))
|
||||||
|
|
||||||
|
export const relayUrlsByNom = derived(qualifiedGroups, $qualifiedGroups => {
|
||||||
|
const $relayUrlsByNom = new Map()
|
||||||
|
|
||||||
|
for (const {relay, group} of $qualifiedGroups) {
|
||||||
|
pushToMapKey($relayUrlsByNom, group.nom, relay.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return $relayUrlsByNom
|
||||||
|
})
|
||||||
|
|
||||||
// Group membership
|
// Group membership
|
||||||
|
|
||||||
export type GroupMembership = {
|
export type GroupMembership = {
|
||||||
@@ -439,8 +440,7 @@ export const readGroupMembership = (event: CustomEvent) => {
|
|||||||
return {event, ids, noms, urls}
|
return {event, ids, noms, urls}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const groupMemberships = deriveEventsMapped<PublishedGroupMembership>({
|
export const groupMemberships = deriveEventsMapped<PublishedGroupMembership>(repository, {
|
||||||
repository,
|
|
||||||
filters: [{kinds: [GROUPS]}],
|
filters: [{kinds: [GROUPS]}],
|
||||||
eventToItem: readGroupMembership,
|
eventToItem: readGroupMembership,
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
@@ -451,17 +451,72 @@ export const {
|
|||||||
getIndex: getGroupMembersipByPubkey,
|
getIndex: getGroupMembersipByPubkey,
|
||||||
deriveItem: deriveGroupMembership,
|
deriveItem: deriveGroupMembership,
|
||||||
loadItem: loadGroupMembership,
|
loadItem: loadGroupMembership,
|
||||||
// getItem: getGroupMembership,
|
|
||||||
} = createCollection({
|
} = createCollection({
|
||||||
name: 'groupMemberships',
|
name: 'groupMemberships',
|
||||||
store: groupMemberships,
|
store: groupMemberships,
|
||||||
getKey: groupMembership => groupMembership.event.pubkey,
|
getKey: groupMembership => groupMembership.event.pubkey,
|
||||||
load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
|
load: (pubkey: string, relays = [], request: Partial<SubscribeRequest> = {}) =>
|
||||||
load({
|
load({
|
||||||
...request,
|
...request,
|
||||||
relays: [...relays, ...INDEXER_RELAYS],
|
relays: [...relays, ...INDEXER_RELAYS],
|
||||||
filters: [{kinds: [GROUPS], authors: [pubkey]}],
|
filters: [{kinds: [GROUPS], authors: [pubkey]}],
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Group Messages
|
||||||
|
|
||||||
|
export type GroupMessage = {
|
||||||
|
nom: string
|
||||||
|
event: CustomEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
export const readGroupMessage = (event: CustomEvent): Maybe<GroupMessage> => {
|
||||||
|
const nom = event.tags.find(nthEq(0, 'h'))?.[1]
|
||||||
|
|
||||||
|
if (!nom) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return {nom, event}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const groupMessages = deriveEventsMapped<GroupMessage>(repository, {
|
||||||
|
filters: [{}],
|
||||||
|
eventToItem: readGroupMessage,
|
||||||
|
itemToEvent: item => item.event,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Group Conversations
|
||||||
|
|
||||||
|
export type GroupConversation = {
|
||||||
|
nom: string
|
||||||
|
messages: GroupMessage[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const groupConversations = derived(groupMessages, $groupMessages => {
|
||||||
|
const groupMessagesByNom = groupBy($groupMessage => $groupMessage.nom, $groupMessages)
|
||||||
|
|
||||||
|
return Array.from(groupMessagesByNom.entries()).map(([nom, messages]) => ({nom, messages}))
|
||||||
|
})
|
||||||
|
|
||||||
|
export const {
|
||||||
|
indexStore: groupConversationByNom,
|
||||||
|
getIndex: getGroupMembersipByNom,
|
||||||
|
deriveItem: deriveGroupConversation,
|
||||||
|
loadItem: loadGroupConversation,
|
||||||
|
} = createCollection({
|
||||||
|
name: 'groupConversations',
|
||||||
|
store: groupConversations,
|
||||||
|
getKey: groupConversation => groupConversation.nom,
|
||||||
|
load: (nom: string, hints = [], request: Partial<SubscribeRequest> = {}) => {
|
||||||
|
const relays = [...hints, ...get(relayUrlsByNom).get(nom) || []]
|
||||||
|
|
||||||
|
if (relays.length === 0) {
|
||||||
|
console.warn(`Attempted to load conversation for ${nom} with no qualified groups`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return load({...request, relays, filters: [{'#h': [nom]}]})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// User stuff
|
// User stuff
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="12" cy="12" r="10" stroke="#1C274C" stroke-width="1.5"/>
|
||||||
|
<path d="M8.5 12.5L10.5 14.5L15.5 9.5" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 296 B |
@@ -12,6 +12,7 @@
|
|||||||
import AddCircle from "@assets/icons/Add Circle.svg?dataurl"
|
import AddCircle from "@assets/icons/Add Circle.svg?dataurl"
|
||||||
import AltArrowRight from "@assets/icons/Alt Arrow Right.svg?dataurl"
|
import AltArrowRight from "@assets/icons/Alt Arrow Right.svg?dataurl"
|
||||||
import AltArrowLeft from "@assets/icons/Alt Arrow Left.svg?dataurl"
|
import AltArrowLeft from "@assets/icons/Alt Arrow Left.svg?dataurl"
|
||||||
|
import CheckCircle from "@assets/icons/Check Circle.svg?dataurl"
|
||||||
import ClipboardText from "@assets/icons/Clipboard Text.svg?dataurl"
|
import ClipboardText from "@assets/icons/Clipboard Text.svg?dataurl"
|
||||||
import CloseCircle from "@assets/icons/Close Circle.svg?dataurl"
|
import CloseCircle from "@assets/icons/Close Circle.svg?dataurl"
|
||||||
import Copy from "@assets/icons/Copy.svg?dataurl"
|
import Copy from "@assets/icons/Copy.svg?dataurl"
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
"add-circle": AddCircle,
|
"add-circle": AddCircle,
|
||||||
"alt-arrow-right": AltArrowRight,
|
"alt-arrow-right": AltArrowRight,
|
||||||
"alt-arrow-left": AltArrowLeft,
|
"alt-arrow-left": AltArrowLeft,
|
||||||
|
"check-circle": CheckCircle,
|
||||||
"clipboard-text": ClipboardText,
|
"clipboard-text": ClipboardText,
|
||||||
"close-circle": CloseCircle,
|
"close-circle": CloseCircle,
|
||||||
copy: Copy,
|
copy: Copy,
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {goto} from '$app/navigation'
|
||||||
import CardButton from "@lib/components/CardButton.svelte"
|
import CardButton from "@lib/components/CardButton.svelte"
|
||||||
import SpaceCreate from '@app/components/SpaceCreate.svelte'
|
import SpaceCreate from '@app/components/SpaceCreate.svelte'
|
||||||
import {pushModal} from '@app/modal'
|
import {pushModal} from '@app/modal'
|
||||||
|
|
||||||
export const createSpace = () => pushModal(SpaceCreate)
|
const createSpace = () => pushModal(SpaceCreate)
|
||||||
|
|
||||||
|
const browseSpaces = () => goto("/spaces")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="hero min-h-screen bg-base-200">
|
<div class="hero min-h-screen bg-base-200">
|
||||||
@@ -15,7 +18,7 @@
|
|||||||
<CardButton icon="add-circle" title="Create a space" class="h-24" on:click={createSpace}>
|
<CardButton icon="add-circle" title="Create a space" class="h-24" on:click={createSpace}>
|
||||||
Invite all your friends, do life together.
|
Invite all your friends, do life together.
|
||||||
</CardButton>
|
</CardButton>
|
||||||
<CardButton icon="compass" title="Discover spaces" class="h-24">
|
<CardButton icon="compass" title="Discover spaces" class="h-24" on:click={browseSpaces}>
|
||||||
Find a community based on your hobbies or interests.
|
Find a community based on your hobbies or interests.
|
||||||
</CardButton>
|
</CardButton>
|
||||||
<CardButton icon="plain" title="Leave feedback" class="h-24">
|
<CardButton icon="plain" title="Leave feedback" class="h-24">
|
||||||
|
|||||||
@@ -1,11 +1,29 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from 'svelte'
|
import {onMount} from 'svelte'
|
||||||
import Masonry from 'svelte-bricks'
|
import Masonry from 'svelte-bricks'
|
||||||
import {GROUP_META} from '@welshman/util'
|
import {append, remove} from '@welshman/lib'
|
||||||
|
import {GROUP_META, displayRelayUrl} from '@welshman/util'
|
||||||
import Icon from '@lib/components/Icon.svelte'
|
import Icon from '@lib/components/Icon.svelte'
|
||||||
import {load, relays, groups, searchGroups} from '@app/state'
|
import Button from '@lib/components/Button.svelte'
|
||||||
|
import Spinner from '@lib/components/Spinner.svelte'
|
||||||
|
import {makeSpacePath} from '@app/routes'
|
||||||
|
import {load, relays, groups, searchGroups, relayUrlsByNom, userMembership} from '@app/state'
|
||||||
|
import {updateGroupMemberships} from '@app/commands'
|
||||||
|
|
||||||
|
const getRelayUrls = (nom: string): string[] => $relayUrlsByNom.get(nom) || []
|
||||||
|
|
||||||
|
const join = async (nom: string) => {
|
||||||
|
loading = append(nom, loading)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateGroupMemberships(getRelayUrls(nom).map(url => ["group", nom, url]))
|
||||||
|
} finally {
|
||||||
|
loading = remove(nom, loading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let term = ""
|
let term = ""
|
||||||
|
let loading: string[] = []
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
load({
|
load({
|
||||||
@@ -22,22 +40,40 @@
|
|||||||
<Icon icon="magnifer" />
|
<Icon icon="magnifer" />
|
||||||
<input bind:value={term} class="grow" type="text" placeholder="Search for spaces..." />
|
<input bind:value={term} class="grow" type="text" placeholder="Search for spaces..." />
|
||||||
</label>
|
</label>
|
||||||
<Masonry animate={false} items={$searchGroups.searchOptions(term)} minColWidth={250} maxColWidth={800} gap={16} idKey="nom" let:item>
|
<Masonry animate={false} items={$searchGroups.searchOptions(term)} minColWidth={250} maxColWidth={800} gap={16} idKey="nom" let:item={group}>
|
||||||
<div class="card bg-base-100 shadow-xl">
|
<div class="card bg-base-100 shadow-xl">
|
||||||
<div class="avatar center mt-8">
|
<div class="avatar center mt-8">
|
||||||
<div class="w-20 rounded-full bg-base-300 border-2 border-solid border-base-300 !flex center">
|
<div class="w-20 rounded-full bg-base-300 border-2 border-solid border-base-300 !flex center">
|
||||||
{#if item?.picture}
|
{#if group?.picture}
|
||||||
<img alt="" src={item.picture} />
|
<img alt="" src={group.picture} />
|
||||||
{:else}
|
{:else}
|
||||||
<Icon icon="ghost" size={7} />
|
<Icon icon="ghost" size={7} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title justify-center">{item.name}</h2>
|
<a href={makeSpacePath(group.nom)}>
|
||||||
<p class="text-sm py-4">{item.about}</p>
|
<h2 class="card-title justify-center">{group.name}</h2>
|
||||||
|
</a>
|
||||||
|
<div class="text-sm text-center">
|
||||||
|
{#each getRelayUrls(group.nom) as url}
|
||||||
|
<div class="badge badge-neutral">{displayRelayUrl(url)}</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<p class="text-sm py-4">{group.about}</p>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<button class="btn btn-primary w-full">Join Space</button>
|
<Button
|
||||||
|
class="btn btn-primary w-full"
|
||||||
|
disabled={loading.includes(group.nom) || $userMembership?.noms.has(group.nom)}
|
||||||
|
on:click={() => join(group.nom)}>
|
||||||
|
{#if $userMembership?.noms.has(group.nom)}
|
||||||
|
<Icon icon="check-circle" />
|
||||||
|
Joined
|
||||||
|
{:else}
|
||||||
|
<Spinner loading={loading.includes(group.nom)} />
|
||||||
|
Join Space
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let nom
|
import {page} from '$app/stores'
|
||||||
|
import {deriveGroup, deriveGroupConversation} from '@app/state'
|
||||||
|
|
||||||
|
const group = deriveGroup($page.params.nom)
|
||||||
|
const conversation = deriveGroupConversation($page.params.nom)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{nom}
|
<div class="h-screen flex flex-col">
|
||||||
|
<div class="min-h-32 bg-base-100">
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow overflow-auto">
|
||||||
|
</div>
|
||||||
|
<div class="min-h-32 bg-base-100">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<div class="content column gap-4">
|
<div class="content column gap-4">
|
||||||
<h1 class="superheading mt-20">Discover Themes</h1>
|
<h1 class="superheading mt-20">Discover Themes</h1>
|
||||||
<p class="text-center">Make Flotilla look just how you like it</p>
|
<p class="text-center">Make your community feel like home</p>
|
||||||
<label class="input input-bordered w-full flex items-center gap-2">
|
<label class="input input-bordered w-full flex items-center gap-2">
|
||||||
<Icon icon="magnifer" />
|
<Icon icon="magnifer" />
|
||||||
<input bind:value={term} class="grow" type="text" placeholder="Search for themes..." />
|
<input bind:value={term} class="grow" type="text" placeholder="Search for themes..." />
|
||||||
|
|||||||
Reference in New Issue
Block a user