forked from coracle/flotilla
Simplify and optimize notifications
This commit is contained in:
+63
-75
@@ -1,16 +1,15 @@
|
||||
import {writable, derived} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
import {deriveEvents} from "@welshman/store"
|
||||
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, makeThreadPath, makeRoomPath} from "@app/routes"
|
||||
import {pubkey} from "@welshman/app"
|
||||
import {prop, max, sortBy, now} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {MESSAGE} from "@welshman/util"
|
||||
import {makeSpacePath, makeChatPath, makeThreadPath, makeRoomPath} from "@app/routes"
|
||||
import {
|
||||
LEGACY_THREAD,
|
||||
THREAD_FILTER,
|
||||
COMMENT_FILTER,
|
||||
chats,
|
||||
getEventsForUrl,
|
||||
deriveEventsForUrl,
|
||||
getMembershipUrls,
|
||||
userRoomsByUrl,
|
||||
repositoryStore,
|
||||
} from "@app/state"
|
||||
@@ -19,85 +18,74 @@ import {
|
||||
|
||||
export const checked = writable<Record<string, number>>({})
|
||||
|
||||
checked.subscribe(v => console.log("====== checked", v))
|
||||
|
||||
export const deriveChecked = (key: string) => derived(checked, prop(key))
|
||||
|
||||
export const setChecked = (key: string, ts = now()) =>
|
||||
Boolean(console.trace("====== setChecked", key)) ||
|
||||
checked.update(state => ({...state, [key]: ts}))
|
||||
export const setChecked = (key: string) =>
|
||||
checked.update(state => ({...state, [key]: now()}))
|
||||
|
||||
// Filters for various routes
|
||||
// Derived notifications state
|
||||
|
||||
export const CHAT_FILTERS: Filter[] = [{kinds: [DIRECT_MESSAGE]}]
|
||||
export const notifications = derived(
|
||||
[pubkey, checked, chats, userRoomsByUrl, repositoryStore],
|
||||
([$pubkey, $checked, $chats, $userRoomsByUrl, $repository]) => {
|
||||
const hasNotification = (path: string, events: TrustedEvent[]) => {
|
||||
const [latestEvent] = sortBy($e => -$e.created_at, events)
|
||||
|
||||
export const SPACE_FILTERS: Filter[] = [{kinds: [THREAD, MESSAGE, COMMENT]}]
|
||||
if (!latestEvent || latestEvent.pubkey === $pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
export const ROOM_FILTERS: Filter[] = [{kinds: [MESSAGE]}]
|
||||
let checkPath = ""
|
||||
let lastChecked = $checked["*"]
|
||||
|
||||
export const THREAD_FILTERS: Filter[] = [
|
||||
{kinds: [THREAD, LEGACY_THREAD]},
|
||||
{kinds: [COMMENT], "#K": [String(THREAD), String(LEGACY_THREAD)]},
|
||||
]
|
||||
for (const segment of path.slice(1).split("/")) {
|
||||
checkPath += "/" + segment
|
||||
lastChecked = max([lastChecked, $checked[checkPath]])
|
||||
}
|
||||
|
||||
export const getNotificationFilters = (since: number): Filter[] =>
|
||||
[...CHAT_FILTERS, ...SPACE_FILTERS, ...THREAD_FILTERS].map(assoc("since", since))
|
||||
|
||||
export const getRoomFilters = (room: string): Filter[] => ROOM_FILTERS.map(assoc("#h", [room]))
|
||||
|
||||
// Notification derivation
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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]) => {
|
||||
return getNotification($pubkey, max([$allChecked, $checked]), $events)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export const spacesNotifications = derived(
|
||||
[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 lastChecked < latestEvent.created_at
|
||||
}
|
||||
|
||||
return Array.from($userRoomsByUrl.entries())
|
||||
.filter(([url, rooms]) => {
|
||||
if (hasNotification(url, makeThreadPath(url), THREAD_FILTERS)) {
|
||||
return true
|
||||
}
|
||||
const paths = new Set<string>()
|
||||
|
||||
for (const room of rooms) {
|
||||
if (hasNotification(url, makeRoomPath(url, room), [{kinds: [MESSAGE], "#h": [room]}])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for (const {pubkeys, messages} of $chats) {
|
||||
const chatPath = makeChatPath(pubkeys)
|
||||
|
||||
return false
|
||||
})
|
||||
.map(([url]) => makeSpacePath(url))
|
||||
if (hasNotification(chatPath, messages)) {
|
||||
paths.add("/chat")
|
||||
paths.add(chatPath)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [url, rooms] of $userRoomsByUrl.entries()) {
|
||||
const spacePath = makeSpacePath(url)
|
||||
const threadPath = makeThreadPath(url)
|
||||
const threadFilters = [THREAD_FILTER, COMMENT_FILTER]
|
||||
const threadEvents = getEventsForUrl($repository, url, threadFilters)
|
||||
|
||||
if (hasNotification(threadPath, threadEvents)) {
|
||||
paths.add(spacePath)
|
||||
paths.add(threadPath)
|
||||
}
|
||||
|
||||
for (const room of rooms) {
|
||||
const roomPath = makeRoomPath(url, room)
|
||||
const roomFilters = [{kinds: [MESSAGE], "#h": [room]}]
|
||||
const roomEvents = getEventsForUrl($repository, url, roomFilters)
|
||||
|
||||
if (hasNotification(roomPath, roomEvents)) {
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
},
|
||||
)
|
||||
|
||||
export const inactiveSpacesNotifications = derived(
|
||||
[page, spacesNotifications],
|
||||
([$page, $spacesNotifications]) =>
|
||||
$spacesNotifications.filter(path => !$page.url.pathname.startsWith(path)),
|
||||
export const inactiveNotifications = derived(
|
||||
[page, notifications],
|
||||
([$page, $notifications]) =>
|
||||
new Set(Array.from($notifications).filter(path => !$page.url.pathname.startsWith(path))),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user