| 1 | /** |
| 2 | * Clearing a person's conversation with @g1t (docs.g1t.sh/guides/ask/, |
| 3 | * "Clear the conversation"): everything said in their direct message with |
| 4 | * g1t is deleted for good, so g1t remembers none of it and the history is |
| 5 | * gone from Ask and from Chat. |
| 6 | * |
| 7 | * "New conversation" only leaves a mark (src/g1t.ts `conversation`); this |
| 8 | * deletes. What goes, from chat's own database: every message in the |
| 9 | * direct message (top-level and thread replies, g1t's answers with their |
| 10 | * steps and sources, the pages questions were asked from, marks, and the |
| 11 | * empty rows deleted messages left), their reactions, and the read mark |
| 12 | * and last activity that pointed at them. The direct message itself and |
| 13 | * its two members stay, empty, so the Ask panel and Chat open it as |
| 14 | * before. `chat_meter` stays: it counts messages and bytes per workspace |
| 15 | * per day for billing, with no text and no person in it. |
| 16 | * |
| 17 | * The rest of a clear is the service's (src/index.ts `g1tClear`): the |
| 18 | * channel's room throws away an answer still being written |
| 19 | * (src/room.ts `clear`), and notify forgets the person's notifications |
| 20 | * from it. |
| 21 | * |
| 22 | * Who may clear is decided here too, purely, so it is tested on its own |
| 23 | * (clear.test.ts): only the one person in a direct message with g1t, in |
| 24 | * their own. |
| 25 | */ |
| 26 | import { g1tDmPerson } from "./g1t.ts"; |
| 27 | |
| 28 | /** |
| 29 | * Whether `viewer` (a member key, `user:<id>`) may clear this |
| 30 | * conversation: it is a direct message of exactly them and g1t. Never a |
| 31 | * channel, a group message, or anyone else's direct message with g1t, |
| 32 | * whatever their role. |
| 33 | */ |
| 34 | export function mayClear(input: { channelKind: "channel" | "dm"; members: readonly string[]; viewer: string }): boolean { |
| 35 | return input.channelKind === "dm" && g1tDmPerson(input.members) === input.viewer; |
| 36 | } |
| 37 | |
| 38 | /** The part of D1 a clear uses. */ |
| 39 | type Db = Pick<D1Database, "prepare" | "batch">; |
| 40 | |
| 41 | /** |
| 42 | * Deletes everything said in one conversation, in one transaction (a D1 |
| 43 | * batch), and says how many messages went. Clearing again finds nothing |
| 44 | * and deletes nothing: 0. The caller checks it is the viewer's own |
| 45 | * direct message with g1t ([`mayClear`]); this touches only `channelId`. |
| 46 | */ |
| 47 | export async function clearConversation(db: Db, channelId: string): Promise<{ messages: number }> { |
| 48 | const results = await db.batch([ |
| 49 | db.prepare("DELETE FROM reactions WHERE message_id IN (SELECT id FROM messages WHERE channel_id = ?)").bind(channelId), |
| 50 | db.prepare("DELETE FROM messages WHERE channel_id = ?").bind(channelId), |
| 51 | // Nothing is left to have read, or to have been said last. |
| 52 | db.prepare("UPDATE channel_members SET last_read_id = NULL WHERE channel_id = ?").bind(channelId), |
| 53 | db.prepare("UPDATE channels SET last_message_at = NULL WHERE id = ?").bind(channelId), |
| 54 | ]); |
| 55 | return { messages: Number(results[1]?.meta?.changes ?? 0) }; |
| 56 | } |