Page MenuHomePhabricator

D9598.diff
No OneTemporary

D9598.diff

diff --git a/keyserver/src/scripts/create-relationships.js b/keyserver/src/scripts/create-relationships.js
--- a/keyserver/src/scripts/create-relationships.js
+++ b/keyserver/src/scripts/create-relationships.js
@@ -50,7 +50,10 @@
});
}
- await saveMemberships(rowsToSave);
+ await saveMemberships({
+ toSave: rowsToSave,
+ updateMembershipsLastMessage: false,
+ });
}
async function createKnowOfRelationships() {
diff --git a/keyserver/src/updaters/thread-permission-updaters.js b/keyserver/src/updaters/thread-permission-updaters.js
--- a/keyserver/src/updaters/thread-permission-updaters.js
+++ b/keyserver/src/updaters/thread-permission-updaters.js
@@ -11,10 +11,12 @@
getRoleForPermissions,
} from 'lib/permissions/thread-permissions.js';
import type { CalendarQuery } from 'lib/types/entry-types.js';
+import { messageTypes } from 'lib/types/message-types-enum.js';
import type {
ThreadPermissionsBlob,
ThreadRolePermissionsBlob,
} from 'lib/types/thread-permission-types.js';
+import { threadPermissions } from 'lib/types/thread-permission-types.js';
import {
type ThreadType,
assertThreadType,
@@ -881,7 +883,15 @@
const membershipInsertBatchSize = 50;
-async function saveMemberships(toSave: $ReadOnlyArray<MembershipRowToSave>) {
+const visibleExtractString = `$.${threadPermissions.VISIBLE}.value`;
+
+async function saveMemberships({
+ toSave,
+ updateMembershipsLastMessage,
+}: {
+ toSave: $ReadOnlyArray<MembershipRowToSave>,
+ updateMembershipsLastMessage: boolean,
+}) {
if (toSave.length === 0) {
return;
}
@@ -933,6 +943,79 @@
`;
await dbQuery(query);
}
+
+ if (!updateMembershipsLastMessage) {
+ return;
+ }
+
+ const joinRows = toSave
+ .filter(row => row.intent === 'join')
+ .map(row => [row.userID, row.threadID, row.unread]);
+
+ if (joinRows.length === 0) {
+ return;
+ }
+
+ const joinedUserThreadPairs = joinRows.map(([user, thread]) => [
+ user,
+ thread,
+ ]);
+
+ const unreadUserThreadPairs = joinRows
+ .filter(([, , unread]) => !!unread)
+ .map(([user, thread]) => [user, thread]);
+
+ let lastReadMessageExpression;
+ if (unreadUserThreadPairs.length === 0) {
+ lastReadMessageExpression = SQL`
+ GREATEST(COALESCE(all_users_query.message, 0),
+ COALESCE(last_subthread_message_for_user_query.message, 0))
+ `;
+ } else {
+ lastReadMessageExpression = SQL`
+ (CASE
+ WHEN ((mm.user, mm.thread) in (${unreadUserThreadPairs})) THEN 0
+ ELSE GREATEST(COALESCE(all_users_query.message, 0),
+ COALESCE(last_subthread_message_for_user_query.message, 0))
+ END)
+ `;
+ }
+
+ // We join two subqueries with the memberships table:
+ // - the first subquery calculates the oldest non-CREATE_SUB_THREAD
+ // message, which is the same for all users
+ // - the second subquery calculates the oldest CREATE_SUB_THREAD messages,
+ // which can be different for each user because of visibility permissions
+ // Then we set the `last_message` column to the greater value of the two.
+ // For `last_read_message` we do the same but only if the user should have
+ // the "unread" status set for this thread.
+ const query = SQL`
+ UPDATE memberships mm
+ LEFT JOIN (
+ SELECT messages.thread, MAX(messages.id) AS message
+ FROM messages
+ WHERE messages.type != ${messageTypes.CREATE_SUB_THREAD}
+ GROUP BY messages.thread
+ ) all_users_query ON mm.thread = all_users_query.thread
+ LEFT JOIN (
+ SELECT m.thread, stm.user, MAX(m.id) AS message
+ FROM messages m
+ LEFT JOIN memberships stm ON m.type = ${messageTypes.CREATE_SUB_THREAD}
+ AND stm.thread = m.content
+ WHERE JSON_EXTRACT(stm.permissions, ${visibleExtractString}) IS TRUE
+ GROUP BY m.thread, stm.user
+ ) last_subthread_message_for_user_query
+ ON mm.thread = last_subthread_message_for_user_query.thread
+ AND mm.user = last_subthread_message_for_user_query.user
+ SET
+ mm.last_message = GREATEST(COALESCE(all_users_query.message, 0),
+ COALESCE(last_subthread_message_for_user_query.message, 0)),
+ mm.last_read_message =
+ `;
+ query.append(lastReadMessageExpression);
+ query.append(SQL`WHERE (mm.user, mm.thread) IN (${joinedUserThreadPairs});`);
+
+ await dbQuery(query);
}
async function deleteMemberships(
@@ -989,10 +1072,12 @@
changedThreadIDs = new Set(),
calendarQuery,
updatesForCurrentSession = 'return',
+ updateMembershipsLastMessage = false,
}: {
+changedThreadIDs?: Set<string>,
+calendarQuery?: ?CalendarQuery,
+updatesForCurrentSession?: UpdatesForCurrentSession,
+ +updateMembershipsLastMessage?: boolean,
} = emptyCommitMembershipChangesetConfig,
): Promise<ChangesetCommitResult> {
if (!viewer.loggedIn) {
@@ -1053,7 +1138,7 @@
const [updateDatas] = await Promise.all([
updateChangedUndirectedRelationships(relationshipRows),
- saveMemberships(toSave),
+ saveMemberships({ toSave, updateMembershipsLastMessage }),
deleteMemberships(toDelete),
rescindPushNotifsForMemberDeletion(toRescindPushNotifs),
]);
diff --git a/keyserver/src/updaters/thread-updaters.js b/keyserver/src/updaters/thread-updaters.js
--- a/keyserver/src/updaters/thread-updaters.js
+++ b/keyserver/src/updaters/thread-updaters.js
@@ -759,6 +759,9 @@
Object.keys(sqlUpdate).length > 0
? new Set([request.threadID])
: new Set(),
+ // last_message will be updated automatically if we send a message,
+ // so we only need to handle it here when we silence new messages
+ updateMembershipsLastMessage: silenceMessages,
});
let newMessageInfos = [];

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 26, 6:03 PM (11 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2707856
Default Alt Text
D9598.diff (5 KB)

Event Timeline