diff --git a/src/app/components/image-editor/ImageEditor.css.ts b/src/app/components/image-editor/ImageEditor.css.ts new file mode 100644 index 000000000..6b42c22da --- /dev/null +++ b/src/app/components/image-editor/ImageEditor.css.ts @@ -0,0 +1,35 @@ +import { style } from '@vanilla-extract/css'; +import { DefaultReset, color, config } from 'folds'; + +export const ImageEditor = style([ + DefaultReset, + { + height: '100%', + }, +]); + +export const ImageEditorHeader = style([ + DefaultReset, + { + paddingLeft: config.space.S200, + paddingRight: config.space.S200, + borderBottomWidth: config.borderWidth.B300, + flexShrink: 0, + gap: config.space.S200, + }, +]); + +export const ImageEditorContent = style([ + DefaultReset, + { + backgroundColor: color.Background.Container, + color: color.Background.OnContainer, + overflow: 'hidden', + }, +]); + +export const Image = style({ + width: '100%', + height: '100%', + objectFit: 'contain', +}); diff --git a/src/app/components/image-editor/ImageEditor.tsx b/src/app/components/image-editor/ImageEditor.tsx new file mode 100644 index 000000000..1ec474e8b --- /dev/null +++ b/src/app/components/image-editor/ImageEditor.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Box, Chip, Header, Icon, IconButton, Icons, Text, as } from 'folds'; +import * as css from './ImageEditor.css'; + +export type ImageEditorProps = { + name: string; + url: string; + requestClose: () => void; +}; + +export const ImageEditor = as<'div', ImageEditorProps>( + ({ className, name, url, requestClose, ...props }, ref) => { + const handleApply = () => { + // + }; + + return ( + +
+ + + + + + Image Editor + + + + + Save + + +
+ + {name} + +
+ ); + } +); diff --git a/src/app/components/image-editor/index.ts b/src/app/components/image-editor/index.ts new file mode 100644 index 000000000..51907cbb7 --- /dev/null +++ b/src/app/components/image-editor/index.ts @@ -0,0 +1 @@ +export * from './ImageEditor'; diff --git a/src/app/features/settings/Account.tsx b/src/app/features/settings/Account.tsx index 7ab14aa63..e4d414533 100644 --- a/src/app/features/settings/Account.tsx +++ b/src/app/features/settings/Account.tsx @@ -1,17 +1,38 @@ -import React, { useCallback, useEffect } from 'react'; -import { Box, Text, IconButton, Icon, Icons, Scroll, Input, Avatar, Button, Chip } from 'folds'; +import React, { useCallback, useEffect, useState } from 'react'; +import { + Box, + Text, + IconButton, + Icon, + Icons, + Scroll, + Input, + Avatar, + Button, + Chip, + Overlay, + OverlayBackdrop, + OverlayCenter, + Modal, +} from 'folds'; +import FocusTrap from 'focus-trap-react'; import { Page, PageContent, PageHeader } from '../../components/page'; import { SequenceCard } from '../../components/sequence-card'; import { SequenceCardStyle } from './styles.css'; import { SettingTile } from '../../components/setting-tile'; import { useMatrixClient } from '../../hooks/useMatrixClient'; -import { useUserProfile } from '../../hooks/useUserProfile'; +import { UserProfile, useUserProfile } from '../../hooks/useUserProfile'; import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; import { UserAvatar } from '../../components/user-avatar'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { nameInitials } from '../../utils/common'; import { copyToClipboard } from '../../utils/dom'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; +import { useFilePicker } from '../../hooks/useFilePicker'; +import { useObjectURL } from '../../hooks/useObjectURL'; +import { stopPropagation } from '../../utils/keyboard'; +import { ImageEditor } from '../../components/image-editor'; +import { ModalWide } from '../../styles/Modal.css'; function MatrixId() { const mx = useMatrixClient(); @@ -39,17 +60,95 @@ function MatrixId() { ); } -function Profile() { +type ProfileAvatarProps = { + profile: UserProfile; + userId: string; +}; +function ProfileAvatar({ profile, userId }: ProfileAvatarProps) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); - const userId = mx.getUserId()!; - const profile = useUserProfile(userId); const defaultDisplayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId; const avatarUrl = profile.avatarUrl ? mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined : undefined; + const [imageFile, setImageFile] = useState(); + const imageFileURL = useObjectURL(imageFile); + + const pickFile = useFilePicker(setImageFile, false); + + const handleImageCropperClose = useCallback(() => { + setImageFile(undefined); + }, []); + + return ( + + Avatar + + } + before={ + + {nameInitials(defaultDisplayName)}} + /> + + } + > + + + {avatarUrl && ( + + )} + + {imageFileURL && ( + }> + + + + + + + + + )} + + + ); +} + +function Profile() { + const mx = useMatrixClient(); + const userId = mx.getUserId()!; + const profile = useUserProfile(userId); + const defaultDisplayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId; + return ( Profile @@ -59,33 +158,7 @@ function Profile() { direction="Column" gap="400" > - - Avatar - - } - before={ - - {nameInitials(defaultDisplayName)}} - /> - - } - > - - - {avatarUrl && ( - - )} - - + @@ -136,7 +209,7 @@ function ContactInformation() { {emailIds?.map((email) => ( - + {email.address} ))}