flotilla/src/app/notifications.ts

104 lines
3.3 KiB
TypeScript
Raw Normal View History

2024-11-14 18:53:13 +00:00
import {writable, derived} from "svelte/store"
2024-11-15 22:03:54 +00:00
import {page} from "$app/stores"
2024-11-14 18:53:13 +00:00
import {deriveEvents} from "@welshman/store"
import {repository, pubkey} from "@welshman/app"
import {prop, max, sortBy, assoc, lt, now} from "@welshman/lib"
2024-11-15 22:03:54 +00:00
import type {Filter, TrustedEvent} from "@welshman/util"
2024-12-10 21:07:17 +00:00
import {DIRECT_MESSAGE, MESSAGE, THREAD, COMMENT} from "@welshman/util"
2024-12-16 18:30:42 +00:00
import {makeSpacePath, makeThreadPath, makeRoomPath} from "@app/routes"
import {
LEGACY_THREAD,
getEventsForUrl,
deriveEventsForUrl,
getMembershipUrls,
userRoomsByUrl,
repositoryStore,
} from "@app/state"
2024-11-14 18:53:13 +00:00
// Checked state
export const checked = writable<Record<string, number>>({})
2024-12-11 21:57:46 +00:00
checked.subscribe(v => console.log("====== checked", v))
2024-12-10 21:38:34 +00:00
2024-11-14 18:53:13 +00:00
export const deriveChecked = (key: string) => derived(checked, prop(key))
export const setChecked = (key: string, ts = now()) =>
2024-12-11 22:10:20 +00:00
Boolean(console.trace("====== setChecked", key)) ||
2024-11-14 18:53:13 +00:00
checked.update(state => ({...state, [key]: ts}))
// Filters for various routes
export const CHAT_FILTERS: Filter[] = [{kinds: [DIRECT_MESSAGE]}]
export const SPACE_FILTERS: Filter[] = [{kinds: [THREAD, MESSAGE, COMMENT]}]
export const ROOM_FILTERS: Filter[] = [{kinds: [MESSAGE]}]
export const THREAD_FILTERS: Filter[] = [
{kinds: [THREAD, LEGACY_THREAD]},
{kinds: [COMMENT], "#K": [String(THREAD), String(LEGACY_THREAD)]},
2024-11-14 18:53:13 +00:00
]
export const getNotificationFilters = (since: number): Filter[] =>
[...CHAT_FILTERS, ...SPACE_FILTERS, ...THREAD_FILTERS].map(assoc("since", since))
2024-12-05 21:37:15 +00:00
export const getRoomFilters = (room: string): Filter[] => ROOM_FILTERS.map(assoc("#h", [room]))
2024-11-14 18:53:13 +00:00
// Notification derivation
2024-11-15 22:03:54 +00:00
export const getNotification = (
pubkey: string | null,
lastChecked: number,
events: TrustedEvent[],
) => {
const [latestEvent] = sortBy($e => -$e.created_at, events)
return latestEvent?.pubkey !== pubkey && lt(lastChecked, latestEvent?.created_at)
}
2024-11-14 18:53:13 +00:00
export const deriveNotification = (path: string, filters: Filter[], url?: string) => {
const events = url ? deriveEventsForUrl(url, filters) : deriveEvents(repository, {filters})
return derived(
[pubkey, deriveChecked("*"), deriveChecked(path), events],
([$pubkey, $allChecked, $checked, $events]) => {
2024-11-15 22:03:54 +00:00
return getNotification($pubkey, max([$allChecked, $checked]), $events)
2024-11-14 18:53:13 +00:00
},
)
}
2024-11-14 20:16:44 +00:00
2024-11-15 22:03:54 +00:00
export const spacesNotifications = derived(
2024-12-16 18:30:42 +00:00
[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 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
2024-12-11 22:10:20 +00:00
})
2024-12-16 18:30:42 +00:00
.map(([url]) => makeSpacePath(url))
2024-11-14 20:16:44 +00:00
},
)
2024-11-15 22:03:54 +00:00
export const inactiveSpacesNotifications = derived(
[page, spacesNotifications],
([$page, $spacesNotifications]) =>
2024-12-11 22:10:20 +00:00
$spacesNotifications.filter(path => !$page.url.pathname.startsWith(path)),
2024-11-15 22:03:54 +00:00
)