145 lines
3.3 KiB
TypeScript
145 lines
3.3 KiB
TypeScript
/**
|
|
* rChats Automerge document schemas.
|
|
*
|
|
* Granularity: one directory doc per space + one doc per channel.
|
|
* DocId format: {space}:chats:channels (directory)
|
|
* {space}:chats:channel:{channelId} (messages)
|
|
* {space}:chats:dm:{sortedDid1+Did2} (DM channels)
|
|
*/
|
|
|
|
import type { DocSchema } from '../../shared/local-first/document';
|
|
|
|
// ── Transclusion: structured ref to any rSpace object ──
|
|
|
|
export interface Transclusion {
|
|
module: string; // "rtasks" | "rcal" | "rdocs" | etc.
|
|
docId: string;
|
|
objectId?: string;
|
|
display: 'inline' | 'card' | 'link';
|
|
snapshot?: { title: string; summary?: string; capturedAt: number };
|
|
}
|
|
|
|
// ── Thread metadata ──
|
|
|
|
export interface ThreadMeta {
|
|
participantDids: string[];
|
|
lastActivity: number;
|
|
replyCount: number;
|
|
}
|
|
|
|
// ── Document types ──
|
|
|
|
export interface ChannelInfo {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
isPrivate: boolean;
|
|
isDm: boolean;
|
|
createdBy: string | null;
|
|
createdAt: number;
|
|
updatedAt: number;
|
|
visibility?: import('../../shared/membrane').ObjectVisibility;
|
|
}
|
|
|
|
export interface ChatsDirectoryDoc {
|
|
meta: {
|
|
module: string;
|
|
collection: string;
|
|
version: number;
|
|
spaceSlug: string;
|
|
createdAt: number;
|
|
};
|
|
channels: Record<string, ChannelInfo>;
|
|
}
|
|
|
|
export interface Member {
|
|
userId: string;
|
|
displayName: string;
|
|
joinedAt: number;
|
|
}
|
|
|
|
export interface ChatMessage {
|
|
id: string;
|
|
channelId: string;
|
|
authorId: string;
|
|
authorName: string;
|
|
content: string;
|
|
replyTo: string | null;
|
|
threadId?: string; // points to root message ID if this is a thread reply
|
|
reactions: Record<string, string[]>; // emoji → DID[]
|
|
transclusions: Transclusion[];
|
|
editedAt: number | null;
|
|
createdAt: number;
|
|
visibility?: import('../../shared/membrane').ObjectVisibility;
|
|
}
|
|
|
|
export interface ChatChannelDoc {
|
|
meta: {
|
|
module: string;
|
|
collection: string;
|
|
version: number;
|
|
spaceSlug: string;
|
|
createdAt: number;
|
|
};
|
|
channelId: string;
|
|
messages: Record<string, ChatMessage>;
|
|
members: Record<string, Member>;
|
|
threads: Record<string, ThreadMeta>; // rootMessageId → thread metadata
|
|
pins: string[]; // pinned message IDs
|
|
isDm: boolean;
|
|
}
|
|
|
|
// ── Schema registration ──
|
|
|
|
export const chatsDirectorySchema: DocSchema<ChatsDirectoryDoc> = {
|
|
module: 'chats',
|
|
collection: 'channels',
|
|
version: 2,
|
|
init: (): ChatsDirectoryDoc => ({
|
|
meta: {
|
|
module: 'chats',
|
|
collection: 'channels',
|
|
version: 2,
|
|
spaceSlug: '',
|
|
createdAt: Date.now(),
|
|
},
|
|
channels: {},
|
|
}),
|
|
};
|
|
|
|
export const chatChannelSchema: DocSchema<ChatChannelDoc> = {
|
|
module: 'chats',
|
|
collection: 'channel',
|
|
version: 2,
|
|
init: (): ChatChannelDoc => ({
|
|
meta: {
|
|
module: 'chats',
|
|
collection: 'channel',
|
|
version: 2,
|
|
spaceSlug: '',
|
|
createdAt: Date.now(),
|
|
},
|
|
channelId: '',
|
|
messages: {},
|
|
members: {},
|
|
threads: {},
|
|
pins: [],
|
|
isDm: false,
|
|
}),
|
|
};
|
|
|
|
// ── Helpers ──
|
|
|
|
export function chatsDirectoryDocId(space: string) {
|
|
return `${space}:chats:channels` as const;
|
|
}
|
|
|
|
export function chatChannelDocId(space: string, channelId: string) {
|
|
return `${space}:chats:channel:${channelId}` as const;
|
|
}
|
|
|
|
export function dmChannelDocId(space: string, did1: string, did2: string): string {
|
|
const sorted = [did1, did2].sort();
|
|
return `${space}:chats:dm:${sorted[0]}+${sorted[1]}`;
|
|
}
|