diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js index bb9fb6adb..5e8622cc9 100644 --- a/native/database/sqlite-api.js +++ b/native/database/sqlite-api.js @@ -1,27 +1,137 @@ // @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 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 { processDBStoreOperations } from '../redux/redux-utils.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, - processDBStoreOperations, + 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, + ); + 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, + }; + 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/native/redux/redux-utils.js b/native/redux/redux-utils.js index b94644c13..1f38d7be5 100644 --- a/native/redux/redux-utils.js +++ b/native/redux/redux-utils.js @@ -1,123 +1,14 @@ // @flow import { useSelector as reactReduxUseSelector } from 'react-redux'; -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, - getKeyserversToRemoveFromNotifsStore, -} 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 type { StoreOperations } from 'lib/types/store-ops-types.js'; -import { values } from 'lib/utils/objects.js'; - import type { AppState } from './state-types.js'; -import { commCoreModule } from '../native-modules.js'; -import { isTaskCancelledError } from '../utils/error-handling.js'; function useSelector( selector: (state: AppState) => SS, equalityFn?: (a: SS, b: SS) => boolean, ): SS { return reactReduxUseSelector(selector, equalityFn); } -async function processDBStoreOperations( - storeOperations: StoreOperations, -): Promise { - const { - draftStoreOperations, - threadStoreOperations, - messageStoreOperations, - reportStoreOperations, - userStoreOperations, - keyserverStoreOperations, - 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, - ); - 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, - }; - 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 { useSelector, processDBStoreOperations }; +export { useSelector }; diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js index c4db88188..03f89d2c7 100644 --- a/web/database/sqlite-api.js +++ b/web/database/sqlite-api.js @@ -1,134 +1,256 @@ // @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 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 { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js'; -import { processDBStoreOperations } from '../shared-worker/utils/store.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, }); }, - processDBStoreOperations, + async processDBStoreOperations( + storeOperations: StoreOperations, + ): Promise { + const { + draftStoreOperations, + threadStoreOperations, + reportStoreOperations, + keyserverStoreOperations, + communityStoreOperations, + integrityStoreOperations, + syncedMetadataStoreOperations, + auxUserStoreOperations, + userStoreOperations, + messageStoreOperations, + threadActivityStoreOperations, + outboundP2PMessages, + entryStoreOperations, + messageSearchStoreOperations, + } = 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 + ) { + 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, + }, + }); + } 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 }; diff --git a/web/redux/initial-state-gate.js b/web/redux/initial-state-gate.js index 20866f8ef..1c0a2e959 100644 --- a/web/redux/initial-state-gate.js +++ b/web/redux/initial-state-gate.js @@ -1,214 +1,213 @@ // @flow import * as React from 'react'; import { PersistGate } from 'redux-persist/es/integration/react.js'; import type { Persistor } from 'redux-persist/es/types'; import { setClientDBStoreActionType } from 'lib/actions/client-db-store-actions.js'; import type { EntryStoreOperation } from 'lib/ops/entries-store-ops.js'; import type { MessageStoreOperation } from 'lib/ops/message-store-ops.js'; import type { ThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; import type { UserStoreOperation } from 'lib/ops/user-store-ops.js'; import { getMessageSearchStoreOps } from 'lib/reducers/db-ops-reducer.js'; import { allUpdatesCurrentAsOfSelector } from 'lib/selectors/keyserver-selectors.js'; import type { RawThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; +import { getConfig } from 'lib/utils/config.js'; import { convertIDToNewSchema } from 'lib/utils/migration-utils.js'; import { entries, values } from 'lib/utils/objects.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import { infoFromURL } from 'lib/utils/url-utils.js'; import { setInitialReduxState, useGetInitialReduxState, } from './action-types.js'; import { useSelector } from './redux-utils.js'; import { authoritativeKeyserverID } from '../authoritative-keyserver.js'; import Loading from '../loading.react.js'; -import { - getClientDBStore, - processDBStoreOperations, -} from '../shared-worker/utils/store.js'; +import { getClientDBStore } from '../shared-worker/utils/store.js'; import type { InitialReduxStateActionPayload } from '../types/redux-types.js'; type Props = { +persistor: Persistor, +children: React.Node, }; function InitialReduxStateGate(props: Props): React.Node { const { children, persistor } = props; const callGetInitialReduxState = useGetInitialReduxState(); const dispatch = useDispatch(); const [initError, setInitError] = React.useState(null); React.useEffect(() => { if (initError) { throw initError; } }, [initError]); const isRehydrated = useSelector(state => !!state._persist?.rehydrated); const allUpdatesCurrentAsOf = useSelector(allUpdatesCurrentAsOfSelector); const prevIsRehydrated = React.useRef(false); React.useEffect(() => { if (prevIsRehydrated.current || !isRehydrated) { return; } prevIsRehydrated.current = isRehydrated; void (async () => { try { let urlInfo = infoFromURL(decodeURI(window.location.href)); // Handle older links if (urlInfo.thread) { urlInfo = { ...urlInfo, thread: convertIDToNewSchema( urlInfo.thread, authoritativeKeyserverID, ), }; } const clientDBStore = await getClientDBStore(); dispatch({ type: setClientDBStoreActionType, payload: clientDBStore, }); const payload = await callGetInitialReduxState({ urlInfo, excludedData: { threadStore: !!clientDBStore.threadStore, messageStore: !!clientDBStore.messages, userStore: !!clientDBStore.users, entryStore: !!clientDBStore.entries, }, allUpdatesCurrentAsOf, }); const currentLoggedInUserID = payload.currentUserInfo?.anonymous ? null : payload.currentUserInfo?.id; if (!currentLoggedInUserID) { dispatch({ type: setInitialReduxState, payload }); return; } let initialReduxState: InitialReduxStateActionPayload = payload; let threadStoreOperations: ThreadStoreOperation[] = []; if (clientDBStore.threadStore) { const { threadStore, ...rest } = initialReduxState; initialReduxState = rest; } else { // When there is no data in the DB, it's necessary to migrate data // from the keyserver payload to the DB threadStoreOperations = entries(payload.threadStore.threadInfos).map( ([id, threadInfo]: [string, RawThreadInfo]) => ({ type: 'replace', payload: { id, threadInfo, }, }), ); } let userStoreOperations: UserStoreOperation[] = []; if (clientDBStore.users) { const { userInfos, ...rest } = initialReduxState; initialReduxState = rest; } else { userStoreOperations = values(payload.userInfos).map(userInfo => ({ type: 'replace_user', payload: userInfo, })); } let messageStoreOperations: MessageStoreOperation[] = []; if (clientDBStore.messages) { const { messageStore, ...rest } = initialReduxState; initialReduxState = rest; } else { const { messages, threads } = payload.messageStore; messageStoreOperations = [ ...entries(messages).map(([id, messageInfo]) => ({ type: 'replace', payload: { id, messageInfo }, })), { type: 'replace_threads', payload: { threads }, }, ]; } let entryStoreOperations: Array = []; if (clientDBStore.entries) { const { entryStore, ...rest } = initialReduxState; initialReduxState = rest; } else { entryStoreOperations = entries(payload.entryStore.entryInfos).map( ([id, entry]) => ({ type: 'replace_entry', payload: { id, entry }, }), ); } if ( threadStoreOperations.length > 0 || userStoreOperations.length > 0 || messageStoreOperations.length > 0 || entryStoreOperations.length > 0 ) { const messageSearchStoreOperations = getMessageSearchStoreOps( messageStoreOperations, ); - await processDBStoreOperations({ + const { sqliteAPI } = getConfig(); + await sqliteAPI.processDBStoreOperations({ threadStoreOperations, draftStoreOperations: [], messageStoreOperations, reportStoreOperations: [], userStoreOperations, keyserverStoreOperations: [], communityStoreOperations: [], integrityStoreOperations: [], syncedMetadataStoreOperations: [], auxUserStoreOperations: [], threadActivityStoreOperations: [], entryStoreOperations, messageSearchStoreOperations, }); } dispatch({ type: setInitialReduxState, payload: initialReduxState, }); } catch (err) { setInitError(err); } })(); }, [callGetInitialReduxState, dispatch, isRehydrated, allUpdatesCurrentAsOf]); const initialStateLoaded = useSelector(state => state.initialStateLoaded); const childFunction = React.useCallback( // This argument is passed from `PersistGate`. It means that the state is // rehydrated and we can start fetching the initial info. (bootstrapped: boolean) => { if (bootstrapped && initialStateLoaded) { return children; } else { return ; } }, [children, initialStateLoaded], ); return {childFunction}; } export default InitialReduxStateGate; diff --git a/web/shared-worker/utils/store.js b/web/shared-worker/utils/store.js index c53fa2454..8d1ef959f 100644 --- a/web/shared-worker/utils/store.js +++ b/web/shared-worker/utils/store.js @@ -1,269 +1,158 @@ // @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 type { - ClientStore, - StoreOperations, -} from 'lib/types/store-ops-types.js'; +import type { ClientStore } from 'lib/types/store-ops-types.js'; import { translateClientDBLocalMessageInfos } from 'lib/utils/message-ops-utils.js'; -import { entries } from 'lib/utils/objects.js'; import { defaultWebState } from '../../redux/default-state.js'; import { workerRequestMessageTypes } from '../../types/worker-types.js'; import { getCommSharedWorker } from '../shared-worker-provider.js'; async function getClientDBStore(): Promise { const sharedWorker = await getCommSharedWorker(); let result: ClientStore = { currentUserID: null, drafts: [], messages: null, threadStore: null, messageStoreThreads: null, reports: null, users: null, keyserverInfos: defaultWebState.keyserverStore.keyserverInfos, communityInfos: null, threadHashes: null, syncedMetadata: null, auxUserInfos: null, threadActivityStore: null, entries: null, messageStoreLocalMessageInfos: null, }; const data = await sharedWorker.schedule({ type: workerRequestMessageTypes.GET_CLIENT_STORE, }); if (data?.store?.drafts) { result = { ...result, drafts: data.store.drafts, }; } if (data?.store?.reports) { result = { ...result, reports: reportStoreOpsHandlers.translateClientDBData(data.store.reports), }; } if (data?.store?.threads && data.store.threads.length > 0) { result = { ...result, threadStore: { threadInfos: threadStoreOpsHandlers.translateClientDBData( data.store.threads, ), }, }; } if (data?.store?.keyservers?.length) { result = { ...result, keyserverInfos: keyserverStoreOpsHandlers.translateClientDBData( data.store.keyservers, ), }; } if (data?.store?.communities) { result = { ...result, communityInfos: communityStoreOpsHandlers.translateClientDBData( data.store.communities, ), }; } if (data?.store?.integrityThreadHashes) { result = { ...result, threadHashes: integrityStoreOpsHandlers.translateClientDBData( data.store.integrityThreadHashes, ), }; } if (data?.store?.syncedMetadata) { result = { ...result, syncedMetadata: syncedMetadataStoreOpsHandlers.translateClientDBData( data.store.syncedMetadata, ), }; } if (data?.store?.auxUserInfos) { result = { ...result, auxUserInfos: auxUserStoreOpsHandlers.translateClientDBData( data.store.auxUserInfos, ), }; } if (data?.store?.users && data.store.users.length > 0) { result = { ...result, users: userStoreOpsHandlers.translateClientDBData(data.store.users), }; } if (data?.store?.messages && data.store.messages.length > 0) { result = { ...result, messages: data.store.messages, }; } if ( data?.store?.messageStoreThreads && data.store.messageStoreThreads.length > 0 ) { result = { ...result, messageStoreThreads: data.store.messageStoreThreads, }; } if ( data?.store?.threadActivityEntries && data.store.threadActivityEntries.length > 0 ) { result = { ...result, threadActivityStore: threadActivityStoreOpsHandlers.translateClientDBData( data.store.threadActivityEntries, ), }; } if (data?.store?.entries && data.store.entries.length > 0) { result = { ...result, entries: entryStoreOpsHandlers.translateClientDBData(data.store.entries), }; } if ( data?.store?.messageStoreLocalMessageInfos && data.store.messageStoreLocalMessageInfos.length > 0 ) { result = { ...result, messageStoreLocalMessageInfos: translateClientDBLocalMessageInfos( data.store.messageStoreLocalMessageInfos, ), }; } return result; } -async function processDBStoreOperations( - storeOperations: StoreOperations, -): Promise { - const { - draftStoreOperations, - threadStoreOperations, - reportStoreOperations, - keyserverStoreOperations, - communityStoreOperations, - integrityStoreOperations, - syncedMetadataStoreOperations, - auxUserStoreOperations, - userStoreOperations, - messageStoreOperations, - threadActivityStoreOperations, - outboundP2PMessages, - entryStoreOperations, - messageSearchStoreOperations, - } = 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 - ) { - 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, - }, - }); - } 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 { getClientDBStore, processDBStoreOperations }; +export { getClientDBStore };