diff --git a/ts/components/dialog/EditProfileDialog.tsx b/ts/components/dialog/EditProfileDialog.tsx
index 34d4947a1..0f664633f 100644
--- a/ts/components/dialog/EditProfileDialog.tsx
+++ b/ts/components/dialog/EditProfileDialog.tsx
@@ -1,20 +1,15 @@
-import React, { ChangeEvent, MouseEvent } from 'react';
+import React, { ChangeEvent, MouseEvent, ReactElement, useState } from 'react';
import { QRCode } from 'react-qr-svg';
import { Avatar, AvatarSize } from '../avatar/Avatar';
import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils';
import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill';
-
-import { ConversationModel } from '../../models/conversation';
-
-import autoBind from 'auto-bind';
import styled from 'styled-components';
import { uploadOurAvatar } from '../../interactions/conversationInteractions';
import { ConversationTypeEnum } from '../../models/conversationAttributes';
import { MAX_USERNAME_BYTES } from '../../session/constants';
import { getConversationController } from '../../session/conversations';
-import { sanitizeSessionUsername } from '../../session/utils/String';
import { editProfileModal } from '../../state/ducks/modalDialog';
import { pickFileForAvatar } from '../../types/attachments/VisualAttachment';
import { saveQRCode } from '../../util/saveQRCode';
@@ -23,6 +18,9 @@ import { SessionWrapperModal } from '../SessionWrapperModal';
import { SessionButton, SessionButtonType } from '../basic/SessionButton';
import { SessionSpinner } from '../basic/SessionSpinner';
import { SessionIconButton } from '../icon';
+import { sanitizeSessionUsername } from '../../session/utils/String';
+import { useOurConversationUsername } from '../../hooks/useParamSelector';
+import { useOurAvatarPath } from '../../hooks/useParamSelector';
const handleSaveQRCode = (event: MouseEvent) => {
event.preventDefault();
@@ -51,311 +49,279 @@ const QRView = ({ sessionID }: { sessionID: string }) => {
);
};
-interface State {
- profileName: string;
- updatedProfileName: string;
- oldAvatarPath: string;
- newAvatarObjectUrl: string | null;
- mode: 'default' | 'edit' | 'qr';
- loading: boolean;
-}
-
-export class EditProfileDialog extends React.Component<{}, State> {
- private readonly convo: ConversationModel;
-
- constructor(props: any) {
- super(props);
-
- autoBind(this);
-
- this.convo = getConversationController().get(UserUtils.getOurPubKeyStrFromCache());
-
- this.state = {
- profileName: this.convo.getRealSessionUsername() || '',
- updatedProfileName: this.convo.getRealSessionUsername() || '',
- oldAvatarPath: this.convo.getAvatarPath() || '',
- newAvatarObjectUrl: null,
- mode: 'default',
- loading: false,
- };
- }
+const commitProfileEdits = async (newName: string, scaledAvatarUrl: string | null) => {
+ const ourNumber = UserUtils.getOurPubKeyStrFromCache();
+ const conversation = await getConversationController().getOrCreateAndWait(
+ ourNumber,
+ ConversationTypeEnum.PRIVATE
+ );
- public componentDidMount() {
- window.addEventListener('keyup', this.onKeyUp);
+ if (scaledAvatarUrl?.length) {
+ try {
+ const blobContent = await (await fetch(scaledAvatarUrl)).blob();
+ if (!blobContent || !blobContent.size) {
+ throw new Error('Failed to fetch blob content from scaled avatar');
+ }
+ await uploadOurAvatar(await blobContent.arrayBuffer());
+ } catch (error) {
+ if (error.message && error.message.length) {
+ ToastUtils.pushToastError('edit-profile', error.message);
+ }
+ window.log.error(
+ 'showEditProfileDialog Error ensuring that image is properly sized:',
+ error && error.stack ? error.stack : error
+ );
+ }
+ return;
}
+ // do not update the avatar if it did not change
+ conversation.setSessionDisplayNameNoCommit(newName);
- public componentWillUnmount() {
- window.removeEventListener('keyup', this.onKeyUp);
- }
+ // might be good to not trigger a sync if the name did not change
+ await conversation.commit();
+ await setLastProfileUpdateTimestamp(Date.now());
+ await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
+};
- public render() {
- const i18n = window.i18n;
+type ProfileAvatarProps = {
+ newAvatarObjectUrl: string | null;
+ oldAvatarPath: string | null;
+ profileName: string | undefined;
+ ourId: string;
+};
- const viewDefault = this.state.mode === 'default';
- const viewEdit = this.state.mode === 'edit';
- const viewQR = this.state.mode === 'qr';
+const ProfileAvatar = (props: ProfileAvatarProps): ReactElement => {
+ const { newAvatarObjectUrl, oldAvatarPath, profileName, ourId } = props;
+ return (
+
+ );
+};
- const sessionID = UserUtils.getOurPubKeyStrFromCache();
+type ProfileHeaderProps = ProfileAvatarProps & {
+ fireInputEvent: () => void;
+ setMode: (mode: ProfileDialogModes) => void;
+};
- const backButton =
- viewEdit || viewQR
- ? [
- {
- iconType: 'chevron',
- iconRotation: 90,
- onClick: () => {
- this.setState({ mode: 'default' });
- },
- },
- ]
- : undefined;
+const ProfileHeader = (props: ProfileHeaderProps): ReactElement => {
+ const { newAvatarObjectUrl, oldAvatarPath, profileName, ourId, fireInputEvent, setMode } = props;
- return (
-
-
+
+
+
{
+ void fireInputEvent();
+ }}
+ data-testid="image-upload-section"
+ />
+
{
+ setMode('qr');
+ }}
+ role="button"
>
- {viewQR &&
}
- {viewDefault && this.renderDefaultView()}
- {viewEdit && this.renderEditView()}
-
-
-
-
-
-
-
- {viewDefault || viewQR ? (
- {
- window.clipboard.writeText(sessionID);
- ToastUtils.pushCopiedToClipBoard();
- }}
- dataTestId="copy-button-profile-update"
- />
- ) : (
- !this.state.loading && (
-
- )
- )}
-
-
-
- );
- }
-
- private renderProfileHeader() {
- return (
- <>
-
-
- {this.renderAvatar()}
-
-
{
- this.setState(state => ({ ...state, mode: 'qr' }));
- }}
- role="button"
- >
-
-
-
+
- >
- );
- }
+
+
+ );
+};
- private async fireInputEvent() {
- const scaledAvatarUrl = await pickFileForAvatar();
+type ProfileDialogModes = 'default' | 'edit' | 'qr';
- if (scaledAvatarUrl) {
- this.setState({
- newAvatarObjectUrl: scaledAvatarUrl,
- mode: 'edit',
- });
- }
- }
+export const EditProfileDialog = (): ReactElement => {
+ const _profileName = useOurConversationUsername() || '';
+ const [profileName, setProfileName] = useState(_profileName);
+ const [updatedProfileName, setUpdateProfileName] = useState(profileName);
+ const oldAvatarPath = useOurAvatarPath() || '';
+ const [newAvatarObjectUrl, setNewAvatarObjectUrl] = useState(null);
- private renderDefaultView() {
- const name = this.state.updatedProfileName || this.state.profileName;
- return (
- <>
- {this.renderProfileHeader()}
+ const [mode, setMode] = useState('default');
+ const [loading, setLoading] = useState(false);
-
-
{name}
-
{
- this.setState({ mode: 'edit' });
- }}
- dataTestId="edit-profile-icon"
- />
-
- >
- );
- }
+ const ourId = UserUtils.getOurPubKeyStrFromCache();
- private renderEditView() {
- const placeholderText = window.i18n('displayName');
+ const closeDialog = () => {
+ window.removeEventListener('keyup', handleOnKeyUp);
+ window.inboxStore?.dispatch(editProfileModal(null));
+ };
+
+ const backButton =
+ mode === 'edit' || mode === 'qr'
+ ? [
+ {
+ iconType: 'chevron',
+ iconRotation: 90,
+ onClick: () => {
+ setMode('default');
+ },
+ },
+ ]
+ : undefined;
+
+ const onClickOK = async () => {
+ /**
+ * Tidy the profile name input text and save the new profile name and avatar
+ */
+ try {
+ const newName = profileName ? profileName.trim() : '';
- return (
- <>
- {this.renderProfileHeader()}
-
-
-
- >
- );
- }
+ if (newName.length === 0 || newName.length > MAX_USERNAME_BYTES) {
+ return;
+ }
- private renderAvatar() {
- const { oldAvatarPath, newAvatarObjectUrl, profileName } = this.state;
- const userName = profileName || this.convo.id;
+ // this throw if the length in bytes is too long
+ const sanitizedName = sanitizeSessionUsername(newName);
+ const trimName = sanitizedName.trim();
- return (
-
- );
- }
+ setUpdateProfileName(trimName);
+ setLoading(true);
- private onNameEdited(event: ChangeEvent) {
- const displayName = event.target.value;
- try {
- const newName = sanitizeSessionUsername(displayName);
- this.setState({
- profileName: newName,
- });
+ await commitProfileEdits(newName, newAvatarObjectUrl);
+ setMode('default');
+ setUpdateProfileName(profileName);
+ setLoading(false);
} catch (e) {
- this.setState({
- profileName: displayName,
- });
ToastUtils.pushToastError('nameTooLong', window.i18n('displayNameTooLong'));
}
- }
+ };
- private onKeyUp(event: any) {
+ const handleOnKeyUp = (event: any) => {
switch (event.key) {
case 'Enter':
- if (this.state.mode === 'edit') {
- this.onClickOK();
+ if (mode === 'edit') {
+ onClickOK();
}
break;
case 'Esc':
case 'Escape':
- this.closeDialog();
+ closeDialog();
break;
default:
}
- }
+ };
- /**
- * Tidy the profile name input text and save the new profile name and avatar
- */
- private onClickOK() {
- const { newAvatarObjectUrl, profileName } = this.state;
- try {
- const newName = profileName ? profileName.trim() : '';
-
- if (newName.length === 0 || newName.length > MAX_USERNAME_BYTES) {
- return;
- }
-
- // this throw if the length in bytes is too long
- const sanitizedName = sanitizeSessionUsername(newName);
- const trimName = sanitizedName.trim();
-
- this.setState(
- {
- profileName: trimName,
- loading: true,
- },
- async () => {
- await commitProfileEdits(newName, newAvatarObjectUrl);
- this.setState({
- loading: false,
+ const fireInputEvent = async () => {
+ const scaledAvatarUrl = await pickFileForAvatar();
+ if (scaledAvatarUrl) {
+ setNewAvatarObjectUrl(scaledAvatarUrl);
+ setMode('edit');
+ }
+ };
- mode: 'default',
- updatedProfileName: this.state.profileName,
- });
- }
- );
+ const onNameEdited = (event: ChangeEvent) => {
+ const displayName = event.target.value;
+ try {
+ const newName = sanitizeSessionUsername(displayName);
+ setProfileName(newName);
} catch (e) {
+ setProfileName(displayName);
ToastUtils.pushToastError('nameTooLong', window.i18n('displayNameTooLong'));
-
- return;
}
- }
+ };
- private closeDialog() {
- window.removeEventListener('keyup', this.onKeyUp);
- window.inboxStore?.dispatch(editProfileModal(null));
- }
-}
+ return (
+
+
+ {mode === 'qr' && }
+ {mode === 'default' && (
+ <>
+
+
+
{updatedProfileName || profileName}
+
{
+ setMode('edit');
+ }}
+ dataTestId="edit-profile-icon"
+ />
+
+ >
+ )}
+ {mode === 'edit' && (
+ <>
+
+
+
+
+ >
+ )}
-async function commitProfileEdits(newName: string, scaledAvatarUrl: string | null) {
- const ourNumber = UserUtils.getOurPubKeyStrFromCache();
- const conversation = await getConversationController().getOrCreateAndWait(
- ourNumber,
- ConversationTypeEnum.PRIVATE
- );
+
+
+
- if (scaledAvatarUrl?.length) {
- try {
- const blobContent = await (await fetch(scaledAvatarUrl)).blob();
- if (!blobContent || !blobContent.size) {
- throw new Error('Failed to fetch blob content from scaled avatar');
- }
- await uploadOurAvatar(await blobContent.arrayBuffer());
- } catch (error) {
- if (error.message && error.message.length) {
- ToastUtils.pushToastError('edit-profile', error.message);
- }
- window.log.error(
- 'showEditProfileDialog Error ensuring that image is properly sized:',
- error && error.stack ? error.stack : error
- );
- }
- return;
- }
- // do not update the avatar if it did not change
- conversation.setSessionDisplayNameNoCommit(newName);
+
- // might be good to not trigger a sync if the name did not change
- await conversation.commit();
- await setLastProfileUpdateTimestamp(Date.now());
- await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
-}
+ {mode === 'default' || mode === 'qr' ? (
+ {
+ window.clipboard.writeText(ourId);
+ ToastUtils.pushCopiedToClipBoard();
+ }}
+ dataTestId="copy-button-profile-update"
+ />
+ ) : (
+ !loading && (
+
+ )
+ )}
+
+
+
+ );
+};