Merge remote-tracking branch 'refs/remotes/origin/feat/datacenter'
Conflicts: package.json packages/app/src/components/create-workspace/index.tsx packages/app/src/components/workspace-modal/index.tsx packages/app/src/components/workspace-modal/languageMenu.tsx packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx packages/app/src/components/workspace-slider-bar/index.tsx packages/app/src/pages/_app.tsx pnpm-lock.yaml
This commit is contained in:
@@ -46,7 +46,6 @@
|
|||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"lint-staged": "^13.1.0",
|
"lint-staged": "^13.1.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
|
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Input from '@/ui/input';
|
import Input from '@/ui/input';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
|
||||||
import { KeyboardEvent } from 'react';
|
import { KeyboardEvent } from 'react';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
interface ICloseParams {
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
workspaceId?: string;
|
|
||||||
}
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: (opts: ICloseParams) => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreateWorkspaceModal = ({ open, onClose }: ModalProps) => {
|
export const CreateWorkspaceModal = ({ open, onClose }: ModalProps) => {
|
||||||
const [workspaceName, setWorkspaceName] = useState('');
|
const [workspaceName, setWorkspaceName] = useState('');
|
||||||
const { createWorkspace, setActiveWorkspace } = useTemporaryHelper();
|
const { createWorkspace } = useWorkspaceHelper();
|
||||||
const handleCreateWorkspace = () => {
|
const router = useRouter();
|
||||||
const workspace = createWorkspace(workspaceName);
|
const handleCreateWorkspace = async () => {
|
||||||
onClose({ workspaceId: workspace.id });
|
const workspace = await createWorkspace(workspaceName);
|
||||||
setActiveWorkspace(workspace);
|
if (workspace && workspace.room) {
|
||||||
|
router.replace(`/workspace/${workspace.room}`);
|
||||||
|
onClose();
|
||||||
|
} else {
|
||||||
|
console.log('create error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@@ -39,7 +42,7 @@ export const CreateWorkspaceModal = ({ open, onClose }: ModalProps) => {
|
|||||||
top={6}
|
top={6}
|
||||||
right={6}
|
right={6}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose({});
|
onClose();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
|
|||||||
71
packages/app/src/components/editor/index.tsx
Normal file
71
packages/app/src/components/editor/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import type { Page, Workspace } from '@blocksuite/store';
|
||||||
|
import '@blocksuite/blocks';
|
||||||
|
import { EditorContainer } from '@blocksuite/editor';
|
||||||
|
import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-v2.0.md';
|
||||||
|
import { styled } from '@/styles';
|
||||||
|
|
||||||
|
const StyledEditorContainer = styled('div')(() => {
|
||||||
|
return {
|
||||||
|
height: 'calc(100vh - 60px)',
|
||||||
|
padding: '0 32px',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
page: Page;
|
||||||
|
workspace: Workspace;
|
||||||
|
setEditor: (editor: EditorContainer) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Editor = ({ page, workspace, setEditor }: Props) => {
|
||||||
|
const editorContainer = useRef<HTMLDivElement>(null);
|
||||||
|
// const { currentWorkspace, currentPage, setEditor } = useAppState();
|
||||||
|
useEffect(() => {
|
||||||
|
const ret = () => {
|
||||||
|
const node = editorContainer.current;
|
||||||
|
while (node?.firstChild) {
|
||||||
|
node.removeChild(node.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const editor = new EditorContainer();
|
||||||
|
editor.page = page;
|
||||||
|
|
||||||
|
editorContainer.current?.appendChild(editor);
|
||||||
|
if (page.isEmpty) {
|
||||||
|
const isFirstPage = workspace?.meta.pageMetas.length === 1;
|
||||||
|
// Can not use useCurrentPageMeta to get new title, cause meta title will trigger rerender, but the second time can not remove title
|
||||||
|
const { title: metaTitle } = page.meta;
|
||||||
|
const title = metaTitle
|
||||||
|
? metaTitle
|
||||||
|
: isFirstPage
|
||||||
|
? 'Welcome to AFFiNE Alpha "Abbey Wood"'
|
||||||
|
: '';
|
||||||
|
workspace?.setPageMeta(page.id, { title });
|
||||||
|
|
||||||
|
const pageId = page.addBlock({
|
||||||
|
flavour: 'affine:page',
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
page.addBlock({ flavour: 'affine:surface' });
|
||||||
|
const frameId = page.addBlock({ flavour: 'affine:frame' }, pageId);
|
||||||
|
page.addBlock({ flavour: 'affine:frame' }, pageId);
|
||||||
|
// If this is a first page in workspace, init an introduction markdown
|
||||||
|
if (isFirstPage) {
|
||||||
|
editor.clipboard.importMarkdown(exampleMarkdown, `${frameId}`);
|
||||||
|
workspace.setPageMeta(page.id, { title });
|
||||||
|
page.resetHistory();
|
||||||
|
}
|
||||||
|
page.resetHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditor(editor);
|
||||||
|
document.title = page?.meta.title || 'Untitled';
|
||||||
|
return ret;
|
||||||
|
}, [workspace, page, setEditor]);
|
||||||
|
|
||||||
|
return <StyledEditorContainer ref={editorContainer} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Editor;
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
StyledTitleWrapper,
|
StyledTitleWrapper,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { Content } from '@/ui/layout';
|
import { Content } from '@/ui/layout';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import EditorModeSwitch from '@/components/editor-mode-switch';
|
import EditorModeSwitch from '@/components/editor-mode-switch';
|
||||||
import QuickSearchButton from './QuickSearchButton';
|
import QuickSearchButton from './QuickSearchButton';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CloudUnsyncedIcon, CloudInsyncIcon } from '@blocksuite/icons';
|
import { CloudUnsyncedIcon, CloudInsyncIcon } from '@blocksuite/icons';
|
||||||
import { useModal } from '@/providers/GlobalModalProvider';
|
import { useModal } from '@/providers/GlobalModalProvider';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { IconButton } from '@/ui/button';
|
import { IconButton } from '@/ui/button';
|
||||||
|
|
||||||
export const SyncUser = () => {
|
export const SyncUser = () => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Button } from '@/ui/button';
|
|||||||
import { Wrapper, Content } from '@/ui/layout';
|
import { Wrapper, Content } from '@/ui/layout';
|
||||||
import Loading from '@/components/loading';
|
import Loading from '@/components/loading';
|
||||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
// import { Tooltip } from '@/ui/tooltip';
|
// import { Tooltip } from '@/ui/tooltip';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { getDataCenter } from '@affine/datacenter';
|
// import { getDataCenter } from '@affine/datacenter';
|
||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { useModal } from '@/providers/GlobalModalProvider';
|
// import { useModal } from '@/providers/GlobalModalProvider';
|
||||||
import { GoogleIcon, StayLogOutIcon } from './Icons';
|
import { GoogleIcon, StayLogOutIcon } from './Icons';
|
||||||
|
|
||||||
export const GoogleLoginButton = () => {
|
export const GoogleLoginButton = () => {
|
||||||
const { triggerLoginModal } = useModal();
|
// const { triggerLoginModal } = useModal();
|
||||||
return (
|
return (
|
||||||
<StyledGoogleButton
|
<StyledGoogleButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import DateCell from '@/components/page-list/DateCell';
|
|||||||
import { IconButton } from '@/ui/button';
|
import { IconButton } from '@/ui/button';
|
||||||
import { Tooltip } from '@/ui/tooltip';
|
import { Tooltip } from '@/ui/tooltip';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { toast } from '@/ui/toast';
|
import { toast } from '@/ui/toast';
|
||||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||||
import { useTheme } from '@/providers/ThemeProvider';
|
import { useTheme } from '@/providers/ThemeProvider';
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ export const Input = (props: {
|
|||||||
const [isComposition, setIsComposition] = useState(false);
|
const [isComposition, setIsComposition] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const { currentWorkspaceId, workspacesMeta, currentWorkspace } =
|
const { currentWorkspaceId, workspaceList, currentWorkspace } = useAppState();
|
||||||
useAppState();
|
const isPublish = workspaceList.find(
|
||||||
const isPublic = workspacesMeta.find(
|
|
||||||
meta => String(meta.id) === String(currentWorkspaceId)
|
meta => String(meta.id) === String(currentWorkspaceId)
|
||||||
)?.public;
|
)?.isPublish;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
inputRef.current?.addEventListener(
|
inputRef.current?.addEventListener(
|
||||||
'blur',
|
'blur',
|
||||||
@@ -80,7 +79,7 @@ export const Input = (props: {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placeholder={
|
placeholder={
|
||||||
isPublic
|
isPublish
|
||||||
? `Search in ${currentWorkspace?.meta.name}`
|
? `Search in ${currentWorkspace?.meta.name}`
|
||||||
: 'Quick Search...'
|
: 'Quick Search...'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,14 @@ const isMac = () => {
|
|||||||
return getUaHelper().isMacOs;
|
return getUaHelper().isMacOs;
|
||||||
};
|
};
|
||||||
export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
|
export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
|
||||||
|
const { currentMetaWorkSpace } = useAppState();
|
||||||
|
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [showCreatePage, setShowCreatePage] = useState(true);
|
const [showCreatePage, setShowCreatePage] = useState(true);
|
||||||
const { triggerQuickSearchModal } = useModal();
|
const { triggerQuickSearchModal } = useModal();
|
||||||
const { currentWorkspaceId, workspacesMeta } = useAppState();
|
|
||||||
|
|
||||||
const currentWorkspace = workspacesMeta.find(
|
const isPublish = currentMetaWorkSpace?.isPublish;
|
||||||
meta => String(meta.id) === String(currentWorkspaceId)
|
|
||||||
);
|
|
||||||
const isPublic = currentWorkspace?.public;
|
|
||||||
|
|
||||||
// Add ‘⌘+K’ shortcut keys as switches
|
// Add ‘⌘+K’ shortcut keys as switches
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -98,7 +96,7 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
|
|||||||
setShowCreatePage={setShowCreatePage}
|
setShowCreatePage={setShowCreatePage}
|
||||||
/>
|
/>
|
||||||
</StyledContent>
|
</StyledContent>
|
||||||
{isPublic ? (
|
{isPublish ? (
|
||||||
<></>
|
<></>
|
||||||
) : showCreatePage ? (
|
) : showCreatePage ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,15 +1,33 @@
|
|||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { stringToColour } from '@/utils';
|
import { stringToColour } from '@/utils';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
interface IWorkspaceAvatar {
|
interface IWorkspaceAvatar {
|
||||||
size: number;
|
size: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
avatar: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceAvatar = (props: IWorkspaceAvatar) => {
|
export const WorkspaceAvatar = (props: IWorkspaceAvatar) => {
|
||||||
const size = props.size || 20;
|
const size = props.size || 20;
|
||||||
const sizeStr = size + 'px';
|
const sizeStr = size + 'px';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{props.avatar ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: sizeStr,
|
||||||
|
height: sizeStr,
|
||||||
|
border: '1px solid #fff',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: '50%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={props.avatar} alt="" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: sizeStr,
|
width: sizeStr,
|
||||||
@@ -25,6 +43,7 @@ export const WorkspaceAvatar = (props: IWorkspaceAvatar) => {
|
|||||||
>
|
>
|
||||||
{(props.name || 'AFFiNE').substring(0, 1)}
|
{(props.name || 'AFFiNE').substring(0, 1)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,27 +10,24 @@ import {
|
|||||||
UsersIcon,
|
UsersIcon,
|
||||||
AddIcon,
|
AddIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
// import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
import { toast } from '@/ui/toast';
|
import { toast } from '@/ui/toast';
|
||||||
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { useUserHelper } from '@/hooks/use-user-helper';
|
||||||
|
|
||||||
interface LoginModalProps {
|
interface WorkspaceModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => {
|
export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
|
||||||
const [createWorkspaceOpen, setCreateWorkspaceOpen] = useState(false);
|
const [createWorkspaceOpen, setCreateWorkspaceOpen] = useState(false);
|
||||||
const { confirm } = useConfirm();
|
// const { confirm } = useConfirm();
|
||||||
const {
|
const { workspaceList, currentWorkspace } = useAppState();
|
||||||
user,
|
const { login, user } = useUserHelper();
|
||||||
login,
|
const router = useRouter();
|
||||||
workspaceMetaList,
|
|
||||||
setActiveWorkspace,
|
|
||||||
updateWorkspaceMeta,
|
|
||||||
} = useTemporaryHelper();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Modal open={open} onClose={onClose}>
|
<Modal open={open} onClose={onClose}>
|
||||||
@@ -51,13 +48,14 @@ export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => {
|
|||||||
</Header>
|
</Header>
|
||||||
<Content>
|
<Content>
|
||||||
<WorkspaceList>
|
<WorkspaceList>
|
||||||
{workspaceMetaList.map((item, index) => {
|
{workspaceList.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<WorkspaceItem
|
<WorkspaceItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveWorkspace(item);
|
router.replace(`/workspace/${item.id}`);
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
|
active={item.id === currentWorkspace.room}
|
||||||
key={index}
|
key={index}
|
||||||
>
|
>
|
||||||
<span style={{ width: '100px' }}>
|
<span style={{ width: '100px' }}>
|
||||||
@@ -69,7 +67,11 @@ export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => {
|
|||||||
marginRight: '10px',
|
marginRight: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar size={50} name={item.name} />
|
<WorkspaceAvatar
|
||||||
|
size={50}
|
||||||
|
name={item.name}
|
||||||
|
avatar={item.avatar ?? ''}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@@ -93,10 +95,10 @@ export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => {
|
|||||||
top: '20px',
|
top: '20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(item.type === 'local' || !item.type) && (
|
{(item.provider === 'local' || !item.provider) && (
|
||||||
<CloudUnsyncedIcon fontSize={24} />
|
<CloudUnsyncedIcon fontSize={24} />
|
||||||
)}
|
)}
|
||||||
{item.type === 'cloud' && (
|
{item.provider === 'affine' && (
|
||||||
<CloudInsyncIcon fontSize={24} />
|
<CloudInsyncIcon fontSize={24} />
|
||||||
)}
|
)}
|
||||||
{item.isPublish && <UsersIcon fontSize={24} />}
|
{item.isPublish && <UsersIcon fontSize={24} />}
|
||||||
@@ -154,26 +156,26 @@ export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => {
|
|||||||
</Footer>
|
</Footer>
|
||||||
<CreateWorkspaceModal
|
<CreateWorkspaceModal
|
||||||
open={createWorkspaceOpen}
|
open={createWorkspaceOpen}
|
||||||
onClose={({ workspaceId }) => {
|
onClose={() => {
|
||||||
setCreateWorkspaceOpen(false);
|
setCreateWorkspaceOpen(false);
|
||||||
onClose();
|
onClose();
|
||||||
confirm({
|
// confirm({
|
||||||
title: 'Enable AFFiNE Cloud?',
|
// title: 'Enable AFFiNE Cloud?',
|
||||||
content: `If enabled, the data in this workspace will be backed up and synchronized via AFFiNE Cloud.`,
|
// content: `If enabled, the data in this workspace will be backed up and synchronized via AFFiNE Cloud.`,
|
||||||
confirmText: user ? 'Enable' : 'Sign in and Enable',
|
// confirmText: user ? 'Enable' : 'Sign in and Enable',
|
||||||
cancelText: 'Skip',
|
// cancelText: 'Skip',
|
||||||
}).then(confirm => {
|
// }).then(confirm => {
|
||||||
if (confirm) {
|
// if (confirm) {
|
||||||
if (user) {
|
// if (user) {
|
||||||
workspaceId &&
|
// // workspaceId &&
|
||||||
updateWorkspaceMeta(workspaceId, { isPublish: true });
|
// // updateWorkspaceMeta(workspaceId, { isPublish: true });
|
||||||
} else {
|
// } else {
|
||||||
login();
|
// // login();
|
||||||
workspaceId &&
|
// // workspaceId &&
|
||||||
updateWorkspaceMeta(workspaceId, { isPublish: true });
|
// // updateWorkspaceMeta(workspaceId, { isPublish: true });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}}
|
}}
|
||||||
></CreateWorkspaceModal>
|
></CreateWorkspaceModal>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
@@ -218,11 +220,17 @@ const WorkspaceList = styled('div')({
|
|||||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||||
});
|
});
|
||||||
|
|
||||||
const WorkspaceItem = styled('div')({
|
export const WorkspaceItem = styled.div<{
|
||||||
|
active: boolean;
|
||||||
|
}>(({ theme, active }) => {
|
||||||
|
const backgroundColor = active ? theme.colors.hoverBackground : 'transparent';
|
||||||
|
return {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
padding: '8px',
|
padding: '8px',
|
||||||
border: '1px solid #eee',
|
border: '1px solid #eee',
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
':hover': {
|
':hover': {
|
||||||
background: '#eee',
|
background: theme.colors.hoverBackground,
|
||||||
},
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Workspace } from '@/hooks/mock-data/mock';
|
|
||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
|
|
||||||
export const ExportPageTitleContainer = styled('div')(() => {
|
export const ExportPageTitleContainer = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
@@ -9,7 +9,7 @@ export const ExportPageTitleContainer = styled('div')(() => {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
export const ExportPage = ({ workspace }: { workspace: Workspace }) => {
|
export const ExportPage = ({ workspace }: { workspace: WorkspaceInfo }) => {
|
||||||
return (
|
return (
|
||||||
<ExportPageTitleContainer>
|
<ExportPageTitleContainer>
|
||||||
Export Workspace{' '}
|
Export Workspace{' '}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
StyledMemberRoleContainer,
|
StyledMemberRoleContainer,
|
||||||
StyledMemberTitleContainer,
|
StyledMemberTitleContainer,
|
||||||
StyledMoreVerticalButton,
|
StyledMoreVerticalButton,
|
||||||
|
StyledPublishExplanation,
|
||||||
} from './style';
|
} from './style';
|
||||||
import { MoreVerticalIcon, EmailIcon, TrashIcon } from '@blocksuite/icons';
|
import { MoreVerticalIcon, EmailIcon, TrashIcon } from '@blocksuite/icons';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -17,20 +18,24 @@ import { Button, IconButton } from '@/ui/button';
|
|||||||
import { InviteMembers } from '../invite-members/index';
|
import { InviteMembers } from '../invite-members/index';
|
||||||
import { Menu, MenuItem } from '@/ui/menu';
|
import { Menu, MenuItem } from '@/ui/menu';
|
||||||
import { Empty } from '@/ui/empty';
|
import { Empty } from '@/ui/empty';
|
||||||
import {
|
// import {
|
||||||
deleteMember,
|
// deleteMember,
|
||||||
getMembers,
|
// getMembers,
|
||||||
User,
|
// User,
|
||||||
Workspace,
|
// Workspace,
|
||||||
} from '@/hooks/mock-data/mock';
|
// } from '@/hooks/mock-data/mock';
|
||||||
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
||||||
import { StyledMemberWarp } from './general/style';
|
import { StyledMemberWarp } from './general/style';
|
||||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
|
|
||||||
// import { useAppState } from '@/providers/app-state-provider';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
export const MembersPage = ({ workspace }: { workspace: WorkspaceInfo }) => {
|
||||||
const [isInviteModalShow, setIsInviteModalShow] = useState(false);
|
const [isInviteModalShow, setIsInviteModalShow] = useState(false);
|
||||||
const [members, setMembers] = useState<User[]>([]);
|
const [members, setMembers] = useState<[{ name: string; email: string }]>([
|
||||||
|
{ name: 'affine', email: 'tttt' },
|
||||||
|
]);
|
||||||
|
console.log('setMembers: ', setMembers);
|
||||||
const { user, login, updateWorkspaceMeta } = useTemporaryHelper();
|
const { user, login, updateWorkspaceMeta } = useTemporaryHelper();
|
||||||
const { confirm } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
// const refreshMembers = useCallback(() => {
|
// const refreshMembers = useCallback(() => {
|
||||||
@@ -48,8 +53,9 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
// });
|
// });
|
||||||
// }, [workspace.id]);
|
// }, [workspace.id]);
|
||||||
const setMembersList = () => {
|
const setMembersList = () => {
|
||||||
const members = getMembers(workspace.id);
|
// setMembers([]);
|
||||||
members && setMembers(members);
|
// const members = getMembers(workspace.id);
|
||||||
|
// members && setMembers(members);
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMembersList();
|
setMembersList();
|
||||||
@@ -58,7 +64,7 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{workspace.type === 'cloud' ? (
|
{workspace.provider === 'cloud' ? (
|
||||||
<>
|
<>
|
||||||
<StyledMemberTitleContainer>
|
<StyledMemberTitleContainer>
|
||||||
<StyledMemberNameContainer>
|
<StyledMemberNameContainer>
|
||||||
@@ -67,13 +73,6 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
<StyledMemberRoleContainer>Access level</StyledMemberRoleContainer>
|
<StyledMemberRoleContainer>Access level</StyledMemberRoleContainer>
|
||||||
</StyledMemberTitleContainer>
|
</StyledMemberTitleContainer>
|
||||||
<StyledMemberListContainer>
|
<StyledMemberListContainer>
|
||||||
{members.length === 0 && (
|
|
||||||
<Empty
|
|
||||||
width={648}
|
|
||||||
sx={{ marginTop: '60px' }}
|
|
||||||
height={300}
|
|
||||||
></Empty>
|
|
||||||
)}
|
|
||||||
{members.length ? (
|
{members.length ? (
|
||||||
members.map((member, index) => {
|
members.map((member, index) => {
|
||||||
return (
|
return (
|
||||||
@@ -103,7 +102,7 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
<>
|
<>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteMember(workspace.id, 0);
|
// deleteMember(workspace.id, 0);
|
||||||
setMembersList();
|
setMembersList();
|
||||||
// confirm({
|
// confirm({
|
||||||
// title: 'Delete Member?',
|
// title: 'Delete Member?',
|
||||||
@@ -142,7 +141,11 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<Empty
|
||||||
|
width={648}
|
||||||
|
sx={{ marginTop: '60px' }}
|
||||||
|
height={300}
|
||||||
|
></Empty>
|
||||||
)}
|
)}
|
||||||
</StyledMemberListContainer>
|
</StyledMemberListContainer>
|
||||||
<StyledMemberButtonContainer>
|
<StyledMemberButtonContainer>
|
||||||
@@ -171,10 +174,8 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<StyledMemberWarp>
|
<StyledMemberWarp>
|
||||||
<div style={{ flex: 1 }}>
|
<>Collaborating with other members requires AFFiNE Cloud service.</>
|
||||||
Collaborating with other members requires AFFiNE Cloud service.
|
<StyledPublishExplanation>
|
||||||
</div>
|
|
||||||
<div style={{ height: '40px' }}>
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@@ -198,7 +199,7 @@ export const MembersPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
>
|
>
|
||||||
Enable AFFiNE Cloud
|
Enable AFFiNE Cloud
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</StyledPublishExplanation>
|
||||||
</StyledMemberWarp>
|
</StyledMemberWarp>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,20 +9,20 @@ import {
|
|||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import Input from '@/ui/input';
|
import Input from '@/ui/input';
|
||||||
import { toast } from '@/ui/toast';
|
import { toast } from '@/ui/toast';
|
||||||
import { Workspace } from '@/hooks/mock-data/mock';
|
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
|
||||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
|
// import { useAppState } from '@/providers/app-state-provider3';
|
||||||
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
|
|
||||||
export const PublishPage = ({ workspace }: { workspace: Workspace }) => {
|
export const PublishPage = ({ workspace }: { workspace: WorkspaceInfo }) => {
|
||||||
console.log('workspace: ', workspace);
|
|
||||||
const shareUrl =
|
const shareUrl =
|
||||||
window.location.host + '/workspace/' + workspace.id + '?share=true';
|
window.location.host + '/workspace/' + workspace.id + '?share=true';
|
||||||
|
const { publishWorkspace } = useWorkspaceHelper();
|
||||||
|
|
||||||
const { login, updateWorkspaceMeta, user } = useTemporaryHelper();
|
|
||||||
const { confirm } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
|
|
||||||
const togglePublic = (flag: boolean) => {
|
const togglePublic = (flag: boolean) => {
|
||||||
updateWorkspaceMeta(workspace.id, { isPublish: flag });
|
workspace.id && publishWorkspace(workspace?.id, flag);
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyUrl = () => {
|
const copyUrl = () => {
|
||||||
@@ -34,22 +34,23 @@ export const PublishPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
confirm({
|
confirm({
|
||||||
title: 'Enable AFFiNE Cloud?',
|
title: 'Enable AFFiNE Cloud?',
|
||||||
content: `If enabled, the data in this workspace will be backed up and synchronized via AFFiNE Cloud.`,
|
content: `If enabled, the data in this workspace will be backed up and synchronized via AFFiNE Cloud.`,
|
||||||
confirmText: user ? 'Enable' : 'Sign in and Enable',
|
confirmText:
|
||||||
|
workspace.provider === 'local' ? 'Enable' : 'Sign in and Enable',
|
||||||
cancelText: 'Skip',
|
cancelText: 'Skip',
|
||||||
}).then(confirm => {
|
}).then(confirm => {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
if (user) {
|
// if (user) {
|
||||||
updateWorkspaceMeta(workspace.id, { type: 'cloud' });
|
// updateWorkspaceMeta(workspace.id, { type: 'cloud' });
|
||||||
} else {
|
// } else {
|
||||||
login();
|
// login();
|
||||||
updateWorkspaceMeta(workspace.id, { type: 'cloud' });
|
// updateWorkspaceMeta(workspace.id, { type: 'cloud' });
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{workspace.type === 'cloud' ? (
|
{workspace.provider === 'cloud' ? (
|
||||||
<div>
|
<div>
|
||||||
<StyledPublishContent>
|
<StyledPublishContent>
|
||||||
{workspace?.isPublish ? (
|
{workspace?.isPublish ? (
|
||||||
|
|||||||
@@ -6,28 +6,48 @@ import {
|
|||||||
import { DownloadIcon } from '@blocksuite/icons';
|
import { DownloadIcon } from '@blocksuite/icons';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { Menu, MenuItem } from '@/ui/menu';
|
import { Menu, MenuItem } from '@/ui/menu';
|
||||||
import { deleteMember, Workspace } from '@/hooks/mock-data/mock';
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
export const SyncPage = ({ workspace }: { workspace: Workspace }) => {
|
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
const { currentWorkspace, updateWorkspaceMeta } = useTemporaryHelper();
|
import { toast } from '@/ui/toast';
|
||||||
|
import { useUserHelper } from '@/hooks/use-user-helper';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
export const SyncPage = ({ workspace }: { workspace: WorkspaceInfo }) => {
|
||||||
|
// console.log('workspace: ', workspace);
|
||||||
|
const { enableWorkspace } = useWorkspaceHelper();
|
||||||
|
const { currentWorkspace } = useAppState();
|
||||||
|
const { confirm } = useConfirm();
|
||||||
|
const { user, login } = useUserHelper();
|
||||||
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<StyledPublishContent>
|
<StyledPublishContent>
|
||||||
{currentWorkspace?.type === 'local' ? (
|
{workspace?.provider === 'local' ? (
|
||||||
<>
|
<>
|
||||||
<StyledPublishExplanation>
|
<StyledPublishExplanation>
|
||||||
{currentWorkspace.name} is Local Workspace. All data is stored on
|
{workspace.name ?? 'Affine'} is Local Workspace. All data is
|
||||||
the current device. You can enable AFFiNE Cloud for this workspace
|
stored on the current device. You can enable AFFiNE Cloud for this
|
||||||
to keep data in sync with the cloud.
|
workspace to keep data in sync with the cloud.
|
||||||
</StyledPublishExplanation>
|
</StyledPublishExplanation>
|
||||||
|
|
||||||
<StyledPublishCopyContainer>
|
<StyledPublishCopyContainer>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateWorkspaceMeta(currentWorkspace.id, {
|
confirm({
|
||||||
type: 'cloud',
|
title: 'Enable AFFiNE Cloud?',
|
||||||
|
content: `If enabled, the data in this workspace will be backed up and synchronized via AFFiNE Cloud.`,
|
||||||
|
confirmText: user ? 'Enable' : 'Sign in and Enable',
|
||||||
|
cancelText: 'Skip',
|
||||||
|
}).then(async confirm => {
|
||||||
|
if (confirm) {
|
||||||
|
// if (user) {
|
||||||
|
// await login();
|
||||||
|
// }
|
||||||
|
const id = await enableWorkspace(currentWorkspace);
|
||||||
|
router.push(`/workspace/${id}`);
|
||||||
|
toast('Enabled success');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -40,8 +60,8 @@ export const SyncPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<StyledPublishExplanation>
|
<StyledPublishExplanation>
|
||||||
<code>{currentWorkspace && currentWorkspace.name}</code> is Cloud
|
<code>{workspace.name ?? 'Affine'}</code> is Cloud Workspace. All
|
||||||
Workspace. All data will be synchronized and saved to the AFFiNE
|
data will be synchronized and saved to the AFFiNE
|
||||||
</StyledPublishExplanation>
|
</StyledPublishExplanation>
|
||||||
<StyledPublishCopyContainer>
|
<StyledPublishCopyContainer>
|
||||||
<Menu
|
<Menu
|
||||||
@@ -49,7 +69,7 @@ export const SyncPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
<>
|
<>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteMember(workspace.id, 0);
|
// deleteMember(workspace.id, 0);
|
||||||
}}
|
}}
|
||||||
icon={<DownloadIcon />}
|
icon={<DownloadIcon />}
|
||||||
>
|
>
|
||||||
@@ -57,7 +77,7 @@ export const SyncPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteMember(workspace.id, 0);
|
// deleteMember(workspace.id, 0);
|
||||||
}}
|
}}
|
||||||
icon={<DownloadIcon />}
|
icon={<DownloadIcon />}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
import Modal, { ModalCloseButton } from '@/ui/modal';
|
|
||||||
import {
|
|
||||||
StyledSettingContainer,
|
|
||||||
StyledSettingContent,
|
|
||||||
StyledSettingSidebar,
|
|
||||||
StyledSettingSidebarHeader,
|
|
||||||
StyledSettingTabContainer,
|
|
||||||
StyledSettingTagIconContainer,
|
|
||||||
WorkspaceSettingTagItem,
|
|
||||||
} from './style';
|
|
||||||
import {
|
|
||||||
EditIcon,
|
|
||||||
UsersIcon,
|
|
||||||
PublishIcon,
|
|
||||||
CloudInsyncIcon,
|
|
||||||
} from '@blocksuite/icons';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { GeneralPage } from './general';
|
|
||||||
import { MembersPage } from './MembersPage';
|
|
||||||
import { PublishPage } from './PublishPage';
|
|
||||||
import { ExportPage } from './ExportPage';
|
|
||||||
import { SyncPage } from './SyncPage';
|
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
|
||||||
|
|
||||||
enum ActiveTab {
|
|
||||||
'general' = 'general',
|
|
||||||
'members' = 'members',
|
|
||||||
'publish' = 'publish',
|
|
||||||
'sync' = 'sync',
|
|
||||||
'export' = 'export',
|
|
||||||
}
|
|
||||||
|
|
||||||
type SettingTabProps = {
|
|
||||||
activeTab: ActiveTab;
|
|
||||||
onTabChange?: (tab: ActiveTab) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
type WorkspaceSettingProps = {
|
|
||||||
isShow: boolean;
|
|
||||||
onClose?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const WorkspaceSettingTab = ({ activeTab, onTabChange }: SettingTabProps) => {
|
|
||||||
return (
|
|
||||||
<StyledSettingTabContainer>
|
|
||||||
<WorkspaceSettingTagItem
|
|
||||||
isActive={activeTab === ActiveTab.general}
|
|
||||||
onClick={() => {
|
|
||||||
onTabChange && onTabChange(ActiveTab.general);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StyledSettingTagIconContainer>
|
|
||||||
<EditIcon />
|
|
||||||
</StyledSettingTagIconContainer>
|
|
||||||
General
|
|
||||||
</WorkspaceSettingTagItem>
|
|
||||||
|
|
||||||
<WorkspaceSettingTagItem
|
|
||||||
isActive={activeTab === ActiveTab.sync}
|
|
||||||
onClick={() => {
|
|
||||||
onTabChange && onTabChange(ActiveTab.sync);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StyledSettingTagIconContainer>
|
|
||||||
<CloudInsyncIcon />
|
|
||||||
</StyledSettingTagIconContainer>
|
|
||||||
Sync
|
|
||||||
</WorkspaceSettingTagItem>
|
|
||||||
|
|
||||||
<WorkspaceSettingTagItem
|
|
||||||
isActive={activeTab === ActiveTab.members}
|
|
||||||
onClick={() => {
|
|
||||||
onTabChange && onTabChange(ActiveTab.members);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StyledSettingTagIconContainer>
|
|
||||||
<UsersIcon />
|
|
||||||
</StyledSettingTagIconContainer>
|
|
||||||
Collaboration
|
|
||||||
</WorkspaceSettingTagItem>
|
|
||||||
<WorkspaceSettingTagItem
|
|
||||||
isActive={activeTab === ActiveTab.publish}
|
|
||||||
onClick={() => {
|
|
||||||
onTabChange && onTabChange(ActiveTab.publish);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StyledSettingTagIconContainer>
|
|
||||||
<PublishIcon />
|
|
||||||
</StyledSettingTagIconContainer>
|
|
||||||
Publish
|
|
||||||
</WorkspaceSettingTagItem>
|
|
||||||
|
|
||||||
<WorkspaceSettingTagItem
|
|
||||||
isActive={activeTab === ActiveTab.export}
|
|
||||||
onClick={() => {
|
|
||||||
onTabChange && onTabChange(ActiveTab.export);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StyledSettingTagIconContainer>
|
|
||||||
<PublishIcon />
|
|
||||||
</StyledSettingTagIconContainer>
|
|
||||||
Export
|
|
||||||
</WorkspaceSettingTagItem>
|
|
||||||
</StyledSettingTabContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const WorkspaceSetting = ({
|
|
||||||
isShow,
|
|
||||||
onClose,
|
|
||||||
}: WorkspaceSettingProps) => {
|
|
||||||
// const { workspaces } = useAppState();
|
|
||||||
const [activeTab, setActiveTab] = useState<ActiveTab>(ActiveTab.general);
|
|
||||||
const handleTabChange = (tab: ActiveTab) => {
|
|
||||||
setActiveTab(tab);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { currentWorkspace } = useTemporaryHelper();
|
|
||||||
const handleClickClose = () => {
|
|
||||||
onClose && onClose();
|
|
||||||
};
|
|
||||||
const isOwner = true;
|
|
||||||
useEffect(() => {
|
|
||||||
// reset tab when modal is closed
|
|
||||||
if (!isShow) {
|
|
||||||
setActiveTab(ActiveTab.general);
|
|
||||||
}
|
|
||||||
}, [isShow]);
|
|
||||||
return (
|
|
||||||
<Modal open={isShow}>
|
|
||||||
<StyledSettingContainer>
|
|
||||||
<ModalCloseButton onClick={handleClickClose} />
|
|
||||||
{isOwner ? (
|
|
||||||
<StyledSettingSidebar>
|
|
||||||
<StyledSettingSidebarHeader>
|
|
||||||
Workspace Settings
|
|
||||||
</StyledSettingSidebarHeader>
|
|
||||||
<WorkspaceSettingTab
|
|
||||||
activeTab={activeTab}
|
|
||||||
onTabChange={handleTabChange}
|
|
||||||
/>
|
|
||||||
</StyledSettingSidebar>
|
|
||||||
) : null}
|
|
||||||
<StyledSettingContent>
|
|
||||||
{activeTab === ActiveTab.general && currentWorkspace && (
|
|
||||||
<GeneralPage workspace={currentWorkspace} />
|
|
||||||
)}
|
|
||||||
{activeTab === ActiveTab.sync && currentWorkspace && (
|
|
||||||
<SyncPage workspace={currentWorkspace} />
|
|
||||||
)}
|
|
||||||
{activeTab === ActiveTab.members && currentWorkspace && (
|
|
||||||
<MembersPage workspace={currentWorkspace} />
|
|
||||||
)}
|
|
||||||
{activeTab === ActiveTab.publish && currentWorkspace && (
|
|
||||||
<PublishPage workspace={currentWorkspace} />
|
|
||||||
)}
|
|
||||||
{activeTab === ActiveTab.export && currentWorkspace && (
|
|
||||||
<ExportPage workspace={currentWorkspace} />
|
|
||||||
)}
|
|
||||||
</StyledSettingContent>
|
|
||||||
</StyledSettingContainer>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -9,26 +9,21 @@ import { StyledSettingH2 } from '../style';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, TextButton } from '@/ui/button';
|
import { Button, TextButton } from '@/ui/button';
|
||||||
import Input from '@/ui/input';
|
import Input from '@/ui/input';
|
||||||
// import { useAppState } from '@/providers/app-state-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { WorkspaceDelete } from './delete';
|
import { WorkspaceDelete } from './delete';
|
||||||
// import { debounce } from '@/utils';
|
|
||||||
import { WorkspaceLeave } from './leave';
|
import { WorkspaceLeave } from './leave';
|
||||||
import { Upload } from '@/components/file-upload';
|
import { Upload } from '@/components/file-upload';
|
||||||
import { Workspace } from '@/hooks/mock-data/mock';
|
|
||||||
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
export const GeneralPage = ({ workspace }: { workspace: Workspace }) => {
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
// const { refreshWorkspacesMeta } = useAppState();
|
export const GeneralPage = ({ workspace }: { workspace: WorkspaceInfo }) => {
|
||||||
const { updateWorkspaceMeta } = useTemporaryHelper();
|
|
||||||
const [showDelete, setShowDelete] = useState<boolean>(false);
|
const [showDelete, setShowDelete] = useState<boolean>(false);
|
||||||
const [showLeave, setShowLeave] = useState<boolean>(false);
|
const [showLeave, setShowLeave] = useState<boolean>(false);
|
||||||
const [uploading, setUploading] = useState<boolean>(false);
|
const [uploading, setUploading] = useState<boolean>(false);
|
||||||
const [workspaceName, setWorkspaceName] = useState<string>('');
|
const [workspaceName, setWorkspaceName] = useState<string>(workspace.name);
|
||||||
// const debouncedRefreshWorkspacesMeta = debounce(() => {
|
const { currentWorkspace } = useAppState();
|
||||||
// refreshWorkspacesMeta();
|
const { updateWorkspace } = useWorkspaceHelper();
|
||||||
// }, 100);
|
|
||||||
const isOwner = true;
|
const isOwner = true;
|
||||||
|
|
||||||
const handleChangeWorkSpaceName = (newName: string) => {
|
const handleChangeWorkSpaceName = (newName: string) => {
|
||||||
setWorkspaceName(newName);
|
setWorkspaceName(newName);
|
||||||
};
|
};
|
||||||
@@ -46,24 +41,15 @@ export const GeneralPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
setShowLeave(false);
|
setShowLeave(false);
|
||||||
};
|
};
|
||||||
const handleUpdateWorkspaceName = () => {
|
const handleUpdateWorkspaceName = () => {
|
||||||
workspace && updateWorkspaceMeta(workspace.id, { name: workspaceName });
|
console.log('currentWorkspace: ', currentWorkspace);
|
||||||
|
updateWorkspace({ name: workspaceName }, currentWorkspace);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileChange = async (file: File) => {
|
const fileChange = async (file: File) => {
|
||||||
console.log('file: ', file);
|
// console.log('file: ', file);
|
||||||
setUploading(true);
|
// setUploading(true);
|
||||||
// const blob = new Blob([file], { type: file.type });
|
const blob = new Blob([file], { type: file.type });
|
||||||
// const blobId = await getDataCenter()
|
updateWorkspace({ avatarBlob: blob }, currentWorkspace);
|
||||||
// .then(dc => dc.apis.uploadBlob({ blob }))
|
|
||||||
// .finally(() => {
|
|
||||||
// setUploading(false);
|
|
||||||
// });
|
|
||||||
// if (blobId) {
|
|
||||||
// currentWorkspace?.meta.setAvatar(blobId);
|
|
||||||
// // workspaces[workspace.id]?.meta.setAvatar(blobId);
|
|
||||||
// setUploading(false);
|
|
||||||
// debouncedRefreshWorkspacesMeta();
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return workspace ? (
|
return workspace ? (
|
||||||
@@ -73,11 +59,14 @@ export const GeneralPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
float: 'left',
|
float: 'left',
|
||||||
|
marginRight: '20px',
|
||||||
marginRight: '5px',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar size={60} name={workspace.name} />
|
<WorkspaceAvatar
|
||||||
|
size={60}
|
||||||
|
name={workspace.name}
|
||||||
|
avatar={workspace.avatar ?? ''}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Upload
|
<Upload
|
||||||
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
|
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
|
||||||
@@ -101,30 +90,14 @@ export const GeneralPage = ({ workspace }: { workspace: Workspace }) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleUpdateWorkspaceName();
|
handleUpdateWorkspaceName();
|
||||||
}}
|
}}
|
||||||
style={{ marginLeft: '10px' }}
|
style={{ marginLeft: '0px' }}
|
||||||
>
|
>
|
||||||
✔️
|
✔️
|
||||||
</TextButton>
|
</TextButton>
|
||||||
</StyledSettingInputContainer>
|
</StyledSettingInputContainer>
|
||||||
{/* {userInfo ? (
|
|
||||||
<div>
|
|
||||||
<StyledSettingH2 marginTop={20}>Workspace Owner</StyledSettingH2>
|
|
||||||
<StyledSettingInputContainer>
|
|
||||||
<Input
|
|
||||||
width={327}
|
|
||||||
disabled
|
|
||||||
value={userInfo?.name}
|
|
||||||
placeholder="Workspace Owner"
|
|
||||||
></Input>
|
|
||||||
</StyledSettingInputContainer>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
<StyledSettingH2 marginTop={20}>Workspace Type</StyledSettingH2>
|
<StyledSettingH2 marginTop={20}>Workspace Type</StyledSettingH2>
|
||||||
<StyledSettingInputContainer>
|
<StyledSettingInputContainer>
|
||||||
<code>{workspace.type} </code>
|
<code>{workspace.provider} </code>
|
||||||
</StyledSettingInputContainer>
|
</StyledSettingInputContainer>
|
||||||
<StyledDeleteButtonContainer>
|
<StyledDeleteButtonContainer>
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ import { useRouter } from 'next/router';
|
|||||||
import {
|
import {
|
||||||
deleteWorkspace,
|
deleteWorkspace,
|
||||||
getWorkspaces,
|
getWorkspaces,
|
||||||
Workspace,
|
// Workspace,
|
||||||
} from '@/hooks/mock-data/mock';
|
} from '@/hooks/mock-data/mock';
|
||||||
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
|
|
||||||
interface WorkspaceDeleteProps {
|
interface WorkspaceDeleteProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
workspace: Workspace;
|
workspace: WorkspaceInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceDelete = ({
|
export const WorkspaceDelete = ({
|
||||||
@@ -55,7 +56,7 @@ export const WorkspaceDelete = ({
|
|||||||
<StyledModalWrapper>
|
<StyledModalWrapper>
|
||||||
<ModalCloseButton onClick={onClose} />
|
<ModalCloseButton onClick={onClose} />
|
||||||
<StyledModalHeader>Delete Workspace</StyledModalHeader>
|
<StyledModalHeader>Delete Workspace</StyledModalHeader>
|
||||||
{workspace.type === 'local' ? (
|
{workspace.provider === 'local' ? (
|
||||||
<StyledTextContent>
|
<StyledTextContent>
|
||||||
Deleting (
|
Deleting (
|
||||||
<StyledWorkspaceName>{workspace.name}</StyledWorkspaceName>) cannot
|
<StyledWorkspaceName>{workspace.name}</StyledWorkspaceName>) cannot
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './Delete';
|
export { WorkspaceDelete } from './Delete';
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './General';
|
export { GeneralPage } from './General';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
import { ModalCloseButton } from '@/ui/modal';
|
import { ModalCloseButton } from '@/ui/modal';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
// import { getDataCenter } from '@affine/datacenter';
|
// import { getDataCenter } from '@affine/datacenter';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
|
||||||
interface WorkspaceDeleteProps {
|
interface WorkspaceDeleteProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -22,8 +22,9 @@ export const WorkspaceLeave = ({
|
|||||||
onClose,
|
onClose,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
}: WorkspaceDeleteProps) => {
|
}: WorkspaceDeleteProps) => {
|
||||||
|
console.log('workspaceId: ', workspaceId);
|
||||||
// const router = useRouter();
|
// const router = useRouter();
|
||||||
const { refreshWorkspacesMeta } = useAppState();
|
// const { refreshWorkspacesMeta } = useAppState();
|
||||||
const handleLeave = async () => {
|
const handleLeave = async () => {
|
||||||
// const dc = await getDataCenter();
|
// const dc = await getDataCenter();
|
||||||
// await dc.apis.leaveWorkspace({ id: workspaceId });
|
// await dc.apis.leaveWorkspace({ id: workspaceId });
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './Leave';
|
export { WorkspaceLeave } from './Leave';
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ export const StyledSettingAvatar = styled(MuiAvatar)(() => {
|
|||||||
export const StyledMemberWarp = styled('div')(() => {
|
export const StyledMemberWarp = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
height: '500px',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: '60px 0',
|
padding: '40px 0',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
export * from './WorkspaceSetting';
|
export * from './general';
|
||||||
|
export * from './ExportPage';
|
||||||
|
export * from './MembersPage';
|
||||||
|
export * from './SyncPage';
|
||||||
|
export * from './PublishPage';
|
||||||
|
|||||||
@@ -4,22 +4,17 @@ import MuiAvatar from '@mui/material/Avatar';
|
|||||||
|
|
||||||
export const StyledSettingContainer = styled('div')(({ theme }) => {
|
export const StyledSettingContainer = styled('div')(({ theme }) => {
|
||||||
return {
|
return {
|
||||||
position: 'relative',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
padding: '0px',
|
padding: '50px',
|
||||||
width: '961px',
|
flexDirection: 'column',
|
||||||
background: theme.colors.popoverBackground,
|
height: '100vh',
|
||||||
borderRadius: '12px',
|
overflow: 'auto',
|
||||||
overflow: 'hidden',
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const StyledSettingSidebar = styled('div')(({ theme }) => {
|
export const StyledSettingSidebar = styled('div')(({ theme }) => {
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
width: '212px',
|
|
||||||
height: '620px',
|
|
||||||
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
flexGrow: 0,
|
flexGrow: 0,
|
||||||
};
|
};
|
||||||
@@ -29,7 +24,6 @@ export const StyledSettingSidebar = styled('div')(({ theme }) => {
|
|||||||
export const StyledSettingContent = styled('div')(() => {
|
export const StyledSettingContent = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
paddingLeft: '48px',
|
paddingLeft: '48px',
|
||||||
height: '620px',
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +31,6 @@ export const StyledSetting = styled('div')(({ theme }) => {
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
width: '236px',
|
width: '236px',
|
||||||
height: '620px',
|
|
||||||
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
|
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -49,7 +42,7 @@ export const StyledSettingSidebarHeader = styled('div')(() => {
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
lineHeight: '26px',
|
lineHeight: '26px',
|
||||||
textAlign: 'center',
|
textAlign: 'left',
|
||||||
marginTop: '37px',
|
marginTop: '37px',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -59,7 +52,6 @@ export const StyledSettingTabContainer = styled('ul')(() => {
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
|
||||||
marginTop: '25px',
|
marginTop: '25px',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -144,7 +136,6 @@ export const StyledMemberRoleContainer = styled('div')(() => {
|
|||||||
export const StyledMemberListContainer = styled('ul')(() => {
|
export const StyledMemberListContainer = styled('ul')(() => {
|
||||||
return {
|
return {
|
||||||
marginTop: '15px',
|
marginTop: '15px',
|
||||||
height: '432px',
|
|
||||||
overflowY: 'scroll',
|
overflowY: 'scroll',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -228,7 +219,6 @@ export const StyledCopyButtonContainer = styled('div')(() => {
|
|||||||
|
|
||||||
export const StyledPublishContent = styled('div')(() => {
|
export const StyledPublishContent = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
height: '494px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,180 +1,183 @@
|
|||||||
import { InformationIcon, LogOutIcon } from '@blocksuite/icons';
|
// import { InformationIcon, LogOutIcon } from '@blocksuite/icons';
|
||||||
import { styled } from '@/styles';
|
// import { styled } from '@/styles';
|
||||||
import { Divider } from '@/ui/divider';
|
// import { Divider } from '@/ui/divider';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { SelectorPopperContainer } from './styles';
|
// import { SelectorPopperContainer } from './styles';
|
||||||
import {
|
// import {
|
||||||
PrivateWorkspaceItem,
|
// PrivateWorkspaceItem,
|
||||||
WorkspaceItem,
|
// WorkspaceItem,
|
||||||
CreateWorkspaceItem,
|
// CreateWorkspaceItem,
|
||||||
ListItem,
|
// ListItem,
|
||||||
LoginItem,
|
// LoginItem,
|
||||||
} from './WorkspaceItem';
|
// } from './WorkspaceItem';
|
||||||
import { WorkspaceSetting } from '@/components/workspace-setting';
|
// import { WorkspaceSetting } from '@/components/workspace-setting';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
// import { useCallback, useEffect, useState } from 'react';
|
||||||
import { WorkspaceType } from '@affine/datacenter';
|
// import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
import { useModal } from '@/providers/GlobalModalProvider';
|
// import { useModal } from '@/providers/GlobalModalProvider';
|
||||||
|
|
||||||
export type WorkspaceDetails = Record<
|
// export type WorkspaceDetails = Record<
|
||||||
string,
|
// string,
|
||||||
{ memberCount: number; owner: { id: string; name: string } }
|
// { memberCount: number; owner: { id: string; name: string } }
|
||||||
>;
|
// >;
|
||||||
|
|
||||||
type SelectorPopperContentProps = {
|
// type SelectorPopperContentProps = {
|
||||||
isShow: boolean;
|
// isShow: boolean;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export const SelectorPopperContent = ({
|
||||||
|
// isShow,
|
||||||
|
// }: SelectorPopperContentProps) => {
|
||||||
|
// const { user, workspacesMeta, refreshWorkspacesMeta } = useAppState();
|
||||||
|
// const [settingWorkspaceId, setSettingWorkspaceId] = useState<string | null>(
|
||||||
|
// null
|
||||||
|
// );
|
||||||
|
// const [workSpaceDetails, setWorkSpaceDetails] = useState<WorkspaceDetails>(
|
||||||
|
// {}
|
||||||
|
// );
|
||||||
|
// const { triggerContactModal } = useModal();
|
||||||
|
|
||||||
|
// const handleClickSettingWorkspace = (workspaceId: string) => {
|
||||||
|
// setSettingWorkspaceId(workspaceId);
|
||||||
|
// };
|
||||||
|
// const handleCloseWorkSpace = () => {
|
||||||
|
// setSettingWorkspaceId(null);
|
||||||
|
// };
|
||||||
|
// const settingWorkspace = settingWorkspaceId
|
||||||
|
// ? workspacesMeta.find(workspace => workspace.id === settingWorkspaceId)
|
||||||
|
// : undefined;
|
||||||
|
|
||||||
|
// const refreshDetails = useCallback(async () => {
|
||||||
|
// const workspaceDetailList = await Promise.all(
|
||||||
|
// workspacesMeta.map(async ({ id, type }) => {
|
||||||
|
// if (user) {
|
||||||
|
// if (type === WorkspaceType.Private) {
|
||||||
|
// return { id, member_count: 1, owner: user };
|
||||||
|
// } else {
|
||||||
|
// // const dc = await getDataCenter();
|
||||||
|
// // const data = await dc.apis.getWorkspaceDetail({ id });
|
||||||
|
// // return { id, ...data } || { id, member_count: 0, owner: user };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// const workSpaceDetails: WorkspaceDetails = {};
|
||||||
|
// workspaceDetailList.forEach(details => {
|
||||||
|
// if (details) {
|
||||||
|
// const { id, member_count, owner } = details;
|
||||||
|
// if (!owner) return;
|
||||||
|
// workSpaceDetails[id] = {
|
||||||
|
// memberCount: member_count || 1,
|
||||||
|
// owner: {
|
||||||
|
// id: owner.id,
|
||||||
|
// name: owner.name,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// setWorkSpaceDetails(workSpaceDetails);
|
||||||
|
// }, [user, workspacesMeta]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (isShow) {
|
||||||
|
// setSettingWorkspaceId(null);
|
||||||
|
// refreshWorkspacesMeta();
|
||||||
|
// refreshDetails();
|
||||||
|
// }
|
||||||
|
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
// }, [isShow]);
|
||||||
|
|
||||||
|
// return !user ? (
|
||||||
|
// <SelectorPopperContainer placement="bottom-start">
|
||||||
|
// <LoginItem />
|
||||||
|
// <StyledDivider />
|
||||||
|
// <ListItem
|
||||||
|
// icon={<InformationIcon />}
|
||||||
|
// name="About AFFiNE"
|
||||||
|
// onClick={() => triggerContactModal()}
|
||||||
|
// />
|
||||||
|
// </SelectorPopperContainer>
|
||||||
|
// ) : (
|
||||||
|
// <SelectorPopperContainer placement="bottom-start">
|
||||||
|
// <PrivateWorkspaceItem
|
||||||
|
// privateWorkspaceId={
|
||||||
|
// workspacesMeta.find(
|
||||||
|
// workspace => workspace.type === WorkspaceType.Private
|
||||||
|
// )?.id
|
||||||
|
// }
|
||||||
|
// />
|
||||||
|
// <StyledDivider />
|
||||||
|
// <WorkspaceGroupTitle>Workspace</WorkspaceGroupTitle>
|
||||||
|
// <WorkspaceWrapper>
|
||||||
|
// {workspacesMeta.map(workspace => {
|
||||||
|
// return workspace.type !== WorkspaceType.Private ? (
|
||||||
|
// <WorkspaceItem
|
||||||
|
// type={workspace.type}
|
||||||
|
// key={workspace.id}
|
||||||
|
// id={workspace.id}
|
||||||
|
// icon={`loading...`}
|
||||||
|
// onClickSetting={handleClickSettingWorkspace}
|
||||||
|
// name={`loading...`}
|
||||||
|
// memberCount={workSpaceDetails[workspace.id]?.memberCount || 1}
|
||||||
|
// />
|
||||||
|
// ) : null;
|
||||||
|
// })}
|
||||||
|
// </WorkspaceWrapper>
|
||||||
|
// <CreateWorkspaceItem />
|
||||||
|
// {settingWorkspace ? (
|
||||||
|
// <WorkspaceSetting
|
||||||
|
// isShow={false}
|
||||||
|
// onClose={handleCloseWorkSpace}
|
||||||
|
// // workspace={settingWorkspace}
|
||||||
|
// // owner={
|
||||||
|
// // (settingWorkspaceId &&
|
||||||
|
// // workSpaceDetails[settingWorkspaceId]?.owner) || {
|
||||||
|
// // id: user.id,
|
||||||
|
// // name: user.name,
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// />
|
||||||
|
// ) : null}
|
||||||
|
// <StyledDivider />
|
||||||
|
// <ListItem
|
||||||
|
// icon={<InformationIcon />}
|
||||||
|
// name="About AFFiNE"
|
||||||
|
// onClick={() => triggerContactModal()}
|
||||||
|
// />
|
||||||
|
// <ListItem
|
||||||
|
// icon={<LogOutIcon />}
|
||||||
|
// name="Sign out"
|
||||||
|
// onClick={() => {
|
||||||
|
// console.log('Sign out');
|
||||||
|
// // FIXME: remove token from local storage and reload the page
|
||||||
|
// localStorage.removeItem('affine_token');
|
||||||
|
// window.location.reload();
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// </SelectorPopperContainer>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const StyledDivider = styled(Divider)({
|
||||||
|
// margin: '8px 12px',
|
||||||
|
// width: 'calc(100% - 24px)',
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const WorkspaceGroupTitle = styled('div')(({ theme }) => {
|
||||||
|
// return {
|
||||||
|
// color: theme.colors.iconColor,
|
||||||
|
// fontSize: theme.font.sm,
|
||||||
|
// lineHeight: '30px',
|
||||||
|
// height: '30px',
|
||||||
|
// padding: '0 12px',
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const WorkspaceWrapper = styled('div')(() => {
|
||||||
|
// return {
|
||||||
|
// maxHeight: '200px',
|
||||||
|
// overflow: 'auto',
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
export const test = () => {
|
||||||
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectorPopperContent = ({
|
|
||||||
isShow,
|
|
||||||
}: SelectorPopperContentProps) => {
|
|
||||||
const { user, workspacesMeta, refreshWorkspacesMeta } = useAppState();
|
|
||||||
const [settingWorkspaceId, setSettingWorkspaceId] = useState<string | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [workSpaceDetails, setWorkSpaceDetails] = useState<WorkspaceDetails>(
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const { triggerContactModal } = useModal();
|
|
||||||
|
|
||||||
const handleClickSettingWorkspace = (workspaceId: string) => {
|
|
||||||
setSettingWorkspaceId(workspaceId);
|
|
||||||
};
|
|
||||||
const handleCloseWorkSpace = () => {
|
|
||||||
setSettingWorkspaceId(null);
|
|
||||||
};
|
|
||||||
const settingWorkspace = settingWorkspaceId
|
|
||||||
? workspacesMeta.find(workspace => workspace.id === settingWorkspaceId)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const refreshDetails = useCallback(async () => {
|
|
||||||
const workspaceDetailList = await Promise.all(
|
|
||||||
workspacesMeta.map(async ({ id, type }) => {
|
|
||||||
if (user) {
|
|
||||||
if (type === WorkspaceType.Private) {
|
|
||||||
return { id, member_count: 1, owner: user };
|
|
||||||
} else {
|
|
||||||
// const dc = await getDataCenter();
|
|
||||||
// const data = await dc.apis.getWorkspaceDetail({ id });
|
|
||||||
// return { id, ...data } || { id, member_count: 0, owner: user };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const workSpaceDetails: WorkspaceDetails = {};
|
|
||||||
workspaceDetailList.forEach(details => {
|
|
||||||
if (details) {
|
|
||||||
const { id, member_count, owner } = details;
|
|
||||||
if (!owner) return;
|
|
||||||
workSpaceDetails[id] = {
|
|
||||||
memberCount: member_count || 1,
|
|
||||||
owner: {
|
|
||||||
id: owner.id,
|
|
||||||
name: owner.name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setWorkSpaceDetails(workSpaceDetails);
|
|
||||||
}, [user, workspacesMeta]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isShow) {
|
|
||||||
setSettingWorkspaceId(null);
|
|
||||||
refreshWorkspacesMeta();
|
|
||||||
refreshDetails();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isShow]);
|
|
||||||
|
|
||||||
return !user ? (
|
|
||||||
<SelectorPopperContainer placement="bottom-start">
|
|
||||||
<LoginItem />
|
|
||||||
<StyledDivider />
|
|
||||||
<ListItem
|
|
||||||
icon={<InformationIcon />}
|
|
||||||
name="About AFFiNE"
|
|
||||||
onClick={() => triggerContactModal()}
|
|
||||||
/>
|
|
||||||
</SelectorPopperContainer>
|
|
||||||
) : (
|
|
||||||
<SelectorPopperContainer placement="bottom-start">
|
|
||||||
<PrivateWorkspaceItem
|
|
||||||
privateWorkspaceId={
|
|
||||||
workspacesMeta.find(
|
|
||||||
workspace => workspace.type === WorkspaceType.Private
|
|
||||||
)?.id
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<StyledDivider />
|
|
||||||
<WorkspaceGroupTitle>Workspace</WorkspaceGroupTitle>
|
|
||||||
<WorkspaceWrapper>
|
|
||||||
{workspacesMeta.map(workspace => {
|
|
||||||
return workspace.type !== WorkspaceType.Private ? (
|
|
||||||
<WorkspaceItem
|
|
||||||
type={workspace.type}
|
|
||||||
key={workspace.id}
|
|
||||||
id={workspace.id}
|
|
||||||
icon={`loading...`}
|
|
||||||
onClickSetting={handleClickSettingWorkspace}
|
|
||||||
name={`loading...`}
|
|
||||||
memberCount={workSpaceDetails[workspace.id]?.memberCount || 1}
|
|
||||||
/>
|
|
||||||
) : null;
|
|
||||||
})}
|
|
||||||
</WorkspaceWrapper>
|
|
||||||
<CreateWorkspaceItem />
|
|
||||||
{settingWorkspace ? (
|
|
||||||
<WorkspaceSetting
|
|
||||||
isShow={false}
|
|
||||||
onClose={handleCloseWorkSpace}
|
|
||||||
// workspace={settingWorkspace}
|
|
||||||
// owner={
|
|
||||||
// (settingWorkspaceId &&
|
|
||||||
// workSpaceDetails[settingWorkspaceId]?.owner) || {
|
|
||||||
// id: user.id,
|
|
||||||
// name: user.name,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<StyledDivider />
|
|
||||||
<ListItem
|
|
||||||
icon={<InformationIcon />}
|
|
||||||
name="About AFFiNE"
|
|
||||||
onClick={() => triggerContactModal()}
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
icon={<LogOutIcon />}
|
|
||||||
name="Sign out"
|
|
||||||
onClick={() => {
|
|
||||||
console.log('Sign out');
|
|
||||||
// FIXME: remove token from local storage and reload the page
|
|
||||||
localStorage.removeItem('affine_token');
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SelectorPopperContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledDivider = styled(Divider)({
|
|
||||||
margin: '8px 12px',
|
|
||||||
width: 'calc(100% - 24px)',
|
|
||||||
});
|
|
||||||
|
|
||||||
const WorkspaceGroupTitle = styled('div')(({ theme }) => {
|
|
||||||
return {
|
|
||||||
color: theme.colors.iconColor,
|
|
||||||
fontSize: theme.font.sm,
|
|
||||||
lineHeight: '30px',
|
|
||||||
height: '30px',
|
|
||||||
padding: '0 12px',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const WorkspaceWrapper = styled('div')(() => {
|
|
||||||
return {
|
|
||||||
maxHeight: '200px',
|
|
||||||
overflow: 'auto',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getDataCenter } from '@affine/datacenter';
|
// import { getDataCenter } from '@affine/datacenter';
|
||||||
import Modal from '@/ui/modal';
|
import Modal from '@/ui/modal';
|
||||||
import Input from '@/ui/input';
|
import Input from '@/ui/input';
|
||||||
import {
|
import {
|
||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
} from './style';
|
} from './style';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ModalCloseButton } from '@/ui/modal';
|
import { ModalCloseButton } from '@/ui/modal';
|
||||||
import router from 'next/router';
|
// import router from 'next/router';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
|
||||||
interface WorkspaceCreateProps {
|
interface WorkspaceCreateProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -30,7 +30,7 @@ const DefaultHeadImgColors = [
|
|||||||
export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
|
export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => {
|
||||||
const [workspaceName, setWorkspaceId] = useState<string>('');
|
const [workspaceName, setWorkspaceId] = useState<string>('');
|
||||||
const [creating, setCreating] = useState<boolean>(false);
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
const { refreshWorkspacesMeta } = useAppState();
|
// const { refreshWorkspacesMeta } = useAppState();
|
||||||
const handlerInputChange = (workspaceName: string) => {
|
const handlerInputChange = (workspaceName: string) => {
|
||||||
setWorkspaceId(workspaceName);
|
setWorkspaceId(workspaceName);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,60 +1,63 @@
|
|||||||
import { styled } from '@/styles';
|
// import { styled } from '@/styles';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
import {
|
// import {
|
||||||
WorkspaceItemAvatar,
|
// WorkspaceItemAvatar,
|
||||||
PrivateWorkspaceWrapper,
|
// PrivateWorkspaceWrapper,
|
||||||
WorkspaceItemContent,
|
// WorkspaceItemContent,
|
||||||
} from './styles';
|
// } from './styles';
|
||||||
import { useRouter } from 'next/router';
|
// import { useRouter } from 'next/router';
|
||||||
|
|
||||||
type PrivateWorkspaceItemProps = {
|
// type PrivateWorkspaceItemProps = {
|
||||||
privateWorkspaceId?: string;
|
// privateWorkspaceId?: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export const PrivateWorkspaceItem = ({
|
||||||
|
// privateWorkspaceId,
|
||||||
|
// }: PrivateWorkspaceItemProps) => {
|
||||||
|
// const { user } = useAppState();
|
||||||
|
// const router = useRouter();
|
||||||
|
// const handleClick = () => {
|
||||||
|
// if (privateWorkspaceId) {
|
||||||
|
// router.push(`/workspace/${privateWorkspaceId}`);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// if (user) {
|
||||||
|
// const Username = user.name;
|
||||||
|
// return (
|
||||||
|
// <PrivateWorkspaceWrapper onClick={handleClick}>
|
||||||
|
// <WorkspaceItemAvatar alt={Username} src={user.avatar_url}>
|
||||||
|
// {Username}
|
||||||
|
// </WorkspaceItemAvatar>
|
||||||
|
// <WorkspaceItemContent>
|
||||||
|
// <Name title={Username}>{Username}</Name>
|
||||||
|
// <Email title={user.email}>{user.email}</Email>
|
||||||
|
// </WorkspaceItemContent>
|
||||||
|
// </PrivateWorkspaceWrapper>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const Name = styled('div')(({ theme }) => {
|
||||||
|
// return {
|
||||||
|
// color: theme.colors.quoteColor,
|
||||||
|
// fontSize: theme.font.base,
|
||||||
|
// fontWeight: 500,
|
||||||
|
// overflow: 'hidden',
|
||||||
|
// textOverflow: 'ellipsis',
|
||||||
|
// whiteSpace: 'nowrap',
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const Email = styled('div')(({ theme }) => {
|
||||||
|
// return {
|
||||||
|
// color: theme.colors.iconColor,
|
||||||
|
// fontSize: theme.font.sm,
|
||||||
|
// overflow: 'hidden',
|
||||||
|
// textOverflow: 'ellipsis',
|
||||||
|
// whiteSpace: 'nowrap',
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
export const test = () => {
|
||||||
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PrivateWorkspaceItem = ({
|
|
||||||
privateWorkspaceId,
|
|
||||||
}: PrivateWorkspaceItemProps) => {
|
|
||||||
const { user } = useAppState();
|
|
||||||
const router = useRouter();
|
|
||||||
const handleClick = () => {
|
|
||||||
if (privateWorkspaceId) {
|
|
||||||
router.push(`/workspace/${privateWorkspaceId}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (user) {
|
|
||||||
const Username = user.name;
|
|
||||||
return (
|
|
||||||
<PrivateWorkspaceWrapper onClick={handleClick}>
|
|
||||||
<WorkspaceItemAvatar alt={Username} src={user.avatar_url}>
|
|
||||||
{Username}
|
|
||||||
</WorkspaceItemAvatar>
|
|
||||||
<WorkspaceItemContent>
|
|
||||||
<Name title={Username}>{Username}</Name>
|
|
||||||
<Email title={user.email}>{user.email}</Email>
|
|
||||||
</WorkspaceItemContent>
|
|
||||||
</PrivateWorkspaceWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Name = styled('div')(({ theme }) => {
|
|
||||||
return {
|
|
||||||
color: theme.colors.quoteColor,
|
|
||||||
fontSize: theme.font.base,
|
|
||||||
fontWeight: 500,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const Email = styled('div')(({ theme }) => {
|
|
||||||
return {
|
|
||||||
color: theme.colors.iconColor,
|
|
||||||
fontSize: theme.font.sm,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,96 +1,100 @@
|
|||||||
import { useRouter } from 'next/router';
|
// import { useRouter } from 'next/router';
|
||||||
import { styled } from '@/styles';
|
// import { styled } from '@/styles';
|
||||||
import {
|
// import {
|
||||||
WorkspaceItemAvatar,
|
// WorkspaceItemAvatar,
|
||||||
WorkspaceItemWrapper,
|
// WorkspaceItemWrapper,
|
||||||
WorkspaceItemContent,
|
// WorkspaceItemContent,
|
||||||
} from '../styles';
|
// } from '../styles';
|
||||||
import { FooterSetting } from './FooterSetting';
|
// import { FooterSetting } from './FooterSetting';
|
||||||
import { FooterUsers } from './FooterUsers';
|
// import { FooterUsers } from './FooterUsers';
|
||||||
import { WorkspaceType } from '@affine/datacenter';
|
// import { WorkspaceType } from '@affine/datacenter';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
// import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
|
||||||
interface WorkspaceItemProps {
|
// interface WorkspaceItemProps {
|
||||||
id: string;
|
// id: string;
|
||||||
name: string;
|
// name: string;
|
||||||
icon: string;
|
// icon: string;
|
||||||
type: WorkspaceType;
|
// type: WorkspaceType;
|
||||||
memberCount: number;
|
// memberCount: number;
|
||||||
onClickSetting?: (workspaceId: string) => void;
|
// onClickSetting?: (workspaceId: string) => void;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const WorkspaceItem = ({
|
// export const WorkspaceItem = ({
|
||||||
id,
|
// id,
|
||||||
name,
|
// name,
|
||||||
icon,
|
// icon,
|
||||||
type,
|
// type,
|
||||||
onClickSetting,
|
// onClickSetting,
|
||||||
memberCount,
|
// memberCount,
|
||||||
}: WorkspaceItemProps) => {
|
// }: WorkspaceItemProps) => {
|
||||||
const router = useRouter();
|
// const router = useRouter();
|
||||||
|
|
||||||
const { currentWorkspaceId } = useAppState();
|
// const { currentWorkspaceId } = useAppState();
|
||||||
|
|
||||||
const handleClickSetting = async () => {
|
// const handleClickSetting = async () => {
|
||||||
onClickSetting && onClickSetting(id);
|
// onClickSetting && onClickSetting(id);
|
||||||
};
|
// };
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<StyledWrapper
|
// <StyledWrapper
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
router.push(`/workspace/${id}`);
|
// router.push(`/workspace/${id}`);
|
||||||
}}
|
// }}
|
||||||
canSet={
|
// canSet={
|
||||||
type !== WorkspaceType.Private && currentWorkspaceId === String(id)
|
// type !== WorkspaceType.Private && currentWorkspaceId === String(id)
|
||||||
}
|
// }
|
||||||
>
|
// >
|
||||||
<WorkspaceItemAvatar alt={name} src={icon}>
|
// <WorkspaceItemAvatar alt={name} src={icon}>
|
||||||
{name.charAt(0)}
|
// {name.charAt(0)}
|
||||||
</WorkspaceItemAvatar>
|
// </WorkspaceItemAvatar>
|
||||||
<WorkspaceItemContent>
|
// <WorkspaceItemContent>
|
||||||
<Name title={name}>{name}</Name>
|
// <Name title={name}>{name}</Name>
|
||||||
</WorkspaceItemContent>
|
// </WorkspaceItemContent>
|
||||||
<Footer>
|
// <Footer>
|
||||||
<FooterUsers memberCount={memberCount} />
|
// <FooterUsers memberCount={memberCount} />
|
||||||
<FooterSetting onClick={handleClickSetting} />
|
// <FooterSetting onClick={handleClickSetting} />
|
||||||
</Footer>
|
// </Footer>
|
||||||
</StyledWrapper>
|
// </StyledWrapper>
|
||||||
);
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const Name = styled('div')(({ theme }) => {
|
||||||
|
// return {
|
||||||
|
// color: theme.colors.quoteColor,
|
||||||
|
// fontSize: theme.font.sm,
|
||||||
|
// fontWeight: 400,
|
||||||
|
// overflow: 'hidden',
|
||||||
|
// textOverflow: 'ellipsis',
|
||||||
|
// whiteSpace: 'nowrap',
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const StyledWrapper = styled(WorkspaceItemWrapper)<{ canSet: boolean }>(
|
||||||
|
// ({ canSet }) => {
|
||||||
|
// return {
|
||||||
|
// '& .footer-setting': {
|
||||||
|
// display: 'none',
|
||||||
|
// },
|
||||||
|
// ':hover .footer-users': {
|
||||||
|
// display: canSet ? 'none' : '',
|
||||||
|
// },
|
||||||
|
// ':hover .footer-setting': {
|
||||||
|
// display: canSet ? 'block' : 'none',
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const Footer = styled('div')({
|
||||||
|
// width: '42px',
|
||||||
|
// flex: '0 42px',
|
||||||
|
// fontSize: '20px',
|
||||||
|
// display: 'flex',
|
||||||
|
// alignItems: 'center',
|
||||||
|
// justifyContent: 'center',
|
||||||
|
// marginLeft: '12px',
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const test = () => {
|
||||||
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Name = styled('div')(({ theme }) => {
|
|
||||||
return {
|
|
||||||
color: theme.colors.quoteColor,
|
|
||||||
fontSize: theme.font.sm,
|
|
||||||
fontWeight: 400,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const StyledWrapper = styled(WorkspaceItemWrapper)<{ canSet: boolean }>(
|
|
||||||
({ canSet }) => {
|
|
||||||
return {
|
|
||||||
'& .footer-setting': {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
':hover .footer-users': {
|
|
||||||
display: canSet ? 'none' : '',
|
|
||||||
},
|
|
||||||
':hover .footer-setting': {
|
|
||||||
display: canSet ? 'block' : 'none',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const Footer = styled('div')({
|
|
||||||
width: '42px',
|
|
||||||
flex: '0 42px',
|
|
||||||
fontSize: '20px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginLeft: '12px',
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export * from './PrivateWorkspaceItem';
|
export * from './PrivateWorkspaceItem';
|
||||||
export * from './WorkspaceItem';
|
// export * from './WorkspaceItem';
|
||||||
export * from './CreateWorkspaceItem';
|
export * from './CreateWorkspaceItem';
|
||||||
export * from './ListItem';
|
export * from './ListItem';
|
||||||
export * from './LoginItem';
|
export * from './LoginItem';
|
||||||
|
|||||||
@@ -2,17 +2,16 @@ import { Avatar, WorkspaceName, SelectorWrapper } from './styles';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { WorkspaceModal } from '@/components/workspace-modal';
|
import { WorkspaceModal } from '@/components/workspace-modal';
|
||||||
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
export const WorkspaceSelector = () => {
|
export const WorkspaceSelector = () => {
|
||||||
const [workspaceListShow, setWorkspaceListShow] = useState(false);
|
const [workspaceListShow, setWorkspaceListShow] = useState(false);
|
||||||
const { currentWorkspace, workspaceMetaList } = useTemporaryHelper();
|
const { currentMetaWorkSpace, workspaceList } = useAppState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspaceMetaList.length === 0) {
|
if (workspaceList.length === 0) {
|
||||||
setWorkspaceListShow(true);
|
setWorkspaceListShow(true);
|
||||||
}
|
}
|
||||||
}, [workspaceMetaList]);
|
}, [workspaceList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectorWrapper
|
<SelectorWrapper
|
||||||
@@ -24,7 +23,7 @@ export const WorkspaceSelector = () => {
|
|||||||
<Avatar
|
<Avatar
|
||||||
alt="Affine"
|
alt="Affine"
|
||||||
data-testid="workspace-avatar"
|
data-testid="workspace-avatar"
|
||||||
src={currentWorkspace?.avatar}
|
src={currentMetaWorkSpace?.avatar}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -33,12 +32,13 @@ export const WorkspaceSelector = () => {
|
|||||||
>
|
>
|
||||||
<WorkspaceAvatar
|
<WorkspaceAvatar
|
||||||
size={28}
|
size={28}
|
||||||
name={currentWorkspace?.name ?? 'AFFiNE'}
|
name={currentMetaWorkSpace?.name ?? 'AFFiNE'}
|
||||||
|
avatar={currentMetaWorkSpace?.avatar ?? ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<WorkspaceName data-testid="workspace-name">
|
<WorkspaceName data-testid="workspace-name">
|
||||||
{currentWorkspace?.name ?? 'AFFiNE'}
|
{currentMetaWorkSpace?.name ?? 'AFFiNE'}
|
||||||
</WorkspaceName>
|
</WorkspaceName>
|
||||||
</SelectorWrapper>
|
</SelectorWrapper>
|
||||||
<WorkspaceModal
|
<WorkspaceModal
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
StyledArrowButton,
|
StyledArrowButton,
|
||||||
StyledLink,
|
StyledLink,
|
||||||
StyledListItem,
|
StyledListItem,
|
||||||
|
StyledListItemForWorkspace,
|
||||||
// StyledListItemForWorkspace,
|
// StyledListItemForWorkspace,
|
||||||
StyledNewPageButton,
|
StyledNewPageButton,
|
||||||
StyledSliderBar,
|
StyledSliderBar,
|
||||||
@@ -26,13 +27,14 @@ import {
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Tooltip } from '@/ui/tooltip';
|
import { Tooltip } from '@/ui/tooltip';
|
||||||
import { useModal } from '@/providers/GlobalModalProvider';
|
import { useModal } from '@/providers/GlobalModalProvider';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { IconButton } from '@/ui/button';
|
import { IconButton } from '@/ui/button';
|
||||||
import useLocalStorage from '@/hooks/use-local-storage';
|
import useLocalStorage from '@/hooks/use-local-storage';
|
||||||
import usePageMetaList from '@/hooks/use-page-meta-list';
|
import usePageMetaList from '@/hooks/use-page-meta-list';
|
||||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||||
import { WorkspaceSetting } from '@/components/workspace-setting';
|
// import { WorkspaceSetting } from '@/components/workspace-setting';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
|
import { WorkspaceSelector } from './WorkspaceSelector/WorkspaceSelector';
|
||||||
|
|
||||||
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
||||||
const { openPage } = usePageHelper();
|
const { openPage } = usePageHelper();
|
||||||
@@ -76,14 +78,15 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
const [showTip, setShowTip] = useState(false);
|
const [showTip, setShowTip] = useState(false);
|
||||||
const [show, setShow] = useLocalStorage('AFFiNE_SLIDE_BAR', false, true);
|
const [show, setShow] = useLocalStorage('AFFiNE_SLIDE_BAR', false, true);
|
||||||
|
|
||||||
const [showWorkspaceSetting, setShowWorkspaceSetting] = useState(false);
|
|
||||||
|
|
||||||
const paths = {
|
const paths = {
|
||||||
all: currentWorkspaceId ? `/workspace/${currentWorkspaceId}/all` : '',
|
all: currentWorkspaceId ? `/workspace/${currentWorkspaceId}/all` : '',
|
||||||
favorite: currentWorkspaceId
|
favorite: currentWorkspaceId
|
||||||
? `/workspace/${currentWorkspaceId}/favorite`
|
? `/workspace/${currentWorkspaceId}/favorite`
|
||||||
: '',
|
: '',
|
||||||
trash: currentWorkspaceId ? `/workspace/${currentWorkspaceId}/trash` : '',
|
trash: currentWorkspaceId ? `/workspace/${currentWorkspaceId}/trash` : '',
|
||||||
|
setting: currentWorkspaceId
|
||||||
|
? `/workspace/${currentWorkspaceId}/setting`
|
||||||
|
: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -113,9 +116,9 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<StyledSliderBarWrapper data-testid="sliderBar">
|
<StyledSliderBarWrapper data-testid="sliderBar">
|
||||||
{/* <StyledListItemForWorkspace>
|
<StyledListItemForWorkspace>
|
||||||
<WorkspaceSelector />
|
<WorkspaceSelector />
|
||||||
</StyledListItemForWorkspace> */}
|
</StyledListItemForWorkspace>
|
||||||
<StyledListItem
|
<StyledListItem
|
||||||
data-testid="sliderBar-quickSearchButton"
|
data-testid="sliderBar-quickSearchButton"
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@@ -150,20 +153,19 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</StyledListItem>
|
</StyledListItem>
|
||||||
<FavoriteList showList={showSubFavorite} />
|
<FavoriteList showList={showSubFavorite} />
|
||||||
<StyledListItem
|
<StyledListItem active={router.asPath === paths.setting}>
|
||||||
onClick={() => {
|
<StyledLink href={{ pathname: paths.setting }}>
|
||||||
setShowWorkspaceSetting(true);
|
<SettingsIcon />
|
||||||
}}
|
Settings
|
||||||
>
|
</StyledLink>
|
||||||
<SettingsIcon /> Setting
|
|
||||||
</StyledListItem>
|
</StyledListItem>
|
||||||
|
|
||||||
<WorkspaceSetting
|
{/* <WorkspaceSetting
|
||||||
isShow={showWorkspaceSetting}
|
isShow={showWorkspaceSetting}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowWorkspaceSetting(false);
|
setShowWorkspaceSetting(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
<StyledListItem
|
<StyledListItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -7,22 +7,19 @@ const defaultOutLineWorkspaceId = 'affine';
|
|||||||
// Cause it not just ensure workspace loaded, but also have router change.
|
// Cause it not just ensure workspace loaded, but also have router change.
|
||||||
export const useEnsureWorkspace = () => {
|
export const useEnsureWorkspace = () => {
|
||||||
const [workspaceLoaded, setWorkspaceLoaded] = useState(false);
|
const [workspaceLoaded, setWorkspaceLoaded] = useState(false);
|
||||||
const { workspacesMeta, loadWorkspace, synced, user } = useAppState();
|
const { workspaceList, loadWorkspace, user } = useAppState();
|
||||||
|
console.log('workspaceList: ', workspaceList);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// const defaultOutLineWorkspaceId = '99ce7eb7';
|
// const defaultOutLineWorkspaceId = '99ce7eb7';
|
||||||
// console.log(defaultOutLineWorkspaceId);
|
// console.log(defaultOutLineWorkspaceId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!synced) {
|
|
||||||
setWorkspaceLoaded(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If router.query.workspaceId is not in workspace list, jump to 404 page
|
// If router.query.workspaceId is not in workspace list, jump to 404 page
|
||||||
// If workspacesMeta is empty, we need to create a default workspace but not jump to 404
|
// If workspaceList is empty, we need to create a default workspace but not jump to 404
|
||||||
if (
|
if (
|
||||||
workspacesMeta.length &&
|
workspaceList.length &&
|
||||||
router.query.workspaceId &&
|
router.query.workspaceId &&
|
||||||
workspacesMeta.findIndex(
|
workspaceList.findIndex(
|
||||||
meta => meta.id.toString() === router.query.workspaceId
|
meta => meta.id.toString() === router.query.workspaceId
|
||||||
) === -1
|
) === -1
|
||||||
) {
|
) {
|
||||||
@@ -40,13 +37,13 @@ export const useEnsureWorkspace = () => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const workspaceId = user
|
const workspaceId = user
|
||||||
? (router.query.workspaceId as string) || workspacesMeta[0]?.id
|
? (router.query.workspaceId as string) || workspaceList[0]?.id
|
||||||
: (router.query.workspaceId as string) || defaultOutLineWorkspaceId;
|
: (router.query.workspaceId as string) || defaultOutLineWorkspaceId;
|
||||||
|
|
||||||
loadWorkspace(workspaceId).finally(() => {
|
loadWorkspace(workspaceId).finally(() => {
|
||||||
setWorkspaceLoaded(true);
|
setWorkspaceLoaded(true);
|
||||||
});
|
});
|
||||||
}, [loadWorkspace, router, synced, user, workspacesMeta]);
|
}, [loadWorkspace, router, user, workspaceList]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
workspaceLoaded,
|
workspaceLoaded,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export const useInitWorkspace = (disabled?: boolean) => {
|
export const useInitWorkspace = (disabled?: boolean) => {
|
||||||
@@ -8,15 +8,11 @@ export const useInitWorkspace = (disabled?: boolean) => {
|
|||||||
const defaultOutLineWorkspaceId = useRef(new Date().getTime().toString());
|
const defaultOutLineWorkspaceId = useRef(new Date().getTime().toString());
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const { workspaceList, loadWorkspace, currentWorkspace, currentWorkspaceId } =
|
||||||
workspacesMeta,
|
useAppState();
|
||||||
loadWorkspace,
|
|
||||||
currentWorkspace,
|
|
||||||
currentWorkspaceId,
|
|
||||||
} = useAppState();
|
|
||||||
const workspaceId =
|
const workspaceId =
|
||||||
(router.query.workspaceId as string) ||
|
(router.query.workspaceId as string) ||
|
||||||
workspacesMeta?.[0]?.id ||
|
workspaceList?.[0]?.id ||
|
||||||
defaultOutLineWorkspaceId.current;
|
defaultOutLineWorkspaceId.current;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
28
packages/app/src/hooks/use-user-helper.ts
Normal file
28
packages/app/src/hooks/use-user-helper.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
import { User } from '@affine/datacenter';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const useUserHelper = () => {
|
||||||
|
const { dataCenter } = useAppState();
|
||||||
|
const [user, setUser] = useState<User | undefined>(undefined);
|
||||||
|
useEffect(() => {
|
||||||
|
dataCenter
|
||||||
|
.getUserInfo()
|
||||||
|
.then(user => {
|
||||||
|
setUser(user);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
setUser(undefined);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const login = async () => {
|
||||||
|
await dataCenter.login();
|
||||||
|
const user = await dataCenter.getUserInfo();
|
||||||
|
setUser(user);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
login,
|
||||||
|
};
|
||||||
|
};
|
||||||
46
packages/app/src/hooks/use-workspace-helper.ts
Normal file
46
packages/app/src/hooks/use-workspace-helper.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
import { Workspace } from '@blocksuite/store';
|
||||||
|
export const useWorkspaceHelper = () => {
|
||||||
|
const { dataCenter } = useAppState();
|
||||||
|
const createWorkspace = async (name: string) => {
|
||||||
|
const workspaceInfo = await dataCenter.createWorkspace({
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
if (workspaceInfo && workspaceInfo.room) {
|
||||||
|
const workspace = await dataCenter.loadWorkspace(workspaceInfo.room);
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// const updateWorkspace = async (workspace: Workspace) => {};
|
||||||
|
|
||||||
|
const publishWorkspace = async (workspaceId: string, publish: boolean) => {
|
||||||
|
dataCenter.setWorkspacePublish(workspaceId, publish);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateWorkspace = async (
|
||||||
|
{ name, avatarBlob }: { name?: string; avatarBlob?: Blob },
|
||||||
|
workspace: Workspace
|
||||||
|
) => {
|
||||||
|
if (name) {
|
||||||
|
await dataCenter.updateWorkspaceMeta({ name }, workspace);
|
||||||
|
}
|
||||||
|
if (avatarBlob) {
|
||||||
|
const blobId = await dataCenter.setBlob(workspace, avatarBlob);
|
||||||
|
await dataCenter.updateWorkspaceMeta({ avatar: blobId }, workspace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const enableWorkspace = async (workspace: Workspace) => {
|
||||||
|
const newWorkspaceId = await dataCenter.enableWorkspaceCloud(workspace);
|
||||||
|
// console.log('newWorkspace: ', newWorkspace);
|
||||||
|
return newWorkspaceId;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
createWorkspace,
|
||||||
|
publishWorkspace,
|
||||||
|
updateWorkspace,
|
||||||
|
enableWorkspace,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import NotfoundPage from '@/components/404';
|
import NotfoundPage from '@/components/404';
|
||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
|
const { workspaceList } = useAppState();
|
||||||
|
console.log('workspaceList: ', workspaceList);
|
||||||
|
|
||||||
return <NotfoundPage></NotfoundPage>;
|
return <NotfoundPage></NotfoundPage>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ import '../utils/print-build-info';
|
|||||||
import ProviderComposer from '@/components/provider-composer';
|
import ProviderComposer from '@/components/provider-composer';
|
||||||
import type { PropsWithChildren, ReactElement, ReactNode } from 'react';
|
import type { PropsWithChildren, ReactElement, ReactNode } from 'react';
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import { AppStateProvider } from '@/providers/app-state-provider/Provider';
|
import { AppStateProvider } from '@/providers/app-state-provider';
|
||||||
import ConfirmProvider from '@/providers/ConfirmProvider';
|
import ConfirmProvider from '@/providers/ConfirmProvider';
|
||||||
import { ModalProvider } from '@/providers/GlobalModalProvider';
|
import { ModalProvider } from '@/providers/GlobalModalProvider';
|
||||||
|
// import AppStateProvider2 from '@/providers/app-state-provider2/provider';
|
||||||
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { PageLoading } from '@/components/loading';
|
import { PageLoading } from '@/components/loading';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import TemporaryHelperProvider from '@/providers/temporary-helper-provider';
|
|
||||||
import '@affine/i18n';
|
import '@affine/i18n';
|
||||||
|
import TemporaryHelperProvider from '@/providers/temporary-helper-provider';
|
||||||
|
|
||||||
const ThemeProvider = dynamic(() => import('@/providers/ThemeProvider'), {
|
const ThemeProvider = dynamic(() => import('@/providers/ThemeProvider'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@@ -56,6 +58,7 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
|
|||||||
<TemporaryHelperProvider key="TemporaryHelperProvider" />,
|
<TemporaryHelperProvider key="TemporaryHelperProvider" />,
|
||||||
<ThemeProvider key="ThemeProvider" />,
|
<ThemeProvider key="ThemeProvider" />,
|
||||||
<AppStateProvider key="appStateProvider" />,
|
<AppStateProvider key="appStateProvider" />,
|
||||||
|
// <AppStateProvider2 key="appStateProvider2" />,
|
||||||
<ModalProvider key="ModalProvider" />,
|
<ModalProvider key="ModalProvider" />,
|
||||||
<ConfirmProvider key="ConfirmProvider" />,
|
<ConfirmProvider key="ConfirmProvider" />,
|
||||||
]}
|
]}
|
||||||
@@ -67,9 +70,8 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AppDefender = ({ children }: PropsWithChildren) => {
|
const AppDefender = ({ children }: PropsWithChildren) => {
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { synced } = useAppState();
|
const { synced } = useAppState();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (router.pathname === '/') {
|
if (router.pathname === '/') {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
import { Empty } from '@/ui/empty';
|
import { Empty } from '@/ui/empty';
|
||||||
import { Avatar } from '@mui/material';
|
import { Avatar } from '@mui/material';
|
||||||
import { getDataCenter } from '@affine/datacenter';
|
// import { getDataCenter } from '@affine/datacenter';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ export default function DevPage() {
|
|||||||
// .catch(err => {
|
// .catch(err => {
|
||||||
// console.log('err: ', err);
|
// console.log('err: ', err);
|
||||||
// });
|
// });
|
||||||
|
setSuccessInvited(true);
|
||||||
|
setInviteData(null);
|
||||||
}, [router.query.invite_code]);
|
}, [router.query.invite_code]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import exampleMarkdown1 from '@/templates/Welcome-to-the-AFFiNE-Alpha.md';
|
|||||||
import exampleMarkdown2 from '@/templates/AFFiNE-Docs.md';
|
import exampleMarkdown2 from '@/templates/AFFiNE-Docs.md';
|
||||||
|
|
||||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
interface Template {
|
interface Template {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -2,88 +2,42 @@
|
|||||||
import {
|
import {
|
||||||
PropsWithChildren,
|
PropsWithChildren,
|
||||||
ReactElement,
|
ReactElement,
|
||||||
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { styled } from '@/styles';
|
|
||||||
import { EditorHeader } from '@/components/header';
|
import { EditorHeader } from '@/components/header';
|
||||||
import EdgelessToolbar from '@/components/edgeless-toolbar';
|
import EdgelessToolbar from '@/components/edgeless-toolbar';
|
||||||
import MobileModal from '@/components/mobile-modal';
|
import MobileModal from '@/components/mobile-modal';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-v2.0.md';
|
|
||||||
import type { NextPageWithLayout } from '../..//_app';
|
import type { NextPageWithLayout } from '../..//_app';
|
||||||
import WorkspaceLayout from '@/components/workspace-layout';
|
import WorkspaceLayout from '@/components/workspace-layout';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
const StyledEditorContainer = styled('div')(() => {
|
import { EditorContainer } from '@blocksuite/editor';
|
||||||
return {
|
const DynamicBlocksuite = dynamic(() => import('@/components/editor'), {
|
||||||
height: 'calc(100vh - 60px)',
|
ssr: false,
|
||||||
padding: '0 32px',
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Page: NextPageWithLayout = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const editorContainer = useRef<HTMLDivElement>(null);
|
const { currentPage, currentWorkspace, setEditor } = useAppState();
|
||||||
const { createEditor, setEditor, currentPage, currentWorkspace } =
|
|
||||||
useAppState();
|
|
||||||
|
|
||||||
useEffect(() => {
|
const setEditorHandler = useCallback(
|
||||||
const ret = () => {
|
(editor: EditorContainer) => setEditor.current(editor),
|
||||||
const node = editorContainer.current;
|
[setEditor]
|
||||||
while (node?.firstChild) {
|
|
||||||
node.removeChild(node.firstChild);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const editor = createEditor?.current?.(currentPage!);
|
|
||||||
if (editor) {
|
|
||||||
editorContainer.current?.appendChild(editor);
|
|
||||||
setEditor?.current?.(editor);
|
|
||||||
if (currentPage!.isEmpty) {
|
|
||||||
const isFirstPage = currentWorkspace?.meta.pageMetas.length === 1;
|
|
||||||
// Can not use useCurrentPageMeta to get new title, cause meta title will trigger rerender, but the second time can not remove title
|
|
||||||
const { title: metaTitle } = currentPage!.meta;
|
|
||||||
const title = metaTitle
|
|
||||||
? metaTitle
|
|
||||||
: isFirstPage
|
|
||||||
? 'Welcome to AFFiNE Alpha "Abbey Wood"'
|
|
||||||
: '';
|
|
||||||
currentWorkspace?.setPageMeta(currentPage!.id, { title });
|
|
||||||
|
|
||||||
const pageId = currentPage!.addBlock({
|
|
||||||
flavour: 'affine:page',
|
|
||||||
title,
|
|
||||||
});
|
|
||||||
currentPage!.addBlock({ flavour: 'affine:surface' }, null);
|
|
||||||
const frameId = currentPage!.addBlock(
|
|
||||||
{ flavour: 'affine:frame' },
|
|
||||||
pageId
|
|
||||||
);
|
);
|
||||||
currentPage!.addBlock({ flavour: 'affine:frame' }, pageId);
|
|
||||||
// If this is a first page in workspace, init an introduction markdown
|
|
||||||
if (isFirstPage) {
|
|
||||||
editor.clipboard
|
|
||||||
.importMarkdown(exampleMarkdown, `${frameId}`)
|
|
||||||
.then(() => {
|
|
||||||
currentWorkspace!.setPageMeta(currentPage!.id, { title });
|
|
||||||
currentPage!.resetHistory();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
currentPage!.resetHistory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = currentPage?.meta.title || 'Untitled';
|
|
||||||
return ret;
|
|
||||||
}, [currentWorkspace, currentPage, createEditor, setEditor]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditorHeader />
|
<EditorHeader />
|
||||||
<MobileModal />
|
<MobileModal />
|
||||||
<StyledEditorContainer ref={editorContainer} />
|
|
||||||
|
{currentPage && (
|
||||||
|
<DynamicBlocksuite
|
||||||
|
page={currentPage}
|
||||||
|
workspace={currentWorkspace}
|
||||||
|
setEditor={setEditorHandler}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<EdgelessToolbar />
|
<EdgelessToolbar />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useAppState } from '@/providers/app-state-provider/context';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import useEnsureWorkspace from '@/hooks/use-ensure-workspace';
|
import useEnsureWorkspace from '@/hooks/use-ensure-workspace';
|
||||||
import { PageLoading } from '@/components/loading';
|
import { PageLoading } from '@/components/loading';
|
||||||
import usePageHelper from '@/hooks/use-page-helper';
|
import usePageHelper from '@/hooks/use-page-helper';
|
||||||
|
|||||||
110
packages/app/src/pages/workspace/[workspaceId]/setting.tsx
Normal file
110
packages/app/src/pages/workspace/[workspaceId]/setting.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
StyledSettingContainer,
|
||||||
|
StyledSettingContent,
|
||||||
|
StyledSettingSidebar,
|
||||||
|
StyledSettingSidebarHeader,
|
||||||
|
StyledSettingTabContainer,
|
||||||
|
StyledSettingTagIconContainer,
|
||||||
|
WorkspaceSettingTagItem,
|
||||||
|
} from '@/components/workspace-setting/style';
|
||||||
|
import {
|
||||||
|
EditIcon,
|
||||||
|
UsersIcon,
|
||||||
|
PublishIcon,
|
||||||
|
CloudInsyncIcon,
|
||||||
|
} from '@blocksuite/icons';
|
||||||
|
import { ReactElement, ReactNode, useState } from 'react';
|
||||||
|
import {
|
||||||
|
GeneralPage,
|
||||||
|
MembersPage,
|
||||||
|
PublishPage,
|
||||||
|
ExportPage,
|
||||||
|
SyncPage,
|
||||||
|
} from '@/components/workspace-setting';
|
||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
|
import WorkspaceLayout from '@/components/workspace-layout';
|
||||||
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
|
|
||||||
|
type TabNames = 'general' | 'members' | 'publish' | 'sync' | 'export';
|
||||||
|
|
||||||
|
const tabMap: {
|
||||||
|
name: TabNames;
|
||||||
|
icon: ReactNode;
|
||||||
|
panelRender: (workspace: WorkspaceInfo) => ReactNode;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
name: 'general',
|
||||||
|
icon: <EditIcon />,
|
||||||
|
panelRender: workspace => <GeneralPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'members',
|
||||||
|
icon: <CloudInsyncIcon />,
|
||||||
|
panelRender: workspace => <MembersPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'publish',
|
||||||
|
icon: <UsersIcon />,
|
||||||
|
panelRender: workspace => <PublishPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sync',
|
||||||
|
icon: <PublishIcon />,
|
||||||
|
panelRender: workspace => <SyncPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'export',
|
||||||
|
icon: <PublishIcon />,
|
||||||
|
panelRender: workspace => <ExportPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const WorkspaceSetting = () => {
|
||||||
|
const { currentMetaWorkSpace } = useAppState();
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState<TabNames>(tabMap[0].name);
|
||||||
|
const handleTabChange = (tab: TabNames) => {
|
||||||
|
setActiveTab(tab);
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeTabPanelRender = tabMap.find(
|
||||||
|
tab => tab.name === activeTab
|
||||||
|
)?.panelRender;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledSettingContainer>
|
||||||
|
<StyledSettingSidebar>
|
||||||
|
<StyledSettingSidebarHeader>
|
||||||
|
Workspace Settings
|
||||||
|
</StyledSettingSidebarHeader>
|
||||||
|
<StyledSettingTabContainer>
|
||||||
|
{tabMap.map(({ icon, name }) => {
|
||||||
|
return (
|
||||||
|
<WorkspaceSettingTagItem
|
||||||
|
key={name}
|
||||||
|
isActive={activeTab === name}
|
||||||
|
onClick={() => {
|
||||||
|
handleTabChange(name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StyledSettingTagIconContainer>
|
||||||
|
{icon}
|
||||||
|
</StyledSettingTagIconContainer>
|
||||||
|
{name}
|
||||||
|
</WorkspaceSettingTagItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</StyledSettingTabContainer>
|
||||||
|
</StyledSettingSidebar>
|
||||||
|
|
||||||
|
<StyledSettingContent>
|
||||||
|
{currentMetaWorkSpace && activeTabPanelRender?.(currentMetaWorkSpace)}
|
||||||
|
</StyledSettingContent>
|
||||||
|
</StyledSettingContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
WorkspaceSetting.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <WorkspaceLayout>{page}</WorkspaceLayout>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WorkspaceSetting;
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import type { Page } from '@blocksuite/store';
|
|
||||||
import '@blocksuite/blocks';
|
|
||||||
import { EditorContainer } from '@blocksuite/editor';
|
|
||||||
import type { CreateEditorHandler } from './context';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
setCreateEditorHandler: (handler: CreateEditorHandler) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DynamicBlocksuite = ({ setCreateEditorHandler }: Props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
const createEditorHandler: CreateEditorHandler = (page: Page) => {
|
|
||||||
const editor = new EditorContainer();
|
|
||||||
editor.page = page;
|
|
||||||
return editor;
|
|
||||||
};
|
|
||||||
|
|
||||||
setCreateEditorHandler(createEditorHandler);
|
|
||||||
}, [setCreateEditorHandler]);
|
|
||||||
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicBlocksuite;
|
|
||||||
@@ -1,143 +1,144 @@
|
|||||||
import { useMemo, useState, useCallback, useRef } from 'react';
|
import { createContext, useContext, useEffect, useState, useRef } from 'react';
|
||||||
import type { ReactNode } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import { getDataCenter } from '@affine/datacenter';
|
import { getDataCenter } from '@affine/datacenter';
|
||||||
import { AppState, AppStateContext } from './context';
|
import {
|
||||||
import type {
|
AppStateContext,
|
||||||
|
AppStateFunction,
|
||||||
AppStateValue,
|
AppStateValue,
|
||||||
CreateEditorHandler,
|
PageMeta,
|
||||||
LoadWorkspaceHandler,
|
} from './interface';
|
||||||
} from './context';
|
import { createDefaultWorkspace } from './utils';
|
||||||
import { Page } from '@blocksuite/store';
|
import { WorkspaceInfo } from '@affine/datacenter';
|
||||||
import { EditorContainer } from '@blocksuite/editor';
|
|
||||||
const DynamicBlocksuite = dynamic(() => import('./DynamicBlocksuite'), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadWorkspaceHandler: LoadWorkspaceHandler = async workspaceId => {
|
type AppStateContextProps = PropsWithChildren<Record<string, unknown>>;
|
||||||
if (workspaceId) {
|
|
||||||
const dc = await getDataCenter();
|
export const AppState = createContext<AppStateContext>({} as AppStateContext);
|
||||||
return dc.load(workspaceId, { providerId: 'affine' });
|
|
||||||
} else {
|
export const useAppState = () => useContext(AppState);
|
||||||
return null;
|
|
||||||
|
export const AppStateProvider = ({
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<AppStateContextProps>) => {
|
||||||
|
const [appState, setAppState] = useState<AppStateValue>({} as AppStateValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const init = async () => {
|
||||||
|
const dataCenter = await getDataCenter();
|
||||||
|
|
||||||
|
if (dataCenter.workspaces.length === 0) {
|
||||||
|
await createDefaultWorkspace(dataCenter);
|
||||||
}
|
}
|
||||||
};
|
const currentWorkspace = await dataCenter.loadWorkspace(
|
||||||
export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
dataCenter.workspaces[0].id
|
||||||
const refreshWorkspacesMeta = async () => {
|
);
|
||||||
const dc = await getDataCenter();
|
const currentMetaWorkSpace = dataCenter.workspaces.find(item => {
|
||||||
const workspacesMeta = await dc.apis.getWorkspaces().catch(() => {
|
return item.id === currentWorkspace.room;
|
||||||
return [];
|
|
||||||
});
|
});
|
||||||
setState(state => ({ ...state, workspacesMeta }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const [state, setState] = useState<AppStateValue>({
|
setAppState({
|
||||||
user: null,
|
dataCenter,
|
||||||
workspacesMeta: [],
|
user: (await dataCenter.getUserInfo()) || null,
|
||||||
currentWorkspaceId: '',
|
workspaceList: dataCenter.workspaces,
|
||||||
currentWorkspace: null,
|
currentWorkspaceId: dataCenter.workspaces[0].id,
|
||||||
|
currentWorkspace,
|
||||||
|
pageList: currentWorkspace.meta.pageMetas as PageMeta[],
|
||||||
currentPage: null,
|
currentPage: null,
|
||||||
editor: null,
|
editor: null,
|
||||||
refreshWorkspacesMeta,
|
|
||||||
synced: true,
|
synced: true,
|
||||||
|
currentMetaWorkSpace: currentMetaWorkSpace ?? null,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const [createEditorHandler, _setCreateEditorHandler] =
|
init();
|
||||||
useState<CreateEditorHandler>();
|
}, []);
|
||||||
|
|
||||||
const setCreateEditorHandler = useCallback(
|
useEffect(() => {
|
||||||
(handler: CreateEditorHandler) => {
|
if (!appState?.currentWorkspace) {
|
||||||
_setCreateEditorHandler(() => handler);
|
return;
|
||||||
},
|
|
||||||
[_setCreateEditorHandler]
|
|
||||||
);
|
|
||||||
|
|
||||||
const loadWorkspace = useRef<AppStateContext['loadWorkspace']>(() =>
|
|
||||||
Promise.resolve(null)
|
|
||||||
);
|
|
||||||
loadWorkspace.current = async (workspaceId: string) => {
|
|
||||||
if (state.currentWorkspaceId === workspaceId) {
|
|
||||||
return state.currentWorkspace;
|
|
||||||
}
|
}
|
||||||
const workspace =
|
const currentWorkspace = appState.currentWorkspace;
|
||||||
(await loadWorkspaceHandler(workspaceId, state.user)) || null;
|
const dispose = currentWorkspace.meta.pagesUpdated.on(() => {
|
||||||
|
setAppState({
|
||||||
|
...appState,
|
||||||
|
pageList: currentWorkspace.meta.pageMetas as PageMeta[],
|
||||||
|
});
|
||||||
|
}).dispose;
|
||||||
|
return () => {
|
||||||
|
dispose();
|
||||||
|
};
|
||||||
|
}, [appState]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
useEffect(() => {
|
||||||
// @ts-expect-error
|
const { dataCenter } = appState;
|
||||||
window.workspace = workspace;
|
// FIXME: onWorkspacesChange should have dispose function
|
||||||
// FIXME: there needs some method to destroy websocket.
|
dataCenter?.onWorkspacesChange(() => {
|
||||||
// Or we need a manager to manage websocket.
|
setAppState({
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
...appState,
|
||||||
// @ts-expect-error
|
workspaceList: dataCenter.workspaces,
|
||||||
state.currentWorkspace?.__ws__?.destroy();
|
});
|
||||||
|
});
|
||||||
|
}, [appState]);
|
||||||
|
|
||||||
setState(state => ({
|
const loadPage = useRef<AppStateFunction['loadPage']>();
|
||||||
...state,
|
loadPage.current = (pageId: string) => {
|
||||||
|
const { currentWorkspace, currentPage } = appState;
|
||||||
|
if (pageId === currentPage?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const page = currentWorkspace?.getPage(pageId) || null;
|
||||||
|
setAppState({
|
||||||
|
...appState,
|
||||||
|
currentPage: page,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadWorkspace = useRef<AppStateFunction['loadWorkspace']>();
|
||||||
|
loadWorkspace.current = async (workspaceId: string) => {
|
||||||
|
const { dataCenter, workspaceList, currentWorkspaceId, currentWorkspace } =
|
||||||
|
appState;
|
||||||
|
if (!workspaceList.find(v => v.id === workspaceId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (workspaceId === currentWorkspaceId) {
|
||||||
|
return currentWorkspace;
|
||||||
|
}
|
||||||
|
const workspace = await dataCenter.loadWorkspace(workspaceId);
|
||||||
|
const currentMetaWorkSpace = dataCenter.workspaces.find(
|
||||||
|
(item: WorkspaceInfo) => {
|
||||||
|
return item.id === workspace.room;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setAppState({
|
||||||
|
...appState,
|
||||||
currentWorkspace: workspace,
|
currentWorkspace: workspace,
|
||||||
currentWorkspaceId: workspaceId,
|
currentWorkspaceId: workspaceId,
|
||||||
}));
|
currentMetaWorkSpace: currentMetaWorkSpace ?? null,
|
||||||
|
pageList: currentWorkspace.meta.pageMetas as PageMeta[],
|
||||||
|
currentPage: null,
|
||||||
|
editor: null,
|
||||||
|
});
|
||||||
|
|
||||||
return workspace;
|
return workspace;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadPage = useRef<AppStateContext['loadPage']>(() =>
|
const setEditor: AppStateFunction['setEditor'] =
|
||||||
Promise.resolve(null)
|
useRef() as AppStateFunction['setEditor'];
|
||||||
);
|
setEditor.current = editor => {
|
||||||
loadPage.current = async (pageId: string) => {
|
setAppState({
|
||||||
const { currentWorkspace, currentPage } = state;
|
...appState,
|
||||||
if (pageId === currentPage?.id) {
|
editor,
|
||||||
return currentPage;
|
});
|
||||||
}
|
|
||||||
const page = (pageId ? currentWorkspace?.getPage(pageId) : null) || null;
|
|
||||||
setState(state => ({ ...state, currentPage: page }));
|
|
||||||
return page;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createEditor = useRef<
|
|
||||||
((page: Page) => EditorContainer | null) | undefined
|
|
||||||
>();
|
|
||||||
createEditor.current = () => {
|
|
||||||
const { currentPage, currentWorkspace } = state;
|
|
||||||
if (!currentPage || !currentWorkspace) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const editor = createEditorHandler?.(currentPage) || null;
|
|
||||||
|
|
||||||
if (editor) {
|
|
||||||
const pageMeta = currentWorkspace.meta.pageMetas.find(
|
|
||||||
p => p.id === currentPage.id
|
|
||||||
);
|
|
||||||
if (pageMeta?.mode) {
|
|
||||||
editor.mode = pageMeta.mode as 'page' | 'edgeless' | undefined;
|
|
||||||
}
|
|
||||||
if (pageMeta?.trash) {
|
|
||||||
editor.readonly = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setEditor = useRef<(editor: AppStateValue['editor']) => void>();
|
|
||||||
|
|
||||||
setEditor.current = (editor: AppStateValue['editor']) => {
|
|
||||||
setState(state => ({ ...state, editor }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const context = useMemo(
|
|
||||||
() => ({
|
|
||||||
...state,
|
|
||||||
setState,
|
|
||||||
createEditor,
|
|
||||||
setEditor,
|
|
||||||
loadWorkspace: loadWorkspace.current,
|
|
||||||
loadPage: loadPage.current,
|
|
||||||
}),
|
|
||||||
[state, setState, loadPage, loadWorkspace]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppState.Provider value={context}>
|
<AppState.Provider
|
||||||
<DynamicBlocksuite setCreateEditorHandler={setCreateEditorHandler} />
|
value={{
|
||||||
|
...appState,
|
||||||
|
setEditor,
|
||||||
|
loadPage: loadPage.current,
|
||||||
|
loadWorkspace: loadWorkspace.current,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</AppState.Provider>
|
</AppState.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import { createContext, MutableRefObject, useContext } from 'react';
|
|
||||||
import type { AccessTokenMessage, Workspace } from '@affine/datacenter';
|
|
||||||
import type {
|
|
||||||
Page as StorePage,
|
|
||||||
Workspace as StoreWorkspace,
|
|
||||||
} from '@blocksuite/store';
|
|
||||||
import type { EditorContainer } from '@blocksuite/editor';
|
|
||||||
|
|
||||||
export type LoadWorkspaceHandler = (
|
|
||||||
workspaceId: string,
|
|
||||||
user?: AccessTokenMessage | null
|
|
||||||
) => Promise<StoreWorkspace | null> | null;
|
|
||||||
export type CreateEditorHandler = (page: StorePage) => EditorContainer | null;
|
|
||||||
|
|
||||||
export interface AppStateValue {
|
|
||||||
user: AccessTokenMessage | null;
|
|
||||||
workspacesMeta: Workspace[];
|
|
||||||
|
|
||||||
currentWorkspaceId: string;
|
|
||||||
currentWorkspace: StoreWorkspace | null;
|
|
||||||
|
|
||||||
currentPage: StorePage | null;
|
|
||||||
|
|
||||||
// workspaces: Record<string, StoreWorkspace | null>;
|
|
||||||
|
|
||||||
editor: EditorContainer | null;
|
|
||||||
synced: boolean;
|
|
||||||
refreshWorkspacesMeta: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppStateContext extends AppStateValue {
|
|
||||||
setState: (state: AppStateValue) => void;
|
|
||||||
createEditor?: MutableRefObject<
|
|
||||||
((page: StorePage) => EditorContainer | null) | undefined
|
|
||||||
>;
|
|
||||||
setEditor?: MutableRefObject<((page: EditorContainer) => void) | undefined>;
|
|
||||||
loadWorkspace: (workspaceId: string) => Promise<StoreWorkspace | null>;
|
|
||||||
loadPage: (pageId: string) => Promise<StorePage | null>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AppState = createContext<AppStateContext>({
|
|
||||||
user: null,
|
|
||||||
workspacesMeta: [],
|
|
||||||
currentWorkspaceId: '',
|
|
||||||
currentWorkspace: null,
|
|
||||||
currentPage: null,
|
|
||||||
editor: null,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
setState: () => {},
|
|
||||||
createEditor: undefined,
|
|
||||||
setEditor: undefined,
|
|
||||||
loadWorkspace: () => Promise.resolve(null),
|
|
||||||
loadPage: () => Promise.resolve(null),
|
|
||||||
synced: false,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
refreshWorkspacesMeta: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useAppState = () => {
|
|
||||||
return useContext(AppState);
|
|
||||||
};
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './context';
|
export * from './Provider';
|
||||||
export * from './interface';
|
export * from './interface';
|
||||||
|
|||||||
@@ -1,17 +1,40 @@
|
|||||||
import { PageMeta as OriginalPageMeta } from '@blocksuite/store/dist/workspace/workspace';
|
import { DataCenter, User, WorkspaceInfo } from '@affine/datacenter';
|
||||||
|
import type { EditorContainer } from '@blocksuite/editor';
|
||||||
|
|
||||||
// export type PageMeta = {
|
import type {
|
||||||
// favorite: boolean;
|
Page as StorePage,
|
||||||
// trash: boolean;
|
Workspace as StoreWorkspace,
|
||||||
// trashDate: number | void;
|
PageMeta as StorePageMeta,
|
||||||
// updatedDate: number | void;
|
} from '@blocksuite/store';
|
||||||
// mode: EditorContainer['mode'];
|
import { MutableRefObject } from 'react';
|
||||||
// } & OriginalPageMeta;
|
export interface PageMeta extends StorePageMeta {
|
||||||
|
|
||||||
export interface PageMeta extends OriginalPageMeta {
|
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
trash: boolean;
|
trash: boolean;
|
||||||
trashDate: number;
|
trashDate: number;
|
||||||
updatedDate: number;
|
updatedDate: number;
|
||||||
mode: 'edgeless' | 'page';
|
mode: 'edgeless' | 'page';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AppStateValue = {
|
||||||
|
dataCenter: DataCenter;
|
||||||
|
user: User | null;
|
||||||
|
workspaceList: WorkspaceInfo[];
|
||||||
|
currentWorkspace: StoreWorkspace;
|
||||||
|
currentMetaWorkSpace: WorkspaceInfo | null;
|
||||||
|
currentWorkspaceId: string;
|
||||||
|
pageList: PageMeta[];
|
||||||
|
currentPage: StorePage | null;
|
||||||
|
editor?: EditorContainer | null;
|
||||||
|
synced: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppStateFunction = {
|
||||||
|
setEditor: MutableRefObject<(page: EditorContainer) => void>;
|
||||||
|
|
||||||
|
loadWorkspace: (workspaceId: string) => Promise<StoreWorkspace | null>;
|
||||||
|
loadPage: (pageId: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppStateContext = AppStateValue & AppStateFunction;
|
||||||
|
|
||||||
|
export type CreateEditorHandler = (page: StorePage) => EditorContainer | null;
|
||||||
|
|||||||
10
packages/app/src/providers/app-state-provider/utils.ts
Normal file
10
packages/app/src/providers/app-state-provider/utils.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { DataCenter } from '@affine/datacenter';
|
||||||
|
|
||||||
|
const DEFAULT_WORKSPACE_NAME = 'affine';
|
||||||
|
|
||||||
|
export const createDefaultWorkspace = async (dataCenter: DataCenter) => {
|
||||||
|
return dataCenter.createWorkspace({
|
||||||
|
avatar: 'test',
|
||||||
|
name: DEFAULT_WORKSPACE_NAME,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -37,8 +37,11 @@
|
|||||||
"ky-universal": "^0.11.0",
|
"ky-universal": "^0.11.0",
|
||||||
"lib0": "^0.2.58",
|
"lib0": "^0.2.58",
|
||||||
"swr": "^2.0.0",
|
"swr": "^2.0.0",
|
||||||
"vitest": "^0.26.3",
|
"yjs": "^13.5.44",
|
||||||
"y-protocols": "^1.0.5",
|
"y-protocols": "^1.0.5"
|
||||||
"yjs": "^13.5.44"
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@blocksuite/blocks": "0.3.1-*",
|
||||||
|
"@blocksuite/store": "0.3.1-*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { getLogger } from './logger';
|
|||||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||||
import { TauriIPCProvider } from './provider/tauri-ipc';
|
import { TauriIPCProvider } from './provider/tauri-ipc';
|
||||||
import { createBlocksuiteWorkspace } from './utils/index.js';
|
import { createBlocksuiteWorkspace } from './utils/index.js';
|
||||||
import { MessageCenter } from './message/message';
|
import { MessageCenter } from './message';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class DataCenter
|
* @class DataCenter
|
||||||
@@ -313,6 +313,7 @@ export class DataCenter {
|
|||||||
applyUpdate(newWorkspace.doc, encodeStateAsUpdate(workspace.doc));
|
applyUpdate(newWorkspace.doc, encodeStateAsUpdate(workspace.doc));
|
||||||
assert(newWorkspace, 'Create workspace failed');
|
assert(newWorkspace, 'Create workspace failed');
|
||||||
await currentProvider.deleteWorkspace(workspace.room);
|
await currentProvider.deleteWorkspace(workspace.room);
|
||||||
|
return newWorkspace.room;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ const _initializeDataCenter = () => {
|
|||||||
|
|
||||||
export const getDataCenter = _initializeDataCenter();
|
export const getDataCenter = _initializeDataCenter();
|
||||||
|
|
||||||
|
export type { DataCenter };
|
||||||
export type { AccessTokenMessage } from './provider/affine/apis';
|
export type { AccessTokenMessage } from './provider/affine/apis';
|
||||||
export type { WorkspaceInfo } from './types';
|
export * from './types';
|
||||||
export { getLogger } from './logger';
|
export { getLogger } from './logger';
|
||||||
export * from './message';
|
export * from './message';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Observable } from 'lib0/observable';
|
import { Observable } from 'lib0/observable';
|
||||||
import { Message } from 'src/types';
|
import { Message } from '../types';
|
||||||
import { MessageCode } from './code';
|
import { MessageCode } from './code';
|
||||||
|
|
||||||
export class MessageCenter extends Observable<string> {
|
export class MessageCenter extends Observable<string> {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { WebsocketProvider } from './sync.js';
|
|||||||
import { getApis } from './apis/index.js';
|
import { getApis } from './apis/index.js';
|
||||||
import type { Apis, WorkspaceDetail, Callback } from './apis';
|
import type { Apis, WorkspaceDetail, Callback } from './apis';
|
||||||
import { setDefaultAvatar } from '../utils.js';
|
import { setDefaultAvatar } from '../utils.js';
|
||||||
import { MessageCode } from 'src/message/code.js';
|
import { MessageCode } from '../../message';
|
||||||
|
|
||||||
export interface AffineProviderConstructorParams
|
export interface AffineProviderConstructorParams
|
||||||
extends ProviderConstructorParams {
|
extends ProviderConstructorParams {
|
||||||
@@ -146,8 +146,8 @@ export class AffineProvider extends BaseProvider {
|
|||||||
if (workspace) {
|
if (workspace) {
|
||||||
workspaces[i] = {
|
workspaces[i] = {
|
||||||
...workspaces[i],
|
...workspaces[i],
|
||||||
name: workspace.doc.meta.name,
|
name: workspace.meta.name,
|
||||||
avatar: workspace.doc.meta.avatar,
|
avatar: workspace.meta.avatar,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
|
||||||
import { MessageCenter } from 'src/message';
|
import { MessageCenter } from '../message';
|
||||||
import { Logger, User, WorkspaceInfo, WorkspaceMeta } from '../types';
|
import { Logger, User, WorkspaceInfo, WorkspaceMeta } from '../types';
|
||||||
import type { WorkspaceMetaCollectionScope } from '../workspace-meta-collection';
|
import type { WorkspaceMetaCollectionScope } from '../workspace-meta-collection';
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ export class LocalProvider extends BaseProvider {
|
|||||||
public override async linkLocal(workspace: BlocksuiteWorkspace) {
|
public override async linkLocal(workspace: BlocksuiteWorkspace) {
|
||||||
assert(workspace.room);
|
assert(workspace.room);
|
||||||
let idb = this._idbMap.get(workspace.room);
|
let idb = this._idbMap.get(workspace.room);
|
||||||
idb?.destroy();
|
if (!idb) {
|
||||||
idb = new IndexedDBProvider(workspace.room, workspace.doc);
|
idb = new IndexedDBProvider(workspace.room, workspace.doc);
|
||||||
|
}
|
||||||
this._idbMap.set(workspace.room, idb);
|
this._idbMap.set(workspace.room, idb);
|
||||||
this._logger('Local data loaded');
|
this._logger('Local data loaded');
|
||||||
return workspace;
|
return workspace;
|
||||||
|
|||||||
2615
pnpm-lock.yaml
generated
2615
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user