diff --git a/package-lock.json b/package-lock.json index a3ab5cf..af9f34b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@welshman/content": "~0.0.13", "@welshman/dvm": "~0.0.11", "@welshman/feeds": "~0.0.26", - "@welshman/lib": "~0.0.30", + "@welshman/lib": "~0.0.32", "@welshman/net": "~0.0.41", "@welshman/signer": "~0.0.16", "@welshman/store": "~0.0.13", @@ -4715,9 +4715,9 @@ } }, "node_modules/@welshman/lib": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@welshman/lib/-/lib-0.0.30.tgz", - "integrity": "sha512-UeYFh9H3m7NfsokyaOeOCAdp4dg2uv20+XtkDxlaLVMMJDtuePBLJBYQJKuxMavGAJURr4pXt2Xna53hN/R5Lg==", + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@welshman/lib/-/lib-0.0.32.tgz", + "integrity": "sha512-mQ3vG2ttsdlzQm9bkvxBsrjxHVt/3JgQWp9o32ep5H8s1sVbdjG0adtuSv0xrkrZg5zB0p0P3jRb7LXgsq89FA==", "license": "MIT", "dependencies": { "@scure/base": "^1.1.6", diff --git a/package.json b/package.json index f3a2616..630e27e 100644 --- a/package.json +++ b/package.json @@ -62,11 +62,11 @@ "@welshman/content": "~0.0.13", "@welshman/dvm": "~0.0.11", "@welshman/feeds": "~0.0.26", - "@welshman/lib": "~0.0.30", + "@welshman/lib": "~0.0.33", "@welshman/net": "~0.0.41", "@welshman/signer": "~0.0.16", "@welshman/store": "~0.0.13", - "@welshman/util": "~0.0.49", + "@welshman/util": "~0.0.50", "daisyui": "^4.12.10", "date-picker-svelte": "^2.13.0", "dotenv": "^16.4.5", diff --git a/src/app/notifications.ts b/src/app/notifications.ts index 9bce3a3..d070e04 100644 --- a/src/app/notifications.ts +++ b/src/app/notifications.ts @@ -5,8 +5,15 @@ import {repository, pubkey} from "@welshman/app" import {prop, max, sortBy, assoc, lt, now} from "@welshman/lib" import type {Filter, TrustedEvent} from "@welshman/util" import {DIRECT_MESSAGE, MESSAGE, THREAD, COMMENT} from "@welshman/util" -import {makeSpacePath} from "@app/routes" -import {LEGACY_THREAD, deriveEventsForUrl, getMembershipUrls, userMembership} from "@app/state" +import {makeSpacePath, makeThreadPath, makeRoomPath} from "@app/routes" +import { + LEGACY_THREAD, + getEventsForUrl, + deriveEventsForUrl, + getMembershipUrls, + userRoomsByUrl, + repositoryStore, +} from "@app/state" // Checked state @@ -62,17 +69,30 @@ export const deriveNotification = (path: string, filters: Filter[], url?: string } export const spacesNotifications = derived( - [pubkey, checked, userMembership, deriveEvents(repository, {filters: SPACE_FILTERS})], - ([$pubkey, $checked, $userMembership, $events]) => { - return getMembershipUrls($userMembership) - .filter(url => { - const path = makeSpacePath(url) - const lastChecked = max([$checked["*"], $checked[path]]) - const [latestEvent] = sortBy($e => -$e.created_at, $events) + [pubkey, checked, userRoomsByUrl, repositoryStore], + ([$pubkey, $checked, $userRoomsByUrl, $repository]) => { + const hasNotification = (url: string, path: string, filters: Filter[]) => { + const lastChecked = max([$checked["*"], $checked[path]]) + const events = getEventsForUrl($repository, url, filters) - return latestEvent?.pubkey !== $pubkey && lt(lastChecked, latestEvent?.created_at) + return getNotification($pubkey, lastChecked, events) + } + + return Array.from($userRoomsByUrl.entries()) + .filter(([url, rooms]) => { + if (hasNotification(url, makeThreadPath(url), THREAD_FILTERS)) { + return true + } + + for (const room of rooms) { + if (hasNotification(url, makeRoomPath(url, room), [{kinds: [MESSAGE], "#h": [room]}])) { + return true + } + } + + return false }) - .map(url => makeSpacePath(url)) + .map(([url]) => makeSpacePath(url)) }, ) diff --git a/src/app/requests.ts b/src/app/requests.ts index 12612da..87e1506 100644 --- a/src/app/requests.ts +++ b/src/app/requests.ts @@ -4,7 +4,7 @@ import {MESSAGE, DELETE, THREAD, COMMENT} from "@welshman/util" import type {SubscribeRequestWithHandlers, Subscription} from "@welshman/net" import {SubscriptionEvent} from "@welshman/net" import type {AppSyncOpts} from "@welshman/app" -import {subscribe, load, pull, hasNegentropy} from "@welshman/app" +import {subscribe, repository, load, pull, hasNegentropy} from "@welshman/app" import {userRoomsByUrl, LEGACY_MESSAGE, GENERAL, getEventsForUrl} from "@app/state" // Utils @@ -16,7 +16,7 @@ export const pullConservatively = ({relays, filters}: AppSyncOpts) => { // Since pulling from relays without negentropy is expensive, limit how many // duplicates we repeatedly download for (const url of dumb) { - const events = getEventsForUrl(url, filters) + const events = getEventsForUrl(repository, url, filters) if (events.length > 100) { filters = filters.map(assoc("since", events[10]!.created_at)) diff --git a/src/app/routes.ts b/src/app/routes.ts index e9039ca..14382d8 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -15,8 +15,15 @@ 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) => - `/spaces/${encodeRelay(url)}/threads/${eventId}` +export const makeThreadPath = (url: string, eventId?: string) => { + let path = `/spaces/${encodeRelay(url)}/threads` + + if (eventId) { + path += "/" + eventId + } + + return path +} export const getPrimaryNavItem = ($page: Page) => $page.route?.id?.split("/")[1] diff --git a/src/app/state.ts b/src/app/state.ts index 1c397bf..8535d4e 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -53,6 +53,7 @@ import { makeRouter, tracker, makeTrackerStore, + makeRepositoryStore, relay, getSession, getSigner, @@ -208,6 +209,8 @@ export const ensureUnwrapped = async (event: TrustedEvent) => { export const trackerStore = makeTrackerStore() +export const repositoryStore = makeRepositoryStore() + export const deriveEvent = (idOrAddress: string, hints: string[] = []) => { let attempted = false @@ -251,7 +254,7 @@ export const getUrlsForEvent = derived([trackerStore, thunks], ([$tracker, $thun } }) -export const getEventsForUrl = (url: string, filters: Filter[]) => { +export const getEventsForUrl = (repository: Repository, url: string, filters: Filter[]) => { const $getUrlsForEvent = get(getUrlsForEvent) const $events = repository.query(filters) @@ -604,19 +607,24 @@ export const userMembership = withGetter( export const userRoomsByUrl = withGetter( derived(userMembership, $userMembership => { + const tags = getListTags($userMembership) const $userRoomsByUrl = new Map>() - for (const [_, room, url] of getGroupTags(getListTags($userMembership))) { + for (const [_, room, url] of getGroupTags(tags)) { addToMapKey($userRoomsByUrl, url, room) } + for (const url of getRelayTagValues(tags)) { + addToMapKey($userRoomsByUrl, url, GENERAL) + } + return $userRoomsByUrl }), ) export const deriveUserRooms = (url: string) => derived(userRoomsByUrl, $userRoomsByUrl => - sortBy(roomComparator(url), uniq(Array.from($userRoomsByUrl.get(url) || []).concat(GENERAL))), + sortBy(roomComparator(url), uniq(Array.from($userRoomsByUrl.get(url) || []))), ) export const deriveOtherRooms = (url: string) =>