From faa0554112aabcd189221f141d45fd5c70b31a1a Mon Sep 17 00:00:00 2001 From: Hakan Shehu Date: Mon, 13 Jan 2025 16:23:36 +0100 Subject: [PATCH] Improve message interactions tracking --- .../src/main/data/workspace/migrations.ts | 3 +- .../desktop/src/main/data/workspace/schema.ts | 3 +- .../mutations/messages/message-mark-seen.ts | 32 ++++++++++++------- .../src/main/services/message-service.ts | 10 +++--- .../src/main/services/radar-service.ts | 6 ++-- apps/desktop/src/main/utils.ts | 4 ++- apps/desktop/src/shared/types/messages.ts | 4 ++- apps/server/src/data/migrations.ts | 3 +- apps/server/src/data/schema.ts | 3 +- apps/server/src/services/message-service.ts | 12 ++++--- .../src/synchronizers/message-interactions.ts | 3 +- .../src/synchronizers/message-interactions.ts | 3 +- 12 files changed, 56 insertions(+), 30 deletions(-) diff --git a/apps/desktop/src/main/data/workspace/migrations.ts b/apps/desktop/src/main/data/workspace/migrations.ts index 7c6bef6b..00ea6b67 100644 --- a/apps/desktop/src/main/data/workspace/migrations.ts +++ b/apps/desktop/src/main/data/workspace/migrations.ts @@ -184,7 +184,8 @@ const createMessageInteractionsTable: Migration = { .addColumn('message_id', 'text', (col) => col.notNull()) .addColumn('collaborator_id', 'text', (col) => col.notNull()) .addColumn('root_id', 'text', (col) => col.notNull()) - .addColumn('seen_at', 'text') + .addColumn('first_seen_at', 'text') + .addColumn('last_seen_at', 'text') .addColumn('first_opened_at', 'text') .addColumn('last_opened_at', 'text') .addColumn('version', 'integer', (col) => col.notNull()) diff --git a/apps/desktop/src/main/data/workspace/schema.ts b/apps/desktop/src/main/data/workspace/schema.ts index 574ae6fa..4dd08f4b 100644 --- a/apps/desktop/src/main/data/workspace/schema.ts +++ b/apps/desktop/src/main/data/workspace/schema.ts @@ -138,7 +138,8 @@ interface MessageInteractionTable { message_id: ColumnType; collaborator_id: ColumnType; root_id: ColumnType; - seen_at: ColumnType; + first_seen_at: ColumnType; + last_seen_at: ColumnType; first_opened_at: ColumnType; last_opened_at: ColumnType; version: ColumnType; diff --git a/apps/desktop/src/main/mutations/messages/message-mark-seen.ts b/apps/desktop/src/main/mutations/messages/message-mark-seen.ts index 1c05d119..c72d5414 100644 --- a/apps/desktop/src/main/mutations/messages/message-mark-seen.ts +++ b/apps/desktop/src/main/mutations/messages/message-mark-seen.ts @@ -38,12 +38,23 @@ export class MessageMarkSeenMutationHandler .where('collaborator_id', '=', input.userId) .executeTakeFirst(); - if (existingInteraction && existingInteraction.seen_at !== null) { - return { - success: true, - }; + if (existingInteraction) { + const lastSeenAt = existingInteraction.last_seen_at; + if ( + lastSeenAt && + lastSeenAt > new Date(Date.now() - 5 * 1000).toISOString() + ) { + return { + success: true, + }; + } } + const lastSeenAt = new Date().toISOString(); + const firstSeenAt = existingInteraction + ? existingInteraction.first_seen_at + : lastSeenAt; + const { createdInteraction, createdMutation } = await workspaceDatabase .transaction() .execute(async (trx) => { @@ -53,17 +64,16 @@ export class MessageMarkSeenMutationHandler .values({ message_id: input.messageId, collaborator_id: input.userId, - seen_at: new Date().toISOString(), + first_seen_at: firstSeenAt, + last_seen_at: lastSeenAt, version: 0n, root_id: message.root_id, }) .onConflict((b) => - b - .columns(['message_id', 'collaborator_id']) - .doUpdateSet({ - seen_at: new Date().toISOString(), - }) - .where('seen_at', 'is', null) + b.columns(['message_id', 'collaborator_id']).doUpdateSet({ + first_seen_at: firstSeenAt, + last_seen_at: lastSeenAt, + }) ) .executeTakeFirst(); diff --git a/apps/desktop/src/main/services/message-service.ts b/apps/desktop/src/main/services/message-service.ts index 23718a00..2d6cfc4c 100644 --- a/apps/desktop/src/main/services/message-service.ts +++ b/apps/desktop/src/main/services/message-service.ts @@ -268,16 +268,18 @@ class MessageService { message_id: messageInteraction.messageId, collaborator_id: messageInteraction.collaboratorId, root_id: messageInteraction.rootId, - seen_at: messageInteraction.seenAt, - last_opened_at: messageInteraction.lastOpenedAt, + first_seen_at: messageInteraction.firstSeenAt, + last_seen_at: messageInteraction.lastSeenAt, first_opened_at: messageInteraction.firstOpenedAt, + last_opened_at: messageInteraction.lastOpenedAt, version, }) .onConflict((b) => b.columns(['message_id', 'collaborator_id']).doUpdateSet({ - seen_at: messageInteraction.seenAt, - last_opened_at: messageInteraction.lastOpenedAt, + first_seen_at: messageInteraction.firstSeenAt, + last_seen_at: messageInteraction.lastSeenAt, first_opened_at: messageInteraction.firstOpenedAt, + last_opened_at: messageInteraction.lastOpenedAt, version, }) ) diff --git a/apps/desktop/src/main/services/radar-service.ts b/apps/desktop/src/main/services/radar-service.ts index 34de964c..be01466a 100644 --- a/apps/desktop/src/main/services/radar-service.ts +++ b/apps/desktop/src/main/services/radar-service.ts @@ -124,7 +124,7 @@ class RadarWorkspace { ) .select(['message.id as message_id', 'message.entry_id as entry_id']) .where('message.created_by', '!=', this.workspace.userId) - .where('message_interactions.seen_at', 'is', null) + .where('message_interactions.last_seen_at', 'is', null) .where('entry_interactions.last_seen_at', 'is not', null) .whereRef('message.created_at', '>=', 'entry_interactions.first_seen_at') .execute(); @@ -149,7 +149,7 @@ class RadarWorkspace { return; } - if (interaction.seenAt) { + if (interaction.lastSeenAt) { const unreadMessage = this.unreadMessages.get(interaction.messageId); if (unreadMessage) { this.unreadMessages.delete(interaction.messageId); @@ -187,7 +187,7 @@ class RadarWorkspace { .where('collaborator_id', '=', this.workspace.userId) .executeTakeFirst(); - if (messageInteraction && messageInteraction.seen_at) { + if (messageInteraction && messageInteraction.last_seen_at) { return; } diff --git a/apps/desktop/src/main/utils.ts b/apps/desktop/src/main/utils.ts index ed7a60f1..7546fd68 100644 --- a/apps/desktop/src/main/utils.ts +++ b/apps/desktop/src/main/utils.ts @@ -345,7 +345,9 @@ export const mapMessageInteraction = ( messageId: row.message_id, collaboratorId: row.collaborator_id, rootId: row.root_id, - seenAt: row.seen_at, + firstSeenAt: row.first_seen_at, + lastSeenAt: row.last_seen_at, + firstOpenedAt: row.first_opened_at, lastOpenedAt: row.last_opened_at, version: row.version, }; diff --git a/apps/desktop/src/shared/types/messages.ts b/apps/desktop/src/shared/types/messages.ts index 49f4be75..fcdba855 100644 --- a/apps/desktop/src/shared/types/messages.ts +++ b/apps/desktop/src/shared/types/messages.ts @@ -31,7 +31,9 @@ export type MessageInteraction = { messageId: string; collaboratorId: string; rootId: string; - seenAt: string | null; + firstSeenAt: string | null; + lastSeenAt: string | null; + firstOpenedAt: string | null; lastOpenedAt: string | null; version: bigint; }; diff --git a/apps/server/src/data/migrations.ts b/apps/server/src/data/migrations.ts index 0f340739..93b5c461 100644 --- a/apps/server/src/data/migrations.ts +++ b/apps/server/src/data/migrations.ts @@ -506,7 +506,8 @@ const createMessageInteractionsTable: Migration = { .addColumn('collaborator_id', 'varchar(30)', (col) => col.notNull()) .addColumn('root_id', 'varchar(30)', (col) => col.notNull()) .addColumn('workspace_id', 'varchar(30)', (col) => col.notNull()) - .addColumn('seen_at', 'timestamptz') + .addColumn('first_seen_at', 'timestamptz') + .addColumn('last_seen_at', 'timestamptz') .addColumn('first_opened_at', 'timestamptz') .addColumn('last_opened_at', 'timestamptz') .addColumn('version', 'bigint', (col) => diff --git a/apps/server/src/data/schema.ts b/apps/server/src/data/schema.ts index 5f4d5cf9..4d360fd1 100644 --- a/apps/server/src/data/schema.ts +++ b/apps/server/src/data/schema.ts @@ -213,7 +213,8 @@ interface MessageInteractionTable { collaborator_id: ColumnType; root_id: ColumnType; workspace_id: ColumnType; - seen_at: ColumnType; + first_seen_at: ColumnType; + last_seen_at: ColumnType; first_opened_at: ColumnType; last_opened_at: ColumnType; version: ColumnType; diff --git a/apps/server/src/services/message-service.ts b/apps/server/src/services/message-service.ts index 4ee48f51..53a6345b 100644 --- a/apps/server/src/services/message-service.ts +++ b/apps/server/src/services/message-service.ts @@ -345,25 +345,29 @@ class MessageService { if ( existingInteraction && - existingInteraction.seen_at !== null && - existingInteraction.seen_at <= new Date(mutation.data.seenAt) + existingInteraction.last_seen_at !== null && + existingInteraction.last_seen_at >= new Date(mutation.data.seenAt) ) { return true; } + const lastSeenAt = new Date(mutation.data.seenAt); + const firstSeenAt = existingInteraction?.first_seen_at ?? lastSeenAt; const createdInteraction = await database .insertInto('message_interactions') .returningAll() .values({ message_id: mutation.data.messageId, collaborator_id: user.id, - seen_at: new Date(mutation.data.seenAt), + first_seen_at: firstSeenAt, + last_seen_at: lastSeenAt, root_id: root.id, workspace_id: root.workspace_id, }) .onConflict((b) => b.columns(['message_id', 'collaborator_id']).doUpdateSet({ - seen_at: new Date(mutation.data.seenAt), + last_seen_at: lastSeenAt, + first_seen_at: firstSeenAt, }) ) .executeTakeFirst(); diff --git a/apps/server/src/synchronizers/message-interactions.ts b/apps/server/src/synchronizers/message-interactions.ts index 99881863..ef843458 100644 --- a/apps/server/src/synchronizers/message-interactions.ts +++ b/apps/server/src/synchronizers/message-interactions.ts @@ -60,7 +60,8 @@ export class MessageInteractionSynchronizer extends BaseSynchronizer ({ messageId: messageInteraction.message_id, collaboratorId: messageInteraction.collaborator_id, - seenAt: messageInteraction.seen_at?.toISOString() ?? null, + firstSeenAt: messageInteraction.first_seen_at?.toISOString() ?? null, + lastSeenAt: messageInteraction.last_seen_at?.toISOString() ?? null, firstOpenedAt: messageInteraction.first_opened_at?.toISOString() ?? null, lastOpenedAt: messageInteraction.last_opened_at?.toISOString() ?? null, diff --git a/packages/core/src/synchronizers/message-interactions.ts b/packages/core/src/synchronizers/message-interactions.ts index 56ac9c40..c843992f 100644 --- a/packages/core/src/synchronizers/message-interactions.ts +++ b/packages/core/src/synchronizers/message-interactions.ts @@ -6,7 +6,8 @@ export type SyncMessageInteractionsInput = { export type SyncMessageInteractionData = { messageId: string; collaboratorId: string; - seenAt: string | null; + firstSeenAt: string | null; + lastSeenAt: string | null; firstOpenedAt: string | null; lastOpenedAt: string | null; rootId: string;