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 @@ -74,6 +74,7 @@ dbID: DatabaseIdentifier, currentUserID: ?string, ) => Promise, + +getDatabaseVersion: (dbID: DatabaseIdentifier) => Promise, // write operations +removeInboundP2PMessages: (ids: $ReadOnlyArray) => Promise, diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js --- a/lib/utils/__mocks__/config.js +++ b/lib/utils/__mocks__/config.js @@ -51,6 +51,7 @@ getClientDBStore: jest.fn(), copyContentFromBackupDatabase: jest.fn(), migrateBackupSchema: jest.fn(), + getDatabaseVersion: jest.fn(), }, encryptedNotifUtilsAPI: { generateAESKey: jest.fn(), diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h @@ -255,6 +255,8 @@ jsi::String deviceID) override; virtual jsi::Value getSyncedDatabaseVersion(jsi::Runtime &rt) override; + virtual jsi::Value + getDatabaseVersion(jsi::Runtime &rt, jsi::String dbID) override; virtual jsi::Value markPrekeysAsPublished(jsi::Runtime &rt) override; virtual jsi::Value getRelatedMessages(jsi::Runtime &rt, jsi::String messageID) override; diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -3052,6 +3052,34 @@ }); } +jsi::Value +CommCoreModule::getDatabaseVersion(jsi::Runtime &rt, jsi::String dbID) { + DatabaseIdentifier identifier = stringToDatabaseIdentifier(dbID.utf8(rt)); + return createPromiseAsJSIValue( + rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { + taskType job = [=, &innerRt]() { + std::string error; + int version = 0; + try { + version = DatabaseManager::getQueryExecutor(identifier) + .getDatabaseVersion(); + } catch (std::system_error &e) { + error = e.what(); + } + + this->jsInvoker_->invokeAsync([&innerRt, error, promise, version]() { + if (error.size()) { + promise->reject(error); + return; + } + promise->resolve(jsi::Value(version)); + }); + }; + GlobalDBSingleton::instance.scheduleOrRunCancellable( + job, promise, this->jsInvoker_); + }); +} + jsi::Value CommCoreModule::markPrekeysAsPublished(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp --- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp @@ -211,6 +211,9 @@ static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSyncedDatabaseVersion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getSyncedDatabaseVersion(rt); } +static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDatabaseVersion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->getDatabaseVersion(rt, args[0].asString(rt)); +} static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_markPrekeysAsPublished(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->markPrekeysAsPublished(rt); } @@ -302,6 +305,7 @@ methodMap_["removeOutboundP2PMessage"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeOutboundP2PMessage}; methodMap_["resetOutboundP2PMessagesForDevice"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_resetOutboundP2PMessagesForDevice}; methodMap_["getSyncedDatabaseVersion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSyncedDatabaseVersion}; + methodMap_["getDatabaseVersion"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDatabaseVersion}; methodMap_["markPrekeysAsPublished"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_markPrekeysAsPublished}; methodMap_["getRelatedMessages"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getRelatedMessages}; methodMap_["searchMessages"] = MethodMetadata {4, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_searchMessages}; diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h --- a/native/cpp/CommonCpp/_generated/commJSI.h +++ b/native/cpp/CommonCpp/_generated/commJSI.h @@ -84,6 +84,7 @@ virtual jsi::Value removeOutboundP2PMessage(jsi::Runtime &rt, jsi::String messageID, jsi::String deviceID) = 0; virtual jsi::Value resetOutboundP2PMessagesForDevice(jsi::Runtime &rt, jsi::String deviceID) = 0; virtual jsi::Value getSyncedDatabaseVersion(jsi::Runtime &rt) = 0; + virtual jsi::Value getDatabaseVersion(jsi::Runtime &rt, jsi::String dbID) = 0; virtual jsi::Value markPrekeysAsPublished(jsi::Runtime &rt) = 0; virtual jsi::Value getRelatedMessages(jsi::Runtime &rt, jsi::String messageID) = 0; virtual jsi::Value searchMessages(jsi::Runtime &rt, jsi::String query, jsi::String threadID, std::optional timestampCursor, std::optional messageIDCursor) = 0; @@ -625,6 +626,14 @@ return bridging::callFromJs( rt, &T::getSyncedDatabaseVersion, jsInvoker_, instance_); } + jsi::Value getDatabaseVersion(jsi::Runtime &rt, jsi::String dbID) override { + static_assert( + bridging::getParameterCount(&T::getDatabaseVersion) == 2, + "Expected getDatabaseVersion(...) to have 2 parameters"); + + return bridging::callFromJs( + rt, &T::getDatabaseVersion, jsInvoker_, instance_, std::move(dbID)); + } jsi::Value markPrekeysAsPublished(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::markPrekeysAsPublished) == 1, diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js --- a/native/database/sqlite-api.js +++ b/native/database/sqlite-api.js @@ -24,6 +24,7 @@ fetchMessages: commCoreModule.fetchMessages, fetchDMOperationsByType: commCoreModule.getDMOperationsByType, getClientDBStore, + getDatabaseVersion: commCoreModule.getDatabaseVersion, // write operations removeInboundP2PMessages: commCoreModule.removeInboundP2PMessages, diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js --- a/native/schema/CommCoreModuleSchema.js +++ b/native/schema/CommCoreModuleSchema.js @@ -200,6 +200,8 @@ deviceID: string, ) => Promise>; +getSyncedDatabaseVersion: () => Promise; + // This type should be DatabaseIdentifier + +getDatabaseVersion: (dbID: string) => Promise; +markPrekeysAsPublished: () => Promise; +getRelatedMessages: ( messageID: string, diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js --- a/web/database/sqlite-api.js +++ b/web/database/sqlite-api.js @@ -133,6 +133,15 @@ getClientDBStore, + async getDatabaseVersion(dbID: DatabaseIdentifier): Promise { + const sharedWorker = await getCommSharedWorker(); + const data = await sharedWorker.schedule({ + type: workerRequestMessageTypes.GET_DATABASE_VERSION, + dbID, + }); + return data?.databaseVersion ?? 0; + }, + // write operations async removeInboundP2PMessages(ids: $ReadOnlyArray): Promise { const sharedWorker = await getCommSharedWorker(); diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js --- a/web/shared-worker/worker/shared-worker.js +++ b/web/shared-worker/worker/shared-worker.js @@ -359,6 +359,26 @@ type: workerResponseMessageTypes.DM_OPERATIONS, operations, }; + } else if (message.type === workerRequestMessageTypes.GET_DATABASE_VERSION) { + if (message.dbID && message.dbID === databaseIdentifier.RESTORED) { + const backupQueryExecutor = getSQLiteQueryExecutor( + databaseIdentifier.RESTORED, + ); + if (!backupQueryExecutor) { + throw new Error( + `Backup not initialized, unable to process request type: ${message.type}`, + ); + } + return { + type: workerResponseMessageTypes.GET_DATABASE_VERSION, + databaseVersion: backupQueryExecutor.getDatabaseVersion(), + }; + } + + return { + type: workerResponseMessageTypes.GET_DATABASE_VERSION, + databaseVersion: sqliteQueryExecutor.getDatabaseVersion(), + }; } // write operations diff --git a/web/types/worker-types.js b/web/types/worker-types.js --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -55,6 +55,7 @@ GET_DM_OPERATIONS_BY_TYPE: 27, MIGRATE_BACKUP_SCHEMA: 28, COPY_CONTENT_FROM_BACKUP_DB: 29, + GET_DATABASE_VERSION: 30, }); export const workerWriteRequests: $ReadOnlyArray = [ @@ -253,6 +254,11 @@ +type: 29, }; +export type GetDatabaseVersionRequestMessage = { + +type: 30, + +dbID: DatabaseIdentifier, +}; + export type WorkerRequestMessage = | PingWorkerRequestMessage | InitWorkerRequestMessage @@ -283,7 +289,8 @@ | GetInboundP2PMessagesByIDRequestMessage | GetDMOperationsByTypeRequestMessage | MigrateBackupSchemaRequestMessage - | CopyContentFromBackupDatabaseRequestMessage; + | CopyContentFromBackupDatabaseRequestMessage + | GetDatabaseVersionRequestMessage; export type WorkerRequestProxyMessage = { +id: number, @@ -303,6 +310,7 @@ GET_MESSAGES: 8, RESET_OUTBOUND_P2P_MESSAGES: 9, DM_OPERATIONS: 10, + GET_DATABASE_VERSION: 11, }); export type PongWorkerResponseMessage = { @@ -360,6 +368,11 @@ +operations: $ReadOnlyArray, }; +export type GetDatabaseVersionResponseMessage = { + +type: 11, + +databaseVersion: number, +}; + export type WorkerResponseMessage = | PongWorkerResponseMessage | ClientStoreResponseMessage @@ -371,7 +384,8 @@ | GetOutboundP2PMessagesResponseMessage | GetMessagesResponse | ResetOutboundP2PMessagesResponseMessage - | DMOperationsResponseMessage; + | DMOperationsResponseMessage + | GetDatabaseVersionResponseMessage; export type WorkerResponseProxyMessage = { +id?: number,