Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3536271
D9598.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D9598.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D9598: [keyserver] Set last_message correctly for threads
Attached
Detach File
Event Timeline
Log In to Comment