diff --git a/lib/shared/redux/client-db-utils.js b/lib/shared/redux/client-db-utils.js index 5f9ea44f0..9586fb155 100644 --- a/lib/shared/redux/client-db-utils.js +++ b/lib/shared/redux/client-db-utils.js @@ -1,49 +1,132 @@ // @flow import _keyBy from 'lodash/fp/keyBy.js'; +import { auxUserStoreOpsHandlers } from '../../ops/aux-user-store-ops.js'; +import { communityStoreOpsHandlers } from '../../ops/community-store-ops.js'; +import { entryStoreOpsHandlers } from '../../ops/entries-store-ops.js'; +import { integrityStoreOpsHandlers } from '../../ops/integrity-store-ops.js'; +import { keyserverStoreOpsHandlers } from '../../ops/keyserver-store-ops.js'; +import { messageStoreOpsHandlers } from '../../ops/message-store-ops.js'; +import { reportStoreOpsHandlers } from '../../ops/report-store-ops.js'; +import { syncedMetadataStoreOpsHandlers } from '../../ops/synced-metadata-store-ops.js'; +import { threadActivityStoreOpsHandlers } from '../../ops/thread-activity-store-ops.js'; import type { ClientDBThreadStoreOperation } from '../../ops/thread-store-ops.js'; +import { threadStoreOpsHandlers } from '../../ops/thread-store-ops.js'; +import { userStoreOpsHandlers } from '../../ops/user-store-ops.js'; +import type { + ClientDBStoreOperations, + StoreOperations, +} from '../../types/store-ops-types.js'; import type { RawThreadInfos, ClientDBThreadInfo, } from '../../types/thread-types.js'; import { values } from '../../utils/objects.js'; import { convertClientDBThreadInfoToRawThreadInfo, convertRawThreadInfoToClientDBThreadInfo, } from '../../utils/thread-ops-utils.js'; function createUpdateDBOpsForThreadStoreThreadInfos( clientDBThreadInfos: $ReadOnlyArray, migrationFunc: RawThreadInfos => RawThreadInfos, ): $ReadOnlyArray { // 1. Translate `ClientDBThreadInfo`s to `RawThreadInfo`s. const rawThreadInfos = clientDBThreadInfos.map( convertClientDBThreadInfoToRawThreadInfo, ); // 2. Convert `RawThreadInfo`s to a map of `threadID` => `threadInfo`. const keyedRawThreadInfos = _keyBy('id')(rawThreadInfos); // 3. Apply `migrationFunc` to `ThreadInfo`s. const updatedKeyedRawThreadInfos = migrationFunc(keyedRawThreadInfos); // 4. Convert the updated `RawThreadInfos` back into an array. const updatedKeyedRawThreadInfosArray = values(updatedKeyedRawThreadInfos); // 5. Translate `RawThreadInfo`s back to `ClientDBThreadInfo`s. const updatedClientDBThreadInfos = updatedKeyedRawThreadInfosArray.map( convertRawThreadInfoToClientDBThreadInfo, ); // 6. Construct `replace` `ClientDBThreadStoreOperation`s. const replaceThreadOperations = updatedClientDBThreadInfos.map(thread => ({ type: 'replace', payload: thread, })); // 7. Prepend `replaceThreadOperations` with `remove_all` op and return. return [{ type: 'remove_all' }, ...replaceThreadOperations]; } -export { createUpdateDBOpsForThreadStoreThreadInfos }; +function convertStoreOperationsToClientDBStoreOperations( + storeOperations: StoreOperations, +): ClientDBStoreOperations { + const { + draftStoreOperations, + threadStoreOperations, + messageStoreOperations, + reportStoreOperations, + keyserverStoreOperations, + userStoreOperations, + integrityStoreOperations, + communityStoreOperations, + syncedMetadataStoreOperations, + auxUserStoreOperations, + threadActivityStoreOperations, + outboundP2PMessages, + entryStoreOperations, + messageSearchStoreOperations, + } = storeOperations; + + const convertedThreadStoreOperations = + threadStoreOpsHandlers.convertOpsToClientDBOps(threadStoreOperations); + const convertedMessageStoreOperations = + messageStoreOpsHandlers.convertOpsToClientDBOps(messageStoreOperations); + const convertedReportStoreOperations = + reportStoreOpsHandlers.convertOpsToClientDBOps(reportStoreOperations); + const convertedUserStoreOperations = + userStoreOpsHandlers.convertOpsToClientDBOps(userStoreOperations); + const convertedKeyserverStoreOperations = + keyserverStoreOpsHandlers.convertOpsToClientDBOps(keyserverStoreOperations); + const convertedCommunityStoreOperations = + communityStoreOpsHandlers.convertOpsToClientDBOps(communityStoreOperations); + const convertedSyncedMetadataStoreOperations = + syncedMetadataStoreOpsHandlers.convertOpsToClientDBOps( + syncedMetadataStoreOperations, + ); + const convertedIntegrityStoreOperations = + integrityStoreOpsHandlers.convertOpsToClientDBOps(integrityStoreOperations); + const convertedAuxUserStoreOperations = + auxUserStoreOpsHandlers.convertOpsToClientDBOps(auxUserStoreOperations); + const convertedThreadActivityStoreOperations = + threadActivityStoreOpsHandlers.convertOpsToClientDBOps( + threadActivityStoreOperations, + ); + const convertedEntryStoreOperations = + entryStoreOpsHandlers.convertOpsToClientDBOps(entryStoreOperations); + + return { + draftStoreOperations: draftStoreOperations, + threadStoreOperations: convertedThreadStoreOperations, + messageStoreOperations: convertedMessageStoreOperations, + reportStoreOperations: convertedReportStoreOperations, + userStoreOperations: convertedUserStoreOperations, + keyserverStoreOperations: convertedKeyserverStoreOperations, + communityStoreOperations: convertedCommunityStoreOperations, + integrityStoreOperations: convertedIntegrityStoreOperations, + syncedMetadataStoreOperations: convertedSyncedMetadataStoreOperations, + auxUserStoreOperations: convertedAuxUserStoreOperations, + threadActivityStoreOperations: convertedThreadActivityStoreOperations, + outboundP2PMessages, + entryStoreOperations: convertedEntryStoreOperations, + messageSearchStoreOperations, + }; +} + +export { + createUpdateDBOpsForThreadStoreThreadInfos, + convertStoreOperationsToClientDBStoreOperations, +}; diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js index 5e8622cc9..d3e4b4f1c 100644 --- a/native/database/sqlite-api.js +++ b/native/database/sqlite-api.js @@ -1,137 +1,64 @@ // @flow -import { auxUserStoreOpsHandlers } from 'lib/ops/aux-user-store-ops.js'; -import { communityStoreOpsHandlers } from 'lib/ops/community-store-ops.js'; -import { entryStoreOpsHandlers } from 'lib/ops/entries-store-ops.js'; -import { integrityStoreOpsHandlers } from 'lib/ops/integrity-store-ops.js'; -import { - getKeyserversToRemoveFromNotifsStore, - keyserverStoreOpsHandlers, -} from 'lib/ops/keyserver-store-ops.js'; -import { messageStoreOpsHandlers } from 'lib/ops/message-store-ops.js'; -import { reportStoreOpsHandlers } from 'lib/ops/report-store-ops.js'; -import { syncedMetadataStoreOpsHandlers } from 'lib/ops/synced-metadata-store-ops.js'; -import { threadActivityStoreOpsHandlers } from 'lib/ops/thread-activity-store-ops.js'; -import { threadStoreOpsHandlers } from 'lib/ops/thread-store-ops.js'; -import { userStoreOpsHandlers } from 'lib/ops/user-store-ops.js'; +import { getKeyserversToRemoveFromNotifsStore } from 'lib/ops/keyserver-store-ops.js'; +import { convertStoreOperationsToClientDBStoreOperations } from 'lib/shared/redux/client-db-utils.js'; import type { SQLiteAPI } from 'lib/types/sqlite-types.js'; import type { StoreOperations } from 'lib/types/store-ops-types'; import { values } from 'lib/utils/objects.js'; import { commCoreModule } from '../native-modules.js'; import { isTaskCancelledError } from '../utils/error-handling.js'; const sqliteAPI: SQLiteAPI = { // read operations getAllInboundP2PMessages: commCoreModule.getAllInboundP2PMessages, getAllOutboundP2PMessages: commCoreModule.getAllOutboundP2PMessages, getRelatedMessages: commCoreModule.getRelatedMessages, getOutboundP2PMessagesByID: commCoreModule.getOutboundP2PMessagesByID, searchMessages: commCoreModule.searchMessages, // write operations removeInboundP2PMessages: commCoreModule.removeInboundP2PMessages, markOutboundP2PMessageAsSent: commCoreModule.markOutboundP2PMessageAsSent, resetOutboundP2PMessagesForDevice: commCoreModule.resetOutboundP2PMessagesForDevice, removeOutboundP2PMessagesOlderThan: commCoreModule.removeOutboundP2PMessagesOlderThan, async processDBStoreOperations( storeOperations: StoreOperations, ): Promise { - const { - draftStoreOperations, - threadStoreOperations, - messageStoreOperations, - reportStoreOperations, - keyserverStoreOperations, - userStoreOperations, - integrityStoreOperations, - communityStoreOperations, - syncedMetadataStoreOperations, - auxUserStoreOperations, - threadActivityStoreOperations, - outboundP2PMessages, - entryStoreOperations, - messageSearchStoreOperations, - } = storeOperations; - - const convertedThreadStoreOperations = - threadStoreOpsHandlers.convertOpsToClientDBOps(threadStoreOperations); - const convertedMessageStoreOperations = - messageStoreOpsHandlers.convertOpsToClientDBOps(messageStoreOperations); - const convertedReportStoreOperations = - reportStoreOpsHandlers.convertOpsToClientDBOps(reportStoreOperations); - const convertedUserStoreOperations = - userStoreOpsHandlers.convertOpsToClientDBOps(userStoreOperations); - const convertedKeyserverStoreOperations = - keyserverStoreOpsHandlers.convertOpsToClientDBOps( - keyserverStoreOperations, - ); - const convertedCommunityStoreOperations = - communityStoreOpsHandlers.convertOpsToClientDBOps( - communityStoreOperations, - ); - const convertedSyncedMetadataStoreOperations = - syncedMetadataStoreOpsHandlers.convertOpsToClientDBOps( - syncedMetadataStoreOperations, - ); const keyserversToRemoveFromNotifsStore = - getKeyserversToRemoveFromNotifsStore(keyserverStoreOperations ?? []); - const convertedIntegrityStoreOperations = - integrityStoreOpsHandlers.convertOpsToClientDBOps( - integrityStoreOperations, - ); - const convertedAuxUserStoreOperations = - auxUserStoreOpsHandlers.convertOpsToClientDBOps(auxUserStoreOperations); - const convertedThreadActivityStoreOperations = - threadActivityStoreOpsHandlers.convertOpsToClientDBOps( - threadActivityStoreOperations, + getKeyserversToRemoveFromNotifsStore( + storeOperations.keyserverStoreOperations ?? [], ); - const convertedEntryStoreOperations = - entryStoreOpsHandlers.convertOpsToClientDBOps(entryStoreOperations); try { const promises = []; if (keyserversToRemoveFromNotifsStore.length > 0) { promises.push( commCoreModule.removeKeyserverDataFromNotifStorage( keyserversToRemoveFromNotifsStore, ), ); } - const dbOps = { - draftStoreOperations, - threadStoreOperations: convertedThreadStoreOperations, - messageStoreOperations: convertedMessageStoreOperations, - reportStoreOperations: convertedReportStoreOperations, - userStoreOperations: convertedUserStoreOperations, - keyserverStoreOperations: convertedKeyserverStoreOperations, - communityStoreOperations: convertedCommunityStoreOperations, - integrityStoreOperations: convertedIntegrityStoreOperations, - syncedMetadataStoreOperations: convertedSyncedMetadataStoreOperations, - auxUserStoreOperations: convertedAuxUserStoreOperations, - threadActivityStoreOperations: convertedThreadActivityStoreOperations, - outboundP2PMessages, - entryStoreOperations: convertedEntryStoreOperations, - messageSearchStoreOperations, - }; + const dbOps = + convertStoreOperationsToClientDBStoreOperations(storeOperations); if (values(dbOps).some(ops => ops && ops.length > 0)) { promises.push(commCoreModule.processDBStoreOperations(dbOps)); } await Promise.all(promises); } catch (e) { if (isTaskCancelledError(e)) { return; } // this code will make an entry in SecureStore and cause re-creating // database when user will open app again commCoreModule.reportDBOperationsFailure(); commCoreModule.terminate(); } }, }; export { sqliteAPI }; diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js index 03f89d2c7..bd87bd1e8 100644 --- a/web/database/sqlite-api.js +++ b/web/database/sqlite-api.js @@ -1,256 +1,170 @@ // @flow -import { auxUserStoreOpsHandlers } from 'lib/ops/aux-user-store-ops.js'; -import { communityStoreOpsHandlers } from 'lib/ops/community-store-ops.js'; -import { entryStoreOpsHandlers } from 'lib/ops/entries-store-ops.js'; -import { integrityStoreOpsHandlers } from 'lib/ops/integrity-store-ops.js'; -import { keyserverStoreOpsHandlers } from 'lib/ops/keyserver-store-ops.js'; -import { messageStoreOpsHandlers } from 'lib/ops/message-store-ops.js'; -import { reportStoreOpsHandlers } from 'lib/ops/report-store-ops.js'; -import { syncedMetadataStoreOpsHandlers } from 'lib/ops/synced-metadata-store-ops.js'; -import { threadActivityStoreOpsHandlers } from 'lib/ops/thread-activity-store-ops.js'; -import { threadStoreOpsHandlers } from 'lib/ops/thread-store-ops.js'; -import { userStoreOpsHandlers } from 'lib/ops/user-store-ops.js'; +import { convertStoreOperationsToClientDBStoreOperations } from 'lib/shared/redux/client-db-utils.js'; import type { ClientDBMessageInfo } from 'lib/types/message-types.js'; import type { SQLiteAPI, InboundP2PMessage, OutboundP2PMessage, } from 'lib/types/sqlite-types.js'; import type { StoreOperations } from 'lib/types/store-ops-types.js'; -import { entries } from 'lib/utils/objects.js'; +import { entries, values } from 'lib/utils/objects.js'; import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js'; import { workerRequestMessageTypes } from '../types/worker-types.js'; const sqliteAPI: SQLiteAPI = { // read operations async getAllInboundP2PMessages(): Promise { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES, }); const messages: ?$ReadOnlyArray = data?.inboundP2PMessages; return messages ? [...messages] : []; }, async getAllOutboundP2PMessages(): Promise { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES, }); const messages: ?$ReadOnlyArray = data?.outboundP2PMessages; return messages ? [...messages] : []; }, async getRelatedMessages(messageID: string): Promise { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.GET_RELATED_MESSAGES, messageID, }); const messages: ?$ReadOnlyArray = data?.messages; return messages ? [...messages] : []; }, async getOutboundP2PMessagesByID( ids: $ReadOnlyArray, ): Promise> { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES_BY_ID, messageIDs: ids, }); const messages: ?$ReadOnlyArray = data?.outboundP2PMessages; return messages ? [...messages] : []; }, async searchMessages( query: string, threadID: string, timestampCursor: ?string, messageIDCursor: ?string, ): Promise { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.SEARCH_MESSAGES, query, threadID, timestampCursor, messageIDCursor, }); const messages: ?$ReadOnlyArray = data?.messages; return messages ? [...messages] : []; }, // write operations async removeInboundP2PMessages(ids: $ReadOnlyArray): Promise { const sharedWorker = await getCommSharedWorker(); await sharedWorker.schedule({ type: workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES, ids, }); }, async markOutboundP2PMessageAsSent( messageID: string, deviceID: string, ): Promise { const sharedWorker = await getCommSharedWorker(); await sharedWorker.schedule({ type: workerRequestMessageTypes.MARK_OUTBOUND_P2P_MESSAGE_AS_SENT, messageID, deviceID, }); }, async resetOutboundP2PMessagesForDevice( deviceID: string, ): Promise> { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.RESET_OUTBOUND_P2P_MESSAGES, deviceID, }); const messageIDs: ?$ReadOnlyArray = data?.messageIDs; return messageIDs ? [...messageIDs] : []; }, async removeOutboundP2PMessagesOlderThan( messageID: string, deviceID: string, ): Promise { const sharedWorker = await getCommSharedWorker(); await sharedWorker.schedule({ type: workerRequestMessageTypes.REMOVE_OUTBOUND_P2P_MESSAGES, messageID, deviceID, }); }, async processDBStoreOperations( storeOperations: StoreOperations, ): Promise { - const { - draftStoreOperations, - threadStoreOperations, - reportStoreOperations, - keyserverStoreOperations, - communityStoreOperations, - integrityStoreOperations, - syncedMetadataStoreOperations, - auxUserStoreOperations, - userStoreOperations, - messageStoreOperations, - threadActivityStoreOperations, - outboundP2PMessages, - entryStoreOperations, - messageSearchStoreOperations, - } = storeOperations; + const dbOps = + convertStoreOperationsToClientDBStoreOperations(storeOperations); - const convertedThreadStoreOperations = - threadStoreOpsHandlers.convertOpsToClientDBOps(threadStoreOperations); - const convertedReportStoreOperations = - reportStoreOpsHandlers.convertOpsToClientDBOps(reportStoreOperations); - const convertedKeyserverStoreOperations = - keyserverStoreOpsHandlers.convertOpsToClientDBOps( - keyserverStoreOperations, - ); - const convertedCommunityStoreOperations = - communityStoreOpsHandlers.convertOpsToClientDBOps( - communityStoreOperations, - ); - const convertedIntegrityStoreOperations = - integrityStoreOpsHandlers.convertOpsToClientDBOps( - integrityStoreOperations, - ); - const convertedSyncedMetadataStoreOperations = - syncedMetadataStoreOpsHandlers.convertOpsToClientDBOps( - syncedMetadataStoreOperations, - ); - const convertedAuxUserStoreOperations = - auxUserStoreOpsHandlers.convertOpsToClientDBOps(auxUserStoreOperations); - const convertedUserStoreOperations = - userStoreOpsHandlers.convertOpsToClientDBOps(userStoreOperations); - const convertedMessageStoreOperations = - messageStoreOpsHandlers.convertOpsToClientDBOps(messageStoreOperations); - const convertedThreadActivityStoreOperations = - threadActivityStoreOpsHandlers.convertOpsToClientDBOps( - threadActivityStoreOperations, - ); - const convertedEntryStoreOperations = - entryStoreOpsHandlers.convertOpsToClientDBOps(entryStoreOperations); - - if ( - convertedThreadStoreOperations.length === 0 && - convertedReportStoreOperations.length === 0 && - (!draftStoreOperations || draftStoreOperations.length === 0) && - convertedKeyserverStoreOperations.length === 0 && - convertedCommunityStoreOperations.length === 0 && - convertedIntegrityStoreOperations.length === 0 && - convertedSyncedMetadataStoreOperations.length === 0 && - convertedAuxUserStoreOperations.length === 0 && - convertedUserStoreOperations.length === 0 && - convertedMessageStoreOperations.length === 0 && - convertedThreadActivityStoreOperations.length === 0 && - convertedEntryStoreOperations.length === 0 && - outboundP2PMessages?.length === 0 - ) { + if (!values(dbOps).some(ops => ops && ops.length > 0)) { return; } const sharedWorker = await getCommSharedWorker(); const isSupported = await sharedWorker.isSupported(); if (!isSupported) { return; } try { await sharedWorker.schedule({ type: workerRequestMessageTypes.PROCESS_STORE_OPERATIONS, - storeOperations: { - draftStoreOperations, - reportStoreOperations: convertedReportStoreOperations, - threadStoreOperations: convertedThreadStoreOperations, - keyserverStoreOperations: convertedKeyserverStoreOperations, - communityStoreOperations: convertedCommunityStoreOperations, - integrityStoreOperations: convertedIntegrityStoreOperations, - syncedMetadataStoreOperations: convertedSyncedMetadataStoreOperations, - auxUserStoreOperations: convertedAuxUserStoreOperations, - userStoreOperations: convertedUserStoreOperations, - messageStoreOperations: convertedMessageStoreOperations, - threadActivityStoreOperations: convertedThreadActivityStoreOperations, - outboundP2PMessages, - entryStoreOperations: convertedEntryStoreOperations, - messageSearchStoreOperations, - }, + storeOperations: dbOps, }); } catch (e) { console.log(e); if ( entries(storeOperations).some( ([key, ops]) => key !== 'draftStoreOperations' && key !== 'reportStoreOperations' && ops.length > 0, ) ) { await sharedWorker.init({ clearDatabase: true, markAsCorrupted: true }); location.reload(); } } }, }; export { sqliteAPI };