Skip to content

Commit

Permalink
[lib] refactor Message Store Operations to use generic type
Browse files Browse the repository at this point in the history
Summary:
Make logic part of the general ops interface.

Note: Messages has a lot of different converters of a different types, so this could not be fitted in generic spec, so I chose the most general converter.

Depends on D8621

Test Plan: `Flow`

Reviewers: michal, tomek, atul

Reviewed By: atul

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D8622
  • Loading branch information
xsanm committed Jul 26, 2023
1 parent 23d408e commit 4fa8840
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 124 deletions.
118 changes: 118 additions & 0 deletions lib/ops/message-store-ops.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// @flow

import { type BaseStoreOpsHandlers } from './base-ops.js';
import type {
ClientDBMessageInfo,
ClientDBThreadMessageInfo,
MessageStore,
MessageStoreThreads,
RawMessageInfo,
} from '../types/message-types.js';
import {
translateClientDBMessageInfoToRawMessageInfo,
translateRawMessageInfoToClientDBMessageInfo,
translateThreadMessageInfoToClientDBThreadMessageInfo,
} from '../utils/message-ops-utils.js';

// MessageStore messages ops
export type RemoveMessageOperation = {
Expand Down Expand Up @@ -76,3 +83,114 @@ export type ClientDBMessageStoreOperation =
| ClientDBReplaceThreadsOperation
| RemoveMessageStoreThreadsOperation
| RemoveMessageStoreAllThreadsOperation;

export const messageStoreOpsHandlers: BaseStoreOpsHandlers<
MessageStore,
MessageStoreOperation,
ClientDBMessageStoreOperation,
{ +[id: string]: RawMessageInfo },
ClientDBMessageInfo,
> = {
processStoreOperations(
store: MessageStore,
ops: $ReadOnlyArray<MessageStoreOperation>,
): MessageStore {
if (ops.length === 0) {
return store;
}
let processedMessages = { ...store.messages };
let processedThreads = { ...store.threads };
for (const operation of ops) {
if (operation.type === 'replace') {
processedMessages[operation.payload.id] = operation.payload.messageInfo;
} else if (operation.type === 'remove') {
for (const id of operation.payload.ids) {
delete processedMessages[id];
}
} else if (operation.type === 'remove_messages_for_threads') {
for (const msgID in processedMessages) {
if (
operation.payload.threadIDs.includes(
processedMessages[msgID].threadID,
)
) {
delete processedMessages[msgID];
}
}
} else if (operation.type === 'rekey') {
processedMessages[operation.payload.to] =
processedMessages[operation.payload.from];
delete processedMessages[operation.payload.from];
} else if (operation.type === 'remove_all') {
processedMessages = {};
} else if (operation.type === 'replace_threads') {
for (const threadID in operation.payload.threads) {
processedThreads[threadID] = operation.payload.threads[threadID];
}
} else if (operation.type === 'remove_threads') {
for (const id of operation.payload.ids) {
delete processedThreads[id];
}
} else if (operation.type === 'remove_all_threads') {
processedThreads = {};
}
}
return {
...store,
threads: processedThreads,
messages: processedMessages,
};
},

convertOpsToClientDBOps(
ops: $ReadOnlyArray<MessageStoreOperation>,
): $ReadOnlyArray<ClientDBMessageStoreOperation> {
const convertedOperations = ops.map(messageStoreOperation => {
if (messageStoreOperation.type === 'replace') {
return {
type: 'replace',
payload: translateRawMessageInfoToClientDBMessageInfo(
messageStoreOperation.payload.messageInfo,
),
};
}

if (messageStoreOperation.type !== 'replace_threads') {
return messageStoreOperation;
}

const threadMessageInfo: MessageStoreThreads =
messageStoreOperation.payload.threads;
const dbThreadMessageInfos: ClientDBThreadMessageInfo[] = [];
for (const threadID in threadMessageInfo) {
dbThreadMessageInfos.push(
translateThreadMessageInfoToClientDBThreadMessageInfo(
threadID,
threadMessageInfo[threadID],
),
);
}
if (dbThreadMessageInfos.length === 0) {
return undefined;
}
return {
type: 'replace_threads',
payload: {
threads: dbThreadMessageInfos,
},
};
});
return convertedOperations.filter(Boolean);
},

translateClientDBData(data: $ReadOnlyArray<ClientDBMessageInfo>): {
+[id: string]: RawMessageInfo,
} {
return Object.fromEntries(
data.map((dbMessageInfo: ClientDBMessageInfo) => [
dbMessageInfo.id,
translateClientDBMessageInfoToRawMessageInfo(dbMessageInfo),
]),
);
},
};
66 changes: 8 additions & 58 deletions lib/reducers/message-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ import {
logInActionTypes,
registerActionTypes,
} from '../actions/user-actions.js';
import type {
MessageStoreOperation,
ReplaceMessageOperation,
import {
messageStoreOpsHandlers,
type MessageStoreOperation,
type ReplaceMessageOperation,
} from '../ops/message-store-ops.js';
import { pendingToRealizedThreadIDsSelector } from '../selectors/thread-selectors.js';
import {
Expand Down Expand Up @@ -101,10 +102,7 @@ import {
} from '../types/update-types.js';
import { setNewSessionActionType } from '../utils/action-utils.js';
import { isDev } from '../utils/dev-utils.js';
import {
translateClientDBMessageInfosToRawMessageInfos,
translateClientDBThreadMessageInfos,
} from '../utils/message-ops-utils.js';
import { translateClientDBThreadMessageInfos } from '../utils/message-ops-utils.js';
import { assertObjectsAreEqual } from '../utils/objects.js';

const PROCESSED_MSG_STORE_INVARIANTS_DISABLED = !isDev;
Expand Down Expand Up @@ -740,56 +738,8 @@ function ensureRealizedThreadIDIsUsedWhenPossible<T: RawMessageInfo>(
: payload;
}

function processMessageStoreOperations(
messageStore: MessageStore,
messageStoreOperations: $ReadOnlyArray<MessageStoreOperation>,
): MessageStore {
if (messageStoreOperations.length === 0) {
return messageStore;
}
let processedMessages = { ...messageStore.messages };
let processedThreads = { ...messageStore.threads };
for (const operation of messageStoreOperations) {
if (operation.type === 'replace') {
processedMessages[operation.payload.id] = operation.payload.messageInfo;
} else if (operation.type === 'remove') {
for (const id of operation.payload.ids) {
delete processedMessages[id];
}
} else if (operation.type === 'remove_messages_for_threads') {
for (const msgID in processedMessages) {
if (
operation.payload.threadIDs.includes(
processedMessages[msgID].threadID,
)
) {
delete processedMessages[msgID];
}
}
} else if (operation.type === 'rekey') {
processedMessages[operation.payload.to] =
processedMessages[operation.payload.from];
delete processedMessages[operation.payload.from];
} else if (operation.type === 'remove_all') {
processedMessages = {};
} else if (operation.type === 'replace_threads') {
for (const threadID in operation.payload.threads) {
processedThreads[threadID] = operation.payload.threads[threadID];
}
} else if (operation.type === 'remove_threads') {
for (const id of operation.payload.ids) {
delete processedThreads[id];
}
} else if (operation.type === 'remove_all_threads') {
processedThreads = {};
}
}
return {
...messageStore,
threads: processedThreads,
messages: processedMessages,
};
}
const { processStoreOperations: processMessageStoreOperations } =
messageStoreOpsHandlers;

type ReduceMessageStoreResult = {
+messageStoreOperations: $ReadOnlyArray<MessageStoreOperation>,
Expand Down Expand Up @@ -1705,7 +1655,7 @@ function reduceMessageStore(
}
const threadsNeedMsgIDsResorting = new Set();
const actionPayloadMessages =
translateClientDBMessageInfosToRawMessageInfos(payloadMessages);
messageStoreOpsHandlers.translateClientDBData(payloadMessages);

// When starting the app on native, we filter out any local-only multimedia
// messages because the relevant context is no longer available
Expand Down
61 changes: 0 additions & 61 deletions lib/utils/message-ops-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import {
encryptedMediaBlobURI,
encryptedVideoThumbnailBlobURI,
} from '../media/media-utils.js';
import type {
ClientDBMessageStoreOperation,
MessageStoreOperation,
} from '../ops/message-store-ops.js';
import { messageID } from '../shared/message-utils.js';
import { messageSpecs } from '../shared/messages/message-specs.js';
import type {
Expand All @@ -29,7 +25,6 @@ import {
type RawMessageInfo,
type ClientDBThreadMessageInfo,
type ThreadMessageInfo,
type MessageStoreThreads,
} from '../types/message-types.js';
import type { MediaMessageServerDBContent } from '../types/messages/media.js';

Expand Down Expand Up @@ -246,17 +241,6 @@ function translateClientDBMessageInfoToRawMessageInfo(
].rawMessageInfoFromClientDB(clientDBMessageInfo);
}

function translateClientDBMessageInfosToRawMessageInfos(
clientDBMessageInfos: $ReadOnlyArray<ClientDBMessageInfo>,
): { +[id: string]: RawMessageInfo } {
return Object.fromEntries(
clientDBMessageInfos.map((dbMessageInfo: ClientDBMessageInfo) => [
dbMessageInfo.id,
translateClientDBMessageInfoToRawMessageInfo(dbMessageInfo),
]),
);
}

type TranslatedThreadMessageInfo = {
+startReached: boolean,
+lastNavigatedTo: number,
Expand Down Expand Up @@ -295,49 +279,6 @@ function translateThreadMessageInfoToClientDBThreadMessageInfo(
};
}

function convertMessageStoreOperationsToClientDBOperations(
messageStoreOperations: $ReadOnlyArray<MessageStoreOperation>,
): $ReadOnlyArray<ClientDBMessageStoreOperation> {
const convertedOperations = messageStoreOperations.map(
messageStoreOperation => {
if (messageStoreOperation.type === 'replace') {
return {
type: 'replace',
payload: translateRawMessageInfoToClientDBMessageInfo(
messageStoreOperation.payload.messageInfo,
),
};
}

if (messageStoreOperation.type !== 'replace_threads') {
return messageStoreOperation;
}

const threadMessageInfo: MessageStoreThreads =
messageStoreOperation.payload.threads;
const dbThreadMessageInfos: ClientDBThreadMessageInfo[] = [];
for (const threadID in threadMessageInfo) {
dbThreadMessageInfos.push(
translateThreadMessageInfoToClientDBThreadMessageInfo(
threadID,
threadMessageInfo[threadID],
),
);
}
if (dbThreadMessageInfos.length === 0) {
return undefined;
}
return {
type: 'replace_threads',
payload: {
threads: dbThreadMessageInfos,
},
};
},
);
return convertedOperations.filter(Boolean);
}

function getPinnedContentFromClientDBMessageInfo(
clientDBMessageInfo: ClientDBMessageInfo,
): string {
Expand All @@ -357,8 +298,6 @@ export {
translateClientDBMediaInfoToImage,
translateRawMessageInfoToClientDBMessageInfo,
translateClientDBMessageInfoToRawMessageInfo,
translateClientDBMessageInfosToRawMessageInfos,
convertMessageStoreOperationsToClientDBOperations,
translateClientDBMediaInfosToMedia,
getPinnedContentFromClientDBMessageInfo,
translateClientDBThreadMessageInfos,
Expand Down
8 changes: 5 additions & 3 deletions native/redux/persist.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
convertCalendarFilterToNewIDSchema,
convertConnectionInfoToNewIDSchema,
} from 'lib/_generated/migration-utils.js';
import type { ClientDBMessageStoreOperation } from 'lib/ops/message-store-ops.js';
import {
type ClientDBMessageStoreOperation,
messageStoreOpsHandlers,
} from 'lib/ops/message-store-ops.js';
import {
type ReportStoreOperation,
type ClientDBReportStoreOperation,
Expand Down Expand Up @@ -49,7 +52,6 @@ import type {
import { defaultConnectionInfo } from 'lib/types/socket-types.js';
import type { ClientDBThreadInfo } from 'lib/types/thread-types.js';
import {
convertMessageStoreOperationsToClientDBOperations,
translateClientDBMessageInfoToRawMessageInfo,
translateRawMessageInfoToClientDBMessageInfo,
} from 'lib/utils/message-ops-utils.js';
Expand Down Expand Up @@ -537,7 +539,7 @@ const migrations = {
return state;
},
[37]: state => {
const operations = convertMessageStoreOperationsToClientDBOperations([
const operations = messageStoreOpsHandlers.convertOpsToClientDBOps([
{
type: 'remove_all_threads',
},
Expand Down
4 changes: 2 additions & 2 deletions native/redux/redux-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import { useSelector as reactReduxUseSelector } from 'react-redux';

import { messageStoreOpsHandlers } from 'lib/ops/message-store-ops.js';
import { reportStoreOpsHandlers } from 'lib/ops/report-store-ops.js';
import { threadStoreOpsHandlers } from 'lib/ops/thread-store-ops.js';
import type { StoreOperations } from 'lib/types/store-ops-types.js';
import { convertMessageStoreOperationsToClientDBOperations } from 'lib/utils/message-ops-utils.js';

import type { AppState } from './state-types.js';
import { commCoreModule } from '../native-modules.js';
Expand All @@ -31,7 +31,7 @@ async function processDBStoreOperations(
const convertedThreadStoreOperations =
threadStoreOpsHandlers.convertOpsToClientDBOps(threadStoreOperations);
const convertedMessageStoreOperations =
convertMessageStoreOperationsToClientDBOperations(messageStoreOperations);
messageStoreOpsHandlers.convertOpsToClientDBOps(messageStoreOperations);
const convertedReportStoreOperations =
reportStoreOpsHandlers.convertOpsToClientDBOps(reportStoreOperations);

Expand Down

0 comments on commit 4fa8840

Please sign in to comment.