From 5684d1a9cfa0016227de55187c864267b5491c14 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Thu, 6 Feb 2025 09:29:30 -0800 Subject: [PATCH] Add calendar actions, menus, etc --- .../components/CalendarEventActions.svelte | 96 +++++++++++++++ ...eate.svelte => CalendarEventCreate.svelte} | 0 ...ntItem.svelte => CalendarEventItem.svelte} | 10 +- src/app/components/CalendarEventMenu.svelte | 67 ++++++++++ src/app/components/CalendarEventShare.svelte | 66 ++++++++++ src/app/components/ThreadActions.svelte | 4 +- src/app/notifications.ts | 36 +++++- src/app/routes.ts | 22 ++-- src/routes/spaces/[relay]/+page.svelte | 17 ++- .../spaces/[relay]/calendar/+page.svelte | 8 +- .../spaces/[relay]/calendar/[id]/+page.svelte | 114 ++++++++++++++++++ 11 files changed, 416 insertions(+), 24 deletions(-) create mode 100644 src/app/components/CalendarEventActions.svelte rename src/app/components/{EventCreate.svelte => CalendarEventCreate.svelte} (100%) rename src/app/components/{EventItem.svelte => CalendarEventItem.svelte} (83%) create mode 100644 src/app/components/CalendarEventMenu.svelte create mode 100644 src/app/components/CalendarEventShare.svelte create mode 100644 src/routes/spaces/[relay]/calendar/[id]/+page.svelte diff --git a/src/app/components/CalendarEventActions.svelte b/src/app/components/CalendarEventActions.svelte new file mode 100644 index 00000000..56cbeac6 --- /dev/null +++ b/src/app/components/CalendarEventActions.svelte @@ -0,0 +1,96 @@ + + +
+
+ + {#if $deleted} +
Deleted
+ {:else if thunk} + + {/if} + {#if showActivity} +
+ + {$replies.length} {$replies.length === 1 ? "reply" : "replies"} +
+ + {/if} + + + +
+
diff --git a/src/app/components/EventCreate.svelte b/src/app/components/CalendarEventCreate.svelte similarity index 100% rename from src/app/components/EventCreate.svelte rename to src/app/components/CalendarEventCreate.svelte diff --git a/src/app/components/EventItem.svelte b/src/app/components/CalendarEventItem.svelte similarity index 83% rename from src/app/components/EventItem.svelte rename to src/app/components/CalendarEventItem.svelte index 91f3ef37..03a29dd2 100644 --- a/src/app/components/EventItem.svelte +++ b/src/app/components/CalendarEventItem.svelte @@ -3,11 +3,13 @@ import {formatTimestamp, formatTimestampAsDate, formatTimestampAsTime} from "@welshman/app" import {preventDefault} from "@lib/html" import Icon from "@lib/components/Icon.svelte" + import Link from "@lib/components/Link.svelte" import Button from "@lib/components/Button.svelte" import Content from "@app/components/Content.svelte" - import ThreadActions from "@app/components/ThreadActions.svelte" + import CalendarEventActions from "@app/components/CalendarEventActions.svelte" import ProfileName from "@app/components/ProfileName.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte" + import {makeCalendarPath} from "@app/routes" import {pushModal} from "@app/modal" const {url, event} = $props() @@ -22,7 +24,7 @@ const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey}) -
+
{meta.title || meta.name}
@@ -40,6 +42,6 @@ @ - +
-
+ diff --git a/src/app/components/CalendarEventMenu.svelte b/src/app/components/CalendarEventMenu.svelte new file mode 100644 index 00000000..85ada53c --- /dev/null +++ b/src/app/components/CalendarEventMenu.svelte @@ -0,0 +1,67 @@ + + + diff --git a/src/app/components/CalendarEventShare.svelte b/src/app/components/CalendarEventShare.svelte new file mode 100644 index 00000000..6b7d3e4b --- /dev/null +++ b/src/app/components/CalendarEventShare.svelte @@ -0,0 +1,66 @@ + + +
+ + {#snippet title()} +
Share Event
+ {/snippet} + {#snippet info()} +
Which room would you like to share this event to?
+ {/snippet} +
+
+ {#each $channelsByUrl.get(url) || [] as channel (channel.room)} + + {/each} +
+ + + + +
diff --git a/src/app/components/ThreadActions.svelte b/src/app/components/ThreadActions.svelte index 60af31b1..250ec677 100644 --- a/src/app/components/ThreadActions.svelte +++ b/src/app/components/ThreadActions.svelte @@ -16,7 +16,7 @@ import ThreadMenu from "@app/components/ThreadMenu.svelte" import {publishDelete, publishReaction} from "@app/commands" import {notifications} from "@app/notifications" - import {makeSpacePath} from "@app/routes" + import {makeThreadPath} from "@app/routes" interface Props { url: any @@ -28,7 +28,7 @@ const thunk = $derived($thunks[event.id]) const deleted = deriveIsDeleted(repository, event) - const path = makeSpacePath(url, "threads", event.id) + const path = makeThreadPath(url, event.id) const filters = [{kinds: [COMMENT], "#E": [event.id]}] const replies = deriveEvents(repository, {filters}) diff --git a/src/app/notifications.ts b/src/app/notifications.ts index 5d7a79b6..c583c80f 100644 --- a/src/app/notifications.ts +++ b/src/app/notifications.ts @@ -3,8 +3,14 @@ import {synced, throttled} from "@welshman/store" import {pubkey} from "@welshman/app" import {prop, spec, identity, now, groupBy} from "@welshman/lib" import type {TrustedEvent} from "@welshman/util" -import {MESSAGE, THREAD, COMMENT, getTagValue} from "@welshman/util" -import {makeSpacePath, makeChatPath, makeThreadPath, makeRoomPath} from "@app/routes" +import {EVENT_TIME, MESSAGE, THREAD, COMMENT, getTagValue} from "@welshman/util" +import { + makeSpacePath, + makeChatPath, + makeThreadPath, + makeCalendarPath, + makeRoomPath, +} from "@app/routes" import {chats, getUrlsForEvent, userRoomsByUrl, repositoryStore} from "@app/state" // Checked state @@ -57,18 +63,31 @@ export const notifications = derived( {kinds: [THREAD]}, {kinds: [COMMENT], "#K": [String(THREAD)]}, ]) + + const allCalendarEvents = $repository.query([ + {kinds: [EVENT_TIME]}, + {kinds: [COMMENT], "#K": [String(EVENT_TIME)]}, + ]) + const allMessageEvents = $repository.query([{kinds: [MESSAGE]}]) for (const [url, rooms] of $userRoomsByUrl.entries()) { const spacePath = makeSpacePath(url) const threadPath = makeThreadPath(url) + const calendarPath = makeCalendarPath(url) const threadEvents = allThreadEvents.filter(e => $getUrlsForEvent(e.id).includes(url)) + const calendarEvents = allCalendarEvents.filter(e => $getUrlsForEvent(e.id).includes(url)) if (hasNotification(threadPath, threadEvents[0])) { paths.add(spacePath) paths.add(threadPath) } + if (hasNotification(calendarPath, calendarEvents[0])) { + paths.add(spacePath) + paths.add(calendarPath) + } + const commentsByThreadId = groupBy( e => getTagValue("E", e.tags), threadEvents.filter(spec({kind: COMMENT})), @@ -82,6 +101,19 @@ export const notifications = derived( } } + const commentsByEventId = groupBy( + e => getTagValue("E", e.tags), + calendarEvents.filter(spec({kind: COMMENT})), + ) + + for (const [eventId, [comment]] of commentsByEventId.entries()) { + const calendarEventPath = makeCalendarPath(url, eventId) + + if (hasNotification(calendarEventPath, comment)) { + paths.add(calendarEventPath) + } + } + for (const room of rooms) { const roomPath = makeRoomPath(url, room) const latestEvent = allMessageEvents.find( diff --git a/src/app/routes.ts b/src/app/routes.ts index fdcfc5a8..96aad395 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -1,11 +1,17 @@ import type {Page} from "@sveltejs/kit" +import {identity} from "@welshman/lib" import {makeChatId, decodeRelay, encodeRelay, userRoomsByUrl} from "@app/state" -export const makeSpacePath = (url: string, ...extra: string[]) => { +export const makeSpacePath = (url: string, ...extra: (string | undefined)[]) => { let path = `/spaces/${encodeRelay(url)}` if (extra.length > 0) { - path += "/" + extra.map(s => encodeURIComponent(s)).join("/") + path += + "/" + + extra + .filter(identity) + .map(s => encodeURIComponent(s as string)) + .join("/") } return path @@ -15,15 +21,11 @@ export const makeChatPath = (pubkeys: string[]) => `/chat/${makeChatId(pubkeys)} export const makeRoomPath = (url: string, room: string) => `/spaces/${encodeRelay(url)}/${room}` -export const makeThreadPath = (url: string, eventId?: string) => { - let path = `/spaces/${encodeRelay(url)}/threads` +export const makeThreadPath = (url: string, eventId?: string) => + makeSpacePath(url, "threads", eventId) - if (eventId) { - path += "/" + eventId - } - - return path -} +export const makeCalendarPath = (url: string, eventId?: string) => + makeSpacePath(url, "calendar", eventId) export const getPrimaryNavItem = ($page: Page) => $page.route?.id?.split("/")[1] diff --git a/src/routes/spaces/[relay]/+page.svelte b/src/routes/spaces/[relay]/+page.svelte index 923a0c7a..c7cbf4ec 100644 --- a/src/routes/spaces/[relay]/+page.svelte +++ b/src/routes/spaces/[relay]/+page.svelte @@ -25,7 +25,7 @@ deriveOtherRooms, userRoomsByUrl, } from "@app/state" - import {makeChatPath, makeRoomPath, makeSpacePath} from "@app/routes" + import {makeChatPath, makeThreadPath, makeCalendarPath, makeRoomPath} from "@app/routes" import {notifications} from "@app/notifications" import {pushModal} from "@app/modal" @@ -33,7 +33,8 @@ const relay = deriveRelay(url) const userRooms = deriveUserRooms(url) const otherRooms = deriveOtherRooms(url) - const threadsPath = makeSpacePath(url, "threads") + const threadsPath = makeThreadPath(url) + const calendarPath = makeCalendarPath(url) const joinSpace = () => pushModal(SpaceJoin, {url}) @@ -137,6 +138,18 @@ {/if}
+ +
+ + Calendar + {#if $notifications.has(calendarPath)} +
+
+ {/if} +
+ {#each $userRooms as room (room)} {@const roomPath = makeRoomPath(url, room)} diff --git a/src/routes/spaces/[relay]/calendar/+page.svelte b/src/routes/spaces/[relay]/calendar/+page.svelte index 0c9c9553..6ad1edc6 100644 --- a/src/routes/spaces/[relay]/calendar/+page.svelte +++ b/src/routes/spaces/[relay]/calendar/+page.svelte @@ -14,8 +14,8 @@ import PageBar from "@lib/components/PageBar.svelte" import Divider from "@lib/components/Divider.svelte" import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte" - import EventItem from "@app/components/EventItem.svelte" - import EventCreate from "@app/components/EventCreate.svelte" + import CalendarEventItem from "@app/components/CalendarEventItem.svelte" + import CalendarEventCreate from "@app/components/CalendarEventCreate.svelte" import {pushModal} from "@app/modal" import {GENERAL, getEventsForUrl, decodeRelay} from "@app/state" import {makeCalendarFeed} from "@app/requests" @@ -23,7 +23,7 @@ const url = decodeRelay($page.params.relay) - const createEvent = () => pushModal(EventCreate, {url}) + const createEvent = () => pushModal(CalendarEventCreate, {url}) const getStart = (event: TrustedEvent) => parseInt(getTagValue("start", event.tags) || "") @@ -144,7 +144,7 @@ {#if dateDisplay} {dateDisplay} {/if} - + {/each} {#if loading} diff --git a/src/routes/spaces/[relay]/calendar/[id]/+page.svelte b/src/routes/spaces/[relay]/calendar/[id]/+page.svelte new file mode 100644 index 00000000..180d9267 --- /dev/null +++ b/src/routes/spaces/[relay]/calendar/[id]/+page.svelte @@ -0,0 +1,114 @@ + + +
+
+ {#if $event} + {#if !showReply} +
+ +
+ {/if} + {#each sortBy(e => -e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)} + +
+ + +
+
+ {/each} + {#if !showAll && $replies.length > 4} +
+ +
+ {/if} + +
+ + +
+
+ {:else} + {#await sleep(5000)} + Loading thread... + {:then} +

Failed to load thread.

+ {/await} + {/if} + + {#snippet icon()} +
+ +
+ {/snippet} + {#snippet title()} +

{getTagValue("title", $event?.tags || []) || ""}

+ {/snippet} + {#snippet action()} +
+ +
+ {/snippet} +
+
+{#if showReply} + +{/if}