import { ChangeEvent, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components'; import { Avatar, AvatarSize } from '../avatar/Avatar'; import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils'; import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill'; import { useOurAvatarPath, useOurConversationUsername } from '../../hooks/useParamSelector'; import { ConversationTypeEnum } from '../../models/conversationAttributes'; import { MAX_NAME_LENGTH_BYTES } from '../../session/constants'; import { getConversationController } from '../../session/conversations'; import { sanitizeSessionUsername } from '../../session/utils/String'; import { editProfileModal, updateEditProfilePictureModel } from '../../state/ducks/modalDialog'; import { getTheme } from '../../state/selectors/theme'; import { getThemeValue } from '../../themes/globals'; import { setLastProfileUpdateTimestamp } from '../../util/storage'; import { SessionQRCode } from '../SessionQRCode'; import { SessionWrapperModal } from '../SessionWrapperModal'; import { Flex } from '../basic/Flex'; import { SessionButton } from '../basic/SessionButton'; import { Spacer2XL, Spacer3XL, SpacerLG, SpacerSM, SpacerXL, SpacerXS } from '../basic/Text'; import { SessionIconButton } from '../icon'; import { SessionSpinner } from '../loading'; const StyledEditProfileDialog = styled.div` .session-modal { width: 468px; .session-modal__body { overflow: initial; } } .avatar-center-inner { position: relative; .qr-view-button { cursor: pointer; display: flex; align-items: center; justify-content: center; position: absolute; top: -8px; right: -8px; height: 34px; width: 34px; border-radius: 50%; background-color: var(--white-color); transition: var(--default-duration); &:hover { filter: brightness(90%); } .session-icon-button { opacity: 1; } } } `; const StyledProfileName = styled(Flex)` input { height: 38px; border-radius: 5px; text-align: center; font-size: var(--font-size-md); } &.uneditable { p { margin: 0; padding: 0px var(--margins-lg) 0 var(--margins-sm); } .session-icon-button { padding: 0px; } } `; const StyledSessionIdSection = styled(Flex)` .session-button { width: 160px; } `; const QRView = ({ sessionID }: { sessionID: string }) => { const theme = useSelector(getTheme); return ( ); }; const updateDisplayName = async (newName: string) => { const ourNumber = UserUtils.getOurPubKeyStrFromCache(); const conversation = await getConversationController().getOrCreateAndWait( ourNumber, ConversationTypeEnum.PRIVATE ); 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); }; type ProfileAvatarProps = { avatarPath: string | null; newAvatarObjectUrl?: string | null; profileName: string | undefined; ourId: string; }; export const ProfileAvatar = (props: ProfileAvatarProps) => { const { newAvatarObjectUrl, avatarPath, profileName, ourId } = props; return ( ); }; type ProfileHeaderProps = ProfileAvatarProps & { onClick: () => void; setMode: (mode: ProfileDialogModes) => void; }; const ProfileHeader = (props: ProfileHeaderProps) => { const { avatarPath, profileName, ourId, onClick, setMode } = props; return (
{ setMode('qr'); }} role="button" >
); }; type ProfileDialogModes = 'default' | 'edit' | 'qr'; export const EditProfileDialog = () => { const dispatch = useDispatch(); const _profileName = useOurConversationUsername() || ''; const [profileName, setProfileName] = useState(_profileName); const [updatedProfileName, setUpdateProfileName] = useState(profileName); const avatarPath = useOurAvatarPath() || ''; const ourId = UserUtils.getOurPubKeyStrFromCache(); const [mode, setMode] = useState('default'); const [loading, setLoading] = useState(false); 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() : ''; if (newName.length === 0 || newName.length > MAX_NAME_LENGTH_BYTES) { return; } // this throw if the length in bytes is too long const sanitizedName = sanitizeSessionUsername(newName); const trimName = sanitizedName.trim(); setUpdateProfileName(trimName); setLoading(true); await updateDisplayName(newName); setMode('default'); setUpdateProfileName(profileName); setLoading(false); } catch (e) { ToastUtils.pushToastError('nameTooLong', window.i18n('displayNameTooLong')); } }; const handleOnKeyUp = (event: any) => { switch (event.key) { case 'Enter': if (mode === 'edit') { void onClickOK(); } break; case 'Esc': case 'Escape': closeDialog(); break; default: } }; const handleProfileHeaderClick = () => { closeDialog(); dispatch( updateEditProfilePictureModel({ avatarPath, profileName, ourId, }) ); }; 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 ( /* The
element has a child element that allows keyboard interaction We use edit-profile-default class to prevent the qr icon on the avatar from clipping */ {mode === 'qr' && ( <> )} {mode === 'default' && ( <> { setMode('edit'); }} dataTestId="edit-profile-icon" />

{updatedProfileName || profileName}

)} {mode === 'edit' && ( <> {/* TODO swap with new session input */} )} {mode === 'default' || mode === 'qr' ? ( { window.clipboard.writeText(ourId); ToastUtils.pushCopiedToClipBoard(); }} dataTestId="copy-button-profile-update" /> {mode === 'default' ? ( { setMode('qr'); }} dataTestId="qr-view-profile-update" /> ) : null} ) : ( !loading && ( ) )}
); };