diff --git a/packages/app/src/components/workspace-setting/general/delete/delete.tsx b/packages/app/src/components/workspace-setting/general/delete/delete.tsx new file mode 100644 index 0000000000..385cac1600 --- /dev/null +++ b/packages/app/src/components/workspace-setting/general/delete/delete.tsx @@ -0,0 +1,81 @@ +import Modal from '@/ui/modal'; +import Input from '@/ui/input'; +import { + StyledModalHeader, + StyledTextContent, + StyledModalWrapper, + StyledInputContent, + StyledButtonContent, + StyledWorkspaceName, +} from './style'; +import { useState } from 'react'; +import { ModalCloseButton } from '@/ui/modal'; +import { Button } from '@/ui/button'; +import { deleteWorkspace } from '@pathfinder/data-services'; +import { useRouter } from 'next/router'; + +interface WorkspaceDeleteProps { + open: boolean; + onClose: () => void; + workspaceName: string; + workspaceId: string; + nextWorkSpaceId: string; +} + +export const WorkspaceDelete = ({ + open, + onClose, + workspaceId, + workspaceName, + nextWorkSpaceId, +}: WorkspaceDeleteProps) => { + const [deleteStr, setDeleteStr] = useState(''); + const router = useRouter(); + + const handlerInputChange = (workspaceName: string) => { + setDeleteStr(workspaceName); + }; + + const handleDelete = async () => { + await deleteWorkspace({ id: workspaceId }); + router.push(`/workspace/${nextWorkSpaceId}`); + onClose(); + }; + + return ( + + + + Delete Workspace + + This action cannot be undone. This will permanently delete ( + {workspaceName}) along with + all its content. + + + + + + + + + + + ); +}; + +export default WorkspaceDelete; diff --git a/packages/app/src/components/workspace-setting/general/delete/index.ts b/packages/app/src/components/workspace-setting/general/delete/index.ts new file mode 100644 index 0000000000..2a41f028d2 --- /dev/null +++ b/packages/app/src/components/workspace-setting/general/delete/index.ts @@ -0,0 +1 @@ +export * from './delete'; diff --git a/packages/app/src/components/workspace-setting/general/delete/style.ts b/packages/app/src/components/workspace-setting/general/delete/style.ts new file mode 100644 index 0000000000..36cb07ee08 --- /dev/null +++ b/packages/app/src/components/workspace-setting/general/delete/style.ts @@ -0,0 +1,76 @@ +import { styled } from '@/styles'; +import { Button } from '@/ui/button'; + +export const StyledModalWrapper = styled('div')(({ theme }) => { + return { + position: 'relative', + padding: '0px', + width: '460px', + background: theme.colors.popoverBackground, + borderRadius: '12px', + }; +}); + +export const StyledModalHeader = styled('div')(({ theme }) => { + return { + margin: '44px 0px 12px 0px', + width: '460px', + fontWeight: '600', + fontSize: '20px;', + textAlign: 'center', + color: theme.colors.popoverColor, + }; +}); + +// export const StyledModalContent = styled('div')(({ theme }) => {}); + +export const StyledTextContent = styled('div')(({ theme }) => { + return { + margin: 'auto', + width: '425px', + fontFamily: 'Avenir Next', + fontStyle: 'normal', + fontWeight: '400', + fontSize: '18px', + lineHeight: '26px', + textAlign: 'center', + }; +}); + +export const StyledInputContent = styled('div')(({ theme }) => { + return { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + margin: '40px 0 24px 0', + }; +}); + +export const StyledButtonContent = styled('div')(({ theme }) => { + return { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + margin: '0px 0 32px 0', + }; +}); + +export const StyledWorkspaceName = styled('span')(({ theme }) => { + return { + color: '#E8178A', + }; +}); + +// export const StyledCancelButton = styled(Button)(({ theme }) => { +// return { +// width: '100px', +// justifyContent: 'center', +// }; +// }); + +// export const StyledDeleteButton = styled(Button)(({ theme }) => { +// return { +// width: '100px', +// justifyContent: 'center', +// }; +// }); diff --git a/packages/app/src/components/workspace-setting/general/general.tsx b/packages/app/src/components/workspace-setting/general/general.tsx index 917928c88f..6928420d5f 100644 --- a/packages/app/src/components/workspace-setting/general/general.tsx +++ b/packages/app/src/components/workspace-setting/general/general.tsx @@ -1,10 +1,10 @@ -import Modal, { ModalCloseButton } from '@/ui/modal'; import { StyledDeleteButtonContainer, StyledSettingAvatar, StyledSettingAvatarContent, StyledSettingInputContainer, } from './style'; +import { StyledSettingH2 } from '../style'; import { useState } from 'react'; import { Button } from '@/ui/button'; @@ -12,15 +12,20 @@ import Input from '@/ui/input'; import { Workspace, WorkspaceType } from '@pathfinder/data-services'; import { useAppState } from '@/providers/app-state-provider'; import { WorkspaceDetails } from '@/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent'; -import { StyledSettingH2 } from '../style'; +import { WorkspaceDelete } from './delete'; +import { Workspace as StoreWorkspaces } from '@blocksuite/store'; + export const GeneralPage = ({ workspace, owner, + workspaces, }: { workspace: Workspace; owner: WorkspaceDetails[string]['owner']; + workspaces: Record; }) => { - const { workspaces, user, currentWorkspace } = useAppState(); + const { user, currentWorkspace, workspacesMeta } = useAppState(); + const [showDelete, setShowDelete] = useState(false); const [workspaceName, setWorkspaceName] = useState( workspaces[workspace.id]?.meta.name || (workspace.type === WorkspaceType.Private && user @@ -33,8 +38,28 @@ export const GeneralPage = ({ currentWorkspace?.meta.setName(newName); workspaces[workspace.id]?.meta.setName(newName); }; + const currentWorkspaceIndex = workspacesMeta.findIndex( + meta => meta.id === workspace.id + ); + const nextWorkSpaceId = + currentWorkspaceIndex > workspacesMeta.length - 1 + ? workspacesMeta[currentWorkspaceIndex - 1]?.id + : workspacesMeta[currentWorkspaceIndex + 1]?.id; + const handleCloseDelete = () => { + setShowDelete(false); + }; + const handleClickDelete = () => { + setShowDelete(true); + }; return workspace ? (
+ Workspace Avatar W + {/* TODO: add upload logic */} {/* {isOwner ? ( upload ) : null} */} @@ -70,7 +96,7 @@ export const GeneralPage = ({ > - diff --git a/packages/app/src/components/workspace-setting/workspace-setting.tsx b/packages/app/src/components/workspace-setting/workspace-setting.tsx index db515a65d0..d04885b131 100644 --- a/packages/app/src/components/workspace-setting/workspace-setting.tsx +++ b/packages/app/src/components/workspace-setting/workspace-setting.tsx @@ -40,17 +40,16 @@ import { InviteMembers } from '../invite-members/index'; import { getWorkspaceMembers, Workspace, - WorkspaceType, Member, removeMember, } from '@pathfinder/data-services'; import { Avatar } from '@mui/material'; import { Menu, MenuItem } from '@/ui/menu'; import { toast } from '@/ui/toast'; -import { useConfirm } from '@/providers/confirm-provider'; import { useAppState } from '@/providers/app-state-provider'; import { WorkspaceDetails } from '../workspace-slider-bar/WorkspaceSelector/SelectorPopperContent'; import { GeneralPage } from './general'; +import { Workspace as StoreWorkspaces } from '@blocksuite/store'; enum ActiveTab { 'general' = 'general', @@ -68,6 +67,7 @@ type WorkspaceSettingProps = { onClose?: () => void; workspace: Workspace; owner: WorkspaceDetails[string]['owner']; + workspaces: Record; }; const WorkspaceSettingTab = ({ activeTab, onTabChange }: SettingTabProps) => { @@ -115,8 +115,9 @@ export const WorkspaceSetting = ({ onClose, workspace, owner, + workspaces, }: WorkspaceSettingProps) => { - const { workspaces, user } = useAppState(); + const { user } = useAppState(); const [activeTab, setActiveTab] = useState(ActiveTab.general); const handleTabChange = (tab: ActiveTab) => { setActiveTab(tab); @@ -148,7 +149,11 @@ export const WorkspaceSetting = ({ ) : null} {activeTab === ActiveTab.general && ( - + )} {activeTab === ActiveTab.members && workspace && ( diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx index 0f676141b0..28ef05047a 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx @@ -12,7 +12,12 @@ import { } from './WorkspaceItem'; import { WorkspaceSetting } from '@/components/workspace-setting'; import { useCallback, useEffect, useState } from 'react'; -import { getWorkspaceDetail, WorkspaceType } from '@pathfinder/data-services'; +import { + downloadWorkspace, + getWorkspaceDetail, + WorkspaceType, +} from '@pathfinder/data-services'; +import { Workspace } from '@blocksuite/store'; export type WorkspaceDetails = Record< string, @@ -26,13 +31,16 @@ type SelectorPopperContentProps = { export const SelectorPopperContent = ({ isShow, }: SelectorPopperContentProps) => { - const { user, workspacesMeta, workspaces } = useAppState(); + const { user, workspacesMeta } = useAppState(); const [settingWorkspaceId, setSettingWorkspaceId] = useState( null ); const [workSpaceDetails, setWorkSpaceDetails] = useState( {} ); + const [workspaces, setWorkspaces] = useState< + Record + >({}); const handleClickSettingWorkspace = (workspaceId: string) => { setSettingWorkspaceId(workspaceId); }; @@ -73,10 +81,38 @@ export const SelectorPopperContent = ({ setWorkSpaceDetails(workSpaceDetails); }, [user, workspacesMeta]); + // TODO: add to context + const refreshWorkspaces = useCallback(async () => { + const workspacesList = await Promise.all( + workspacesMeta.map(async ({ id }) => { + const workspace = new Workspace({ + room: id, + providers: [], + }); + const updates = await downloadWorkspace({ workspaceId: id }); + updates && + Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(updates)); + // if after update, the space:meta is empty, then we need to get map with doc + workspace.doc.getMap('space:meta'); + return { id, workspace }; + }) + ); + const workspaces: Record = {}; + + workspacesList.forEach(({ id, workspace }) => { + workspaces[id] = workspace; + }); + + console.log('workspaces', workspaces); + + setWorkspaces(workspaces); + }, []); + useEffect(() => { if (isShow) { setSettingWorkspaceId(null); refreshDetails(); + refreshWorkspaces(); } }, [isShow, refreshDetails]); @@ -128,6 +164,7 @@ export const SelectorPopperContent = ({ name: user.name, } } + workspaces={workspaces} /> ) : null} diff --git a/packages/app/src/providers/app-state-provider/context.ts b/packages/app/src/providers/app-state-provider/context.ts index 6b56cd2819..406a2be566 100644 --- a/packages/app/src/providers/app-state-provider/context.ts +++ b/packages/app/src/providers/app-state-provider/context.ts @@ -15,7 +15,6 @@ export type CreateEditorHandler = (page: StorePage) => EditorContainer | null; export interface AppStateValue { user: AccessTokenMessage | null; workspacesMeta: Workspace[]; - workspaces: Record; currentWorkspaceId: string; currentWorkspace: StoreWorkspace | null; @@ -56,7 +55,7 @@ export const AppState = createContext({ loadPage: () => Promise.resolve(null), createPage: () => Promise.resolve(null), synced: false, - workspaces: {}, + // workspaces: {}, }); export const useAppState = () => { diff --git a/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx b/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx index 70442dd282..0e17b6bec0 100644 --- a/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx +++ b/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx @@ -13,7 +13,7 @@ import type { LoadWorkspaceHandler, CreateEditorHandler, } from './context'; -import { downloadWorkspace, token } from '@pathfinder/data-services'; +import { token } from '@pathfinder/data-services'; import { WebsocketProvider } from './y-websocket'; const getEditorParams = (workspaceId: string) => { @@ -90,19 +90,9 @@ const DynamicBlocksuite = ({ ); if (indexDBProvider) { (indexDBProvider as IndexedDBDocProvider).on('synced', async () => { - // const updates = await downloadWorkspace({ workspaceId }); - // updates && - // Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(updates)); - // if after update, the space:meta is empty, then we need to get map with doc - workspace.doc.getMap('space:meta'); resolve(workspace); }); } else { - const updates = await downloadWorkspace({ workspaceId }); - updates && - Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(updates)); - // if after update, the space:meta is empty, then we need to get map with doc - workspace.doc.getMap('space:meta'); resolve(workspace); } }); diff --git a/packages/app/src/providers/app-state-provider/provider.tsx b/packages/app/src/providers/app-state-provider/provider.tsx index 197eed5390..f43c5e4006 100644 --- a/packages/app/src/providers/app-state-provider/provider.tsx +++ b/packages/app/src/providers/app-state-provider/provider.tsx @@ -26,7 +26,6 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { currentWorkspace: null, currentPage: null, editor: null, - workspaces: {}, // Synced is used to ensure that the provider has synced with the server, // So after Synced set to true, the other state is sure to be set. synced: false, diff --git a/packages/app/src/ui/input/input.tsx b/packages/app/src/ui/input/input.tsx index bb64b18414..b92646bca3 100644 --- a/packages/app/src/ui/input/input.tsx +++ b/packages/app/src/ui/input/input.tsx @@ -25,6 +25,11 @@ export const Input = (props: inputProps) => { } = props; const [value, setValue] = useState(valueProp || ''); const handleChange: InputHTMLAttributes['onChange'] = e => { + if ( + (maxLength && e.target.value.length > maxLength) || + (minLength && e.target.value.length < minLength) + ) + return; setValue(e.target.value); onChange && onChange(e.target.value); };