diff --git a/packages/app/src/user.ts b/packages/app/src/user.ts index 85226aa..cdf9b67 100644 --- a/packages/app/src/user.ts +++ b/packages/app/src/user.ts @@ -1,5 +1,5 @@ import {derived, Readable} from "svelte/store" -import {withGetter} from "@welshman/store" +import {withGetter, memoized} from "@welshman/store" import {pubkey} from "./session.js" import {profilesByPubkey, loadProfile} from "./profiles.js" import {followsByPubkey, loadFollows} from "./follows.js" @@ -19,13 +19,15 @@ export type MakeUserDataOptions = { export const makeUserData = ({mapStore, loadItem}: MakeUserDataOptions) => withGetter( - derived([mapStore, pubkey], ([$mapStore, $pubkey]) => { - if (!$pubkey) return undefined + memoized( + derived([mapStore, pubkey], ([$mapStore, $pubkey]) => { + if (!$pubkey) return undefined - loadItem($pubkey) + loadItem($pubkey) - return $mapStore.get($pubkey) - }), + return $mapStore.get($pubkey) + }), + ), ) export const makeUserLoader = diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index 8e6e3d1..feeaef3 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -1030,11 +1030,11 @@ export const call = (f: () => T, ...args: unknown[]) => f() * @param f - Function to memoize * @returns Memoized function */ -export const memoize = (f: (...args: any[]) => T) => { - let prevArgs: any[] +export const memoize = (f: (...args: Args) => T) => { + let prevArgs: Args let result: T - return (...args: any[]) => { + return (...args: Args): T => { if (!equals(prevArgs, args)) { prevArgs = args result = f(...args) diff --git a/packages/store/src/collection.ts b/packages/store/src/collection.ts index 8b15448..b0919fc 100644 --- a/packages/store/src/collection.ts +++ b/packages/store/src/collection.ts @@ -1,6 +1,7 @@ import {readable, derived, writable, Readable, Subscriber} from "svelte/store" import {batch, indexBy, remove, assoc, now} from "@welshman/lib" import {withGetter, ReadableWithGetter} from "./getter.js" +import {memoized} from "./memoize.js" // Collection utility @@ -126,7 +127,7 @@ export const collection = ({name, store, getKey, load}: CollectionOptions) // store will update when it arrives loadItem(key, relays) - return derived(indexStore, $index => $index.get(key)) + return memoized(derived(indexStore, $index => $index.get(key))) } const onItem = (cb: Subscriber) => { diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index a9abc16..771f6d2 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -1,6 +1,7 @@ export * from "./synced.js" export * from "./getter.js" export * from "./throttle.js" +export * from "./memoize.js" export * from "./custom.js" export * from "./repository.js" export * from "./collection.js" diff --git a/packages/store/src/memoize.ts b/packages/store/src/memoize.ts new file mode 100644 index 0000000..579651e --- /dev/null +++ b/packages/store/src/memoize.ts @@ -0,0 +1,8 @@ +import {Readable, Subscriber} from "svelte/store" +import {memoize} from "@welshman/lib" + +export const memoized = (store: Readable) => { + const {subscribe} = store + + return {...store, subscribe: (f: Subscriber) => subscribe(memoize(f))} +} diff --git a/packages/util/src/Room.ts b/packages/util/src/Room.ts index d27b93b..cb198b6 100644 --- a/packages/util/src/Room.ts +++ b/packages/util/src/Room.ts @@ -1,4 +1,4 @@ -import {randomId, nthNe} from "@welshman/lib" +import {randomId, spec} from "@welshman/lib" import { ROOM_META, ROOM_DELETE, @@ -8,10 +8,18 @@ import { ROOM_LEAVE, } from "./Kinds.js" import {makeEvent, TrustedEvent, getIdentifier} from "./Events.js" +import {getTag, getTagValue} from "./Tags.js" export type RoomMeta = { - id: string - tags: string[][] + h: string + name?: string + about?: string + picture?: string + pictureMeta?: string[] + isClosed?: boolean + isHidden?: boolean + isPrivate?: boolean + isRestricted?: boolean event?: TrustedEvent } @@ -19,38 +27,79 @@ export type PublishedRoomMeta = Omit & { event: TrustedEvent } -export const makeRoomMeta = (room: Partial = {}): RoomMeta => ({ - id: randomId(), - tags: [], - ...room, -}) +export const makeRoomMeta = (room: Partial = {}): RoomMeta => { + return { + h: randomId(), + ...room, + } +} export const readRoomMeta = (event: TrustedEvent): PublishedRoomMeta => { if (event.kind !== ROOM_META) { throw new Error("Invalid group meta event") } - const id = getIdentifier(event) + const h = getIdentifier(event) - if (!id) { + if (!h) { throw new Error("Group meta event had no d tag") } - const tags = event.tags.filter(nthNe(0, "d")) - - return {id, tags, event} + return { + h, + event, + name: getTagValue("name", event.tags), + about: getTagValue("about", event.tags), + picture: getTagValue("picture", event.tags), + pictureMeta: getTag("picture", event.tags)?.slice(2), + isClosed: event.tags.some(spec(["closed"])), + isHidden: event.tags.some(spec(["hidden"])), + isPrivate: event.tags.some(spec(["private"])), + isRestricted: event.tags.some(spec(["restricted"])), + } } export const makeRoomCreateEvent = (room: RoomMeta) => - makeEvent(ROOM_CREATE, {tags: [["h", room.id]]}) + makeEvent(ROOM_CREATE, {tags: [["h", room.h]]}) export const makeRoomDeleteEvent = (room: RoomMeta) => - makeEvent(ROOM_DELETE, {tags: [["h", room.id]]}) + makeEvent(ROOM_DELETE, {tags: [["h", room.h]]}) -export const makeRoomEditEvent = (room: RoomMeta) => - makeEvent(ROOM_EDIT_META, {tags: [["h", room.id], ...room.tags]}) +export const makeRoomEditEvent = (room: RoomMeta) => { + const tags = [["h", room.h]] -export const makeRoomJoinEvent = (room: RoomMeta) => makeEvent(ROOM_JOIN, {tags: [["h", room.id]]}) + if (room.name) tags.push(["name", room.name]) + if (room.about) tags.push(["about", room.about]) -export const makeRoomLeaveEvent = (room: RoomMeta) => - makeEvent(ROOM_LEAVE, {tags: [["h", room.id]]}) + if (room.picture) { + const tag = ["picture", room.picture] + + if (room.pictureMeta) { + tag.push(...room.pictureMeta) + } + + tags.push(tag) + } + + if (room.isClosed) tags.push(["closed"]) + if (room.isHidden) tags.push(["hidden"]) + if (room.isPrivate) tags.push(["private"]) + if (room.isRestricted) tags.push(["restricted"]) + + if (room.event) { + for (const t of room.event.tags) { + if (tags.some(spec(t.slice(0, 1)))) continue + if (["closed", "hidden", "private", "restricted"].includes(t[0])) continue + + tags.push(t) + } + } + + console.log(room, tags) + + return makeEvent(ROOM_EDIT_META, {tags}) +} + +export const makeRoomJoinEvent = (room: RoomMeta) => makeEvent(ROOM_JOIN, {tags: [["h", room.h]]}) + +export const makeRoomLeaveEvent = (room: RoomMeta) => makeEvent(ROOM_LEAVE, {tags: [["h", room.h]]})