feat: add tooltip in user & workspace setting (#4260)

This commit is contained in:
Qi
2023-09-13 12:37:25 +08:00
committed by GitHub
parent 5e0fd0b839
commit 32ee946670
5 changed files with 86 additions and 56 deletions

View File

@@ -1,12 +1,15 @@
import { FlexWrapper, Input, toast, Wrapper } from '@affine/component';
import { FlexWrapper, Input, Wrapper } from '@affine/component';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import type { AffineOfficialWorkspace } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CameraIcon, DoneIcon } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import { CameraIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import { Tooltip } from '@toeverything/components/tooltip';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import clsx from 'clsx';
import { useSetAtom } from 'jotai';
import {
type KeyboardEvent,
startTransition,
@@ -24,6 +27,7 @@ export interface ProfilePanelProps extends WorkspaceSettingDetailProps {
export const ProfilePanel = ({ workspace, isOwner }: ProfilePanelProps) => {
const t = useAFFiNEI18N();
const pushNotification = useSetAtom(pushNotificationAtom);
const [, update] = useBlockSuiteWorkspaceAvatarUrl(
workspace.blockSuiteWorkspace
@@ -34,13 +38,18 @@ export const ProfilePanel = ({ workspace, isOwner }: ProfilePanelProps) => {
);
const [input, setInput] = useState<string>(name);
const [tooltipContainer, setTooltipContainer] =
useState<HTMLDivElement | null>(null);
const handleUpdateWorkspaceName = useCallback(
(name: string) => {
setName(name);
toast(t['Update workspace name success']());
pushNotification({
title: t['Update workspace name success'](),
type: 'success',
});
},
[setName, t]
[pushNotification, setName, t]
);
const handleSetInput = useCallback((value: string) => {
@@ -64,23 +73,34 @@ export const ProfilePanel = ({ workspace, isOwner }: ProfilePanelProps) => {
return (
<div className={style.profileWrapper}>
<div className={clsx(style.avatarWrapper, { disable: !isOwner })}>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={update}
data-testid="upload-avatar"
<Tooltip
content={t['Click to replace photo']()}
portalOptions={{
container: tooltipContainer,
}}
>
<div
className={clsx(style.avatarWrapper, { disable: !isOwner })}
ref={setTooltipContainer}
>
<>
<div className="camera-icon-wrapper">
<CameraIcon />
</div>
<WorkspaceAvatar
size={56}
workspace={workspace.blockSuiteWorkspace}
/>
</>
</Upload>
</div>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={update}
data-testid="upload-avatar"
>
<>
<div className="camera-icon-wrapper">
<CameraIcon />
</div>
<WorkspaceAvatar
size={56}
workspace={workspace.blockSuiteWorkspace}
/>
</>
</Upload>
</div>
</Tooltip>
<Wrapper marginLeft={20}>
<div className={style.label}>{t['Workspace Name']()}</div>
<FlexWrapper alignItems="center" flexGrow="1">
@@ -97,16 +117,15 @@ export const ProfilePanel = ({ workspace, isOwner }: ProfilePanelProps) => {
onKeyUp={handleKeyUp}
/>
{input === workspace.blockSuiteWorkspace.meta.name ? null : (
<IconButton
<Button
data-testid="save-workspace-name"
onClick={handleClick}
active={true}
style={{
marginLeft: '12px',
}}
>
<DoneIcon />
</IconButton>
{t['com.affine.editCollection.save']()}
</Button>
)}
</FlexWrapper>
</Wrapper>

View File

@@ -26,7 +26,6 @@ export const avatarWrapper = style({
height: '56px',
borderRadius: '50%',
position: 'relative',
overflow: 'hidden',
cursor: 'pointer',
flexShrink: '0',
selectors: {
@@ -43,8 +42,9 @@ globalStyle(`${avatarWrapper}:hover .camera-icon-wrapper`, {
display: 'flex',
});
globalStyle(`${avatarWrapper} .camera-icon-wrapper`, {
width: '100%',
height: '100%',
width: '56px',
height: '56px',
borderRadius: '50%',
position: 'absolute',
display: 'none',
justifyContent: 'center',

View File

@@ -8,8 +8,9 @@ import { UserAvatar } from '@affine/component/user-avatar';
import { allBlobSizesQuery, uploadAvatarMutation } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useMutation, useQuery } from '@affine/workspace/affine/gql';
import { ArrowRightSmallIcon, CameraIcon, DoneIcon } from '@blocksuite/icons';
import { Button, IconButton } from '@toeverything/components/button';
import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import { Tooltip } from '@toeverything/components/tooltip';
import { useSetAtom } from 'jotai';
import { type FC, Suspense, useCallback, useState } from 'react';
@@ -24,6 +25,8 @@ export const AvatarAndName = () => {
const t = useAFFiNEI18N();
const user = useCurrentUser();
const [tooltipContainer, setTooltipContainer] =
useState<HTMLDivElement | null>(null);
const [input, setInput] = useState<string>(user.name);
const { trigger: avatarTrigger } = useMutation({
@@ -56,25 +59,32 @@ export const AvatarAndName = () => {
spreadCol={false}
>
<FlexWrapper style={{ margin: '12px 0 24px 0' }} alignItems="center">
<div className={style.avatarWrapper}>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={handleUpdateUserAvatar}
data-testid="upload-user-avatar"
>
<>
<div className="camera-icon-wrapper">
<CameraIcon />
</div>
<UserAvatar
size={56}
name={user.name}
url={user.image}
className="avatar"
/>
</>
</Upload>
</div>
<Tooltip
content={t['Click to replace photo']()}
portalOptions={{
container: tooltipContainer,
}}
>
<div className={style.avatarWrapper} ref={setTooltipContainer}>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={handleUpdateUserAvatar}
data-testid="upload-user-avatar"
>
<>
<div className="camera-icon-wrapper">
<CameraIcon />
</div>
<UserAvatar
size={56}
name={user.name}
url={user.image}
className="avatar"
/>
</>
</Upload>
</div>
</Tooltip>
<div className={style.profileInputWrapper}>
<label>{t['com.affine.settings.profile.name']()}</label>
@@ -90,18 +100,17 @@ export const AvatarAndName = () => {
onChange={setInput}
/>
{input && input === user.name ? null : (
<IconButton
<Button
data-testid="save-user-name"
onClick={() => {
handleUpdateUserName(input);
}}
style={{
color: 'var(--affine-primary-color)',
marginLeft: '12px',
}}
>
<DoneIcon />
</IconButton>
{t['com.affine.editCollection.save']()}
</Button>
)}
</FlexWrapper>
</div>

View File

@@ -14,7 +14,6 @@ export const avatarWrapper = style({
height: '56px',
borderRadius: '50%',
position: 'relative',
overflow: 'hidden',
cursor: 'pointer',
flexShrink: '0',
selectors: {
@@ -28,8 +27,9 @@ globalStyle(`${avatarWrapper}:hover .camera-icon-wrapper`, {
display: 'flex',
});
globalStyle(`${avatarWrapper} .camera-icon-wrapper`, {
width: '100%',
height: '100%',
width: '56px',
height: '56px',
borderRadius: '50%',
position: 'absolute',
display: 'none',
justifyContent: 'center',