diff --git a/lib/reducers/master-reducer.js b/lib/reducers/master-reducer.js --- a/lib/reducers/master-reducer.js +++ b/lib/reducers/master-reducer.js @@ -37,6 +37,7 @@ keyserverStoreOpsHandlers, type ReplaceKeyserverOperation, } from '../ops/keyserver-store-ops.js'; +import { createMessagesToPeersFromAction } from '../shared/dm-ops/dm-op-utils.js'; import { isStaff } from '../shared/staff-utils.js'; import type { BaseNavInfo } from '../types/nav-types.js'; import type { BaseAppState, BaseAction } from '../types/redux-types.js'; @@ -44,6 +45,7 @@ fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types.js'; +import type { OutboundP2PMessage } from '../types/sqlite-types.js'; import type { StoreOperations } from '../types/store-ops-types.js'; import { isDev } from '../utils/dev-utils.js'; @@ -196,6 +198,11 @@ const { threadActivityStore, threadActivityStoreOperations } = reduceThreadActivity(state.threadActivityStore, action); + const currentUserInfo = reduceCurrentUserInfo(state.currentUserInfo, action); + + const outboundP2PMessages: $ReadOnlyArray = + createMessagesToPeersFromAction(action, auxUserStore, currentUserInfo); + return { state: { ...state, @@ -203,7 +210,7 @@ draftStore, entryStore, loadingStatuses: reduceLoadingStatuses(state.loadingStatuses, action), - currentUserInfo: reduceCurrentUserInfo(state.currentUserInfo, action), + currentUserInfo, threadStore, userStore, messageStore, @@ -246,6 +253,7 @@ auxUserStoreOperations, threadActivityStoreOperations, entryStoreOperations, + outboundP2PMessages, }, }; } diff --git a/lib/shared/dm-ops/create-sidebar-spec.js b/lib/shared/dm-ops/create-sidebar-spec.js --- a/lib/shared/dm-ops/create-sidebar-spec.js +++ b/lib/shared/dm-ops/create-sidebar-spec.js @@ -103,6 +103,7 @@ updateInfos: [threadJoinUpdateInfo], }; }, + fromAction: () => null, }); export { createSidebarSpec }; diff --git a/lib/shared/dm-ops/create-thread-spec.js b/lib/shared/dm-ops/create-thread-spec.js --- a/lib/shared/dm-ops/create-thread-spec.js +++ b/lib/shared/dm-ops/create-thread-spec.js @@ -157,6 +157,7 @@ updateInfos: [threadJoinUpdateInfo], }; }, + fromAction: () => null, }); export { createThickRawThreadInfo, createThreadSpec }; diff --git a/lib/shared/dm-ops/dm-op-spec.js b/lib/shared/dm-ops/dm-op-spec.js --- a/lib/shared/dm-ops/dm-op-spec.js +++ b/lib/shared/dm-ops/dm-op-spec.js @@ -2,16 +2,24 @@ import type { DMOperation, DMOperationResult } from '../../types/dm-ops.js'; import type { RawMessageInfo } from '../../types/message-types.js'; +import type { BaseAction } from '../../types/redux-types.js'; export type ProcessDMOperationUtilities = { // Needed to fetch sidebar source messages +fetchMessage: (messageID: string) => Promise, }; +export type DMOperationSpecification = { + +op: DMOp, + +supportsAutoRetry: boolean, + +recipients: 'all_peer_devices' | 'self_devices', +}; + export type DMOperationSpec = { +processDMOperation: ( dmOp: DMOp, viewerID: string, utilities: ProcessDMOperationUtilities, ) => Promise, + +fromAction: (action: BaseAction) => ?DMOperationSpecification, }; diff --git a/lib/shared/dm-ops/dm-op-utils.js b/lib/shared/dm-ops/dm-op-utils.js new file mode 100644 --- /dev/null +++ b/lib/shared/dm-ops/dm-op-utils.js @@ -0,0 +1,81 @@ +// @flow + +import type { DMOperationSpecification } from './dm-op-spec.js'; +import { dmOpSpecs } from './dm-op-specs.js'; +import type { AuxUserStore } from '../../types/aux-user-types.js'; +import type { DMOperation } from '../../types/dm-ops.js'; +import type { BaseAction } from '../../types/redux-types.js'; +import type { OutboundP2PMessage } from '../../types/sqlite-types.js'; +import { outboundP2PMessageStatuses } from '../../types/sqlite-types.js'; +import type { CurrentUserInfo } from '../../types/user-types.js'; +import { values } from '../../utils/objects.js'; +import { getUUID } from '../../utils/uuid.js'; + +function generateMessagesToPeers( + message: DMOperation, + peers: $ReadOnlyArray, + userID: string, + supportsAutoRetry: boolean, +): $ReadOnlyArray { + const outboundP2PMessages = []; + for (const peerID of peers) { + const messageToPeer: OutboundP2PMessage = { + messageID: getUUID(), + deviceID: peerID, + userID, + timestamp: new Date().getTime().toString(), + plaintext: JSON.stringify(message), + ciphertext: '', + status: outboundP2PMessageStatuses.persisted, + supportsAutoRetry: supportsAutoRetry ? '1' : '0', + }; + outboundP2PMessages.push(messageToPeer); + } + return outboundP2PMessages; +} + +function createOpsFromAction( + action: BaseAction, +): $ReadOnlyArray> { + const result = []; + for (const spec of values(dmOpSpecs)) { + const op = spec.fromAction(action); + if (op) { + result.push(op); + } + } + return result; +} + +function createMessagesToPeersFromAction( + action: BaseAction, + auxUserStore: AuxUserStore, + currentUserInfo: ?CurrentUserInfo, +): $ReadOnlyArray { + if (!currentUserInfo?.id) { + return []; + } + const ops = createOpsFromAction(action); + if (ops.length === 0) { + return []; + } + const selfDevices = + auxUserStore.auxUserInfos[currentUserInfo.id].deviceList?.devices ?? []; + const allPeerDevices = values(auxUserStore.auxUserInfos) + .map(info => info.deviceList?.devices ?? []) + .flat(); + const result: Array = []; + for (const op of ops) { + result.push( + ...generateMessagesToPeers( + op.op, + op.recipients === 'all_peer_devices' ? allPeerDevices : selfDevices, + currentUserInfo.id, + op.supportsAutoRetry, + ), + ); + } + return result; +} + +export { createMessagesToPeersFromAction }; diff --git a/lib/shared/dm-ops/send-text-message-spec.js b/lib/shared/dm-ops/send-text-message-spec.js --- a/lib/shared/dm-ops/send-text-message-spec.js +++ b/lib/shared/dm-ops/send-text-message-spec.js @@ -37,6 +37,7 @@ updateInfos, }; }, + fromAction: () => null, }); export { sendTextMessageSpec }; diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js --- a/lib/types/sqlite-types.js +++ b/lib/types/sqlite-types.js @@ -26,6 +26,7 @@ +status: string, }; +// How to get it from a DMOperation? export type OutboundP2PMessage = { +messageID: string, +deviceID: string,