Remove message place table (#11)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina
2024-12-25 18:20:06 +04:00
committed by GitHub
parent 506f6f26be
commit db8de799d2
24 changed files with 395 additions and 379 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"type": "module",
"workspaces": ["packages/*"],
"scripts": {
"build": "sh scripts/build.sh",
"build": "bun x tsc --noEmit && sh scripts/build.sh",
"lint": "eslint packages/**/src/*.ts",
"format": "prettier --write packages/**/src/*.ts && bun run lint"
},
+41 -21
View File
@@ -10,7 +10,10 @@ import {
type FindNotificationContextParams,
type NotificationContext,
type FindNotificationsParams,
type Notification
type Notification,
type ThreadID,
type Attachment,
type Reaction
} from '@communication/types'
import {
type Client,
@@ -30,16 +33,15 @@ class DbClient implements Client {
private readonly personWorkspace: string
) {}
async createMessage(card: CardID, content: RichText, creator: SocialID): Promise<MessageID> {
async createMessage(thread: ThreadID, content: RichText, creator: SocialID): Promise<MessageID> {
const created = new Date()
const id = await this.db.createMessage(content, creator, created)
await this.db.placeMessage(id, card, this.workspace)
const id = await this.db.createMessage(this.workspace, thread, content, creator, created)
const event: MessageCreatedEvent = {
type: EventType.MessageCreated,
card,
message: {
id,
thread,
content,
creator,
created,
@@ -54,42 +56,42 @@ class DbClient implements Client {
return id
}
async removeMessage(message: MessageID) {
async removeMessage(thread: ThreadID, message: MessageID) {
await this.db.removeMessage(message)
this.onEvent({ type: EventType.MessageRemoved, message })
this.onEvent({ type: EventType.MessageRemoved, message, thread })
}
async createPatch(message: MessageID, content: RichText, creator: SocialID): Promise<void> {
async createPatch(thread: ThreadID, message: MessageID, content: RichText, creator: SocialID): Promise<void> {
const created = new Date()
await this.db.createPatch(message, content, creator, created)
this.onEvent({ type: EventType.PatchCreated, patch: { message, content, creator, created } })
this.onEvent({ type: EventType.PatchCreated, thread, patch: { message, content, creator, created } })
}
async createReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void> {
async createReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void> {
const created = new Date()
await this.db.createReaction(message, reaction, creator, created)
this.onEvent({ type: EventType.ReactionCreated, reaction: { message, reaction, creator, created } })
this.onEvent({ type: EventType.ReactionCreated, thread, reaction: { message, reaction, creator, created } })
}
async removeReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void> {
async removeReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void> {
await this.db.removeReaction(message, reaction, creator)
this.onEvent({ type: EventType.ReactionRemoved, message, reaction, creator })
this.onEvent({ type: EventType.ReactionRemoved, thread, message, reaction, creator })
}
async createAttachment(message: MessageID, card: CardID, creator: SocialID): Promise<void> {
async createAttachment(thread: ThreadID, message: MessageID, card: CardID, creator: SocialID): Promise<void> {
const created = new Date()
await this.db.createAttachment(message, card, creator, created)
this.onEvent({ type: EventType.AttachmentCreated, attachment: { message, card, creator, created } })
this.onEvent({ type: EventType.AttachmentCreated, thread, attachment: { message, card, creator, created } })
}
async removeAttachment(message: MessageID, card: CardID): Promise<void> {
async removeAttachment(thread: ThreadID, message: MessageID, card: CardID): Promise<void> {
await this.db.removeAttachment(message, card)
this.onEvent({ type: EventType.AttachmentRemoved, message, card })
this.onEvent({ type: EventType.AttachmentRemoved, message, card, thread })
}
async findMessages(params: FindMessagesParams): Promise<Message[]> {
const rawMessages = await this.db.findMessages(this.workspace, params)
return rawMessages.map(this.toMessage)
return rawMessages.map((it) => this.toMessage(it))
}
async findMessage(params: FindMessagesParams): Promise<Message | undefined> {
@@ -99,15 +101,33 @@ class DbClient implements Client {
toMessage(raw: any): Message {
return {
id: raw.id,
thread: raw.thread,
content: raw.content,
creator: raw.creator,
created: new Date(raw.created),
edited: new Date(raw.edited),
reactions: raw.reactions,
attachments: raw.attachments
reactions: raw.reactions.map((it: any) => this.toReaction(it)),
attachments: raw.attachments.map((it: any) => this.toAttachment(it))
}
}
toAttachment(raw: any): Attachment {
return {
message: raw.message,
card: raw.card,
creator: raw.creator,
created: new Date(raw.created)
}
}
toReaction(raw: any): Reaction {
return {
message: raw.message,
reaction: raw.reaction,
creator: raw.creator,
created: new Date(raw.created)
}
}
async createNotification(message: MessageID, context: ContextID): Promise<void> {
await this.db.createNotification(message, context)
}
@@ -117,7 +137,7 @@ class DbClient implements Client {
}
async createNotificationContext(card: CardID, lastView?: Date, lastUpdate?: Date): Promise<ContextID> {
return await this.db.createContext(this.workspace, card, this.personWorkspace, lastView, lastUpdate)
return await this.db.createContext(this.personWorkspace, this.workspace, card, lastView, lastUpdate)
}
async updateNotificationContext(context: ContextID, update: NotificationContextUpdate): Promise<void> {
+40 -12
View File
@@ -1,4 +1,5 @@
import {
type Attachment,
type CardID,
type ContextID,
type FindMessagesParams,
@@ -9,8 +10,10 @@ import {
type Notification,
type NotificationContext,
type NotificationContextUpdate,
type Reaction,
type RichText,
type SocialID
type SocialID,
type ThreadID
} from '@communication/types'
import {
type BroadcastEvent,
@@ -53,10 +56,10 @@ class WsClient implements Client {
}
}
async createMessage(card: CardID, content: RichText, creator: SocialID): Promise<MessageID> {
async createMessage(thread: ThreadID, content: RichText, creator: SocialID): Promise<MessageID> {
const event: CreateMessageEvent = {
type: EventType.CreateMessage,
card,
thread,
content,
creator
}
@@ -64,17 +67,19 @@ class WsClient implements Client {
return (result as CreateMessageResult).id
}
async removeMessage(message: MessageID) {
async removeMessage(thread: ThreadID, message: MessageID): Promise<void> {
const event: RemoveMessageEvent = {
type: EventType.RemoveMessage,
thread,
message
}
await this.sendEvent(event)
}
async createPatch(message: MessageID, content: RichText, creator: SocialID): Promise<void> {
async createPatch(thread: ThreadID, message: MessageID, content: RichText, creator: SocialID): Promise<void> {
const event: CreatePatchEvent = {
type: EventType.CreatePatch,
thread,
message,
content,
creator
@@ -82,9 +87,10 @@ class WsClient implements Client {
await this.sendEvent(event)
}
async createReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void> {
async createReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void> {
const event: CreateReactionEvent = {
type: EventType.CreateReaction,
thread,
message,
reaction,
creator
@@ -92,9 +98,10 @@ class WsClient implements Client {
await this.sendEvent(event)
}
async removeReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void> {
async removeReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void> {
const event: RemoveReactionEvent = {
type: EventType.RemoveReaction,
thread,
message,
reaction,
creator
@@ -102,9 +109,10 @@ class WsClient implements Client {
await this.sendEvent(event)
}
async createAttachment(message: MessageID, card: CardID, creator: SocialID): Promise<void> {
async createAttachment(thread: ThreadID, message: MessageID, card: CardID, creator: SocialID): Promise<void> {
const event: CreateAttachmentEvent = {
type: EventType.CreateAttachment,
thread,
message,
card,
creator
@@ -112,9 +120,10 @@ class WsClient implements Client {
await this.sendEvent(event)
}
async removeAttachment(message: MessageID, card: CardID): Promise<void> {
async removeAttachment(thread: ThreadID, message: MessageID, card: CardID): Promise<void> {
const event: RemoveAttachmentEvent = {
type: EventType.RemoveAttachment,
thread,
message,
card
}
@@ -123,18 +132,37 @@ class WsClient implements Client {
async findMessages(params: FindMessagesParams, queryId?: number): Promise<Message[]> {
const rawMessages = await this.ws.send('findMessages', [params, queryId])
return rawMessages.map(this.toMessage)
return rawMessages.map((it: any) => this.toMessage(it))
}
toMessage(raw: any): Message {
return {
id: raw.id,
thread: raw.thread,
content: raw.content,
creator: raw.creator,
created: new Date(raw.created),
edited: new Date(raw.edited),
reactions: raw.reactions,
attachments: raw.attachments
reactions: raw.reactions.map((it: any) => this.toReaction(it)),
attachments: raw.attachments.map((it: any) => this.toAttachment(it))
}
}
toAttachment(raw: any): Attachment {
return {
message: raw.message,
card: raw.card,
creator: raw.creator,
created: new Date(raw.created)
}
}
toReaction(raw: any): Reaction {
return {
message: raw.message,
reaction: raw.reaction,
creator: raw.creator,
created: new Date(raw.created)
}
}
+7 -18
View File
@@ -1,24 +1,13 @@
CREATE TABLE IF NOT EXISTS message
(
id UUID NOT NULL DEFAULT gen_random_uuid(),
content TEXT NOT NULL,
creator VARCHAR(255) NOT NULL,
created TIMESTAMPTZ NOT NULL,
id UUID NOT NULL DEFAULT gen_random_uuid(),
workspace_id UUID NOT NULL,
thread_id UUID NOT NULL,
content TEXT NOT NULL,
creator VARCHAR(255) NOT NULL,
created TIMESTAMPTZ NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS message_place
(
workspace_id UUID NOT NULL,
card_id UUID NOT NULL,
message_id UUID NOT NULL,
PRIMARY KEY (workspace_id, card_id, message_id),
FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE
);
CREATE INDEX idx_message_place_workspace_card ON message_place (workspace_id, card_id);
CREATE INDEX idx_message_place_message_id ON message_place (message_id);
+10 -7
View File
@@ -11,7 +11,8 @@ import {
type FindNotificationContextParams,
type NotificationContext,
type FindNotificationsParams,
type Notification
type Notification,
type ThreadID
} from '@communication/types'
import type { DbAdapter } from '@communication/sdk-types'
@@ -31,12 +32,14 @@ export class CockroachAdapter implements DbAdapter {
this.notification = new NotificationsDb(this.sqlClient)
}
async createMessage(content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
return await this.message.createMessage(content, creator, created)
}
async placeMessage(message: MessageID, card: CardID, workspace: string): Promise<void> {
return await this.message.placeMessage(message, card, workspace)
async createMessage(
workspace: string,
thread: ThreadID,
content: RichText,
creator: SocialID,
created: Date
): Promise<MessageID> {
return await this.message.createMessage(workspace, thread, content, creator, created)
}
async createPatch(message: MessageID, content: RichText, creator: SocialID, created: Date): Promise<void> {
+31 -42
View File
@@ -6,14 +6,13 @@ import {
SortOrder,
type SocialID,
type RichText,
Direction, type Reaction, type Attachment
Direction, type Reaction, type Attachment, type ThreadID
} from '@communication/types'
import {BaseDb} from './base.ts'
import {
TableName,
type MessageDb,
type MessagePlaceDb,
type AttachmentDb,
type ReactionDb,
type PatchDb
@@ -21,8 +20,10 @@ import {
export class MessagesDb extends BaseDb {
//Message
async createMessage(content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
async createMessage(workspace: string, thread: ThreadID, content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
const dbData: MessageDb = {
workspace_id: workspace,
thread_id: thread,
content: content,
creator: creator,
created: created,
@@ -37,15 +38,6 @@ export class MessagesDb extends BaseDb {
await this.remove(TableName.Message, {id: message})
}
async placeMessage(message: MessageID, card: CardID, workspace: string): Promise<void> {
const dbData: MessagePlaceDb = {
workspace_id: workspace,
card_id: card,
message_id: message
}
await this.insert(TableName.MessagePlace, dbData)
}
async createPatch(message: MessageID, content: RichText, creator: SocialID, created: Date): Promise<void> {
const dbData: PatchDb = {
message_id: message,
@@ -104,8 +96,7 @@ export class MessagesDb extends BaseDb {
${this.subSelectPatches()},
${this.subSelectAttachments()},
${this.subSelectReactions()}
FROM ${TableName.Message} m
INNER JOIN ${TableName.MessagePlace} mp ON m.id = mp.message_id`
FROM ${TableName.Message} m`
const {where, values} = this.buildMessageWhere(workspace, params)
const orderBy = params.sort ? `ORDER BY m.created ${params.sort === SortOrder.Asc ? 'ASC' : 'DESC'}` : ''
@@ -118,40 +109,37 @@ export class MessagesDb extends BaseDb {
}
buildMessageWhere(workspace: string, params: FindMessagesParams): { where: string, values: any[] } {
const where: string[] = ['mp.workspace_id = $1']
const where: string[] = ['m.workspace_id = $1']
const values: any[] = [workspace]
let index = 2
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'id': {
where.push(`m.id = $${index++}`)
values.push(value)
break
}
case 'card': {
where.push(`mp.card_id = $${index++}`)
values.push(value)
break
}
case 'from': {
const exclude = params.excluded ?? false
const direction = params.direction ?? Direction.Forward
const getOperator = () => {
if (exclude) {
return direction === Direction.Forward ? '>' : '<'
} else {
return direction === Direction.Forward ? '>=' : '<='
}
}
where.push(`m.created ${getOperator()} $${index++}`)
values.push(value)
break
let index = 2
if (params.id != null) {
where.push(`m.id = $${index++}`)
values.push(params.id)
}
if (params.thread != null) {
where.push(`m.thread_id = $${index++}`)
values.push(params.thread)
}
if (params.from != null) {
const exclude = params.excluded ?? false
const direction = params.direction ?? Direction.Forward
const getOperator = () => {
if (exclude) {
return direction === Direction.Forward ? '>' : '<'
} else {
return direction === Direction.Forward ? '>=' : '<='
}
}
where.push(`m.created ${getOperator()} $${index++}`)
values.push(params.from)
}
return {where: `WHERE ${where.join(' AND ')}`, values}
}
@@ -198,6 +186,7 @@ export class MessagesDb extends BaseDb {
return {
id: row.id,
thread: row.thread_id,
content: lastPatch?.content ?? row.content,
creator: row.creator,
created: new Date(row.created),
+31 -44
View File
@@ -66,12 +66,12 @@ export class NotificationsDb extends BaseDb {
const sql = `UPDATE ${TableName.NotificationContext}
SET ${keys.map((k, idx) => `"${k}" = $${idx + 1}`).join(', ')}
WHERE id =$${keys.length + 1}`
WHERE id = $${keys.length + 1}`
await this.client.unsafe(sql, [values, context])
}
async findContexts( params: FindNotificationContextParams, personWorkspaces: string[], workspace?: string,): Promise<NotificationContext[]> {
async findContexts(params: FindNotificationContextParams, personWorkspaces: string[], workspace?: string,): Promise<NotificationContext[]> {
const select = `
SELECT nc.id, nc.card_id, nc.archived_from, nc.last_view, nc.last_update
FROM ${TableName.NotificationContext} nc`;
@@ -91,6 +91,7 @@ export class NotificationsDb extends BaseDb {
const select = `
SELECT n.message_id,
n.context,
m.thread_id AS message_thread,
m.content AS message_content,
m.creator AS message_creator,
m.created AS message_created,
@@ -130,25 +131,19 @@ export class NotificationsDb extends BaseDb {
const values: any[] = []
let index = 1
if(workspace != null) {
if (workspace != null) {
where.push(`nc.workspace_id = $${index++}`)
values.push(workspace)
}
if(personWorkspaces.length > 0) {
if (personWorkspaces.length > 0) {
where.push(`nc.person_workspace IN (${personWorkspaces.map((it) => `$${index++}`).join(', ')})`)
values.push(...personWorkspaces)
}
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'card': {
where.push(`nc.card_id = $${index++}`)
values.push(value)
break
}
}
if (params.card != null) {
where.push(`nc.card_id = $${index++}`)
values.push(params.card)
}
return {where: `WHERE ${where.join(' AND ')}`, values}
@@ -162,43 +157,34 @@ export class NotificationsDb extends BaseDb {
const values: any[] = [personWorkspace]
let index = 2
if(workspace != null) {
if (workspace != null) {
where.push(`nc.workspace_id = $${index++}`)
values.push(workspace)
}
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'context': {
where.push(`n.context = $${index++}`)
values.push(value)
break
}
case 'card': {
where.push(`nc.card_id = $${index++}`)
values.push(value)
break
}
case 'read': {
if (value === true) {
where.push(`nc.last_view IS NOT NULL AND nc.last_view >= m.created`)
} else if (value === false) {
where.push(`(nc.last_view IS NULL OR nc.last_view > m.created)`)
}
break
}
case 'archived': {
if (value === true) {
where.push(`nc.archived_from IS NOT NULL AND nc.archived_from >= m.created`)
} else if (value === false) {
where.push(`(nc.archived_from IS NULL OR nc.archived_from > m.created)`)
}
break
}
}
if (params.context != null) {
where.push(`n.context = $${index++}`)
values.push(params.context)
}
if (params.read === true) {
where.push(`nc.last_view IS NOT NULL AND nc.last_view >= m.created`)
}
if (params.read === false) {
where.push(`(nc.last_view IS NULL OR nc.last_view > m.created)`)
}
if (params.archived === true) {
where.push(`nc.archived_from IS NOT NULL AND nc.archived_from >= m.created`)
}
if (params.archived === false) {
where.push(`(nc.archived_from IS NULL OR nc.archived_from > m.created)`)
}
return {where: `WHERE ${where.join(' AND ')}`, values}
}
@@ -223,6 +209,7 @@ export class NotificationsDb extends BaseDb {
return {
message: {
id: row.id,
thread: row.message_thread,
content: lastPatch?.content ?? row.message_content,
creator: row.message_creator,
created,
+3 -9
View File
@@ -1,9 +1,8 @@
import type {CardID, ContextID, MessageID, RichText, SocialID } from "@communication/types"
import type {CardID, ContextID, MessageID, RichText, SocialID, ThreadID } from "@communication/types"
export enum TableName {
Message = 'message',
Patch = 'patch',
MessagePlace = 'message_place',
Attachment = 'attachment',
Reaction = 'reaction',
Notification = 'notification',
@@ -11,6 +10,8 @@ export enum TableName {
}
export interface MessageDb {
workspace_id: string,
thread_id: ThreadID,
content: RichText,
creator: SocialID,
created: Date,
@@ -22,13 +23,6 @@ export interface PatchDb {
creator: SocialID,
created: Date,
}
export interface MessagePlaceDb {
workspace_id: string,
card_id: CardID,
message_id: MessageID
}
export interface ReactionDb {
message_id: MessageID,
reaction: string,
+9 -9
View File
@@ -1,12 +1,12 @@
import { type CardID, type Message, SortOrder, type Window } from '@communication/types'
import { type Message, type SocialID, SortOrder, type ThreadID, type Window } from '@communication/types'
import { getWebsocketClient } from '@communication/client-ws'
import { getSqliteClient } from '@communication/client-sqlite'
import { createMessagesQuery, initLiveQueries } from '@communication/client-query'
const card1: CardID = 'cd0aba36-1c4f-4170-95f2-27a12a5415f6'
const thread = 'cd0aba36-1c4f-4170-95f2-27a12a5415f6' as ThreadID
const workspace = 'cd0aba36-1c4f-4170-95f2-27a12a5415f6'
const personWorkspace = 'cd0aba36-1c4f-4170-95f2-27a12a5415f5'
const creator1 = 'email:vasya@huly.com'
const creator1 = 'email:vasya@huly.com' as SocialID
async function getClient(type: 'ws' | 'sqlite') {
if (type === 'ws') {
@@ -26,7 +26,7 @@ export async function example() {
let window: Window<Message> | undefined = undefined
query1.query({ card: card1, sort: SortOrder.Desc }, (res) => {
query1.query({ thread, sort: SortOrder.Desc }, (res) => {
window = res
const r = window.getResult()
r.reverse()
@@ -44,19 +44,19 @@ export async function example() {
})
async function editMessage(message: Message) {
await client.createPatch(message.id, message.content + '_1_', creator1)
await client.createPatch(thread, message.id, message.content + '_1_', creator1)
}
async function deleteMessage(message: Message) {
await client.removeMessage(message.id)
await client.removeMessage(thread, message.id)
}
async function addReaction(message: Message) {
await client.createReaction(message.id, '👍', creator1)
await client.createReaction(thread, message.id, '👍', creator1)
}
async function removeReaction(message: Message) {
await client.removeReaction(message.id, '👍', creator1)
await client.removeReaction(thread, message.id, '👍', creator1)
}
function scrollToBottom() {
@@ -116,7 +116,7 @@ export async function example() {
const el = event.target?.getElementsByTagName('input')[0] as HTMLInputElement
if (el.value == '' || el.value == null) return
await client.createMessage(card1, el.value, creator1)
await client.createMessage(thread, el.value, creator1)
el.value = ''
})
+3 -4
View File
@@ -1,5 +1,4 @@
import {
type CardID,
type FindMessagesParams,
type ID,
type Message,
@@ -65,7 +64,7 @@ export class MessagesQuery extends BaseQuery<Message, FindMessagesParams> {
const exists = this.result.get(message.id)
if (exists !== undefined) return
if (!this.match(message, event.card)) return
if (!this.match(message)) return
if (this.result.isTail()) {
if (this.params.sort === SortOrder.Asc) {
@@ -77,11 +76,11 @@ export class MessagesQuery extends BaseQuery<Message, FindMessagesParams> {
}
}
private match(message: Message, card: CardID): boolean {
private match(message: Message): boolean {
if (this.params.id != null && this.params.id !== message.id) {
return false
}
if (this.params.card != null && this.params.card !== card) {
if (this.params.thread != null && this.params.thread !== message.thread) {
return false
}
return true
+9 -8
View File
@@ -9,22 +9,23 @@ import type {
NotificationContextUpdate,
RichText,
SocialID,
Notification
Notification,
ThreadID
} from '@communication/types'
import type { FindMessagesParams } from '@communication/types'
import type { BroadcastEvent } from './event.ts'
export interface Client {
createMessage(card: CardID, content: RichText, creator: SocialID): Promise<MessageID>
removeMessage(id: MessageID): Promise<void>
createPatch(message: MessageID, content: RichText, creator: SocialID): Promise<void>
createMessage(thread: ThreadID, content: RichText, creator: SocialID): Promise<MessageID>
removeMessage(thread: ThreadID, id: MessageID): Promise<void>
createPatch(thread: ThreadID, message: MessageID, content: RichText, creator: SocialID): Promise<void>
createReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void>
removeReaction(message: MessageID, reaction: string, creator: SocialID): Promise<void>
createReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void>
removeReaction(thread: ThreadID, message: MessageID, reaction: string, creator: SocialID): Promise<void>
createAttachment(message: MessageID, card: CardID, creator: SocialID): Promise<void>
removeAttachment(message: MessageID, card: CardID): Promise<void>
createAttachment(thread: ThreadID, message: MessageID, card: CardID, creator: SocialID): Promise<void>
removeAttachment(thread: ThreadID, message: MessageID, card: CardID): Promise<void>
createNotification(message: MessageID, context: ContextID): Promise<void>
removeNotification(message: MessageID, context: ContextID): Promise<void>
+9 -4
View File
@@ -10,14 +10,19 @@ import type {
NotificationContextUpdate,
RichText,
SocialID,
Notification
Notification,
ThreadID
} from '@communication/types'
export interface DbAdapter {
createMessage(content: RichText, creator: SocialID, created: Date): Promise<MessageID>
createMessage(
workspace: string,
thread: ThreadID,
content: RichText,
creator: SocialID,
created: Date
): Promise<MessageID>
removeMessage(id: MessageID): Promise<void>
placeMessage(message: MessageID, card: CardID, workspace: string): Promise<void>
createPatch(message: MessageID, content: RichText, creator: SocialID, created: Date): Promise<void>
createReaction(message: MessageID, reaction: string, creator: SocialID, created: Date): Promise<void>
+15 -3
View File
@@ -10,7 +10,8 @@ import type {
Reaction,
RichText,
SocialID,
Notification
Notification,
ThreadID
} from '@communication/types'
export enum EventType {
@@ -57,18 +58,20 @@ export type Event =
export interface CreateMessageEvent {
type: EventType.CreateMessage
card: CardID
thread: ThreadID
content: RichText
creator: SocialID
}
export interface RemoveMessageEvent {
type: EventType.RemoveMessage
thread: ThreadID
message: MessageID
}
export interface CreatePatchEvent {
type: EventType.CreatePatch
thread: ThreadID
message: MessageID
content: RichText
creator: SocialID
@@ -76,6 +79,7 @@ export interface CreatePatchEvent {
export interface CreateReactionEvent {
type: EventType.CreateReaction
thread: ThreadID
message: MessageID
reaction: string
creator: SocialID
@@ -83,6 +87,7 @@ export interface CreateReactionEvent {
export interface RemoveReactionEvent {
type: EventType.RemoveReaction
thread: ThreadID
message: MessageID
reaction: string
creator: SocialID
@@ -90,6 +95,7 @@ export interface RemoveReactionEvent {
export interface CreateAttachmentEvent {
type: EventType.CreateAttachment
thread: ThreadID
message: MessageID
card: CardID
creator: SocialID
@@ -97,6 +103,7 @@ export interface CreateAttachmentEvent {
export interface RemoveAttachmentEvent {
type: EventType.RemoveAttachment
thread: ThreadID
message: MessageID
card: CardID
}
@@ -158,27 +165,30 @@ export type BroadcastEvent =
export interface MessageCreatedEvent {
type: EventType.MessageCreated
card: CardID
message: Message
}
export interface MessageRemovedEvent {
type: EventType.MessageRemoved
thread: ThreadID
message: MessageID
}
export interface PatchCreatedEvent {
type: EventType.PatchCreated
thread: ThreadID
patch: Patch
}
export interface ReactionCreatedEvent {
type: EventType.ReactionCreated
thread: ThreadID
reaction: Reaction
}
export interface ReactionRemovedEvent {
type: EventType.ReactionRemoved
thread: ThreadID
message: MessageID
reaction: string
creator: SocialID
@@ -186,11 +196,13 @@ export interface ReactionRemovedEvent {
export interface AttachmentCreatedEvent {
type: EventType.AttachmentCreated
thread: ThreadID
attachment: Attachment
}
export interface AttachmentRemovedEvent {
type: EventType.AttachmentRemoved
thread: ThreadID
message: MessageID
card: CardID
}
+9 -4
View File
@@ -73,10 +73,10 @@ export class EventProcessor {
private async createMessage(event: CreateMessageEvent): Promise<Result> {
const created = new Date()
const id = await this.db.createMessage(event.content, event.creator, created)
await this.db.placeMessage(id, event.card, this.workspace)
const id = await this.db.createMessage(this.workspace, event.thread, event.content, event.creator, created)
const message: Message = {
id,
thread: event.thread,
content: event.content,
creator: event.creator,
created: created,
@@ -86,7 +86,6 @@ export class EventProcessor {
}
const broadcastEvent: MessageCreatedEvent = {
type: EventType.MessageCreated,
card: event.card,
message
}
return {
@@ -107,6 +106,7 @@ export class EventProcessor {
}
const broadcastEvent: PatchCreatedEvent = {
type: EventType.PatchCreated,
thread: event.thread,
patch
}
return {
@@ -120,6 +120,7 @@ export class EventProcessor {
const broadcastEvent: MessageRemovedEvent = {
type: EventType.MessageRemoved,
thread: event.thread,
message: event.message
}
@@ -141,6 +142,7 @@ export class EventProcessor {
}
const broadcastEvent: ReactionCreatedEvent = {
type: EventType.ReactionCreated,
thread: event.thread,
reaction
}
return {
@@ -153,6 +155,7 @@ export class EventProcessor {
await this.db.removeReaction(event.message, event.reaction, event.creator)
const broadcastEvent: ReactionRemovedEvent = {
type: EventType.ReactionRemoved,
thread: event.thread,
message: event.message,
reaction: event.reaction,
creator: event.creator
@@ -175,6 +178,7 @@ export class EventProcessor {
}
const broadcastEvent: AttachmentCreatedEvent = {
type: EventType.AttachmentCreated,
thread: event.thread,
attachment
}
@@ -188,6 +192,7 @@ export class EventProcessor {
await this.db.removeAttachment(event.message, event.card)
const broadcastEvent: AttachmentRemovedEvent = {
type: EventType.AttachmentRemoved,
thread: event.thread,
message: event.message,
card: event.card
}
@@ -222,9 +227,9 @@ export class EventProcessor {
private async createNotificationContext(event: CreateNotificationContextEvent): Promise<Result> {
const id = await this.db.createContext(
this.personWorkspace,
this.workspace,
event.card,
this.personWorkspace,
event.lastView,
event.lastUpdate
)
+2 -1
View File
@@ -2,6 +2,7 @@ import WebSocket, { WebSocketServer, type RawData } from 'ws'
import { createDbAdapter } from '@communication/cockroach'
import type { Response, HelloRequest } from '@communication/sdk-types'
import { decodeToken } from '@hcengineering/server-token'
import type { SocialID } from '@communication/types'
import type { ConnectionInfo } from './types.ts'
import { deserializeRequest, serializeResponse } from './utils/serialize.ts'
@@ -137,5 +138,5 @@ async function validateToken(token: string): Promise<ConnectionInfo> {
}
const personWorkspace = 'cd0aba36-1c4f-4170-95f2-27a12a5415f7'
return { workspace: info.workspaceId, personWorkspace, socialId: email }
return { workspace: info.workspaceId, personWorkspace, socialId: email as SocialID }
}
+37 -7
View File
@@ -3,12 +3,16 @@ import {
type BroadcastEvent,
type DbAdapter,
EventType,
type MessageCreatedEvent,
type NotificationContextCreatedEvent,
type NotificationCreatedEvent,
type Response
} from '@communication/sdk-types'
import type { FindMessagesParams, FindNotificationContextParams, FindNotificationsParams } from '@communication/types'
import type {
FindMessagesParams,
FindNotificationContextParams,
FindNotificationsParams,
MessageID
} from '@communication/types'
import { Session } from './session'
import type { ConnectionInfo } from './types'
@@ -112,14 +116,40 @@ export class Manager {
private match(event: BroadcastEvent, info: SessionInfo): boolean {
switch (event.type) {
case EventType.MessageCreated:
return this.matchMessagesQuery(event, Array.from(info.messageQueries.values()))
return this.matchMessagesQuery(
{ id: event.message.id, thread: event.message.thread },
Array.from(info.messageQueries.values())
)
case EventType.PatchCreated:
return this.matchMessagesQuery(
{ thread: event.thread, id: event.patch.message },
Array.from(info.messageQueries.values())
)
case EventType.MessageRemoved:
return this.matchMessagesQuery(
{ thread: event.thread, id: event.message },
Array.from(info.messageQueries.values())
)
case EventType.ReactionCreated:
return this.matchMessagesQuery(
{ thread: event.thread, id: event.reaction.message },
Array.from(info.messageQueries.values())
)
case EventType.ReactionRemoved:
return this.matchMessagesQuery(
{ thread: event.thread, id: event.message },
Array.from(info.messageQueries.values())
)
case EventType.AttachmentCreated:
return this.matchMessagesQuery(
{ thread: event.thread, id: event.attachment.message },
Array.from(info.messageQueries.values())
)
case EventType.AttachmentRemoved:
return info.messageQueries.size > 0
return this.matchMessagesQuery(
{ thread: event.thread, id: event.message },
Array.from(info.messageQueries.values())
)
case EventType.NotificationCreated:
return (
info.session.info.personWorkspace === event.personWorkspace &&
@@ -139,12 +169,12 @@ export class Manager {
}
}
private matchMessagesQuery(event: MessageCreatedEvent, queries: FindMessagesParams[]): boolean {
private matchMessagesQuery(params: { id?: MessageID; thread?: string }, queries: FindMessagesParams[]): boolean {
if (queries.length === 0) return false
for (const query of queries) {
if (query.id != null && query.id !== event.message.id) continue
if (query.card != null && query.card !== event.card) continue
if (query.id != null && query.id !== params.id) continue
if (query.thread != null && query.thread !== params.thread) continue
return true
}
+4 -4
View File
@@ -6,7 +6,7 @@ import {
type NotificationContextCreatedEvent,
type NotificationCreatedEvent
} from '@communication/sdk-types'
import type { NotificationContext, ContextID } from '@communication/types'
import type { NotificationContext, ContextID, CardID } from '@communication/types'
export class Triggers {
constructor(private readonly db: DbAdapter) {}
@@ -21,7 +21,7 @@ export class Triggers {
}
private async createNotifications(event: MessageCreatedEvent, workspace: string): Promise<BroadcastEvent[]> {
const card = event.card
const card = event.message.thread as any as CardID
const subscribedPersonWorkspaces = ['cd0aba36-1c4f-4170-95f2-27a12a5415f7', 'cd0aba36-1c4f-4170-95f2-27a12a5415f8']
const res: BroadcastEvent[] = []
@@ -62,7 +62,7 @@ export class Triggers {
private async getOrCreateContextId(
workspace: string,
card: string,
card: CardID,
personWorkspace: string,
res: BroadcastEvent[],
lastUpdate: Date,
@@ -71,7 +71,7 @@ export class Triggers {
if (context !== undefined) {
return context.id
} else {
const contextId = await this.db.createContext(workspace, card, personWorkspace, undefined, lastUpdate)
const contextId = await this.db.createContext(personWorkspace, workspace, card, undefined, lastUpdate)
const newContext = {
id: contextId,
card,
+10 -7
View File
@@ -10,7 +10,8 @@ import {
type FindNotificationsParams,
type FindNotificationContextParams,
type NotificationContext,
type Notification
type Notification,
type ThreadID
} from '@communication/types'
import type { DbAdapter } from '@communication/sdk-types'
@@ -31,12 +32,14 @@ export class SqliteAdapter implements DbAdapter {
this.notification = new NotificationsDb(worker, dbId)
}
async createMessage(content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
return await this.message.createMessage(content, creator, created)
}
async placeMessage(message: MessageID, card: CardID, workspace: string): Promise<void> {
return await this.message.placeMessage(message, card, workspace)
async createMessage(
workspace: string,
thread: ThreadID,
content: RichText,
creator: SocialID,
created: Date
): Promise<MessageID> {
return await this.message.createMessage(workspace, thread, content, creator, created)
}
async createPatch(message: MessageID, content: RichText, creator: SocialID, created: Date): Promise<void> {
+28 -42
View File
@@ -6,14 +6,13 @@ import {
SortOrder,
type SocialID,
type RichText,
Direction, type Reaction, type Attachment
Direction, type Reaction, type Attachment, type ThreadID
} from '@communication/types'
import {BaseDb} from './base.ts'
import {
TableName,
type MessageDb,
type MessagePlaceDb,
type AttachmentDb,
type ReactionDb,
type PatchDb
@@ -21,9 +20,11 @@ import {
export class MessagesDb extends BaseDb {
//Message
async createMessage(content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
async createMessage(workspace: string, thread: ThreadID, content: RichText, creator: SocialID, created: Date): Promise<MessageID> {
const dbData: MessageDb = {
id: self.crypto.randomUUID(),
workspace_id: workspace,
thread_id: thread,
content: content,
creator: creator,
created: created,
@@ -36,15 +37,6 @@ export class MessagesDb extends BaseDb {
await this.remove(TableName.Message, {id: message})
}
async placeMessage(message: MessageID, card: CardID, workspace: string): Promise<void> {
const dbData: MessagePlaceDb = {
workspace_id: workspace,
card_id: card,
message_id: message
}
await this.insert(TableName.MessagePlace, dbData)
}
async createPatch(message: MessageID, content: RichText, creator: SocialID, created: Date): Promise<void> {
const dbData: PatchDb = {
id: self.crypto.randomUUID(),
@@ -97,6 +89,7 @@ export class MessagesDb extends BaseDb {
//Find messages
async find(workspace: string, params: FindMessagesParams): Promise<Message[]> {
const select = `SELECT m.id,
m.thread_id,
m.content,
m.creator,
m.created,
@@ -124,12 +117,11 @@ export class MessagesDb extends BaseDb {
)
) AS reactions
FROM ${TableName.Message} m
INNER JOIN ${TableName.MessagePlace} mp ON m.id = mp.message_id
LEFT JOIN ${TableName.Patch} p ON p.message_id = m.id
LEFT JOIN ${TableName.Attachment} a ON a.message_id = m.id
LEFT JOIN ${TableName.Reaction} r ON r.message_id = m.id`
LEFT JOIN ${TableName.Patch} p ON p.message_id = m.id
LEFT JOIN ${TableName.Attachment} a ON a.message_id = m.id
LEFT JOIN ${TableName.Reaction} r ON r.message_id = m.id`
const where= this.buildMessageWhere(workspace, params)
const where = this.buildMessageWhere(workspace, params)
const groupBy = `GROUP BY m.id`
const orderBy = params.sort ? `ORDER BY m.created ${params.sort === SortOrder.Asc ? 'ASC' : 'DESC'}` : ''
const limit = params.limit ? ` LIMIT ${params.limit}` : ''
@@ -141,34 +133,27 @@ export class MessagesDb extends BaseDb {
}
buildMessageWhere(workspace: string, params: FindMessagesParams): string {
const where: string[] = [`mp.workspace_id = '${workspace}'`]
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'id': {
where.push(`m.id = '${value}'`)
break
}
case 'card': {
where.push(`mp.card_id = '${value}'`)
break
}
case 'from': {
if(value == null) continue
const exclude = params.excluded ?? false
const direction = params.direction ?? Direction.Forward
const getOperator = () => {
if (exclude) {
return direction === Direction.Forward ? '>' : '<'
} else {
return direction === Direction.Forward ? '>=' : '<='
}
}
const where: string[] = [`m.workspace_id = '${workspace}'`]
where.push(`m.created ${getOperator()} ${value}`)
break
if (params.thread != null) {
where.push(`m.thread_id = '${params.thread}'`)
}
if (params.id != null) {
where.push(`m.id = '${params.id}'`)
}
if (params.from != null) {
const exclude = params.excluded ?? false
const direction = params.direction ?? Direction.Forward
const getOperator = () => {
if (exclude) {
return direction === Direction.Forward ? '>' : '<'
} else {
return direction === Direction.Forward ? '>=' : '<='
}
}
where.push(`m.created ${getOperator()} ${params.from}`)
}
return `WHERE ${where.join(' AND ')}`
@@ -183,6 +168,7 @@ export class MessagesDb extends BaseDb {
return {
id: row.id,
thread: row.thread_id,
content: lastPatch?.content ?? row.content,
creator: row.creator,
created: new Date(row.created),
+61 -71
View File
@@ -9,7 +9,7 @@ import {
} from '@communication/types'
import {BaseDb} from './base.ts'
import {TableName, type ContextDb, type NotificationDb } from './types.ts'
import {TableName, type ContextDb, type NotificationDb} from './types.ts'
export class NotificationsDb extends BaseDb {
async createNotification(message: MessageID, context: ContextID): Promise<void> {
@@ -80,7 +80,13 @@ export class NotificationsDb extends BaseDb {
async findContexts(params: FindNotificationContextParams, personWorkspaces: string[], workspace?: string,): Promise<NotificationContext[]> {
const select = `
SELECT nc.id, nc.card_id, nc.archived_from, nc.last_view, nc.last_update, nc.workspace_id, nc.person_workspace
SELECT nc.id,
nc.card_id,
nc.archived_from,
nc.last_view,
nc.last_update,
nc.workspace_id,
nc.person_workspace
FROM ${TableName.NotificationContext} nc`;
const where = this.buildContextWhere(params, personWorkspaces, workspace);
// const orderSql = `ORDER BY nc.created ${params.sort === SortOrder.Asc ? 'ASC' : 'DESC'}`
@@ -96,104 +102,87 @@ export class NotificationsDb extends BaseDb {
async findNotifications(params: FindNotificationsParams, personWorkspace: string, workspace?: string): Promise<Notification[]> {
//TODO: should join with attachments and reactions?
const select = `
SELECT
n.message_id,
n.context_id,
m.content AS message_content,
m.creator AS message_creator,
m.created AS message_created,
nc.card_id,
nc.archived_from,
nc.last_view,
nc.last_update,
json_group_array(
json_object(
'id', p.id,
'content', p.content,
'creator', p.creator,
'created', p.created
)
) AS patches
FROM
${TableName.Notification} n
JOIN
${TableName.NotificationContext} nc ON n.context_id = nc.id
JOIN
${TableName.Message} m ON n.message_id = m.id
LEFT JOIN
${TableName.Patch} p ON p.message_id = m.id
SELECT n.message_id,
n.context_id,
m.thread_id AS message_thread,
m.content AS message_content,
m.creator AS message_creator,
m.created AS message_created,
nc.card_id,
nc.archived_from,
nc.last_view,
nc.last_update,
json_group_array(
json_object(
'id', p.id,
'content', p.content,
'creator', p.creator,
'created', p.created
)
) AS patches
FROM ${TableName.Notification} n
JOIN
${TableName.NotificationContext} nc ON n.context_id = nc.id
JOIN
${TableName.Message} m ON n.message_id = m.id
LEFT JOIN
${TableName.Patch} p ON p.message_id = m.id
`;
const where = this.buildNotificationWhere(params, personWorkspace, workspace)
const groupBy = `GROUP BY n.message_id, n.context_id, m.id, nc.card_id, nc.archived_from, nc.last_view, nc.last_update`;
const orderBy = `ORDER BY m.created ${params.sort === SortOrder.Asc ? 'ASC' : 'DESC'}`
const limit = params.limit ? ` LIMIT ${params.limit}` : ''
const sql = [select, where, groupBy,orderBy, limit].join(' ')
const sql = [select, where, groupBy, orderBy, limit].join(' ')
const result = await this.select(sql)
return result.map(it => this.toNotification(it));
}
buildContextWhere(params: FindNotificationContextParams, personWorkspaces: string[], workspace?: string, ): string {
buildContextWhere(params: FindNotificationContextParams, personWorkspaces: string[], workspace?: string,): string {
const where: string[] = []
if(workspace != null) {
if (workspace != null) {
where.push(`nc.workspace_id = '${workspace}'`)
}
if(personWorkspaces.length > 0) {
if (personWorkspaces.length > 0) {
where.push(`nc.person_workspace IN (${personWorkspaces.map(it => `'${it}'`).join(', ')})`)
}
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'card': {
where.push(`nc.card_id = '${value}'`)
break
}
}
if (params.card != null) {
where.push(`nc.card_id = '${params.card}'`)
}
return `WHERE ${where.join(' AND ')}`
}
buildNotificationWhere(params: FindNotificationsParams, personWorkspace: string, workspace?: string ): string {
buildNotificationWhere(params: FindNotificationsParams, personWorkspace: string, workspace?: string): string {
const where: string[] = [`nc.person_workspace = '${personWorkspace}'`]
if(workspace != null) {
if (workspace != null) {
where.push(`nc.workspace_id = '${workspace}'`)
}
for (const key of Object.keys(params)) {
const value = (params as any)[key]
switch (key) {
case 'context': {
where.push(`n.context = '${value}'`)
break
}
case 'card': {
where.push(`nc.card_id = '${value}'`)
break
}
case 'read': {
if (value === true) {
where.push(`nc.last_view IS NOT NULL AND nc.last_view >= m.created`)
} else if (value === false) {
where.push(`(nc.last_view IS NULL OR nc.last_view > m.created)`)
}
break
}
case 'archived': {
if (value === true) {
where.push(`nc.archived_from IS NOT NULL AND nc.archived_from >= m.created`)
} else if (value === false) {
where.push(`(nc.archived_from IS NULL OR nc.archived_from > m.created)`)
}
break
}
}
if (params.context != null) {
where.push(`n.context_id = '${params.context}'`)
}
return `WHERE ${where.join(' AND ')}`
if (params.read === true) {
where.push(`nc.last_view IS NOT NULL AND nc.last_view >= m.created`)
}
if (params.read === false) {
where.push(`(nc.last_view IS NULL OR nc.last_view > m.created)`)
}
if (params.archived === true) {
where.push(`nc.archived_from IS NOT NULL AND nc.archived_from >= m.created`)
}
if (params.archived === false) {
where.push(`(nc.archived_from IS NULL OR nc.archived_from > m.created)`)
}
return `WHERE ${where.join(' AND ')}`
}
toNotificationContext(row: any): NotificationContext {
@@ -217,6 +206,7 @@ export class NotificationsDb extends BaseDb {
return {
message: {
id: row.message_id,
thread: row.message_thread,
content: lastPatch?.content ?? row.message_content,
creator: row.message_creator,
created,
+2 -7
View File
@@ -3,7 +3,6 @@ import type {CardID, ContextID, MessageID, RichText, SocialID } from "@communica
export enum TableName {
Message = 'message',
Patch = 'patch',
MessagePlace = 'message_place',
Attachment = 'attachment',
Reaction = 'reaction',
Notification = 'notification',
@@ -12,6 +11,8 @@ export enum TableName {
export interface MessageDb {
id: string
workspace_id: string,
thread_id: string,
content: RichText,
creator: SocialID,
created: Date,
@@ -25,12 +26,6 @@ export interface PatchDb {
created: Date,
}
export interface MessagePlaceDb {
workspace_id: string,
card_id: CardID,
message_id: MessageID
}
export interface ReactionDb {
message_id: MessageID,
reaction: string,
+27 -50
View File
@@ -10,40 +10,17 @@ async function migrationV1(worker: Sqlite3Worker1Promiser, dbId: string): Promis
sql: `
CREATE TABLE IF NOT EXISTS message
(
id TEXT NOT NULL,
content TEXT NOT NULL,
creator TEXT NOT NULL,
created DATETIME NOT NULL,
id TEXT NOT NULL,
workspace_id TEXT NOT NULL,
thread_id TEXT NOT NULL,
content TEXT NOT NULL,
creator TEXT NOT NULL,
created DATETIME NOT NULL,
PRIMARY KEY (id)
)
`
})
await worker('exec', {
dbId,
sql: `
CREATE TABLE IF NOT EXISTS message_place
(
workspace_id TEXT NOT NULL,
card_id TEXT NOT NULL,
message_id TEXT NOT NULL,
PRIMARY KEY (workspace_id, card_id, message_id),
FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE
)
`
})
await worker('exec', {
dbId,
sql: `CREATE INDEX IF NOT EXISTS idx_message_place_workspace_card ON message_place (workspace_id, card_id)`
})
await worker('exec', {
dbId,
sql: `CREATE INDEX IF NOT EXISTS idx_message_place_message_id ON message_place (message_id)`
})
await worker('exec', {
dbId,
sql: `
@@ -110,29 +87,29 @@ async function migrationV1(worker: Sqlite3Worker1Promiser, dbId: string): Promis
await worker('exec', {
dbId,
sql: `
CREATE TABLE IF NOT EXISTS notification_context
(
id TEXT NOT NULL,
workspace_id TEXT NOT NULL,
card_id TEXT NOT NULL,
person_workspace TEXT NOT NULL,
archived_from DATETIME,
last_view DATETIME,
last_update DATETIME,
CREATE TABLE IF NOT EXISTS notification_context
(
id TEXT NOT NULL,
workspace_id TEXT NOT NULL,
card_id TEXT NOT NULL,
person_workspace TEXT NOT NULL,
archived_from DATETIME,
last_view DATETIME,
last_update DATETIME,
PRIMARY KEY (id),
UNIQUE (workspace_id, card_id, person_workspace)
);
PRIMARY KEY (id),
UNIQUE (workspace_id, card_id, person_workspace)
);
CREATE TABLE IF NOT EXISTS notification
(
message_id TEXT NOT NULL,
context_id TEXT NOT NULL,
CREATE TABLE IF NOT EXISTS notification
(
message_id TEXT NOT NULL,
context_id TEXT NOT NULL,
PRIMARY KEY (message_id, context_id),
FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE,
FOREIGN KEY (context_id) REFERENCES notification_context (id) ON DELETE CASCADE
);
`
PRIMARY KEY (message_id, context_id),
FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE,
FOREIGN KEY (context_id) REFERENCES notification_context (id) ON DELETE CASCADE
);
`
})
}
+4 -2
View File
@@ -1,5 +1,6 @@
export type CardID = string
export type SocialID = string
export type CardID = string & { card: true }
export type SocialID = string & { social: true }
export type ThreadID = string & { thread: true }
export type RichText = string
export type ID = string
@@ -12,6 +13,7 @@ interface Object {
export interface Message extends Object {
id: MessageID
thread: ThreadID
content: RichText
edited: Date
reactions: Reaction[]
+2 -2
View File
@@ -1,4 +1,4 @@
import type { CardID, MessageID } from './message'
import type { CardID, MessageID, ThreadID } from './message'
import type { ContextID } from './notification'
export enum SortOrder {
@@ -31,7 +31,7 @@ interface FindParams {
export interface FindMessagesParams extends FindParams {
id?: MessageID
card?: CardID
thread?: ThreadID
}
export interface FindNotificationsParams extends FindParams {