diff --git a/apps/core/src/components/affine/new-workspace-setting-detail/labels.tsx b/apps/core/src/components/affine/new-workspace-setting-detail/labels.tsx
new file mode 100644
index 0000000000..f6005c3f23
--- /dev/null
+++ b/apps/core/src/components/affine/new-workspace-setting-detail/labels.tsx
@@ -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 (
+
+ );
+};
+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 (
+
+ {labelConditions.map(
+ ({ condition, label }) =>
+ condition && (
+
+ )
+ )}
+
+ );
+};
diff --git a/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts b/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts
index 366d229771..6881c116a7 100644
--- a/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts
+++ b/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts
@@ -12,6 +12,15 @@ export const profileHandlerWrapper = style({
marginLeft: '20px',
});
+export const labelWrapper = style({
+ width: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: '24px',
+ gap: '10px',
+ flexWrap: 'wrap',
+});
+
export const avatarWrapper = style({
width: '56px',
height: '56px',
@@ -146,3 +155,17 @@ export const label = style({
color: 'var(--affine-text-secondary-color)',
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',
+});
diff --git a/apps/core/src/components/pure/header/style.css.tsx b/apps/core/src/components/pure/header/style.css.tsx
index 17c6d72a0c..8a90c0116d 100644
--- a/apps/core/src/components/pure/header/style.css.tsx
+++ b/apps/core/src/components/pure/header/style.css.tsx
@@ -7,6 +7,7 @@ export const header = style({
position: 'relative',
padding: '0 16px',
minHeight: '52px',
+ background: 'var(--affine-background-primary-color)',
borderBottom: '1px solid var(--affine-border-color)',
zIndex: 2,
selectors: {
diff --git a/apps/core/src/components/pure/workspace-list-modal/index.tsx b/apps/core/src/components/pure/workspace-list-modal/index.tsx
index 9396daa361..ed16caf3fc 100644
--- a/apps/core/src/components/pure/workspace-list-modal/index.tsx
+++ b/apps/core/src/components/pure/workspace-list-modal/index.tsx
@@ -110,9 +110,9 @@ const CloudWorkSpaceList = ({
<>
-
+
{t['com.affine.workspace.cloud']()}
-
+
diff --git a/apps/core/src/components/pure/workspace-list-modal/styles.ts b/apps/core/src/components/pure/workspace-list-modal/styles.ts
index 8af728d9be..0e7d76672b 100644
--- a/apps/core/src/components/pure/workspace-list-modal/styles.ts
+++ b/apps/core/src/components/pure/workspace-list-modal/styles.ts
@@ -236,9 +236,8 @@ export const StyledModalBody = styled('div')(() => {
export const StyledWorkspaceFlavourTitle = styled('div')(() => {
return {
- fontSize: '12px',
- fontWeight: 600,
+ fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-secondary-color)',
- lineHeight: '20px',
+ marginBottom: '4px',
};
});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/styles.ts b/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/styles.ts
index ceabd79481..1aa77e2000 100644
--- a/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/styles.ts
+++ b/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/styles.ts
@@ -1,6 +1,10 @@
import { displayFlex, textEllipsis } from '@affine/component';
import { styled } from '@affine/component';
-export const StyledSelectorContainer = styled('div')(() => {
+export const StyledSelectorContainer = styled('div')(({
+ disableHoverBackground,
+}: {
+ disableHoverBackground: boolean;
+}) => {
return {
height: '58px',
display: 'flex',
@@ -10,7 +14,7 @@ export const StyledSelectorContainer = styled('div')(() => {
color: 'var(--affine-text-primary-color)',
':hover': {
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)',
color: 'var(--affine-text-secondary-color)',
userSelect: 'none',
+ padding: '0 4px',
+ gap: '4px',
+ zIndex: '1',
svg: {
color: 'var(--affine-icon-color)',
fontSize: 'var(--affine-font-base)',
- marginRight: '4px',
+ },
+ ':hover': {
+ cursor: 'pointer',
+ borderRadius: '4px',
+ background: 'var(--affine-hover-color)',
},
};
});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/workspace-selector.tsx b/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/workspace-selector.tsx
index 17f290c219..79f3cdd373 100644
--- a/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/workspace-selector.tsx
+++ b/apps/core/src/components/pure/workspace-slider-bar/WorkspaceSelector/workspace-selector.tsx
@@ -1,9 +1,16 @@
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 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 { workspaceAvatarStyle } from './index.css';
import {
@@ -29,7 +36,8 @@ export const WorkspaceSelector = ({
const [name] = useBlockSuiteWorkspaceName(
currentWorkspace.blockSuiteWorkspace
);
-
+ const [isHovered, setIsHovered] = useState(false);
+ const [container, setContainer] = useState(null);
// 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: Delete this?
@@ -43,13 +51,48 @@ export const WorkspaceSelector = ({
},
[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 (
+ <>
+
+ Offline
+ >
+ );
+ }
+ return (
+ <>
+ {currentWorkspace.flavour === 'local' ? (
+
+ ) : (
+
+ )}
+ {currentWorkspace.flavour === 'local' ? 'Local' : 'AFFiNE Cloud'}
+ >
+ );
+ };
return (
@@ -63,14 +106,25 @@ export const WorkspaceSelector = ({
{name}
-
- {currentWorkspace.flavour === 'local' ? (
-
- ) : (
-
- )}
- {currentWorkspace.flavour === 'local' ? 'Local' : 'AFFiNE Cloud'}
-
+
+
+ {
+ setIsHovered(true);
+ }}
+ ref={setContainer}
+ onMouseLeave={() => setIsHovered(false)}
+ onClick={e => e.stopPropagation()}
+ >
+
+
+
+
);
diff --git a/packages/component/src/components/app-sidebar/index.css.ts b/packages/component/src/components/app-sidebar/index.css.ts
index 25f377c32d..16a9940d1f 100644
--- a/packages/component/src/components/app-sidebar/index.css.ts
+++ b/packages/component/src/components/app-sidebar/index.css.ts
@@ -13,7 +13,7 @@ export const navWrapperStyle = style({
width: navWidthVar,
minWidth: navWidthVar,
height: '100%',
- zIndex: 1,
+ zIndex: 3,
paddingBottom: '8px',
backgroundColor: 'transparent',
'@media': {