diff --git a/native/cpp/CommonCpp/NativeModules/CMakeLists.txt b/native/cpp/CommonCpp/NativeModules/CMakeLists.txt --- a/native/cpp/CommonCpp/NativeModules/CMakeLists.txt +++ b/native/cpp/CommonCpp/NativeModules/CMakeLists.txt @@ -73,10 +73,12 @@ ${_data_stores_path}/BaseDataStore.h ${_data_stores_path}/DraftStore.h ${_data_stores_path}/ThreadStore.h + ${_data_stores_path}/MessageStore.h ) set(DATA_STORES_SRCS ${_data_stores_path}/DraftStore.cpp ${_data_stores_path}/ThreadStore.cpp + ${_data_stores_path}/MessageStore.cpp ) add_library(comm-modules-persistentstorage 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 @@ -6,6 +6,7 @@ #include "../_generated/commJSI.h" #include "JSIRust.h" #include "PersistentStorageUtilities/DataStores/DraftStore.h" +#include "PersistentStorageUtilities/DataStores/MessageStore.h" #include "PersistentStorageUtilities/DataStores/ThreadStore.h" #include #include @@ -26,6 +27,7 @@ std::unique_ptr cryptoModule; DraftStore draftStore; ThreadStore threadStore; + MessageStore messageStore; virtual jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) override; virtual jsi::Value 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 @@ -107,79 +107,6 @@ }); } -jsi::Array parseDBMessages( - jsi::Runtime &rt, - std::shared_ptr>>> - messagesVectorPtr) { - size_t numMessages = messagesVectorPtr->size(); - jsi::Array jsiMessages = jsi::Array(rt, numMessages); - size_t writeIndex = 0; - for (const auto &[message, media] : *messagesVectorPtr) { - auto jsiMessage = jsi::Object(rt); - jsiMessage.setProperty(rt, "id", message.id); - - if (message.local_id) { - auto local_id = message.local_id.get(); - jsiMessage.setProperty(rt, "local_id", *local_id); - } - - jsiMessage.setProperty(rt, "thread", message.thread); - jsiMessage.setProperty(rt, "user", message.user); - jsiMessage.setProperty(rt, "type", std::to_string(message.type)); - - if (message.future_type) { - auto future_type = message.future_type.get(); - jsiMessage.setProperty(rt, "future_type", std::to_string(*future_type)); - } - - if (message.content) { - auto content = message.content.get(); - jsiMessage.setProperty(rt, "content", *content); - } - - jsiMessage.setProperty(rt, "time", std::to_string(message.time)); - - size_t media_idx = 0; - jsi::Array jsiMediaArray = jsi::Array(rt, media.size()); - for (const auto &media_info : media) { - auto jsiMedia = jsi::Object(rt); - jsiMedia.setProperty(rt, "id", media_info.id); - jsiMedia.setProperty(rt, "uri", media_info.uri); - jsiMedia.setProperty(rt, "type", media_info.type); - jsiMedia.setProperty(rt, "extras", media_info.extras); - - jsiMediaArray.setValueAtIndex(rt, media_idx++, jsiMedia); - } - - jsiMessage.setProperty(rt, "media_infos", jsiMediaArray); - - jsiMessages.setValueAtIndex(rt, writeIndex++, jsiMessage); - } - return jsiMessages; -} - -jsi::Array parseDBMessageStoreThreads( - jsi::Runtime &rt, - std::shared_ptr> threadsVectorPtr) { - size_t numThreads = threadsVectorPtr->size(); - jsi::Array jsiThreads = jsi::Array(rt, numThreads); - size_t writeIdx = 0; - - for (const MessageStoreThread &thread : *threadsVectorPtr) { - jsi::Object jsiThread = jsi::Object(rt); - jsiThread.setProperty(rt, "id", thread.id); - jsiThread.setProperty( - rt, "start_reached", std::to_string(thread.start_reached)); - jsiThread.setProperty( - rt, "last_navigated_to", std::to_string(thread.last_navigated_to)); - jsiThread.setProperty( - rt, "last_pruned", std::to_string(thread.last_pruned)); - - jsiThreads.setValueAtIndex(rt, writeIdx++, jsiThread); - } - return jsiThreads; -} - jsi::Array parseDBReportStore( jsi::Runtime &rt, std::shared_ptr> reportStoreVectorPtr) { @@ -239,7 +166,8 @@ error, promise, draftStore = this->draftStore, - threadStore = this->threadStore]() { + threadStore = this->threadStore, + messageStore = this->messageStore]() { if (error.size()) { promise->reject(error); return; @@ -247,11 +175,12 @@ jsi::Array jsiDrafts = draftStore.parseDBDataStore(innerRt, draftsVectorPtr); jsi::Array jsiMessages = - parseDBMessages(innerRt, messagesVectorPtr); + messageStore.parseDBDataStore(innerRt, messagesVectorPtr); jsi::Array jsiThreads = threadStore.parseDBDataStore(innerRt, threadsVectorPtr); - jsi::Array jsiMessageStoreThreads = parseDBMessageStoreThreads( - innerRt, messageStoreThreadsVectorPtr); + jsi::Array jsiMessageStoreThreads = + messageStore.parseDBMessageStoreThreads( + innerRt, messageStoreThreadsVectorPtr); jsi::Array jsiReportStore = parseDBReportStore(innerRt, reportStoreVectorPtr); @@ -302,7 +231,8 @@ auto messagesVectorPtr = std::make_shared>>>( std::move(messagesVector)); - jsi::Array jsiMessages = parseDBMessages(rt, messagesVectorPtr); + jsi::Array jsiMessages = + this->messageStore.parseDBDataStore(rt, messagesVectorPtr); return jsiMessages; } @@ -312,138 +242,17 @@ return this->draftStore.processStoreOperations(rt, std::move(operations)); } -const std::string REKEY_OPERATION = "rekey"; -const std::string REMOVE_OPERATION = "remove"; -const std::string REPLACE_OPERATION = "replace"; -const std::string REMOVE_MSGS_FOR_THREADS_OPERATION = - "remove_messages_for_threads"; -const std::string REMOVE_ALL_OPERATION = "remove_all"; - -const std::string REPLACE_MESSAGE_THREADS_OPERATION = "replace_threads"; -const std::string REMOVE_MESSAGE_THREADS_OPERATION = "remove_threads"; -const std::string REMOVE_ALL_MESSAGE_THREADS_OPERATION = "remove_all_threads"; - -std::vector> -createMessageStoreOperations(jsi::Runtime &rt, const jsi::Array &operations) { - - std::vector> messageStoreOps; - - for (auto idx = 0; idx < operations.size(rt); idx++) { - auto op = operations.getValueAtIndex(rt, idx).asObject(rt); - auto op_type = op.getProperty(rt, "type").asString(rt).utf8(rt); - - if (op_type == REMOVE_ALL_OPERATION) { - messageStoreOps.push_back(std::make_unique()); - continue; - } - if (op_type == REMOVE_ALL_MESSAGE_THREADS_OPERATION) { - messageStoreOps.push_back( - std::make_unique()); - continue; - } - - auto payload_obj = op.getProperty(rt, "payload").asObject(rt); - if (op_type == REMOVE_OPERATION) { - messageStoreOps.push_back( - std::make_unique(rt, payload_obj)); - - } else if (op_type == REMOVE_MSGS_FOR_THREADS_OPERATION) { - messageStoreOps.push_back( - std::make_unique(rt, payload_obj)); - - } else if (op_type == REPLACE_OPERATION) { - messageStoreOps.push_back( - std::make_unique(rt, payload_obj)); - - } else if (op_type == REKEY_OPERATION) { - messageStoreOps.push_back( - std::make_unique(rt, payload_obj)); - - } else if (op_type == REPLACE_MESSAGE_THREADS_OPERATION) { - messageStoreOps.push_back( - std::make_unique(rt, payload_obj)); - } else if (op_type == REMOVE_MESSAGE_THREADS_OPERATION) { - messageStoreOps.push_back( - std::make_unique( - rt, payload_obj)); - } else { - throw std::runtime_error("unsupported operation: " + op_type); - } - } - - return messageStoreOps; -} - jsi::Value CommCoreModule::processMessageStoreOperations( jsi::Runtime &rt, jsi::Array operations) { - - std::string createOperationsError; - std::shared_ptr>> - messageStoreOpsPtr; - try { - auto messageStoreOps = createMessageStoreOperations(rt, operations); - messageStoreOpsPtr = std::make_shared< - std::vector>>( - std::move(messageStoreOps)); - } catch (std::runtime_error &e) { - createOperationsError = e.what(); - } - - return createPromiseAsJSIValue( - rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { - taskType job = [=]() { - std::string error = createOperationsError; - - if (!error.size()) { - try { - DatabaseManager::getQueryExecutor().beginTransaction(); - for (const auto &operation : *messageStoreOpsPtr) { - operation->execute(); - } - DatabaseManager::getQueryExecutor().commitTransaction(); - } catch (std::system_error &e) { - error = e.what(); - DatabaseManager::getQueryExecutor().rollbackTransaction(); - } - } - - this->jsInvoker_->invokeAsync([=]() { - if (error.size()) { - promise->reject(error); - } else { - promise->resolve(jsi::Value::undefined()); - } - }); - }; - GlobalDBSingleton::instance.scheduleOrRunCancellable( - job, promise, this->jsInvoker_); - }); + return this->messageStore.processStoreOperations(rt, std::move(operations)); } void CommCoreModule::processMessageStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { - std::vector> messageStoreOps; - - try { - messageStoreOps = createMessageStoreOperations(rt, operations); - } catch (const std::exception &e) { - throw jsi::JSError(rt, e.what()); - } - - NativeModuleUtils::runSyncOrThrowJSError(rt, [&messageStoreOps]() { - try { - DatabaseManager::getQueryExecutor().beginTransaction(); - for (const auto &operation : messageStoreOps) { - operation->execute(); - } - DatabaseManager::getQueryExecutor().commitTransaction(); - } catch (const std::exception &e) { - DatabaseManager::getQueryExecutor().rollbackTransaction(); - throw e; - } - }); + return this->messageStore.processStoreOperationsSync( + rt, std::move(operations)); } jsi::Array CommCoreModule::getAllThreadsSync(jsi::Runtime &rt) { @@ -895,7 +704,8 @@ : facebook::react::CommCoreModuleSchemaCxxSpecJSI(jsInvoker), cryptoThread(std::make_unique("crypto")), draftStore(jsInvoker), - threadStore(jsInvoker) { + threadStore(jsInvoker), + messageStore(jsInvoker) { GlobalDBSingleton::instance.enableMultithreading(); } diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../../../DatabaseManagers/entities/Media.h" +#include "../../../DatabaseManagers/entities/Message.h" +#include "BaseDataStore.h" +#include "MessageStoreOperations.h" + +#include + +namespace comm { + +using MessageEntity = std::pair>; + +class MessageStore + : public BaseDataStore { +private: + static OperationType REKEY_OPERATION; + static OperationType REMOVE_OPERATION; + static OperationType REPLACE_OPERATION; + static OperationType REMOVE_MSGS_FOR_THREADS_OPERATION; + static OperationType REMOVE_ALL_OPERATION; + + static OperationType REPLACE_MESSAGE_THREADS_OPERATION; + static OperationType REMOVE_MESSAGE_THREADS_OPERATION; + static OperationType REMOVE_ALL_MESSAGE_THREADS_OPERATION; + +public: + MessageStore(std::shared_ptr jsInvoker); + + std::vector> createOperations( + jsi::Runtime &rt, + const jsi::Array &operations) const override; + + jsi::Array parseDBDataStore( + jsi::Runtime &rt, + std::shared_ptr> dataVectorPtr) const override; + + jsi::Array parseDBMessageStoreThreads( + jsi::Runtime &rt, + std::shared_ptr> threadsVectorPtr) const; +}; + +} // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.cpp b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.cpp new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/DataStores/MessageStore.cpp @@ -0,0 +1,152 @@ +#include "MessageStore.h" + +#include +#include + +namespace comm { + +using namespace facebook::react; + +OperationType MessageStore::REKEY_OPERATION = "rekey"; +OperationType MessageStore::REMOVE_OPERATION = "remove"; +OperationType MessageStore::REPLACE_OPERATION = "replace"; +OperationType MessageStore::REMOVE_MSGS_FOR_THREADS_OPERATION = + "remove_messages_for_threads"; +OperationType MessageStore::REMOVE_ALL_OPERATION = "remove_all"; + +OperationType MessageStore::REPLACE_MESSAGE_THREADS_OPERATION = + "replace_threads"; +OperationType MessageStore::REMOVE_MESSAGE_THREADS_OPERATION = "remove_threads"; +OperationType MessageStore::REMOVE_ALL_MESSAGE_THREADS_OPERATION = + "remove_all_threads"; + +MessageStore::MessageStore( + std::shared_ptr jsInvoker) + : BaseDataStore(jsInvoker) { +} + +jsi::Array MessageStore::parseDBDataStore( + jsi::Runtime &rt, + std::shared_ptr> messagesVectorPtr) const { + size_t numMessages = messagesVectorPtr->size(); + jsi::Array jsiMessages = jsi::Array(rt, numMessages); + size_t writeIndex = 0; + for (const auto &[message, media] : *messagesVectorPtr) { + auto jsiMessage = jsi::Object(rt); + jsiMessage.setProperty(rt, "id", message.id); + + if (message.local_id) { + auto local_id = message.local_id.get(); + jsiMessage.setProperty(rt, "local_id", *local_id); + } + + jsiMessage.setProperty(rt, "thread", message.thread); + jsiMessage.setProperty(rt, "user", message.user); + jsiMessage.setProperty(rt, "type", std::to_string(message.type)); + + if (message.future_type) { + auto future_type = message.future_type.get(); + jsiMessage.setProperty(rt, "future_type", std::to_string(*future_type)); + } + + if (message.content) { + auto content = message.content.get(); + jsiMessage.setProperty(rt, "content", *content); + } + + jsiMessage.setProperty(rt, "time", std::to_string(message.time)); + + size_t media_idx = 0; + jsi::Array jsiMediaArray = jsi::Array(rt, media.size()); + for (const auto &media_info : media) { + auto jsiMedia = jsi::Object(rt); + jsiMedia.setProperty(rt, "id", media_info.id); + jsiMedia.setProperty(rt, "uri", media_info.uri); + jsiMedia.setProperty(rt, "type", media_info.type); + jsiMedia.setProperty(rt, "extras", media_info.extras); + + jsiMediaArray.setValueAtIndex(rt, media_idx++, jsiMedia); + } + + jsiMessage.setProperty(rt, "media_infos", jsiMediaArray); + + jsiMessages.setValueAtIndex(rt, writeIndex++, jsiMessage); + } + return jsiMessages; +} + +std::vector> +MessageStore::createOperations(jsi::Runtime &rt, const jsi::Array &operations) + const { + + std::vector> messageStoreOps; + + for (auto idx = 0; idx < operations.size(rt); idx++) { + auto op = operations.getValueAtIndex(rt, idx).asObject(rt); + auto op_type = op.getProperty(rt, "type").asString(rt).utf8(rt); + + if (op_type == REMOVE_ALL_OPERATION) { + messageStoreOps.push_back(std::make_unique()); + continue; + } + if (op_type == REMOVE_ALL_MESSAGE_THREADS_OPERATION) { + messageStoreOps.push_back( + std::make_unique()); + continue; + } + + auto payload_obj = op.getProperty(rt, "payload").asObject(rt); + if (op_type == REMOVE_OPERATION) { + messageStoreOps.push_back( + std::make_unique(rt, payload_obj)); + + } else if (op_type == REMOVE_MSGS_FOR_THREADS_OPERATION) { + messageStoreOps.push_back( + std::make_unique(rt, payload_obj)); + + } else if (op_type == REPLACE_OPERATION) { + messageStoreOps.push_back( + std::make_unique(rt, payload_obj)); + + } else if (op_type == REKEY_OPERATION) { + messageStoreOps.push_back( + std::make_unique(rt, payload_obj)); + + } else if (op_type == REPLACE_MESSAGE_THREADS_OPERATION) { + messageStoreOps.push_back( + std::make_unique(rt, payload_obj)); + } else if (op_type == REMOVE_MESSAGE_THREADS_OPERATION) { + messageStoreOps.push_back( + std::make_unique( + rt, payload_obj)); + } else { + throw std::runtime_error("unsupported operation: " + op_type); + } + } + + return messageStoreOps; +} + +jsi::Array MessageStore::parseDBMessageStoreThreads( + jsi::Runtime &rt, + std::shared_ptr> threadsVectorPtr) const { + size_t numThreads = threadsVectorPtr->size(); + jsi::Array jsiThreads = jsi::Array(rt, numThreads); + size_t writeIdx = 0; + + for (const MessageStoreThread &thread : *threadsVectorPtr) { + jsi::Object jsiThread = jsi::Object(rt); + jsiThread.setProperty(rt, "id", thread.id); + jsiThread.setProperty( + rt, "start_reached", std::to_string(thread.start_reached)); + jsiThread.setProperty( + rt, "last_navigated_to", std::to_string(thread.last_navigated_to)); + jsiThread.setProperty( + rt, "last_pruned", std::to_string(thread.last_pruned)); + + jsiThreads.setValueAtIndex(rt, writeIdx++, jsiThread); + } + return jsiThreads; +} + +} // namespace comm diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 8EA59BD62A6E8E0400EB4F53 /* DraftStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8EA59BD42A6E8E0400EB4F53 /* DraftStore.cpp */; }; 8ED8B5342A4DD4EB00D3DA26 /* CommQueryExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8ED8B5332A4DCCE300D3DA26 /* CommQueryExecutor.cpp */; }; 8EF7756B2A7433630046A385 /* ThreadStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8EF775692A7433630046A385 /* ThreadStore.cpp */; }; + 8EF7756E2A7513F40046A385 /* MessageStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7756D2A7513F40046A385 /* MessageStore.cpp */; }; B71AFF1F265EDD8600B22352 /* IBMPlexSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B71AFF1E265EDD8600B22352 /* IBMPlexSans-Medium.ttf */; }; CB1648AF27CFBE6A00394D9D /* CryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */; }; CB24361829A39A2500FEC4E1 /* NotificationsCryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB24361729A39A2500FEC4E1 /* NotificationsCryptoModule.cpp */; }; @@ -205,6 +206,8 @@ 8EE6E4A02A39CCAB00AE6BCD /* DraftStoreOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DraftStoreOperations.h; sourceTree = ""; }; 8EF775692A7433630046A385 /* ThreadStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadStore.cpp; path = PersistentStorageUtilities/DataStores/ThreadStore.cpp; sourceTree = ""; }; 8EF7756A2A7433630046A385 /* ThreadStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadStore.h; path = PersistentStorageUtilities/DataStores/ThreadStore.h; sourceTree = ""; }; + 8EF7756C2A7513F40046A385 /* MessageStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MessageStore.h; path = PersistentStorageUtilities/DataStores/MessageStore.h; sourceTree = ""; }; + 8EF7756D2A7513F40046A385 /* MessageStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MessageStore.cpp; path = PersistentStorageUtilities/DataStores/MessageStore.cpp; sourceTree = ""; }; 913E5A7BDECB327E3DE11053 /* Pods-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.release.xcconfig"; sourceTree = ""; }; 994BEBDD4E4959F69CEA0BC3 /* libPods-Comm.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Comm.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B7055C6B26E477CF00BE0548 /* MessageStoreOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageStoreOperations.h; sourceTree = ""; }; @@ -564,6 +567,8 @@ 8EA59BD02A6E786200EB4F53 /* DataStores */ = { isa = PBXGroup; children = ( + 8EF7756D2A7513F40046A385 /* MessageStore.cpp */, + 8EF7756C2A7513F40046A385 /* MessageStore.h */, 8EF775692A7433630046A385 /* ThreadStore.cpp */, 8EF7756A2A7433630046A385 /* ThreadStore.h */, 8EA59BD42A6E8E0400EB4F53 /* DraftStore.cpp */, @@ -1035,6 +1040,7 @@ 71CA4AEC262F236100835C89 /* Tools.mm in Sources */, 71762A75270D8AAE00F565ED /* PlatformSpecificTools.mm in Sources */, 71BF5B7126B3FF0900EDE27D /* Session.cpp in Sources */, + 8EF7756E2A7513F40046A385 /* MessageStore.cpp in Sources */, 71BF5B7526B401D300EDE27D /* Tools.cpp in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 7FE4D9F5291DFE9300667BF6 /* commJSI-generated.cpp in Sources */,