diff options
Diffstat (limited to 'gui/src/logic/nostrill.ts')
| -rw-r--r-- | gui/src/logic/nostrill.ts | 155 |
1 files changed, 136 insertions, 19 deletions
diff --git a/gui/src/logic/nostrill.ts b/gui/src/logic/nostrill.ts index 97d2156..f976c95 100644 --- a/gui/src/logic/nostrill.ts +++ b/gui/src/logic/nostrill.ts @@ -1,9 +1,12 @@ import type { Event } from "@/types/nostr"; -import type { Content, FC, Poast } from "@/types/trill"; +import type { Content, Cursor, FC, FlatFeed, Poast } from "@/types/trill"; import { engagementBunt, openLock } from "./bunts"; import type { UserType } from "@/types/nostrill"; import type { Result } from "@/types/ui"; import { isValidPatp } from "urbit-ob"; +import { IMAGE_SUBREGEX, URL_REGEX, VIDEO_SUBREGEX } from "./constants"; +import { decodeNostrKey } from "./nostr"; + export function eventsToFc(postEvents: Event[]): FC { const fc = postEvents.reduce( (acc: FC, event: Event) => { @@ -18,9 +21,49 @@ export function eventsToFc(postEvents: Event[]): FC { ); return fc; } +export function addEventToFc(event: Event, fc: FC): FC { + const p = eventToPoast(event); + if (!p) return fc; + fc.feed[p.id] = p; + if (!fc.start || event.created_at < Number(fc.start)) fc.start = p.id; + if (!fc.end || event.created_at > Number(fc.end)) fc.end = p.id; + return fc; +} +export function extractURLs(text: string): { + text: Array<{ text: string } | { link: { href: string; show: string } }>; + pics: string[]; + vids: string[]; +} { + const pics: string[] = []; + const vids: string[] = []; + const tokens: Array< + { text: string } | { link: { href: string; show: string } } + > = []; + const sections = text.split(URL_REGEX); + for (const sec of sections) { + if (!sec) continue; + const s = sec.trim(); + if (!s) continue; + if (URL_REGEX.test(s)) { + if (IMAGE_SUBREGEX.test(s)) { + pics.push(s); + } else if (VIDEO_SUBREGEX.test(s)) { + vids.push(s); + } else tokens.push({ link: { href: s, show: s } }); + } else tokens.push({ text: s }); + } + + return { text: tokens, pics, vids }; +} + export function eventToPoast(event: Event): Poast | null { if (event.kind !== 1) return null; - const contents: Content = [{ paragraph: [{ text: event.content }] }]; + const inl = extractURLs(event.content || ""); + const contents: Content = [ + { paragraph: inl.text }, + { media: { images: inl.pics } }, + ]; + if (inl.vids.length > 0) contents.push({ media: { video: inl.vids[0] } }); const ts = event.created_at * 1000; const id = `${ts}`; const poast: Poast = { @@ -28,11 +71,12 @@ export function eventToPoast(event: Event): Poast | null { host: event.pubkey, author: event.pubkey, contents, - thread: id, + thread: null, parent: null, read: openLock, write: openLock, tags: [], + hash: event.id, time: ts, engagement: engagementBunt, children: [], @@ -48,17 +92,21 @@ export function eventToPoast(event: Event): Poast | null { // TODO if (marker === "root") poast.thread = eventId; else if (marker === "reply") poast.parent = eventId; + // TODO this are kinda useful too as quotes or whatever + // else if (marker === "mention") poast.parent = eventId; } // - if (ff === "r") + else if (ff === "r") contents.push({ paragraph: [{ link: { show: tag[1]!, href: tag[1]! } }], }); - if (ff === "p") - contents.push({ - paragraph: [{ ship: tag[1]! }], - }); - if (ff === "q") + else if (ff === "p") { + // + } + // contents.push({ + // paragraph: [{ ship: tag[1]! }], + // }); + else if (ff === "q") contents.push({ ref: { type: "nostr", @@ -66,10 +114,23 @@ export function eventToPoast(event: Event): Poast | null { path: tag[2] || "" + `/${tag[3] || ""}`, }, }); + // else console.log("odd tag", tag); } + if (!poast.parent && !poast.thread) { + const tags = event.tags.filter((t) => t[0] !== "p"); + console.log("no parent", { event, poast, tags }); + } + if (!poast.parent && poast.thread) poast.parent = poast.thread; return poast; } +export function stringToUser(s: string): Result<UserType> { + const p = isValidPatp(s); + if (p) return { ok: { urbit: s } }; + const dec = decodeNostrKey(s); + if (dec) return { ok: { nostr: s } }; + else return { error: "invalid user" }; +} export function userToString(user: UserType): Result<string> { if ("urbit" in user) { const isValid = isValidPatp(user.urbit); @@ -78,16 +139,6 @@ export function userToString(user: UserType): Result<string> { } else if ("nostr" in user) return { ok: user.nostr }; else return { error: "unknown user" }; } -export function isValidNostrPubkey(pubkey: string): boolean { - // TODO - if (pubkey.length !== 64) return false; - try { - BigInt("0x" + pubkey); - return true; - } catch (_e) { - return false; - } -} // NOTE common tags: // imeta // client @@ -138,3 +189,69 @@ export function isValidNostrPubkey(pubkey: string): boolean { // } // return effects; // } +// + +function findId(feed: FlatFeed, id: string): Result<string> { + const has = feed[id]; + if (!has) return { ok: id }; + else { + try { + const bigint = BigInt(id); + const n = bigint + 1n; + return findId(feed, n.toString()); + } catch (e) { + return { error: "not a number" }; + } + } +} +function updateCursor(cursor: Cursor, ncursor: Cursor, earlier: boolean) { + if (!cursor) return ncursor; + if (!ncursor) return cursor; + const or = BigInt(cursor); + const nw = BigInt(ncursor); + const shouldChange = earlier ? nw < or : nw > or; + return shouldChange ? ncursor : cursor; +} +export function consolidateFeeds(fols: Map<string, FC>): FC { + const f: FlatFeed = {}; + let start: Cursor = null; + let end: Cursor = null; + const feeds = fols.entries(); + for (const [_userString, fc] of feeds) { + start = updateCursor(start, fc.start, true); + end = updateCursor(end, fc.end, false); + + const poasts = Object.values(fc.feed); + for (const p of poasts) { + const nid = findId(f, p.id); + if ("error" in nid) continue; + f[nid.ok] = p; + } + } + return { start, end, feed: f }; +} +export function disaggregate( + fols: Map<string, FC>, + choice: "urbit" | "nostr", +): FC { + const f: FlatFeed = {}; + let start: Cursor = null; + let end: Cursor = null; + const feeds = fols.entries(); + for (const [userString, fc] of feeds) { + const want = + choice === "urbit" + ? isValidPatp(userString) + : !!decodeNostrKey(userString); + if (!want) continue; + start = updateCursor(start, fc.start, true); + end = updateCursor(end, fc.end, false); + const poasts = Object.values(fc.feed); + for (const p of poasts) { + const nid = findId(f, p.id); + if ("error" in nid) continue; + f[nid.ok] = p; + } + } + return { start, end, feed: f }; +} |
