refactor!: next generation AFFiNE code structure (#1176)

This commit is contained in:
Himself65
2023-03-01 01:40:01 -06:00
committed by GitHub
parent 2dcccc772c
commit e0481d29ad
270 changed files with 8308 additions and 6829 deletions
@@ -0,0 +1,41 @@
import React from 'react';
import { RemWorkspace } from '../../../../shared';
import { WorkspaceAvatar } from '../../workspace-avatar';
import { SelectorWrapper, WorkspaceName } from './styles';
export type WorkspaceSelectorProps = {
currentWorkspace: RemWorkspace | null;
onClick: () => void;
};
export const WorkspaceSelector: React.FC<WorkspaceSelectorProps> = ({
currentWorkspace,
onClick,
}) => {
let name = 'Untitled Workspace';
if (currentWorkspace) {
if (currentWorkspace.flavour === 'affine') {
if (currentWorkspace.firstBinarySynced) {
name = currentWorkspace.blockSuiteWorkspace.meta.name;
}
} else if (currentWorkspace.flavour === 'local') {
name = currentWorkspace.blockSuiteWorkspace.meta.name;
}
}
return (
<>
<SelectorWrapper onClick={onClick} data-testid="current-workspace">
<WorkspaceAvatar
data-testid="workspace-avatar"
style={{
flexShrink: 0,
}}
size={32}
workspace={currentWorkspace}
/>
<WorkspaceName data-testid="workspace-name">{name}</WorkspaceName>
</SelectorWrapper>
</>
);
};
@@ -0,0 +1 @@
export * from './WorkspaceSelector';
@@ -0,0 +1,32 @@
import { MuiAvatar, textEllipsis } from '@affine/component';
import { styled } from '@affine/component';
export const SelectorWrapper = styled('div')(({ theme }) => {
return {
height: '42px',
display: 'flex',
alignItems: 'center',
padding: '0 12px',
borderRadius: '5px',
color: theme.colors.textColor,
':hover': {
cursor: 'pointer',
background: theme.colors.hoverBackground,
},
};
});
export const Avatar = styled(MuiAvatar)({
height: '28px',
width: '28px',
});
export const WorkspaceName = styled('span')(({ theme }) => {
return {
marginLeft: '12px',
fontSize: theme.font.h6,
fontWeight: 500,
marginTop: '4px',
flexGrow: 1,
...textEllipsis(1),
};
});
@@ -0,0 +1,13 @@
export const Arrow = () => {
return (
<svg
width="6"
height="10"
viewBox="0 0 6 10"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0.354 9.22997C0.201333 9.0773 0.125 8.91764 0.125 8.75097C0.125 8.5843 0.201333 8.42464 0.354 8.27197L3.625 5.00097L0.354 1.72997C0.201333 1.5773 0.125 1.41764 0.125 1.25097C0.125 1.0843 0.201333 0.924636 0.354 0.771969C0.506667 0.619302 0.666333 0.542969 0.833 0.542969C0.999667 0.542969 1.15933 0.619302 1.312 0.771969L4.979 4.43897C5.06233 4.52164 5.125 4.61164 5.167 4.70897C5.20833 4.8063 5.229 4.90364 5.229 5.00097C5.229 5.0983 5.20833 5.19564 5.167 5.29297C5.125 5.3903 5.06233 5.4803 4.979 5.56297L1.312 9.22997C1.15933 9.38264 0.999667 9.45897 0.833 9.45897C0.666333 9.45897 0.506667 9.38264 0.354 9.22997Z" />
</svg>
);
};
@@ -0,0 +1,27 @@
export const AffineIcon = () => {
return (
<svg
width="40"
height="40"
viewBox="0 0 40 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="0.5"
y="0.5"
width="39"
height="39"
rx="19.5"
stroke="#6880FF"
fill="#FFF"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18.6303 8.79688L11.2559 29.8393H15.5752L20.2661 15.2858L24.959 29.8393H29.2637L21.8881 8.79688H18.6303Z"
fill="#6880FF"
/>
</svg>
);
};
@@ -0,0 +1,273 @@
import { MuiCollapse } from '@affine/component';
import { Tooltip } from '@affine/component';
import { IconButton } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import {
ArrowDownSmallIcon,
DeleteTemporarilyIcon,
FavoriteIcon,
FolderIcon,
PlusIcon,
SearchIcon,
SettingsIcon,
} from '@blocksuite/icons';
import { PageMeta } from '@blocksuite/store';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useCallback, useMemo, useState } from 'react';
import { usePageMeta } from '../../../hooks/use-page-meta';
import { RemWorkspace } from '../../../shared';
import { Arrow } from './icons';
import {
StyledArrowButton,
StyledLink,
StyledListItem,
StyledNewPageButton,
StyledSliderBar,
StyledSliderBarWrapper,
StyledSubListItem,
} from './style';
import { WorkspaceSelector } from './WorkspaceSelector';
export type FavoriteListProps = {
currentPageId: string | null;
openPage: (pageId: string) => void;
showList: boolean;
pageMeta: PageMeta[];
};
const FavoriteList: React.FC<FavoriteListProps> = ({
pageMeta,
openPage,
showList,
}) => {
const router = useRouter();
const { t } = useTranslation();
const favoriteList = useMemo(
() => pageMeta.filter(p => p.favorite && !p.trash),
[pageMeta]
);
return (
<MuiCollapse in={showList}>
{favoriteList.map((pageMeta, index) => {
const active = router.query.pageId === pageMeta.id;
return (
<StyledSubListItem
data-testid={`favorite-list-item-${pageMeta.id}`}
active={active}
key={`${pageMeta}-${index}`}
onClick={() => {
if (active) {
return;
}
openPage(pageMeta.id);
}}
>
{pageMeta.title || 'Untitled'}
</StyledSubListItem>
);
})}
{favoriteList.length === 0 && (
<StyledSubListItem disable={true}>{t('No item')}</StyledSubListItem>
)}
</MuiCollapse>
);
};
export type WorkSpaceSliderBarProps = {
isPublicWorkspace: boolean;
onOpenQuickSearchModal: () => void;
onOpenWorkspaceListModal: () => void;
currentWorkspace: RemWorkspace | null;
currentPageId: string | null;
openPage: (pageId: string) => void;
createPage: () => Promise<string>;
show: boolean;
setShow: (show: boolean) => void;
currentPath: string;
paths: {
all: (workspaceId: string) => string;
favorite: (workspaceId: string) => string;
trash: (workspaceId: string) => string;
setting: (workspaceId: string) => string;
};
};
export const WorkSpaceSliderBar: React.FC<WorkSpaceSliderBarProps> = ({
isPublicWorkspace,
currentWorkspace,
currentPageId,
openPage,
createPage,
show,
setShow,
currentPath,
paths,
onOpenQuickSearchModal,
onOpenWorkspaceListModal,
}) => {
const currentWorkspaceId = currentWorkspace?.id || null;
const [showSubFavorite, setShowSubFavorite] = useState(true);
const [showTip, setShowTip] = useState(false);
const { t } = useTranslation();
const pageMeta = usePageMeta(currentWorkspace?.blockSuiteWorkspace ?? null);
const onClickNewPage = useCallback(async () => {
const pageId = await createPage();
if (pageId) {
openPage(pageId);
}
}, [createPage, openPage]);
return (
<>
<StyledSliderBar show={isPublicWorkspace ? false : show}>
<Tooltip
content={show ? t('Collapse sidebar') : t('Expand sidebar')}
placement="right"
visible={showTip}
>
<StyledArrowButton
data-testid="sliderBar-arrowButton"
isShow={show}
style={{
visibility: isPublicWorkspace ? 'hidden' : 'visible',
}}
onClick={useCallback(() => {
setShow(!show);
setShowTip(false);
}, [setShow, show])}
onMouseEnter={useCallback(() => {
setShowTip(true);
}, [])}
onMouseLeave={useCallback(() => {
setShowTip(false);
}, [])}
>
<Arrow />
</StyledArrowButton>
</Tooltip>
<StyledSliderBarWrapper data-testid="sliderBar">
<WorkspaceSelector
currentWorkspace={currentWorkspace}
onClick={onOpenWorkspaceListModal}
/>
<StyledListItem
data-testid="slider-bar-quick-search-button"
style={{ cursor: 'pointer' }}
onClick={useCallback(() => {
onOpenQuickSearchModal();
}, [onOpenQuickSearchModal])}
>
<SearchIcon />
{t('Quick search')}
</StyledListItem>
<Link
href={{
pathname: currentWorkspaceId && paths.all(currentWorkspaceId),
}}
>
<StyledListItem
active={
currentPath ===
(currentWorkspaceId && paths.all(currentWorkspaceId))
}
>
<FolderIcon />
<span data-testid="all-pages">{t('All pages')}</span>
</StyledListItem>
</Link>
<StyledListItem
active={
currentPath ===
(currentWorkspaceId && paths.favorite(currentWorkspaceId))
}
>
<StyledLink
href={{
pathname:
currentWorkspaceId && paths.favorite(currentWorkspaceId),
}}
>
<FavoriteIcon />
{t('Favorites')}
</StyledLink>
<IconButton
darker={true}
onClick={useCallback(() => {
setShowSubFavorite(!showSubFavorite);
}, [showSubFavorite])}
>
<ArrowDownSmallIcon
style={{
transform: `rotate(${showSubFavorite ? '180' : '0'}deg)`,
}}
/>
</IconButton>
</StyledListItem>
<FavoriteList
currentPageId={currentPageId}
showList={showSubFavorite}
openPage={openPage}
pageMeta={pageMeta}
/>
<StyledListItem
active={
currentPath ===
(currentWorkspaceId && paths.setting(currentWorkspaceId))
}
>
<StyledLink
href={{
pathname:
currentWorkspaceId && paths.setting(currentWorkspaceId),
}}
>
<SettingsIcon />
{t('Workspace Settings')}
</StyledLink>
</StyledListItem>
{/* <WorkspaceSetting
isShow={showWorkspaceSetting}
onClose={() => {
setShowWorkspaceSetting(false);
}}
/> */}
{/* TODO: will finish the feature next version */}
{/* <StyledListItem
onClick={() => {
triggerImportModal();
}}
>
<ImportIcon /> {t('Import')}
</StyledListItem> */}
<Link
href={{
pathname: currentWorkspaceId && paths.trash(currentWorkspaceId),
}}
>
<StyledListItem
active={
currentPath ===
(currentWorkspaceId && paths.trash(currentWorkspaceId))
}
>
<DeleteTemporarilyIcon /> {t('Trash')}
</StyledListItem>
</Link>
<StyledNewPageButton
data-testid="new-page-button"
onClick={onClickNewPage}
>
<PlusIcon /> {t('New Page')}
</StyledNewPageButton>
</StyledSliderBarWrapper>
</StyledSliderBar>
</>
);
};
export default WorkSpaceSliderBar;
@@ -0,0 +1,139 @@
import { displayFlex, styled, textEllipsis } from '@affine/component';
import Link from 'next/link';
export const StyledSliderBar = styled.div<{ show: boolean }>(
({ theme, show }) => {
return {
width: show ? '256px' : '0',
height: '100vh',
minHeight: '450px',
background: theme.colors.hubBackground,
boxShadow: theme.shadow.modal,
transition: 'width .15s, padding .15s',
position: 'relative',
zIndex: theme.zIndex.modal,
padding: show ? '24px 12px' : '24px 0',
flexShrink: 0,
};
}
);
export const StyledSliderBarWrapper = styled.div(() => {
return {
height: '100%',
overflowX: 'hidden',
overflowY: 'auto',
position: 'relative',
};
});
export const StyledArrowButton = styled.button<{ isShow: boolean }>(
({ theme, isShow }) => {
return {
width: '32px',
height: '32px',
...displayFlex('center', 'center'),
color: theme.colors.primaryColor,
backgroundColor: theme.colors.hoverBackground,
borderRadius: '50%',
transition: 'all .15s',
position: 'absolute',
top: '34px',
right: '-20px',
zIndex: theme.zIndex.modal,
svg: {
transform: isShow ? 'rotate(180deg)' : 'unset',
},
':hover': {
color: '#fff',
backgroundColor: theme.colors.primaryColor,
},
};
}
);
export const StyledListItem = styled.div<{
active?: boolean;
disabled?: boolean;
}>(({ theme, active, disabled }) => {
return {
height: '32px',
marginTop: '12px',
color: active ? theme.colors.primaryColor : theme.colors.textColor,
paddingLeft: '12px',
borderRadius: '5px',
cursor: 'pointer',
...displayFlex('flex-start', 'center'),
...(disabled
? {
cursor: 'not-allowed',
color: theme.colors.borderColor,
}
: {}),
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
':hover:not([disabled])': {
color: theme.colors.primaryColor,
backgroundColor: theme.colors.hoverBackground,
},
};
});
export const StyledLink = styled(Link)(() => {
return {
flexGrow: 1,
textAlign: 'left',
color: 'inherit',
...displayFlex('flex-start', 'center'),
':visited': {
color: 'inherit',
},
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});
export const StyledNewPageButton = styled(StyledListItem)(() => {
return {
position: 'absolute',
bottom: '24px',
left: '0',
right: '0',
margin: 'auto',
':hover': {
cursor: 'pointer',
},
};
});
export const StyledSubListItem = styled.button<{
disable?: boolean;
active?: boolean;
}>(({ theme, disable, active }) => {
return {
width: '100%',
height: '32px',
marginTop: '4px',
color: disable
? theme.colors.disableColor
: active
? theme.colors.primaryColor
: theme.colors.popoverColor,
backgroundColor: active ? theme.colors.hoverBackground : 'unset',
cursor: disable ? 'not-allowed' : 'pointer',
paddingLeft: '45px',
lineHeight: '32px',
textAlign: 'start',
...textEllipsis(1),
':hover': disable
? {}
: {
color: theme.colors.primaryColor,
backgroundColor: theme.colors.hoverBackground,
},
};
});