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 && (
)
)}
);
};