feat: add worksapce type label (#4045)

Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
JimmFly
2023-08-31 13:16:58 -05:00
committed by GitHub
co-authored by Alex Yang
parent 18d5a99af5
commit df4d71b0c8
9 changed files with 213 additions and 20 deletions
@@ -14,6 +14,7 @@ import { useMemo } from 'react';
import { useWorkspace } from '../../../hooks/use-workspace'; import { useWorkspace } from '../../../hooks/use-workspace';
import { DeleteLeaveWorkspace } from './delete-leave-workspace'; import { DeleteLeaveWorkspace } from './delete-leave-workspace';
import { ExportPanel } from './export'; import { ExportPanel } from './export';
import { LabelsPanel } from './labels';
import { MembersPanel } from './members'; import { MembersPanel } from './members';
import { ProfilePanel } from './profile'; import { ProfilePanel } from './profile';
import { PublishPanel } from './publish'; import { PublishPanel } from './publish';
@@ -69,6 +70,7 @@ export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => {
spreadCol={false} spreadCol={false}
> >
<ProfilePanel workspace={workspace} {...props} /> <ProfilePanel workspace={workspace} {...props} />
<LabelsPanel workspace={workspace} {...props} />
</SettingRow> </SettingRow>
</SettingWrapper> </SettingWrapper>
<SettingWrapper title={t['AFFiNE Cloud']()}> <SettingWrapper title={t['AFFiNE Cloud']()}>
@@ -0,0 +1,103 @@
import type { AffineOfficialWorkspace } from '@affine/env/workspace';
import { useMemo } from 'react';
import { type WorkspaceSettingDetailProps } from './index';
import * as style from './style.css';
export interface LabelsPanelProps extends WorkspaceSettingDetailProps {
workspace: AffineOfficialWorkspace;
}
type WorkspaceStatus =
| 'local'
| 'syncCloud'
| 'syncDocker'
| 'selfHosted'
| 'joinedWorkspace'
| 'availableOffline'
| 'publishedToWeb';
type LabelProps = {
value: string;
background: string;
};
type LabelMap = {
[key in WorkspaceStatus]: LabelProps;
};
type labelConditionsProps = {
condition: boolean;
label: WorkspaceStatus;
};
const Label = ({ value, background }: LabelProps) => {
return (
<div>
<div className={style.workspaceLabel} style={{ background: background }}>
{value}
</div>
</div>
);
};
export const LabelsPanel = ({ workspace, isOwner }: LabelsPanelProps) => {
const labelMap: LabelMap = useMemo(
() => ({
local: {
value: 'Local',
background: 'var(--affine-tag-orange)',
},
syncCloud: {
value: 'Sync with AFFiNE Cloud',
background: 'var(--affine-tag-blue)',
},
syncDocker: {
value: 'Sync with AFFiNE Docker',
background: 'var(--affine-tag-green)',
},
selfHosted: {
value: 'Self-Hosted Server',
background: 'var(--affine-tag-purple)',
},
joinedWorkspace: {
value: 'Joined Workspace',
background: 'var(--affine-tag-yellow)',
},
availableOffline: {
value: 'Available Offline',
background: 'var(--affine-tag-green)',
},
publishedToWeb: {
value: 'Published to Web',
background: 'var(--affine-tag-blue)',
},
}),
[]
);
const labelConditions: labelConditionsProps[] = [
{ condition: !isOwner, label: 'joinedWorkspace' },
{ condition: workspace.flavour === 'local', label: 'local' },
{ condition: workspace.flavour === 'affine-cloud', label: 'syncCloud' },
{
condition: workspace.flavour === 'affine-public',
label: 'publishedToWeb',
},
//TODO: add these labels
// { status==="synced", label: 'availableOffline' }
// { workspace.flavour === 'affine-Docker', label: 'syncDocker' }
// { workspace.flavour === 'self-hosted', label: 'selfHosted' }
];
return (
<div className={style.labelWrapper}>
{labelConditions.map(
({ condition, label }) =>
condition && (
<Label
key={label}
value={labelMap[label].value}
background={labelMap[label].background}
/>
)
)}
</div>
);
};
@@ -12,6 +12,15 @@ export const profileHandlerWrapper = style({
marginLeft: '20px', marginLeft: '20px',
}); });
export const labelWrapper = style({
width: '100%',
display: 'flex',
alignItems: 'center',
marginTop: '24px',
gap: '10px',
flexWrap: 'wrap',
});
export const avatarWrapper = style({ export const avatarWrapper = style({
width: '56px', width: '56px',
height: '56px', height: '56px',
@@ -146,3 +155,17 @@ export const label = style({
color: 'var(--affine-text-secondary-color)', color: 'var(--affine-text-secondary-color)',
marginBottom: '5px', marginBottom: '5px',
}); });
export const workspaceLabel = style({
width: '100%',
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
alignItems: 'center',
borderRadius: '6px',
padding: '2px 10px',
border: '1px solid var(--affine-white-30)',
fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-primary-color)',
lineHeight: '20px',
whiteSpace: 'nowrap',
});
@@ -7,6 +7,7 @@ export const header = style({
position: 'relative', position: 'relative',
padding: '0 16px', padding: '0 16px',
minHeight: '52px', minHeight: '52px',
background: 'var(--affine-background-primary-color)',
borderBottom: '1px solid var(--affine-border-color)', borderBottom: '1px solid var(--affine-border-color)',
zIndex: 2, zIndex: 2,
selectors: { selectors: {
@@ -110,9 +110,9 @@ const CloudWorkSpaceList = ({
<> <>
<StyledModalHeader> <StyledModalHeader>
<StyledModalHeaderLeft> <StyledModalHeaderLeft>
<StyledModalTitle> <StyledWorkspaceFlavourTitle>
{t['com.affine.workspace.cloud']()} {t['com.affine.workspace.cloud']()}
</StyledModalTitle> </StyledWorkspaceFlavourTitle>
</StyledModalHeaderLeft> </StyledModalHeaderLeft>
</StyledModalHeader> </StyledModalHeader>
<StyledModalContent> <StyledModalContent>
@@ -236,9 +236,8 @@ export const StyledModalBody = styled('div')(() => {
export const StyledWorkspaceFlavourTitle = styled('div')(() => { export const StyledWorkspaceFlavourTitle = styled('div')(() => {
return { return {
fontSize: '12px', fontSize: 'var(--affine-font-xs)',
fontWeight: 600,
color: 'var(--affine-text-secondary-color)', color: 'var(--affine-text-secondary-color)',
lineHeight: '20px', marginBottom: '4px',
}; };
}); });
@@ -1,6 +1,10 @@
import { displayFlex, textEllipsis } from '@affine/component'; import { displayFlex, textEllipsis } from '@affine/component';
import { styled } from '@affine/component'; import { styled } from '@affine/component';
export const StyledSelectorContainer = styled('div')(() => { export const StyledSelectorContainer = styled('div')(({
disableHoverBackground,
}: {
disableHoverBackground: boolean;
}) => {
return { return {
height: '58px', height: '58px',
display: 'flex', display: 'flex',
@@ -10,7 +14,7 @@ export const StyledSelectorContainer = styled('div')(() => {
color: 'var(--affine-text-primary-color)', color: 'var(--affine-text-primary-color)',
':hover': { ':hover': {
cursor: 'pointer', cursor: 'pointer',
background: 'var(--affine-hover-color)', background: disableHoverBackground ? '' : 'var(--affine-hover-color)',
}, },
}; };
}); });
@@ -38,10 +42,17 @@ export const StyledWorkspaceStatus = styled('div')(() => {
fontSize: 'var(--affine-font-sm)', fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-secondary-color)', color: 'var(--affine-text-secondary-color)',
userSelect: 'none', userSelect: 'none',
padding: '0 4px',
gap: '4px',
zIndex: '1',
svg: { svg: {
color: 'var(--affine-icon-color)', color: 'var(--affine-icon-color)',
fontSize: 'var(--affine-font-base)', fontSize: 'var(--affine-font-base)',
marginRight: '4px', },
':hover': {
cursor: 'pointer',
borderRadius: '4px',
background: 'var(--affine-hover-color)',
}, },
}; };
}); });
@@ -1,9 +1,16 @@
import { WorkspaceAvatar } from '@affine/component/workspace-avatar'; import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { CloudWorkspaceIcon, LocalWorkspaceIcon } from '@blocksuite/icons'; import {
CloudWorkspaceIcon,
LocalWorkspaceIcon,
NoNetworkIcon,
} from '@blocksuite/icons';
import { Tooltip } from '@toeverything/components/tooltip';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import type React from 'react'; import type React from 'react';
import { useCallback } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useCurrentLoginStatus } from '../../../../hooks/affine/use-current-login-status';
import { useSystemOnline } from '../../../../hooks/use-system-online';
import type { AllWorkspace } from '../../../../shared'; import type { AllWorkspace } from '../../../../shared';
import { workspaceAvatarStyle } from './index.css'; import { workspaceAvatarStyle } from './index.css';
import { import {
@@ -29,7 +36,8 @@ export const WorkspaceSelector = ({
const [name] = useBlockSuiteWorkspaceName( const [name] = useBlockSuiteWorkspaceName(
currentWorkspace.blockSuiteWorkspace currentWorkspace.blockSuiteWorkspace
); );
const [isHovered, setIsHovered] = useState(false);
const [container, setContainer] = useState<HTMLDivElement | null>(null);
// Open dialog when `Enter` or `Space` pressed // Open dialog when `Enter` or `Space` pressed
// TODO-Doma Refactor with `@radix-ui/react-dialog` or other libraries that handle these out of the box and be accessible by default // TODO-Doma Refactor with `@radix-ui/react-dialog` or other libraries that handle these out of the box and be accessible by default
// TODO: Delete this? // TODO: Delete this?
@@ -43,13 +51,48 @@ export const WorkspaceSelector = ({
}, },
[onClick] [onClick]
); );
const loginStatus = useCurrentLoginStatus();
const isOnline = useSystemOnline();
const content = useMemo(() => {
if (!isOnline) {
return 'Disconnected, please check your network connection';
}
if (
loginStatus === 'authenticated' &&
currentWorkspace.flavour !== 'local'
) {
return 'Sync with AFFiNE Cloud';
}
return 'Saved locally';
}, [currentWorkspace.flavour, isOnline, loginStatus]);
const WorkspaceStatus = () => {
if (!isOnline) {
return (
<>
<NoNetworkIcon />
Offline
</>
);
}
return (
<>
{currentWorkspace.flavour === 'local' ? (
<LocalWorkspaceIcon />
) : (
<CloudWorkspaceIcon />
)}
{currentWorkspace.flavour === 'local' ? 'Local' : 'AFFiNE Cloud'}
</>
);
};
return ( return (
<StyledSelectorContainer <StyledSelectorContainer
role="button" role="button"
tabIndex={0} tabIndex={0}
onClick={onClick} onClick={onClick}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
disableHoverBackground={isHovered}
data-testid="current-workspace" data-testid="current-workspace"
id="current-workspace" id="current-workspace"
> >
@@ -63,14 +106,25 @@ export const WorkspaceSelector = ({
<StyledWorkspaceName data-testid="workspace-name"> <StyledWorkspaceName data-testid="workspace-name">
{name} {name}
</StyledWorkspaceName> </StyledWorkspaceName>
<StyledWorkspaceStatus> <div style={{ display: 'flex' }}>
{currentWorkspace.flavour === 'local' ? ( <Tooltip
<LocalWorkspaceIcon /> content={content}
) : ( portalOptions={{
<CloudWorkspaceIcon /> container,
)} }}
{currentWorkspace.flavour === 'local' ? 'Local' : 'AFFiNE Cloud'} >
</StyledWorkspaceStatus> <StyledWorkspaceStatus
onMouseEnter={() => {
setIsHovered(true);
}}
ref={setContainer}
onMouseLeave={() => setIsHovered(false)}
onClick={e => e.stopPropagation()}
>
<WorkspaceStatus />
</StyledWorkspaceStatus>
</Tooltip>
</div>
</StyledSelectorWrapper> </StyledSelectorWrapper>
</StyledSelectorContainer> </StyledSelectorContainer>
); );
@@ -13,7 +13,7 @@ export const navWrapperStyle = style({
width: navWidthVar, width: navWidthVar,
minWidth: navWidthVar, minWidth: navWidthVar,
height: '100%', height: '100%',
zIndex: 1, zIndex: 3,
paddingBottom: '8px', paddingBottom: '8px',
backgroundColor: 'transparent', backgroundColor: 'transparent',
'@media': { '@media': {