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 { SyncUtils, ToastUtils, UserUtils } from '../../session/utils'; import { getConversationController } from '../../session/conversations'; import { editProfileModal, updateDisplayPictureModel } from '../../state/ducks/modalDialog'; import { pickFileForAvatar } from '../../types/attachments/VisualAttachment'; import { saveQRCode } from '../../util/saveQRCode'; import { setLastProfileUpdateTimestamp } from '../../util/storage'; 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'; import { useDispatch } from 'react-redux'; const handleSaveQRCode = (event: MouseEvent) => { event.preventDefault(); saveQRCode('session-id', '220px', '220px', 'var(--white-color)', 'var(--black-color)'); }; const StyledQRView = styled.div` cursor: pointer; `; const QRView = ({ sessionID }: { sessionID: string }) => { return ( ); }; const commitProfileEdits = async (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); }; export type ProfileAvatarProps = { newAvatarObjectUrl: string | null; oldAvatarPath: string | null; profileName: string | undefined; ourId: string; }; export const ProfileAvatar = (props: ProfileAvatarProps): ReactElement => { const { newAvatarObjectUrl, oldAvatarPath, profileName, ourId } = props; return ( ); }; type ProfileHeaderProps = ProfileAvatarProps & { onClick: () => void; setMode: (mode: ProfileDialogModes) => void; }; const ProfileHeader = (props: ProfileHeaderProps): ReactElement => { const { newAvatarObjectUrl, oldAvatarPath, profileName, ourId, onClick, setMode } = props; return (
{ setMode('qr'); }} role="button" >
); }; type ProfileDialogModes = 'default' | 'edit' | 'qr'; export const EditProfileDialog = (): ReactElement => { const dispatch = useDispatch(); const _profileName = useOurConversationUsername() || ''; const [profileName, setProfileName] = useState(_profileName); const [updatedProfileName, setUpdateProfileName] = useState(profileName); const oldAvatarPath = useOurAvatarPath() || ''; const [newAvatarObjectUrl, setNewAvatarObjectUrl] = useState(null); const [mode, setMode] = useState('default'); const [loading, setLoading] = useState(false); const ourId = UserUtils.getOurPubKeyStrFromCache(); 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_USERNAME_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 commitProfileEdits(newName, newAvatarObjectUrl); 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') { onClickOK(); } break; case 'Esc': case 'Escape': closeDialog(); break; default: } }; const fireInputEvent = async () => { const scaledAvatarUrl = await pickFileForAvatar(); if (scaledAvatarUrl) { setNewAvatarObjectUrl(scaledAvatarUrl); setMode('edit'); } return scaledAvatarUrl; }; const handleProfileHeaderClick = () => { closeDialog(); dispatch( updateDisplayPictureModel({ newAvatarObjectUrl, oldAvatarPath, profileName, ourId, avatarAction: fireInputEvent, removeAction: () => {}, }) ); }; 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 (
{mode === 'qr' && } {mode === 'default' && ( <>

{updatedProfileName || profileName}

{ setMode('edit'); }} dataTestId="edit-profile-icon" />
)} {mode === 'edit' && ( <>
)}
{mode === 'default' || mode === 'qr' ? ( { window.clipboard.writeText(ourId); ToastUtils.pushCopiedToClipBoard(); }} dataTestId="copy-button-profile-update" /> ) : ( !loading && ( ) )}
); };