diff --git a/native/account/registration/username-selection.react.js b/native/account/registration/username-selection.react.js --- a/native/account/registration/username-selection.react.js +++ b/native/account/registration/username-selection.react.js @@ -12,6 +12,7 @@ import { validUsernameRegex } from 'lib/shared/account-utils.js'; import { useLegacyAshoatKeyserverCall } from 'lib/utils/action-utils.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; +import { usingCommServicesAccessToken } from 'lib/utils/services-utils.js'; import { isValidEthereumAddress } from 'lib/utils/siwe-utils.js'; import RegistrationButtonContainer from './registration-button-container.react.js'; @@ -22,6 +23,7 @@ import type { RegistrationNavigationProp } from './registration-navigator.react.js'; import RegistrationTextInput from './registration-text-input.react.js'; import type { CoolOrNerdMode } from './registration-types.js'; +import { commRustModule } from '../../native-modules.js'; import { type NavigationRoute, PasswordSelectionRouteName, @@ -88,11 +90,21 @@ return; } - const searchPromise = exactSearchUserCall(username); - void dispatchActionPromise(exactSearchUserActionTypes, searchPromise); - const { userInfo } = await searchPromise; + let userAlreadyExists; + if (usingCommServicesAccessToken) { + const findUserIDResponseString = + await commRustModule.findUserIDForUsername(username); + const findUserIDResponse = JSON.parse(findUserIDResponseString); + userAlreadyExists = + !!findUserIDResponse.userID || findUserIDResponse.isReserved; + } else { + const searchPromise = exactSearchUserCall(username); + void dispatchActionPromise(exactSearchUserActionTypes, searchPromise); + const { userInfo } = await searchPromise; + userAlreadyExists = !!userInfo; + } - if (userInfo) { + if (userAlreadyExists) { setUsernameError('username_taken'); return; } diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.h b/native/cpp/CommonCpp/NativeModules/CommRustModule.h --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.h @@ -125,6 +125,8 @@ virtual jsi::Value findUserIDForWalletAddress( jsi::Runtime &rt, jsi::String walletAddress) override; + virtual jsi::Value + findUserIDForUsername(jsi::Runtime &rt, jsi::String username) override; public: CommRustModule(std::shared_ptr jsInvoker); diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp @@ -600,4 +600,24 @@ }); } +jsi::Value +CommRustModule::findUserIDForUsername(jsi::Runtime &rt, jsi::String username) { + auto usernameRust = jsiStringToRustString(username, rt); + return createPromiseAsJSIValue( + rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { + std::string error; + try { + auto currentID = RustPromiseManager::instance.addPromise( + {promise, this->jsInvoker_, innerRt}); + identityFindUserIDForUsername(usernameRust, currentID); + } catch (const std::exception &e) { + error = e.what(); + }; + if (!error.empty()) { + this->jsInvoker_->invokeAsync( + [error, promise]() { promise->reject(error); }); + } + }); +} + } // namespace comm diff --git a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp --- a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp @@ -60,6 +60,9 @@ static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_findUserIDForWalletAddress(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->findUserIDForWalletAddress(rt, args[0].asString(rt)); } +static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_findUserIDForUsername(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->findUserIDForUsername(rt, args[0].asString(rt)); +} CommRustModuleSchemaCxxSpecJSI::CommRustModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule("CommRustTurboModule", jsInvoker) { @@ -79,6 +82,7 @@ methodMap_["updateDeviceList"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_updateDeviceList}; methodMap_["uploadSecondaryDeviceKeysAndLogIn"] = MethodMetadata {10, __hostFunction_CommRustModuleSchemaCxxSpecJSI_uploadSecondaryDeviceKeysAndLogIn}; methodMap_["findUserIDForWalletAddress"] = MethodMetadata {1, __hostFunction_CommRustModuleSchemaCxxSpecJSI_findUserIDForWalletAddress}; + methodMap_["findUserIDForUsername"] = MethodMetadata {1, __hostFunction_CommRustModuleSchemaCxxSpecJSI_findUserIDForUsername}; } diff --git a/native/cpp/CommonCpp/_generated/rustJSI.h b/native/cpp/CommonCpp/_generated/rustJSI.h --- a/native/cpp/CommonCpp/_generated/rustJSI.h +++ b/native/cpp/CommonCpp/_generated/rustJSI.h @@ -36,6 +36,7 @@ virtual jsi::Value updateDeviceList(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String updatePayload) = 0; virtual jsi::Value uploadSecondaryDeviceKeysAndLogIn(jsi::Runtime &rt, jsi::String userID, jsi::String challengeResponse, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) = 0; virtual jsi::Value findUserIDForWalletAddress(jsi::Runtime &rt, jsi::String walletAddress) = 0; + virtual jsi::Value findUserIDForUsername(jsi::Runtime &rt, jsi::String username) = 0; }; @@ -185,6 +186,14 @@ return bridging::callFromJs( rt, &T::findUserIDForWalletAddress, jsInvoker_, instance_, std::move(walletAddress)); } + jsi::Value findUserIDForUsername(jsi::Runtime &rt, jsi::String username) override { + static_assert( + bridging::getParameterCount(&T::findUserIDForUsername) == 2, + "Expected findUserIDForUsername(...) to have 2 parameters"); + + return bridging::callFromJs( + rt, &T::findUserIDForUsername, jsInvoker_, instance_, std::move(username)); + } private: T *instance_; diff --git a/native/native_rust_library/src/exact_user_search.rs b/native/native_rust_library/src/exact_user_search.rs --- a/native/native_rust_library/src/exact_user_search.rs +++ b/native/native_rust_library/src/exact_user_search.rs @@ -2,6 +2,7 @@ handle_string_result_as_callback, Error, CODE_VERSION, DEVICE_TYPE, IDENTITY_SOCKET_ADDR, RUNTIME, }; +use find_user_id_request::Identifier as RequestIdentifier; use grpc_clients::identity::{ get_unauthenticated_client, protos::unauth::{find_user_id_request, FindUserIdRequest}, @@ -14,6 +15,7 @@ struct FindUserIDResponse { #[serde(rename = "userID")] user_id: Option, + #[serde(rename = "isReserved")] is_reserved: bool, } @@ -23,15 +25,27 @@ promise_id: u32, ) { RUNTIME.spawn(async move { - let result = find_user_id_helper(wallet_address).await; + let result = + find_user_id_helper(RequestIdentifier::WalletAddress(wallet_address)) + .await; handle_string_result_as_callback(result, promise_id); }); } -async fn find_user_id_helper(wallet_address: String) -> Result { - use find_user_id_request::Identifier as RequestIdentifier; +#[instrument] +pub fn find_user_id_for_username(username: String, promise_id: u32) { + RUNTIME.spawn(async move { + let result = + find_user_id_helper(RequestIdentifier::Username(username)).await; + handle_string_result_as_callback(result, promise_id); + }); +} + +async fn find_user_id_helper( + identifier: RequestIdentifier, +) -> Result { let find_user_id_request = FindUserIdRequest { - identifier: Some(RequestIdentifier::WalletAddress(wallet_address)), + identifier: Some(identifier), }; let mut identity_client = get_unauthenticated_client( diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -1,7 +1,9 @@ use backup::ffi::*; use comm_opaque2::client::{Login, Registration}; use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error; -use exact_user_search::find_user_id_for_wallet_address; +use exact_user_search::{ + find_user_id_for_username, find_user_id_for_wallet_address, +}; use ffi::{bool_callback, string_callback, void_callback}; use future_manager::ffi::*; use grpc_clients::identity::protos::auth::{ @@ -230,6 +232,9 @@ #[cxx_name = "identityFindUserIDForWalletAddress"] fn find_user_id_for_wallet_address(wallet_address: String, promise_id: u32); + #[cxx_name = "identityFindUserIDForUsername"] + fn find_user_id_for_username(username: String, promise_id: u32); + // Argon2 #[cxx_name = "compute_backup_key"] fn compute_backup_key_str( diff --git a/native/schema/CommRustModuleSchema.js b/native/schema/CommRustModuleSchema.js --- a/native/schema/CommRustModuleSchema.js +++ b/native/schema/CommRustModuleSchema.js @@ -118,6 +118,7 @@ notifOneTimeKeys: $ReadOnlyArray, ) => Promise; +findUserIDForWalletAddress: (walletAddress: string) => Promise; + +findUserIDForUsername: (username: string) => Promise; } export default (TurboModuleRegistry.getEnforcing(