diff --git a/native/account/siwe-panel.react.js b/native/account/siwe-panel.react.js
index 365d86a43..9d6748d1f 100644
--- a/native/account/siwe-panel.react.js
+++ b/native/account/siwe-panel.react.js
@@ -1,266 +1,268 @@
// @flow
import BottomSheet from '@gorhom/bottom-sheet';
import * as React from 'react';
import { ActivityIndicator, View, Alert } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import WebView from 'react-native-webview';
import {
getSIWENonce,
getSIWENonceActionTypes,
siweAuth,
siweAuthActionTypes,
} from 'lib/actions/siwe-actions.js';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
import type { LogInStartingPayload } from 'lib/types/account-types.js';
import type { SIWEWebViewMessage } from 'lib/types/siwe-types.js';
import {
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils.js';
import type { LoggedOutMode } from './logged-out-modal.react.js';
import { commCoreModule } from '../native-modules.js';
import { NavContext } from '../navigation/navigation-context.js';
import { useSelector } from '../redux/redux-utils.js';
import { nativeLogInExtraInfoSelector } from '../selectors/account-selectors.js';
import { defaultLandingURLPrefix } from '../utils/url-utils.js';
const commSIWE = `${defaultLandingURLPrefix}/siwe`;
const getSIWENonceLoadingStatusSelector = createLoadingStatusSelector(
getSIWENonceActionTypes,
);
const siweAuthLoadingStatusSelector =
createLoadingStatusSelector(siweAuthActionTypes);
type Props = {
+onClose: () => mixed,
+nextMode: LoggedOutMode,
};
function SIWEPanel(props: Props): React.Node {
const navContext = React.useContext(NavContext);
const dispatchActionPromise = useDispatchActionPromise();
const getSIWENonceCall = useServerCall(getSIWENonce);
const siweAuthCall = useServerCall(siweAuth);
const logInExtraInfo = useSelector(state =>
nativeLogInExtraInfoSelector({
redux: state,
navContext,
}),
);
const getSIWENonceCallFailed = useSelector(
state => getSIWENonceLoadingStatusSelector(state) === 'error',
);
const { onClose } = props;
React.useEffect(() => {
if (getSIWENonceCallFailed) {
Alert.alert(
'Unknown error',
'Uhh... try again?',
[{ text: 'OK', onPress: onClose }],
{ cancelable: false },
);
}
}, [getSIWENonceCallFailed, onClose]);
const siweAuthCallLoading = useSelector(
state => siweAuthLoadingStatusSelector(state) === 'loading',
);
const [nonce, setNonce] = React.useState(null);
const [primaryIdentityPublicKey, setPrimaryIdentityPublicKey] =
React.useState(null);
React.useEffect(() => {
(async () => {
dispatchActionPromise(
getSIWENonceActionTypes,
(async () => {
const response = await getSIWENonceCall();
setNonce(response);
})(),
);
await commCoreModule.initializeCryptoAccount();
- const { ed25519 } = await commCoreModule.getUserPublicKey();
+ const {
+ primaryIdentityPublicKeys: { ed25519 },
+ } = await commCoreModule.getUserPublicKey();
setPrimaryIdentityPublicKey(ed25519);
})();
}, [dispatchActionPromise, getSIWENonceCall]);
const [isLoading, setLoading] = React.useState(true);
const [isWalletConnectModalOpen, setWalletConnectModalOpen] =
React.useState(false);
const insets = useSafeAreaInsets();
const bottomInset = insets.bottom;
const snapPoints = React.useMemo(() => {
if (isLoading) {
return [1];
} else if (isWalletConnectModalOpen) {
return [bottomInset + 600];
} else {
return [bottomInset + 435, bottomInset + 600];
}
}, [isLoading, isWalletConnectModalOpen, bottomInset]);
const bottomSheetRef = React.useRef();
const snapToIndex = bottomSheetRef.current?.snapToIndex;
React.useEffect(() => {
// When the snapPoints change, always reset to the first one
// Without this, when we close the WalletConnect modal we don't resize
snapToIndex?.(0);
}, [snapToIndex, snapPoints]);
const callSIWE = React.useCallback(
async (message, signature, extraInfo) => {
try {
return await siweAuthCall({
message,
signature,
...extraInfo,
});
} catch (e) {
Alert.alert(
'Unknown error',
'Uhh... try again?',
[{ text: 'OK', onPress: onClose }],
{ cancelable: false },
);
throw e;
}
},
[onClose, siweAuthCall],
);
const handleSIWE = React.useCallback(
async ({ message, signature }) => {
const { primaryIdentityPublicKey: publicKey, ...extraInfo } =
await logInExtraInfo();
dispatchActionPromise(
siweAuthActionTypes,
callSIWE(message, signature, extraInfo),
undefined,
({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload),
);
},
[logInExtraInfo, dispatchActionPromise, callSIWE],
);
const closeBottomSheet = bottomSheetRef.current?.close;
const { nextMode } = props;
const disableOnClose = React.useRef(false);
const handleMessage = React.useCallback(
async event => {
const data: SIWEWebViewMessage = JSON.parse(event.nativeEvent.data);
if (data.type === 'siwe_success') {
const { address, message, signature } = data;
if (address && signature) {
disableOnClose.current = true;
closeBottomSheet?.();
await handleSIWE({ message, signature });
}
} else if (data.type === 'siwe_closed') {
onClose();
closeBottomSheet?.();
} else if (data.type === 'walletconnect_modal_update') {
setWalletConnectModalOpen(data.state === 'open');
}
},
[handleSIWE, onClose, closeBottomSheet],
);
const prevNextModeRef = React.useRef();
React.useEffect(() => {
if (nextMode === 'prompt' && prevNextModeRef.current === 'siwe') {
closeBottomSheet?.();
}
prevNextModeRef.current = nextMode;
}, [nextMode, closeBottomSheet]);
const source = React.useMemo(
() => ({
uri: commSIWE,
headers: {
'siwe-nonce': nonce,
'siwe-primary-identity-public-key': primaryIdentityPublicKey,
},
}),
[nonce, primaryIdentityPublicKey],
);
const onWebViewLoaded = React.useCallback(() => {
setLoading(false);
}, []);
const backgroundStyle = React.useMemo(
() => ({
backgroundColor: '#242529',
}),
[],
);
const bottomSheetHandleIndicatorStyle = React.useMemo(
() => ({
backgroundColor: 'white',
}),
[],
);
const onBottomSheetChange = React.useCallback(
(index: number) => {
if (disableOnClose.current) {
disableOnClose.current = false;
return;
}
if (index === -1) {
onClose();
}
},
[onClose],
);
let bottomSheet;
if (nonce && primaryIdentityPublicKey) {
bottomSheet = (
);
}
let activity;
if (!getSIWENonceCallFailed && (isLoading || siweAuthCallLoading)) {
activity = ;
}
const activityContainer = React.useMemo(
() => ({
flex: 1,
}),
[],
);
return (
<>
{activity}
{bottomSheet}
>
);
}
export default SIWEPanel;
diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp
index d54c0e21f..431704f71 100644
--- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp
+++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp
@@ -1,96 +1,98 @@
#include "DatabaseManager.h"
+#include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h"
#include "../Tools/CommSecureStore.h"
#include "Logger.h"
#include "SQLiteQueryExecutor.h"
namespace comm {
std::once_flag DatabaseManager::initialized;
typedef const std::string DatabaseManagerStatus;
DatabaseManagerStatus DB_MANAGER_WORKABLE = "WORKABLE";
DatabaseManagerStatus DB_MANAGER_FIRST_FAILURE = "FIRST_FAILURE";
DatabaseManagerStatus DB_MANAGER_SECOND_FAILURE = "SECOND_FAILURE";
DatabaseManagerStatus DB_OPERATIONS_FAILURE = "DB_OPERATIONS_FAILURE";
const std::string DATABASE_MANAGER_STATUS_KEY = "DATABASE_MANAGER_STATUS";
const DatabaseQueryExecutor &DatabaseManager::getQueryExecutor() {
// TODO: conditionally create desired type of db manager
// maybe basing on some preprocessor flag
thread_local SQLiteQueryExecutor instance;
// creating an instance means that migration code was executed
// and finished without error and database is workable
std::call_once(DatabaseManager::initialized, []() {
DatabaseManager::indicateQueryExecutorCreation();
});
return instance;
}
void DatabaseManager::clearSensitiveData() {
SQLiteQueryExecutor::clearSensitiveData();
+ NotificationsCryptoModule::clearSensitiveData();
DatabaseManager::setDatabaseStatusAsWorkable();
}
void DatabaseManager::initializeQueryExecutor(std::string &databasePath) {
comm::CommSecureStore commSecureStore{};
try {
SQLiteQueryExecutor::initialize(databasePath);
DatabaseManager::getQueryExecutor();
DatabaseManager::indicateQueryExecutorCreation();
Logger::log("Database manager initialized");
} catch (...) {
folly::Optional databaseManagerStatus =
commSecureStore.get(DATABASE_MANAGER_STATUS_KEY);
if (!databaseManagerStatus.hasValue() ||
databaseManagerStatus.value() == DB_MANAGER_WORKABLE) {
commSecureStore.set(
DATABASE_MANAGER_STATUS_KEY, DB_MANAGER_FIRST_FAILURE);
Logger::log("Database manager initialization issue, terminating app");
throw;
}
if (databaseManagerStatus.value() == DB_MANAGER_FIRST_FAILURE) {
commSecureStore.set(
DATABASE_MANAGER_STATUS_KEY, DB_MANAGER_SECOND_FAILURE);
Logger::log(
"Database manager initialization issue, app proceeding, but "
"database needs to be deleted");
return;
}
}
}
void DatabaseManager::setDatabaseStatusAsWorkable() {
comm::CommSecureStore commSecureStore{};
commSecureStore.set(DATABASE_MANAGER_STATUS_KEY, DB_MANAGER_WORKABLE);
}
void DatabaseManager::indicateQueryExecutorCreation() {
comm::CommSecureStore commSecureStore{};
folly::Optional databaseManagerStatus =
commSecureStore.get(DATABASE_MANAGER_STATUS_KEY);
if (!databaseManagerStatus.hasValue() ||
databaseManagerStatus.value() != DB_OPERATIONS_FAILURE) {
// creating query executor means that schema was created without error,
// but this doesn't imply that schema has a proper structure,
// and operation will not crash, this case should not be overridden
commSecureStore.set(DATABASE_MANAGER_STATUS_KEY, DB_MANAGER_WORKABLE);
}
}
bool DatabaseManager::checkIfDatabaseNeedsDeletion() {
comm::CommSecureStore commSecureStore{};
folly::Optional databaseManagerStatus =
commSecureStore.get(DATABASE_MANAGER_STATUS_KEY);
return databaseManagerStatus.hasValue() &&
(databaseManagerStatus.value() == DB_MANAGER_SECOND_FAILURE ||
databaseManagerStatus.value() == DB_OPERATIONS_FAILURE);
}
void DatabaseManager::reportDBOperationsFailure() {
comm::CommSecureStore commSecureStore{};
commSecureStore.set(DATABASE_MANAGER_STATUS_KEY, DB_OPERATIONS_FAILURE);
}
} // namespace comm
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
index 0c2540094..2ce02c0ad 100644
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
@@ -1,1076 +1,1121 @@
#include "CommCoreModule.h"
#include "../CryptoTools/DeviceID.h"
+#include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h"
#include "DatabaseManager.h"
#include "DraftStoreOperations.h"
#include "InternalModules/GlobalDBSingleton.h"
#include "MessageStoreOperations.h"
#include "TerminateApp.h"
#include "ThreadStoreOperations.h"
#include
#include
#include
#include
namespace comm {
using namespace facebook::react;
template
T CommCoreModule::runSyncOrThrowJSError(
jsi::Runtime &rt,
std::function task) {
std::promise promise;
GlobalDBSingleton::instance.scheduleOrRunCancellable([&promise, &task]() {
try {
if constexpr (std::is_void::value) {
task();
promise.set_value();
} else {
promise.set_value(task());
}
} catch (const std::exception &e) {
promise.set_exception(std::make_exception_ptr(e));
}
});
// We cannot instantiate JSError on database thread, so
// on the main thread we re-throw C++ error, catch it and
// transform to informative JSError on the main thread
try {
return promise.get_future().get();
} catch (const std::exception &e) {
throw jsi::JSError(rt, e.what());
}
}
jsi::Value CommCoreModule::getDraft(jsi::Runtime &rt, jsi::String key) {
std::string keyStr = key.utf8(rt);
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=, &innerRt]() {
std::string error;
std::string draftStr;
try {
draftStr = DatabaseManager::getQueryExecutor().getDraft(keyStr);
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([=, &innerRt]() {
if (error.size()) {
promise->reject(error);
return;
}
jsi::String draft = jsi::String::createFromUtf8(innerRt, draftStr);
promise->resolve(std::move(draft));
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::updateDraft(
jsi::Runtime &rt,
jsi::String key,
jsi::String text) {
std::string keyStr = key.utf8(rt);
std::string textStr = text.utf8(rt);
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().updateDraft(keyStr, textStr);
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([=]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(true);
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::moveDraft(
jsi::Runtime &rt,
jsi::String oldKey,
jsi::String newKey) {
std::string oldKeyStr = oldKey.utf8(rt);
std::string newKeyStr = newKey.utf8(rt);
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=]() {
std::string error;
bool result = false;
try {
result = DatabaseManager::getQueryExecutor().moveDraft(
oldKeyStr, newKeyStr);
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([=]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(result);
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Array parseDBDrafts(
jsi::Runtime &rt,
std::shared_ptr> draftsVectorPtr) {
size_t numDrafts = count_if(
draftsVectorPtr->begin(), draftsVectorPtr->end(), [](Draft draft) {
return !draft.text.empty();
});
jsi::Array jsiDrafts = jsi::Array(rt, numDrafts);
size_t writeIndex = 0;
for (Draft draft : *draftsVectorPtr) {
if (draft.text.empty()) {
continue;
}
auto jsiDraft = jsi::Object(rt);
jsiDraft.setProperty(rt, "key", draft.key);
jsiDraft.setProperty(rt, "text", draft.text);
jsiDrafts.setValueAtIndex(rt, writeIndex++, jsiDraft);
}
return jsiDrafts;
}
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 parseDBThreads(
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 Thread &thread : *threadsVectorPtr) {
jsi::Object jsiThread = jsi::Object(rt);
jsiThread.setProperty(rt, "id", thread.id);
jsiThread.setProperty(rt, "type", thread.type);
jsiThread.setProperty(
rt,
"name",
thread.name ? jsi::String::createFromUtf8(rt, *thread.name)
: jsi::Value::null());
jsiThread.setProperty(
rt,
"description",
thread.description
? jsi::String::createFromUtf8(rt, *thread.description)
: jsi::Value::null());
jsiThread.setProperty(rt, "color", thread.color);
jsiThread.setProperty(
rt, "creationTime", std::to_string(thread.creation_time));
jsiThread.setProperty(
rt,
"parentThreadID",
thread.parent_thread_id
? jsi::String::createFromUtf8(rt, *thread.parent_thread_id)
: jsi::Value::null());
jsiThread.setProperty(
rt,
"containingThreadID",
thread.containing_thread_id
? jsi::String::createFromUtf8(rt, *thread.containing_thread_id)
: jsi::Value::null());
jsiThread.setProperty(
rt,
"community",
thread.community ? jsi::String::createFromUtf8(rt, *thread.community)
: jsi::Value::null());
jsiThread.setProperty(rt, "members", thread.members);
jsiThread.setProperty(rt, "roles", thread.roles);
jsiThread.setProperty(rt, "currentUser", thread.current_user);
jsiThread.setProperty(
rt,
"sourceMessageID",
thread.source_message_id
? jsi::String::createFromUtf8(rt, *thread.source_message_id)
: jsi::Value::null());
jsiThread.setProperty(rt, "repliesCount", thread.replies_count);
jsiThreads.setValueAtIndex(rt, writeIdx++, jsiThread);
}
return jsiThreads;
}
jsi::Value CommCoreModule::getClientDBStore(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=, &innerRt]() {
std::string error;
std::vector draftsVector;
std::vector threadsVector;
std::vector>> messagesVector;
try {
draftsVector = DatabaseManager::getQueryExecutor().getAllDrafts();
messagesVector =
DatabaseManager::getQueryExecutor().getAllMessages();
threadsVector = DatabaseManager::getQueryExecutor().getAllThreads();
} catch (std::system_error &e) {
error = e.what();
}
auto draftsVectorPtr =
std::make_shared>(std::move(draftsVector));
auto messagesVectorPtr = std::make_shared<
std::vector>>>(
std::move(messagesVector));
auto threadsVectorPtr =
std::make_shared>(std::move(threadsVector));
this->jsInvoker_->invokeAsync([&innerRt,
draftsVectorPtr,
messagesVectorPtr,
threadsVectorPtr,
error,
promise]() {
if (error.size()) {
promise->reject(error);
return;
}
jsi::Array jsiDrafts = parseDBDrafts(innerRt, draftsVectorPtr);
jsi::Array jsiMessages =
parseDBMessages(innerRt, messagesVectorPtr);
jsi::Array jsiThreads = parseDBThreads(innerRt, threadsVectorPtr);
auto jsiClientDBStore = jsi::Object(innerRt);
jsiClientDBStore.setProperty(innerRt, "messages", jsiMessages);
jsiClientDBStore.setProperty(innerRt, "threads", jsiThreads);
jsiClientDBStore.setProperty(innerRt, "drafts", jsiDrafts);
promise->resolve(std::move(jsiClientDBStore));
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::removeAllDrafts(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().removeAllDrafts();
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([=]() {
if (error.size()) {
promise->reject(error);
return;
}
promise->resolve(jsi::Value::undefined());
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Array CommCoreModule::getAllMessagesSync(jsi::Runtime &rt) {
auto messagesVector = this->runSyncOrThrowJSError<
std::vector>>>(rt, []() {
return DatabaseManager::getQueryExecutor().getAllMessages();
});
auto messagesVectorPtr =
std::make_shared>>>(
std::move(messagesVector));
jsi::Array jsiMessages = parseDBMessages(rt, messagesVectorPtr);
return jsiMessages;
}
const std::string UPDATE_DRAFT_OPERATION = "update";
const std::string MOVE_DRAFT_OPERATION = "move";
const std::string REMOVE_ALL_DRAFTS_OPERATION = "remove_all";
std::vector>
createDraftStoreOperations(jsi::Runtime &rt, const jsi::Array &operations) {
std::vector> draftStoreOps;
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_DRAFTS_OPERATION) {
draftStoreOps.push_back(std::make_unique());
continue;
}
auto payload_obj = op.getProperty(rt, "payload").asObject(rt);
if (op_type == UPDATE_DRAFT_OPERATION) {
draftStoreOps.push_back(
std::make_unique(rt, payload_obj));
} else if (op_type == MOVE_DRAFT_OPERATION) {
draftStoreOps.push_back(
std::make_unique(rt, payload_obj));
} else {
throw std::runtime_error("unsupported operation: " + op_type);
}
}
return draftStoreOps;
}
jsi::Value CommCoreModule::processDraftStoreOperations(
jsi::Runtime &rt,
jsi::Array operations) {
std::string createOperationsError;
std::shared_ptr>>
draftStoreOpsPtr;
try {
auto draftStoreOps = createDraftStoreOperations(rt, operations);
draftStoreOpsPtr =
std::make_shared>>(
std::move(draftStoreOps));
} 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 : *draftStoreOpsPtr) {
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_);
});
}
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";
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;
}
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 {
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_);
});
}
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());
}
this->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;
}
});
}
jsi::Array CommCoreModule::getAllThreadsSync(jsi::Runtime &rt) {
auto threadsVector = this->runSyncOrThrowJSError>(
rt, []() { return DatabaseManager::getQueryExecutor().getAllThreads(); });
auto threadsVectorPtr =
std::make_shared>(std::move(threadsVector));
jsi::Array jsiThreads = parseDBThreads(rt, threadsVectorPtr);
return jsiThreads;
}
std::vector>
createThreadStoreOperations(jsi::Runtime &rt, const jsi::Array &operations) {
std::vector> threadStoreOps;
for (size_t idx = 0; idx < operations.size(rt); idx++) {
jsi::Object op = operations.getValueAtIndex(rt, idx).asObject(rt);
std::string opType = op.getProperty(rt, "type").asString(rt).utf8(rt);
if (opType == REMOVE_OPERATION) {
std::vector threadIDsToRemove;
jsi::Object payloadObj = op.getProperty(rt, "payload").asObject(rt);
jsi::Array threadIDs =
payloadObj.getProperty(rt, "ids").asObject(rt).asArray(rt);
for (int threadIdx = 0; threadIdx < threadIDs.size(rt); threadIdx++) {
threadIDsToRemove.push_back(
threadIDs.getValueAtIndex(rt, threadIdx).asString(rt).utf8(rt));
}
threadStoreOps.push_back(std::make_unique(
std::move(threadIDsToRemove)));
} else if (opType == REMOVE_ALL_OPERATION) {
threadStoreOps.push_back(std::make_unique());
} else if (opType == REPLACE_OPERATION) {
jsi::Object threadObj = op.getProperty(rt, "payload").asObject(rt);
std::string threadID =
threadObj.getProperty(rt, "id").asString(rt).utf8(rt);
int type = std::lround(threadObj.getProperty(rt, "type").asNumber());
jsi::Value maybeName = threadObj.getProperty(rt, "name");
std::unique_ptr name = maybeName.isString()
? std::make_unique(maybeName.asString(rt).utf8(rt))
: nullptr;
jsi::Value maybeDescription = threadObj.getProperty(rt, "description");
std::unique_ptr description = maybeDescription.isString()
? std::make_unique(
maybeDescription.asString(rt).utf8(rt))
: nullptr;
std::string color =
threadObj.getProperty(rt, "color").asString(rt).utf8(rt);
int64_t creationTime = std::stoll(
threadObj.getProperty(rt, "creationTime").asString(rt).utf8(rt));
jsi::Value maybeParentThreadID =
threadObj.getProperty(rt, "parentThreadID");
std::unique_ptr parentThreadID =
maybeParentThreadID.isString()
? std::make_unique(
maybeParentThreadID.asString(rt).utf8(rt))
: nullptr;
jsi::Value maybeContainingThreadID =
threadObj.getProperty(rt, "containingThreadID");
std::unique_ptr containingThreadID =
maybeContainingThreadID.isString()
? std::make_unique(
maybeContainingThreadID.asString(rt).utf8(rt))
: nullptr;
jsi::Value maybeCommunity = threadObj.getProperty(rt, "community");
std::unique_ptr community = maybeCommunity.isString()
? std::make_unique(maybeCommunity.asString(rt).utf8(rt))
: nullptr;
std::string members =
threadObj.getProperty(rt, "members").asString(rt).utf8(rt);
std::string roles =
threadObj.getProperty(rt, "roles").asString(rt).utf8(rt);
std::string currentUser =
threadObj.getProperty(rt, "currentUser").asString(rt).utf8(rt);
jsi::Value maybeSourceMessageID =
threadObj.getProperty(rt, "sourceMessageID");
std::unique_ptr sourceMessageID =
maybeSourceMessageID.isString()
? std::make_unique(
maybeSourceMessageID.asString(rt).utf8(rt))
: nullptr;
int repliesCount =
std::lround(threadObj.getProperty(rt, "repliesCount").asNumber());
Thread thread{
threadID,
type,
std::move(name),
std::move(description),
color,
creationTime,
std::move(parentThreadID),
std::move(containingThreadID),
std::move(community),
members,
roles,
currentUser,
std::move(sourceMessageID),
repliesCount};
threadStoreOps.push_back(
std::make_unique(std::move(thread)));
} else {
throw std::runtime_error("unsupported operation: " + opType);
}
};
return threadStoreOps;
}
jsi::Value CommCoreModule::processThreadStoreOperations(
jsi::Runtime &rt,
jsi::Array operations) {
std::string operationsError;
std::shared_ptr>>
threadStoreOpsPtr;
try {
auto threadStoreOps = createThreadStoreOperations(rt, operations);
threadStoreOpsPtr = std::make_shared<
std::vector>>(
std::move(threadStoreOps));
} catch (std::runtime_error &e) {
operationsError = e.what();
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=]() {
std::string error = operationsError;
if (!error.size()) {
try {
DatabaseManager::getQueryExecutor().beginTransaction();
for (const auto &operation : *threadStoreOpsPtr) {
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_);
});
}
void CommCoreModule::processThreadStoreOperationsSync(
jsi::Runtime &rt,
jsi::Array operations) {
std::vector> threadStoreOps;
try {
threadStoreOps = createThreadStoreOperations(rt, operations);
} catch (const std::exception &e) {
throw jsi::JSError(rt, e.what());
}
this->runSyncOrThrowJSError(rt, [&threadStoreOps]() {
try {
DatabaseManager::getQueryExecutor().beginTransaction();
for (const auto &operation : threadStoreOps) {
operation->execute();
}
DatabaseManager::getQueryExecutor().commitTransaction();
} catch (const std::exception &e) {
DatabaseManager::getQueryExecutor().rollbackTransaction();
throw e;
}
});
}
void CommCoreModule::terminate(jsi::Runtime &rt) {
TerminateApp::terminate();
}
jsi::Value CommCoreModule::initializeCryptoAccount(jsi::Runtime &rt) {
folly::Optional storedSecretKey =
this->secureStore.get(this->secureStoreAccountDataKey);
if (!storedSecretKey.hasValue()) {
storedSecretKey = crypto::Tools::generateRandomString(64);
this->secureStore.set(
this->secureStoreAccountDataKey, storedSecretKey.value());
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=]() {
crypto::Persist persist;
std::string error;
try {
folly::Optional accountData =
DatabaseManager::getQueryExecutor().getOlmPersistAccountData();
if (accountData.hasValue()) {
persist.account =
crypto::OlmBuffer(accountData->begin(), accountData->end());
// handle sessions data
std::vector sessionsData =
DatabaseManager::getQueryExecutor()
.getOlmPersistSessionsData();
for (OlmPersistSession &sessionsDataItem : sessionsData) {
crypto::OlmBuffer sessionDataBuffer(
sessionsDataItem.session_data.begin(),
sessionsDataItem.session_data.end());
persist.sessions.insert(std::make_pair(
sessionsDataItem.target_user_id, sessionDataBuffer));
}
}
} catch (std::system_error &e) {
error = e.what();
}
this->cryptoThread->scheduleTask([=]() {
std::string error;
this->cryptoModule.reset(new crypto::CryptoModule(
this->publicCryptoAccountID, storedSecretKey.value(), persist));
if (persist.isEmpty()) {
crypto::Persist newPersist =
this->cryptoModule->storeAsB64(storedSecretKey.value());
GlobalDBSingleton::instance.scheduleOrRunCancellable(
[=]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().storeOlmPersistData(
newPersist);
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([=]() {
if (error.size()) {
promise->reject(error);
return;
}
- promise->resolve(jsi::Value::undefined());
});
},
promise,
this->jsInvoker_);
} else {
this->cryptoModule->restoreFromB64(
storedSecretKey.value(), persist);
this->jsInvoker_->invokeAsync([=]() {
if (error.size()) {
promise->reject(error);
return;
}
- promise->resolve(jsi::Value::undefined());
});
}
+ try {
+ NotificationsCryptoModule::initializeNotificationsCryptoAccount(
+ "Comm");
+ } catch (const std::exception &e) {
+ error = e.what();
+ }
+ this->jsInvoker_->invokeAsync([=]() {
+ if (error.size()) {
+ promise->reject(error);
+ return;
+ }
+ promise->resolve(jsi::Value::undefined());
+ });
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::getUserPublicKey(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=, &innerRt]() {
std::string error;
- std::string result;
+ std::string primaryKeysResult;
+ std::string notificationsKeysResult;
if (this->cryptoModule == nullptr) {
error = "user has not been initialized";
} else {
- result = this->cryptoModule->getIdentityKeys();
+ primaryKeysResult = this->cryptoModule->getIdentityKeys();
+ }
+ try {
+ if (!error.size()) {
+ notificationsKeysResult =
+ NotificationsCryptoModule::getNotificationsIdentityKeys();
+ }
+ } catch (const std::exception &e) {
+ error = e.what();
}
this->jsInvoker_->invokeAsync([=, &innerRt]() {
if (error.size()) {
promise->reject(error);
return;
}
- folly::dynamic parsed = folly::parseJson(result);
- auto curve25519{jsi::String::createFromUtf8(
- innerRt, parsed["curve25519"].asString())};
- auto ed25519{jsi::String::createFromUtf8(
- innerRt, parsed["ed25519"].asString())};
+
+ folly::dynamic parsedPrimary = folly::parseJson(primaryKeysResult);
+ auto primaryCurve25519{jsi::String::createFromUtf8(
+ innerRt, parsedPrimary["curve25519"].asString())};
+ auto primaryEd25519{jsi::String::createFromUtf8(
+ innerRt, parsedPrimary["ed25519"].asString())};
+ auto jsiPrimaryIdentityPublicKeys = jsi::Object(innerRt);
+ jsiPrimaryIdentityPublicKeys.setProperty(
+ innerRt, "ed25519", primaryEd25519);
+ jsiPrimaryIdentityPublicKeys.setProperty(
+ innerRt, "curve25519", primaryCurve25519);
+
+ folly::dynamic parsedNotifications =
+ folly::parseJson(notificationsKeysResult);
+ auto notificationsCurve25519{jsi::String::createFromUtf8(
+ innerRt, parsedNotifications["curve25519"].asString())};
+ auto notificationsEd25519{jsi::String::createFromUtf8(
+ innerRt, parsedNotifications["ed25519"].asString())};
+ auto jsiNotificationIdentityPublicKeys = jsi::Object(innerRt);
+ jsiNotificationIdentityPublicKeys.setProperty(
+ innerRt, "ed25519", notificationsEd25519);
+ jsiNotificationIdentityPublicKeys.setProperty(
+ innerRt, "curve25519", notificationsCurve25519);
auto jsiClientPublicKeys = jsi::Object(innerRt);
- jsiClientPublicKeys.setProperty(innerRt, "curve25519", curve25519);
- jsiClientPublicKeys.setProperty(innerRt, "ed25519", ed25519);
+ jsiClientPublicKeys.setProperty(
+ innerRt,
+ "primaryIdentityPublicKeys",
+ jsiPrimaryIdentityPublicKeys);
+ jsiClientPublicKeys.setProperty(
+ innerRt,
+ "notificationIdentityPublicKeys",
+ jsiNotificationIdentityPublicKeys);
promise->resolve(std::move(jsiClientPublicKeys));
});
};
this->cryptoThread->scheduleTask(job);
});
}
jsi::Value CommCoreModule::getUserOneTimeKeys(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [=, &innerRt]() {
std::string error;
std::string result;
if (this->cryptoModule == nullptr) {
error = "user has not been initialized";
} else {
result = this->cryptoModule->getOneTimeKeys();
}
this->jsInvoker_->invokeAsync([=, &innerRt]() {
if (error.size()) {
promise->reject(error);
return;
}
promise->resolve(jsi::String::createFromUtf8(innerRt, result));
});
};
this->cryptoThread->scheduleTask(job);
});
}
CommCoreModule::CommCoreModule(
std::shared_ptr jsInvoker)
: facebook::react::CommCoreModuleSchemaCxxSpecJSI(jsInvoker),
cryptoThread(std::make_unique("crypto")) {
GlobalDBSingleton::instance.enableMultithreading();
}
double CommCoreModule::getCodeVersion(jsi::Runtime &rt) {
return this->codeVersion;
}
jsi::Value CommCoreModule::setNotifyToken(jsi::Runtime &rt, jsi::String token) {
auto notifyToken{token.utf8(rt)};
return createPromiseAsJSIValue(
rt,
[this,
notifyToken](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this, notifyToken, promise]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().setNotifyToken(notifyToken);
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([error, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::Value::undefined());
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::clearNotifyToken(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this, promise]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().clearNotifyToken();
} catch (std::system_error &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([error, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::Value::undefined());
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
};
jsi::Value
CommCoreModule::setCurrentUserID(jsi::Runtime &rt, jsi::String userID) {
auto currentUserID{userID.utf8(rt)};
return createPromiseAsJSIValue(
rt,
[this,
currentUserID](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this, promise, currentUserID]() {
std::string error;
try {
DatabaseManager::getQueryExecutor().setCurrentUserID(currentUserID);
} catch (const std::exception &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([error, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::Value::undefined());
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::getCurrentUserID(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this, &innerRt, promise]() {
std::string error;
std::string result;
try {
result = DatabaseManager::getQueryExecutor().getCurrentUserID();
} catch (const std::exception &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([&innerRt, error, result, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::String::createFromUtf8(innerRt, result));
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value
CommCoreModule::setDeviceID(jsi::Runtime &rt, jsi::String deviceType) {
std::string type = deviceType.utf8(rt);
std::string deviceID;
std::string deviceIDGenerationError;
try {
deviceID = DeviceIDGenerator::generateDeviceID(type);
} catch (std::invalid_argument &e) {
deviceIDGenerationError =
"setDeviceID: incorrect function argument. Must be one of: KEYSERVER, "
"WEB, MOBILE.";
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this,
&innerRt,
promise,
deviceIDGenerationError,
deviceID]() {
std::string error = deviceIDGenerationError;
if (!error.size()) {
try {
DatabaseManager::getQueryExecutor().setDeviceID(deviceID);
} catch (const std::exception &e) {
error = e.what();
}
}
this->jsInvoker_->invokeAsync([&innerRt, promise, error, deviceID]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::String::createFromUtf8(innerRt, deviceID));
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::getDeviceID(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) {
taskType job = [this, &innerRt, promise]() {
std::string error;
std::string result;
try {
result = DatabaseManager::getQueryExecutor().getDeviceID();
} catch (const std::exception &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([&innerRt, error, result, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::String::createFromUtf8(innerRt, result));
}
});
};
GlobalDBSingleton::instance.scheduleOrRunCancellable(
job, promise, this->jsInvoker_);
});
}
jsi::Value CommCoreModule::clearSensitiveData(jsi::Runtime &rt) {
return createPromiseAsJSIValue(
rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) {
GlobalDBSingleton::instance.setTasksCancelled(true);
taskType job = [this, promise]() {
std::string error;
try {
DatabaseManager::clearSensitiveData();
} catch (const std::exception &e) {
error = e.what();
}
this->jsInvoker_->invokeAsync([error, promise]() {
if (error.size()) {
promise->reject(error);
} else {
promise->resolve(jsi::Value::undefined());
}
});
GlobalDBSingleton::instance.scheduleOrRun(
[]() { GlobalDBSingleton::instance.setTasksCancelled(false); });
};
GlobalDBSingleton::instance.scheduleOrRun(job);
});
}
bool CommCoreModule::checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) {
return DatabaseManager::checkIfDatabaseNeedsDeletion();
}
void CommCoreModule::reportDBOperationsFailure(jsi::Runtime &rt) {
DatabaseManager::reportDBOperationsFailure();
}
} // namespace comm
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
index 994b52b8a..360aea25f 100644
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -1,73 +1,79 @@
// @flow
'use strict';
import { TurboModuleRegistry } from 'react-native';
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport.js';
import type {
ClientDBDraftInfo,
ClientDBDraftStoreOperation,
} from 'lib/types/draft-types.js';
import type {
ClientDBMessageInfo,
ClientDBMessageStoreOperation,
} from 'lib/types/message-types.js';
import type {
ClientDBThreadInfo,
ClientDBThreadStoreOperation,
} from 'lib/types/thread-types.js';
type ClientDBStore = {
+messages: $ReadOnlyArray,
+drafts: $ReadOnlyArray,
+threads: $ReadOnlyArray,
};
type ClientPublicKeys = {
- +curve25519: string,
- +ed25519: string,
+ +primaryIdentityPublicKeys: {
+ +ed25519: string,
+ +curve25519: string,
+ },
+ +notificationIdentityPublicKeys: {
+ +ed25519: string,
+ +curve25519: string,
+ },
};
export interface Spec extends TurboModule {
+getDraft: (key: string) => Promise;
+updateDraft: (key: string, text: string) => Promise;
+moveDraft: (oldKey: string, newKey: string) => Promise;
+getClientDBStore: () => Promise;
+removeAllDrafts: () => Promise;
+getAllMessagesSync: () => $ReadOnlyArray;
+processDraftStoreOperations: (
operations: $ReadOnlyArray,
) => Promise;
+processMessageStoreOperations: (
operations: $ReadOnlyArray,
) => Promise;
+processMessageStoreOperationsSync: (
operations: $ReadOnlyArray,
) => void;
+getAllThreadsSync: () => $ReadOnlyArray;
+processThreadStoreOperations: (
operations: $ReadOnlyArray,
) => Promise;
+processThreadStoreOperationsSync: (
operations: $ReadOnlyArray,
) => void;
+initializeCryptoAccount: () => Promise;
+getUserPublicKey: () => Promise;
+getUserOneTimeKeys: () => Promise;
+getCodeVersion: () => number;
+terminate: () => void;
+setNotifyToken: (token: string) => Promise;
+clearNotifyToken: () => Promise;
+setCurrentUserID: (userID: string) => Promise;
+getCurrentUserID: () => Promise;
+setDeviceID: (deviceType: string) => Promise;
+getDeviceID: () => Promise;
+clearSensitiveData: () => Promise;
+checkIfDatabaseNeedsDeletion: () => boolean;
+reportDBOperationsFailure: () => void;
}
export default (TurboModuleRegistry.getEnforcing(
'CommTurboModule',
): Spec);
diff --git a/native/selectors/account-selectors.js b/native/selectors/account-selectors.js
index b8db0aa22..542664ce8 100644
--- a/native/selectors/account-selectors.js
+++ b/native/selectors/account-selectors.js
@@ -1,55 +1,57 @@
// @flow
import { createSelector } from 'reselect';
import { logInExtraInfoSelector } from 'lib/selectors/account-selectors.js';
import type { LogInExtraInfo } from 'lib/types/account-types.js';
import type { UserPolicies } from 'lib/types/policy-types.js';
import { values } from 'lib/utils/objects.js';
import { commCoreModule } from '../native-modules.js';
import { calendarActiveSelector } from '../navigation/nav-selectors.js';
import type { AppState } from '../redux/state-types.js';
import type { ConnectivityInfo } from '../types/connectivity.js';
import type { NavPlusRedux } from '../types/selector-types.js';
const nativeLogInExtraInfoSelector: (
input: NavPlusRedux,
) => () => Promise = createSelector(
(input: NavPlusRedux) => logInExtraInfoSelector(input.redux),
(input: NavPlusRedux) => calendarActiveSelector(input.navContext),
(
logInExtraInfoFunc: (calendarActive: boolean) => LogInExtraInfo,
calendarActive: boolean,
) => {
const loginExtraFuncWithIdentityKey = async () => {
await commCoreModule.initializeCryptoAccount();
- const { ed25519 } = await commCoreModule.getUserPublicKey();
+ const {
+ primaryIdentityPublicKeys: { ed25519 },
+ } = await commCoreModule.getUserPublicKey();
return {
...logInExtraInfoFunc(calendarActive),
primaryIdentityPublicKey: ed25519,
};
};
return loginExtraFuncWithIdentityKey;
},
);
const noDataAfterPolicyAcknowledgmentSelector: (state: AppState) => boolean =
createSelector(
(state: AppState) => state.connectivity,
(state: AppState) => state.messageStore.currentAsOf,
(state: AppState) => state.userPolicies,
(
connectivity: ConnectivityInfo,
currentAsOf: number,
userPolicies: UserPolicies,
) =>
connectivity.connected &&
currentAsOf === 0 &&
values(userPolicies).every(policy => policy.isAcknowledged),
);
export {
nativeLogInExtraInfoSelector,
noDataAfterPolicyAcknowledgmentSelector,
};