mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 09:36:17 +08:00
Merge branch 'feat/filesystem_and_search' of github.com:toeverything/AFFiNE into feat/filesystem_and_search
This commit is contained in:
@@ -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 = () => {
|
||||
<StyledToolbarItem
|
||||
disable={!canUndo}
|
||||
onClick={() => {
|
||||
page?.undo();
|
||||
currentPage?.undo();
|
||||
}}
|
||||
>
|
||||
<UndoIcon />
|
||||
@@ -120,7 +123,7 @@ const UndoRedo = () => {
|
||||
<StyledToolbarItem
|
||||
disable={!canRedo}
|
||||
onClick={() => {
|
||||
page?.redo();
|
||||
currentPage?.redo();
|
||||
}}
|
||||
>
|
||||
<RedoIcon />
|
||||
@@ -131,7 +134,7 @@ const UndoRedo = () => {
|
||||
};
|
||||
|
||||
export const EdgelessToolbar = () => {
|
||||
const { mode } = useEditor();
|
||||
const { mode } = useCurrentPageMeta() || {};
|
||||
|
||||
return (
|
||||
<Slide
|
||||
|
||||
@@ -13,8 +13,8 @@ import type {
|
||||
} from './type';
|
||||
import { useTheme } from '@/providers/themeProvider';
|
||||
import { EdgelessIcon, PaperIcon } from './icons';
|
||||
import { useEditor } from '@/providers/editor-provider';
|
||||
import { useAppState } from '@/providers/app-state-provider/context';
|
||||
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
|
||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||
|
||||
const PaperItem = ({ active }: { active?: boolean }) => {
|
||||
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}
|
||||
>
|
||||
<AnimateRadioItem
|
||||
isLeft={true}
|
||||
@@ -112,7 +111,7 @@ export const EditorModeSwitch = ({
|
||||
active={mode === 'page'}
|
||||
status={radioItemStatus.left}
|
||||
onClick={() => {
|
||||
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({
|
||||
|
||||
@@ -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 (
|
||||
<Header>
|
||||
{title && (
|
||||
|
||||
@@ -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 = () => {
|
||||
<MenuItem
|
||||
icon={mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
|
||||
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 (
|
||||
<StyledHeaderContainer hasWarning={showWarning}>
|
||||
<BrowserWarning
|
||||
|
||||
@@ -8,12 +8,11 @@ import {
|
||||
import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './icons';
|
||||
import Grow from '@mui/material/Grow';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { useEditor } from '@/providers/editor-provider';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { useTheme } from '@/providers/themeProvider';
|
||||
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
|
||||
|
||||
export type IslandItemNames = 'contact' | 'shortcuts';
|
||||
// export type IslandShowMap = Record<IslandItemNames, boolean>;
|
||||
|
||||
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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PageMeta, useEditor } from '@/providers/editor-provider';
|
||||
import { PageMeta } from '@/providers/app-state-provider';
|
||||
import {
|
||||
FavouritedIcon,
|
||||
FavouritesIcon,
|
||||
|
||||
@@ -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 }) => {
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
goToPage(id);
|
||||
openPage(id);
|
||||
}}
|
||||
icon={<OpenInNewIcon />}
|
||||
>
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
<RestoreIcon />
|
||||
|
||||
@@ -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();
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB |
File diff suppressed because one or more lines are too long
@@ -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: {
|
||||
<Command.Item
|
||||
key={result.id}
|
||||
onSelect={() => {
|
||||
goToPage(result.id);
|
||||
openPage(result.id);
|
||||
triggerQuickSearchModal();
|
||||
}}
|
||||
value={result.id}
|
||||
@@ -76,12 +74,7 @@ export const Results = (props: {
|
||||
) : (
|
||||
<StyledNotFound>
|
||||
<span>Find 0 result</span>
|
||||
<NextImage
|
||||
alt="no result"
|
||||
src={noResultSVG}
|
||||
width={150}
|
||||
height={150}
|
||||
></NextImage>
|
||||
<NoResultSVG />
|
||||
</StyledNotFound>
|
||||
)
|
||||
) : (
|
||||
|
||||
@@ -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>(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 = ({
|
||||
/>
|
||||
</StyledSettingSidebar>
|
||||
<StyledSettingContent>
|
||||
{activeTab === ActiveTab.general && <GeneralPage />}
|
||||
{activeTab === ActiveTab.general && (
|
||||
<GeneralPage workspace={workspace} />
|
||||
)}
|
||||
{activeTab === ActiveTab.members && <MembersPage />}
|
||||
{activeTab === ActiveTab.publish && <PublishPage />}
|
||||
</StyledSettingContent>
|
||||
@@ -135,14 +142,14 @@ export const WorkspaceSetting = ({
|
||||
);
|
||||
};
|
||||
|
||||
const GeneralPage = () => {
|
||||
const GeneralPage = ({ workspace }: { workspace?: Workspace }) => {
|
||||
return (
|
||||
<div>
|
||||
<StyledSettingH2 marginTop={56}>Workspace Avatar</StyledSettingH2>
|
||||
<StyledSettingAvatarContent>
|
||||
<StyledSettingAvatar alt="workspace avatar">W</StyledSettingAvatar>
|
||||
<StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn>
|
||||
<Button shape="round">remove</Button>
|
||||
{/* <StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn>
|
||||
<Button shape="round">remove</Button> */}
|
||||
</StyledSettingAvatarContent>
|
||||
<StyledSettingH2 marginTop={36}>Workspace Name</StyledSettingH2>
|
||||
<StyledSettingInputContainer>
|
||||
|
||||
+8
@@ -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 = () => {
|
||||
<WorkspaceSetting
|
||||
isShow={Boolean(settingWorkspaceId)}
|
||||
onClose={handleCloseWorkSpace}
|
||||
workspace={
|
||||
settingWorkspaceId
|
||||
? workspacesMeta.find(
|
||||
workspace => workspace.id === settingWorkspaceId
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<StyledDivider />
|
||||
<ListItem
|
||||
|
||||
@@ -23,17 +23,19 @@ import {
|
||||
} from '@blocksuite/icons';
|
||||
import Link from 'next/link';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { useEditor } from '@/providers/editor-provider';
|
||||
import { useModal } from '@/providers/global-modal-provider';
|
||||
import { useGoToPage } from '@/providers/app-state-provider/hooks';
|
||||
import { useAppState } from '@/providers/app-state-provider/context';
|
||||
|
||||
import { IconButton } from '@/ui/button';
|
||||
import { WorkspaceSelector } from './WorkspaceSelector';
|
||||
import useLocalStorage from '@/hooks/use-local-storage';
|
||||
import { useTranslation } from '@/libs/i18n';
|
||||
import usePageMetaList from '@/hooks/use-page-meta-list';
|
||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||
|
||||
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
||||
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 = () => {
|
||||
<StyledNewPageButton
|
||||
onClick={async () => {
|
||||
const pageId = await createPage?.current?.();
|
||||
console.log('pageID', pageId);
|
||||
if (pageId) {
|
||||
goToPage(pageId);
|
||||
openPage(pageId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Page } from '@blocksuite/store';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export type EventCallBack<T> = (callback: (props: T) => void) => void;
|
||||
export type UseHistoryUpdated = (page?: Page) => EventCallBack<Page>;
|
||||
|
||||
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;
|
||||
@@ -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<Page>;
|
||||
// openPage: (
|
||||
// pageId: string,
|
||||
// query?: { [key: string]: string }
|
||||
// ) => Promise<boolean>;
|
||||
openPage: (
|
||||
pageId: string,
|
||||
query?: { [key: string]: string }
|
||||
) => Promise<boolean>;
|
||||
// getPageMeta: (pageId?: string) => PageMeta;
|
||||
toggleDeletePage: (pageId: string) => void;
|
||||
toggleFavoritePage: (pageId: string) => void;
|
||||
// permanentlyDeletePage: (pageId: string) => void;
|
||||
toggleDeletePage: (pageId: string) => Promise<boolean>;
|
||||
toggleFavoritePage: (pageId: string) => Promise<boolean>;
|
||||
permanentlyDeletePage: (pageId: string) => void;
|
||||
search: (query: QueryContent) => Map<string, string | undefined>;
|
||||
// changeEditorMode: (pageId: string) => void;
|
||||
changePageMode: (
|
||||
pageId: string,
|
||||
mode: EditorContainer['mode']
|
||||
) => Promise<EditorContainer['mode']>;
|
||||
};
|
||||
|
||||
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,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
export type EventCallBack<T> = (callback: (props: T) => void) => void;
|
||||
|
||||
export type UsePropsUpdated = (
|
||||
editor?: EditorContainer
|
||||
) => EventCallBack<EditorContainer>;
|
||||
|
||||
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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<StoreWorkspace | null> | null;
|
||||
|
||||
@@ -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}`);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './context';
|
||||
export * from './interface';
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -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<PageMeta[]>([]);
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<string, string | undefined>;
|
||||
changeEditorMode: (pageId: string) => void;
|
||||
};
|
||||
|
||||
@@ -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<ThemeProviderValue>({
|
||||
mode: 'light',
|
||||
@@ -38,7 +38,7 @@ export const ThemeProvider = ({
|
||||
}: PropsWithChildren<ThemeProviderProps>) => {
|
||||
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
||||
const [mode, setMode] = useState<ThemeMode>('auto');
|
||||
const { mode: editorMode } = useEditor();
|
||||
const { mode: editorMode = 'page' } = useCurrentPageMeta() || {};
|
||||
const themeStyle =
|
||||
theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode);
|
||||
const changeMode = (themeMode: ThemeMode) => {
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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<unknown>) {
|
||||
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<unknown>) {
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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<any, any>) {
|
||||
const { data, status } = response;
|
||||
export async function handleResponseError(error: AxiosError<any, any>) {
|
||||
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<any, any>) {
|
||||
const { status, data } = response;
|
||||
if (status === 200) {
|
||||
if (data.error) {
|
||||
// TODO - common error handling
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { handleResponseError } from './handle-error';
|
||||
export { handleResponseError, handleResponse } from './handle-error';
|
||||
|
||||
@@ -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<ResponseInviteCollaborator>(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<ResponseInviteCollaborator, ServiceError>(
|
||||
[COLLABORATOR_INVITE_URL, req],
|
||||
doInviteCollaborator,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function useRemoveCollaborator(
|
||||
req: RequestRemoveCollaborator,
|
||||
config?: SWRConfiguration
|
||||
) {
|
||||
const { data, error } = useSWR<unknown, ServiceError>(
|
||||
[REMOVE_COLLABORATOR_URL, req],
|
||||
doRemoveCollaborator,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function useAcceptInviting(
|
||||
req: RequestAcceptInviting,
|
||||
config?: SWRConfiguration
|
||||
) {
|
||||
const { data, error } = useSWR<unknown, ServiceError>(
|
||||
[COLLABORATOR_INVITE_URL, req],
|
||||
doAcceptInviting,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -1,3 +1 @@
|
||||
export * from './common';
|
||||
export * from './workspace';
|
||||
export * from './account';
|
||||
|
||||
@@ -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<Workspace>;
|
||||
read: Array<Workspace>;
|
||||
write: Array<Workspace>;
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -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<ResponseGetWorkspaces>(url);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getWorkSpaces() {
|
||||
return await doGetWorkSpaces(WORKSPACE_URL);
|
||||
}
|
||||
|
||||
async function doCreateWorkspace(url: string, req: RequestCreateWorkspace) {
|
||||
const { data } = await request.put<ResponseCreateWorkspace>(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<MayError>(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<MayError>(url);
|
||||
return data;
|
||||
}
|
||||
|
||||
export function deleteWorkspace(id: string) {
|
||||
return doDeleteWorkspace(`${WORKSPACE_URL}${id}`);
|
||||
}
|
||||
|
||||
export function useGetWorkspaces(config?: SWRConfiguration) {
|
||||
const { data, error } = useSWR<ResponseGetWorkspaces, ServiceError, string>(
|
||||
WORKSPACE_URL,
|
||||
doGetWorkSpaces,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !data,
|
||||
isError: data && !error,
|
||||
};
|
||||
}
|
||||
|
||||
export function useCreateWorkspace(
|
||||
req: RequestCreateWorkspace,
|
||||
config?: SWRConfiguration
|
||||
) {
|
||||
const { data, error } = useSWR<ResponseGetWorkspaces, ServiceError>(
|
||||
[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<MayError, ServiceError>(
|
||||
[`${WORKSPACE_URL}${id}`, req],
|
||||
doUpdateWorkspace,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function useDeleteWorkspace(id: string, config?: SWRConfiguration) {
|
||||
const { data, error } = useSWR<MayError, ServiceError>(
|
||||
`${WORKSPACE_URL}${id}`,
|
||||
doDeleteWorkspace,
|
||||
config
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user