mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
feat: add tooltip in user & workspace setting (#4260)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user