mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 09:21:24 +08:00
feat:merge dev
This commit is contained in:
@@ -1,10 +1,24 @@
|
|||||||
import { NotFoundTitle, PageContainer } from './styles';
|
import { NotFoundTitle, PageContainer } from './styles';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
|
import { Button } from '@/ui/button';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
export const NotfoundPage = () => {
|
export const NotfoundPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<NotFoundTitle>{t('404 - Page Not Found')}</NotFoundTitle>
|
<NotFoundTitle>
|
||||||
|
{t('404 - Page Not Found')}
|
||||||
|
<p>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
router.push('/workspace');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Back Home
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
</NotFoundTitle>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-1
@@ -23,7 +23,11 @@ export const WorkspaceAvatar = (props: IWorkspaceAvatar) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<picture>
|
<picture>
|
||||||
<img src={props.avatar} alt="" />
|
<img
|
||||||
|
style={{ width: sizeStr, height: sizeStr }}
|
||||||
|
src={props.avatar}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
</picture>
|
</picture>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import type { WorkspaceUnit } from '@affine/datacenter';
|
||||||
|
import { WorkspaceAvatar as Avatar } from './Avatar';
|
||||||
|
|
||||||
|
const useAvatar = (workspaceUnit?: WorkspaceUnit) => {
|
||||||
|
const [avatarUrl, setAvatarUrl] = useState('');
|
||||||
|
const avatarId =
|
||||||
|
workspaceUnit?.avatar || workspaceUnit?.blocksuiteWorkspace?.meta.avatar;
|
||||||
|
useEffect(() => {
|
||||||
|
if (avatarId && workspaceUnit?.blocksuiteWorkspace?.blobs) {
|
||||||
|
workspaceUnit.blocksuiteWorkspace.blobs.then(blobs => {
|
||||||
|
blobs?.get(avatarId).then(url => setAvatarUrl(url || ''));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setAvatarUrl('');
|
||||||
|
}
|
||||||
|
}, [avatarId]);
|
||||||
|
|
||||||
|
return avatarUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceUnitAvatar = ({
|
||||||
|
size = 20,
|
||||||
|
name,
|
||||||
|
workspaceUnit,
|
||||||
|
}: {
|
||||||
|
size?: number;
|
||||||
|
name?: string;
|
||||||
|
workspaceUnit?: WorkspaceUnit | null;
|
||||||
|
}) => {
|
||||||
|
const avatarUrl = useAvatar(workspaceUnit || undefined);
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
size={size}
|
||||||
|
name={name || workspaceUnit?.name || ''}
|
||||||
|
avatar={avatarUrl}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { WorkspaceAvatar } from './Avatar';
|
||||||
|
export { WorkspaceUnitAvatar } from './WorkspaceUnitAvatar';
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
AddIcon,
|
AddIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { toast } from '@/ui/toast';
|
import { toast } from '@/ui/toast';
|
||||||
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
import { WorkspaceUnitAvatar } from '@/components/workspace-avatar';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
@@ -67,11 +67,7 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
|
|||||||
marginRight: '10px',
|
marginRight: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar
|
<WorkspaceUnitAvatar size={50} workspaceUnit={item} />
|
||||||
size={50}
|
|
||||||
name={item.name}
|
|
||||||
avatar={item.avatar ?? ''}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { useAppState } from '@/providers/app-state-provider';
|
|||||||
import { WorkspaceDelete } from './delete';
|
import { WorkspaceDelete } from './delete';
|
||||||
import { WorkspaceLeave } from './leave';
|
import { WorkspaceLeave } from './leave';
|
||||||
import { Upload } from '@/components/file-upload';
|
import { Upload } from '@/components/file-upload';
|
||||||
import { WorkspaceAvatar } from '@/components/workspace-avatar';
|
import { WorkspaceUnitAvatar } from '@/components/workspace-avatar';
|
||||||
import { WorkspaceUnit } from '@affine/datacenter';
|
import { WorkspaceUnit } from '@affine/datacenter';
|
||||||
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
@@ -22,34 +22,19 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
|||||||
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>(workspace.name);
|
const [workspaceName, setWorkspaceName] = useState<string>(workspace.name);
|
||||||
const { currentWorkspace } = useAppState();
|
const { currentWorkspace, isOwner } = useAppState();
|
||||||
const { updateWorkspace } = useWorkspaceHelper();
|
const { updateWorkspace } = useWorkspaceHelper();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const isOwner = true;
|
|
||||||
const handleChangeWorkSpaceName = (newName: string) => {
|
const handleChangeWorkSpaceName = (newName: string) => {
|
||||||
setWorkspaceName(newName);
|
setWorkspaceName(newName);
|
||||||
};
|
};
|
||||||
const handleClickDelete = () => {
|
|
||||||
setShowDelete(true);
|
|
||||||
};
|
|
||||||
const handleCloseDelete = () => {
|
|
||||||
setShowDelete(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClickLeave = () => {
|
|
||||||
setShowLeave(true);
|
|
||||||
};
|
|
||||||
const handleCloseLeave = () => {
|
|
||||||
setShowLeave(false);
|
|
||||||
};
|
|
||||||
const handleUpdateWorkspaceName = () => {
|
const handleUpdateWorkspaceName = () => {
|
||||||
currentWorkspace &&
|
currentWorkspace &&
|
||||||
updateWorkspace({ name: workspaceName }, currentWorkspace);
|
updateWorkspace({ name: workspaceName }, currentWorkspace);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileChange = async (file: File) => {
|
const fileChange = async (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 });
|
||||||
currentWorkspace &&
|
currentWorkspace &&
|
||||||
@@ -67,10 +52,10 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
|||||||
marginRight: '20px',
|
marginRight: '20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar
|
<WorkspaceUnitAvatar
|
||||||
size={60}
|
size={60}
|
||||||
name={workspace.name}
|
name={workspace.name}
|
||||||
avatar={workspace.avatar ?? ''}
|
workspaceUnit={workspace}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Upload
|
<Upload
|
||||||
@@ -89,16 +74,19 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
|||||||
placeholder={t('Workspace Name')}
|
placeholder={t('Workspace Name')}
|
||||||
maxLength={14}
|
maxLength={14}
|
||||||
minLength={1}
|
minLength={1}
|
||||||
|
disabled={!isOwner}
|
||||||
onChange={handleChangeWorkSpaceName}
|
onChange={handleChangeWorkSpaceName}
|
||||||
></Input>
|
></Input>
|
||||||
<TextButton
|
{isOwner ? (
|
||||||
onClick={() => {
|
<TextButton
|
||||||
handleUpdateWorkspaceName();
|
onClick={() => {
|
||||||
}}
|
handleUpdateWorkspaceName();
|
||||||
style={{ marginLeft: '0px' }}
|
}}
|
||||||
>
|
style={{ marginLeft: '0px' }}
|
||||||
✔️
|
>
|
||||||
</TextButton>
|
✔️
|
||||||
|
</TextButton>
|
||||||
|
) : null}
|
||||||
</StyledSettingInputContainer>
|
</StyledSettingInputContainer>
|
||||||
<StyledSettingH2 marginTop={20}>{t('Workspace Type')}</StyledSettingH2>
|
<StyledSettingH2 marginTop={20}>{t('Workspace Type')}</StyledSettingH2>
|
||||||
<StyledSettingInputContainer>
|
<StyledSettingInputContainer>
|
||||||
@@ -107,25 +95,39 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
|||||||
<StyledDeleteButtonContainer>
|
<StyledDeleteButtonContainer>
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
<>
|
<>
|
||||||
<Button type="danger" shape="circle" onClick={handleClickDelete}>
|
<Button
|
||||||
|
type="danger"
|
||||||
|
shape="circle"
|
||||||
|
onClick={() => {
|
||||||
|
setShowDelete(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('Delete Workspace')}
|
{t('Delete Workspace')}
|
||||||
</Button>
|
</Button>
|
||||||
<WorkspaceDelete
|
<WorkspaceDelete
|
||||||
open={showDelete}
|
open={showDelete}
|
||||||
onClose={handleCloseDelete}
|
onClose={() => {
|
||||||
|
setShowDelete(false);
|
||||||
|
}}
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Button type="danger" shape="circle" onClick={handleClickLeave}>
|
<Button
|
||||||
|
type="danger"
|
||||||
|
shape="circle"
|
||||||
|
onClick={() => {
|
||||||
|
setShowLeave(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('Leave Workspace')}
|
{t('Leave Workspace')}
|
||||||
</Button>
|
</Button>
|
||||||
<WorkspaceLeave
|
<WorkspaceLeave
|
||||||
open={showLeave}
|
open={showLeave}
|
||||||
onClose={handleCloseLeave}
|
onClose={() => {
|
||||||
workspaceName={workspaceName}
|
setShowLeave(false);
|
||||||
workspaceId={workspace.id}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -12,13 +12,11 @@ import { useState } from 'react';
|
|||||||
import { ModalCloseButton } from '@/ui/modal';
|
import { ModalCloseButton } from '@/ui/modal';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import {
|
|
||||||
deleteWorkspace,
|
|
||||||
getWorkspaces,
|
|
||||||
// Workspace,
|
|
||||||
} from '@/hooks/mock-data/mock';
|
|
||||||
import { WorkspaceUnit } from '@affine/datacenter';
|
import { WorkspaceUnit } from '@affine/datacenter';
|
||||||
import { Trans, useTranslation } from '@affine/i18n';
|
import { Trans, useTranslation } from '@affine/i18n';
|
||||||
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
|
|
||||||
interface WorkspaceDeleteProps {
|
interface WorkspaceDeleteProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -33,23 +31,15 @@ export const WorkspaceDelete = ({
|
|||||||
const [deleteStr, setDeleteStr] = useState<string>('');
|
const [deleteStr, setDeleteStr] = useState<string>('');
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { deleteWorkSpace } = useWorkspaceHelper();
|
||||||
const handlerInputChange = (workspaceName: string) => {
|
const handlerInputChange = (workspaceName: string) => {
|
||||||
setDeleteStr(workspaceName);
|
setDeleteStr(workspaceName);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
// const dc = await getDataCenter();
|
await deleteWorkSpace();
|
||||||
// await dc.apis.deleteWorkspace({ id: workspaceId });
|
|
||||||
// router.push(`/workspace/${nextWorkSpaceId}`);
|
|
||||||
deleteWorkspace(workspace.id);
|
|
||||||
const workspaceList = getWorkspaces();
|
|
||||||
if (workspaceList.length) {
|
|
||||||
router.push(`/workspace/${workspaceList[0].id}`);
|
|
||||||
} else {
|
|
||||||
router.push(`/workspace`);
|
|
||||||
}
|
|
||||||
onClose();
|
onClose();
|
||||||
|
router.push(`/workspace`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -8,30 +8,20 @@ import {
|
|||||||
import { ModalCloseButton } from '@/ui/modal';
|
import { ModalCloseButton } from '@/ui/modal';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
// 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;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
workspaceName: string;
|
|
||||||
workspaceId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceLeave = ({
|
export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => {
|
||||||
open,
|
const { leaveWorkSpace } = useWorkspaceHelper();
|
||||||
onClose,
|
|
||||||
workspaceId,
|
|
||||||
}: WorkspaceDeleteProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
console.log('workspaceId: ', workspaceId);
|
|
||||||
// const router = useRouter();
|
|
||||||
// const { refreshWorkspacesMeta } = useAppState();
|
|
||||||
const handleLeave = async () => {
|
const handleLeave = async () => {
|
||||||
// const dc = await getDataCenter();
|
await leaveWorkSpace();
|
||||||
// await dc.apis.leaveWorkspace({ id: workspaceId });
|
|
||||||
// // router.push(`/workspace/${nextWorkSpaceId}`);
|
|
||||||
// refreshWorkspacesMeta();
|
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,20 +20,20 @@ import { InviteMemberModal } from './InviteMemberModal';
|
|||||||
import { Menu, MenuItem } from '@/ui/menu';
|
import { Menu, MenuItem } from '@/ui/menu';
|
||||||
import { Empty } from '@/ui/empty';
|
import { Empty } from '@/ui/empty';
|
||||||
import { WorkspaceUnit } from '@affine/datacenter';
|
import { WorkspaceUnit } from '@affine/datacenter';
|
||||||
import { useTemporaryHelper } from '@/providers/temporary-helper-provider';
|
|
||||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||||
import { toast } from '@/ui/toast';
|
import { toast } from '@/ui/toast';
|
||||||
import useMembers from '@/hooks/use-members';
|
import useMembers from '@/hooks/use-members';
|
||||||
import Loading from '@/components/loading';
|
import Loading from '@/components/loading';
|
||||||
import { Wrapper } from '@/ui/layout';
|
import { Wrapper } from '@/ui/layout';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
|
|
||||||
export const MembersPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
export const MembersPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
||||||
const [isInviteModalShow, setIsInviteModalShow] = useState(false);
|
const [isInviteModalShow, setIsInviteModalShow] = useState(false);
|
||||||
const { members, removeMember, loaded } = useMembers();
|
const { members, removeMember, loaded } = useMembers();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// FIXME: DELETE THIS
|
// FIXME: DELETE THIS
|
||||||
const { user, login, updateWorkspaceMeta } = useTemporaryHelper();
|
const { enableWorkspace } = useWorkspaceHelper();
|
||||||
const { confirm } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
|
|
||||||
if (workspace.provider === 'affine') {
|
if (workspace.provider === 'affine') {
|
||||||
@@ -164,22 +164,8 @@ export const MembersPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
confirm({
|
await enableWorkspace();
|
||||||
title: `${t('Enable AFFiNE Cloud')}?`,
|
|
||||||
content: t('Enable AFFiNE Cloud Description'),
|
|
||||||
confirmText: user ? t('Enable') : t('Sign in and Enable'),
|
|
||||||
cancelText: t('Skip'),
|
|
||||||
}).then(confirm => {
|
|
||||||
if (confirm) {
|
|
||||||
if (user) {
|
|
||||||
updateWorkspaceMeta(workspace.id, { isPublish: true });
|
|
||||||
} else {
|
|
||||||
login();
|
|
||||||
updateWorkspaceMeta(workspace.id, { isPublish: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Enable AFFiNE Cloud')}
|
{t('Enable AFFiNE Cloud')}
|
||||||
|
|||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
import { Avatar, WorkspaceName, SelectorWrapper } from './styles';
|
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 { WorkspaceUnitAvatar } from '@/components/workspace-avatar';
|
||||||
import { useAppState } from '@/providers/app-state-provider';
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
export const WorkspaceSelector = () => {
|
export const WorkspaceSelector = () => {
|
||||||
const [workspaceListShow, setWorkspaceListShow] = useState(false);
|
const [workspaceListShow, setWorkspaceListShow] = useState(false);
|
||||||
@@ -23,17 +23,17 @@ export const WorkspaceSelector = () => {
|
|||||||
<Avatar
|
<Avatar
|
||||||
alt="Affine"
|
alt="Affine"
|
||||||
data-testid="workspace-avatar"
|
data-testid="workspace-avatar"
|
||||||
src={currentWorkspace?.avatar}
|
// src={currentWorkspace?.avatar}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
float: 'left',
|
float: 'left',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar
|
<WorkspaceUnitAvatar
|
||||||
size={28}
|
size={28}
|
||||||
name={currentWorkspace?.name ?? 'AFFiNE'}
|
name={currentWorkspace?.name ?? 'AFFiNE'}
|
||||||
avatar={currentWorkspace?.avatar ?? ''}
|
workspaceUnit={currentWorkspace}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
export interface Workspace {
|
|
||||||
name: string; // 名称
|
|
||||||
id: string; //唯一标识
|
|
||||||
isPublish?: boolean; // 是否公开
|
|
||||||
isLocal?: boolean; // 是否全部数据都在本地
|
|
||||||
avatar?: string; // 封面
|
|
||||||
type: 'local' | 'cloud' | 'join'; // cloud: 云端(本次暂不支持),local: 本地,join : 加入别人的
|
|
||||||
workspaceOwner?: User; // 本地工作空间的拥有者
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface User {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
avatar: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateWorkspaceMeta(
|
|
||||||
workspaceId: string,
|
|
||||||
workspaceData: {
|
|
||||||
name?: string;
|
|
||||||
avatar?: string;
|
|
||||||
type?: 'local' | 'cloud' | 'join';
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.map((workspace: Workspace) => {
|
|
||||||
if (workspace.id === workspaceId) {
|
|
||||||
workspaceData.name && (workspace.name = workspaceData.name);
|
|
||||||
workspaceData.avatar && (workspace.avatar = workspaceData.avatar);
|
|
||||||
workspaceData.type && (workspace.type = workspaceData.type);
|
|
||||||
return workspaceData;
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
localStorage.setItem('affine-workspace', JSON.stringify(newWorkspacesMeta));
|
|
||||||
const activeWorkspace = getActiveWorkspace();
|
|
||||||
workspaceData.name && (activeWorkspace.name = workspaceData.name);
|
|
||||||
workspaceData.avatar && (activeWorkspace.avatar = workspaceData.avatar);
|
|
||||||
|
|
||||||
workspaceData.type && (activeWorkspace.type = workspaceData.type);
|
|
||||||
console.log(workspaceData);
|
|
||||||
setActiveWorkspace(activeWorkspace);
|
|
||||||
}
|
|
||||||
export function createWorkspace(workspaceName: string) {
|
|
||||||
const workspaceId = 'workspace-' + Date.now();
|
|
||||||
const workspaceData = {
|
|
||||||
name: workspaceName,
|
|
||||||
id: workspaceId,
|
|
||||||
isPublish: false,
|
|
||||||
isLocal: true,
|
|
||||||
avatar: '',
|
|
||||||
type: 'local',
|
|
||||||
} as Workspace;
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
workspacesMeta.push(workspaceData);
|
|
||||||
localStorage.setItem('affine-workspace', JSON.stringify(workspacesMeta));
|
|
||||||
setActiveWorkspace(workspaceData);
|
|
||||||
return { workspaceId };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorkspaces(): Workspace[] {
|
|
||||||
const workspacesMeta = JSON.parse(
|
|
||||||
localStorage.getItem('affine-workspace') ?? '[]'
|
|
||||||
);
|
|
||||||
return workspacesMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteWorkspace(workspaceId: string) {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.filter(() => {
|
|
||||||
return workspaceId !== workspaceId;
|
|
||||||
});
|
|
||||||
localStorage.setItem('affine-workspace', JSON.stringify(newWorkspacesMeta));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMembers(id: string): User[] {
|
|
||||||
const memberMap = JSON.parse(localStorage.getItem('affine-member') ?? '{}');
|
|
||||||
return memberMap[id] || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setMember(workspaceId: string, member: User) {
|
|
||||||
const memberMap = JSON.parse(localStorage.getItem('affine-member') ?? '{}');
|
|
||||||
memberMap[workspaceId] = memberMap[workspaceId] || [];
|
|
||||||
memberMap[workspaceId].push(member);
|
|
||||||
localStorage.setItem('affine-member', JSON.stringify(memberMap));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteMember(workspaceId: string, index: number) {
|
|
||||||
const memberMap = JSON.parse(localStorage.getItem('affine-member') ?? '{}');
|
|
||||||
const memberList = memberMap[workspaceId];
|
|
||||||
memberList.splice(index, 1);
|
|
||||||
memberMap[workspaceId] = memberList;
|
|
||||||
localStorage.setItem('affine-member', JSON.stringify(memberMap));
|
|
||||||
}
|
|
||||||
export function leaveWorkspace() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setWorkspacePublish(id: string, isPublish: boolean): boolean {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.map((workspace: Workspace) => {
|
|
||||||
if (workspace.id === id) {
|
|
||||||
workspace.isPublish = isPublish;
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
});
|
|
||||||
localStorage.setItem('affine-workspace', JSON.stringify(newWorkspacesMeta));
|
|
||||||
return isPublish;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorkspaceById(id: string) {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
return workspacesMeta.find((workspace: Workspace) => {
|
|
||||||
return workspace.id === id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPagesByWorkspaceId(workspaceId: string) {
|
|
||||||
if (!workspaceId) return [];
|
|
||||||
const workspacesMeta = [];
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
workspacesMeta.push({
|
|
||||||
id: 'page-' + i,
|
|
||||||
name: 'page ' + i,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getActiveWorkspace(): Workspace {
|
|
||||||
return JSON.parse(localStorage.getItem('affine-active-workspace') ?? '{}');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setActiveWorkspace(workspaceData: Workspace) {
|
|
||||||
console.log('workspaceData: ', workspaceData);
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-active-workspace',
|
|
||||||
JSON.stringify(workspaceData)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserInfo(): User {
|
|
||||||
return JSON.parse(localStorage.getItem('affine-user') ?? 'null');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Login(): void {
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-user',
|
|
||||||
JSON.stringify({
|
|
||||||
name: 'Diamond',
|
|
||||||
id: 'ttt',
|
|
||||||
email: 'diamond.shx@gmail.com',
|
|
||||||
avatar: 'string',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SignOut(): void {
|
|
||||||
localStorage.removeItem('affine-user');
|
|
||||||
}
|
|
||||||
@@ -44,22 +44,41 @@ export const useWorkspaceHelper = () => {
|
|||||||
cancelText: 'Skip',
|
cancelText: 'Skip',
|
||||||
}).then(async confirm => {
|
}).then(async confirm => {
|
||||||
if (confirm && currentWorkspace) {
|
if (confirm && currentWorkspace) {
|
||||||
if (user) {
|
if (!user) {
|
||||||
await login();
|
await login();
|
||||||
}
|
}
|
||||||
const newWorkspaceId = await dataCenter.enableWorkspaceCloud(
|
const workspace = await dataCenter.enableWorkspaceCloud(
|
||||||
currentWorkspace
|
currentWorkspace
|
||||||
);
|
);
|
||||||
router.push(`/workspace/${newWorkspaceId}/setting`);
|
workspace && router.push(`/workspace/${workspace.id}/setting`);
|
||||||
toast('Enabled success');
|
toast('Enabled success');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteWorkSpace = async () => {
|
||||||
|
currentWorkspace &&
|
||||||
|
(await dataCenter.deleteWorkspace(currentWorkspace?.id));
|
||||||
|
};
|
||||||
|
const leaveWorkSpace = async () => {
|
||||||
|
currentWorkspace && (await dataCenter.leaveWorkspace(currentWorkspace?.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptInvite = async (inviteCode: string) => {
|
||||||
|
let inviteInfo;
|
||||||
|
if (inviteCode) {
|
||||||
|
inviteInfo = await dataCenter.acceptInvitation(inviteCode);
|
||||||
|
}
|
||||||
|
return inviteInfo;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createWorkspace,
|
createWorkspace,
|
||||||
publishWorkspace,
|
publishWorkspace,
|
||||||
updateWorkspace,
|
updateWorkspace,
|
||||||
enableWorkspace,
|
enableWorkspace,
|
||||||
|
deleteWorkSpace,
|
||||||
|
leaveWorkSpace,
|
||||||
|
acceptInvite,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ 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 '@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,
|
||||||
@@ -55,10 +54,8 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
|
|||||||
<Logger />
|
<Logger />
|
||||||
<ProviderComposer
|
<ProviderComposer
|
||||||
contexts={[
|
contexts={[
|
||||||
<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" />,
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,44 +1,37 @@
|
|||||||
|
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
|
||||||
|
import { useAppState } from '@/providers/app-state-provider';
|
||||||
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 { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
const User = ({ name, avatar }: { name: string; avatar?: string }) => {
|
// const User = ({ name, avatar }: { name: string; avatar?: string }) => {
|
||||||
return (
|
// return (
|
||||||
<UserContent>
|
// <UserContent>
|
||||||
{avatar ? (
|
// {avatar ? (
|
||||||
<Avatar src={avatar}></Avatar>
|
// <Avatar src={avatar}></Avatar>
|
||||||
) : (
|
// ) : (
|
||||||
<UserIcon>{name.slice(0, 1)}</UserIcon>
|
// <UserIcon>{name.slice(0, 1)}</UserIcon>
|
||||||
)}
|
// )}
|
||||||
<span>{name}</span>
|
// <span>{name}</span>
|
||||||
</UserContent>
|
// </UserContent>
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
export default function DevPage() {
|
export default function DevPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [successInvited, setSuccessInvited] = useState<boolean>(false);
|
const [successInvited, setSuccessInvited] = useState<boolean>(false);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const { acceptInvite } = useWorkspaceHelper();
|
||||||
const [inviteData, setInviteData] = useState<any>(null);
|
const { user } = useAppState();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// getDataCenter()
|
router.query.invite_code &&
|
||||||
// .then(dc =>
|
acceptInvite(router.query.invite_code as string).then(data => {
|
||||||
// dc.apis.acceptInviting({
|
if (data && data.accepted) {
|
||||||
// invitingCode: router.query.invite_code as string,
|
setSuccessInvited(true);
|
||||||
// })
|
user && router.push(`/workspace/${data.workspace_id}`);
|
||||||
// )
|
}
|
||||||
// .then(data => {
|
});
|
||||||
// setSuccessInvited(true);
|
|
||||||
// setInviteData(data);
|
|
||||||
// })
|
|
||||||
// .catch(err => {
|
|
||||||
// console.log('err: ', err);
|
|
||||||
// });
|
|
||||||
setSuccessInvited(true);
|
|
||||||
setInviteData(null);
|
|
||||||
}, [router.query.invite_code]);
|
}, [router.query.invite_code]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -47,11 +40,12 @@ export default function DevPage() {
|
|||||||
<Empty width={310} height={310}></Empty>
|
<Empty width={310} height={310}></Empty>
|
||||||
|
|
||||||
<Content>
|
<Content>
|
||||||
<User name={inviteData?.name ? inviteData.name : '-'}></User> invited
|
{/* TODO add inviteInfo*/}
|
||||||
you to join
|
{/* <User name={inviteData?.name ? inviteData.name : '-'}></User> invited */}
|
||||||
<User
|
{/* you to join */}
|
||||||
|
{/* <User
|
||||||
name={inviteData?.workspaceName ? inviteData.workspaceName : '-'}
|
name={inviteData?.workspaceName ? inviteData.workspaceName : '-'}
|
||||||
></User>
|
></User> */}
|
||||||
{successInvited ? (
|
{successInvited ? (
|
||||||
<Status>
|
<Status>
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import { WorkspaceModal } from '@/components/workspace-modal';
|
|
||||||
import { getWorkspaces } from '@/hooks/mock-data/mock';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { styled } from '@/styles';
|
|
||||||
import Button from '@/ui/button/Button';
|
|
||||||
|
|
||||||
const Page = () => {
|
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const data = getWorkspaces();
|
|
||||||
if (!data.length) {
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<Workspace>
|
|
||||||
<h1>workspace</h1>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
View Workspace List
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<WorkspaceModal
|
|
||||||
open={open}
|
|
||||||
onClose={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
></WorkspaceModal>
|
|
||||||
</Workspace>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Page;
|
|
||||||
|
|
||||||
const Workspace = styled.div(({ theme }) => {
|
|
||||||
return {
|
|
||||||
height: '100vh',
|
|
||||||
background: theme.colors.pageBackground,
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '18px',
|
|
||||||
fontWeight: 500,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -61,8 +61,9 @@ const tabMap: {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const WorkspaceSetting = () => {
|
const WorkspaceSetting = () => {
|
||||||
const { currentWorkspace } = useAppState();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentWorkspace, isOwner } = useAppState();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<TabNames>(tabMap[0].name);
|
const [activeTab, setActiveTab] = useState<TabNames>(tabMap[0].name);
|
||||||
const handleTabChange = (tab: TabNames) => {
|
const handleTabChange = (tab: TabNames) => {
|
||||||
setActiveTab(tab);
|
setActiveTab(tab);
|
||||||
@@ -71,7 +72,20 @@ const WorkspaceSetting = () => {
|
|||||||
const activeTabPanelRender = tabMap.find(
|
const activeTabPanelRender = tabMap.find(
|
||||||
tab => tab.name === activeTab
|
tab => tab.name === activeTab
|
||||||
)?.panelRender;
|
)?.panelRender;
|
||||||
|
let tableArr: {
|
||||||
|
name: TabNames;
|
||||||
|
icon: ReactNode;
|
||||||
|
panelRender: (workspace: WorkspaceUnit) => ReactNode;
|
||||||
|
}[] = tabMap;
|
||||||
|
if (!isOwner) {
|
||||||
|
tableArr = [
|
||||||
|
{
|
||||||
|
name: 'general',
|
||||||
|
icon: <EditIcon />,
|
||||||
|
panelRender: workspace => <GeneralPage workspace={workspace} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<StyledSettingContainer>
|
<StyledSettingContainer>
|
||||||
<StyledSettingSidebar>
|
<StyledSettingSidebar>
|
||||||
@@ -79,7 +93,7 @@ const WorkspaceSetting = () => {
|
|||||||
{t('Workspace Settings')}
|
{t('Workspace Settings')}
|
||||||
</StyledSettingSidebarHeader>
|
</StyledSettingSidebarHeader>
|
||||||
<StyledSettingTabContainer>
|
<StyledSettingTabContainer>
|
||||||
{tabMap.map(({ icon, name }) => {
|
{tableArr.map(({ icon, name }) => {
|
||||||
return (
|
return (
|
||||||
<WorkspaceSettingTagItem
|
<WorkspaceSettingTagItem
|
||||||
key={name}
|
key={name}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const AppStateProvider = ({
|
|||||||
currentPage: null,
|
currentPage: null,
|
||||||
editor: null,
|
editor: null,
|
||||||
synced: true,
|
synced: true,
|
||||||
|
isOwner: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ export const AppStateProvider = ({
|
|||||||
|
|
||||||
const loadWorkspace = useRef<AppStateFunction['loadWorkspace']>();
|
const loadWorkspace = useRef<AppStateFunction['loadWorkspace']>();
|
||||||
loadWorkspace.current = async (workspaceId: string) => {
|
loadWorkspace.current = async (workspaceId: string) => {
|
||||||
const { dataCenter, workspaceList, currentWorkspace } = appState;
|
const { dataCenter, workspaceList, currentWorkspace, user } = appState;
|
||||||
if (!workspaceList.find(v => v.id.toString() === workspaceId)) {
|
if (!workspaceList.find(v => v.id.toString() === workspaceId)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -99,6 +100,13 @@ export const AppStateProvider = ({
|
|||||||
return currentWorkspace;
|
return currentWorkspace;
|
||||||
}
|
}
|
||||||
const workspace = (await dataCenter.loadWorkspace(workspaceId)) ?? null;
|
const workspace = (await dataCenter.loadWorkspace(workspaceId)) ?? null;
|
||||||
|
let isOwner;
|
||||||
|
if (workspace.provider === 'local') {
|
||||||
|
// isOwner is useful only in the cloud
|
||||||
|
isOwner = true;
|
||||||
|
} else {
|
||||||
|
isOwner = workspace?.owner && user?.id === workspace?.owner?.id;
|
||||||
|
}
|
||||||
const pageList =
|
const pageList =
|
||||||
(workspace?.blocksuiteWorkspace?.meta.pageMetas as PageMeta[]) ?? [];
|
(workspace?.blocksuiteWorkspace?.meta.pageMetas as PageMeta[]) ?? [];
|
||||||
setAppState({
|
setAppState({
|
||||||
@@ -107,6 +115,7 @@ export const AppStateProvider = ({
|
|||||||
pageList: pageList,
|
pageList: pageList,
|
||||||
currentPage: null,
|
currentPage: null,
|
||||||
editor: null,
|
editor: null,
|
||||||
|
isOwner,
|
||||||
});
|
});
|
||||||
|
|
||||||
return workspace;
|
return workspace;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type AppStateValue = {
|
|||||||
currentPage: StorePage | null;
|
currentPage: StorePage | null;
|
||||||
editor?: EditorContainer | null;
|
editor?: EditorContainer | null;
|
||||||
synced: boolean;
|
synced: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppStateFunction = {
|
export type AppStateFunction = {
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
import { createContext, useContext, useEffect, useState } from 'react';
|
|
||||||
import type { PropsWithChildren } from 'react';
|
|
||||||
import { getWorkspaces, User, Workspace } from '@/hooks/mock-data/mock';
|
|
||||||
|
|
||||||
type TemporaryContextValue = {
|
|
||||||
workspaceMetaList: Workspace[];
|
|
||||||
currentWorkspace: Workspace | null;
|
|
||||||
user: User | null;
|
|
||||||
|
|
||||||
updateWorkspaceMeta: (
|
|
||||||
workspaceId: string,
|
|
||||||
workspaceData: {
|
|
||||||
name?: string;
|
|
||||||
avatar?: string;
|
|
||||||
isPublish?: boolean;
|
|
||||||
type?: 'local' | 'cloud' | 'join';
|
|
||||||
}
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
createWorkspace: (workspaceName: string) => Workspace;
|
|
||||||
|
|
||||||
deleteWorkspace: (workspaceId: string) => void;
|
|
||||||
|
|
||||||
setWorkspacePublish: (id: string, isPublish: boolean) => void;
|
|
||||||
|
|
||||||
setActiveWorkspace: (workspaceData: Workspace) => void;
|
|
||||||
|
|
||||||
login: () => void;
|
|
||||||
|
|
||||||
signOut: () => void;
|
|
||||||
};
|
|
||||||
type TemporaryContextProps = PropsWithChildren<Record<string, unknown>>;
|
|
||||||
|
|
||||||
export const TemporaryContext = createContext<TemporaryContextValue>({
|
|
||||||
workspaceMetaList: [],
|
|
||||||
currentWorkspace: null,
|
|
||||||
user: null,
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
updateWorkspaceMeta: () => {},
|
|
||||||
|
|
||||||
createWorkspace: () => {
|
|
||||||
return {} as Workspace;
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
deleteWorkspace: () => {},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
setWorkspacePublish: () => {},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
setActiveWorkspace: () => {},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
login: () => {},
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
signOut: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useTemporaryHelper = () => useContext(TemporaryContext);
|
|
||||||
|
|
||||||
export const TemporaryHelperProvider = ({
|
|
||||||
children,
|
|
||||||
}: PropsWithChildren<TemporaryContextProps>) => {
|
|
||||||
const [workspaceMetaList, setWorkspaceMetaList] = useState<Workspace[]>([]);
|
|
||||||
const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [user, setUser] = useState<User | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setWorkspaceMetaList(getWorkspaces());
|
|
||||||
setCurrentWorkspace(
|
|
||||||
JSON.parse(localStorage.getItem('affine-active-workspace') ?? '{}')
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TemporaryContext.Provider
|
|
||||||
value={{
|
|
||||||
workspaceMetaList,
|
|
||||||
currentWorkspace,
|
|
||||||
user,
|
|
||||||
|
|
||||||
updateWorkspaceMeta: (workspaceId, workspaceData) => {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.map(
|
|
||||||
(workspace: Workspace) => {
|
|
||||||
if (workspace.id === workspaceId) {
|
|
||||||
const workspaceObj = Object.assign(workspace, workspaceData);
|
|
||||||
return workspaceObj;
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-workspace',
|
|
||||||
JSON.stringify(newWorkspacesMeta)
|
|
||||||
);
|
|
||||||
setWorkspaceMetaList(newWorkspacesMeta);
|
|
||||||
if (workspaceId === currentWorkspace?.id) {
|
|
||||||
setCurrentWorkspace(
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
newWorkspacesMeta.find(v => v.id === currentWorkspace.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
createWorkspace: workspaceName => {
|
|
||||||
const workspaceData = {
|
|
||||||
name: workspaceName,
|
|
||||||
id: 'workspace-' + Date.now(),
|
|
||||||
isPublish: false,
|
|
||||||
isLocal: true,
|
|
||||||
avatar: '',
|
|
||||||
type: 'local',
|
|
||||||
} as Workspace;
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
workspacesMeta.push(workspaceData);
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-workspace',
|
|
||||||
JSON.stringify(workspacesMeta)
|
|
||||||
);
|
|
||||||
|
|
||||||
setWorkspaceMetaList([...workspacesMeta]);
|
|
||||||
return workspaceData;
|
|
||||||
},
|
|
||||||
deleteWorkspace: workspaceId => {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.filter(() => {
|
|
||||||
return workspaceId !== workspaceId;
|
|
||||||
});
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-workspace',
|
|
||||||
JSON.stringify(newWorkspacesMeta)
|
|
||||||
);
|
|
||||||
setWorkspaceMetaList(workspacesMeta);
|
|
||||||
},
|
|
||||||
|
|
||||||
setWorkspacePublish: (id, isPublish) => {
|
|
||||||
const workspacesMeta = getWorkspaces();
|
|
||||||
const newWorkspacesMeta = workspacesMeta.map(
|
|
||||||
(workspace: Workspace) => {
|
|
||||||
if (workspace.id === id) {
|
|
||||||
workspace.isPublish = isPublish;
|
|
||||||
}
|
|
||||||
return workspace;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-workspace',
|
|
||||||
JSON.stringify(newWorkspacesMeta)
|
|
||||||
);
|
|
||||||
setWorkspaceMetaList(workspacesMeta);
|
|
||||||
},
|
|
||||||
setActiveWorkspace(workspaceData) {
|
|
||||||
localStorage.setItem(
|
|
||||||
'affine-active-workspace',
|
|
||||||
JSON.stringify(workspaceData)
|
|
||||||
);
|
|
||||||
setCurrentWorkspace(workspaceData);
|
|
||||||
},
|
|
||||||
|
|
||||||
login: () => {
|
|
||||||
const userInfo = {
|
|
||||||
name: 'Diamond',
|
|
||||||
id: 'ttt',
|
|
||||||
email: 'diamond.shx@gmail.com',
|
|
||||||
avatar: 'string',
|
|
||||||
};
|
|
||||||
localStorage.setItem('affine-user', JSON.stringify(userInfo));
|
|
||||||
setUser(userInfo);
|
|
||||||
},
|
|
||||||
|
|
||||||
signOut(): void {
|
|
||||||
localStorage.removeItem('affine-user');
|
|
||||||
setUser(null);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</TemporaryContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TemporaryHelperProvider;
|
|
||||||
@@ -23,7 +23,7 @@ export class DataCenter {
|
|||||||
private readonly _workspaceUnitCollection = new WorkspaceUnitCollection();
|
private readonly _workspaceUnitCollection = new WorkspaceUnitCollection();
|
||||||
private readonly _logger = getLogger('dc');
|
private readonly _logger = getLogger('dc');
|
||||||
private _workspaceInstances: Map<string, BlocksuiteWorkspace> = new Map();
|
private _workspaceInstances: Map<string, BlocksuiteWorkspace> = new Map();
|
||||||
private _messageCenter = new MessageCenter();
|
private _messageCenter = MessageCenter.getInstance();
|
||||||
/**
|
/**
|
||||||
* A mainProvider must exist as the only data trustworthy source.
|
* A mainProvider must exist as the only data trustworthy source.
|
||||||
*/
|
*/
|
||||||
@@ -96,12 +96,7 @@ export class DataCenter {
|
|||||||
'There is no provider. You should add provider first.'
|
'There is no provider. You should add provider first.'
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaceMeta = await this._mainProvider.createWorkspaceInfo(params);
|
const workspaceUnit = await this._mainProvider.createWorkspace(params);
|
||||||
|
|
||||||
const workspace = createBlocksuiteWorkspace(workspaceMeta.id);
|
|
||||||
|
|
||||||
await this._mainProvider.createWorkspace(workspace, workspaceMeta);
|
|
||||||
const workspaceUnit = this._workspaceUnitCollection.find(workspaceMeta.id);
|
|
||||||
return workspaceUnit;
|
return workspaceUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +164,9 @@ export class DataCenter {
|
|||||||
this._workspaceInstances.set(workspaceId, workspace);
|
this._workspaceInstances.set(workspaceId, workspace);
|
||||||
await provider.warpWorkspace(workspace);
|
await provider.warpWorkspace(workspace);
|
||||||
this._workspaceUnitCollection.workspaces.forEach(workspaceUnit => {
|
this._workspaceUnitCollection.workspaces.forEach(workspaceUnit => {
|
||||||
workspaceUnit.setBlocksuiteWorkspace(null);
|
const provider = this.providerMap.get(workspaceUnit.provider);
|
||||||
|
assert(provider);
|
||||||
|
provider.closeWorkspace(workspaceUnit.id);
|
||||||
});
|
});
|
||||||
workspaceUnit.setBlocksuiteWorkspace(workspace);
|
workspaceUnit.setBlocksuiteWorkspace(workspace);
|
||||||
return workspaceUnit;
|
return workspaceUnit;
|
||||||
@@ -322,55 +319,33 @@ export class DataCenter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _transWorkspaceProvider(
|
public async enableProvider(
|
||||||
workspace: BlocksuiteWorkspace,
|
workspaceUnit: WorkspaceUnit,
|
||||||
providerId: string
|
providerId = 'affine'
|
||||||
) {
|
) {
|
||||||
assert(workspace.room, 'No workspace id');
|
if (workspaceUnit.provider === providerId) {
|
||||||
const workspaceInfo = this._workspaceUnitCollection.find(workspace.room);
|
|
||||||
assert(workspaceInfo, 'Workspace not found');
|
|
||||||
if (workspaceInfo.provider === providerId) {
|
|
||||||
this._logger('Workspace provider is same');
|
this._logger('Workspace provider is same');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentProvider = this.providerMap.get(workspaceInfo.provider);
|
const provider = this.providerMap.get(providerId);
|
||||||
assert(currentProvider, 'Provider not found');
|
assert(provider);
|
||||||
const newProvider = this.providerMap.get(providerId);
|
const newWorkspaceUnit = await provider.extendWorkspace(workspaceUnit);
|
||||||
assert(newProvider, `provide '${providerId}' is not registered`);
|
|
||||||
this._logger(`create ${providerId} workspace: `, workspaceInfo.name);
|
|
||||||
const newWorkspaceInfo = await newProvider.createWorkspaceInfo({
|
|
||||||
name: workspaceInfo.name,
|
|
||||||
// avatar: workspaceInfo.avatar,
|
|
||||||
});
|
|
||||||
const newWorkspace = createBlocksuiteWorkspace(newWorkspaceInfo.id);
|
|
||||||
// TODO optimize this function
|
|
||||||
await newProvider.createWorkspace(newWorkspace, {
|
|
||||||
...newWorkspaceInfo,
|
|
||||||
name: workspaceInfo.name,
|
|
||||||
avatar: workspaceInfo.avatar,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert(newWorkspace, 'Create workspace failed');
|
// Currently we only allow enable one provider, so after enable new provider,
|
||||||
this._logger(
|
// delete the old workspace from its provider.
|
||||||
`update workspace data from ${workspaceInfo.provider} to ${providerId}`
|
const oldProvider = this.providerMap.get(workspaceUnit.provider);
|
||||||
);
|
assert(oldProvider);
|
||||||
await newProvider.assign(newWorkspace, workspace);
|
await oldProvider.deleteWorkspace(workspaceUnit.id);
|
||||||
assert(newWorkspace, 'Create workspace failed');
|
|
||||||
await currentProvider.deleteWorkspace(workspace.room);
|
return newWorkspaceUnit;
|
||||||
return newWorkspace.room;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable workspace cloud
|
* Enable workspace cloud
|
||||||
* @param {string} id ID of workspace.
|
* @param {string} id ID of workspace.
|
||||||
*/
|
*/
|
||||||
public async enableWorkspaceCloud(workspace: WorkspaceUnit) {
|
public async enableWorkspaceCloud(workspaceUnit: WorkspaceUnit) {
|
||||||
assert(workspace?.id, 'No workspace to enable cloud');
|
return this.enableProvider(workspaceUnit);
|
||||||
assert(workspace.blocksuiteWorkspace);
|
|
||||||
return await this._transWorkspaceProvider(
|
|
||||||
workspace.blocksuiteWorkspace,
|
|
||||||
'affine'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -441,13 +416,14 @@ export class DataCenter {
|
|||||||
/**
|
/**
|
||||||
* accept invitation
|
* accept invitation
|
||||||
* @param {string} inviteCode
|
* @param {string} inviteCode
|
||||||
|
* @returns {Promise<Permission | null>} permission
|
||||||
*/
|
*/
|
||||||
async acceptInvitation(inviteCode: string, providerStr = 'affine') {
|
async acceptInvitation(inviteCode: string, providerStr = 'affine') {
|
||||||
const provider = this.providerMap.get(providerStr);
|
const provider = this.providerMap.get(providerStr);
|
||||||
if (provider) {
|
if (provider) {
|
||||||
return await provider.acceptInvitation(inviteCode);
|
return await provider.acceptInvitation(inviteCode);
|
||||||
}
|
}
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(cb: (message: Message) => void) {
|
onMessage(cb: (message: Message) => void) {
|
||||||
|
|||||||
@@ -1,3 +1,61 @@
|
|||||||
export enum MessageCode {
|
export enum MessageCode {
|
||||||
loginError,
|
loginError,
|
||||||
|
noPermission,
|
||||||
|
loadListFailed,
|
||||||
|
getDetailFailed,
|
||||||
|
createWorkspaceFailed,
|
||||||
|
getMembersFailed,
|
||||||
|
updateWorkspaceFailed,
|
||||||
|
deleteWorkspaceFailed,
|
||||||
|
inviteMemberFailed,
|
||||||
|
removeMemberFailed,
|
||||||
|
acceptInvitingFailed,
|
||||||
|
getBlobFailed,
|
||||||
|
leaveWorkspaceFailed,
|
||||||
|
downloadWorkspaceFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const messages = {
|
||||||
|
[MessageCode.loginError]: {
|
||||||
|
message: 'Login failed',
|
||||||
|
},
|
||||||
|
[MessageCode.noPermission]: {
|
||||||
|
message: 'No permission',
|
||||||
|
},
|
||||||
|
[MessageCode.loadListFailed]: {
|
||||||
|
message: 'Load list failed',
|
||||||
|
},
|
||||||
|
[MessageCode.getDetailFailed]: {
|
||||||
|
message: 'Get detail failed',
|
||||||
|
},
|
||||||
|
[MessageCode.createWorkspaceFailed]: {
|
||||||
|
message: 'Create workspace failed',
|
||||||
|
},
|
||||||
|
[MessageCode.getMembersFailed]: {
|
||||||
|
message: 'Get members failed',
|
||||||
|
},
|
||||||
|
[MessageCode.updateWorkspaceFailed]: {
|
||||||
|
message: 'Update workspace failed',
|
||||||
|
},
|
||||||
|
[MessageCode.deleteWorkspaceFailed]: {
|
||||||
|
message: 'Delete workspace failed',
|
||||||
|
},
|
||||||
|
[MessageCode.inviteMemberFailed]: {
|
||||||
|
message: 'Invite member failed',
|
||||||
|
},
|
||||||
|
[MessageCode.removeMemberFailed]: {
|
||||||
|
message: 'Remove member failed',
|
||||||
|
},
|
||||||
|
[MessageCode.acceptInvitingFailed]: {
|
||||||
|
message: 'Accept inviting failed',
|
||||||
|
},
|
||||||
|
[MessageCode.getBlobFailed]: {
|
||||||
|
message: 'Get blob failed',
|
||||||
|
},
|
||||||
|
[MessageCode.leaveWorkspaceFailed]: {
|
||||||
|
message: 'Leave workspace failed',
|
||||||
|
},
|
||||||
|
[MessageCode.downloadWorkspaceFailed]: {
|
||||||
|
message: 'Download workspace failed',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,24 +1,36 @@
|
|||||||
import { Observable } from 'lib0/observable';
|
import { Observable } from 'lib0/observable';
|
||||||
import { Message } from '../types';
|
import { Message } from '../types';
|
||||||
import { MessageCode } from './code.js';
|
import { MessageCode, messages } from './code.js';
|
||||||
|
|
||||||
export class MessageCenter extends Observable<string> {
|
export class MessageCenter extends Observable<string> {
|
||||||
|
private _messages: Record<number, Omit<Message, 'provider' | 'code'>> =
|
||||||
|
messages;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public send(message: MessageCode) {
|
static instance: MessageCenter;
|
||||||
this.emit('message', [message]);
|
|
||||||
|
static getInstance() {
|
||||||
|
if (!MessageCenter.instance) {
|
||||||
|
MessageCenter.instance = new MessageCenter();
|
||||||
|
}
|
||||||
|
return MessageCenter.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static messageCode = MessageCode;
|
||||||
|
|
||||||
|
public getMessageSender(provider: string) {
|
||||||
|
return this._send.bind(this, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _send(provider: string, messageCode: MessageCode) {
|
||||||
|
this.emit('message', [
|
||||||
|
{ ...this._messages[messageCode], provider, code: messageCode },
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onMessage(callback: (message: Message) => void) {
|
public onMessage(callback: (message: Message) => void) {
|
||||||
this.on('message', callback);
|
this.on('message', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private messages: Record<number, Message> = {
|
|
||||||
[MessageCode.loginError]: {
|
|
||||||
code: MessageCode.loginError,
|
|
||||||
message: 'Login failed',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,26 @@ import { BaseProvider } from '../base.js';
|
|||||||
import type {
|
import type {
|
||||||
ProviderConstructorParams,
|
ProviderConstructorParams,
|
||||||
CreateWorkspaceInfoParams,
|
CreateWorkspaceInfoParams,
|
||||||
WorkspaceMeta0,
|
|
||||||
} from '../base';
|
} from '../base';
|
||||||
import type { User } from '../../types';
|
import type { User } from '../../types';
|
||||||
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
import { BlockSchema } from '@blocksuite/blocks/models';
|
|
||||||
import { storage } from './storage.js';
|
import { storage } from './storage.js';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { WebsocketProvider } from './sync.js';
|
import { WebsocketProvider } from './sync.js';
|
||||||
// import { IndexedDBProvider } from '../local/indexeddb';
|
// import { IndexedDBProvider } from '../local/indexeddb';
|
||||||
import { getApis, Workspace } from './apis/index.js';
|
import { getApis, Workspace } from './apis/index.js';
|
||||||
import type { Apis, WorkspaceDetail, Callback } from './apis';
|
import type { Apis, WorkspaceDetail, Callback } from './apis';
|
||||||
import { setDefaultAvatar } from '../utils.js';
|
|
||||||
import { MessageCode } from '../../message';
|
|
||||||
import { token } from './apis/token.js';
|
import { token } from './apis/token.js';
|
||||||
import { WebsocketClient } from './channel';
|
import { WebsocketClient } from './channel';
|
||||||
import { SyncMode } from '../../workspace-unit';
|
import {
|
||||||
|
loadWorkspaceUnit,
|
||||||
|
createWorkspaceUnit,
|
||||||
|
syncToCloud,
|
||||||
|
} from './utils.js';
|
||||||
|
import { WorkspaceUnit } from '../../workspace-unit.js';
|
||||||
|
import { createBlocksuiteWorkspace, applyUpdate } from '../../utils/index.js';
|
||||||
|
import type { SyncMode } from '../../workspace-unit';
|
||||||
|
import { MessageCenter } from '../../message/index.js';
|
||||||
|
|
||||||
type ChannelMessage = {
|
type ChannelMessage = {
|
||||||
ws_list: Workspace[];
|
ws_list: Workspace[];
|
||||||
@@ -31,12 +35,11 @@ export interface AffineProviderConstructorParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Y: { applyUpdate, encodeStateAsUpdate },
|
Y: { encodeStateAsUpdate },
|
||||||
} = BlocksuiteWorkspace;
|
} = BlocksuiteWorkspace;
|
||||||
|
|
||||||
export class AffineProvider extends BaseProvider {
|
export class AffineProvider extends BaseProvider {
|
||||||
public id = 'affine';
|
public id = 'affine';
|
||||||
private _workspacesCache: Map<string, BlocksuiteWorkspace> = new Map();
|
|
||||||
private _onTokenRefresh?: Callback = undefined;
|
private _onTokenRefresh?: Callback = undefined;
|
||||||
private _wsMap: Map<string, WebsocketProvider> = new Map();
|
private _wsMap: Map<string, WebsocketProvider> = new Map();
|
||||||
private _apis: Apis;
|
private _apis: Apis;
|
||||||
@@ -95,14 +98,23 @@ export class AffineProvider extends BaseProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
this._channel.on('message', (msg: ChannelMessage) => {
|
this._channel.on('message', (msg: ChannelMessage) => {
|
||||||
this.handlerAffineListMessage(msg);
|
this._handlerAffineListMessage(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlerAffineListMessage({ ws_details, metadata }: ChannelMessage) {
|
private async _handlerAffineListMessage({
|
||||||
|
ws_details,
|
||||||
|
metadata,
|
||||||
|
}: ChannelMessage) {
|
||||||
this._logger('receive server message');
|
this._logger('receive server message');
|
||||||
Object.entries(ws_details).forEach(([id, detail]) => {
|
const addedWorkspaces: WorkspaceUnit[] = [];
|
||||||
|
const removeWorkspaceList = this._workspaces.list().map(w => w.id);
|
||||||
|
for (const [id, detail] of Object.entries(ws_details)) {
|
||||||
const { name, avatar } = metadata[id];
|
const { name, avatar } = metadata[id];
|
||||||
|
const index = removeWorkspaceList.indexOf(id);
|
||||||
|
if (index !== -1) {
|
||||||
|
removeWorkspaceList.splice(index, 1);
|
||||||
|
}
|
||||||
assert(name);
|
assert(name);
|
||||||
const workspace = {
|
const workspace = {
|
||||||
name: name,
|
name: name,
|
||||||
@@ -115,15 +127,24 @@ export class AffineProvider extends BaseProvider {
|
|||||||
},
|
},
|
||||||
published: detail.public,
|
published: detail.public,
|
||||||
memberCount: detail.member_count,
|
memberCount: detail.member_count,
|
||||||
provider: 'affine',
|
provider: this.id,
|
||||||
syncMode: 'core' as SyncMode,
|
syncMode: 'core' as SyncMode,
|
||||||
};
|
};
|
||||||
if (this._workspaces.get(id)) {
|
if (this._workspaces.get(id)) {
|
||||||
|
// update workspaces
|
||||||
this._workspaces.update(id, workspace);
|
this._workspaces.update(id, workspace);
|
||||||
} else {
|
} else {
|
||||||
this._workspaces.add({ id, ...workspace });
|
const workspaceUnit = await loadWorkspaceUnit(
|
||||||
|
{ id, ...workspace },
|
||||||
|
this._apis
|
||||||
|
);
|
||||||
|
addedWorkspaces.push(workspaceUnit);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
// add workspaces
|
||||||
|
this._workspaces.add(addedWorkspaces);
|
||||||
|
// remove workspaces
|
||||||
|
this._workspaces.remove(removeWorkspaceList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getWebsocketProvider(workspace: BlocksuiteWorkspace) {
|
private _getWebsocketProvider(workspace: BlocksuiteWorkspace) {
|
||||||
@@ -147,17 +168,10 @@ export class AffineProvider extends BaseProvider {
|
|||||||
blocksuiteWorkspace: BlocksuiteWorkspace,
|
blocksuiteWorkspace: BlocksuiteWorkspace,
|
||||||
published = false
|
published = false
|
||||||
) {
|
) {
|
||||||
const { doc, room: workspaceId } = blocksuiteWorkspace;
|
const { room: workspaceId } = blocksuiteWorkspace;
|
||||||
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
|
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
|
||||||
const updates = await this._apis.downloadWorkspace(workspaceId, published);
|
const updates = await this._apis.downloadWorkspace(workspaceId, published);
|
||||||
if (updates && updates.byteLength) {
|
await applyUpdate(blocksuiteWorkspace, new Uint8Array(updates));
|
||||||
await new Promise(resolve => {
|
|
||||||
doc.once('update', () => {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
});
|
|
||||||
BlocksuiteWorkspace.Y.applyUpdate(doc, new Uint8Array(updates));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) {
|
override async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) {
|
||||||
@@ -194,79 +208,25 @@ export class AffineProvider extends BaseProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const workspacesList = await this._apis.getWorkspaces();
|
const workspacesList = await this._apis.getWorkspaces();
|
||||||
const workspaces: WorkspaceMeta0[] = workspacesList.map(w => {
|
const workspaceUnits = await Promise.all(
|
||||||
return {
|
workspacesList.map(w => {
|
||||||
...w,
|
return loadWorkspaceUnit(
|
||||||
published: w.public,
|
{
|
||||||
memberCount: 0,
|
id: w.id,
|
||||||
name: '',
|
name: '',
|
||||||
provider: 'affine',
|
avatar: undefined,
|
||||||
syncMode: 'core',
|
owner: undefined,
|
||||||
};
|
published: w.public,
|
||||||
});
|
memberCount: 1,
|
||||||
const workspaceInstances = workspaces.map(({ id }) => {
|
provider: this.id,
|
||||||
const workspace =
|
syncMode: 'core',
|
||||||
this._workspacesCache.get(id) ||
|
},
|
||||||
new BlocksuiteWorkspace({
|
this._apis
|
||||||
room: id,
|
);
|
||||||
}).register(BlockSchema);
|
})
|
||||||
this._workspacesCache.set(id, workspace);
|
);
|
||||||
if (workspace) {
|
this._workspaces.add(workspaceUnits);
|
||||||
return new Promise<BlocksuiteWorkspace>(resolve => {
|
return workspaceUnits;
|
||||||
this._apis.downloadWorkspace(id).then(data => {
|
|
||||||
applyUpdate(workspace.doc, new Uint8Array(data));
|
|
||||||
resolve(workspace);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(await Promise.all(workspaceInstances)).forEach((workspace, i) => {
|
|
||||||
if (workspace) {
|
|
||||||
workspaces[i] = {
|
|
||||||
...workspaces[i],
|
|
||||||
name: workspace.meta.name,
|
|
||||||
avatar: workspace.meta.avatar,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const getDetailList = workspacesList.map(w => {
|
|
||||||
const { id } = w;
|
|
||||||
return new Promise<{ id: string; detail: WorkspaceDetail | null }>(
|
|
||||||
resolve => {
|
|
||||||
this._apis.getWorkspaceDetail({ id }).then(data => {
|
|
||||||
resolve({ id, detail: data || null });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const ownerList = await Promise.all(getDetailList);
|
|
||||||
(await Promise.all(ownerList)).forEach(detail => {
|
|
||||||
if (detail) {
|
|
||||||
const { id, detail: workspaceDetail } = detail;
|
|
||||||
if (workspaceDetail) {
|
|
||||||
const { owner, member_count } = workspaceDetail;
|
|
||||||
const currentWorkspace = workspaces.find(w => w.id === id);
|
|
||||||
if (currentWorkspace) {
|
|
||||||
currentWorkspace.owner = {
|
|
||||||
id: owner.id,
|
|
||||||
name: owner.name,
|
|
||||||
avatar: owner.avatar_url,
|
|
||||||
email: owner.email,
|
|
||||||
};
|
|
||||||
currentWorkspace.memberCount = member_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
workspaces.forEach(workspace => {
|
|
||||||
this._workspaces.add(workspace);
|
|
||||||
});
|
|
||||||
|
|
||||||
return workspaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override async auth() {
|
override async auth() {
|
||||||
@@ -283,7 +243,7 @@ export class AffineProvider extends BaseProvider {
|
|||||||
this._connectChannel();
|
this._connectChannel();
|
||||||
}
|
}
|
||||||
if (!user) {
|
if (!user) {
|
||||||
this._messageCenter.send(MessageCode.loginError);
|
this._sendMessage(MessageCenter.messageCode.loginError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,17 +269,17 @@ export class AffineProvider extends BaseProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override async clear(): Promise<void> {
|
public override async clear(): Promise<void> {
|
||||||
for (const w of this._workspacesCache.values()) {
|
for (const w of this._workspaces.list()) {
|
||||||
if (w.room) {
|
if (w.id) {
|
||||||
try {
|
try {
|
||||||
await this.deleteWorkspace(w.room);
|
await this.deleteWorkspace(w.id);
|
||||||
this._workspaces.remove(w.room);
|
this._workspaces.remove(w.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger('has a problem of delete workspace ', e);
|
this._logger('has a problem of delete workspace ', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._workspacesCache.clear();
|
this._workspaces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async closeWorkspace(id: string) {
|
public override async closeWorkspace(id: string) {
|
||||||
@@ -353,52 +313,29 @@ export class AffineProvider extends BaseProvider {
|
|||||||
// return workspace;
|
// return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async createWorkspaceInfo(
|
public override async createWorkspace(
|
||||||
meta: CreateWorkspaceInfoParams
|
meta: CreateWorkspaceInfoParams
|
||||||
): Promise<WorkspaceMeta0> {
|
): Promise<WorkspaceUnit | undefined> {
|
||||||
const { id } = await this._apis.createWorkspace(meta);
|
const { id } = await this._apis.createWorkspace(meta);
|
||||||
|
|
||||||
const workspaceInfo: WorkspaceMeta0 = {
|
const workspaceUnit = await createWorkspaceUnit({
|
||||||
|
id,
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
id: id,
|
avatar: undefined,
|
||||||
published: false,
|
|
||||||
avatar: '',
|
|
||||||
owner: await this.getUserInfo(),
|
owner: await this.getUserInfo(),
|
||||||
syncMode: 'core',
|
|
||||||
memberCount: 1,
|
|
||||||
provider: 'affine',
|
|
||||||
};
|
|
||||||
return workspaceInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async createWorkspace(
|
|
||||||
blocksuiteWorkspace: BlocksuiteWorkspace,
|
|
||||||
meta: WorkspaceMeta0
|
|
||||||
): Promise<BlocksuiteWorkspace | undefined> {
|
|
||||||
const workspaceId = blocksuiteWorkspace.room;
|
|
||||||
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
|
|
||||||
this._logger('Creating affine workspace');
|
|
||||||
|
|
||||||
this._applyCloudUpdates(blocksuiteWorkspace);
|
|
||||||
this.linkLocal(blocksuiteWorkspace);
|
|
||||||
|
|
||||||
const workspaceInfo: WorkspaceMeta0 = {
|
|
||||||
name: meta.name,
|
|
||||||
id: workspaceId,
|
|
||||||
published: false,
|
published: false,
|
||||||
avatar: '',
|
|
||||||
owner: undefined,
|
|
||||||
syncMode: 'core',
|
|
||||||
memberCount: 1,
|
memberCount: 1,
|
||||||
provider: 'affine',
|
provider: this.id,
|
||||||
};
|
syncMode: 'core',
|
||||||
|
});
|
||||||
|
|
||||||
if (!blocksuiteWorkspace.meta.avatar) {
|
await syncToCloud(
|
||||||
await setDefaultAvatar(blocksuiteWorkspace);
|
workspaceUnit.blocksuiteWorkspace!,
|
||||||
workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar;
|
this._apis.token.refresh
|
||||||
}
|
);
|
||||||
this._workspaces.add(workspaceInfo);
|
this._workspaces.add(workspaceUnit);
|
||||||
return blocksuiteWorkspace;
|
|
||||||
|
return workspaceUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async publish(id: string, isPublish: boolean): Promise<void> {
|
public override async publish(id: string, isPublish: boolean): Promise<void> {
|
||||||
@@ -420,22 +357,36 @@ export class AffineProvider extends BaseProvider {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async assign(
|
public override async extendWorkspace(
|
||||||
to: BlocksuiteWorkspace,
|
workspaceUnit: WorkspaceUnit
|
||||||
from: BlocksuiteWorkspace
|
): Promise<WorkspaceUnit> {
|
||||||
): Promise<BlocksuiteWorkspace> {
|
const { id } = await this._apis.createWorkspace({
|
||||||
assert(to.room, 'Blocksuite Workspace without room(workspaceId).');
|
name: workspaceUnit.name,
|
||||||
const ws = this._getWebsocketProvider(to);
|
|
||||||
applyUpdate(to.doc, encodeStateAsUpdate(from.doc));
|
|
||||||
// TODO: upload blobs and make sure doc is synced
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
|
||||||
ws.once('synced', () => {
|
|
||||||
setTimeout(() => resolve(), 1000);
|
|
||||||
});
|
|
||||||
ws.once('lost-connection', () => reject());
|
|
||||||
ws.once('connection-error', () => reject());
|
|
||||||
});
|
});
|
||||||
return to;
|
const newWorkspaceUnit = new WorkspaceUnit({
|
||||||
|
id,
|
||||||
|
name: workspaceUnit.name,
|
||||||
|
avatar: undefined,
|
||||||
|
owner: await this.getUserInfo(),
|
||||||
|
published: false,
|
||||||
|
memberCount: 1,
|
||||||
|
provider: this.id,
|
||||||
|
syncMode: 'core',
|
||||||
|
});
|
||||||
|
|
||||||
|
const blocksuiteWorkspace = createBlocksuiteWorkspace(id);
|
||||||
|
assert(workspaceUnit.blocksuiteWorkspace);
|
||||||
|
await applyUpdate(
|
||||||
|
blocksuiteWorkspace,
|
||||||
|
encodeStateAsUpdate(workspaceUnit.blocksuiteWorkspace.doc)
|
||||||
|
);
|
||||||
|
|
||||||
|
await syncToCloud(blocksuiteWorkspace, this._apis.token.refresh);
|
||||||
|
|
||||||
|
newWorkspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
this._workspaces.add(newWorkspaceUnit);
|
||||||
|
return newWorkspaceUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async logout(): Promise<void> {
|
public override async logout(): Promise<void> {
|
||||||
@@ -449,7 +400,7 @@ export class AffineProvider extends BaseProvider {
|
|||||||
return this._apis.getWorkspaceMembers({ id });
|
return this._apis.getWorkspaceMembers({ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async acceptInvitation(invitingCode: string): Promise<void> {
|
public override async acceptInvitation(invitingCode: string) {
|
||||||
await this._apis.acceptInviting({ invitingCode });
|
return await this._apis.acceptInviting({ invitingCode });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import kyOrigin from 'ky';
|
|
||||||
import ky from 'ky-universal';
|
import ky from 'ky-universal';
|
||||||
|
import { MessageCenter } from '../../../message/index.js';
|
||||||
import { token } from './token.js';
|
import { token } from './token.js';
|
||||||
|
|
||||||
|
const messageCenter = MessageCenter.getInstance();
|
||||||
|
|
||||||
|
const _sendMessage = messageCenter.getMessageSender('affine');
|
||||||
|
|
||||||
export const bareClient = ky.extend({
|
export const bareClient = ky.extend({
|
||||||
prefixUrl: 'http://localhost:8080',
|
prefixUrl: 'http://localhost:8080',
|
||||||
retry: 1,
|
retry: 1,
|
||||||
@@ -41,5 +45,15 @@ export const client = bareClient.extend({
|
|||||||
request.headers.set('Authorization', token.token);
|
request.headers.set('Authorization', token.token);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
beforeError: [
|
||||||
|
error => {
|
||||||
|
const { response } = error;
|
||||||
|
if (response.status === 401) {
|
||||||
|
_sendMessage(MessageCenter.messageCode.noPermission);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
|
import { MessageCenter } from '../../../message/index.js';
|
||||||
import { bareClient, client } from './request.js';
|
import { bareClient, client } from './request.js';
|
||||||
import type { User } from './user';
|
import type { User } from './user';
|
||||||
|
|
||||||
|
const messageCenter = MessageCenter.getInstance();
|
||||||
|
|
||||||
|
const sendMessage = messageCenter.getMessageSender('affine');
|
||||||
|
|
||||||
|
const { messageCode } = MessageCenter;
|
||||||
|
|
||||||
|
class RequestError extends Error {
|
||||||
|
constructor(message: string, cause: unknown | null = null) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'RequestError';
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
export interface GetWorkspaceDetailParams {
|
export interface GetWorkspaceDetailParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
@@ -26,13 +40,18 @@ export interface Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkspaces(): Promise<Workspace[]> {
|
export async function getWorkspaces(): Promise<Workspace[]> {
|
||||||
return client
|
try {
|
||||||
.get('api/workspace', {
|
return client
|
||||||
headers: {
|
.get('api/workspace', {
|
||||||
'Cache-Control': 'no-cache',
|
headers: {
|
||||||
},
|
'Cache-Control': 'no-cache',
|
||||||
})
|
},
|
||||||
.json();
|
})
|
||||||
|
.json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.loadListFailed);
|
||||||
|
throw new RequestError('load list failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkspaceDetail extends Workspace {
|
export interface WorkspaceDetail extends Workspace {
|
||||||
@@ -43,7 +62,13 @@ export interface WorkspaceDetail extends Workspace {
|
|||||||
export async function getWorkspaceDetail(
|
export async function getWorkspaceDetail(
|
||||||
params: GetWorkspaceDetailParams
|
params: GetWorkspaceDetailParams
|
||||||
): Promise<WorkspaceDetail | null> {
|
): Promise<WorkspaceDetail | null> {
|
||||||
return client.get(`api/workspace/${params.id}`).json();
|
try {
|
||||||
|
const response = client.get(`api/workspace/${params.id}`);
|
||||||
|
return response.json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.getDetailFailed);
|
||||||
|
throw new RequestError('get detail failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Permission {
|
export interface Permission {
|
||||||
@@ -74,7 +99,12 @@ export interface GetWorkspaceMembersParams {
|
|||||||
export async function getWorkspaceMembers(
|
export async function getWorkspaceMembers(
|
||||||
params: GetWorkspaceDetailParams
|
params: GetWorkspaceDetailParams
|
||||||
): Promise<Member[]> {
|
): Promise<Member[]> {
|
||||||
return client.get(`api/workspace/${params.id}/permission`).json();
|
try {
|
||||||
|
return client.get(`api/workspace/${params.id}/permission`).json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.getMembersFailed);
|
||||||
|
throw new RequestError('get members failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateWorkspaceParams {
|
export interface CreateWorkspaceParams {
|
||||||
@@ -84,10 +114,12 @@ export interface CreateWorkspaceParams {
|
|||||||
export async function createWorkspace(
|
export async function createWorkspace(
|
||||||
params: CreateWorkspaceParams
|
params: CreateWorkspaceParams
|
||||||
): Promise<{ id: string }> {
|
): Promise<{ id: string }> {
|
||||||
// FIXME: avatar should be optional
|
try {
|
||||||
return client
|
return client.post('api/workspace', { json: params }).json();
|
||||||
.post('api/workspace', { json: { ...params, avatar: '123' } })
|
} catch (error) {
|
||||||
.json();
|
sendMessage(messageCode.createWorkspaceFailed);
|
||||||
|
throw new RequestError('create workspace failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateWorkspaceParams {
|
export interface UpdateWorkspaceParams {
|
||||||
@@ -98,13 +130,18 @@ export interface UpdateWorkspaceParams {
|
|||||||
export async function updateWorkspace(
|
export async function updateWorkspace(
|
||||||
params: UpdateWorkspaceParams
|
params: UpdateWorkspaceParams
|
||||||
): Promise<{ public: boolean | null }> {
|
): Promise<{ public: boolean | null }> {
|
||||||
return client
|
try {
|
||||||
.post(`api/workspace/${params.id}`, {
|
return client
|
||||||
json: {
|
.post(`api/workspace/${params.id}`, {
|
||||||
public: params.public,
|
json: {
|
||||||
},
|
public: params.public,
|
||||||
})
|
},
|
||||||
.json();
|
})
|
||||||
|
.json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.updateWorkspaceFailed);
|
||||||
|
throw new RequestError('update workspace failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteWorkspaceParams {
|
export interface DeleteWorkspaceParams {
|
||||||
@@ -114,7 +151,10 @@ export interface DeleteWorkspaceParams {
|
|||||||
export async function deleteWorkspace(
|
export async function deleteWorkspace(
|
||||||
params: DeleteWorkspaceParams
|
params: DeleteWorkspaceParams
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await client.delete(`api/workspace/${params.id}`);
|
await client.delete(`api/workspace/${params.id}`).catch(error => {
|
||||||
|
sendMessage(messageCode.deleteWorkspaceFailed);
|
||||||
|
throw new RequestError('delete workspace failed', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InviteMemberParams {
|
export interface InviteMemberParams {
|
||||||
@@ -126,13 +166,18 @@ export interface InviteMemberParams {
|
|||||||
* Notice: Only support normal(contrast to private) workspace.
|
* Notice: Only support normal(contrast to private) workspace.
|
||||||
*/
|
*/
|
||||||
export async function inviteMember(params: InviteMemberParams): Promise<void> {
|
export async function inviteMember(params: InviteMemberParams): Promise<void> {
|
||||||
return client
|
try {
|
||||||
.post(`api/workspace/${params.id}/permission`, {
|
return client
|
||||||
json: {
|
.post(`api/workspace/${params.id}/permission`, {
|
||||||
email: params.email,
|
json: {
|
||||||
},
|
email: params.email,
|
||||||
})
|
},
|
||||||
.json();
|
})
|
||||||
|
.json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.inviteMemberFailed);
|
||||||
|
throw new RequestError('invite member failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoveMemberParams {
|
export interface RemoveMemberParams {
|
||||||
@@ -140,7 +185,10 @@ export interface RemoveMemberParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function removeMember(params: RemoveMemberParams): Promise<void> {
|
export async function removeMember(params: RemoveMemberParams): Promise<void> {
|
||||||
await client.delete(`api/permission/${params.permissionId}`);
|
await client.delete(`api/permission/${params.permissionId}`).catch(error => {
|
||||||
|
sendMessage(messageCode.removeMemberFailed);
|
||||||
|
throw new RequestError('remove member failed', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AcceptInvitingParams {
|
export interface AcceptInvitingParams {
|
||||||
@@ -149,8 +197,13 @@ export interface AcceptInvitingParams {
|
|||||||
|
|
||||||
export async function acceptInviting(
|
export async function acceptInviting(
|
||||||
params: AcceptInvitingParams
|
params: AcceptInvitingParams
|
||||||
): Promise<void> {
|
): Promise<Permission> {
|
||||||
await bareClient.post(`api/invitation/${params.invitingCode}`);
|
try {
|
||||||
|
return bareClient.post(`api/invitation/${params.invitingCode}`).json();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.acceptInvitingFailed);
|
||||||
|
throw new RequestError('accept inviting failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadBlob(params: { blob: Blob }): Promise<string> {
|
export async function uploadBlob(params: { blob: Blob }): Promise<string> {
|
||||||
@@ -160,7 +213,12 @@ export async function uploadBlob(params: { blob: Blob }): Promise<string> {
|
|||||||
export async function getBlob(params: {
|
export async function getBlob(params: {
|
||||||
blobId: string;
|
blobId: string;
|
||||||
}): Promise<ArrayBuffer> {
|
}): Promise<ArrayBuffer> {
|
||||||
return client.get(`api/blob/${params.blobId}`).arrayBuffer();
|
try {
|
||||||
|
return client.get(`api/blob/${params.blobId}`).arrayBuffer();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.getBlobFailed);
|
||||||
|
throw new RequestError('get blob failed', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LeaveWorkspaceParams {
|
export interface LeaveWorkspaceParams {
|
||||||
@@ -168,15 +226,26 @@ export interface LeaveWorkspaceParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function leaveWorkspace({ id }: LeaveWorkspaceParams) {
|
export async function leaveWorkspace({ id }: LeaveWorkspaceParams) {
|
||||||
await client.delete(`api/workspace/${id}/permission`).json();
|
await client
|
||||||
|
.delete(`api/workspace/${id}/permission`)
|
||||||
|
.json()
|
||||||
|
.catch(error => {
|
||||||
|
sendMessage(messageCode.leaveWorkspaceFailed);
|
||||||
|
throw new RequestError('leave workspace failed', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadWorkspace(
|
export async function downloadWorkspace(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
published = false
|
published = false
|
||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
if (published) {
|
try {
|
||||||
return bareClient.get(`api/public/doc/${workspaceId}`).arrayBuffer();
|
if (published) {
|
||||||
|
return bareClient.get(`api/public/doc/${workspaceId}`).arrayBuffer();
|
||||||
|
}
|
||||||
|
return client.get(`api/workspace/${workspaceId}/doc`).arrayBuffer();
|
||||||
|
} catch (error) {
|
||||||
|
sendMessage(messageCode.downloadWorkspaceFailed);
|
||||||
|
throw new RequestError('download workspace failed', error);
|
||||||
}
|
}
|
||||||
return client.get(`api/workspace/${workspaceId}/doc`).arrayBuffer();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
|
import { WorkspaceUnit } from '../../workspace-unit.js';
|
||||||
|
import type { WorkspaceUnitCtorParams } from '../../workspace-unit';
|
||||||
|
import { createBlocksuiteWorkspace } from '../../utils/index.js';
|
||||||
|
import type { Apis } from './apis';
|
||||||
|
import { WebsocketProvider } from './sync.js';
|
||||||
|
import { setDefaultAvatar } from '../utils.js';
|
||||||
|
import { applyUpdate } from '../../utils/index.js';
|
||||||
|
|
||||||
|
export const loadWorkspaceUnit = async (
|
||||||
|
params: WorkspaceUnitCtorParams,
|
||||||
|
apis: Apis
|
||||||
|
) => {
|
||||||
|
const workspaceUnit = new WorkspaceUnit(params);
|
||||||
|
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
|
||||||
|
|
||||||
|
const updates = await apis.downloadWorkspace(
|
||||||
|
workspaceUnit.id,
|
||||||
|
params.published
|
||||||
|
);
|
||||||
|
applyUpdate(blocksuiteWorkspace, new Uint8Array(updates));
|
||||||
|
|
||||||
|
const details = await apis.getWorkspaceDetail({ id: workspaceUnit.id });
|
||||||
|
const owner = details?.owner;
|
||||||
|
|
||||||
|
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
|
||||||
|
workspaceUnit.update({
|
||||||
|
name: blocksuiteWorkspace.meta.name,
|
||||||
|
avatar: blocksuiteWorkspace.meta.avatar,
|
||||||
|
memberCount: details?.member_count || 1,
|
||||||
|
owner: owner
|
||||||
|
? {
|
||||||
|
id: owner.id,
|
||||||
|
name: owner.name,
|
||||||
|
avatar: owner.avatar_url,
|
||||||
|
email: owner.email,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return workspaceUnit;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const syncToCloud = async (
|
||||||
|
blocksuiteWorkspace: BlocksuiteWorkspace,
|
||||||
|
refreshToken: string
|
||||||
|
) => {
|
||||||
|
const workspaceId = blocksuiteWorkspace.room;
|
||||||
|
assert(workspaceId, 'Blocksuite workspace without room(workspaceId).');
|
||||||
|
|
||||||
|
const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${
|
||||||
|
window.location.host
|
||||||
|
}/api/sync/`;
|
||||||
|
|
||||||
|
const ws = new WebsocketProvider(
|
||||||
|
wsUrl,
|
||||||
|
workspaceId,
|
||||||
|
blocksuiteWorkspace.doc,
|
||||||
|
{
|
||||||
|
params: { token: refreshToken },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
ws.once('synced', () => {
|
||||||
|
// FIXME: we don't when send local data to cloud successfully, so hack to wait 1s.
|
||||||
|
// Server will support this by add a new api.
|
||||||
|
setTimeout(resolve, 1000);
|
||||||
|
});
|
||||||
|
ws.once('lost-connection', () => reject());
|
||||||
|
ws.once('connection-error', () => reject());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
|
||||||
|
const workspaceUnit = new WorkspaceUnit(params);
|
||||||
|
|
||||||
|
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
|
||||||
|
|
||||||
|
blocksuiteWorkspace.meta.setName(workspaceUnit.name);
|
||||||
|
if (!workspaceUnit.avatar) {
|
||||||
|
await setDefaultAvatar(blocksuiteWorkspace);
|
||||||
|
workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar });
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
return workspaceUnit;
|
||||||
|
};
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
import { MessageCenter } from '../message';
|
import { MessageCenter } from '../message';
|
||||||
import { Logger, User } from '../types';
|
import { Logger, User } from '../types';
|
||||||
import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection';
|
import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection';
|
||||||
import type { WorkspaceUnitCtorParams } from '../workspace-unit';
|
import type { WorkspaceUnitCtorParams, WorkspaceUnit } from '../workspace-unit';
|
||||||
import { Member } from './affine/apis';
|
import { Member } from './affine/apis';
|
||||||
|
import { Permission } from './affine/apis/workspace.js';
|
||||||
|
|
||||||
const defaultLogger = () => {
|
const defaultLogger = () => {
|
||||||
return;
|
return;
|
||||||
@@ -22,10 +23,15 @@ export type UpdateWorkspaceMetaParams = Partial<
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
export class BaseProvider {
|
export class BaseProvider {
|
||||||
|
/** provider id */
|
||||||
public readonly id: string = 'base';
|
public readonly id: string = 'base';
|
||||||
|
/** workspace unit collection */
|
||||||
protected _workspaces!: WorkspaceUnitCollectionScope;
|
protected _workspaces!: WorkspaceUnitCollectionScope;
|
||||||
protected _logger!: Logger;
|
protected _logger!: Logger;
|
||||||
protected _messageCenter!: MessageCenter;
|
/** send message with message center */
|
||||||
|
protected _sendMessage!: ReturnType<
|
||||||
|
InstanceType<typeof MessageCenter>['getMessageSender']
|
||||||
|
>;
|
||||||
|
|
||||||
public constructor({
|
public constructor({
|
||||||
logger,
|
logger,
|
||||||
@@ -34,7 +40,7 @@ export class BaseProvider {
|
|||||||
}: ProviderConstructorParams) {
|
}: ProviderConstructorParams) {
|
||||||
this._logger = (logger || defaultLogger) as Logger;
|
this._logger = (logger || defaultLogger) as Logger;
|
||||||
this._workspaces = workspaces;
|
this._workspaces = workspaces;
|
||||||
this._messageCenter = messageCenter;
|
this._sendMessage = messageCenter.getMessageSender(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,12 +50,6 @@ export class BaseProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createWorkspaceInfo(
|
|
||||||
params: CreateWorkspaceInfoParams
|
|
||||||
): Promise<WorkspaceMeta0> {
|
|
||||||
throw new Error(`provider: ${this.id} createWorkspaceInfo Not implemented`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* auth provider
|
* auth provider
|
||||||
*/
|
*/
|
||||||
@@ -87,7 +87,7 @@ export class BaseProvider {
|
|||||||
/**
|
/**
|
||||||
* load workspaces
|
* load workspaces
|
||||||
**/
|
**/
|
||||||
public async loadWorkspaces(): Promise<WorkspaceMeta0[]> {
|
public async loadWorkspaces(): Promise<WorkspaceUnit[]> {
|
||||||
throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`);
|
throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,13 +183,18 @@ export class BaseProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* create workspace by workspace meta
|
* create workspace by workspace meta
|
||||||
* @param {WorkspaceMeta} meta
|
* @param {CreateWorkspaceInfoParams} meta
|
||||||
*/
|
*/
|
||||||
public async createWorkspace(
|
public async createWorkspace(
|
||||||
blocksuiteWorkspace: BlocksuiteWorkspace,
|
meta: CreateWorkspaceInfoParams
|
||||||
meta: WorkspaceMeta0
|
): Promise<WorkspaceUnit | undefined> {
|
||||||
): Promise<BlocksuiteWorkspace | undefined> {
|
throw new Error(`provider: ${this.id} createWorkspace not implemented`);
|
||||||
return blocksuiteWorkspace;
|
}
|
||||||
|
|
||||||
|
public async extendWorkspace(
|
||||||
|
workspaceUnit: WorkspaceUnit
|
||||||
|
): Promise<WorkspaceUnit | undefined> {
|
||||||
|
throw new Error(`provider: ${this.id} extendWorkspace not implemented`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,16 +219,6 @@ export class BaseProvider {
|
|||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* merge one workspaces to another
|
|
||||||
* @param workspace
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public async assign(to: BlocksuiteWorkspace, from: BlocksuiteWorkspace) {
|
|
||||||
from;
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get workspace members
|
* get workspace members
|
||||||
* @param {string} workspaceId
|
* @param {string} workspaceId
|
||||||
@@ -239,8 +234,10 @@ export class BaseProvider {
|
|||||||
* @param {string} inviteCode
|
* @param {string} inviteCode
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async acceptInvitation(inviteCode: string): Promise<void> {
|
public async acceptInvitation(
|
||||||
|
inviteCode: string
|
||||||
|
): Promise<Permission | null> {
|
||||||
inviteCode;
|
inviteCode;
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import * as idb from 'lib0/indexeddb.js';
|
import * as idb from 'lib0/indexeddb.js';
|
||||||
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
|
import { applyUpdate } from '../../../utils/index.js';
|
||||||
|
|
||||||
const { encodeStateAsUpdate } = BlocksuiteWorkspace.Y;
|
const { encodeStateAsUpdate, mergeUpdates } = BlocksuiteWorkspace.Y;
|
||||||
|
|
||||||
export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => {
|
export const writeUpdatesToLocal = async (
|
||||||
|
blocksuiteWorkspace: BlocksuiteWorkspace
|
||||||
|
) => {
|
||||||
const workspaceId = blocksuiteWorkspace.room;
|
const workspaceId = blocksuiteWorkspace.room;
|
||||||
assert(workspaceId);
|
assert(workspaceId);
|
||||||
await idb.deleteDB(workspaceId);
|
await idb.deleteDB(workspaceId);
|
||||||
@@ -17,4 +20,23 @@ export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => {
|
|||||||
if (updatesStore) {
|
if (updatesStore) {
|
||||||
await idb.addAutoKey(updatesStore, currState);
|
await idb.addAutoKey(updatesStore, currState);
|
||||||
}
|
}
|
||||||
|
db.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const applyLocalUpdates = async (
|
||||||
|
blocksuiteWorkspace: BlocksuiteWorkspace
|
||||||
|
) => {
|
||||||
|
const workspaceId = blocksuiteWorkspace.room;
|
||||||
|
assert(workspaceId, 'Blocksuite workspace without room(workspaceId).');
|
||||||
|
const db = await idb.openDB(workspaceId, db =>
|
||||||
|
idb.createStores(db, [['updates', { autoIncrement: true }], ['custom']])
|
||||||
|
);
|
||||||
|
|
||||||
|
const [updatesStore] = idb.transact(db, ['updates']); // , 'readonly')
|
||||||
|
if (updatesStore) {
|
||||||
|
const updates = await idb.getAll(updatesStore);
|
||||||
|
const mergedUpdates = mergeUpdates(updates);
|
||||||
|
await applyUpdate(blocksuiteWorkspace, mergedUpdates);
|
||||||
|
}
|
||||||
|
return blocksuiteWorkspace;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ test.describe.serial('local provider', () => {
|
|||||||
let workspaceId: string | undefined;
|
let workspaceId: string | undefined;
|
||||||
|
|
||||||
test('create workspace', async () => {
|
test('create workspace', async () => {
|
||||||
const workspaceInfo = await provider.createWorkspaceInfo({
|
const workspaceUnit = await provider.createWorkspace({
|
||||||
name: workspaceName,
|
name: workspaceName,
|
||||||
});
|
});
|
||||||
workspaceId = workspaceInfo.id;
|
workspaceId = workspaceUnit?.id;
|
||||||
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceId);
|
|
||||||
await provider.createWorkspace(blocksuiteWorkspace, workspaceInfo);
|
|
||||||
|
|
||||||
expect(workspaceMetaCollection.workspaces.length).toEqual(1);
|
expect(workspaceMetaCollection.workspaces.length).toEqual(1);
|
||||||
expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName);
|
expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName);
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import type {
|
|||||||
import { varStorage as storage } from 'lib0/storage';
|
import { varStorage as storage } from 'lib0/storage';
|
||||||
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
|
||||||
import { IndexedDBProvider } from './indexeddb/indexeddb.js';
|
import { IndexedDBProvider } from './indexeddb/indexeddb.js';
|
||||||
import { initStore } from './indexeddb/utils.js';
|
import { applyLocalUpdates } from './indexeddb/utils.js';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { setDefaultAvatar } from '../utils.js';
|
import { loadWorkspaceUnit, createWorkspaceUnit } from './utils.js';
|
||||||
|
import type { WorkspaceUnit } from '../../workspace-unit';
|
||||||
|
|
||||||
const WORKSPACE_KEY = 'workspaces';
|
const WORKSPACE_KEY = 'workspaces';
|
||||||
|
|
||||||
@@ -22,21 +23,12 @@ export class LocalProvider extends BaseProvider {
|
|||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _storeWorkspaces(workspaces: WorkspaceMeta0[]) {
|
private _storeWorkspaces(workspaceUnits: WorkspaceUnit[]) {
|
||||||
storage.setItem(
|
storage.setItem(
|
||||||
WORKSPACE_KEY,
|
WORKSPACE_KEY,
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
workspaces.map(w => {
|
workspaceUnits.map(w => {
|
||||||
return {
|
return w.toJSON();
|
||||||
id: w.id,
|
|
||||||
name: w.name,
|
|
||||||
avatar: w.avatar,
|
|
||||||
owner: w.owner,
|
|
||||||
published: w.published,
|
|
||||||
memberCount: w.memberCount,
|
|
||||||
provider: w.provider,
|
|
||||||
syncMode: w.syncMode,
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -57,24 +49,28 @@ export class LocalProvider extends BaseProvider {
|
|||||||
workspace: BlocksuiteWorkspace
|
workspace: BlocksuiteWorkspace
|
||||||
): Promise<BlocksuiteWorkspace> {
|
): Promise<BlocksuiteWorkspace> {
|
||||||
assert(workspace.room);
|
assert(workspace.room);
|
||||||
|
await applyLocalUpdates(workspace);
|
||||||
await this.linkLocal(workspace);
|
await this.linkLocal(workspace);
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
override loadWorkspaces(): Promise<WorkspaceMeta0[]> {
|
override async loadWorkspaces(): Promise<WorkspaceUnit[]> {
|
||||||
const workspaceStr = storage.getItem(WORKSPACE_KEY);
|
const workspaceStr = storage.getItem(WORKSPACE_KEY);
|
||||||
let workspaces: WorkspaceMeta0[] = [];
|
|
||||||
if (workspaceStr) {
|
if (workspaceStr) {
|
||||||
try {
|
try {
|
||||||
workspaces = JSON.parse(workspaceStr) as WorkspaceMeta0[];
|
const workspaceMetas = JSON.parse(workspaceStr) as WorkspaceMeta0[];
|
||||||
workspaces.forEach(workspace => {
|
const workspaceUnits = await Promise.all(
|
||||||
this._workspaces.add(workspace);
|
workspaceMetas.map(meta => {
|
||||||
});
|
return loadWorkspaceUnit(meta);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this._workspaces.add(workspaceUnits);
|
||||||
|
return workspaceUnits;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._logger(`Failed to parse workspaces from storage`);
|
this._logger(`Failed to parse workspaces from storage`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve(workspaces);
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async deleteWorkspace(id: string): Promise<void> {
|
public override async deleteWorkspace(id: string): Promise<void> {
|
||||||
@@ -96,10 +92,10 @@ export class LocalProvider extends BaseProvider {
|
|||||||
this._storeWorkspaces(this._workspaces.list());
|
this._storeWorkspaces(this._workspaces.list());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async createWorkspaceInfo(
|
public override async createWorkspace(
|
||||||
meta: CreateWorkspaceInfoParams
|
meta: CreateWorkspaceInfoParams
|
||||||
): Promise<WorkspaceMeta0> {
|
): Promise<WorkspaceUnit | undefined> {
|
||||||
const workspaceInfo: WorkspaceMeta0 = {
|
const workspaceUnit = await createWorkspaceUnit({
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
published: false,
|
published: false,
|
||||||
@@ -107,36 +103,11 @@ export class LocalProvider extends BaseProvider {
|
|||||||
owner: undefined,
|
owner: undefined,
|
||||||
syncMode: 'core',
|
syncMode: 'core',
|
||||||
memberCount: 1,
|
memberCount: 1,
|
||||||
provider: 'local',
|
provider: this.id,
|
||||||
};
|
});
|
||||||
return Promise.resolve(workspaceInfo);
|
this._workspaces.add(workspaceUnit);
|
||||||
}
|
|
||||||
|
|
||||||
public override async createWorkspace(
|
|
||||||
blocksuiteWorkspace: BlocksuiteWorkspace,
|
|
||||||
meta: WorkspaceMeta0
|
|
||||||
): Promise<BlocksuiteWorkspace | undefined> {
|
|
||||||
const workspaceId = blocksuiteWorkspace.room;
|
|
||||||
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
|
|
||||||
this._logger('Creating affine workspace');
|
|
||||||
|
|
||||||
const workspaceInfo: WorkspaceMeta0 = {
|
|
||||||
...meta,
|
|
||||||
};
|
|
||||||
|
|
||||||
blocksuiteWorkspace.meta.setName(meta.name);
|
|
||||||
|
|
||||||
if (!meta.avatar) {
|
|
||||||
await setDefaultAvatar(blocksuiteWorkspace);
|
|
||||||
workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
await initStore(blocksuiteWorkspace);
|
|
||||||
|
|
||||||
this._workspaces.add(workspaceInfo);
|
|
||||||
this._storeWorkspaces(this._workspaces.list());
|
this._storeWorkspaces(this._workspaces.list());
|
||||||
|
return workspaceUnit;
|
||||||
return blocksuiteWorkspace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async clear(): Promise<void> {
|
public override async clear(): Promise<void> {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { WorkspaceUnit } from '../../workspace-unit.js';
|
||||||
|
import type { WorkspaceUnitCtorParams } from '../../workspace-unit';
|
||||||
|
import { createBlocksuiteWorkspace } from '../../utils/index.js';
|
||||||
|
import { applyLocalUpdates, writeUpdatesToLocal } from './indexeddb/utils.js';
|
||||||
|
import { setDefaultAvatar } from '../utils.js';
|
||||||
|
|
||||||
|
export const loadWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
|
||||||
|
const workspaceUnit = new WorkspaceUnit(params);
|
||||||
|
|
||||||
|
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
|
||||||
|
|
||||||
|
await applyLocalUpdates(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
return workspaceUnit;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
|
||||||
|
const workspaceUnit = new WorkspaceUnit(params);
|
||||||
|
|
||||||
|
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
|
||||||
|
blocksuiteWorkspace.meta.setName(workspaceUnit.name);
|
||||||
|
if (!workspaceUnit.avatar) {
|
||||||
|
await setDefaultAvatar(blocksuiteWorkspace);
|
||||||
|
workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar });
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeUpdatesToLocal(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
|
||||||
|
|
||||||
|
return workspaceUnit;
|
||||||
|
};
|
||||||
@@ -25,4 +25,5 @@ export type Logger = ReturnType<typeof getLogger>;
|
|||||||
export type Message = {
|
export type Message = {
|
||||||
code: number;
|
code: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
provider: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,3 +45,25 @@ export async function getDefaultHeadImgBlob(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const applyUpdate = async (
|
||||||
|
blocksuiteWorkspace: BlocksuiteWorkspace,
|
||||||
|
updates: Uint8Array
|
||||||
|
) => {
|
||||||
|
if (updates && updates.byteLength) {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
// FIXME: if we merge two empty doc, there will no update event.
|
||||||
|
// So we set a timer to cancel update listener.
|
||||||
|
const doc = blocksuiteWorkspace.doc;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
doc.off('update', resolve);
|
||||||
|
resolve(undefined);
|
||||||
|
}, 1000);
|
||||||
|
doc.once('update', () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
|
BlocksuiteWorkspace.Y.applyUpdate(doc, new Uint8Array(updates));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
import { WorkspaceUnitCollection } from './workspace-unit-collection.js';
|
import { WorkspaceUnitCollection } from './workspace-unit-collection.js';
|
||||||
import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection';
|
import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection';
|
||||||
|
import { WorkspaceUnit } from './workspace-unit.js';
|
||||||
|
|
||||||
test.describe.serial('workspace meta collection observable', () => {
|
test.describe.serial('workspace meta collection observable', () => {
|
||||||
const workspaceUnitCollection = new WorkspaceUnitCollection();
|
const workspaceUnitCollection = new WorkspaceUnitCollection();
|
||||||
@@ -14,13 +15,15 @@ test.describe.serial('workspace meta collection observable', () => {
|
|||||||
expect(event.added?.[0]?.id).toEqual('123');
|
expect(event.added?.[0]?.id).toEqual('123');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
scope.add({
|
const workspaceUnit = new WorkspaceUnit({
|
||||||
id: '123',
|
id: '123',
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
avatar: undefined,
|
||||||
memberCount: 1,
|
memberCount: 1,
|
||||||
provider: '',
|
provider: '',
|
||||||
syncMode: 'core',
|
syncMode: 'core',
|
||||||
});
|
});
|
||||||
|
scope.add(workspaceUnit);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('list workspace', () => {
|
test('list workspace', () => {
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import { Observable } from 'lib0/observable';
|
import { Observable } from 'lib0/observable';
|
||||||
import { WorkspaceUnit } from './workspace-unit.js';
|
|
||||||
import type {
|
import type {
|
||||||
WorkspaceUnitCtorParams,
|
WorkspaceUnit,
|
||||||
UpdateWorkspaceUnitParams,
|
UpdateWorkspaceUnitParams,
|
||||||
} from './workspace-unit';
|
} from './workspace-unit';
|
||||||
|
|
||||||
export interface WorkspaceUnitCollectionScope {
|
export interface WorkspaceUnitCollectionScope {
|
||||||
get: (workspaceId: string) => WorkspaceUnit | undefined;
|
get: (workspaceId: string) => WorkspaceUnit | undefined;
|
||||||
list: () => WorkspaceUnit[];
|
list: () => WorkspaceUnit[];
|
||||||
add: (workspace: WorkspaceUnitCtorParams) => void;
|
add: (workspace: WorkspaceUnit | WorkspaceUnit[]) => void;
|
||||||
remove: (workspaceId: string) => boolean;
|
remove: (workspaceId: string | string[]) => boolean;
|
||||||
clear: () => void;
|
clear: () => void;
|
||||||
update: (
|
update: (
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
workspaceMeta: UpdateWorkspaceUnitParams
|
workspaceUnit: UpdateWorkspaceUnitParams
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkspaceUnitCollectionChangeEvent {
|
export interface WorkspaceUnitCollectionChangeEvent {
|
||||||
added?: WorkspaceUnit[];
|
added?: WorkspaceUnit[];
|
||||||
deleted?: WorkspaceUnit;
|
deleted?: WorkspaceUnit[];
|
||||||
updated?: WorkspaceUnit;
|
updated?: WorkspaceUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,45 +58,66 @@ export class WorkspaceUnitCollection {
|
|||||||
return this._workspaceUnitMap.get(workspaceId);
|
return this._workspaceUnitMap.get(workspaceId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const add = (workspace: WorkspaceUnitCtorParams) => {
|
const add = (workspaceUnit: WorkspaceUnit | WorkspaceUnit[]) => {
|
||||||
if (this._workspaceUnitMap.has(workspace.id)) {
|
const workspaceUnits = Array.isArray(workspaceUnit)
|
||||||
// FIXME: multiple add same workspace
|
? workspaceUnit
|
||||||
|
: [workspaceUnit];
|
||||||
|
let added = false;
|
||||||
|
|
||||||
|
workspaceUnits.forEach(workspaceUnit => {
|
||||||
|
if (this._workspaceUnitMap.has(workspaceUnit.id)) {
|
||||||
|
// FIXME: multiple add same workspace
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
added = true;
|
||||||
|
this._workspaceUnitMap.set(workspaceUnit.id, workspaceUnit);
|
||||||
|
scopedWorkspaceIds.add(workspaceUnit.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!added) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceUnit = new WorkspaceUnit(workspace);
|
|
||||||
this._workspaceUnitMap.set(workspace.id.toString(), workspaceUnit);
|
|
||||||
|
|
||||||
scopedWorkspaceIds.add(workspace.id);
|
|
||||||
|
|
||||||
this._events.emit('change', [
|
this._events.emit('change', [
|
||||||
{
|
{
|
||||||
added: [workspaceUnit],
|
added: workspaceUnits,
|
||||||
} as WorkspaceUnitCollectionChangeEvent,
|
} as WorkspaceUnitCollectionChangeEvent,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const remove = (workspaceId: string) => {
|
const remove = (workspaceId: string | string[]) => {
|
||||||
if (!scopedWorkspaceIds.has(workspaceId)) {
|
const workspaceIds = Array.isArray(workspaceId)
|
||||||
return true;
|
? workspaceId
|
||||||
}
|
: [workspaceId];
|
||||||
|
const workspaceUnits: WorkspaceUnit[] = [];
|
||||||
|
|
||||||
const workspaceUnit = this._workspaceUnitMap.get(workspaceId);
|
workspaceIds.forEach(workspaceId => {
|
||||||
if (workspaceUnit) {
|
if (!scopedWorkspaceIds.has(workspaceId)) {
|
||||||
const ret = this._workspaceUnitMap.delete(workspaceId);
|
return;
|
||||||
// If deletion failed, return.
|
|
||||||
if (!ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
const workspaceUnit = this._workspaceUnitMap.get(workspaceId);
|
||||||
|
if (workspaceUnit) {
|
||||||
|
const ret = this._workspaceUnitMap.delete(workspaceId);
|
||||||
|
// If deletion failed, return.
|
||||||
|
if (!ret) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scopedWorkspaceIds.delete(workspaceId);
|
workspaceUnits.push(workspaceUnit);
|
||||||
|
scopedWorkspaceIds.delete(workspaceId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this._events.emit('change', [
|
if (!workspaceUnits.length) {
|
||||||
{
|
return false;
|
||||||
deleted: workspaceUnit,
|
|
||||||
} as WorkspaceUnitCollectionChangeEvent,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._events.emit('change', [
|
||||||
|
{
|
||||||
|
deleted: workspaceUnits,
|
||||||
|
} as WorkspaceUnitCollectionChangeEvent,
|
||||||
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,10 +127,7 @@ export class WorkspaceUnitCollection {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const update = (
|
const update = (workspaceId: string, meta: UpdateWorkspaceUnitParams) => {
|
||||||
workspaceId: string,
|
|
||||||
workspaceMeta: UpdateWorkspaceUnitParams
|
|
||||||
) => {
|
|
||||||
if (!scopedWorkspaceIds.has(workspaceId)) {
|
if (!scopedWorkspaceIds.has(workspaceId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -120,7 +137,7 @@ export class WorkspaceUnitCollection {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceUnit.update(workspaceMeta);
|
workspaceUnit.update(meta);
|
||||||
|
|
||||||
this._events.emit('change', [
|
this._events.emit('change', [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,16 @@ export class WorkspaceUnit {
|
|||||||
this.update(params);
|
this.update(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isPublish() {
|
||||||
|
console.error('Suggest changing to published');
|
||||||
|
return this.published;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLocal() {
|
||||||
|
console.error('Suggest changing to syncMode');
|
||||||
|
return this.syncMode === 'all';
|
||||||
|
}
|
||||||
|
|
||||||
get blocksuiteWorkspace() {
|
get blocksuiteWorkspace() {
|
||||||
return this._blocksuiteWorkspace;
|
return this._blocksuiteWorkspace;
|
||||||
}
|
}
|
||||||
@@ -52,13 +62,16 @@ export class WorkspaceUnit {
|
|||||||
Object.assign(this, params);
|
Object.assign(this, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPublish() {
|
toJSON(): Omit<WorkspaceUnitCtorParams, 'blocksuiteWorkspace'> {
|
||||||
console.error('Suggest changing to published');
|
return {
|
||||||
return this.published;
|
id: this.id,
|
||||||
}
|
name: this.name,
|
||||||
|
avatar: this.avatar,
|
||||||
get isLocal() {
|
owner: this.owner,
|
||||||
console.error('Suggest changing to syncMode');
|
published: this.published,
|
||||||
return this.syncMode === 'all';
|
memberCount: this.memberCount,
|
||||||
|
provider: this.provider,
|
||||||
|
syncMode: this.syncMode,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-24
@@ -3011,7 +3011,6 @@ packages:
|
|||||||
|
|
||||||
/@next/env/13.1.0:
|
/@next/env/13.1.0:
|
||||||
resolution: {integrity: sha512-6iNixFzCndH+Bl4FetQzOMjxCJqg8fs0LAlZviig1K6mIjOWH2m2oPcHcOg1Ta5VCe7Bx5KG1Hs+NrWDUkBt9A==}
|
resolution: {integrity: sha512-6iNixFzCndH+Bl4FetQzOMjxCJqg8fs0LAlZviig1K6mIjOWH2m2oPcHcOg1Ta5VCe7Bx5KG1Hs+NrWDUkBt9A==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@next/eslint-plugin-next/12.3.1:
|
/@next/eslint-plugin-next/12.3.1:
|
||||||
resolution: {integrity: sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==}
|
resolution: {integrity: sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==}
|
||||||
@@ -3034,7 +3033,6 @@ packages:
|
|||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-android-arm64/12.3.1:
|
/@next/swc-android-arm64/12.3.1:
|
||||||
@@ -3052,7 +3050,6 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-arm64/12.3.1:
|
/@next/swc-darwin-arm64/12.3.1:
|
||||||
@@ -3070,7 +3067,6 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-x64/12.3.1:
|
/@next/swc-darwin-x64/12.3.1:
|
||||||
@@ -3088,7 +3084,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-freebsd-x64/12.3.1:
|
/@next/swc-freebsd-x64/12.3.1:
|
||||||
@@ -3106,7 +3101,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm-gnueabihf/12.3.1:
|
/@next/swc-linux-arm-gnueabihf/12.3.1:
|
||||||
@@ -3124,7 +3118,6 @@ packages:
|
|||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-gnu/12.3.1:
|
/@next/swc-linux-arm64-gnu/12.3.1:
|
||||||
@@ -3142,7 +3135,6 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-musl/12.3.1:
|
/@next/swc-linux-arm64-musl/12.3.1:
|
||||||
@@ -3160,7 +3152,6 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-gnu/12.3.1:
|
/@next/swc-linux-x64-gnu/12.3.1:
|
||||||
@@ -3178,7 +3169,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-musl/12.3.1:
|
/@next/swc-linux-x64-musl/12.3.1:
|
||||||
@@ -3196,7 +3186,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-arm64-msvc/12.3.1:
|
/@next/swc-win32-arm64-msvc/12.3.1:
|
||||||
@@ -3214,7 +3203,6 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-ia32-msvc/12.3.1:
|
/@next/swc-win32-ia32-msvc/12.3.1:
|
||||||
@@ -3232,7 +3220,6 @@ packages:
|
|||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-x64-msvc/12.3.1:
|
/@next/swc-win32-x64-msvc/12.3.1:
|
||||||
@@ -3250,7 +3237,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@nodelib/fs.scandir/2.1.5:
|
/@nodelib/fs.scandir/2.1.5:
|
||||||
@@ -3613,7 +3599,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
|
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@szmarczak/http-timer/5.0.1:
|
/@szmarczak/http-timer/5.0.1:
|
||||||
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
|
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
|
||||||
@@ -4580,7 +4565,6 @@ packages:
|
|||||||
|
|
||||||
/client-only/0.0.1:
|
/client-only/0.0.1:
|
||||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cliui/6.0.0:
|
/cliui/6.0.0:
|
||||||
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
||||||
@@ -7512,7 +7496,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
|
||||||
|
|
||||||
/natural-compare-lite/1.4.0:
|
/natural-compare-lite/1.4.0:
|
||||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||||
@@ -7635,7 +7618,6 @@ packages:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-domexception/1.0.0:
|
/node-domexception/1.0.0:
|
||||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||||
@@ -7986,7 +7968,6 @@ packages:
|
|||||||
nanoid: 3.3.4
|
nanoid: 3.3.4
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: false
|
|
||||||
|
|
||||||
/preferred-pm/3.0.3:
|
/preferred-pm/3.0.3:
|
||||||
resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==}
|
resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==}
|
||||||
@@ -8156,7 +8137,6 @@ packages:
|
|||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
scheduler: 0.23.0
|
scheduler: 0.23.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-i18next/11.18.6_i18next@21.10.0:
|
/react-i18next/11.18.6_i18next@21.10.0:
|
||||||
resolution: {integrity: sha512-yHb2F9BiT0lqoQDt8loZ5gWP331GwctHz9tYQ8A2EIEUu+CcEdjBLQWli1USG3RdWQt3W+jqQLg/d4rrQR96LA==}
|
resolution: {integrity: sha512-yHb2F9BiT0lqoQDt8loZ5gWP331GwctHz9tYQ8A2EIEUu+CcEdjBLQWli1USG3RdWQt3W+jqQLg/d4rrQR96LA==}
|
||||||
@@ -8253,7 +8233,6 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/read-pkg-up/7.0.1:
|
/read-pkg-up/7.0.1:
|
||||||
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
||||||
@@ -8531,7 +8510,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
|
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/schema-utils/2.7.1:
|
/schema-utils/2.7.1:
|
||||||
resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==}
|
resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==}
|
||||||
@@ -8690,7 +8668,6 @@ packages:
|
|||||||
/source-map-js/1.0.2:
|
/source-map-js/1.0.2:
|
||||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/source-map-support/0.5.13:
|
/source-map-support/0.5.13:
|
||||||
resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
|
resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
|
||||||
@@ -8939,7 +8916,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/stylis/4.0.13:
|
/stylis/4.0.13:
|
||||||
resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==}
|
resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==}
|
||||||
|
|||||||
Reference in New Issue
Block a user