mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-10 11:28:45 +00:00
Compare commits
17 Commits
v0.5.4-can
...
v0.5.4-can
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10cd000822 | ||
|
|
496225a92e | ||
|
|
1ef408c9ad | ||
|
|
8d8119b39b | ||
|
|
80c1f9e546 | ||
|
|
dbd3249ae5 | ||
|
|
fbbcb4bad9 | ||
|
|
33069c87d0 | ||
|
|
637b8203d3 | ||
|
|
92859bf8b9 | ||
|
|
8a617f91e6 | ||
|
|
84b36c1d35 | ||
|
|
2c49c774af | ||
|
|
de0b300aca | ||
|
|
4a50fe584c | ||
|
|
f7d1d922fa | ||
|
|
1b12972afd |
1
.github/workflows/release-desktop-app.yml
vendored
1
.github/workflows/release-desktop-app.yml
vendored
@@ -63,6 +63,7 @@ jobs:
|
||||
NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
API_SERVER_PROFILE: prod
|
||||
ENABLE_TEST_PROPERTIES: false
|
||||
|
||||
- name: Upload Artifact (web-static)
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine/electron",
|
||||
"private": true,
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"author": "affine",
|
||||
"description": "AFFiNE App",
|
||||
"homepage": "https://github.com/toeverything/AFFiNE",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine/server",
|
||||
"private": true,
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"description": "Affine Node.js server",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
@@ -38,7 +38,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/node": "^18.16.3",
|
||||
"@types/node": "^18.16.5",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"c8": "^7.13.0",
|
||||
"nodemon": "^2.0.22",
|
||||
|
||||
@@ -80,6 +80,11 @@ const nextConfig = {
|
||||
removeConsole: {
|
||||
exclude: ['error', 'log', 'warn', 'info'],
|
||||
},
|
||||
reactRemoveProperties: !buildFlags.enableTestProperties
|
||||
? {
|
||||
properties: ['^data-testid$'],
|
||||
}
|
||||
: false,
|
||||
emotion: {
|
||||
sourceMap: true,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine/web",
|
||||
"private": true,
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
@@ -19,11 +19,11 @@
|
||||
"@affine/jotai": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@affine/workspace": "workspace:*",
|
||||
"@blocksuite/blocks": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/blocks": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/icons": "^2.1.15",
|
||||
"@blocksuite/store": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"@dnd-kit/sortable": "^7.0.2",
|
||||
"@emotion/cache": "^11.10.8",
|
||||
@@ -38,7 +38,7 @@
|
||||
"css-spring": "^4.1.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"graphql": "^16.6.0",
|
||||
"jotai": "^2.0.4",
|
||||
"jotai": "^2.1.0",
|
||||
"jotai-devtools": "^0.5.2",
|
||||
"lit": "^2.7.4",
|
||||
"lottie-web": "^5.11.0",
|
||||
@@ -59,14 +59,14 @@
|
||||
"@swc-jotai/debug-label": "^0.0.10",
|
||||
"@swc-jotai/react-refresh": "^0.0.8",
|
||||
"@types/react": "^18.2.5",
|
||||
"@types/react-dom": "^18.2.3",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"@vanilla-extract/css": "^1.11.0",
|
||||
"@vanilla-extract/next-plugin": "^2.1.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-next": "^13.4.0",
|
||||
"next": "^13.4.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-next": "^13.4.1",
|
||||
"next": "^13.4.1",
|
||||
"next-debug-local": "^0.1.5",
|
||||
"next-router-mock": "^0.9.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
|
||||
@@ -18,6 +18,9 @@ export const blockSuiteFeatureFlags = {
|
||||
* @type {import('@affine/env').BuildFlags}
|
||||
*/
|
||||
export const buildFlags = {
|
||||
enableTestProperties: process.env.ENABLE_TEST_PROPERTIES
|
||||
? process.env.ENABLE_TEST_PROPERTIES === 'true'
|
||||
: true,
|
||||
enableLegacyCloud: process.env.ENABLE_LEGACY_PROVIDER
|
||||
? process.env.ENABLE_LEGACY_PROVIDER === 'true'
|
||||
: true,
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { MenuItem } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ArrowRightSmallIcon, MoveToIcon } from '@blocksuite/icons';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { PinboardMenu } from '../pinboard';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
|
||||
export type MoveToProps = CommonMenuItemProps<{
|
||||
dragId: string;
|
||||
dropId: string;
|
||||
}> & {
|
||||
metas: PageMeta[];
|
||||
currentMeta: PageMeta;
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const MoveTo = ({
|
||||
metas,
|
||||
currentMeta,
|
||||
blockSuiteWorkspace,
|
||||
onSelect,
|
||||
onItemClick,
|
||||
}: MoveToProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
const open = anchorEl !== null;
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
ref={ref}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setAnchorEl(ref.current);
|
||||
onItemClick?.();
|
||||
}}
|
||||
icon={<MoveToIcon />}
|
||||
endIcon={<ArrowRightSmallIcon />}
|
||||
data-testid="move-to-menu-item"
|
||||
>
|
||||
{t['Move to']()}
|
||||
</MenuItem>
|
||||
<PinboardMenu
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
placement="left"
|
||||
metas={metas}
|
||||
currentMeta={currentMeta}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onPinboardClick={onSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MenuItem, MuiClickAwayListener, PureMenu } from '@affine/component';
|
||||
import { CopyLink, MoveToTrash } from '@affine/component/page-list';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
MoreVerticalIcon,
|
||||
@@ -13,7 +14,6 @@ import { useMemo, useRef, useState } from 'react';
|
||||
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import type { BlockSuiteWorkspace } from '../../../../shared';
|
||||
import { toast } from '../../../../utils';
|
||||
import { CopyLink, MoveToTrash } from '../../operation-menu-items';
|
||||
import { PinboardMenu } from '../pinboard-menu/';
|
||||
import { StyledOperationButton } from '../styles';
|
||||
|
||||
@@ -152,7 +152,7 @@ export const OperationButton = ({
|
||||
/>
|
||||
<MoveToTrash.ConfirmModal
|
||||
open={confirmModalOpen}
|
||||
meta={currentMeta}
|
||||
title={currentMeta.title}
|
||||
onConfirm={() => {
|
||||
toast(t['Moved to Trash']());
|
||||
removeToTrash(currentMeta.id);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const pageListEmptyStyle = style({
|
||||
height: 'calc(100% - 52px)',
|
||||
});
|
||||
@@ -1,35 +1,171 @@
|
||||
import { Empty } from '@affine/component';
|
||||
import type { ListData, TrashListData } from '@affine/component/page-list';
|
||||
import { PageList, PageListTrashView } from '@affine/component/page-list';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import dayjs from 'dayjs';
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { workspacePreferredModeAtom } from '../../../atoms';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import PageList from './page-list';
|
||||
import { toast } from '../../../utils';
|
||||
import { pageListEmptyStyle } from './index.css';
|
||||
|
||||
export type BlockSuitePageListProps = {
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
listType: 'all' | 'trash' | 'favorite' | 'shared' | 'public';
|
||||
isPublic?: true;
|
||||
onOpenPage: (pageId: string, newTab?: boolean) => void;
|
||||
};
|
||||
|
||||
const filter = {
|
||||
all: (pageMeta: PageMeta) => !pageMeta.trash,
|
||||
public: (pageMeta: PageMeta) => !pageMeta.trash,
|
||||
trash: (pageMeta: PageMeta, allMetas: PageMeta[]) => {
|
||||
const parentMeta = allMetas.find(m => m.subpageIds?.includes(pageMeta.id));
|
||||
return !parentMeta?.trash && pageMeta.trash;
|
||||
},
|
||||
favorite: (pageMeta: PageMeta) => pageMeta.favorite && !pageMeta.trash,
|
||||
shared: (pageMeta: PageMeta) => pageMeta.isPublic && !pageMeta.trash,
|
||||
};
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
const formatDate = (date?: number | unknown) => {
|
||||
const dateStr =
|
||||
typeof date === 'number' ? dayjs(date).format('YYYY-MM-DD HH:mm') : '--';
|
||||
return dateStr;
|
||||
};
|
||||
|
||||
const PageListEmpty = (props: {
|
||||
listType: BlockSuitePageListProps['listType'];
|
||||
}) => {
|
||||
const { listType } = props;
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const getEmptyDescription = () => {
|
||||
if (listType === 'all') {
|
||||
return t['emptyAllPages']();
|
||||
}
|
||||
if (listType === 'favorite') {
|
||||
return t['emptyFavorite']();
|
||||
}
|
||||
if (listType === 'trash') {
|
||||
return t['emptyTrash']();
|
||||
}
|
||||
if (listType === 'shared') {
|
||||
return t['emptySharedPages']();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={pageListEmptyStyle}>
|
||||
<Empty description={getEmptyDescription()} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const BlockSuitePageList: React.FC<BlockSuitePageListProps> = ({
|
||||
blockSuiteWorkspace,
|
||||
onOpenPage,
|
||||
listType,
|
||||
isPublic = false,
|
||||
}) => {
|
||||
return (
|
||||
<PageList
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={onOpenPage}
|
||||
listType="all"
|
||||
/>
|
||||
const pageMetas = useBlockSuitePageMeta(blockSuiteWorkspace);
|
||||
const {
|
||||
toggleFavorite,
|
||||
removeToTrash,
|
||||
restoreFromTrash,
|
||||
permanentlyDeletePage,
|
||||
cancelPublicPage,
|
||||
} = useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const t = useAFFiNEI18N();
|
||||
const list = useMemo(
|
||||
() => pageMetas.filter(pageMeta => filter[listType](pageMeta, pageMetas)),
|
||||
[pageMetas, listType]
|
||||
);
|
||||
};
|
||||
const record = useAtomValue(workspacePreferredModeAtom);
|
||||
if (list.length === 0) {
|
||||
return <PageListEmpty listType={listType} />;
|
||||
}
|
||||
|
||||
if (listType === 'trash') {
|
||||
const pageList: TrashListData[] = list.map(pageMeta => {
|
||||
return {
|
||||
icon:
|
||||
record[pageMeta.id] === 'edgeless' ? <EdgelessIcon /> : <PageIcon />,
|
||||
pageId: pageMeta.id,
|
||||
title: pageMeta.title,
|
||||
createDate: formatDate(pageMeta.createDate),
|
||||
updatedDate: formatDate(pageMeta.updatedDate),
|
||||
onClickPage: () => onOpenPage(pageMeta.id),
|
||||
onClickRestore: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
},
|
||||
onRestorePage: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
toast(t['restored']({ title: pageMeta.title || 'Untitled' }));
|
||||
},
|
||||
onPermanentlyDeletePage: () => {
|
||||
permanentlyDeletePage(pageMeta.id);
|
||||
toast(t['Permanently deleted']());
|
||||
},
|
||||
};
|
||||
});
|
||||
return <PageListTrashView list={pageList} />;
|
||||
}
|
||||
|
||||
const pageList: ListData[] = list.map(pageMeta => {
|
||||
return {
|
||||
icon:
|
||||
record[pageMeta.id] === 'edgeless' ? <EdgelessIcon /> : <PageIcon />,
|
||||
pageId: pageMeta.id,
|
||||
title: pageMeta.title,
|
||||
favorite: !!pageMeta.favorite,
|
||||
isPublicPage: !!pageMeta.isPublic,
|
||||
createDate: formatDate(pageMeta.createDate),
|
||||
updatedDate: formatDate(pageMeta.updatedDate),
|
||||
onClickPage: () => onOpenPage(pageMeta.id),
|
||||
onOpenPageInNewTab: () => onOpenPage(pageMeta.id, true),
|
||||
onClickRestore: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
},
|
||||
removeToTrash: () => {
|
||||
removeToTrash(pageMeta.id);
|
||||
toast(t['Successfully deleted']());
|
||||
},
|
||||
onRestorePage: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
toast(t['restored']({ title: pageMeta.title || 'Untitled' }));
|
||||
},
|
||||
bookmarkPage: () => {
|
||||
toggleFavorite(pageMeta.id);
|
||||
toast(
|
||||
pageMeta.favorite
|
||||
? t['Removed from Favorites']()
|
||||
: t['Added to Favorites']()
|
||||
);
|
||||
},
|
||||
onDisablePublicSharing: () => {
|
||||
cancelPublicPage(pageMeta.id);
|
||||
toast('Successfully disabled', {
|
||||
portal: document.body,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const BlockSuitePublicPageList: React.FC<BlockSuitePageListProps> = ({
|
||||
blockSuiteWorkspace,
|
||||
onOpenPage,
|
||||
}) => {
|
||||
return (
|
||||
<PageList
|
||||
isPublic={true}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={onOpenPage}
|
||||
isPublicWorkspace={isPublic}
|
||||
list={pageList}
|
||||
listType={listType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { TableCellProps } from '@affine/component';
|
||||
import { TableCell } from '@affine/component';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import dayjs from 'dayjs';
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import React from 'react';
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
export const DateCell = ({
|
||||
pageMeta,
|
||||
dateKey,
|
||||
backupKey = 'updatedDate',
|
||||
...props
|
||||
}: {
|
||||
pageMeta: PageMeta;
|
||||
dateKey: keyof PageMeta;
|
||||
backupKey?: keyof PageMeta;
|
||||
} & Omit<TableCellProps, 'children'>) => {
|
||||
const value = pageMeta[dateKey] ?? pageMeta[backupKey];
|
||||
return (
|
||||
<TableCell ellipsis={true} {...props}>
|
||||
{typeof value === 'number'
|
||||
? dayjs(value).format('YYYY-MM-DD HH:mm')
|
||||
: '--'}
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateCell;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Empty } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import React from 'react';
|
||||
export const PageListEmpty = (props: { listType?: string }) => {
|
||||
const { listType } = props;
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const getEmptyDescription = () => {
|
||||
if (listType === 'all') {
|
||||
return t['emptyAllPages']();
|
||||
}
|
||||
if (listType === 'favorite') {
|
||||
return t['emptyFavorite']();
|
||||
}
|
||||
if (listType === 'trash') {
|
||||
return t['emptyTrash']();
|
||||
}
|
||||
if (listType === 'shared') {
|
||||
return t['emptySharedPages']();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: 'calc(100% - 52px)' }}>
|
||||
<Empty description={getEmptyDescription()} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageListEmpty;
|
||||
@@ -1,252 +0,0 @@
|
||||
import {
|
||||
Content,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
} from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
EdgelessIcon,
|
||||
FavoritedIcon,
|
||||
FavoriteIcon,
|
||||
PageIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import { useMediaQuery, useTheme } from '@mui/material';
|
||||
import {
|
||||
useBlockSuitePageMeta,
|
||||
usePageMetaHelper,
|
||||
} from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { workspacePreferredModeAtom } from '../../../../atoms';
|
||||
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import type { BlockSuiteWorkspace } from '../../../../shared';
|
||||
import { toast } from '../../../../utils';
|
||||
import DateCell from './DateCell';
|
||||
import Empty from './Empty';
|
||||
import { OperationCell, TrashOperationCell } from './OperationCell';
|
||||
import {
|
||||
StyledTableContainer,
|
||||
StyledTableRow,
|
||||
StyledTitleLink,
|
||||
StyledTitleWrapper,
|
||||
} from './styles';
|
||||
|
||||
export type FavoriteTagProps = {
|
||||
pageMeta: PageMeta;
|
||||
onClick: () => void;
|
||||
};
|
||||
const FavoriteTag: React.FC<FavoriteTagProps> = ({
|
||||
pageMeta: { favorite },
|
||||
onClick,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<Tooltip
|
||||
content={favorite ? t['Favorited']() : t['Favorite']()}
|
||||
placement="top-start"
|
||||
>
|
||||
<IconButton
|
||||
iconSize={[20, 20]}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onClick();
|
||||
toast(
|
||||
favorite ? t['Removed from Favorites']() : t['Added to Favorites']()
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
color: favorite
|
||||
? 'var(--affine-primary-color)'
|
||||
: 'var(--affine-icon-color)',
|
||||
}}
|
||||
className={favorite ? '' : 'favorite-button'}
|
||||
>
|
||||
{favorite ? (
|
||||
<FavoritedIcon data-testid="favorited-icon" />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
type PageListProps = {
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
isPublic?: boolean;
|
||||
listType?: 'all' | 'trash' | 'favorite' | 'shared';
|
||||
onClickPage: (pageId: string, newTab?: boolean) => void;
|
||||
};
|
||||
|
||||
const filter = {
|
||||
all: (pageMeta: PageMeta) => !pageMeta.trash,
|
||||
trash: (pageMeta: PageMeta, allMetas: PageMeta[]) => {
|
||||
const parentMeta = allMetas.find(m => m.subpageIds?.includes(pageMeta.id));
|
||||
return !parentMeta?.trash && pageMeta.trash;
|
||||
},
|
||||
favorite: (pageMeta: PageMeta) => pageMeta.favorite && !pageMeta.trash,
|
||||
shared: (pageMeta: PageMeta) => pageMeta.isPublic && !pageMeta.trash,
|
||||
};
|
||||
|
||||
export const PageList: React.FC<PageListProps> = ({
|
||||
blockSuiteWorkspace,
|
||||
isPublic = false,
|
||||
listType,
|
||||
onClickPage,
|
||||
}) => {
|
||||
const pageList = useBlockSuitePageMeta(blockSuiteWorkspace);
|
||||
const helper = usePageMetaHelper(blockSuiteWorkspace);
|
||||
const { removeToTrash, restoreFromTrash } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const t = useAFFiNEI18N();
|
||||
const theme = useTheme();
|
||||
const matches = useMediaQuery(theme.breakpoints.up('sm'));
|
||||
const isTrash = listType === 'trash';
|
||||
const isShared = listType === 'shared';
|
||||
const record = useAtomValue(workspacePreferredModeAtom);
|
||||
const list = useMemo(
|
||||
() =>
|
||||
pageList.filter(pageMeta =>
|
||||
filter[listType ?? 'all'](pageMeta, pageList)
|
||||
),
|
||||
[pageList, listType]
|
||||
);
|
||||
if (list.length === 0) {
|
||||
return <Empty listType={listType} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{matches && (
|
||||
<>
|
||||
<TableCell proportion={0.5}>{t['Title']()}</TableCell>
|
||||
<TableCell proportion={0.2}>{t['Created']()}</TableCell>
|
||||
<TableCell proportion={0.2}>
|
||||
{isTrash
|
||||
? t['Moved to Trash']()
|
||||
: isShared
|
||||
? 'Shared'
|
||||
: t['Updated']()}
|
||||
</TableCell>
|
||||
<TableCell proportion={0.1}></TableCell>
|
||||
</>
|
||||
)}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{list.map((pageMeta, index) => {
|
||||
return (
|
||||
<StyledTableRow
|
||||
data-testid={`page-list-item-${pageMeta.id}`}
|
||||
key={`${pageMeta.id}-${index}`}
|
||||
>
|
||||
<TableCell
|
||||
onClick={() => {
|
||||
onClickPage(pageMeta.id);
|
||||
}}
|
||||
>
|
||||
<StyledTitleWrapper>
|
||||
<StyledTitleLink>
|
||||
{record[pageMeta.id] === 'edgeless' ? (
|
||||
<EdgelessIcon />
|
||||
) : (
|
||||
<PageIcon />
|
||||
)}
|
||||
<Content ellipsis={true} color="inherit">
|
||||
{pageMeta.title || t['Untitled']()}
|
||||
</Content>
|
||||
</StyledTitleLink>
|
||||
{listType && !isTrash && (
|
||||
<FavoriteTag
|
||||
onClick={() => {
|
||||
helper.setPageMeta(pageMeta.id, {
|
||||
favorite: !pageMeta.favorite,
|
||||
});
|
||||
}}
|
||||
pageMeta={pageMeta}
|
||||
/>
|
||||
)}
|
||||
</StyledTitleWrapper>
|
||||
</TableCell>
|
||||
{matches && (
|
||||
<>
|
||||
<DateCell
|
||||
pageMeta={pageMeta}
|
||||
dateKey="createDate"
|
||||
onClick={() => {
|
||||
onClickPage(pageMeta.id);
|
||||
}}
|
||||
/>
|
||||
<DateCell
|
||||
pageMeta={pageMeta}
|
||||
dateKey={isTrash ? 'trashDate' : 'updatedDate'}
|
||||
backupKey={isTrash ? 'trashDate' : 'createDate'}
|
||||
onClick={() => {
|
||||
onClickPage(pageMeta.id);
|
||||
}}
|
||||
/>
|
||||
{!isPublic && (
|
||||
<TableCell
|
||||
style={{ padding: 0 }}
|
||||
data-testid={`more-actions-${pageMeta.id}`}
|
||||
>
|
||||
{isTrash ? (
|
||||
<TrashOperationCell
|
||||
pageMeta={pageMeta}
|
||||
onPermanentlyDeletePage={pageId => {
|
||||
blockSuiteWorkspace.removePage(pageId);
|
||||
}}
|
||||
onRestorePage={() => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
}}
|
||||
onOpenPage={pageId => {
|
||||
onClickPage(pageId, false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<OperationCell
|
||||
pageMeta={pageMeta}
|
||||
metas={pageList}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onOpenPageInNewTab={pageId => {
|
||||
onClickPage(pageId, true);
|
||||
}}
|
||||
onToggleFavoritePage={(pageId: string) => {
|
||||
helper.setPageMeta(pageId, {
|
||||
favorite: !pageMeta.favorite,
|
||||
});
|
||||
}}
|
||||
onToggleTrashPage={(pageId, isTrash) => {
|
||||
if (isTrash) {
|
||||
removeToTrash(pageId);
|
||||
} else {
|
||||
restoreFromTrash(pageId);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StyledTableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageList;
|
||||
@@ -21,7 +21,7 @@ export const StyledEditorModeSwitch = styled('div')<{
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
background: 'var(--affine-background-primary-color)',
|
||||
boxShadow: 'var(--affine-shadow)',
|
||||
boxShadow: 'var(--affine-shadow-1)',
|
||||
borderRadius: '8px',
|
||||
zIndex: 1,
|
||||
position: 'absolute',
|
||||
@@ -40,7 +40,7 @@ export const StyledSwitchItem = styled('button')<{
|
||||
height: '24px',
|
||||
borderRadius: '8px',
|
||||
WebkitAppRegion: 'no-drag',
|
||||
boxShadow: active ? 'var(--affine-shadow)' : 'none',
|
||||
boxShadow: active ? 'var(--affine-shadow-1)' : 'none',
|
||||
color: active ? 'var(--affine-primary-color)' : 'var(--affine-icon-color)',
|
||||
display: hide ? 'none' : 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// fixme(himself65): refactor this file
|
||||
import { FlexWrapper, IconButton, Menu, MenuItem } from '@affine/component';
|
||||
import { Export, MoveToTrash } from '@affine/component/page-list';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
EdgelessIcon,
|
||||
@@ -22,7 +23,6 @@ import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suit
|
||||
import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id';
|
||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||
import { toast } from '../../../../utils';
|
||||
import { Export, MoveToTrash } from '../../../affine/operation-menu-items';
|
||||
import { MenuThemeModeSwitch } from '../header-right-items/theme-mode-switch';
|
||||
import {
|
||||
StyledHorizontalDivider,
|
||||
@@ -152,7 +152,7 @@ const PageMenu = () => {
|
||||
</Menu>
|
||||
<MoveToTrash.ConfirmModal
|
||||
open={openConfirm}
|
||||
meta={pageMeta}
|
||||
title={pageMeta.title}
|
||||
onConfirm={() => {
|
||||
removeToTrash(pageMeta.id);
|
||||
toast(t['Moved to Trash']());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DarkModeIcon, LightModeIcon } from '@blocksuite/icons';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import {
|
||||
StyledSwitchItem,
|
||||
@@ -13,11 +12,6 @@ import {
|
||||
|
||||
export const MenuThemeModeSwitch = () => {
|
||||
const { setTheme, resolvedTheme, theme } = useTheme();
|
||||
useEffect(() => {
|
||||
if (environment.isDesktop) {
|
||||
window.apis?.onThemeChange(resolvedTheme === 'dark' ? 'dark' : 'light');
|
||||
}
|
||||
}, [resolvedTheme]);
|
||||
return (
|
||||
<StyledThemeModeContainer>
|
||||
<StyledThemeModeSwitch data-testid="change-theme-container" inMenu={true}>
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { EditorContainer } from '@blocksuite/editor';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
|
||||
import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import Head from 'next/head';
|
||||
@@ -34,7 +35,7 @@ export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
|
||||
isPreview,
|
||||
}) => {
|
||||
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||
const page = blockSuiteWorkspace.getPage(pageId);
|
||||
const page = useBlockSuiteWorkspacePage(blockSuiteWorkspace, pageId);
|
||||
if (!page) {
|
||||
throw new PageNotFoundError(blockSuiteWorkspace, pageId);
|
||||
}
|
||||
@@ -63,7 +64,7 @@ export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
|
||||
style={{
|
||||
height: 'calc(100% - 52px)',
|
||||
}}
|
||||
key={`${workspace.flavour}-${workspace.id}-${[pageId]}`}
|
||||
key={`${workspace.flavour}-${workspace.id}-${pageId}`}
|
||||
mode={isPublic ? 'page' : currentMode}
|
||||
page={page}
|
||||
onInit={useCallback(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAtom } from 'jotai';
|
||||
import { lazy, Suspense, useState } from 'react';
|
||||
|
||||
import { openOnboardingModalAtom } from '../../../atoms';
|
||||
import { useCurrentMode } from '../../../hooks/current/use-current-mode';
|
||||
import { ShortcutsModal } from '../shortcuts-modal';
|
||||
import { ContactIcon, HelpIcon, KeyboardIcon } from './Icons';
|
||||
import {
|
||||
@@ -32,6 +33,7 @@ export const HelpIsland = ({
|
||||
}: {
|
||||
showList?: IslandItemNames[];
|
||||
}) => {
|
||||
const mode = useCurrentMode();
|
||||
const [, setOpenOnboarding] = useAtom(openOnboardingModalAtom);
|
||||
const [spread, setShowSpread] = useState(false);
|
||||
// const { triggerShortcutsModal, triggerContactModal } = useModal();
|
||||
@@ -62,6 +64,7 @@ export const HelpIsland = ({
|
||||
onClick={() => {
|
||||
setShowSpread(!spread);
|
||||
}}
|
||||
inEdgelessPage={mode === 'edgeless'}
|
||||
>
|
||||
<StyledAnimateWrapper
|
||||
style={{ height: spread ? `${showList.length * 44}px` : 0 }}
|
||||
|
||||
@@ -2,12 +2,17 @@ import { displayFlex, positionAbsolute, styled } from '@affine/component';
|
||||
|
||||
export const StyledIsland = styled('div')<{
|
||||
spread: boolean;
|
||||
}>(({ spread }) => {
|
||||
inEdgelessPage?: boolean;
|
||||
}>(({ spread, inEdgelessPage }) => {
|
||||
return {
|
||||
transition: 'box-shadow 0.2s',
|
||||
width: '44px',
|
||||
position: 'relative',
|
||||
boxShadow: spread ? 'var(--affine-menu-shadow)' : 'unset',
|
||||
boxShadow: spread
|
||||
? 'var(--affine-menu-shadow)'
|
||||
: inEdgelessPage
|
||||
? 'var(--affine-menu-shadow)'
|
||||
: 'unset',
|
||||
padding: '0 4px 44px',
|
||||
borderRadius: '10px',
|
||||
background: spread
|
||||
@@ -15,6 +20,7 @@ export const StyledIsland = styled('div')<{
|
||||
: 'var(--affine-background-primary-color)',
|
||||
':hover': {
|
||||
background: spread ? null : 'var(--affine-white)',
|
||||
boxShadow: spread ? null : 'var(--affine-menu-shadow)',
|
||||
},
|
||||
'::after': {
|
||||
content: '""',
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { Footer } from './Footer';
|
||||
import { NavigationPath } from './navigation-path';
|
||||
import { PublishedResults } from './PublishedResults';
|
||||
import { Results } from './Results';
|
||||
import { SearchInput } from './SearchInput';
|
||||
@@ -110,12 +109,12 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<NavigationPath
|
||||
{/* <NavigationPath
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onJumpToPage={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
<Command
|
||||
shouldFilter={false}
|
||||
//Handle KeyboardEvent conflicts with blocksuite
|
||||
|
||||
@@ -47,7 +47,7 @@ export const StyledCreateWorkspaceCard = styled('div')(() => {
|
||||
height: '124px',
|
||||
cursor: 'pointer',
|
||||
padding: '16px',
|
||||
boxShadow: 'var(--affine-shadow)',
|
||||
boxShadow: 'var(--affine-shadow-1)',
|
||||
borderRadius: '12px',
|
||||
transition: 'all .1s',
|
||||
background: 'var(--affine-white-80)',
|
||||
|
||||
@@ -15,6 +15,32 @@ export function useBlockSuiteMetaHelper(
|
||||
useReferenceLinkHelper(blockSuiteWorkspace);
|
||||
const metas = useBlockSuitePageMeta(blockSuiteWorkspace);
|
||||
|
||||
const addToFavorite = useCallback(
|
||||
(pageId: string) => {
|
||||
setPageMeta(pageId, {
|
||||
favorite: true,
|
||||
});
|
||||
},
|
||||
[setPageMeta]
|
||||
);
|
||||
const removeFromFavorite = useCallback(
|
||||
(pageId: string) => {
|
||||
setPageMeta(pageId, {
|
||||
favorite: false,
|
||||
});
|
||||
},
|
||||
[setPageMeta]
|
||||
);
|
||||
const toggleFavorite = useCallback(
|
||||
(pageId: string) => {
|
||||
const { favorite } = getPageMeta(pageId) ?? {};
|
||||
setPageMeta(pageId, {
|
||||
favorite: !favorite,
|
||||
});
|
||||
},
|
||||
[getPageMeta, setPageMeta]
|
||||
);
|
||||
|
||||
const removeToTrash = useCallback(
|
||||
(pageId: string, isRoot = true) => {
|
||||
const parentMeta = metas.find(m => m.subpageIds?.includes(pageId));
|
||||
@@ -58,8 +84,47 @@ export function useBlockSuiteMetaHelper(
|
||||
[addReferenceLink, getPageMeta, setPageMeta]
|
||||
);
|
||||
|
||||
const permanentlyDeletePage = useCallback(
|
||||
(pageId: string) => {
|
||||
blockSuiteWorkspace.removePage(pageId);
|
||||
},
|
||||
[blockSuiteWorkspace]
|
||||
);
|
||||
|
||||
/**
|
||||
* see {@link useBlockSuiteWorkspacePageIsPublic}
|
||||
*/
|
||||
const publicPage = useCallback(
|
||||
(pageId: string) => {
|
||||
setPageMeta(pageId, {
|
||||
isPublic: true,
|
||||
});
|
||||
},
|
||||
[setPageMeta]
|
||||
);
|
||||
|
||||
/**
|
||||
* see {@link useBlockSuiteWorkspacePageIsPublic}
|
||||
*/
|
||||
const cancelPublicPage = useCallback(
|
||||
(pageId: string) => {
|
||||
setPageMeta(pageId, {
|
||||
isPublic: false,
|
||||
});
|
||||
},
|
||||
[setPageMeta]
|
||||
);
|
||||
|
||||
return {
|
||||
publicPage,
|
||||
cancelPublicPage,
|
||||
|
||||
addToFavorite,
|
||||
removeFromFavorite,
|
||||
toggleFavorite,
|
||||
|
||||
removeToTrash,
|
||||
restoreFromTrash,
|
||||
permanentlyDeletePage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ export function useSyncRouterWithCurrentPageId(router: NextRouter) {
|
||||
if (typeof pageId === 'string') {
|
||||
console.log('set page id', pageId);
|
||||
setCurrentPageId(pageId);
|
||||
} else if (pageId === undefined) {
|
||||
console.log('cleanup page');
|
||||
setCurrentPageId(null);
|
||||
}
|
||||
}, [router.isReady, router.query.pageId, setCurrentPageId]);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Typography } from '@mui/material';
|
||||
import type React from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import PageList from '../../components/blocksuite/block-suite-page-list/page-list';
|
||||
import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
|
||||
import { StyledPage, StyledWrapper } from '../../layouts/styles';
|
||||
import { toast } from '../../utils';
|
||||
|
||||
@@ -57,9 +57,10 @@ const BroadcastPage: React.FC = () => {
|
||||
>
|
||||
Create Page
|
||||
</Button>
|
||||
<PageList
|
||||
<BlockSuitePageList
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={() => {
|
||||
listType="all"
|
||||
onOpenPage={() => {
|
||||
toast('do nothing');
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Breadcrumbs, IconButton, ListSkeleton } from '@affine/component';
|
||||
import { StyledTableContainer } from '@affine/component/page-list';
|
||||
import { SearchIcon } from '@blocksuite/icons';
|
||||
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
|
||||
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
publicWorkspaceIdAtom,
|
||||
} from '../../atoms/public-workspace';
|
||||
import { QueryParamError } from '../../components/affine/affine-error-eoundary';
|
||||
import { StyledTableContainer } from '../../components/blocksuite/block-suite-page-list/page-list/styles';
|
||||
import { WorkspaceAvatar } from '../../components/pure/footer';
|
||||
import { PageLoading } from '../../components/pure/loading';
|
||||
import {
|
||||
@@ -23,9 +23,9 @@ import {
|
||||
import type { NextPageWithLayout } from '../../shared';
|
||||
import { NavContainer, StyledBreadcrumbs } from './[workspaceId]/[pageId]';
|
||||
|
||||
const BlockSuitePublicPageList = lazy(() =>
|
||||
const BlockSuitePageList = lazy(() =>
|
||||
import('../../components/blocksuite/block-suite-page-list').then(module => ({
|
||||
default: module.BlockSuitePublicPageList,
|
||||
default: module.BlockSuitePageList,
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -79,7 +79,9 @@ const ListPageInner: React.FC<{
|
||||
</StyledTableContainer>
|
||||
}
|
||||
>
|
||||
<BlockSuitePublicPageList
|
||||
<BlockSuitePageList
|
||||
listType="public"
|
||||
isPublic={true}
|
||||
onOpenPage={handleClickPage}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import PageList from '../../../components/blocksuite/block-suite-page-list/page-list';
|
||||
import { BlockSuitePageList } from '../../../components/blocksuite/block-suite-page-list';
|
||||
import { PageLoading } from '../../../components/pure/loading';
|
||||
import { WorkspaceTitle } from '../../../components/pure/workspace-title';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
@@ -50,9 +50,9 @@ const FavouritePage: NextPageWithLayout = () => {
|
||||
>
|
||||
{t['Favorites']()}
|
||||
</WorkspaceTitle>
|
||||
<PageList
|
||||
<BlockSuitePageList
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={onClickPage}
|
||||
onOpenPage={onClickPage}
|
||||
listType="favorite"
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import PageList from '../../../components/blocksuite/block-suite-page-list/page-list';
|
||||
import { BlockSuitePageList } from '../../../components/blocksuite/block-suite-page-list';
|
||||
import { PageLoading } from '../../../components/pure/loading';
|
||||
import { WorkspaceTitle } from '../../../components/pure/workspace-title';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
@@ -50,9 +50,9 @@ const SharedPages: NextPageWithLayout = () => {
|
||||
>
|
||||
{t['Shared Pages']()}
|
||||
</WorkspaceTitle>
|
||||
<PageList
|
||||
<BlockSuitePageList
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={onClickPage}
|
||||
onOpenPage={onClickPage}
|
||||
listType="shared"
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import PageList from '../../../components/blocksuite/block-suite-page-list/page-list';
|
||||
import { BlockSuitePageList } from '../../../components/blocksuite/block-suite-page-list';
|
||||
import { PageLoading } from '../../../components/pure/loading';
|
||||
import { WorkspaceTitle } from '../../../components/pure/workspace-title';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
@@ -53,9 +53,9 @@ const TrashPage: NextPageWithLayout = () => {
|
||||
>
|
||||
{t['Trash']()}
|
||||
</WorkspaceTitle>
|
||||
<PageList
|
||||
<BlockSuitePageList
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
onClickPage={onClickPage}
|
||||
onOpenPage={onClickPage}
|
||||
listType="trash"
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -45,7 +45,7 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const getPersistenceAllWorkspace = () => {
|
||||
const items = storage.getItem(AFFINE_STORAGE_KEY);
|
||||
const items = storage.getItem(AFFINE_STORAGE_KEY, []);
|
||||
const allWorkspaces: AffineWorkspace[] = [];
|
||||
if (
|
||||
Array.isArray(items) &&
|
||||
@@ -152,7 +152,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
||||
return id;
|
||||
},
|
||||
delete: async workspace => {
|
||||
const items = storage.getItem(AFFINE_STORAGE_KEY);
|
||||
const items = storage.getItem(AFFINE_STORAGE_KEY, []);
|
||||
if (
|
||||
Array.isArray(items) &&
|
||||
items.every(item => schema.safeParse(item).success)
|
||||
@@ -226,7 +226,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
||||
permission: workspace.permission,
|
||||
} satisfies z.infer<typeof schema>;
|
||||
});
|
||||
const old = storage.getItem(AFFINE_STORAGE_KEY);
|
||||
const old = storage.getItem(AFFINE_STORAGE_KEY, []);
|
||||
if (
|
||||
Array.isArray(old) &&
|
||||
old.every(item => schema.safeParse(item).success)
|
||||
@@ -304,6 +304,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
||||
PageList: ({ blockSuiteWorkspace, onOpenPage }) => {
|
||||
return (
|
||||
<BlockSuitePageList
|
||||
listType="all"
|
||||
onOpenPage={onOpenPage}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
/>
|
||||
|
||||
@@ -78,6 +78,7 @@ export const LocalPlugin: WorkspacePlugin<WorkspaceFlavour.LOCAL> = {
|
||||
PageList: ({ blockSuiteWorkspace, onOpenPage }) => {
|
||||
return (
|
||||
<BlockSuitePageList
|
||||
listType="all"
|
||||
onOpenPage={onOpenPage}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import type { ThemeProviderProps } from '@affine/component';
|
||||
import { ThemeProvider as NextThemeProvider } from 'next-themes';
|
||||
import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import type React from 'react';
|
||||
import { memo, useRef } from 'react';
|
||||
|
||||
const themes = ['dark', 'light'];
|
||||
|
||||
const DesktopThemeSync = memo(function DesktopThemeSync() {
|
||||
const { theme } = useTheme();
|
||||
const lastThemeRef = useRef(theme);
|
||||
if (lastThemeRef.current !== theme) {
|
||||
if (environment.isDesktop && theme) {
|
||||
window.apis?.onThemeChange(theme);
|
||||
}
|
||||
lastThemeRef.current = theme;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
export const ThemeProvider = ({
|
||||
children,
|
||||
...props
|
||||
@@ -12,6 +25,7 @@ export const ThemeProvider = ({
|
||||
return (
|
||||
<NextThemeProvider themes={themes} enableSystem={true} {...props}>
|
||||
{children}
|
||||
<DesktopThemeSync />
|
||||
</NextThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "AFFiNE",
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"private": true,
|
||||
"author": "toeverything",
|
||||
"license": "MPL-2.0",
|
||||
@@ -49,14 +49,14 @@
|
||||
"@playwright/test": "^1.33.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@types/eslint": "^8.37.0",
|
||||
"@types/node": "^18.16.3",
|
||||
"@types/node": "^18.16.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/parser": "^5.59.2",
|
||||
"@vanilla-extract/vite-plugin": "^3.8.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"@vitest/coverage-istanbul": "^0.31.0",
|
||||
"@vitest/ui": "^0.31.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
@@ -77,7 +77,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"serve": "^14.2.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "^4.3.4",
|
||||
"vite": "^4.3.5",
|
||||
"vite-plugin-istanbul": "^4.0.1",
|
||||
"vite-tsconfig-paths": "^4.2.0",
|
||||
"vitest": "^0.31.0",
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.3"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@toeverything/theme": "workspace:*",
|
||||
"@vanilla-extract/dynamic": "^2.0.3",
|
||||
"clsx": "^1.2.1",
|
||||
"jotai": "^2.0.4",
|
||||
"jotai": "^2.1.0",
|
||||
"lit": "^2.7.4",
|
||||
"lottie-web": "^5.11.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -48,38 +48,38 @@
|
||||
"react-is": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blocksuite/blocks": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/blocks": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/icons": "^2.1.15",
|
||||
"@blocksuite/store": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@storybook/addon-actions": "^7.0.8",
|
||||
"@blocksuite/store": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@storybook/addon-actions": "^7.0.9",
|
||||
"@storybook/addon-coverage": "^0.0.8",
|
||||
"@storybook/addon-essentials": "^7.0.8",
|
||||
"@storybook/addon-interactions": "^7.0.8",
|
||||
"@storybook/addon-links": "^7.0.8",
|
||||
"@storybook/addon-storysource": "^7.0.8",
|
||||
"@storybook/blocks": "^7.0.8",
|
||||
"@storybook/builder-vite": "^7.0.8",
|
||||
"@storybook/addon-essentials": "^7.0.9",
|
||||
"@storybook/addon-interactions": "^7.0.9",
|
||||
"@storybook/addon-links": "^7.0.9",
|
||||
"@storybook/addon-storysource": "^7.0.9",
|
||||
"@storybook/blocks": "^7.0.9",
|
||||
"@storybook/builder-vite": "^7.0.9",
|
||||
"@storybook/jest": "^0.1.0",
|
||||
"@storybook/react": "^7.0.8",
|
||||
"@storybook/react-vite": "^7.0.8",
|
||||
"@storybook/react": "^7.0.9",
|
||||
"@storybook/react-vite": "^7.0.9",
|
||||
"@storybook/test-runner": "^0.10.0",
|
||||
"@storybook/testing-library": "^0.1.0",
|
||||
"@types/react": "^18.2.5",
|
||||
"@types/react-dnd": "^3.0.2",
|
||||
"@types/react-dom": "18.2.3",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"@vanilla-extract/css": "^1.11.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"concurrently": "^8.0.1",
|
||||
"jest-mock": "^29.5.0",
|
||||
"serve": "^14.2.0",
|
||||
"storybook": "^7.0.8",
|
||||
"storybook": "^7.0.9",
|
||||
"storybook-dark-mode": "^3.0.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "^4.3.4",
|
||||
"vite": "^4.3.5",
|
||||
"wait-on": "^7.0.1",
|
||||
"yjs": "^13.6.1"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
355
packages/component/src/components/page-list/all-page.tsx
Normal file
355
packages/component/src/components/page-list/all-page.tsx
Normal file
@@ -0,0 +1,355 @@
|
||||
import type { IconButtonProps, TableCellProps } from '@affine/component';
|
||||
import {
|
||||
Content,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
} from '@affine/component';
|
||||
import { OperationCell, TrashOperationCell } from '@affine/component/page-list';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons';
|
||||
import { useMediaQuery, useTheme } from '@mui/material';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import {
|
||||
StyledTableContainer,
|
||||
StyledTableRow,
|
||||
StyledTitleLink,
|
||||
StyledTitleWrapper,
|
||||
} from './styles';
|
||||
|
||||
export type FavoriteTagProps = {
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const FavoriteTag = forwardRef<
|
||||
HTMLButtonElement,
|
||||
FavoriteTagProps & Omit<IconButtonProps, 'children'>
|
||||
>(({ active, onClick, ...props }, ref) => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<Tooltip
|
||||
content={active ? t['Favorited']() : t['Favorite']()}
|
||||
placement="top-start"
|
||||
>
|
||||
<IconButton
|
||||
ref={ref}
|
||||
iconSize={[20, 20]}
|
||||
style={{
|
||||
color: active
|
||||
? 'var(--affine-primary-color)'
|
||||
: 'var(--affine-icon-color)',
|
||||
}}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onClick?.(e);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{active ? (
|
||||
<FavoritedIcon data-testid="favorited-icon" />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
export type PageListProps = {
|
||||
isPublicWorkspace?: boolean;
|
||||
list: ListData[];
|
||||
listType: 'all' | 'favorite' | 'shared' | 'public';
|
||||
onClickPage: (pageId: string, newTab?: boolean) => void;
|
||||
};
|
||||
|
||||
const TitleCell = ({
|
||||
icon,
|
||||
text,
|
||||
suffix,
|
||||
...props
|
||||
}: {
|
||||
icon: JSX.Element;
|
||||
text: string;
|
||||
suffix?: JSX.Element;
|
||||
} & TableCellProps) => {
|
||||
return (
|
||||
<TableCell {...props}>
|
||||
<StyledTitleWrapper>
|
||||
<StyledTitleLink>
|
||||
{icon}
|
||||
<Content ellipsis={true} color="inherit">
|
||||
{text}
|
||||
</Content>
|
||||
</StyledTitleLink>
|
||||
{suffix}
|
||||
</StyledTitleWrapper>
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
|
||||
export type ListData = {
|
||||
pageId: string;
|
||||
icon: JSX.Element;
|
||||
title: string;
|
||||
favorite: boolean;
|
||||
createDate: string;
|
||||
updatedDate?: string;
|
||||
trashDate?: string;
|
||||
isPublicPage: boolean;
|
||||
onClickPage: () => void;
|
||||
onOpenPageInNewTab: () => void;
|
||||
bookmarkPage: () => void;
|
||||
removeToTrash: () => void;
|
||||
onDisablePublicSharing: () => void;
|
||||
};
|
||||
|
||||
export const PageList: React.FC<PageListProps> = ({
|
||||
isPublicWorkspace = false,
|
||||
list,
|
||||
listType,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const isShared = listType === 'shared';
|
||||
|
||||
const theme = useTheme();
|
||||
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
if (isSmallDevices) {
|
||||
return <PageListMobileView list={list} />;
|
||||
}
|
||||
|
||||
const ListHead = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell proportion={0.5}>{t['Title']()}</TableCell>
|
||||
<TableCell proportion={0.2}>{t['Created']()}</TableCell>
|
||||
<TableCell proportion={0.2}>
|
||||
{isShared
|
||||
? // TODO add i18n
|
||||
'Shared'
|
||||
: t['Updated']()}
|
||||
</TableCell>
|
||||
<TableCell proportion={0.1}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
};
|
||||
|
||||
const ListItems = list.map(
|
||||
(
|
||||
{
|
||||
pageId,
|
||||
title,
|
||||
icon,
|
||||
isPublicPage,
|
||||
favorite,
|
||||
createDate,
|
||||
updatedDate,
|
||||
onClickPage,
|
||||
bookmarkPage,
|
||||
onOpenPageInNewTab,
|
||||
removeToTrash,
|
||||
onDisablePublicSharing,
|
||||
},
|
||||
index
|
||||
) => {
|
||||
return (
|
||||
<StyledTableRow
|
||||
data-testid={`page-list-item-${pageId}`}
|
||||
key={`${pageId}-${index}`}
|
||||
>
|
||||
<TitleCell
|
||||
icon={icon}
|
||||
text={title || t['Untitled']()}
|
||||
suffix={
|
||||
<FavoriteTag
|
||||
className={favorite ? '' : 'favorite-button'}
|
||||
onClick={bookmarkPage}
|
||||
active={!!favorite}
|
||||
/>
|
||||
}
|
||||
onClick={onClickPage}
|
||||
/>
|
||||
<TableCell ellipsis={true} onClick={onClickPage}>
|
||||
{createDate}
|
||||
</TableCell>
|
||||
<TableCell ellipsis={true} onClick={onClickPage}>
|
||||
{updatedDate ?? createDate}
|
||||
</TableCell>
|
||||
{!isPublicWorkspace && (
|
||||
<TableCell
|
||||
style={{ padding: 0 }}
|
||||
data-testid={`more-actions-${pageId}`}
|
||||
>
|
||||
<OperationCell
|
||||
title={title}
|
||||
favorite={favorite}
|
||||
isPublic={isPublicPage}
|
||||
onOpenPageInNewTab={onOpenPageInNewTab}
|
||||
onToggleFavoritePage={bookmarkPage}
|
||||
onRemoveToTrash={removeToTrash}
|
||||
onDisablePublicSharing={onDisablePublicSharing}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
</StyledTableRow>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<ListHead />
|
||||
<TableBody>{ListItems}</TableBody>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const TrashListHead = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell proportion={0.5}>{t['Title']()}</TableCell>
|
||||
<TableCell proportion={0.2}>{t['Created']()}</TableCell>
|
||||
<TableCell proportion={0.2}>{t['Moved to Trash']()}</TableCell>
|
||||
<TableCell proportion={0.1}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
};
|
||||
|
||||
export type TrashListData = {
|
||||
pageId: string;
|
||||
icon: JSX.Element;
|
||||
title: string;
|
||||
createDate: string;
|
||||
updatedDate?: string;
|
||||
trashDate?: string;
|
||||
// isPublic: boolean;
|
||||
onClickPage: () => void;
|
||||
onRestorePage: () => void;
|
||||
onPermanentlyDeletePage: () => void;
|
||||
};
|
||||
|
||||
export const PageListTrashView: React.FC<{
|
||||
list: TrashListData[];
|
||||
}> = ({ list }) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const theme = useTheme();
|
||||
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
if (isSmallDevices) {
|
||||
const mobileList = list.map(({ pageId, icon, title, onClickPage }) => ({
|
||||
title,
|
||||
icon,
|
||||
pageId,
|
||||
onClickPage,
|
||||
}));
|
||||
return <PageListMobileView list={mobileList} />;
|
||||
}
|
||||
const ListItems = list.map(
|
||||
(
|
||||
{
|
||||
pageId,
|
||||
title,
|
||||
icon,
|
||||
createDate,
|
||||
trashDate,
|
||||
onClickPage,
|
||||
onPermanentlyDeletePage,
|
||||
onRestorePage,
|
||||
},
|
||||
index
|
||||
) => {
|
||||
return (
|
||||
<StyledTableRow
|
||||
data-testid={`page-list-item-${pageId}`}
|
||||
key={`${pageId}-${index}`}
|
||||
>
|
||||
<TitleCell
|
||||
icon={icon}
|
||||
text={title || t['Untitled']()}
|
||||
onClick={onClickPage}
|
||||
/>
|
||||
<TableCell ellipsis={true} onClick={onClickPage}>
|
||||
{createDate}
|
||||
</TableCell>
|
||||
<TableCell ellipsis={true} onClick={onClickPage}>
|
||||
{trashDate}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
style={{ padding: 0 }}
|
||||
data-testid={`more-actions-${pageId}`}
|
||||
>
|
||||
<TrashOperationCell
|
||||
onPermanentlyDeletePage={onPermanentlyDeletePage}
|
||||
onRestorePage={onRestorePage}
|
||||
onOpenPage={onClickPage}
|
||||
/>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<TrashListHead />
|
||||
<TableBody>{ListItems}</TableBody>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const PageListMobileView: React.FC<{
|
||||
list: {
|
||||
pageId: string;
|
||||
title: string;
|
||||
icon: JSX.Element;
|
||||
onClickPage: () => void;
|
||||
}[];
|
||||
}> = ({ list }) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const ListItems = list.map(({ pageId, title, icon, onClickPage }, index) => {
|
||||
return (
|
||||
<StyledTableRow
|
||||
data-testid={`page-list-item-${pageId}`}
|
||||
key={`${pageId}-${index}`}
|
||||
>
|
||||
<TableCell onClick={onClickPage}>
|
||||
<StyledTitleWrapper>
|
||||
<StyledTitleLink>
|
||||
{icon}
|
||||
<Content ellipsis={true} color="inherit">
|
||||
{title || t['Untitled']()}
|
||||
</Content>
|
||||
</StyledTitleLink>
|
||||
</StyledTitleWrapper>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<TableBody>{ListItems}</TableBody>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageList;
|
||||
4
packages/component/src/components/page-list/index.tsx
Normal file
4
packages/component/src/components/page-list/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './all-page';
|
||||
export * from './operation-cell';
|
||||
export * from './operation-menu-items';
|
||||
export * from './styles';
|
||||
@@ -15,42 +15,34 @@ import {
|
||||
OpenInNewIcon,
|
||||
ResetIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import type React from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import type { BlockSuiteWorkspace } from '../../../../shared';
|
||||
import { toast } from '../../../../utils';
|
||||
import {
|
||||
DisablePublicSharing,
|
||||
MoveToTrash,
|
||||
} from '../../../affine/operation-menu-items';
|
||||
import { DisablePublicSharing, MoveToTrash } from './operation-menu-items';
|
||||
|
||||
export type OperationCellProps = {
|
||||
pageMeta: PageMeta;
|
||||
metas: PageMeta[];
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
onOpenPageInNewTab: (pageId: string) => void;
|
||||
onToggleFavoritePage: (pageId: string) => void;
|
||||
onToggleTrashPage: (pageId: string, isTrash: boolean) => void;
|
||||
title: string;
|
||||
favorite: boolean;
|
||||
isPublic: boolean;
|
||||
onOpenPageInNewTab: () => void;
|
||||
onToggleFavoritePage: () => void;
|
||||
onRemoveToTrash: () => void;
|
||||
onDisablePublicSharing: () => void;
|
||||
};
|
||||
|
||||
export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
pageMeta,
|
||||
blockSuiteWorkspace,
|
||||
title,
|
||||
favorite,
|
||||
isPublic,
|
||||
onOpenPageInNewTab,
|
||||
onToggleFavoritePage,
|
||||
onToggleTrashPage,
|
||||
onRemoveToTrash,
|
||||
onDisablePublicSharing,
|
||||
}) => {
|
||||
const { id, favorite, isPublic } = pageMeta;
|
||||
const t = useAFFiNEI18N();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openDisableShared, setOpenDisableShared] = useState(false);
|
||||
|
||||
const page = blockSuiteWorkspace.getPage(id);
|
||||
assertExists(page);
|
||||
|
||||
const OperationMenu = (
|
||||
<>
|
||||
{isPublic && (
|
||||
@@ -62,12 +54,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
onToggleFavoritePage(id);
|
||||
toast(
|
||||
favorite ? t['Removed from Favorites']() : t['Added to Favorites']()
|
||||
);
|
||||
}}
|
||||
onClick={onToggleFavoritePage}
|
||||
icon={
|
||||
favorite ? (
|
||||
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
|
||||
@@ -79,23 +66,16 @@ export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
{favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
|
||||
</MenuItem>
|
||||
{!environment.isDesktop && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
onOpenPageInNewTab(id);
|
||||
}}
|
||||
icon={<OpenInNewIcon />}
|
||||
>
|
||||
<MenuItem onClick={onOpenPageInNewTab} icon={<OpenInNewIcon />}>
|
||||
{t['Open in new tab']()}
|
||||
</MenuItem>
|
||||
)}
|
||||
{!pageMeta.isRootPinboard && (
|
||||
<MoveToTrash
|
||||
testId="move-to-trash"
|
||||
onItemClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<MoveToTrash
|
||||
testId="move-to-trash"
|
||||
onItemClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
@@ -114,10 +94,9 @@ export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
</FlexWrapper>
|
||||
<MoveToTrash.ConfirmModal
|
||||
open={open}
|
||||
meta={pageMeta}
|
||||
title={title}
|
||||
onConfirm={() => {
|
||||
onToggleTrashPage(id, true);
|
||||
toast(t['Moved to Trash']());
|
||||
onRemoveToTrash();
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => {
|
||||
@@ -128,7 +107,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
}}
|
||||
/>
|
||||
<DisablePublicSharing.DisablePublicSharingModal
|
||||
page={page}
|
||||
onConfirmDisable={onDisablePublicSharing}
|
||||
open={openDisableShared}
|
||||
onClose={() => {
|
||||
setOpenDisableShared(false);
|
||||
@@ -139,18 +118,15 @@ export const OperationCell: React.FC<OperationCellProps> = ({
|
||||
};
|
||||
|
||||
export type TrashOperationCellProps = {
|
||||
pageMeta: PageMeta;
|
||||
onPermanentlyDeletePage: (pageId: string) => void;
|
||||
onRestorePage: (pageId: string) => void;
|
||||
onOpenPage: (pageId: string) => void;
|
||||
onPermanentlyDeletePage: () => void;
|
||||
onRestorePage: () => void;
|
||||
onOpenPage: () => void;
|
||||
};
|
||||
|
||||
export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
|
||||
pageMeta,
|
||||
onPermanentlyDeletePage,
|
||||
onRestorePage,
|
||||
}) => {
|
||||
const { id, title } = pageMeta;
|
||||
const t = useAFFiNEI18N();
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
@@ -159,8 +135,7 @@ export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
|
||||
<IconButton
|
||||
style={{ marginRight: '12px' }}
|
||||
onClick={() => {
|
||||
onRestorePage(id);
|
||||
toast(t['restored']({ title: title || 'Untitled' }));
|
||||
onRestorePage();
|
||||
}}
|
||||
>
|
||||
<ResetIcon />
|
||||
@@ -182,8 +157,7 @@ export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
|
||||
confirmType="danger"
|
||||
open={open}
|
||||
onConfirm={() => {
|
||||
onPermanentlyDeletePage(id);
|
||||
toast(t['Permanently deleted']());
|
||||
onPermanentlyDeletePage();
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => {
|
||||
@@ -1,10 +1,8 @@
|
||||
import { MenuItem } from '@affine/component';
|
||||
import { MenuItem, toast } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CopyIcon } from '@blocksuite/icons';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
//
|
||||
import { toast } from '../../../utils';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
|
||||
export const CopyLink = ({ onItemClick, onSelect }: CommonMenuItemProps) => {
|
||||
@@ -1,5 +1,4 @@
|
||||
import { MenuItem, styled } from '@affine/component';
|
||||
import type { PublicLinkDisableProps } from '@affine/component/share-menu';
|
||||
import { PublicLinkDisableModal } from '@affine/component/share-menu';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ShareIcon } from '@blocksuite/icons';
|
||||
@@ -47,12 +46,4 @@ export const DisablePublicSharing = ({
|
||||
);
|
||||
};
|
||||
|
||||
const DisablePublicSharingModal = ({
|
||||
page,
|
||||
open,
|
||||
onClose,
|
||||
}: PublicLinkDisableProps) => {
|
||||
return <PublicLinkDisableModal page={page} open={open} onClose={onClose} />;
|
||||
};
|
||||
|
||||
DisablePublicSharing.DisablePublicSharingModal = DisablePublicSharingModal;
|
||||
DisablePublicSharing.DisablePublicSharingModal = PublicLinkDisableModal;
|
||||
@@ -2,7 +2,6 @@ import type { ConfirmProps } from '@affine/component';
|
||||
import { Confirm, MenuItem } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { DeleteTemporarilyIcon } from '@blocksuite/icons';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
|
||||
@@ -30,10 +29,10 @@ export const MoveToTrash = ({
|
||||
};
|
||||
|
||||
const ConfirmModal = ({
|
||||
meta,
|
||||
title,
|
||||
...confirmModalProps
|
||||
}: {
|
||||
meta: PageMeta;
|
||||
title: string;
|
||||
} & ConfirmProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
@@ -41,7 +40,7 @@ const ConfirmModal = ({
|
||||
<Confirm
|
||||
title={t['Delete page?']()}
|
||||
content={t['will be moved to Trash']({
|
||||
title: meta.title || 'Untitled',
|
||||
title: title || 'Untitled',
|
||||
})}
|
||||
confirmText={t.Delete()}
|
||||
confirmType="danger"
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './CopyLink';
|
||||
export * from './DisablePublicSharing';
|
||||
export * from './Export';
|
||||
export * from './MoveTo';
|
||||
// export * from './MoveTo';
|
||||
export * from './MoveToTrash';
|
||||
@@ -44,11 +44,11 @@ export const StyledTableRow = styled(TableRow)(() => {
|
||||
return {
|
||||
cursor: 'pointer',
|
||||
'.favorite-button': {
|
||||
display: 'none',
|
||||
visibility: 'hidden',
|
||||
},
|
||||
'&:hover': {
|
||||
'.favorite-button': {
|
||||
display: 'flex',
|
||||
visibility: 'visible',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -56,6 +56,12 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
|
||||
navigator.clipboard.writeText(sharingUrl);
|
||||
toast(t['Copied link to clipboard']());
|
||||
}, [sharingUrl, t]);
|
||||
const onDisablePublic = useCallback(() => {
|
||||
setIsPublic(false);
|
||||
toast('Successfully disabled', {
|
||||
portal: document.body,
|
||||
});
|
||||
}, [setIsPublic]);
|
||||
|
||||
return (
|
||||
<div className={menuItemStyle}>
|
||||
@@ -104,8 +110,8 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
|
||||
{t['Disable Public Link']()}
|
||||
</StyledDisableButton>
|
||||
<PublicLinkDisableModal
|
||||
page={props.currentPage}
|
||||
open={showDisable}
|
||||
onConfirmDisable={onDisablePublic}
|
||||
onClose={() => {
|
||||
setShowDisable(false);
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { Modal, ModalCloseButton, toast } from '../../..';
|
||||
import { Modal, ModalCloseButton } from '../../..';
|
||||
import {
|
||||
StyledButton,
|
||||
StyledButtonContent,
|
||||
@@ -14,25 +11,17 @@ import {
|
||||
} from './style';
|
||||
|
||||
export type PublicLinkDisableProps = {
|
||||
page: Page;
|
||||
open: boolean;
|
||||
onConfirmDisable: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const PublicLinkDisableModal = ({
|
||||
page,
|
||||
open,
|
||||
onConfirmDisable,
|
||||
onClose,
|
||||
}: PublicLinkDisableProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const [, setIsPublic] = useBlockSuiteWorkspacePageIsPublic(page);
|
||||
const handleDisable = useCallback(() => {
|
||||
setIsPublic(false);
|
||||
toast('Successfully disabled', {
|
||||
portal: document.body,
|
||||
});
|
||||
onClose();
|
||||
}, [onClose, setIsPublic]);
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<StyledModalWrapper>
|
||||
@@ -47,7 +36,10 @@ export const PublicLinkDisableModal = ({
|
||||
<StyledButton onClick={onClose}>{t['Cancel']()}</StyledButton>
|
||||
<StyledDangerButton
|
||||
data-testid="disable-public-link-confirm-button"
|
||||
onClick={handleDisable}
|
||||
onClick={() => {
|
||||
onConfirmDisable();
|
||||
onClose();
|
||||
}}
|
||||
style={{ marginLeft: '24px' }}
|
||||
>
|
||||
{t['Disable']()}
|
||||
|
||||
@@ -27,7 +27,6 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
wrapperPosition={['center', 'center']}
|
||||
hideBackdrop
|
||||
>
|
||||
<ModalWrapper width={545} height={442} data-testid="onboarding-modal">
|
||||
<ModalCloseButton
|
||||
@@ -60,7 +59,7 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
|
||||
onClick={() => setStep(1)}
|
||||
data-testid="onboarding-modal-next-button"
|
||||
>
|
||||
Next Tip Please !
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,6 +39,6 @@ export const buttonStyle = style({
|
||||
padding: '4 20px',
|
||||
':hover': {
|
||||
backgroundColor: 'var(--affine-primary-color)',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
color: 'var(--affine-white)',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export const StyledCard = styled('div')<{
|
||||
height: '124px',
|
||||
cursor: 'pointer',
|
||||
padding: '16px',
|
||||
boxShadow: 'var(--affine-shadow)',
|
||||
boxShadow: 'var(--affine-shadow-1)',
|
||||
borderRadius: '12px',
|
||||
border: `1px solid ${borderColor}`,
|
||||
...displayFlex('flex-start', 'flex-start'),
|
||||
|
||||
@@ -44,5 +44,5 @@ export const Close: StoryFn = () => {
|
||||
Close.play = async ({ canvasElement }) => {
|
||||
const element = within(canvasElement);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await element.getByTestId('change-log-close-button').click();
|
||||
element.getByTestId('change-log-close-button').click();
|
||||
};
|
||||
|
||||
112
packages/component/src/stories/PageList.stories.tsx
Normal file
112
packages/component/src/stories/PageList.stories.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { PageIcon } from '@blocksuite/icons';
|
||||
import type { StoryFn } from '@storybook/react';
|
||||
|
||||
import { AffineLoading } from '../components/affine-loading';
|
||||
import type {
|
||||
PageListProps,
|
||||
TrashListData,
|
||||
} from '../components/page-list/all-page';
|
||||
import { PageListTrashView } from '../components/page-list/all-page';
|
||||
import PageList from '../components/page-list/all-page';
|
||||
import type { OperationCellProps } from '../components/page-list/operation-cell';
|
||||
import { OperationCell } from '../components/page-list/operation-cell';
|
||||
import { toast } from '../ui/toast';
|
||||
|
||||
export default {
|
||||
title: 'AFFiNE/PageList',
|
||||
component: AffineLoading,
|
||||
};
|
||||
|
||||
export const AffineOperationCell: StoryFn<OperationCellProps> = ({
|
||||
...props
|
||||
}) => (
|
||||
<div>
|
||||
<OperationCell {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
AffineOperationCell.args = {
|
||||
title: 'Example Page',
|
||||
favorite: false,
|
||||
isPublic: true,
|
||||
onToggleFavoritePage: () => toast('Toggle favorite page'),
|
||||
onDisablePublicSharing: () => toast('Disable public sharing'),
|
||||
onOpenPageInNewTab: () => toast('Open page in new tab'),
|
||||
onRemoveToTrash: () => toast('Remove to trash'),
|
||||
};
|
||||
|
||||
export const AffineAllPageList: StoryFn<PageListProps> = ({ ...props }) => (
|
||||
<div>
|
||||
<PageList {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
AffineAllPageList.args = {
|
||||
isPublicWorkspace: false,
|
||||
listType: 'all',
|
||||
list: [
|
||||
{
|
||||
pageId: '1',
|
||||
favorite: false,
|
||||
icon: <PageIcon />,
|
||||
isPublicPage: true,
|
||||
title: 'Example Public Page with long title that will be truncated',
|
||||
createDate: '2021-01-01',
|
||||
updatedDate: '2021-01-01',
|
||||
bookmarkPage: () => toast('Bookmark page'),
|
||||
onClickPage: () => toast('Click page'),
|
||||
onDisablePublicSharing: () => toast('Disable public sharing'),
|
||||
onOpenPageInNewTab: () => toast('Open page in new tab'),
|
||||
removeToTrash: () => toast('Remove to trash'),
|
||||
},
|
||||
{
|
||||
pageId: '2',
|
||||
favorite: true,
|
||||
isPublicPage: false,
|
||||
icon: <PageIcon />,
|
||||
title: 'Favorited Page',
|
||||
createDate: '2021-01-01',
|
||||
updatedDate: '2021-01-01',
|
||||
bookmarkPage: () => toast('Bookmark page'),
|
||||
onClickPage: () => toast('Click page'),
|
||||
onDisablePublicSharing: () => toast('Disable public sharing'),
|
||||
onOpenPageInNewTab: () => toast('Open page in new tab'),
|
||||
removeToTrash: () => toast('Remove to trash'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const AffineTrashPageList: StoryFn<{
|
||||
list: TrashListData[];
|
||||
}> = ({ ...props }) => (
|
||||
<div>
|
||||
<PageListTrashView {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
AffineTrashPageList.args = {
|
||||
list: [
|
||||
{
|
||||
pageId: '1',
|
||||
icon: <PageIcon />,
|
||||
title: 'Example Page',
|
||||
updatedDate: '2021-01-01',
|
||||
createDate: '2021-01-01',
|
||||
trashDate: '2021-01-01',
|
||||
onClickPage: () => toast('Click page'),
|
||||
onPermanentlyDeletePage: () => toast('Permanently delete page'),
|
||||
onRestorePage: () => toast('Restore page'),
|
||||
},
|
||||
{
|
||||
pageId: '2',
|
||||
icon: <PageIcon />,
|
||||
title: 'Example Page with long title that will be truncated',
|
||||
updatedDate: '2021-01-01',
|
||||
createDate: '2021-01-01',
|
||||
trashDate: '2021-01-01',
|
||||
onClickPage: () => toast('Click page'),
|
||||
onPermanentlyDeletePage: () => toast('Permanently delete page'),
|
||||
onRestorePage: () => toast('Restore page'),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -5,8 +5,11 @@ import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { expect } from '@storybook/jest';
|
||||
import type { StoryFn } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { PublicLinkDisableModal } from '../components/share-menu/disable-public-link';
|
||||
import { ShareMenu } from '../components/share-menu/ShareMenu';
|
||||
import { StyledDisableButton } from '../components/share-menu/styles';
|
||||
import toast from '../ui/toast/toast';
|
||||
|
||||
export default {
|
||||
@@ -36,9 +39,9 @@ const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
|
||||
WorkspaceFlavour.LOCAL
|
||||
);
|
||||
|
||||
initPage(blockSuiteWorkspace.createPage('page0'));
|
||||
initPage(blockSuiteWorkspace.createPage('page1'));
|
||||
initPage(blockSuiteWorkspace.createPage('page2'));
|
||||
initPage(blockSuiteWorkspace.createPage({ id: 'page0' }));
|
||||
initPage(blockSuiteWorkspace.createPage({ id: 'page1' }));
|
||||
initPage(blockSuiteWorkspace.createPage({ id: 'page2' }));
|
||||
|
||||
const localWorkspace: LocalWorkspace = {
|
||||
id: 'test-workspace',
|
||||
@@ -103,3 +106,22 @@ export const AffineBasic: StoryFn = () => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const DisableModal: StoryFn = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<StyledDisableButton onClick={() => setOpen(!open)}>
|
||||
Disable Public Link
|
||||
</StyledDisableButton>
|
||||
<PublicLinkDisableModal
|
||||
open={open}
|
||||
onConfirmDisable={() => {
|
||||
toast('Disabled');
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,3 @@ export default {
|
||||
export const Basic: StoryFn = () => {
|
||||
return <Switch />;
|
||||
};
|
||||
Basic.args = {
|
||||
logoSrc: '/imgs/affine-text-logo.png',
|
||||
};
|
||||
|
||||
@@ -163,6 +163,7 @@ input {
|
||||
border-radius: 0; /*Solve the problem of rounded corners of the input box on ios*/
|
||||
outline: medium; /*Remove the default yellow border on mouse click*/
|
||||
background-color: transparent;
|
||||
caret-color: var(--affine-primary-color);
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.7"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
6
packages/env/package.json
vendored
6
packages/env/package.json
vendored
@@ -4,8 +4,8 @@
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"devDependencies": {
|
||||
"@blocksuite/global": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"next": "^13.4.0",
|
||||
"@blocksuite/global": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"next": "^13.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"zod": "^3.21.4"
|
||||
@@ -21,5 +21,5 @@
|
||||
"dependencies": {
|
||||
"lit": "^2.7.4"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
1
packages/env/src/config.ts
vendored
1
packages/env/src/config.ts
vendored
@@ -6,6 +6,7 @@ import { z } from 'zod';
|
||||
import { getUaHelper } from './ua-helper';
|
||||
|
||||
export const buildFlagsSchema = z.object({
|
||||
enableTestProperties: z.boolean(),
|
||||
enableBroadCastChannelProvider: z.boolean(),
|
||||
enableDebugPage: z.boolean(),
|
||||
enableLegacyCloud: z.boolean(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@affine/graphql",
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"description": "Autogenerated GraphQL client for affine.pro",
|
||||
"license": "MPL-2.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"private": true,
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
"react-i18next": "^12.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.3",
|
||||
"@types/node": "^18.16.5",
|
||||
"@types/prettier": "^2.7.2",
|
||||
"next": "^13.4.0",
|
||||
"next": "^13.4.1",
|
||||
"prettier": "^2.8.8",
|
||||
"react-dom": "^18.2.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"main": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"@affine/env": "workspace:*",
|
||||
"jotai": "^2.0.4"
|
||||
"jotai": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blocksuite/blocks": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/blocks": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"lottie-web": "^5.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -20,5 +20,5 @@
|
||||
"@blocksuite/store": "*",
|
||||
"lottie-web": "*"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@ type SetStateActionWithReset<Value> =
|
||||
// similar to atomWithStorage, but will not trigger twice on init
|
||||
// https://github.com/pmndrs/jotai/discussions/1737
|
||||
export function atomWithSyncStorage<Value>(key: string, initialValue: Value) {
|
||||
const storedValue = storage.getItem(key) as Value;
|
||||
const storedValue = storage.getItem(key, initialValue) as Value;
|
||||
const _value =
|
||||
typeof storedValue === 'symbol'
|
||||
? initialValue
|
||||
: (storage.getItem(key) as Value);
|
||||
: (storage.getItem(key, initialValue) as Value);
|
||||
const baseAtom = atom(_value);
|
||||
|
||||
baseAtom.onMount = setAtom => {
|
||||
if (storage.subscribe) {
|
||||
return storage.subscribe(key, setAtom);
|
||||
return storage.subscribe(key, setAtom, initialValue);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.15.2",
|
||||
"@types/node": "^18.16.3"
|
||||
"@types/node": "^18.16.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
@@ -26,5 +26,5 @@
|
||||
"universal": "napi universal",
|
||||
"version": "napi version"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"exports": {
|
||||
"./*.md": "./src/*.md"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@toeverything/theme",
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build"
|
||||
@@ -8,6 +8,9 @@
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.umd.cjs",
|
||||
@@ -17,11 +20,13 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.umd.cjs"
|
||||
}
|
||||
},
|
||||
"./style.css": "./dist/style.css"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.4",
|
||||
"@vanilla-extract/vite-plugin": "^3.8.0",
|
||||
"vite": "^4.3.5",
|
||||
"vite-plugin-dts": "^2.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
11
packages/theme/src/index.css.ts
Normal file
11
packages/theme/src/index.css.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { globalStyle } from '@vanilla-extract/css';
|
||||
|
||||
import { darkCssVariables, lightCssVariables } from './index';
|
||||
|
||||
globalStyle(':root', {
|
||||
vars: lightCssVariables,
|
||||
});
|
||||
|
||||
globalStyle(':root[data-theme="dark"]', {
|
||||
vars: darkCssVariables,
|
||||
});
|
||||
@@ -32,6 +32,7 @@ export const baseTheme = {
|
||||
fontFamily: `Avenir Next, Poppins, ${basicFontFamily}`,
|
||||
fontNumberFamily: `Roboto Mono, ${basicFontFamily}`,
|
||||
fontCodeFamily: `Space Mono, Consolas, Menlo, Monaco, Courier, monospace, ${basicFontFamily}`,
|
||||
fontTitle: '36px',
|
||||
fontH1: '28px',
|
||||
fontH2: '26px',
|
||||
fontH3: '24px',
|
||||
@@ -45,13 +46,37 @@ export const baseTheme = {
|
||||
lineHeight: 'calc(1em + 8px)',
|
||||
|
||||
zIndexModal: '1000',
|
||||
zIndexPopover: '100',
|
||||
zIndexPopover: '1000',
|
||||
|
||||
paragraphSpace: '8px',
|
||||
popoverRadius: '12px',
|
||||
|
||||
zoom: '1',
|
||||
scale: 'calc(1 / var(--affine-zoom))',
|
||||
paletteLineYellow: 'rgb(255, 232, 56)',
|
||||
paletteLineOrange: 'rgb(255, 175, 56)',
|
||||
paletteLineTangerine: 'rgb(255, 99, 31)',
|
||||
paletteLineRed: 'rgb(252, 63, 85)',
|
||||
paletteLineMagenta: 'rgb(255, 56, 179)',
|
||||
paletteLinePurple: 'rgb(182, 56, 255)',
|
||||
paletteLineNavy: 'rgb(59, 37, 204)',
|
||||
paletteLineBlue: 'rgb(79, 144, 255)',
|
||||
paletteLineGreen: 'rgb(16, 203, 134)',
|
||||
paletteLineWhite: 'rgb(255, 255, 255)',
|
||||
paletteLineBlack: 'rgb(0, 0, 0)',
|
||||
paletteLineGrey: 'rgb(153, 153, 153)',
|
||||
paletteShapeYellow: 'rgb(255, 241, 136)',
|
||||
paletteShapeOrange: 'rgb(255, 207, 136)',
|
||||
paletteShapeTangerine: 'rgb(255, 161, 121)',
|
||||
paletteShapeRed: 'rgb(253, 140, 153)',
|
||||
paletteShapeMagenta: 'rgb(255, 136, 209)',
|
||||
paletteShapePurple: 'rgb(211, 136, 255)',
|
||||
paletteShapeNavy: 'rgb(137, 124, 224)',
|
||||
paletteShapeBlue: 'rgb(149, 188, 255)',
|
||||
paletteShapeGreen: 'rgb(112, 224, 182)',
|
||||
paletteShapeWhite: 'rgb(255, 255, 255)',
|
||||
paletteShapeBlack: 'rgb(0, 0, 0)',
|
||||
paletteShapeGrey: 'rgb(194, 194, 194)',
|
||||
};
|
||||
|
||||
// Refs: https://github.com/toeverything/AFFiNE/issues/1796
|
||||
@@ -71,7 +96,7 @@ export const lightTheme = {
|
||||
backgroundPrimaryColor: 'rgb(255, 255, 255)',
|
||||
backgroundOverlayPanelColor: 'rgb(251, 251, 252)',
|
||||
backgroundSecondaryColor: 'rgb(251, 250, 252)',
|
||||
backgroundTertiaryColor: 'rgb(233, 233, 236)',
|
||||
backgroundTertiaryColor: 'rgb(245, 243, 247)',
|
||||
backgroundCodeBlock: 'rgb(250, 251, 253)',
|
||||
backgroundModalColor: 'rgba(0, 0, 0, 0.4)',
|
||||
textPrimaryColor: 'rgb(66, 65, 73)',
|
||||
@@ -79,7 +104,7 @@ export const lightTheme = {
|
||||
textDisableColor: 'rgb(169, 169, 173)',
|
||||
textEmphasisColor: 'rgb(84, 56, 255)',
|
||||
hoverColor: 'rgba(0, 0, 0, 0.04)',
|
||||
linkColor: 'rgb(125, 145, 255)',
|
||||
linkColor: 'rgba(88, 114, 251, 1)',
|
||||
quoteColor: 'rgb(100, 95, 130)',
|
||||
iconColor: 'rgb(119, 117, 125)',
|
||||
iconSecondary: 'rgba(119, 117, 125, 0.6)',
|
||||
@@ -115,38 +140,16 @@ export const lightTheme = {
|
||||
tagBlue: 'rgba(225, 238, 255, 1)',
|
||||
tagPurple: 'rgba(243, 240, 255, 1)',
|
||||
tagPink: 'rgba(251, 231, 255, 1)',
|
||||
paletteLineYellow: 'rgb(255, 232, 56)',
|
||||
paletteLineOrange: 'rgb(255, 175, 56)',
|
||||
paletteLineTangerine: 'rgb(255, 99, 31)',
|
||||
paletteLineRed: 'rgb(252, 63, 85)',
|
||||
paletteLineMagenta: 'rgb(255, 56, 179)',
|
||||
paletteLinePurple: 'rgb(182, 56, 255)',
|
||||
paletteLineNavy: 'rgb(59, 37, 204)',
|
||||
paletteLineBlue: 'rgb(79, 144, 255)',
|
||||
paletteLineGreen: 'rgb(16, 203, 134)',
|
||||
paletteLineWhite: 'rgb(255, 255, 255)',
|
||||
paletteLineBlack: 'rgb(0, 0, 0)',
|
||||
paletteLineGrey: 'rgb(153, 153, 153)',
|
||||
paletteShapeYellow: 'rgb(255, 241, 136)',
|
||||
paletteShapeOrange: 'rgb(255, 207, 136)',
|
||||
paletteShapeTangerine: 'rgb(255, 161, 121)',
|
||||
paletteShapeRed: 'rgb(253, 140, 153)',
|
||||
paletteShapeMagenta: 'rgb(255, 136, 209)',
|
||||
paletteShapePurple: 'rgb(211, 136, 255)',
|
||||
paletteShapeNavy: 'rgb(137, 124, 224)',
|
||||
paletteShapeBlue: 'rgb(149, 188, 255)',
|
||||
paletteShapeGreen: 'rgb(112, 224, 182)',
|
||||
paletteShapeWhite: 'rgb(255, 255, 255)',
|
||||
paletteShapeBlack: 'rgb(0, 0, 0)',
|
||||
paletteShapeGrey: 'rgb(194, 194, 194)',
|
||||
tooltip: '#424149',
|
||||
tooltip: 'rgba(66, 65, 73, 1)',
|
||||
menuShadow:
|
||||
'0px 0px 12px rgba(66, 65, 73, 0.1), inset 0px 0px 0px 0.5px rgba(227, 227, 228, 1)',
|
||||
shadow: '0px 0px 4px rgba(66, 65, 73, 0.1)',
|
||||
'0px 0px 12px rgba(66, 65, 73, 0.14), inset 0px 0px 0px 0.5px rgba(227, 227, 228, 1)',
|
||||
shadow1: '0px 0px 4px 0px rgba(66, 65, 73, 0.14)',
|
||||
shadow2: '0px 0px 12px 0px rgba(66, 65, 73, 0.18)',
|
||||
shadow3: '0px 0px 20px 0px rgba(66, 65, 73, 0.22)',
|
||||
popoverShadow:
|
||||
'0px 0px 30px rgba(75, 75, 75, 0.2), 0px 0px 4px rgba(75, 75, 75, 0.3), inset 0px 0px 0px rgba(227, 226, 228, 1)',
|
||||
floatButtonShadow:
|
||||
'0px 1px 16px rgba(0, 0, 0, 0.1), 0px 2px 3px rgba(0, 0, 0, 0.1)',
|
||||
'0px 10px 12px -3px rgba(66, 65, 73, 0.1), 0px 4px 6px -2px rgba(66, 65, 73, 0.05)',
|
||||
};
|
||||
|
||||
export const darkTheme = {
|
||||
@@ -155,13 +158,13 @@ export const darkTheme = {
|
||||
themeMode: 'dark',
|
||||
|
||||
brandColor: 'rgba(156, 140, 255, 1)',
|
||||
primaryColor: 'rgba(156, 140, 255, 1)',
|
||||
primaryColor: 'rgba(106, 86, 229, 1)',
|
||||
secondaryColor: 'rgb(144, 150, 245)',
|
||||
tertiaryColor: 'rgb(30, 30, 30)',
|
||||
hoverColor: 'rgba(255, 255, 255, 0.1)',
|
||||
iconColor: 'rgb(168, 168, 160)',
|
||||
iconSecondary: 'rgba(168,168,160,0.6)',
|
||||
borderColor: 'rgb(57, 57, 57)',
|
||||
borderColor: 'rgba(46, 46, 46, 1)',
|
||||
dividerColor: 'rgb(114, 114, 114)',
|
||||
placeholderColor: 'rgb(62, 62, 63)',
|
||||
quoteColor: 'rgb(147, 144, 163)',
|
||||
@@ -191,7 +194,7 @@ export const darkTheme = {
|
||||
white: 'rgb(0, 0, 0)',
|
||||
backgroundCodeBlock: 'rgb(41, 44, 51)',
|
||||
backgroundTertiaryColor: 'rgb(30, 30, 30)',
|
||||
backgroundProcessingColor: 'rgba(20, 22, 26, 1)',
|
||||
backgroundProcessingColor: 'rgba(24, 27, 32, 1)',
|
||||
backgroundErrorColor: 'rgba(21, 14, 13, 1)',
|
||||
backgroundWarningColor: 'rgba(26, 10, 3, 1)',
|
||||
backgroundSuccessColor: 'rgba(8, 21, 18, 1)',
|
||||
@@ -209,38 +212,16 @@ export const darkTheme = {
|
||||
tagYellow: 'rgba(150, 132, 49, 1)',
|
||||
tagOrange: 'rgba(185, 129, 46, 1)',
|
||||
tagGray: 'rgba(41, 41, 41, 1)',
|
||||
paletteLineYellow: 'rgb(255, 232, 56)',
|
||||
paletteLineOrange: 'rgb(255, 175, 56)',
|
||||
paletteLineTangerine: 'rgb(255, 99, 31)',
|
||||
paletteLineRed: 'rgb(252, 63, 85)',
|
||||
paletteLineMagenta: 'rgb(255, 56, 179)',
|
||||
paletteLinePurple: 'rgb(182, 56, 255)',
|
||||
paletteLineNavy: 'rgb(59, 37, 204)',
|
||||
paletteLineBlue: 'rgb(79, 144, 255)',
|
||||
paletteLineGreen: 'rgb(16, 203, 134)',
|
||||
paletteLineWhite: 'rgb(255, 255, 255)',
|
||||
paletteLineBlack: 'rgb(0, 0, 0)',
|
||||
paletteLineGrey: 'rgb(153, 153, 153)',
|
||||
paletteShapeYellow: 'rgb(255, 241, 136)',
|
||||
paletteShapeOrange: 'rgb(255, 207, 136)',
|
||||
paletteShapeTangerine: 'rgb(255, 161, 121)',
|
||||
paletteShapeRed: 'rgb(253, 140, 153)',
|
||||
paletteShapeMagenta: 'rgb(255, 136, 209)',
|
||||
paletteShapePurple: 'rgb(211, 136, 255)',
|
||||
paletteShapeNavy: 'rgb(137, 124, 224)',
|
||||
paletteShapeBlue: 'rgb(149, 188, 255)',
|
||||
paletteShapeGreen: 'rgb(112, 224, 182)',
|
||||
paletteShapeWhite: 'rgb(255, 255, 255)',
|
||||
paletteShapeBlack: 'rgb(0, 0, 0)',
|
||||
paletteShapeGrey: 'rgb(194, 194, 194)',
|
||||
tooltip: '#EAEAEA',
|
||||
tooltip: 'rgba(234, 234, 234, 1)',
|
||||
menuShadow:
|
||||
'0px 0px 12px rgba(0, 0, 0, 1), inset 0px 0px 0px 0.5px rgba(46, 46, 46, 1)',
|
||||
shadow: '0px 0px 4px rgba(20, 20, 20, 1)',
|
||||
shadow1: '0px 0px 4px 2px rgba(15, 15, 15, 1)',
|
||||
shadow2: '0px 0px 12px 4px rgba(15, 15, 15, 0.8)',
|
||||
shadow3: '0px 0px 22px 8px rgba(15, 15, 15, 0.9)',
|
||||
popoverShadow:
|
||||
'0px 0px 30px rgba(12, 12, 12, 0.8), 0px 0px 8px rgba(12, 12, 12, 1), inset 0px 0px 0px rgba(46, 46, 46, 1)',
|
||||
floatButtonShadow:
|
||||
'0px 1px 16px rgba(0, 0, 0, 1), 0px 2px 3px rgba(0, 0, 0, 1)',
|
||||
' 0px 10px 12px -3px rgba(15, 15, 15, 0.88), 0px 4px 6px -2px rgba(0, 0, 0, 0.88)',
|
||||
} satisfies Omit<AffineTheme, 'editorMode'>;
|
||||
|
||||
export const lightCssVariables = Object.entries(lightTheme).reduce(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { defineConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
@@ -13,13 +14,12 @@ export default defineConfig({
|
||||
},
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
fileName: 'index',
|
||||
entry: {
|
||||
index: resolve(__dirname, 'src/index.ts'),
|
||||
css: resolve(__dirname, 'src/index.css.ts'),
|
||||
},
|
||||
name: 'ToEverythingTheme',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['idb', 'yjs'],
|
||||
},
|
||||
},
|
||||
plugins: [dts()],
|
||||
plugins: [dts(), vanillaExtractPlugin()],
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@affine/env": "workspace:*",
|
||||
"@toeverything/y-indexeddb": "workspace:*",
|
||||
"firebase": "^9.21.0",
|
||||
"jotai": "^2.0.4",
|
||||
"jotai": "^2.1.0",
|
||||
"js-base64": "^3.7.5",
|
||||
"ky": "^0.33.3",
|
||||
"lib0": "^0.2.74",
|
||||
@@ -38,5 +38,5 @@
|
||||
"@types/ws": "^8.5.4",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ const getStorage = () => createJSONStorage(() => localStorage);
|
||||
|
||||
export const getStoredWorkspaceMeta = () => {
|
||||
const storage = getStorage();
|
||||
const data = storage.getItem('jotai-workspaces') as RootWorkspaceMetadata[];
|
||||
return data;
|
||||
return storage.getItem('jotai-workspaces', []) as RootWorkspaceMetadata[];
|
||||
};
|
||||
|
||||
// global store
|
||||
|
||||
@@ -21,8 +21,9 @@ const logger = new DebugLogger('affine:workspace:local:crud');
|
||||
*/
|
||||
export function saveWorkspaceToLocalStorage(workspaceId: string) {
|
||||
const storage = getStorage();
|
||||
!Array.isArray(storage.getItem(kStoreKey)) && storage.setItem(kStoreKey, []);
|
||||
const data = storage.getItem(kStoreKey) as z.infer<typeof schema>;
|
||||
!Array.isArray(storage.getItem(kStoreKey, [])) &&
|
||||
storage.setItem(kStoreKey, []);
|
||||
const data = storage.getItem(kStoreKey, []) as z.infer<typeof schema>;
|
||||
const id = data.find(id => id === workspaceId);
|
||||
if (!id) {
|
||||
logger.debug('saveWorkspaceToLocalStorage', workspaceId);
|
||||
@@ -34,9 +35,9 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.LOCAL> = {
|
||||
get: async workspaceId => {
|
||||
logger.debug('get', workspaceId);
|
||||
const storage = getStorage();
|
||||
!Array.isArray(storage.getItem(kStoreKey)) &&
|
||||
!Array.isArray(storage.getItem(kStoreKey, [])) &&
|
||||
storage.setItem(kStoreKey, []);
|
||||
const data = storage.getItem(kStoreKey) as z.infer<typeof schema>;
|
||||
const data = storage.getItem(kStoreKey, []) as z.infer<typeof schema>;
|
||||
const id = data.find(id => id === workspaceId);
|
||||
if (!id) {
|
||||
return null;
|
||||
@@ -56,7 +57,7 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.LOCAL> = {
|
||||
create: async ({ doc }) => {
|
||||
logger.debug('create', doc);
|
||||
const storage = getStorage();
|
||||
!Array.isArray(storage.getItem(kStoreKey)) &&
|
||||
!Array.isArray(storage.getItem(kStoreKey, [])) &&
|
||||
storage.setItem(kStoreKey, []);
|
||||
const binary = BlockSuiteWorkspace.Y.encodeStateAsUpdateV2(doc);
|
||||
const id = nanoid();
|
||||
@@ -76,9 +77,9 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.LOCAL> = {
|
||||
delete: async workspace => {
|
||||
logger.debug('delete', workspace);
|
||||
const storage = getStorage();
|
||||
!Array.isArray(storage.getItem(kStoreKey)) &&
|
||||
!Array.isArray(storage.getItem(kStoreKey, [])) &&
|
||||
storage.setItem(kStoreKey, []);
|
||||
const data = storage.getItem(kStoreKey) as z.infer<typeof schema>;
|
||||
const data = storage.getItem(kStoreKey, []) as z.infer<typeof schema>;
|
||||
const idx = data.findIndex(id => id === workspace.id);
|
||||
if (idx === -1) {
|
||||
throw new Error('workspace not found');
|
||||
@@ -93,8 +94,10 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.LOCAL> = {
|
||||
list: async () => {
|
||||
logger.debug('list');
|
||||
const storage = getStorage();
|
||||
let allWorkspaceIDs: string[] = Array.isArray(storage.getItem(kStoreKey))
|
||||
? (storage.getItem(kStoreKey) as z.infer<typeof schema>)
|
||||
let allWorkspaceIDs: string[] = Array.isArray(
|
||||
storage.getItem(kStoreKey, [])
|
||||
)
|
||||
? (storage.getItem(kStoreKey, []) as z.infer<typeof schema>)
|
||||
: [];
|
||||
|
||||
// workspaces in desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@toeverything/y-indexeddb",
|
||||
"type": "module",
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"scripts": {
|
||||
"build": "vite build"
|
||||
},
|
||||
@@ -28,9 +28,9 @@
|
||||
"idb": "^7.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blocksuite/blocks": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230503040956-5c49643f-nightly",
|
||||
"vite": "^4.3.4",
|
||||
"@blocksuite/blocks": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230505225643-03f75e5e-nightly",
|
||||
"vite": "^4.3.5",
|
||||
"vite-plugin-dts": "^2.3.0",
|
||||
"y-indexeddb": "^9.0.11"
|
||||
},
|
||||
|
||||
@@ -136,10 +136,12 @@ describe('indexeddb provider', () => {
|
||||
rootDBName
|
||||
);
|
||||
provider.connect();
|
||||
expect(provider.connected).toBe(true);
|
||||
const p1 = provider.whenSynced;
|
||||
await p1;
|
||||
const snapshot = encodeStateAsUpdate(workspace.doc);
|
||||
provider.disconnect();
|
||||
expect(provider.connected).toBe(false);
|
||||
{
|
||||
const page = workspace.createPage('page0');
|
||||
const pageBlockId = page.addBlock('affine:page', { title: '' });
|
||||
@@ -151,14 +153,18 @@ describe('indexeddb provider', () => {
|
||||
expect(updates.length).toBe(1);
|
||||
expect(updates[0]).toEqual(snapshot);
|
||||
}
|
||||
expect(provider.connected).toBe(false);
|
||||
provider.connect();
|
||||
expect(provider.connected).toBe(true);
|
||||
const p2 = provider.whenSynced;
|
||||
await p2;
|
||||
{
|
||||
const updates = await getUpdates(workspace.id);
|
||||
expect(updates).not.toEqual([]);
|
||||
}
|
||||
expect(provider.connected).toBe(true);
|
||||
provider.disconnect();
|
||||
expect(provider.connected).toBe(false);
|
||||
expect(p1).not.toBe(p2);
|
||||
});
|
||||
|
||||
|
||||
@@ -209,6 +209,8 @@ export const createIndexedDBProvider = (
|
||||
};
|
||||
const apis = {
|
||||
connect: async () => {
|
||||
if (connected) return;
|
||||
|
||||
apis.whenSynced = new Promise<void>((_resolve, _reject) => {
|
||||
early = true;
|
||||
resolve = _resolve;
|
||||
@@ -287,6 +289,9 @@ export const createIndexedDBProvider = (
|
||||
(await dbPromise).delete('workspace', id);
|
||||
},
|
||||
whenSynced: Promise.resolve(),
|
||||
get connected() {
|
||||
return connected;
|
||||
},
|
||||
};
|
||||
|
||||
return apis;
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface IndexedDBProvider {
|
||||
disconnect: () => void;
|
||||
cleanup: () => Promise<void>;
|
||||
whenSynced: Promise<void>;
|
||||
readonly connected: boolean;
|
||||
}
|
||||
|
||||
export type UpdateMessage = {
|
||||
|
||||
2
tests/fixtures/package.json
vendored
2
tests/fixtures/package.json
vendored
@@ -3,5 +3,5 @@
|
||||
"exports": {
|
||||
"./*": "./*"
|
||||
},
|
||||
"version": "0.5.4-canary.22"
|
||||
"version": "0.5.4-canary.25"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine-test/kit",
|
||||
"private": true,
|
||||
"version": "0.5.4-canary.22",
|
||||
"version": "0.5.4-canary.25",
|
||||
"exports": {
|
||||
"./playwright": "./playwright.ts"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user