| 1 | /** |
| 2 | * A conversation the person cleared (chat's `g1t_clear`, their direct |
| 3 | * message with @g1t): the feed deletes the notifications it kept from it, |
| 4 | * whose text previews messages that no longer exist, and remembers when, |
| 5 | * so a notification of a message from before the clear that arrives late |
| 6 | * (an answer that ended as the clear ran) is dropped rather than kept. |
| 7 | * |
| 8 | * Pure, so it is tested on its own (forget.test.ts); src/feed.ts keeps |
| 9 | * the times in its `meta` table under `forgotKey`. |
| 10 | */ |
| 11 | import type { FeedNotification } from "@g1t/contracts"; |
| 12 | |
| 13 | /** The `meta` key a forgotten conversation's time is kept under. */ |
| 14 | export function forgotKey(workspace: string, channelId: string): string { |
| 15 | return `forgot:${workspace.toLowerCase()}:${channelId}`; |
| 16 | } |
| 17 | |
| 18 | /** A channel id as chat makes them: what forgetting takes, and nothing else. */ |
| 19 | export function cleanChannelId(value: unknown): string | null { |
| 20 | return typeof value === "string" && /^[a-z0-9_]{1,64}$/.test(value) ? value : null; |
| 21 | } |
| 22 | |
| 23 | /** |
| 24 | * Whether a notification is of a message said before its conversation |
| 25 | * was cleared at `forgotAt` (RFC 3339): such a message is gone. One with |
| 26 | * no time, or a bad one, counts as before: it is dropped. |
| 27 | */ |
| 28 | export function beforeForgetting(notification: Pick<FeedNotification, "created_at">, forgotAt: string | null): boolean { |
| 29 | if (!forgotAt) return false; |
| 30 | const at = Date.parse(notification.created_at); |
| 31 | return !Number.isFinite(at) || at <= Date.parse(forgotAt); |
| 32 | } |