diff --git a/src/app/components/EventCreate.svelte b/src/app/components/EventCreate.svelte index e62ad27..8d30440 100644 --- a/src/app/components/EventCreate.svelte +++ b/src/app/components/EventCreate.svelte @@ -1,6 +1,6 @@
diff --git a/src/app/requests.ts b/src/app/requests.ts index 7dfe8f5..7a5e7c7 100644 --- a/src/app/requests.ts +++ b/src/app/requests.ts @@ -1,6 +1,14 @@ import {get, writable} from "svelte/store" -import {partition, insert, sortBy, assoc, now} from "@welshman/lib" -import {MESSAGE, DELETE, THREAD, COMMENT, matchFilters, getTagValues} from "@welshman/util" +import {partition, int, YEAR, MONTH, insert, sortBy, assoc, now} from "@welshman/lib" +import { + MESSAGE, + DELETE, + THREAD, + EVENT_TIME, + COMMENT, + matchFilters, + getTagValues, +} from "@welshman/util" import type {TrustedEvent, Filter} from "@welshman/util" import {feedFromFilters, makeRelayFeed, makeIntersectionFeed} from "@welshman/feeds" import type {Subscription} from "@welshman/net" @@ -15,6 +23,7 @@ import { createFeedController, } from "@welshman/app" import {createScroller} from "@lib/html" +import {daysBetween} from "@lib/util" import {userRoomsByUrl, getUrlsForEvent} from "@app/state" // Utils @@ -131,6 +140,120 @@ export const makeFeed = ({ } } +export const makeCalendarFeed = ({ + relays, + feedFilters, + subscriptionFilters, + element, + onExhausted, + initialEvents = [], +}: { + relays: string[] + feedFilters: Filter[] + subscriptionFilters: Filter[] + element: HTMLElement + onExhausted?: () => void + initialEvents?: TrustedEvent[] +}) => { + const events = writable(initialEvents) + + let backwardWindow = [now() - MONTH, now()] + let forwardWindow = [now(), now() + MONTH] + + const insertEvent = (event: TrustedEvent) => { + events.update($events => { + for (let i = 0; i < $events.length; i++) { + if ($events[i].id === event.id) return $events + if ($events[i].created_at < event.created_at) return insert(i, event, $events) + } + + return [...$events, event] + }) + } + + const removeEvents = (ids: string[]) => { + events.update($events => $events.filter(e => !ids.includes(e.id))) + } + + const handleDelete = (e: TrustedEvent) => removeEvents(getTagValues(["e", "a"], e.tags)) + + const onThunk = (thunk: Thunk) => { + if (matchFilters(feedFilters, thunk.event)) { + insertEvent(thunk.event) + + thunk.controller.signal.addEventListener("abort", () => { + removeEvents([thunk.event.id]) + }) + } else if (thunk.event.kind === DELETE) { + handleDelete(thunk.event) + } + } + + const sub = subscribe({ + relays, + filters: subscriptionFilters, + onEvent: (e: TrustedEvent) => { + if (matchFilters(feedFilters, e)) insertEvent(e) + if (e.kind === DELETE) handleDelete(e) + }, + }) + + const loadTimeframe = (since: number, until: number) => { + const hashes = daysBetween(since, until).map(String) + + console.log(since, until, hashes) + + load({ + relays, + filters: [{kinds: [EVENT_TIME], "#D": hashes}], + onEvent: insertEvent, + }) + } + + const backwardScroller = createScroller({ + element, + reverse: true, + onScroll: () => { + const [since, until] = backwardWindow + + backwardWindow = [since - MONTH, since] + + if (until > now() - int(2, YEAR)) { + loadTimeframe(since, until) + } else { + backwardScroller.stop() + } + }, + }) + + const forwardScroller = createScroller({ + element, + onScroll: () => { + const [since, until] = forwardWindow + + forwardWindow = [until, until + MONTH] + + if (until < now() + int(2, YEAR)) { + loadTimeframe(since, until) + } else { + forwardScroller.stop() + } + }, + }) + + thunkWorker.addGlobalHandler(onThunk) + + return { + events, + cleanup: () => { + thunkWorker.removeGlobalHandler(onThunk) + backwardScroller.stop() + forwardScroller.stop() + sub.close() + }, + } +} + // Application requests export const listenForNotifications = () => { diff --git a/src/app/state.ts b/src/app/state.ts index de9ac31..5cc56af 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -40,14 +40,7 @@ import { asDecryptedEvent, normalizeRelayUrl, } from "@welshman/util" -import type { - TrustedEvent, - Repository, - SignedEvent, - PublishedList, - List, - Filter, -} from "@welshman/util" +import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util" import {Nip59} from "@welshman/signer" import { pubkey, @@ -261,7 +254,7 @@ export const getUrlsForEvent = derived([trackerStore, thunks], ([$tracker, $thun } }) -export const getEventsForUrl = (repository: Repository, url: string, filters: Filter[]) => { +export const getEventsForUrl = (url: string, filters: Filter[]) => { const $getUrlsForEvent = get(getUrlsForEvent) const $events = repository.query(filters) diff --git a/src/lib/html.ts b/src/lib/html.ts index 0d664c6..c1f989a 100644 --- a/src/lib/html.ts +++ b/src/lib/html.ts @@ -21,6 +21,7 @@ export type ScrollerOpts = { onScroll: () => any element: Element threshold?: number + reverse?: boolean delay?: number } @@ -34,6 +35,7 @@ export const createScroller = ({ element, delay = 1000, threshold = 2000, + reverse = false, }: ScrollerOpts) => { let done = false diff --git a/src/lib/util.ts b/src/lib/util.ts index ee27e2b..b0c2ab9 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -26,6 +26,6 @@ export const nsecDecode = (nsec: string) => { return bytesToHex(data) } -export const day = (seconds: number) => Math.floor(seconds / DAY).toString() +export const day = (seconds: number) => Math.floor(seconds / DAY) export const daysBetween = (start: number, end: number) => [...range(start, end, DAY)].map(day) diff --git a/src/routes/spaces/[relay]/[room]/+page.svelte b/src/routes/spaces/[relay]/[room]/+page.svelte index 1f7d6f8..626cad7 100644 --- a/src/routes/spaces/[relay]/[room]/+page.svelte +++ b/src/routes/spaces/[relay]/[room]/+page.svelte @@ -6,7 +6,7 @@ import {now} from "@welshman/lib" import type {TrustedEvent, EventContent} from "@welshman/util" import {createEvent, MESSAGE, DELETE, REACTION} from "@welshman/util" - import {formatTimestampAsDate, pubkey, publishThunk, deriveRelay, repository} from "@welshman/app" + import {formatTimestampAsDate, pubkey, publishThunk, deriveRelay} from "@welshman/app" import {slide, fade, fly} from "@lib/transition" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" @@ -179,7 +179,7 @@ relays: [url], feedFilters: [filter], subscriptionFilters: [{kinds: [DELETE, REACTION, MESSAGE], "#h": [room], since: now()}], - initialEvents: getEventsForUrl(repository, url, [{...filter, limit: 20}]), + initialEvents: getEventsForUrl(url, [{...filter, limit: 20}]), onExhausted: () => { loading = false }, diff --git a/src/routes/spaces/[relay]/calendar/+page.svelte b/src/routes/spaces/[relay]/calendar/+page.svelte index ebf495e..bb57bf0 100644 --- a/src/routes/spaces/[relay]/calendar/+page.svelte +++ b/src/routes/spaces/[relay]/calendar/+page.svelte @@ -1,11 +1,12 @@
@@ -89,7 +94,7 @@
{/snippet} -
+
{#each items as { event, dateDisplay }, i (event.id)} {#if dateDisplay} {dateDisplay}