mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-03-13 14:40:01 +01:00
add option to update profile avatar
This commit is contained in:
parent
9c46a6f43a
commit
de281adc36
1 changed files with 122 additions and 38 deletions
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Text,
|
Text,
|
||||||
|
@ -14,6 +14,9 @@ import {
|
||||||
OverlayBackdrop,
|
OverlayBackdrop,
|
||||||
OverlayCenter,
|
OverlayCenter,
|
||||||
Modal,
|
Modal,
|
||||||
|
Dialog,
|
||||||
|
Header,
|
||||||
|
config,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||||
|
@ -33,6 +36,9 @@ import { useObjectURL } from '../../hooks/useObjectURL';
|
||||||
import { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
import { ImageEditor } from '../../components/image-editor';
|
import { ImageEditor } from '../../components/image-editor';
|
||||||
import { ModalWide } from '../../styles/Modal.css';
|
import { ModalWide } from '../../styles/Modal.css';
|
||||||
|
import { createUploadAtom, UploadSuccess } from '../../state/upload';
|
||||||
|
import { CompactUploadCardRenderer } from '../../components/upload-card';
|
||||||
|
import { useCapabilities } from '../../hooks/useCapabilities';
|
||||||
|
|
||||||
function MatrixId() {
|
function MatrixId() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
@ -67,6 +73,9 @@ type ProfileAvatarProps = {
|
||||||
function ProfileAvatar({ profile, userId }: ProfileAvatarProps) {
|
function ProfileAvatar({ profile, userId }: ProfileAvatarProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
const capabilities = useCapabilities();
|
||||||
|
const [alertRemove, setAlertRemove] = useState(false);
|
||||||
|
const disableSetAvatar = capabilities['m.set_avatar_url']?.enabled === false;
|
||||||
|
|
||||||
const defaultDisplayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
|
const defaultDisplayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
|
||||||
const avatarUrl = profile.avatarUrl
|
const avatarUrl = profile.avatarUrl
|
||||||
|
@ -75,13 +84,31 @@ function ProfileAvatar({ profile, userId }: ProfileAvatarProps) {
|
||||||
|
|
||||||
const [imageFile, setImageFile] = useState<File>();
|
const [imageFile, setImageFile] = useState<File>();
|
||||||
const imageFileURL = useObjectURL(imageFile);
|
const imageFileURL = useObjectURL(imageFile);
|
||||||
|
const uploadAtom = useMemo(() => {
|
||||||
|
if (imageFile) return createUploadAtom(imageFile);
|
||||||
|
return undefined;
|
||||||
|
}, [imageFile]);
|
||||||
|
|
||||||
const pickFile = useFilePicker(setImageFile, false);
|
const pickFile = useFilePicker(setImageFile, false);
|
||||||
|
|
||||||
const handleImageCropperClose = useCallback(() => {
|
const handleRemoveUpload = useCallback(() => {
|
||||||
setImageFile(undefined);
|
setImageFile(undefined);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleUploaded = useCallback(
|
||||||
|
(upload: UploadSuccess) => {
|
||||||
|
const { mxc } = upload;
|
||||||
|
mx.setAvatarUrl(mxc);
|
||||||
|
handleRemoveUpload();
|
||||||
|
},
|
||||||
|
[mx, handleRemoveUpload]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemoveAvatar = () => {
|
||||||
|
mx.setAvatarUrl('');
|
||||||
|
setAlertRemove(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title={
|
title={
|
||||||
|
@ -99,46 +126,103 @@ function ProfileAvatar({ profile, userId }: ProfileAvatarProps) {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box gap="200">
|
{uploadAtom ? (
|
||||||
<Button
|
<Box gap="200" direction="Column">
|
||||||
onClick={() => pickFile('image/*')}
|
<CompactUploadCardRenderer
|
||||||
size="300"
|
uploadAtom={uploadAtom}
|
||||||
variant="Secondary"
|
onRemove={handleRemoveUpload}
|
||||||
fill="Soft"
|
onComplete={handleUploaded}
|
||||||
outlined
|
/>
|
||||||
radii="300"
|
</Box>
|
||||||
>
|
) : (
|
||||||
<Text size="B300">Upload</Text>
|
<Box gap="200">
|
||||||
</Button>
|
<Button
|
||||||
{avatarUrl && (
|
onClick={() => pickFile('image/*')}
|
||||||
<Button size="300" variant="Critical" fill="None" radii="300">
|
size="300"
|
||||||
<Text size="B300">Remove</Text>
|
variant="Secondary"
|
||||||
|
fill="Soft"
|
||||||
|
outlined
|
||||||
|
radii="300"
|
||||||
|
disabled={disableSetAvatar}
|
||||||
|
>
|
||||||
|
<Text size="B300">Upload</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
{avatarUrl && (
|
||||||
|
<Button
|
||||||
|
size="300"
|
||||||
|
variant="Critical"
|
||||||
|
fill="None"
|
||||||
|
radii="300"
|
||||||
|
disabled={disableSetAvatar}
|
||||||
|
onClick={() => setAlertRemove(true)}
|
||||||
|
>
|
||||||
|
<Text size="B300">Remove</Text>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
{imageFileURL && (
|
{imageFileURL && (
|
||||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
<Overlay open={false} backdrop={<OverlayBackdrop />}>
|
||||||
<OverlayCenter>
|
<OverlayCenter>
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: handleImageCropperClose,
|
onDeactivate: handleRemoveUpload,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Modal className={ModalWide} variant="Surface" size="500">
|
||||||
|
<ImageEditor
|
||||||
|
name={imageFile?.name ?? 'Unnamed'}
|
||||||
|
url={imageFileURL}
|
||||||
|
requestClose={handleRemoveUpload}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</FocusTrap>
|
||||||
|
</OverlayCenter>
|
||||||
|
</Overlay>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Overlay open={alertRemove} backdrop={<OverlayBackdrop />}>
|
||||||
|
<OverlayCenter>
|
||||||
|
<FocusTrap
|
||||||
|
focusTrapOptions={{
|
||||||
|
initialFocus: false,
|
||||||
|
onDeactivate: () => setAlertRemove(false),
|
||||||
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dialog variant="Surface">
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
|
borderBottomWidth: config.borderWidth.B300,
|
||||||
}}
|
}}
|
||||||
|
variant="Surface"
|
||||||
|
size="500"
|
||||||
>
|
>
|
||||||
<Modal className={ModalWide} variant="Surface" size="500">
|
<Box grow="Yes">
|
||||||
<ImageEditor
|
<Text size="H4">Remove Avatar</Text>
|
||||||
name={imageFile?.name ?? 'Unnamed'}
|
</Box>
|
||||||
url={imageFileURL}
|
<IconButton size="300" onClick={() => setAlertRemove(false)} radii="300">
|
||||||
requestClose={handleImageCropperClose}
|
<Icon src={Icons.Cross} />
|
||||||
/>
|
</IconButton>
|
||||||
</Modal>
|
</Header>
|
||||||
</FocusTrap>
|
<Box style={{ padding: config.space.S400 }} direction="Column" gap="400">
|
||||||
</OverlayCenter>
|
<Box direction="Column" gap="200">
|
||||||
</Overlay>
|
<Text priority="400">Are you sure you want to remove profile avatar?</Text>
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
<Button variant="Critical" onClick={handleRemoveAvatar}>
|
||||||
|
<Text size="B400">Remove</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
|
</FocusTrap>
|
||||||
|
</OverlayCenter>
|
||||||
|
</Overlay>
|
||||||
</SettingTile>
|
</SettingTile>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue