feat: call setName/setNameTruncated depending on usecase for nts

pull/3206/head
Audric Ackermann 7 months ago
parent 44336ed394
commit 25cf11f86a

@ -10,7 +10,6 @@ import { YourSessionIDPill, YourSessionIDSelectable } from '../../basic/YourSess
import { useHotkey } from '../../../hooks/useHotkey'; import { useHotkey } from '../../../hooks/useHotkey';
import { useOurAvatarPath, useOurConversationUsername } from '../../../hooks/useParamSelector'; import { useOurAvatarPath, useOurConversationUsername } from '../../../hooks/useParamSelector';
import { ProfileManager } from '../../../session/profile_manager/ProfileManager'; import { ProfileManager } from '../../../session/profile_manager/ProfileManager';
import LIBSESSION_CONSTANTS from '../../../session/utils/libsession/libsession_constants';
import { editProfileModal, updateEditProfilePictureModal } from '../../../state/ducks/modalDialog'; import { editProfileModal, updateEditProfilePictureModal } from '../../../state/ducks/modalDialog';
import { SessionWrapperModal } from '../../SessionWrapperModal'; import { SessionWrapperModal } from '../../SessionWrapperModal';
import { Flex } from '../../basic/Flex'; import { Flex } from '../../basic/Flex';
@ -214,6 +213,8 @@ export const EditProfileDialog = () => {
try { try {
setLoading(true); setLoading(true);
// Note: this will not throw, but just truncate the display name if it is too long.
// I guess it is expected as there is no UI to show anything else than a generic error?
const validName = await ProfileManager.updateOurProfileDisplayName(profileName); const validName = await ProfileManager.updateOurProfileDisplayName(profileName);
setUpdateProfileName(validName); setUpdateProfileName(validName);
setProfileName(validName); setProfileName(validName);
@ -330,7 +331,6 @@ export const EditProfileDialog = () => {
tabIndex={0} tabIndex={0}
required={true} required={true}
error={profileNameError} error={profileNameError}
maxLength={LIBSESSION_CONSTANTS.CONTACT_MAX_NAME_LENGTH}
textSize={'xl'} textSize={'xl'}
centerText={true} centerText={true}
inputRef={inputRef} inputRef={inputRef}

@ -6,7 +6,6 @@ import { mnDecode } from '../../../session/crypto/mnemonic';
import { ProfileManager } from '../../../session/profile_manager/ProfileManager'; import { ProfileManager } from '../../../session/profile_manager/ProfileManager';
import { StringUtils } from '../../../session/utils'; import { StringUtils } from '../../../session/utils';
import { fromHex } from '../../../session/utils/String'; import { fromHex } from '../../../session/utils/String';
import LIBSESSION_CONSTANTS from '../../../session/utils/libsession/libsession_constants';
import { trigger } from '../../../shims/events'; import { trigger } from '../../../shims/events';
import { import {
AccountCreation, AccountCreation,
@ -93,7 +92,8 @@ export const CreateAccount = () => {
} }
try { try {
const validName = await ProfileManager.updateOurProfileDisplayName(displayName, true); // this throws if the display name is too long
const validName = await ProfileManager.updateOurProfileDisplayNameOnboarding(displayName);
await signUp({ await signUp({
displayName: validName, displayName: validName,
@ -102,12 +102,13 @@ export const CreateAccount = () => {
dispatch(setAccountCreationStep(AccountCreation.Done)); dispatch(setAccountCreationStep(AccountCreation.Done));
} catch (err) { } catch (err) {
const errorString = err.message || String(err);
window.log.error( window.log.error(
`[onboarding] create account: signUpWithDetails failed! Error: ${errorString}` `[onboarding] create account: signUpWithDetails failed! Error: ${err.message || String(err)}`
); );
dispatch(setAccountCreationStep(AccountCreation.DisplayName)); dispatch(setAccountCreationStep(AccountCreation.DisplayName));
dispatch(setDisplayNameError(errorString)); // Note: we have to assume here that libsession threw an error because the name was too long.
// The error reporterd by libsession is not localized
dispatch(setDisplayNameError(window.i18n('displayNameErrorDescriptionShorter')));
} }
}; };
@ -146,7 +147,6 @@ export const CreateAccount = () => {
}} }}
onEnterPressed={signUpWithDetails} onEnterPressed={signUpWithDetails}
error={displayNameError} error={displayNameError}
maxLength={LIBSESSION_CONSTANTS.CONTACT_MAX_NAME_LENGTH}
inputDataTestId="display-name-input" inputDataTestId="display-name-input"
/> />
<SpacerLG /> <SpacerLG />

@ -7,7 +7,6 @@ import { ProfileManager } from '../../../session/profile_manager/ProfileManager'
import { PromiseUtils } from '../../../session/utils'; import { PromiseUtils } from '../../../session/utils';
import { TaskTimedOutError } from '../../../session/utils/Promise'; import { TaskTimedOutError } from '../../../session/utils/Promise';
import { NotFoundError } from '../../../session/utils/errors'; import { NotFoundError } from '../../../session/utils/errors';
import LIBSESSION_CONSTANTS from '../../../session/utils/libsession/libsession_constants';
import { trigger } from '../../../shims/events'; import { trigger } from '../../../shims/events';
import { import {
AccountRestoration, AccountRestoration,
@ -181,7 +180,8 @@ export const RestoreAccount = () => {
} }
try { try {
const validName = await ProfileManager.updateOurProfileDisplayName(displayName, true); // this will throw if the display name is too long
const validName = await ProfileManager.updateOurProfileDisplayNameOnboarding(displayName);
const trimmedPassword = recoveryPassword.trim(); const trimmedPassword = recoveryPassword.trim();
setRecoveryPassword(trimmedPassword); setRecoveryPassword(trimmedPassword);
@ -192,12 +192,14 @@ export const RestoreAccount = () => {
dispatch, dispatch,
}); });
} catch (err) { } catch (err) {
const errorString = err.message || String(err);
window.log.error( window.log.error(
`[onboarding] restore account: Failed with new display name! Error: ${errorString}` `[onboarding] restore account: Failed with new display name! Error: ${err.message || String(err)}`
); );
dispatch(setAccountRestorationStep(AccountRestoration.DisplayName)); dispatch(setAccountRestorationStep(AccountRestoration.DisplayName));
dispatch(setDisplayNameError(errorString));
// Note: we have to assume here that libsession threw an error because the name was too long.
// The error reporterd by libsession is not localized
dispatch(setDisplayNameError(window.i18n('displayNameErrorDescriptionShorter')));
} }
}; };
@ -314,7 +316,6 @@ export const RestoreAccount = () => {
}} }}
onEnterPressed={recoverAndEnterDisplayName} onEnterPressed={recoverAndEnterDisplayName}
error={displayNameError} error={displayNameError}
maxLength={LIBSESSION_CONSTANTS.CONTACT_MAX_NAME_LENGTH}
inputDataTestId="display-name-input" inputDataTestId="display-name-input"
/> />
<SpacerLG /> <SpacerLG />

@ -7,24 +7,15 @@ export function sanitizeDisplayNameOrToast(
onDisplayNameError: (error: string | undefined) => any, onDisplayNameError: (error: string | undefined) => any,
dispatch?: Dispatch dispatch?: Dispatch
) { ) {
try { const sanitizedName = sanitizeSessionUsername(displayName);
const sanitizedName = sanitizeSessionUsername(displayName); const errorString = !sanitizedName ? window.i18n('displayNameErrorDescription') : undefined;
const errorString = !sanitizedName ? window.i18n('displayNameErrorDescription') : undefined; if (dispatch) {
if (dispatch) { dispatch(onDisplayNameError(errorString));
dispatch(onDisplayNameError(errorString)); } else {
} else { onDisplayNameError(errorString); // this is is either calling dispatch in the caller or just `setDisplayNameError`
onDisplayNameError(errorString); // this is is either calling dispatch in the caller or just `setDisplayNameError`
}
return sanitizedName;
} catch (e) {
if (dispatch) {
dispatch(onDisplayNameError(window.i18n('displayNameErrorDescriptionShorter')));
} else {
onDisplayNameError(window.i18n('displayNameErrorDescriptionShorter'));
}
return displayName;
} }
return sanitizedName;
} }
/** /**

@ -1376,11 +1376,13 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
const ourDbProfileKey = fromHexToArray(ourConversation.profileKey || ''); const ourDbProfileKey = fromHexToArray(ourConversation.profileKey || '');
const ourConvoPriority = ourConversation.priority; const ourConvoPriority = ourConversation.priority;
// we don't want to throw if somehow our display name in the DB is too long here, so we use the truncated version.
userProfileWrapper.setNameTruncated(ourDbName);
userProfileWrapper.setPriority(ourConvoPriority);
if (ourDbProfileUrl && !isEmpty(ourDbProfileKey)) { if (ourDbProfileUrl && !isEmpty(ourDbProfileKey)) {
userProfileWrapper.setUserInfo(ourDbName, ourConvoPriority, { userProfileWrapper.setProfilePic({ key: ourDbProfileKey, url: ourDbProfileUrl });
url: ourDbProfileUrl, } else {
key: ourDbProfileKey, userProfileWrapper.setProfilePic({ key: null, url: null });
});
} }
MIGRATION_HELPERS.V31.insertContactIntoContactWrapper( MIGRATION_HELPERS.V31.insertContactIntoContactWrapper(

@ -208,8 +208,10 @@ async function updateLibsessionLatestProcessedUserTimestamp(
* Instead you will need to updateOurProfileLegacyOrViaLibSession() to support them * Instead you will need to updateOurProfileLegacyOrViaLibSession() to support them
*/ */
async function handleUserProfileUpdate(result: IncomingConfResult): Promise<IncomingConfResult> { async function handleUserProfileUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
const updateUserInfo = await UserConfigWrapperActions.getUserInfo(); const profilePic = await UserConfigWrapperActions.getProfilePic();
if (!updateUserInfo) { const displayName = await UserConfigWrapperActions.getName();
const priority = await UserConfigWrapperActions.getPriority();
if (!profilePic || isEmpty(profilePic)) {
return result; return result;
} }
@ -219,15 +221,15 @@ async function handleUserProfileUpdate(result: IncomingConfResult): Promise<Inco
await window.setSettingValue(SettingsKey.hasBlindedMsgRequestsEnabled, newBlindedMsgRequest); // this does the dispatch to redux await window.setSettingValue(SettingsKey.hasBlindedMsgRequestsEnabled, newBlindedMsgRequest); // this does the dispatch to redux
} }
const picUpdate = !isEmpty(updateUserInfo.key) && !isEmpty(updateUserInfo.url); const picUpdate = !isEmpty(profilePic.key) && !isEmpty(profilePic.url);
// NOTE: if you do any changes to the user's settings which are synced, it should be done above the `updateOurProfileLegacyOrViaLibSession` call // NOTE: if you do any changes to the user's settings which are synced, it should be done above the `updateOurProfileLegacyOrViaLibSession` call
await updateOurProfileLegacyOrViaLibSession({ await updateOurProfileLegacyOrViaLibSession({
sentAt: result.latestEnvelopeTimestamp, sentAt: result.latestEnvelopeTimestamp,
displayName: updateUserInfo.name, displayName: displayName || '',
profileUrl: picUpdate ? updateUserInfo.url : null, profileUrl: picUpdate ? profilePic.url : null,
profileKey: picUpdate ? updateUserInfo.key : null, profileKey: picUpdate ? profilePic.key : null,
priority: updateUserInfo.priority, priority,
}); });
// NOTE: If we want to update the conversation in memory with changes from the updated user profile we need to wait untl the profile has been updated to prevent multiple merge conflicts // NOTE: If we want to update the conversation in memory with changes from the updated user profile we need to wait untl the profile has been updated to prevent multiple merge conflicts

@ -404,11 +404,11 @@ export class SwarmPolling {
await UserConfigWrapperActions.init(privateKeyEd25519, null); await UserConfigWrapperActions.init(privateKeyEd25519, null);
await UserConfigWrapperActions.merge(incomingConfigMessages); await UserConfigWrapperActions.merge(incomingConfigMessages);
const userInfo = await UserConfigWrapperActions.getUserInfo(); const name = await UserConfigWrapperActions.getName();
if (!userInfo) { if (!name) {
throw new Error('UserInfo not found'); throw new Error('UserInfo not found or name is empty');
} }
return userInfo.name; return name;
} catch (e) { } catch (e) {
window.log.warn('LibSessionUtil.initializeLibSessionUtilWrappers failed with', e.message); window.log.warn('LibSessionUtil.initializeLibSessionUtilWrappers failed with', e.message);
} finally { } finally {

@ -1,4 +1,4 @@
import { isEmpty } from 'lodash'; import { isEmpty, isNil } from 'lodash';
import { setLastProfileUpdateTimestamp } from '../../util/storage'; import { setLastProfileUpdateTimestamp } from '../../util/storage';
import { UserConfigWrapperActions } from '../../webworker/workers/browser/libsession_worker_interface'; import { UserConfigWrapperActions } from '../../webworker/workers/browser/libsession_worker_interface';
import { getConversationController } from '../conversations'; import { getConversationController } from '../conversations';
@ -95,26 +95,38 @@ async function updateProfileOfContact(
} }
} }
export async function updateOurProfileDisplayName(newName: string, onboarding?: true) { /**
* This will throw if the display name given is too long.
* When registering a user/linking a device, we want to enforce a limit on the displayName length.
* That limit is enforced by libsession when calling `setName` on the `UserConfigWrapper`.
* `updateOurProfileDisplayNameOnboarding` is used to create a temporary `UserConfigWrapper`, call `setName` on it and release the memory used by the wrapper.
* @returns the set displayName set if no error where thrown.
*/
async function updateOurProfileDisplayNameOnboarding(newName: string) {
const cleanName = sanitizeSessionUsername(newName).trim(); const cleanName = sanitizeSessionUsername(newName).trim();
if (onboarding) { try {
try { // create a temp user config wrapper to test the display name with libsession
// create a temp user config wrapper to test the display name with libsession const privKey = new Uint8Array(64);
const privKey = new Uint8Array(64); crypto.getRandomValues(privKey);
crypto.getRandomValues(privKey); await UserConfigWrapperActions.init(privKey, null);
await UserConfigWrapperActions.init(privKey, null); // this throws if the name is too long
const userInfoName = await UserConfigWrapperActions.setUserInfo( await UserConfigWrapperActions.setName(cleanName);
cleanName, const appliedName = await UserConfigWrapperActions.getName();
CONVERSATION_PRIORITIES.default,
null if (isNil(appliedName)) {
throw new Error(
'updateOurProfileDisplayNameOnboarding failed to retrieve name after setting it'
); );
return userInfoName;
} finally {
await UserConfigWrapperActions.free();
} }
return appliedName;
} finally {
await UserConfigWrapperActions.free();
} }
}
async function updateOurProfileDisplayName(newName: string) {
const ourNumber = UserUtils.getOurPubKeyStrFromCache(); const ourNumber = UserUtils.getOurPubKeyStrFromCache();
const conversation = await getConversationController().getOrCreateAndWait( const conversation = await getConversationController().getOrCreateAndWait(
ourNumber, ourNumber,
@ -127,29 +139,32 @@ export async function updateOurProfileDisplayName(newName: string, onboarding?:
: null; : null;
const dbPriority = conversation.get('priority') || CONVERSATION_PRIORITIES.default; const dbPriority = conversation.get('priority') || CONVERSATION_PRIORITIES.default;
await UserConfigWrapperActions.setUserInfo( // we don't want to throw if somehow our display name in the DB is too long here, so we use the truncated version.
cleanName, await UserConfigWrapperActions.setNameTruncated(sanitizeSessionUsername(newName).trim());
dbPriority, const truncatedName = await UserConfigWrapperActions.getName();
dbProfileUrl && dbProfileKey if (isNil(truncatedName)) {
? { throw new Error('updateOurProfileDisplayName: failed to get truncated displayName back');
url: dbProfileUrl, }
key: dbProfileKey, await UserConfigWrapperActions.setPriority(dbPriority);
} if (dbProfileUrl && !isEmpty(dbProfileKey)) {
: null await UserConfigWrapperActions.setProfilePic({ key: dbProfileKey, url: dbProfileUrl });
); } else {
await UserConfigWrapperActions.setProfilePic({ key: null, url: null });
}
conversation.setSessionDisplayNameNoCommit(newName); conversation.setSessionDisplayNameNoCommit(truncatedName);
// might be good to not trigger a sync if the name did not change // might be good to not trigger a sync if the name did not change
await conversation.commit(); await conversation.commit();
await setLastProfileUpdateTimestamp(Date.now()); await setLastProfileUpdateTimestamp(Date.now());
await SyncUtils.forceSyncConfigurationNowIfNeeded(true); await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
return cleanName; return truncatedName;
} }
export const ProfileManager = { export const ProfileManager = {
updateOurProfileSync, updateOurProfileSync,
updateProfileOfContact, updateProfileOfContact,
updateOurProfileDisplayName, updateOurProfileDisplayName,
updateOurProfileDisplayNameOnboarding,
}; };

@ -38,14 +38,15 @@ async function insertUserProfileIntoWrapper(convoId: string) {
})}` })}`
); );
// we don't want to throw if somehow our display name in the DB is too long here, so we use the truncated version.
await UserConfigWrapperActions.setNameTruncated(dbName);
await UserConfigWrapperActions.setPriority(priority);
if (dbProfileUrl && !isEmpty(dbProfileKey)) { if (dbProfileUrl && !isEmpty(dbProfileKey)) {
await UserConfigWrapperActions.setUserInfo(dbName, priority, { await UserConfigWrapperActions.setProfilePic({ key: dbProfileKey, url: dbProfileUrl });
url: dbProfileUrl,
key: dbProfileKey,
});
} else { } else {
await UserConfigWrapperActions.setUserInfo(dbName, priority, null); await UserConfigWrapperActions.setProfilePic({ key: null, url: null });
} }
await UserConfigWrapperActions.setEnableBlindedMsgRequest(areBlindedMsgRequestEnabled); await UserConfigWrapperActions.setEnableBlindedMsgRequest(areBlindedMsgRequestEnabled);
await UserConfigWrapperActions.setNoteToSelfExpiry(expirySeconds); await UserConfigWrapperActions.setNoteToSelfExpiry(expirySeconds);

@ -7,6 +7,7 @@ import {
ContactsWrapperActionsCalls, ContactsWrapperActionsCalls,
ConvoInfoVolatileWrapperActionsCalls, ConvoInfoVolatileWrapperActionsCalls,
LegacyGroupInfo, LegacyGroupInfo,
ProfilePicture,
UserConfigWrapperActionsCalls, UserConfigWrapperActionsCalls,
UserGroupsWrapperActionsCalls, UserGroupsWrapperActionsCalls,
} from 'libsession_util_nodejs'; } from 'libsession_util_nodejs';
@ -106,17 +107,33 @@ export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
currentHashes: async () => GenericWrapperActions.currentHashes('UserConfig'), currentHashes: async () => GenericWrapperActions.currentHashes('UserConfig'),
/** UserConfig wrapper specific actions */ /** UserConfig wrapper specific actions */
getUserInfo: async () => getPriority: async () =>
callLibSessionWorker(['UserConfig', 'getUserInfo']) as Promise< callLibSessionWorker(['UserConfig', 'getPriority']) as Promise<
ReturnType<UserConfigWrapperActionsCalls['getUserInfo']> ReturnType<UserConfigWrapperActionsCalls['getPriority']>
>, >,
setUserInfo: async ( getName: async () =>
name: string, callLibSessionWorker(['UserConfig', 'getName']) as Promise<
priority: number, ReturnType<UserConfigWrapperActionsCalls['getName']>
profilePic: { url: string; key: Uint8Array } | null >,
) => getProfilePic: async () =>
callLibSessionWorker(['UserConfig', 'setUserInfo', name, priority, profilePic]) as Promise< callLibSessionWorker(['UserConfig', 'getProfilePic']) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setUserInfo']> ReturnType<UserConfigWrapperActionsCalls['getProfilePic']>
>,
setPriority: async (priority: number) =>
callLibSessionWorker(['UserConfig', 'setPriority', priority]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setPriority']>
>,
setName: async (name: string) =>
callLibSessionWorker(['UserConfig', 'setName', name]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setName']>
>,
setNameTruncated: async (name: string) =>
callLibSessionWorker(['UserConfig', 'setNameTruncated', name]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setNameTruncated']>
>,
setProfilePic: async (profilePic: ProfilePicture) =>
callLibSessionWorker(['UserConfig', 'setProfilePic', profilePic]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setProfilePic']>
>, >,
getEnableBlindedMsgRequest: async () => getEnableBlindedMsgRequest: async () =>
callLibSessionWorker(['UserConfig', 'getEnableBlindedMsgRequest']) as Promise< callLibSessionWorker(['UserConfig', 'getEnableBlindedMsgRequest']) as Promise<

Loading…
Cancel
Save