Skip to content

Commit 3e9f0f1

Browse files
authored
Merge pull request oxen-io#2560 from Bilb/mark-read-do-not-return-messages-if-not-needed
fix: do not return updated messages from markAllRead if not needed
2 parents ff37dee + 0b8a10a commit 3e9f0f1

File tree

7 files changed

+88
-32
lines changed

7 files changed

+88
-32
lines changed

ts/data/data.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,17 @@ async function getUnreadByConversation(conversationId: string): Promise<MessageC
493493
}
494494

495495
async function markAllAsReadByConversationNoExpiration(
496-
conversationId: string
496+
conversationId: string,
497+
returnMessagesUpdated: boolean // for performance reason we do not return them because usually they are not needed
497498
): Promise<Array<number>> {
498-
const messagesIds = await channels.markAllAsReadByConversationNoExpiration(conversationId);
499+
// tslint:disable-next-line: no-console
500+
console.time('markAllAsReadByConversationNoExpiration');
501+
const messagesIds = await channels.markAllAsReadByConversationNoExpiration(
502+
conversationId,
503+
returnMessagesUpdated
504+
);
505+
// tslint:disable-next-line: no-console
506+
console.timeEnd('markAllAsReadByConversationNoExpiration');
499507
return messagesIds;
500508
}
501509

ts/interactions/conversationInteractions.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,7 @@ export function showLeaveGroupByConvoId(conversationId: string) {
254254
export function showInviteContactByConvoId(conversationId: string) {
255255
window.inboxStore?.dispatch(updateInviteContactModal({ conversationId }));
256256
}
257-
export async function onMarkAllReadByConvoId(conversationId: string) {
258-
const conversation = getConversationController().get(conversationId);
259257

260-
await conversation.markReadBouncy(Date.now());
261-
}
262258

263259
export function showAddModeratorsByConvoId(conversationId: string) {
264260
window.inboxStore?.dispatch(updateAddModeratorsModal({ conversationId }));

ts/models/conversation.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
actions as conversationActions,
3333
conversationChanged,
3434
conversationsChanged,
35+
markConversationFullyRead,
3536
MessageModelPropsWithoutConvoProps,
3637
ReduxConversationType,
3738
} from '../state/ducks/conversations';
@@ -1231,27 +1232,38 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
12311232
* Send read receipt if needed.
12321233
*/
12331234
public async markAllAsRead() {
1234-
if (this.isOpenGroupV2()) {
1235+
/**
1236+
* when marking all as read, there is a bunch of things we need to do.
1237+
* - we need to update all the messages in the DB not read yet for that conversation
1238+
* - we need to send the read receipts if there is one needed for those messages
1239+
* - we need to trigger a change on the redux store, so those messages are read AND mark the whole convo as read.
1240+
* - we need to remove any notifications related to this conversation ID.
1241+
*
1242+
*
1243+
* (if there is an expireTimer, we do it the slow way, handling each message separately)
1244+
*/
1245+
const expireTimerSet = !!this.get('expireTimer');
1246+
if (this.isOpenGroupV2() || !expireTimerSet) {
12351247
// for opengroups, we batch everything as there is no expiration timer to take care (and potentially a lot of messages)
12361248

1237-
await Data.markAllAsReadByConversationNoExpiration(this.id);
1249+
const isOpenGroup = this.isOpenGroupV2();
1250+
// if this is an opengroup there is no need to send read receipt, and so no need to fetch messages updated.
1251+
const allReadMessages = await Data.markAllAsReadByConversationNoExpiration(
1252+
this.id,
1253+
!isOpenGroup
1254+
);
12381255
this.set({ mentionedUs: false, unreadCount: 0 });
12391256

12401257
await this.commit();
1241-
return;
1242-
}
1243-
1244-
// if the conversation has no expiration timer, we can also batch everything, but we also need to send read receipts potentially
1245-
// so we grab them from the db
1246-
if (!this.get('expireTimer')) {
1247-
const allReadMessages = await Data.markAllAsReadByConversationNoExpiration(this.id);
1248-
this.set({ mentionedUs: false, unreadCount: 0 });
1249-
await this.commit();
1250-
if (allReadMessages.length) {
1258+
if (!this.isOpenGroupV2() && allReadMessages.length) {
12511259
await this.sendReadReceiptsIfNeeded(uniq(allReadMessages));
12521260
}
1261+
Notifications.clearByConversationID(this.id);
1262+
window.inboxStore?.dispatch(markConversationFullyRead(this.id));
1263+
12531264
return;
12541265
}
1266+
// otherwise, do it the slow way
12551267
await this.markReadBouncy(Date.now());
12561268
}
12571269

ts/node/database_utility.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ export function rebuildFtsTable(db: BetterSqlite3.Database) {
264264
CREATE TRIGGER messages_on_delete AFTER DELETE ON ${MESSAGES_TABLE} BEGIN
265265
DELETE FROM ${MESSAGES_FTS_TABLE} WHERE id = old.id;
266266
END;
267-
CREATE TRIGGER messages_on_update AFTER UPDATE ON ${MESSAGES_TABLE} BEGIN
267+
CREATE TRIGGER messages_on_update AFTER UPDATE ON ${MESSAGES_TABLE} WHEN new.body <> old.body BEGIN
268268
DELETE FROM ${MESSAGES_FTS_TABLE} WHERE id = old.id;
269269
INSERT INTO ${MESSAGES_FTS_TABLE}(
270270
id,

ts/node/migration/sessionMigrations.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const LOKI_SCHEMA_VERSIONS = [
7878
updateToSessionSchemaVersion26,
7979
updateToSessionSchemaVersion27,
8080
updateToSessionSchemaVersion28,
81+
updateToSessionSchemaVersion29,
8182
];
8283

8384
function updateToSessionSchemaVersion1(currentVersion: number, db: BetterSqlite3.Database) {
@@ -1181,6 +1182,28 @@ function updateToSessionSchemaVersion28(currentVersion: number, db: BetterSqlite
11811182
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
11821183
}
11831184

1185+
function updateToSessionSchemaVersion29(currentVersion: number, db: BetterSqlite3.Database) {
1186+
const targetVersion = 29;
1187+
if (currentVersion >= targetVersion) {
1188+
return;
1189+
}
1190+
1191+
console.log(`updateToSessionSchemaVersion${targetVersion}: starting...`);
1192+
1193+
db.transaction(() => {
1194+
dropFtsAndTriggers(db);
1195+
db.exec(`CREATE INDEX messages_unread_by_conversation ON ${MESSAGES_TABLE} (
1196+
unread,
1197+
conversationId
1198+
);`);
1199+
rebuildFtsTable(db);
1200+
// Keeping this empty migration because some people updated to this already, even if it is not needed anymore
1201+
writeSessionSchemaVersion(targetVersion, db);
1202+
})();
1203+
1204+
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
1205+
}
1206+
11841207
// function printTableColumns(table: string, db: BetterSqlite3.Database) {
11851208
// console.info(db.pragma(`table_info('${table}');`));
11861209
// }

ts/node/sql.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,18 +1117,23 @@ function getUnreadByConversation(conversationId: string) {
11171117
* Warning: This does not start expiration timer
11181118
*/
11191119
function markAllAsReadByConversationNoExpiration(
1120-
conversationId: string
1121-
): Array<{ id: string; timestamp: number }> {
1122-
const messagesUnreadBefore = assertGlobalInstance()
1123-
.prepare(
1124-
`SELECT json FROM ${MESSAGES_TABLE} WHERE
1125-
unread = $unread AND
1126-
conversationId = $conversationId;`
1127-
)
1128-
.all({
1129-
unread: 1,
1130-
conversationId,
1131-
});
1120+
conversationId: string,
1121+
returnMessagesUpdated: boolean
1122+
): Array<number> {
1123+
let toReturn: Array<number> = [];
1124+
if (returnMessagesUpdated) {
1125+
const messagesUnreadBefore = assertGlobalInstance()
1126+
.prepare(
1127+
`SELECT json FROM ${MESSAGES_TABLE} WHERE
1128+
unread = $unread AND
1129+
conversationId = $conversationId;`
1130+
)
1131+
.all({
1132+
unread: 1,
1133+
conversationId,
1134+
});
1135+
toReturn = compact(messagesUnreadBefore.map(row => jsonToObject(row.json).sent_at));
1136+
}
11321137

11331138
assertGlobalInstance()
11341139
.prepare(
@@ -1142,7 +1147,7 @@ function markAllAsReadByConversationNoExpiration(
11421147
conversationId,
11431148
});
11441149

1145-
return compact(messagesUnreadBefore.map(row => jsonToObject(row.json).sent_at));
1150+
return toReturn;
11461151
}
11471152

11481153
function getUnreadCountByConversation(conversationId: string) {

ts/state/ducks/conversations.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,11 +699,23 @@ const conversationsSlice = createSlice({
699699
return state;
700700
}
701701

702+
let updatedMessages = state.messages;
703+
704+
// if some are unread, mark them as read
705+
if (state.messages.some(m => m.propsForMessage.isUnread)) {
706+
updatedMessages = state.messages.map(m => ({
707+
...m,
708+
propsForMessage: { ...m.propsForMessage, isUnread: false },
709+
}));
710+
}
711+
702712
// keep the unread visible just like in other apps. It will be shown until the user changes convo
703713
return {
704714
...state,
705715
shouldHighlightMessage: false,
706716
firstUnreadMessageId: undefined,
717+
718+
messages: updatedMessages,
707719
};
708720
},
709721
/**

0 commit comments

Comments
 (0)