Skip to content

Commit

Permalink
[lib] refactor code to update reply count and unread status to function
Browse files Browse the repository at this point in the history
Summary:
[ENG-9273](https://linear.app/comm/issue/ENG-9273/update-the-reply-count-for-thick-sidebars).

I need to reuse this function to bump reply count as part of `useSendComposableDMOperation`.

Did some additional cleanup to avoid selecting the same data multiple times.

Test Plan: Flow, functionality tested later in the stack

Reviewers: tomek, marcin

Reviewed By: tomek

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D13426
  • Loading branch information
xsanm committed Sep 23, 2024
1 parent 7ec573d commit 0a5c4d4
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 94 deletions.
105 changes: 99 additions & 6 deletions lib/shared/dm-ops/dm-op-utils.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// @flow

import invariant from 'invariant';
import _groupBy from 'lodash/fp/groupBy.js';
import * as React from 'react';
import uuid from 'uuid';

import { type ProcessDMOperationUtilities } from './dm-op-spec.js';
import { dmOpSpecs } from './dm-op-specs.js';
import { useProcessAndSendDMOperation } from './process-dm-ops.js';
import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js';
import type {
CreateThickRawThreadInfoInput,
DMAddMembersOperation,
DMAddViewerToThreadMembersOperation,
DMOperation,
ComposableDMOperation,
} from '../../types/dm-ops.js';
import type { RawMessageInfo } from '../../types/message-types.js';
import type {
RawThreadInfo,
ThickRawThreadInfo,
ThreadInfo,
} from '../../types/minimally-encoded-thread-permissions-types.js';
Expand All @@ -26,14 +31,17 @@ import {
assertThickThreadType,
thickThreadTypes,
} from '../../types/thread-types-enum.js';
import type { RawThreadInfos } from '../../types/thread-types.js';
import type { LegacyRawThreadInfo } from '../../types/thread-types.js';
import {
type DMOperationP2PMessage,
userActionsP2PMessageTypes,
} from '../../types/tunnelbroker/user-actions-peer-to-peer-message-types.js';
import type { CurrentUserInfo } from '../../types/user-types.js';
import { updateTypes } from '../../types/update-types-enum.js';
import type { ClientUpdateInfo } from '../../types/update-types.js';
import { getContentSigningKey } from '../../utils/crypto-utils.js';
import { useSelector } from '../../utils/redux-utils.js';
import { messageSpecs } from '../messages/message-specs.js';
import { updateSpecs } from '../updates/update-specs.js';

function generateMessagesToPeers(
message: DMOperation,
Expand Down Expand Up @@ -113,17 +121,17 @@ async function createMessagesToPeersFromDMOp(
+userID: string,
+deviceID: string,
}>,
currentUserInfo: ?CurrentUserInfo,
threadInfos: RawThreadInfos,
utilities: ProcessDMOperationUtilities,
): Promise<$ReadOnlyArray<OutboundP2PMessage>> {
if (!currentUserInfo?.id) {
const { viewerID, threadInfos } = utilities;
if (!viewerID) {
return [];
}

let peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs;
if (recipients.type === 'self_devices') {
peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs.filter(
peer => peer.userID === currentUserInfo.id,
peer => peer.userID === viewerID,
);
} else if (recipients.type === 'some_users') {
const userIDs = new Set(recipients.userIDs);
Expand Down Expand Up @@ -259,8 +267,93 @@ function useAddDMThreadMembers(): (
);
}

function getThreadUpdatesForNewMessages(
rawMessageInfos: $ReadOnlyArray<RawMessageInfo>,
updateInfos: $ReadOnlyArray<ClientUpdateInfo>,
utilities: ProcessDMOperationUtilities,
): Array<ClientUpdateInfo> {
const { threadInfos, viewerID } = utilities;

const { rawMessageInfos: allNewMessageInfos } = mergeUpdatesWithMessageInfos(
rawMessageInfos,
updateInfos,
);
const messagesByThreadID = _groupBy(message => message.threadID)(
allNewMessageInfos,
);

const updatedThreadInfosByThreadID: {
[string]: RawThreadInfo | LegacyRawThreadInfo,
} = {};
for (const threadID in messagesByThreadID) {
updatedThreadInfosByThreadID[threadID] = threadInfos[threadID];
}
for (const update of updateInfos) {
const updatedThreadInfo = updateSpecs[update.type].getUpdatedThreadInfo?.(
update,
updatedThreadInfosByThreadID,
);
if (updatedThreadInfo) {
updatedThreadInfosByThreadID[updatedThreadInfo.id] = updatedThreadInfo;
}
}

const newUpdateInfos: Array<ClientUpdateInfo> = [];
for (const threadID in messagesByThreadID) {
const repliesCountIncreasingMessages = messagesByThreadID[threadID].filter(
message => messageSpecs[message.type].includedInRepliesCount,
);

let threadInfo = updatedThreadInfosByThreadID[threadID];

if (repliesCountIncreasingMessages.length > 0) {
const repliesCountIncreaseTime = Math.max(
repliesCountIncreasingMessages.map(message => message.time),
);
const newThreadInfo = {
...threadInfo,
repliesCount:
threadInfo.repliesCount + repliesCountIncreasingMessages.length,
};
newUpdateInfos.push({
type: updateTypes.UPDATE_THREAD,
id: uuid.v4(),
time: repliesCountIncreaseTime,
threadInfo: newThreadInfo,
});
threadInfo = newThreadInfo;
}

const messagesFromOtherPeers = messagesByThreadID[threadID].filter(
message => message.creatorID !== viewerID,
);
if (messagesFromOtherPeers.length === 0) {
continue;
}
// We take the most recent timestamp to make sure that
// change_thread_read_status operation older
// than it won't flip the status to read.
const time = Math.max(messagesFromOtherPeers.map(message => message.time));
invariant(threadInfo.thick, 'Thread should be thick');

// We aren't checking if the unread timestamp is lower than the time.
// We're doing this because we want to flip the thread to unread after
// any new message from a non-viewer.
newUpdateInfos.push({
type: updateTypes.UPDATE_THREAD_READ_STATUS,
id: uuid.v4(),
time,
threadID: threadInfo.id,
unread: true,
});
}

return newUpdateInfos;
}

export {
createMessagesToPeersFromDMOp,
useAddDMThreadMembers,
getCreateThickRawThreadInfoInputFromThreadInfo,
getThreadUpdatesForNewMessages,
};
96 changes: 8 additions & 88 deletions lib/shared/dm-ops/process-dm-ops.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// @flow

import invariant from 'invariant';
import _groupBy from 'lodash/fp/groupBy.js';
import * as React from 'react';
import uuid from 'uuid';

import type { ProcessDMOperationUtilities } from './dm-op-spec.js';
import { dmOpSpecs } from './dm-op-specs.js';
Expand All @@ -13,6 +11,7 @@ import {
createMessagesToPeersFromDMOp,
dmOperationSpecificationTypes,
type OutboundComposableDMOperationSpecification,
getThreadUpdatesForNewMessages,
} from './dm-op-utils.js';
import { useProcessBlobHolders } from '../../actions/holder-actions.js';
import {
Expand All @@ -22,7 +21,6 @@ import {
import { useLoggedInUserInfo } from '../../hooks/account-hooks.js';
import { useGetLatestMessageEdit } from '../../hooks/latest-message-edit.js';
import { useDispatchWithMetadata } from '../../hooks/ops-hooks.js';
import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js';
import { getAllPeerUserIDAndDeviceIDs } from '../../selectors/user-selectors.js';
import {
usePeerToPeerCommunication,
Expand All @@ -33,16 +31,11 @@ import {
queueDMOpsActionType,
dmOperationValidator,
} from '../../types/dm-ops.js';
import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
import type { NotificationsCreationData } from '../../types/notif-types.js';
import type { DispatchMetadata } from '../../types/redux-types.js';
import type { OutboundP2PMessage } from '../../types/sqlite-types.js';
import type { LegacyRawThreadInfo } from '../../types/thread-types.js';
import { updateTypes } from '../../types/update-types-enum.js';
import { extractUserIDsFromPayload } from '../../utils/conversion-utils.js';
import { useSelector, useDispatch } from '../../utils/redux-utils.js';
import { messageSpecs } from '../messages/message-specs.js';
import { updateSpecs } from '../updates/update-specs.js';

function useSendDMOperationUtils() {
const fetchMessage = useGetLatestMessageEdit();
Expand All @@ -67,11 +60,9 @@ function useProcessDMOperation(): (
dmOperationSpecification: DMOperationSpecification,
dmOpID: ?string,
) => Promise<void> {
const threadInfos = useSelector(state => state.threadStore.threadInfos);
const baseUtilities = useSendDMOperationUtils();
const dispatchWithMetadata = useDispatchWithMetadata();
const allPeerUserIDAndDeviceIDs = useSelector(getAllPeerUserIDAndDeviceIDs);
const currentUserInfo = useSelector(state => state.currentUserInfo);
const processBlobHolders = useProcessBlobHolders();

const dispatch = useDispatch();
Expand Down Expand Up @@ -101,8 +92,7 @@ function useProcessDMOperation(): (
dmOp,
dmOperationSpecification.recipients,
allPeerUserIDAndDeviceIDs,
currentUserInfo,
threadInfos,
utilities,
);
}

Expand Down Expand Up @@ -247,76 +237,13 @@ function useProcessDMOperation(): (
.filter(Boolean);
void processBlobHolders(holderOps);

const { rawMessageInfos: allNewMessageInfos } =
mergeUpdatesWithMessageInfos(rawMessageInfos, updateInfos);
const messagesByThreadID = _groupBy(message => message.threadID)(
allNewMessageInfos,
const newUpdateInfos = getThreadUpdatesForNewMessages(
rawMessageInfos,
updateInfos,
utilities,
);

const updatedThreadInfosByThreadID: {
[string]: RawThreadInfo | LegacyRawThreadInfo,
} = {};
for (const threadID in messagesByThreadID) {
updatedThreadInfosByThreadID[threadID] = threadInfos[threadID];
}
for (const update of updateInfos) {
const updatedThreadInfo = updateSpecs[
update.type
].getUpdatedThreadInfo?.(update, updatedThreadInfosByThreadID);
if (updatedThreadInfo) {
updatedThreadInfosByThreadID[updatedThreadInfo.id] =
updatedThreadInfo;
}
}

for (const threadID in messagesByThreadID) {
const repliesCountIncreasingMessages = messagesByThreadID[
threadID
].filter(message => messageSpecs[message.type].includedInRepliesCount);

const threadInfo = updatedThreadInfosByThreadID[threadID];

if (repliesCountIncreasingMessages.length > 0) {
const repliesCountIncreaseTime = Math.max(
repliesCountIncreasingMessages.map(message => message.time),
);
updateInfos.push({
type: updateTypes.UPDATE_THREAD,
id: uuid.v4(),
time: repliesCountIncreaseTime,
threadInfo: {
...threadInfo,
repliesCount:
threadInfo.repliesCount + repliesCountIncreasingMessages.length,
},
});
}

const messagesFromOtherPeers = messagesByThreadID[threadID].filter(
message => message.creatorID !== viewerID,
);
if (messagesFromOtherPeers.length === 0) {
continue;
}
// We take the most recent timestamp to make sure that
// change_thread_read_status operation older
// than it won't flip the status to read.
const time = Math.max(
messagesFromOtherPeers.map(message => message.time),
);
invariant(threadInfo.thick, 'Thread should be thick');

// We aren't checking if the unread timestamp is lower than the time.
// We're doing this because we want to flip the thread to unread after
// any new message from a non-viewer.
updateInfos.push({
type: updateTypes.UPDATE_THREAD_READ_STATUS,
id: uuid.v4(),
time,
threadID: threadInfo.id,
unread: true,
});
}
updateInfos.push(...newUpdateInfos);

dispatchWithMetadata(
{
Expand All @@ -336,8 +263,6 @@ function useProcessDMOperation(): (
baseUtilities,
dispatchWithMetadata,
allPeerUserIDAndDeviceIDs,
currentUserInfo,
threadInfos,
dispatch,
processBlobHolders,
],
Expand All @@ -363,11 +288,9 @@ function useProcessAndSendDMOperation(): (
function useSendComposableDMOperation(): (
dmOperationSpecification: OutboundComposableDMOperationSpecification,
) => Promise<ProcessOutboundP2PMessagesResult> {
const threadInfos = useSelector(state => state.threadStore.threadInfos);
const { getDMOpsSendingPromise } = usePeerToPeerCommunication();
const dispatchWithMetadata = useDispatchWithMetadata();
const allPeerUserIDAndDeviceIDs = useSelector(getAllPeerUserIDAndDeviceIDs);
const currentUserInfo = useSelector(state => state.currentUserInfo);
const baseUtilities = useSendDMOperationUtils();
const { processOutboundMessages } = usePeerToPeerCommunication();
const localMessageInfos = useSelector(state => state.messageStore.local);
Expand Down Expand Up @@ -418,8 +341,7 @@ function useSendComposableDMOperation(): (
op,
recipients,
allPeerUserIDAndDeviceIDs,
currentUserInfo,
threadInfos,
utilities,
);

const notificationsCreationData = await dmOpSpecs[
Expand Down Expand Up @@ -456,12 +378,10 @@ function useSendComposableDMOperation(): (
},
[
allPeerUserIDAndDeviceIDs,
currentUserInfo,
dispatchWithMetadata,
getDMOpsSendingPromise,
localMessageInfos,
processOutboundMessages,
threadInfos,
baseUtilities,
],
);
Expand Down

0 comments on commit 0a5c4d4

Please sign in to comment.