diff --git a/packages/app/src/components/edgeless-toolbar/index.tsx b/packages/app/src/components/edgeless-toolbar/index.tsx index 00f821e6ba..54076c974d 100644 --- a/packages/app/src/components/edgeless-toolbar/index.tsx +++ b/packages/app/src/components/edgeless-toolbar/index.tsx @@ -16,7 +16,9 @@ import { } from './icons'; import { Tooltip } from '@/ui/tooltip'; import Slide from '@mui/material/Slide'; -import { useEditor } from '@/providers/editor-provider'; +import useCurrentPageMeta from '@/hooks/use-current-page-meta'; +import { useAppState } from '@/providers/app-state-provider'; +import useHistoryUpdated from '@/hooks/use-history-update'; const toolbarList1 = [ { @@ -95,7 +97,8 @@ const toolbarList2 = [ const UndoRedo = () => { const [canUndo, setCanUndo] = useState(false); const [canRedo, setCanRedo] = useState(false); - const { onHistoryUpdated, page } = useEditor(); + const { currentPage } = useAppState(); + const onHistoryUpdated = useHistoryUpdated(); useEffect(() => { onHistoryUpdated(page => { @@ -110,7 +113,7 @@ const UndoRedo = () => { { - page?.undo(); + currentPage?.undo(); }} > @@ -120,7 +123,7 @@ const UndoRedo = () => { { - page?.redo(); + currentPage?.redo(); }} > @@ -131,7 +134,7 @@ const UndoRedo = () => { }; export const EdgelessToolbar = () => { - const { mode } = useEditor(); + const { mode } = useCurrentPageMeta() || {}; return ( { const { @@ -67,9 +67,8 @@ export const EditorModeSwitch = ({ style = {}, }: AnimateRadioProps) => { const { mode: themeMode } = useTheme(); - const { getPageMeta, currentPage } = useAppState(); - const { mode, setMode } = useEditor(); - const pageMeta = currentPage?.pageId ? getPageMeta(currentPage.pageId) : null; + const { changePageMode } = usePageHelper(); + const { trash, mode = 'page', id = '' } = useCurrentPageMeta() || {}; const modifyRadioItemStatus = (): RadioItemStatus => { return { @@ -103,7 +102,7 @@ export const EditorModeSwitch = ({ data-testid="editor-mode-switcher" shrink={!isHover} style={style} - disabled={!!pageMeta?.trash} + disabled={!!trash} > { - setMode('page'); + changePageMode(id, 'page'); }} onMouseEnter={() => { setRadioItemStatus({ @@ -132,7 +131,7 @@ export const EditorModeSwitch = ({ active={mode === 'edgeless'} status={radioItemStatus.right} onClick={() => { - setMode('edgeless'); + changePageMode(id, 'edgeless'); }} onMouseEnter={() => { setRadioItemStatus({ diff --git a/packages/app/src/components/header/editor-header.tsx b/packages/app/src/components/header/editor-header.tsx index 744ca447cd..14ee04c21f 100644 --- a/packages/app/src/components/header/editor-header.tsx +++ b/packages/app/src/components/header/editor-header.tsx @@ -6,18 +6,19 @@ import { StyledTitleWrapper, } from './styles'; import { Content } from '@/ui/layout'; -import { useEditor } from '@/providers/editor-provider'; import { useAppState } from '@/providers/app-state-provider/context'; import EditorModeSwitch from '@/components/editor-mode-switch'; import QuickSearchButton from './quick-search-button'; import Header from './header'; +import usePropsUpdated from '@/hooks/use-props-updated'; +import useCurrentPageMeta from '@/hooks/use-current-page-meta'; export const EditorHeader = () => { const [title, setTitle] = useState(''); const [isHover, setIsHover] = useState(false); - const { getPageMeta, currentPage, editor } = useAppState(); - - const { onPropsUpdated } = useEditor(); + const { editor } = useAppState(); + const pageMeta = useCurrentPageMeta(); + const onPropsUpdated = usePropsUpdated(); useEffect(() => { onPropsUpdated(editor => { @@ -32,8 +33,6 @@ export const EditorHeader = () => { }, 300); }, [editor]); - const pageMeta = currentPage?.pageId ? getPageMeta(currentPage.pageId) : null; - return (
{title && ( diff --git a/packages/app/src/components/header/header.tsx b/packages/app/src/components/header/header.tsx index 7aeee29cde..bdf6503c3e 100644 --- a/packages/app/src/components/header/header.tsx +++ b/packages/app/src/components/header/header.tsx @@ -17,7 +17,6 @@ import { FavouritedIcon, TrashIcon, } from '@blocksuite/icons'; -import { useEditor } from '@/providers/editor-provider'; import { useAppState } from '@/providers/app-state-provider/context'; import ThemeModeSwitch from '@/components/theme-mode-switch'; import { IconButton, Button } from '@/ui/button'; @@ -31,18 +30,16 @@ import { toast } from '@/components/toast'; import useCurrentPageMeta from '@/hooks/use-current-page-meta'; import { usePageHelper } from '@/hooks/use-page-helper'; const PopoverContent = () => { - const { getPageMeta, editor, currentPage } = useAppState(); + const { editor } = useAppState(); const { toggleFavoritePage, toggleDeletePage } = usePageHelper(); - const { mode, setMode } = useEditor(); + const { changePageMode } = usePageHelper(); const { confirm } = useConfirm(); - - const { id, favorite, title } = (currentPage?.id - ? getPageMeta(currentPage.id) - : null) ?? { - id: '', - favorite: false, - title: '', - }; + const { + mode = 'page', + id = '', + favorite = false, + title = '', + } = useCurrentPageMeta() || {}; return ( <> @@ -58,7 +55,7 @@ const PopoverContent = () => { : } onClick={() => { - setMode(mode === 'page' ? 'edgeless' : 'page'); + changePageMode(id); }} > Convert to {mode === 'page' ? 'Edgeless' : 'Page'} @@ -128,11 +125,12 @@ const BrowserWarning = ({ }; const HeaderRight = () => { - const { pageList, permanentlyDeletePage } = useEditor(); - const { currentWorkspaceId, toggleDeletePage } = useAppState(); + const { permanentlyDeletePage } = usePageHelper(); + const { currentWorkspaceId } = useAppState(); + const { toggleDeletePage } = usePageHelper(); const { confirm } = useConfirm(); const router = useRouter(); - const currentPageMeta = pageList.find(p => p.id === router.query.pageId); + const currentPageMeta = useCurrentPageMeta(); const isTrash = !!currentPageMeta?.trash; if (isTrash) { @@ -188,8 +186,6 @@ const HeaderRight = () => { export const Header = ({ children }: PropsWithChildren<{}>) => { const [showWarning, setShowWarning] = useState(shouldShowWarning()); - const currentPageMeta = useCurrentPageMeta(); - console.log('currentPageMeta', currentPageMeta); return ( ; export const HelpIsland = ({ showList = ['contact', 'shortcuts'], @@ -22,7 +21,7 @@ export const HelpIsland = ({ }) => { const [showContent, setShowContent] = useState(false); const { mode } = useTheme(); - const { mode: editorMode } = useEditor(); + const { mode: editorMode } = useCurrentPageMeta() || {}; const { triggerShortcutsModal, triggerContactModal } = useModal(); const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless'; diff --git a/packages/app/src/components/page-list/date-cell.tsx b/packages/app/src/components/page-list/date-cell.tsx index e7d90f64e8..4ffc4e95cd 100644 --- a/packages/app/src/components/page-list/date-cell.tsx +++ b/packages/app/src/components/page-list/date-cell.tsx @@ -1,6 +1,6 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'; import dayjs from 'dayjs'; -import { PageMeta } from '@/providers/editor-provider'; +import { PageMeta } from '@/providers/app-state-provider'; import { TableCell } from '@/ui/table'; import React from 'react'; diff --git a/packages/app/src/components/page-list/index.tsx b/packages/app/src/components/page-list/index.tsx index 5573b5be03..4baed96ebd 100644 --- a/packages/app/src/components/page-list/index.tsx +++ b/packages/app/src/components/page-list/index.tsx @@ -1,4 +1,4 @@ -import { PageMeta, useEditor } from '@/providers/editor-provider'; +import { PageMeta } from '@/providers/app-state-provider'; import { FavouritedIcon, FavouritesIcon, diff --git a/packages/app/src/components/page-list/operation-cell.tsx b/packages/app/src/components/page-list/operation-cell.tsx index 9111bc47c1..bfa562e5ae 100644 --- a/packages/app/src/components/page-list/operation-cell.tsx +++ b/packages/app/src/components/page-list/operation-cell.tsx @@ -1,7 +1,5 @@ -import { PageMeta, useEditor } from '@/providers/editor-provider'; import { useConfirm } from '@/providers/confirm-provider'; -import { useAppState } from '@/providers/app-state-provider/context'; -import { useGoToPage } from '@/providers/app-state-provider/hooks'; +import { useAppState, PageMeta } from '@/providers/app-state-provider'; import { Menu, MenuItem } from '@/ui/menu'; import { Wrapper } from '@/ui/layout'; import { IconButton } from '@/ui/button'; @@ -19,7 +17,7 @@ import { usePageHelper } from '@/hooks/use-page-helper'; export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { const { id, favorite } = pageMeta; - const goToPage = useGoToPage(); + const { openPage } = usePageHelper(); const { toggleFavoritePage, toggleDeletePage } = usePageHelper(); const { confirm } = useConfirm(); @@ -36,7 +34,7 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { { - goToPage(id); + openPage(id); }} icon={} > @@ -73,9 +71,9 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { export const TrashOperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { const { id } = pageMeta; - const goToPage = useGoToPage(); - const { getPageMeta, toggleDeletePage } = useAppState(); - const { permanentlyDeletePage } = useEditor(); + const { openPage } = usePageHelper(); + const { getPageMeta } = useAppState(); + const { toggleDeletePage, permanentlyDeletePage } = usePageHelper(); const { confirm } = useConfirm(); return ( @@ -86,7 +84,7 @@ export const TrashOperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { onClick={() => { toggleDeletePage(id); toast(`${getPageMeta(id)?.title || 'Untitled'} restored`); - goToPage(id); + openPage(id); }} > diff --git a/packages/app/src/components/quick-search/footer.tsx b/packages/app/src/components/quick-search/footer.tsx index 4f2b356577..d3f66b143e 100644 --- a/packages/app/src/components/quick-search/footer.tsx +++ b/packages/app/src/components/quick-search/footer.tsx @@ -4,12 +4,12 @@ import { StyledModalFooterContent } from './style'; import { useModal } from '@/providers/global-modal-provider'; import { useAppState } from '@/providers/app-state-provider/context'; import { Command } from 'cmdk'; -import { useGoToPage } from '@/providers/app-state-provider/hooks'; +import { usePageHelper } from '@/hooks/use-page-helper'; export const Footer = (props: { query: string }) => { const { createPage } = useAppState(); const { triggerQuickSearchModal } = useModal(); - const goToPage = useGoToPage(); + const { openPage } = usePageHelper(); const query = props.query; return ( @@ -18,7 +18,7 @@ export const Footer = (props: { query: string }) => { onSelect={async () => { const pageId = await createPage?.current?.(); if (pageId) { - goToPage(pageId); + openPage(pageId); } triggerQuickSearchModal(); diff --git a/packages/app/src/components/quick-search/noResult.svg b/packages/app/src/components/quick-search/noResult.svg deleted file mode 100644 index 7412a1b1f0..0000000000 --- a/packages/app/src/components/quick-search/noResult.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/app/src/components/quick-search/noResultSVG.tsx b/packages/app/src/components/quick-search/noResultSVG.tsx new file mode 100644 index 0000000000..16aabb73d9 --- /dev/null +++ b/packages/app/src/components/quick-search/noResultSVG.tsx @@ -0,0 +1,95 @@ +export const NoResultSVG = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/app/src/components/quick-search/results.tsx b/packages/app/src/components/quick-search/results.tsx index 8cf3d2591a..c9a5a56706 100644 --- a/packages/app/src/components/quick-search/results.tsx +++ b/packages/app/src/components/quick-search/results.tsx @@ -6,11 +6,9 @@ import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useAppState } from '@/providers/app-state-provider/context'; import { useRouter } from 'next/router'; import { config } from './config'; -import { useGoToPage } from '@/providers/app-state-provider/hooks'; import { usePageList } from '@/providers/app-state-provider/usePageList'; import { useLoadWorkspace } from '@/providers/app-state-provider/hooks'; -import NextImage from 'next/image'; -import noResultSVG from './noResult.svg'; +import { NoResultSVG } from './noResultSVG'; import { usePageHelper } from '@/hooks/use-page-helper'; export const Results = (props: { query: string; @@ -25,7 +23,7 @@ export const Results = (props: { const { triggerQuickSearchModal } = useModal(); const workspace = useLoadWorkspace(); const pageList = usePageList(workspace); - const goToPage = useGoToPage(); + const { openPage } = usePageHelper(); const router = useRouter(); const { currentWorkspaceId } = useAppState(); const { search } = usePageHelper(); @@ -56,7 +54,7 @@ export const Results = (props: { { - goToPage(result.id); + openPage(result.id); triggerQuickSearchModal(); }} value={result.id} @@ -76,12 +74,7 @@ export const Results = (props: { ) : ( Find 0 result - + ) ) : ( diff --git a/packages/app/src/components/workspace-setting/workspace-setting.tsx b/packages/app/src/components/workspace-setting/workspace-setting.tsx index ff25400507..fcf3962c2d 100644 --- a/packages/app/src/components/workspace-setting/workspace-setting.tsx +++ b/packages/app/src/components/workspace-setting/workspace-setting.tsx @@ -39,6 +39,8 @@ import { useEffect, useState } from 'react'; import { Button } from '@/ui/button'; import Input from '@/ui/input'; import { InviteMembers } from '../invite-members/index'; +import { Workspace, WorkspaceType } from '@pathfinder/data-services'; + enum ActiveTab { 'general' = 'general', 'members' = 'members', @@ -53,6 +55,7 @@ type SettingTabProps = { type WorkspaceSettingProps = { isShow: boolean; onClose?: () => void; + workspace?: Workspace; }; const WorkspaceSettingTab = ({ activeTab, onTabChange }: SettingTabProps) => { @@ -98,6 +101,7 @@ const WorkspaceSettingTab = ({ activeTab, onTabChange }: SettingTabProps) => { export const WorkspaceSetting = ({ isShow, onClose, + workspace, }: WorkspaceSettingProps) => { const [activeTab, setActiveTab] = useState(ActiveTab.general); const handleTabChange = (tab: ActiveTab) => { @@ -106,6 +110,7 @@ export const WorkspaceSetting = ({ const handleClickClose = () => { onClose && onClose(); }; + const isPrivate = workspace?.type === WorkspaceType.Private; useEffect(() => { // reset tab when modal is closed if (!isShow) { @@ -126,7 +131,9 @@ export const WorkspaceSetting = ({ /> - {activeTab === ActiveTab.general && } + {activeTab === ActiveTab.general && ( + + )} {activeTab === ActiveTab.members && } {activeTab === ActiveTab.publish && } @@ -135,14 +142,14 @@ export const WorkspaceSetting = ({ ); }; -const GeneralPage = () => { +const GeneralPage = ({ workspace }: { workspace?: Workspace }) => { return (
Workspace Avatar W - upload - + {/* upload + */} Workspace Name 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 e22c48a520..7c6b487d27 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx @@ -12,6 +12,7 @@ import { } from './WorkspaceItem'; import { WorkspaceSetting } from '@/components/workspace-setting'; import { useState } from 'react'; +import { WorkspaceType } from '@pathfinder/data-services'; export const SelectorPopperContent = () => { const { user, workspacesMeta } = useAppState(); @@ -56,6 +57,13 @@ export const SelectorPopperContent = () => { workspace.id === settingWorkspaceId + ) + : undefined + } /> { - const { pageList, openPage } = useEditor(); + const { openPage } = usePageHelper(); + const pageList = usePageMetaList(); const router = useRouter(); const favoriteList = pageList.filter(p => p.favorite && !p.trash); @@ -66,7 +68,7 @@ export const WorkSpaceSliderBar = () => { const { triggerQuickSearchModal } = useModal(); const [showSubFavorite, setShowSubFavorite] = useState(true); const { createPage, currentWorkspaceId } = useAppState(); - const goToPage = useGoToPage(); + const { openPage } = usePageHelper(); const router = useRouter(); const [showTip, setShowTip] = useState(false); @@ -142,9 +144,8 @@ export const WorkSpaceSliderBar = () => { { const pageId = await createPage?.current?.(); - console.log('pageID', pageId); if (pageId) { - goToPage(pageId); + openPage(pageId); } }} > diff --git a/packages/app/src/hooks/use-change-page-meta.ts b/packages/app/src/hooks/use-change-page-meta.ts index 55f266838c..1b553f2099 100644 --- a/packages/app/src/hooks/use-change-page-meta.ts +++ b/packages/app/src/hooks/use-change-page-meta.ts @@ -1,6 +1,5 @@ import { useCallback } from 'react'; -import { useAppState } from '@/providers/app-state-provider'; -import { PageMeta } from '@blocksuite/store'; +import { useAppState, PageMeta } from '@/providers/app-state-provider'; export type ChangePageMeta = ( pageId: string, diff --git a/packages/app/src/hooks/use-current-page-meta.ts b/packages/app/src/hooks/use-current-page-meta.ts index 0117074e7c..36ccf4befe 100644 --- a/packages/app/src/hooks/use-current-page-meta.ts +++ b/packages/app/src/hooks/use-current-page-meta.ts @@ -1,17 +1,17 @@ import { useCallback, useEffect, useState } from 'react'; -import { useAppState } from '@/providers/app-state-provider'; -import { PageMeta } from '@blocksuite/store'; +import { useAppState, PageMeta } from '@/providers/app-state-provider'; -export const useCurrentPageMeta = () => { +export const useCurrentPageMeta = (): PageMeta | null => { const { currentPage, currentWorkspace } = useAppState(); - const pageMetaHandler = useCallback(() => { + const pageMetaHandler = useCallback((): PageMeta | null => { if (!currentPage || !currentWorkspace) { return null; } return ( - currentWorkspace?.meta.pageMetas.find(p => p.id === currentPage.pageId) ?? - null + (currentWorkspace?.meta.pageMetas.find( + p => p.id === currentPage.pageId + ) as PageMeta) ?? null ); }, [currentPage, currentWorkspace]); diff --git a/packages/app/src/hooks/use-editor-mode.ts b/packages/app/src/hooks/use-editor-mode.ts deleted file mode 100644 index b93e6e2c4b..0000000000 --- a/packages/app/src/hooks/use-editor-mode.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useState } from 'react'; -import { EditorContainer } from '@blocksuite/editor'; -import { useAppState } from '@/providers/app-state-provider'; - -export const usePageHandler = () => { - return null; -}; diff --git a/packages/app/src/hooks/use-history-update.ts b/packages/app/src/hooks/use-history-update.ts new file mode 100644 index 0000000000..8329988e0c --- /dev/null +++ b/packages/app/src/hooks/use-history-update.ts @@ -0,0 +1,37 @@ +import { Page } from '@blocksuite/store'; +import { useAppState } from '@/providers/app-state-provider'; +import { useEffect, useRef } from 'react'; + +export type EventCallBack = (callback: (props: T) => void) => void; +export type UseHistoryUpdated = (page?: Page) => EventCallBack; + +export const useHistoryUpdate: UseHistoryUpdated = () => { + const { currentPage } = useAppState(); + + const callbackQueue = useRef<((page: Page) => void)[]>([]); + + useEffect(() => { + if (!currentPage) { + return; + } + + setTimeout(() => { + currentPage.signals.historyUpdated.on(() => { + callbackQueue.current.forEach(callback => { + callback(currentPage); + }); + }); + }, 300); + + return () => { + callbackQueue.current = []; + currentPage.signals.historyUpdated.dispose(); + }; + }, [currentPage]); + + return callback => { + callbackQueue.current.push(callback); + }; +}; + +export default useHistoryUpdate; diff --git a/packages/app/src/hooks/use-page-helper.ts b/packages/app/src/hooks/use-page-helper.ts index 910e24f996..36ce66e356 100644 --- a/packages/app/src/hooks/use-page-helper.ts +++ b/packages/app/src/hooks/use-page-helper.ts @@ -1,52 +1,88 @@ -import { Page } from '@blocksuite/store'; +import { Workspace } from '@blocksuite/store'; import { QueryContent } from '@blocksuite/store/dist/workspace/search'; -import { PageMeta } from '@/providers/editor-provider'; import { useAppState } from '@/providers/app-state-provider'; +import { EditorContainer } from '@blocksuite/editor'; +import { useChangePageMeta } from '@/hooks/use-change-page-meta'; +import { useRouter } from 'next/router'; export type EditorHandlers = { // createPage: (params?: { pageId?: string; title?: string }) => Promise; - // openPage: ( - // pageId: string, - // query?: { [key: string]: string } - // ) => Promise; + openPage: ( + pageId: string, + query?: { [key: string]: string } + ) => Promise; // getPageMeta: (pageId?: string) => PageMeta; - toggleDeletePage: (pageId: string) => void; - toggleFavoritePage: (pageId: string) => void; - // permanentlyDeletePage: (pageId: string) => void; + toggleDeletePage: (pageId: string) => Promise; + toggleFavoritePage: (pageId: string) => Promise; + permanentlyDeletePage: (pageId: string) => void; search: (query: QueryContent) => Map; // changeEditorMode: (pageId: string) => void; + changePageMode: ( + pageId: string, + mode: EditorContainer['mode'] + ) => Promise; }; +const getPageMeta = (workspace: Workspace | null, pageId: string) => { + return workspace?.meta.pageMetas.find(p => p.id === pageId); +}; export const usePageHelper = (): EditorHandlers => { - const { currentWorkspace } = useAppState(); + const router = useRouter(); + const changePageMeta = useChangePageMeta(); + const { currentWorkspace, editor, currentWorkspaceId } = useAppState(); return { - toggleFavoritePage: (pageId: string) => { - if (!currentWorkspace) { - return; - } - const pageMeta = currentWorkspace.meta.pageMetas.find( - p => p.id === pageId - ); - if (pageMeta) { - currentWorkspace.setPageMeta(pageId, { - favorite: !pageMeta.favorite, - }); + toggleFavoritePage: async pageId => { + const pageMeta = getPageMeta(currentWorkspace, pageId); + if (!pageMeta) { + return Promise.reject('No page'); } + const favorite = !pageMeta.favorite; + changePageMeta(pageMeta.id, { + favorite, + }); + return favorite; }, - toggleDeletePage: (pageId: string) => { - const pageMeta = currentWorkspace?.meta.pageMetas.find( - p => p.id === pageId - ); - if (pageMeta) { - currentWorkspace!.setPageMeta(pageId, { - trash: !pageMeta.trash, - trashDate: +new Date(), - }); + toggleDeletePage: async pageId => { + const pageMeta = getPageMeta(currentWorkspace, pageId); + + if (!pageMeta) { + return Promise.reject('No page'); } + const trash = !pageMeta.trash; + + changePageMeta(pageMeta.id, { + trash, + trashDate: +new Date(), + }); + return trash; }, - search: (query: QueryContent) => { + search: (query: string) => { return currentWorkspace!.search(query); }, + changePageMode: async (pageId, mode) => { + const pageMeta = getPageMeta(currentWorkspace, pageId); + if (!pageMeta) { + return Promise.reject('No page'); + } + + editor?.setAttribute('mode', mode as string); + + changePageMeta(pageMeta.id, { + mode, + }); + return mode; + }, + permanentlyDeletePage: pageId => { + // TODO: workspace.meta.removePage or workspace.removePage? + currentWorkspace!.meta.removePage(pageId); + }, + openPage: (pageId, query = {}) => { + pageId = pageId.replace('space:', ''); + return router.push({ + pathname: `/workspace/${currentWorkspaceId}/${pageId}`, + query, + }); + }, }; }; diff --git a/packages/app/src/hooks/use-page-meta-list.ts b/packages/app/src/hooks/use-page-meta-list.ts index 35bd93ac39..3c55ec9d26 100644 --- a/packages/app/src/hooks/use-page-meta-list.ts +++ b/packages/app/src/hooks/use-page-meta-list.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { PageMeta } from '@/providers/app-state-provider/usePageList'; +import { PageMeta } from '@/providers/app-state-provider'; import { useAppState } from '@/providers/app-state-provider'; export const usePageMetaList = () => { diff --git a/packages/app/src/hooks/use-props-updated.ts b/packages/app/src/hooks/use-props-updated.ts new file mode 100644 index 0000000000..d76ada43d2 --- /dev/null +++ b/packages/app/src/hooks/use-props-updated.ts @@ -0,0 +1,38 @@ +import { useEffect, useRef } from 'react'; +import { EditorContainer } from '@blocksuite/editor'; +import { useAppState } from '@/providers/app-state-provider'; +export type EventCallBack = (callback: (props: T) => void) => void; + +export type UsePropsUpdated = ( + editor?: EditorContainer +) => EventCallBack; + +export const usePropsUpdated: UsePropsUpdated = () => { + const { editor } = useAppState(); + + const callbackQueue = useRef<((editor: EditorContainer) => void)[]>([]); + + useEffect(() => { + if (!editor) { + return; + } + setTimeout(() => { + editor.model?.propsUpdated.on(() => { + callbackQueue.current.forEach(callback => { + callback(editor); + }); + }); + }, 300); + + return () => { + callbackQueue.current = []; + editor?.model?.propsUpdated.dispose(); + }; + }, [editor]); + + return callback => { + callbackQueue.current.push(callback); + }; +}; + +export default usePropsUpdated; diff --git a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx index c8b59769e7..3a26f736ef 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -9,7 +9,7 @@ import { useLoadPage, } from '@/providers/app-state-provider/hooks'; import { useAppState } from '@/providers/app-state-provider/context'; -import exampleMarkdown from '@/providers/editor-provider/example-markdown'; +import exampleMarkdown from '@/static/example-markdown'; const StyledEditorContainer = styled('div')(({ theme }) => { return { diff --git a/packages/app/src/providers/app-state-provider/context.ts b/packages/app/src/providers/app-state-provider/context.ts index 78a6be0b45..115ef7fb84 100644 --- a/packages/app/src/providers/app-state-provider/context.ts +++ b/packages/app/src/providers/app-state-provider/context.ts @@ -1,14 +1,12 @@ -import { createContext, MutableRefObject, RefObject, useContext } from 'react'; +import { createContext, MutableRefObject, useContext } from 'react'; import type { Workspace } from '@pathfinder/data-services'; import { AccessTokenMessage } from '@pathfinder/data-services'; import type { Page as StorePage, - PageMeta, Workspace as StoreWorkspace, } from '@blocksuite/store'; import type { EditorContainer } from '@blocksuite/editor'; -import { QueryContent } from '@blocksuite/store/dist/workspace/search'; - +import { PageMeta } from './interface'; export type LoadWorkspaceHandler = ( workspaceId: string ) => Promise | null; diff --git a/packages/app/src/providers/app-state-provider/hooks.ts b/packages/app/src/providers/app-state-provider/hooks.ts index 6df6738636..c57dd89ab9 100644 --- a/packages/app/src/providers/app-state-provider/hooks.ts +++ b/packages/app/src/providers/app-state-provider/hooks.ts @@ -28,12 +28,3 @@ export const useLoadPage = () => { return currentPage?.pageId === pageId ? currentPage : null; }; - -export const useGoToPage = () => { - const router = useRouter(); - const { currentWorkspaceId } = useAppState(); - - return (pageId: string) => { - router.push(`/workspace/${currentWorkspaceId}/${pageId}`); - }; -}; diff --git a/packages/app/src/providers/app-state-provider/index.ts b/packages/app/src/providers/app-state-provider/index.ts index c38e8e8215..88c38fd7cf 100644 --- a/packages/app/src/providers/app-state-provider/index.ts +++ b/packages/app/src/providers/app-state-provider/index.ts @@ -1 +1,2 @@ export * from './context'; +export * from './interface'; diff --git a/packages/app/src/providers/app-state-provider/interface.ts b/packages/app/src/providers/app-state-provider/interface.ts new file mode 100644 index 0000000000..078eebbf1a --- /dev/null +++ b/packages/app/src/providers/app-state-provider/interface.ts @@ -0,0 +1,19 @@ +import { EditorContainer } from '@blocksuite/editor'; +import { PageMeta as OriginalPageMeta } from '@blocksuite/store/dist/workspace/workspace'; + +// export type PageMeta = { +// favorite: boolean; +// trash: boolean; +// trashDate: number | void; +// updatedDate: number | void; +// mode: EditorContainer['mode']; +// } & OriginalPageMeta; + +export interface PageMeta extends OriginalPageMeta { + favorite: boolean; + trash: boolean; + trashDate: number; + updatedDate: number; + // @ts-ignore + mode: 'edgeless' | 'page'; +} diff --git a/packages/app/src/providers/app-state-provider/provider.tsx b/packages/app/src/providers/app-state-provider/provider.tsx index 69c366233a..50ff911f74 100644 --- a/packages/app/src/providers/app-state-provider/provider.tsx +++ b/packages/app/src/providers/app-state-provider/provider.tsx @@ -14,7 +14,7 @@ import type { } from './context'; import type { Page, Workspace } from '@blocksuite/store'; import { EditorContainer } from '@blocksuite/editor'; - +import { PageMeta } from './interface'; const DynamicBlocksuite = dynamic(() => import('./dynamic-blocksuite'), { ssr: false, }); @@ -143,8 +143,9 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { return null; } return ( - currentWorkspace.meta.pageMetas.find(page => page.id === pageId) || - null + (currentWorkspace.meta.pageMetas.find( + page => page.id === pageId + ) as PageMeta) || null ); }, }), diff --git a/packages/app/src/providers/app-state-provider/usePageList.ts b/packages/app/src/providers/app-state-provider/usePageList.ts index 6d2515200f..e39f5f1b18 100644 --- a/packages/app/src/providers/app-state-provider/usePageList.ts +++ b/packages/app/src/providers/app-state-provider/usePageList.ts @@ -1,15 +1,6 @@ import { useState, useEffect } from 'react'; import type { Workspace } from '@blocksuite/store'; -import type { PageMeta as OriginalPageMeta } from '@blocksuite/store'; -import type { EditorContainer } from '@blocksuite/editor'; - -export type PageMeta = { - favorite: boolean; - trash: boolean; - trashDate: number | void; - updatedDate: number | void; - mode: EditorContainer['mode']; -} & OriginalPageMeta; +import { PageMeta } from './interface'; export const usePageList = (workspace: Workspace | null) => { const [pageList, setPageList] = useState([]); diff --git a/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts index c0b4c11272..ffe4c6a8da 100644 --- a/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts +++ b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts @@ -1,4 +1,3 @@ -import { QueryContent } from '@blocksuite/store/dist/workspace/search'; import { createPage, generateDefaultPageId, initPage } from '../utils'; import { Workspace, Page } from '@blocksuite/store'; import { useRouter } from 'next/router'; @@ -70,9 +69,6 @@ export const useEditorHandler = ({ ); } }, - search: (query: QueryContent) => { - return workspace!.search(query); - }, changeEditorMode: (pageId: string) => { editor!.mode = editor!.mode === 'page' ? 'edgeless' : 'page'; workspace?.setPageMeta(pageId, { mode: editor!.mode }); diff --git a/packages/app/src/providers/editor-provider/index.ts b/packages/app/src/providers/editor-provider/index.ts index 7fd40844ee..d0826b40a6 100644 --- a/packages/app/src/providers/editor-provider/index.ts +++ b/packages/app/src/providers/editor-provider/index.ts @@ -1,4 +1,4 @@ -export * from './editor-provider'; -export * from './example-markdown'; -export * from './utils'; -export * from './interface'; +// export * from './editor-provider'; +// export * from './example-markdown'; +// export * from './utils'; +// export * from './interface'; diff --git a/packages/app/src/providers/editor-provider/interface.ts b/packages/app/src/providers/editor-provider/interface.ts index 1fafdf56ff..a2e893f374 100644 --- a/packages/app/src/providers/editor-provider/interface.ts +++ b/packages/app/src/providers/editor-provider/interface.ts @@ -1,6 +1,5 @@ import { PropsWithChildren } from 'react'; import { Page } from '@blocksuite/store'; -import { QueryContent } from '@blocksuite/store/dist/workspace/search'; import { PageMeta as OriginalPageMeta } from '@blocksuite/store'; import { EditorContainer } from '@blocksuite/editor'; @@ -35,6 +34,5 @@ export type EditorHandlers = { toggleDeletePage: (pageId: string) => void; toggleFavoritePage: (pageId: string) => void; permanentlyDeletePage: (pageId: string) => void; - search: (query: QueryContent) => Map; changeEditorMode: (pageId: string) => void; }; diff --git a/packages/app/src/providers/themeProvider.tsx b/packages/app/src/providers/themeProvider.tsx index f821da5d0a..15185e8f79 100644 --- a/packages/app/src/providers/themeProvider.tsx +++ b/packages/app/src/providers/themeProvider.tsx @@ -21,7 +21,7 @@ import { globalThemeVariables, } from '@/styles/theme'; import { SystemThemeHelper, localStorageThemeHelper } from '@/styles/utils'; -import { useEditor } from '@/providers/editor-provider'; +import useCurrentPageMeta from '@/hooks/use-current-page-meta'; export const ThemeContext = createContext({ mode: 'light', @@ -38,7 +38,7 @@ export const ThemeProvider = ({ }: PropsWithChildren) => { const [theme, setTheme] = useState(defaultTheme); const [mode, setMode] = useState('auto'); - const { mode: editorMode } = useEditor(); + const { mode: editorMode = 'page' } = useCurrentPageMeta() || {}; const themeStyle = theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode); const changeMode = (themeMode: ThemeMode) => { diff --git a/packages/app/src/static/example-markdown.ts b/packages/app/src/static/example-markdown.ts new file mode 100644 index 0000000000..30210ed2c3 --- /dev/null +++ b/packages/app/src/static/example-markdown.ts @@ -0,0 +1,62 @@ +export const exampleMarkdown = `The AFFiNE Alpha is here! You can also view our [Official Website](https://affine.pro/)! + +**What's different in AFFiNE Alpha?** + +1. A much ~smoother editing~ experience, with much ~greater stability~; +2. More complete ~Markdown~ support and improved ~keyboard shortcuts~; +3. New features such as ~dark mode~; + * **Switch between view styles using the** ☀ **and** 🌙. +4. ~Clean and modern UI/UX~ design. + +**Looking for Markdown syntax or keyboard shortcuts?** + +* Find the (?) in the bottom right, then the ️⌨️, to view a full list of \`Keyboard Shortcuts\` + +**Have an enjoyable editing experience !!!** 😃 + +Have some feedback or just want to get in touch? Use the (?), then 🎧 to get in touch and join our communities. + +**Still in development** + +There are also some things you should consider about this AFFiNE Alpha including some ~limitations~: + +* Single page editing - currently editing multiple docs/pages is not supported; +* Changes are not automatically stored, to save changes you should export your data. Options can be found by going to the top right and finding the \`⋮\` icon; +* Without an import/open feature you are still able to access your data by copying it back in. + +**Keyboard Shortcuts:** + +1. Undo: \`⌘+Z\` or \`Ctrl+Z\` +2. Redo: \`⌘+⇧+Z\` or \`Ctrl+Shift+Z\` +3. Bold: \`⌘+B\` or \`Ctrl+B\` +4. Italic: \`⌘+I\` or \`Ctrl+I\` +5. Underline: \`⌘+U\` or \`Ctrl+U\` +6. Strikethrough: \`⌘+⇧+S\` or \`Ctrl+Shift+S\` +7. Inline code: \`⌘+E\` or \`Ctrl+E\` +8. Link: \`⌘+K\` or \`Ctrl+K\` +9. Body text: \`⌘+⌥+0\` or \`Ctrl+Shift+0\` +10. Heading 1: \`⌘+⌥+1\` or \`Ctrl+Shift+1\` +11. Heading 2: \`⌘+⌥+2\` or \`Ctrl+Shift+2\` +12. Heading 3: \`⌘+⌥+3\` or \`Ctrl+Shift+3\` +13. Heading 4: \`⌘+⌥+4\` or \`Ctrl+Shift+4\` +14. Heading 5: \`⌘+⌥+5\` or \`Ctrl+Shift+5\` +15. Heading 6: \`⌘+⌥+6\` or \`Ctrl+Shift+6\` +16. Increase indent: \`Tab\` +17. Reduce indent: \`⇧+Tab\` or \`Shift+Tab\` + +**Markdown Syntax:** + +1. Bold: \`**text**\` +2. Italic: \`*text*\` +3. Underline: \`~text~\` +4. Strikethrough: \`~~text~~\` +5. Inline code: \`\` \`text\` \`\` +6. Heading 1: \`# text\` +7. Heading 2: \`## text\` +8. Heading 3: \`### text\` +9. Heading 4: \`#### text\` +10. Heading 5: \`##### text\` +11. Heading 6: \`###### text\` +`; + +export default exampleMarkdown; diff --git a/packages/data-services/src/request/axios.ts b/packages/data-services/src/request/axios.ts index 807cee8534..1b4bb6d73b 100644 --- a/packages/data-services/src/request/axios.ts +++ b/packages/data-services/src/request/axios.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import { setAuthorization } from './request'; -import { handleResponseError } from './response'; +import { handleResponseError, handleResponse } from './response'; declare module 'axios' { interface AxiosRequestConfig { @@ -8,6 +8,10 @@ declare module 'axios' { * If true, request will send with Authorization header. */ withAuthorization?: boolean; + /** + * If true, indicate this request is refreshing token. + */ + refreshTokenRequest?: boolean; } } @@ -16,4 +20,4 @@ export const request = axios.create({ }); request.interceptors.request.use(setAuthorization); -request.interceptors.response.use(handleResponseError); +request.interceptors.response.use(handleResponse, handleResponseError); diff --git a/packages/data-services/src/request/request/authorization/authorization.ts b/packages/data-services/src/request/request/authorization/authorization.ts index b581bd5ac8..8e8e5668ec 100644 --- a/packages/data-services/src/request/request/authorization/authorization.ts +++ b/packages/data-services/src/request/request/authorization/authorization.ts @@ -36,10 +36,23 @@ function getToken(): { accessToken: string; refreshToken: string } | null { } } +function b64DecodeUnicode(str: string) { + // Going backwards: from byte stream, to percent-encoding, to original string. + return decodeURIComponent( + window + .atob(str) + .split('') + .map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join('') + ); +} + function parseAccessToken(token: string): AccessTokenMessage | null { try { const message: AccessTokenMessage = JSON.parse( - window.atob(token.split('.')[1]) + b64DecodeUnicode(token.split('.')[1]) ); return message; } catch (error) { @@ -76,19 +89,7 @@ export async function setAuthorization(config: AxiosRequestConfig) { throw new Error('No authorization token.'); } if (isAccessTokenExpired(token.accessToken)) { - if (!refreshingToken) { - refreshingToken = login({ - type: 'Refresh', - token: token.refreshToken, - }); - } - const newToken = await refreshingToken; - token = { - accessToken: newToken.token, - refreshToken: newToken.refresh, - }; - setToken(token); - refreshingToken = undefined; + await refreshToken(); } if (!config.headers) { config.headers = {}; @@ -97,3 +98,23 @@ export async function setAuthorization(config: AxiosRequestConfig) { } return config; } + +export const refreshToken = async () => { + let token = getToken(); + if (!token) { + throw new Error('No authorization token.'); + } + if (!refreshingToken) { + refreshingToken = login({ + type: 'Refresh', + token: token.refreshToken, + }); + } + const newToken = await refreshingToken; + token = { + accessToken: newToken.token, + refreshToken: newToken.refresh, + }; + setToken(token); + refreshingToken = undefined; +}; diff --git a/packages/data-services/src/request/response/handle-error.ts b/packages/data-services/src/request/response/handle-error.ts index 5d9bee6a91..e4168691f9 100644 --- a/packages/data-services/src/request/response/handle-error.ts +++ b/packages/data-services/src/request/response/handle-error.ts @@ -1,8 +1,27 @@ -import type { AxiosResponse } from 'axios'; +import type { AxiosResponse, AxiosError } from 'axios'; import { ServiceError } from '../ServiceError'; +import { refreshToken } from '../request/authorization'; +import { request } from '../axios'; -export function handleResponseError(response: AxiosResponse) { - const { data, status } = response; +export async function handleResponseError(error: AxiosError) { + const { response, config, message } = error; + const status = response?.status; + + if (status === 401) { + if (!config) { + throw new ServiceError(message, status.toString()); + } + if (config.refreshTokenRequest) { + throw new ServiceError(message, status.toString()); + } + await refreshToken(); + return request({ ...config, refreshTokenRequest: true }); + } + return response; +} + +export async function handleResponse(response: AxiosResponse) { + const { status, data } = response; if (status === 200) { if (data.error) { // TODO - common error handling diff --git a/packages/data-services/src/request/response/index.ts b/packages/data-services/src/request/response/index.ts index 6180a3906c..94547c0213 100644 --- a/packages/data-services/src/request/response/index.ts +++ b/packages/data-services/src/request/response/index.ts @@ -1 +1 @@ -export { handleResponseError } from './handle-error'; +export { handleResponseError, handleResponse } from './handle-error'; diff --git a/packages/data-services/src/sdks/account.ts b/packages/data-services/src/sdks/account.ts deleted file mode 100644 index b7c91d09d2..0000000000 --- a/packages/data-services/src/sdks/account.ts +++ /dev/null @@ -1,90 +0,0 @@ -import useSWR, { SWRConfiguration } from 'swr'; -import { request, ServiceError } from '../request'; -import type { - RequestAcceptInviting, - RequestInviteCollaborator, - RequestRemoveCollaborator, - ResponseInviteCollaborator, -} from './types'; - -const COLLABORATOR_INVITE_URL = '/api/account/invite'; -const REMOVE_COLLABORATOR_URL = '/api/account/remove_collaborator'; - -async function doInviteCollaborator( - url: string, - req: RequestInviteCollaborator -) { - const { data } = await request.post(url, req); - return data; -} - -export async function inviteCollaborator(req: RequestInviteCollaborator) { - return await doInviteCollaborator(COLLABORATOR_INVITE_URL, req); -} - -async function doRemoveCollaborator( - url: string, - req: RequestRemoveCollaborator -) { - return request.post(url, req); -} - -export async function removeCollaborator(req: RequestRemoveCollaborator) { - return await doRemoveCollaborator(REMOVE_COLLABORATOR_URL, req); -} - -function doAcceptInviting(url: string, req: RequestAcceptInviting) { - return request.post(url, req); -} - -export async function acceptInviting(req: RequestAcceptInviting) { - return await doAcceptInviting(COLLABORATOR_INVITE_URL, req); -} - -export function useInviteCollaborator( - req: RequestInviteCollaborator, - config?: SWRConfiguration -) { - const { data, error } = useSWR( - [COLLABORATOR_INVITE_URL, req], - doInviteCollaborator, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -} - -export function useRemoveCollaborator( - req: RequestRemoveCollaborator, - config?: SWRConfiguration -) { - const { data, error } = useSWR( - [REMOVE_COLLABORATOR_URL, req], - doRemoveCollaborator, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -} - -export function useAcceptInviting( - req: RequestAcceptInviting, - config?: SWRConfiguration -) { - const { data, error } = useSWR( - [COLLABORATOR_INVITE_URL, req], - doAcceptInviting, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -} diff --git a/packages/data-services/src/sdks/types/account.ts b/packages/data-services/src/sdks/types/account.ts deleted file mode 100644 index 8f1fb5f6a8..0000000000 --- a/packages/data-services/src/sdks/types/account.ts +++ /dev/null @@ -1,33 +0,0 @@ -enum WorkspacePermission { - read = 'read', - write = 'write', -} - -export type RequestInviteCollaborator = { - email: string; - workspace_id: string; -}; - -export type ResponseInviteCollaborator = { - inviting_code: string; -}; - -export type RequestRemoveCollaborator = { - user_id: string; - workspace_id: string; -}; - -export type ResponseRemoveCollaborator = { - workspace_id: string; -}; - -export type RequestAcceptInviting = { - inviting_code: string; -}; - -export type ResponseAcceptInviting = { - workspace_id: string; - name: string; - avatar_url: string; - permissions: WorkspacePermission; -}; diff --git a/packages/data-services/src/sdks/types/index.ts b/packages/data-services/src/sdks/types/index.ts index 25b1f4eba7..d0b9323665 100644 --- a/packages/data-services/src/sdks/types/index.ts +++ b/packages/data-services/src/sdks/types/index.ts @@ -1,3 +1 @@ export * from './common'; -export * from './workspace'; -export * from './account'; diff --git a/packages/data-services/src/sdks/types/workspace.ts b/packages/data-services/src/sdks/types/workspace.ts deleted file mode 100644 index 0a6f25ae99..0000000000 --- a/packages/data-services/src/sdks/types/workspace.ts +++ /dev/null @@ -1,32 +0,0 @@ -enum WorkspaceType { - 'personal' = 'personal', - 'team' = 'team', -} - -export type Workspace = { - id: string; - name: string; - workspace_type: WorkspaceType; - avatar_url?: string; - public: boolean; -}; - -export type ResponseGetWorkspaces = { - create: Array; - read: Array; - write: Array; -}; - -export type RequestCreateWorkspace = { - name: string; - avatar_url?: string; - workspace_type: WorkspaceType; -}; - -export type RequestUpdateWorkspace = { - workspace_type?: WorkspaceType; - avatar_url?: string; - public?: boolean; -}; - -export type ResponseCreateWorkspace = any; diff --git a/packages/data-services/src/sdks/workspace.bak.ts b/packages/data-services/src/sdks/workspace.bak.ts deleted file mode 100644 index adbd3c9d77..0000000000 --- a/packages/data-services/src/sdks/workspace.bak.ts +++ /dev/null @@ -1,106 +0,0 @@ -import useSWR, { SWRConfiguration } from 'swr'; -import type { - MayError, - RequestCreateWorkspace, - RequestUpdateWorkspace, - ResponseCreateWorkspace, - ResponseGetWorkspaces, -} from './types'; -import { request, ServiceError } from '../request'; - -const WORKSPACE_URL = '/api/workspace'; - -async function doGetWorkSpaces(url: string) { - const { data } = await request.get(url); - return data; -} - -export async function getWorkSpaces() { - return await doGetWorkSpaces(WORKSPACE_URL); -} - -async function doCreateWorkspace(url: string, req: RequestCreateWorkspace) { - const { data } = await request.put(url, req); - return data; -} - -export async function createWorkspace(req: RequestCreateWorkspace) { - doCreateWorkspace(WORKSPACE_URL, req); -} - -async function doUpdateWorkspace(url: string, req: RequestUpdateWorkspace) { - const { data } = await request.post(url, req); - return data; -} - -export function updateWorkspace(id: string, req: RequestUpdateWorkspace) { - return doUpdateWorkspace(`${WORKSPACE_URL}${id}`, req); -} - -async function doDeleteWorkspace(url: string) { - const { data } = await request.delete(url); - return data; -} - -export function deleteWorkspace(id: string) { - return doDeleteWorkspace(`${WORKSPACE_URL}${id}`); -} - -export function useGetWorkspaces(config?: SWRConfiguration) { - const { data, error } = useSWR( - WORKSPACE_URL, - doGetWorkSpaces, - config - ); - return { - data, - isLoading: !data, - isError: data && !error, - }; -} - -export function useCreateWorkspace( - req: RequestCreateWorkspace, - config?: SWRConfiguration -) { - const { data, error } = useSWR( - [WORKSPACE_URL, req], - doCreateWorkspace, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -} - -export function useUpdateWorkspace( - id: string, - req: RequestCreateWorkspace, - config?: SWRConfiguration -) { - const { data, error } = useSWR( - [`${WORKSPACE_URL}${id}`, req], - doUpdateWorkspace, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -} - -export function useDeleteWorkspace(id: string, config?: SWRConfiguration) { - const { data, error } = useSWR( - `${WORKSPACE_URL}${id}`, - doDeleteWorkspace, - config - ); - return { - data, - isLoading: !error && !data, - isError: error, - }; -}