forked from coracle/flotilla
Migrate to new welshman stores
This commit is contained in:
+158
-140
@@ -1,9 +1,10 @@
|
||||
import {derived, get} from "svelte/store"
|
||||
import {Badge} from "@capawesome/capacitor-badge"
|
||||
import {synced, throttled} from "@welshman/store"
|
||||
import {pubkey, relaysByUrl} from "@welshman/app"
|
||||
import {prop, spec, identity, now, groupBy} from "@welshman/lib"
|
||||
import {pubkey, tracker, repository, relaysByUrl} from "@welshman/app"
|
||||
import {prop, find, call, spec, first, identity, now, groupBy} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {deriveEventsByIdByUrl} from "@welshman/store"
|
||||
import {ZAP_GOAL, EVENT_TIME, MESSAGE, THREAD, COMMENT, getTagValue} from "@welshman/util"
|
||||
import {
|
||||
makeSpacePath,
|
||||
@@ -15,10 +16,8 @@ import {
|
||||
makeRoomPath,
|
||||
} from "@app/util/routes"
|
||||
import {
|
||||
chats,
|
||||
chatsById,
|
||||
hasNip29,
|
||||
getUrlsForEvent,
|
||||
repositoryStore,
|
||||
userSettingsValues,
|
||||
userGroupList,
|
||||
getSpaceUrlsFromGroupList,
|
||||
@@ -40,151 +39,170 @@ export const setChecked = (key: string) => checked.update(state => ({...state, [
|
||||
|
||||
// Derived notifications state
|
||||
|
||||
export const notifications = derived(
|
||||
throttled(
|
||||
1000,
|
||||
derived(
|
||||
[pubkey, checked, chats, userGroupList, repositoryStore, getUrlsForEvent, relaysByUrl],
|
||||
identity,
|
||||
export const notifications = call(() => {
|
||||
const goalCommentFilters = [{kinds: [COMMENT], "#K": [String(ZAP_GOAL)]}]
|
||||
const threadCommentFilters = [{kinds: [COMMENT], "#K": [String(THREAD)]}]
|
||||
const calendarCommentFilters = [{kinds: [COMMENT], "#K": [String(EVENT_TIME)]}]
|
||||
const messageFilters = [{kinds: [MESSAGE, THREAD, ZAP_GOAL, EVENT_TIME]}]
|
||||
|
||||
return derived(
|
||||
throttled(
|
||||
1000,
|
||||
derived(
|
||||
[
|
||||
pubkey,
|
||||
checked,
|
||||
chatsById,
|
||||
userGroupList,
|
||||
relaysByUrl,
|
||||
deriveEventsByIdByUrl({tracker, repository, filters: goalCommentFilters}),
|
||||
deriveEventsByIdByUrl({tracker, repository, filters: threadCommentFilters}),
|
||||
deriveEventsByIdByUrl({tracker, repository, filters: calendarCommentFilters}),
|
||||
deriveEventsByIdByUrl({tracker, repository, filters: messageFilters}),
|
||||
],
|
||||
identity,
|
||||
),
|
||||
),
|
||||
),
|
||||
([$pubkey, $checked, $chats, $userGroupList, $repository, $getUrlsForEvent, $relaysByUrl]) => {
|
||||
const hasNotification = (path: string, latestEvent: TrustedEvent | undefined) => {
|
||||
if (!latestEvent || latestEvent.pubkey === $pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const [entryPath, ts] of Object.entries($checked)) {
|
||||
const isMatch =
|
||||
entryPath === "*" ||
|
||||
entryPath.startsWith(path) ||
|
||||
(entryPath === "/chat/*" && path.startsWith("/chat/"))
|
||||
|
||||
if (isMatch && ts > latestEvent.created_at) {
|
||||
([
|
||||
$pubkey,
|
||||
$checked,
|
||||
$chatsById,
|
||||
$userGroupList,
|
||||
$relaysByUrl,
|
||||
goalCommentsByUrl,
|
||||
threadCommentsByUrl,
|
||||
calendarCommentsByUrl,
|
||||
messagesByUrl,
|
||||
]) => {
|
||||
const hasNotification = (path: string, latestEvent: TrustedEvent | undefined) => {
|
||||
if (!latestEvent || latestEvent.pubkey === $pubkey) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
for (const [entryPath, ts] of Object.entries($checked)) {
|
||||
const isMatch =
|
||||
entryPath === "*" ||
|
||||
entryPath.startsWith(path) ||
|
||||
(entryPath === "/chat/*" && path.startsWith("/chat/"))
|
||||
|
||||
const paths = new Set<string>()
|
||||
|
||||
for (const {pubkeys, messages} of $chats) {
|
||||
const chatPath = makeChatPath(pubkeys)
|
||||
|
||||
if (hasNotification(chatPath, messages[0])) {
|
||||
paths.add("/chat")
|
||||
paths.add(chatPath)
|
||||
}
|
||||
}
|
||||
|
||||
const allGoalComments = $repository.query([{kinds: [COMMENT], "#K": [String(ZAP_GOAL)]}])
|
||||
|
||||
const allThreadComments = $repository.query([{kinds: [COMMENT], "#K": [String(THREAD)]}])
|
||||
|
||||
const allCalendarComments = $repository.query([{kinds: [COMMENT], "#K": [String(EVENT_TIME)]}])
|
||||
|
||||
const allMessages = $repository.query([{kinds: [MESSAGE, THREAD, ZAP_GOAL, EVENT_TIME]}])
|
||||
|
||||
for (const url of getSpaceUrlsFromGroupList($userGroupList)) {
|
||||
const spacePath = makeSpacePath(url)
|
||||
const spacePathMobile = spacePath + ":mobile"
|
||||
const goalPath = makeGoalPath(url)
|
||||
const threadPath = makeThreadPath(url)
|
||||
const calendarPath = makeCalendarPath(url)
|
||||
const messagesPath = makeSpaceChatPath(url)
|
||||
const goalComments = allGoalComments.filter(e => $getUrlsForEvent(e.id).includes(url))
|
||||
const threadComments = allThreadComments.filter(e => $getUrlsForEvent(e.id).includes(url))
|
||||
const calendarComments = allCalendarComments.filter(e => $getUrlsForEvent(e.id).includes(url))
|
||||
const messages = allMessages.filter(e => $getUrlsForEvent(e.id).includes(url))
|
||||
|
||||
const commentsByGoalId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
goalComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [goalId, [comment]] of commentsByGoalId.entries()) {
|
||||
const goalItemPath = makeGoalPath(url, goalId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(goalPath, comment)) {
|
||||
paths.add(goalPath)
|
||||
}
|
||||
|
||||
if (hasNotification(goalItemPath, comment)) {
|
||||
paths.add(goalItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
const commentsByThreadId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
threadComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [threadId, [comment]] of commentsByThreadId.entries()) {
|
||||
const threadItemPath = makeThreadPath(url, threadId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(threadPath, comment)) {
|
||||
paths.add(threadPath)
|
||||
}
|
||||
|
||||
if (hasNotification(threadItemPath, comment)) {
|
||||
paths.add(threadItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
const commentsByEventId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
calendarComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [eventId, [comment]] of commentsByEventId.entries()) {
|
||||
const calendarItemPath = makeCalendarPath(url, eventId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(calendarPath, comment)) {
|
||||
paths.add(calendarPath)
|
||||
}
|
||||
|
||||
if (hasNotification(calendarItemPath, comment)) {
|
||||
paths.add(calendarItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNip29($relaysByUrl.get(url))) {
|
||||
for (const h of getSpaceRoomsFromGroupList(url, $userGroupList)) {
|
||||
const roomPath = makeRoomPath(url, h)
|
||||
const latestEvent = messages.find(e => e.tags.some(spec(["h", h])))
|
||||
|
||||
if (hasNotification(roomPath, latestEvent)) {
|
||||
paths.add(spacePathMobile)
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
if (isMatch && ts > latestEvent.created_at) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hasNotification(messagesPath, messages[0])) {
|
||||
paths.add(spacePathMobile)
|
||||
paths.add(spacePath)
|
||||
paths.add(messagesPath)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const paths = new Set<string>()
|
||||
|
||||
for (const {pubkeys, messages} of $chatsById.values()) {
|
||||
const chatPath = makeChatPath(pubkeys)
|
||||
|
||||
if (hasNotification(chatPath, messages[0])) {
|
||||
paths.add("/chat")
|
||||
paths.add(chatPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
},
|
||||
)
|
||||
for (const url of getSpaceUrlsFromGroupList($userGroupList)) {
|
||||
const spacePath = makeSpacePath(url)
|
||||
const spacePathMobile = spacePath + ":mobile"
|
||||
const goalPath = makeGoalPath(url)
|
||||
const threadPath = makeThreadPath(url)
|
||||
const calendarPath = makeCalendarPath(url)
|
||||
const messagesPath = makeSpaceChatPath(url)
|
||||
const goalComments = goalCommentsByUrl.get(url)?.values() || []
|
||||
const threadComments = threadCommentsByUrl.get(url)?.values() || []
|
||||
const calendarComments = calendarCommentsByUrl.get(url)?.values() || []
|
||||
const messages = messagesByUrl.get(url)?.values() || []
|
||||
|
||||
const commentsByGoalId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
goalComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [goalId, [comment]] of commentsByGoalId.entries()) {
|
||||
const goalItemPath = makeGoalPath(url, goalId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(goalPath, comment)) {
|
||||
paths.add(goalPath)
|
||||
}
|
||||
|
||||
if (hasNotification(goalItemPath, comment)) {
|
||||
paths.add(goalItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
const commentsByThreadId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
threadComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [threadId, [comment]] of commentsByThreadId.entries()) {
|
||||
const threadItemPath = makeThreadPath(url, threadId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(threadPath, comment)) {
|
||||
paths.add(threadPath)
|
||||
}
|
||||
|
||||
if (hasNotification(threadItemPath, comment)) {
|
||||
paths.add(threadItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
const commentsByEventId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
calendarComments.filter(spec({kind: COMMENT})),
|
||||
)
|
||||
|
||||
for (const [eventId, [comment]] of commentsByEventId.entries()) {
|
||||
const calendarItemPath = makeCalendarPath(url, eventId)
|
||||
|
||||
if (hasNotification(spacePathMobile, comment)) {
|
||||
paths.add(spacePathMobile)
|
||||
}
|
||||
|
||||
if (hasNotification(calendarPath, comment)) {
|
||||
paths.add(calendarPath)
|
||||
}
|
||||
|
||||
if (hasNotification(calendarItemPath, comment)) {
|
||||
paths.add(calendarItemPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNip29($relaysByUrl.get(url))) {
|
||||
for (const h of getSpaceRoomsFromGroupList(url, $userGroupList)) {
|
||||
const roomPath = makeRoomPath(url, h)
|
||||
const latestEvent = find(e => e.tags.some(spec(["h", h])), messages)
|
||||
|
||||
if (hasNotification(roomPath, latestEvent)) {
|
||||
paths.add(spacePathMobile)
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hasNotification(messagesPath, first(messages))) {
|
||||
paths.add(spacePathMobile)
|
||||
paths.add(spacePath)
|
||||
paths.add(messagesPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
export const badgeCount = derived(notifications, notifications => {
|
||||
return notifications.size
|
||||
|
||||
+15
-31
@@ -1,5 +1,5 @@
|
||||
import {on, throttle, fromPairs, batch} from "@welshman/lib"
|
||||
import {throttled, freshness} from "@welshman/store"
|
||||
import {on, throttle, indexBy, fromPairs, batch} from "@welshman/lib"
|
||||
import {throttled} from "@welshman/store"
|
||||
import {
|
||||
ALERT_ANDROID,
|
||||
ALERT_EMAIL,
|
||||
@@ -38,13 +38,14 @@ import type {Zapper, TrustedEvent, RelayProfile} from "@welshman/util"
|
||||
import type {RepositoryUpdate, WrapItem} from "@welshman/net"
|
||||
import type {Handle, RelayStats} from "@welshman/app"
|
||||
import {
|
||||
plaintext,
|
||||
tracker,
|
||||
relays,
|
||||
relayStats,
|
||||
plaintext,
|
||||
repository,
|
||||
handles,
|
||||
zappers,
|
||||
relaysByUrl,
|
||||
relayStatsByUrl,
|
||||
onRelayStats,
|
||||
handlesByNip05,
|
||||
zappersByLnurl,
|
||||
onZapper,
|
||||
onHandle,
|
||||
wrapManager,
|
||||
@@ -185,9 +186,9 @@ const relaysAdapter = {
|
||||
name: "relays",
|
||||
keyPath: "url",
|
||||
init: async (table: IDBTable<RelayProfile>) => {
|
||||
relays.set(await table.getAll())
|
||||
relaysByUrl.set(indexBy(r => r.url, await table.getAll()))
|
||||
|
||||
return onRelay(batch(3000, table.bulkPut))
|
||||
return onRelay(batch(1000, table.bulkPut))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -195,9 +196,9 @@ const relayStatsAdapter = {
|
||||
name: "relayStats",
|
||||
keyPath: "url",
|
||||
init: async (table: IDBTable<RelayStats>) => {
|
||||
relayStats.set(await table.getAll())
|
||||
relayStatsByUrl.set(indexBy(r => r.url, await table.getAll()))
|
||||
|
||||
return throttled(3000, relayStats).subscribe(table.bulkPut)
|
||||
return onRelayStats(batch(1000, table.bulkPut))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -205,9 +206,9 @@ const handlesAdapter = {
|
||||
name: "handles",
|
||||
keyPath: "nip05",
|
||||
init: async (table: IDBTable<Handle>) => {
|
||||
handles.set(await table.getAll())
|
||||
handlesByNip05.set(indexBy(r => r.nip05, await table.getAll()))
|
||||
|
||||
return onHandle(batch(3000, table.bulkPut))
|
||||
return onHandle(batch(1000, table.bulkPut))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -215,28 +216,12 @@ const zappersAdapter = {
|
||||
name: "zappers",
|
||||
keyPath: "lnurl",
|
||||
init: async (table: IDBTable<Zapper>) => {
|
||||
zappers.set(await table.getAll())
|
||||
zappersByLnurl.set(indexBy(z => z.lnurl, await table.getAll()))
|
||||
|
||||
return onZapper(batch(3000, table.bulkPut))
|
||||
},
|
||||
}
|
||||
|
||||
type FreshnessItem = {key: string; value: number}
|
||||
|
||||
const freshnessAdapter = {
|
||||
name: "freshness",
|
||||
keyPath: "key",
|
||||
init: async (table: IDBTable<FreshnessItem>) => {
|
||||
const initialRecords = await table.getAll()
|
||||
|
||||
freshness.set(fromPairs(initialRecords.map(({key, value}) => [key, value])))
|
||||
|
||||
return throttled(3000, freshness).subscribe($freshness => {
|
||||
table.bulkPut(Object.entries($freshness).map(([key, value]) => ({key, value})))
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
type PlaintextItem = {key: string; value: string}
|
||||
|
||||
const plaintextAdapter = {
|
||||
@@ -280,7 +265,6 @@ export const adapters = [
|
||||
relayStatsAdapter,
|
||||
handlesAdapter,
|
||||
zappersAdapter,
|
||||
freshnessAdapter,
|
||||
plaintextAdapter,
|
||||
wrapManagerAdapter,
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user