Skip to content

Commit 53c57ef

Browse files
authored
Merge pull request oxen-io#2525 from Bilb/fix-displayname-use-bytes
fix: displayName allowed length based on bytes rather than char
2 parents d936bd5 + 8d946da commit 53c57ef

File tree

13 files changed

+92
-58
lines changed

13 files changed

+92
-58
lines changed

_locales/en/messages.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@
363363
"notificationPreview": "Preview",
364364
"recoveryPhraseEmpty": "Enter your recovery phrase",
365365
"displayNameEmpty": "Please enter a display name",
366+
"displayNameTooLong": "Display name is too long",
366367
"members": "$count$ members",
367368
"activeMembers": "$count$ active members",
368369
"join": "Join",

ts/components/SessionWrapperModal.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useRef } from 'react';
1+
import React, { useRef } from 'react';
22
import classNames from 'classnames';
33

44
import { SessionIconButton } from './icon/';
@@ -63,17 +63,11 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
6363
}
6464
};
6565

66-
useEffect(() => {
67-
document.addEventListener('mousedown', handleClick);
68-
69-
return () => {
70-
document.removeEventListener('mousedown', handleClick);
71-
};
72-
}, []);
73-
7466
return (
7567
<div
7668
className={classNames('loki-dialog modal', additionalClassName ? additionalClassName : null)}
69+
onClick={handleClick}
70+
role="dialog"
7771
>
7872
<div className="session-confirm-wrapper">
7973
<div ref={modalRef} className="session-modal">

ts/components/basic/SessionToast.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React from 'react';
22

33
import { Flex } from '../basic/Flex';
44
import styled from 'styled-components';
5-
import { noop } from 'lodash';
65
import { SessionIcon, SessionIconType } from '../icon';
6+
import { noop } from 'lodash';
77

88
export enum SessionToastType {
99
Info = 'info',
@@ -44,6 +44,8 @@ const IconDiv = styled.div`
4444
padding-inline-end: var(--margins-xs);
4545
`;
4646

47+
// tslint:disable: use-simple-attributes
48+
4749
export const SessionToast = (props: Props) => {
4850
const { title, description, type, icon } = props;
4951

@@ -71,14 +73,10 @@ export const SessionToast = (props: Props) => {
7173
}
7274
}
7375

76+
const onToastClick = props?.onToastClick || noop;
77+
7478
return (
75-
// tslint:disable-next-line: use-simple-attributes
76-
<Flex
77-
container={true}
78-
alignItems="center"
79-
onClick={props?.onToastClick || noop}
80-
data-testid="session-toast"
81-
>
79+
<Flex container={true} alignItems="center" onClick={onToastClick} data-testid="session-toast">
8280
<IconDiv>
8381
<SessionIcon iconType={toastIcon} iconSize={toastIconSize} />
8482
</IconDiv>

ts/components/dialog/EditProfileDialog.tsx

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { uploadOurAvatar } from '../../interactions/conversationInteractions';
1616
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
1717
import { SessionSpinner } from '../basic/SessionSpinner';
1818
import { SessionIconButton } from '../icon';
19-
import { MAX_USERNAME_LENGTH } from '../registration/RegistrationStages';
2019
import { SessionWrapperModal } from '../SessionWrapperModal';
2120
import { pickFileForAvatar } from '../../types/attachments/VisualAttachment';
2221
import { sanitizeSessionUsername } from '../../session/utils/String';
2322
import { setLastProfileUpdateTimestamp } from '../../util/storage';
2423
import { ConversationTypeEnum } from '../../models/conversationAttributes';
24+
import { MAX_USERNAME_BYTES } from '../../session/constants';
2525

2626
interface State {
2727
profileName: string;
@@ -214,7 +214,7 @@ export class EditProfileDialog extends React.Component<{}, State> {
214214
value={this.state.profileName}
215215
placeholder={placeholderText}
216216
onChange={this.onNameEdited}
217-
maxLength={MAX_USERNAME_LENGTH}
217+
maxLength={MAX_USERNAME_BYTES}
218218
tabIndex={0}
219219
required={true}
220220
aria-required={true}
@@ -240,10 +240,18 @@ export class EditProfileDialog extends React.Component<{}, State> {
240240
}
241241

242242
private onNameEdited(event: ChangeEvent<HTMLInputElement>) {
243-
const newName = sanitizeSessionUsername(event.target.value);
244-
this.setState({
245-
profileName: newName,
246-
});
243+
const displayName = event.target.value;
244+
try {
245+
const newName = sanitizeSessionUsername(displayName);
246+
this.setState({
247+
profileName: newName,
248+
});
249+
} catch (e) {
250+
this.setState({
251+
profileName: displayName,
252+
});
253+
ToastUtils.pushToastError('nameTooLong', window.i18n('displayNameTooLong'));
254+
}
247255
}
248256

249257
private onKeyUp(event: any) {
@@ -266,26 +274,37 @@ export class EditProfileDialog extends React.Component<{}, State> {
266274
*/
267275
private onClickOK() {
268276
const { newAvatarObjectUrl, profileName } = this.state;
269-
const newName = profileName ? profileName.trim() : '';
277+
try {
278+
const newName = profileName ? profileName.trim() : '';
279+
280+
if (newName.length === 0 || newName.length > MAX_USERNAME_BYTES) {
281+
return;
282+
}
283+
284+
// this throw if the length in bytes is too long
285+
const sanitizedName = sanitizeSessionUsername(newName);
286+
const trimName = sanitizedName.trim();
287+
288+
this.setState(
289+
{
290+
profileName: trimName,
291+
loading: true,
292+
},
293+
async () => {
294+
await commitProfileEdits(newName, newAvatarObjectUrl);
295+
this.setState({
296+
loading: false,
297+
298+
mode: 'default',
299+
updatedProfileName: this.state.profileName,
300+
});
301+
}
302+
);
303+
} catch (e) {
304+
ToastUtils.pushToastError('nameTooLong', window.i18n('displayNameTooLong'));
270305

271-
if (newName.length === 0 || newName.length > MAX_USERNAME_LENGTH) {
272306
return;
273307
}
274-
275-
this.setState(
276-
{
277-
loading: true,
278-
},
279-
async () => {
280-
await commitProfileEdits(newName, newAvatarObjectUrl);
281-
this.setState({
282-
loading: false,
283-
284-
mode: 'default',
285-
updatedProfileName: this.state.profileName,
286-
});
287-
}
288-
);
289308
}
290309

291310
private closeDialog() {

ts/components/leftpane/ActionsPanel.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import { getSwarmPollingInstance } from '../../session/apis/snode_api';
3939
import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodePool';
4040
import { Avatar, AvatarSize } from '../avatar/Avatar';
4141
import { SessionIconButton } from '../icon';
42-
import { SessionToastContainer } from '../SessionToastContainer';
4342
import { LeftPaneSectionContainer } from './LeftPaneSectionContainer';
4443
import { ipcRenderer } from 'electron';
4544
import { UserUtils } from '../../session/utils';
@@ -277,8 +276,6 @@ export const ActionsPanel = () => {
277276
<Section type={SectionType.Message} />
278277
<Section type={SectionType.Settings} />
279278

280-
<SessionToastContainer />
281-
282279
<Section type={SectionType.PathIndicator} />
283280
<Section type={SectionType.Moon} />
284281
</LeftPaneSectionContainer>

ts/components/leftpane/LeftPane.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { CallInFullScreenContainer } from '../calling/CallInFullScreenContainer'
1212
import { DraggableCallContainer } from '../calling/DraggableCallContainer';
1313
import { IncomingCallDialog } from '../calling/IncomingCallDialog';
1414
import { ModalContainer } from '../dialog/ModalContainer';
15+
import { SessionToastContainer } from '../SessionToastContainer';
1516
import { ActionsPanel } from './ActionsPanel';
1617
import { LeftPaneMessageSection } from './LeftPaneMessageSection';
1718
import { LeftPaneSettingSection } from './LeftPaneSettingSection';
@@ -71,6 +72,7 @@ export const LeftPane = () => {
7172
<div className="module-left-pane-session">
7273
<ModalContainer />
7374
<CallContainer />
75+
<SessionToastContainer />
7476
<ActionsPanel />
7577

7678
<StyledLeftPane className="module-left-pane">

ts/components/registration/RegistrationStages.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
import { fromHex } from '../../session/utils/String';
1818
import { setSignInByLinking, setSignWithRecoveryPhrase, Storage } from '../../util/storage';
1919

20-
export const MAX_USERNAME_LENGTH = 26;
2120
// tslint:disable: use-simple-attributes
2221

2322
export async function resetRegistration() {

ts/components/registration/RegistrationUserDetails.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import classNames from 'classnames';
22
import React from 'react';
3+
import { MAX_USERNAME_BYTES } from '../../session/constants';
34
import { SessionInput } from '../basic/SessionInput';
4-
import { MAX_USERNAME_LENGTH } from './RegistrationStages';
55

66
const DisplayNameInput = (props: {
77
stealAutoFocus?: boolean;
@@ -17,7 +17,7 @@ const DisplayNameInput = (props: {
1717
type="text"
1818
placeholder={window.i18n('enterDisplayName')}
1919
value={props.displayName}
20-
maxLength={MAX_USERNAME_LENGTH}
20+
maxLength={MAX_USERNAME_BYTES}
2121
onValueChanged={props.onDisplayNameChanged}
2222
onEnterPressed={props.handlePressEnter}
2323
inputDataTestId="display-name-input"

ts/components/registration/SignInTab.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useContext, useState } from 'react';
2+
import { ToastUtils } from '../../session/utils';
23
import { sanitizeSessionUsername } from '../../session/utils/String';
34
import { Flex } from '../basic/Flex';
45
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
@@ -95,6 +96,23 @@ const SignInButtons = (props: {
9596
);
9697
};
9798

99+
export function sanitizeDisplayNameOrToast(
100+
displayName: string,
101+
setDisplayName: (sanitized: string) => void,
102+
setDisplayNameError: (error: string | undefined) => void
103+
) {
104+
try {
105+
const sanitizedName = sanitizeSessionUsername(displayName);
106+
const trimName = sanitizedName.trim();
107+
setDisplayName(sanitizedName);
108+
setDisplayNameError(!trimName ? window.i18n('displayNameEmpty') : undefined);
109+
} catch (e) {
110+
setDisplayName(displayName);
111+
setDisplayNameError(window.i18n('displayNameTooLong'));
112+
ToastUtils.pushToastError('toolong', window.i18n('displayNameTooLong'));
113+
}
114+
}
115+
98116
export const SignInTab = () => {
99117
const { setRegistrationPhase, signInMode, setSignInMode } = useContext(RegistrationContext);
100118

@@ -148,10 +166,7 @@ export const SignInTab = () => {
148166
displayName={displayName}
149167
handlePressEnter={continueYourSession}
150168
onDisplayNameChanged={(name: string) => {
151-
const sanitizedName = sanitizeSessionUsername(name);
152-
const trimName = sanitizedName.trim();
153-
setDisplayName(sanitizedName);
154-
setDisplayNameError(!trimName ? window.i18n('displayNameEmpty') : undefined);
169+
sanitizeDisplayNameOrToast(name, setDisplayName, setDisplayNameError);
155170
}}
156171
onSeedChanged={(seed: string) => {
157172
setRecoveryPhrase(seed);

ts/components/registration/SignUpTab.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import React, { useContext, useEffect, useState } from 'react';
2-
import { sanitizeSessionUsername } from '../../session/utils/String';
32
import { Flex } from '../basic/Flex';
43
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
54
import { SessionIdEditable } from '../basic/SessionIdEditable';
65
import { SessionIconButton } from '../icon';
76
import { RegistrationContext, RegistrationPhase, signUp } from './RegistrationStages';
87
import { RegistrationUserDetails } from './RegistrationUserDetails';
9-
import { SignInMode } from './SignInTab';
8+
import { sanitizeDisplayNameOrToast, SignInMode } from './SignInTab';
109
import { TermsAndConditions } from './TermsAndConditions';
1110

1211
export enum SignUpMode {
@@ -144,10 +143,7 @@ export const SignUpTab = () => {
144143
displayName={displayName}
145144
handlePressEnter={signUpWithDetails}
146145
onDisplayNameChanged={(name: string) => {
147-
const sanitizedName = sanitizeSessionUsername(name);
148-
const trimName = sanitizedName.trim();
149-
setDisplayName(sanitizedName);
150-
setDisplayNameError(!trimName ? window.i18n('displayNameEmpty') : undefined);
146+
sanitizeDisplayNameOrToast(name, setDisplayName, setDisplayNameError);
151147
}}
152148
stealAutoFocus={true}
153149
/>

0 commit comments

Comments
 (0)