fix: resolve cycle imports and prevent it by oxlint (#5103)

This commit is contained in:
LongYinan
2023-11-29 04:43:27 +00:00
parent b73e87e4ad
commit a843dcd851
25 changed files with 309 additions and 1034 deletions

View File

@@ -1,5 +1,4 @@
import type { Tag } from '@affine/env/filter';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { EdgelessIcon, PageIcon, ToggleCollapseIcon } from '@blocksuite/icons';
@@ -19,93 +18,8 @@ import {
useAtom,
useAtomValue,
} from './scoped-atoms';
import type {
PageGroupDefinition,
PageGroupProps,
PageListItemProps,
PageListProps,
} from './types';
import { type DateKey } from './types';
import { betweenDaysAgo, shallowEqual, withinDaysAgo } from './utils';
// todo: optimize date matchers
const getDateGroupDefinitions = (key: DateKey): PageGroupDefinition[] => [
{
id: 'today',
label: <Trans i18nKey="com.affine.today" />,
match: item => withinDaysAgo(new Date(item[key] ?? item.createDate), 1),
},
{
id: 'yesterday',
label: <Trans i18nKey="com.affine.yesterday" />,
match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 1, 2),
},
{
id: 'last7Days',
label: <Trans i18nKey="com.affine.last7Days" />,
match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 2, 7),
},
{
id: 'last30Days',
label: <Trans i18nKey="com.affine.last30Days" />,
match: item =>
betweenDaysAgo(new Date(item[key] ?? item.createDate), 7, 30),
},
{
id: 'moreThan30Days',
label: <Trans i18nKey="com.affine.moreThan30Days" />,
match: item => !withinDaysAgo(new Date(item[key] ?? item.createDate), 30),
},
];
const pageGroupDefinitions = {
createDate: getDateGroupDefinitions('createDate'),
updatedDate: getDateGroupDefinitions('updatedDate'),
// add more here later
// todo: some page group definitions maybe dynamic
};
export function pagesToPageGroups(
pages: PageMeta[],
key?: DateKey
): PageGroupProps[] {
if (!key) {
return [
{
id: 'all',
items: pages,
allItems: pages,
},
];
}
// assume pages are already sorted, we will use the page order to determine the group order
const groupDefs = pageGroupDefinitions[key];
const groups: PageGroupProps[] = [];
for (const page of pages) {
// for a single page, there could be multiple groups that it belongs to
const matchedGroups = groupDefs.filter(def => def.match(page));
for (const groupDef of matchedGroups) {
const group = groups.find(g => g.id === groupDef.id);
if (group) {
group.items.push(page);
} else {
const label =
typeof groupDef.label === 'function'
? groupDef.label()
: groupDef.label;
groups.push({
id: groupDef.id,
label: label,
items: [page],
allItems: pages,
});
}
}
}
return groups;
}
import type { PageGroupProps, PageListItemProps, PageListProps } from './types';
import { shallowEqual } from './utils';
export const PageGroupHeader = ({ id, items, label }: PageGroupProps) => {
const [collapseState, setCollapseState] = useAtom(pageGroupCollapseStateAtom);

View File

@@ -0,0 +1,85 @@
import { Trans } from '@affine/i18n';
import type { PageMeta } from '@blocksuite/store';
import type { PageGroupDefinition, PageGroupProps } from './types';
import { type DateKey } from './types';
import { betweenDaysAgo, withinDaysAgo } from './utils';
// todo: optimize date matchers
const getDateGroupDefinitions = (key: DateKey): PageGroupDefinition[] => [
{
id: 'today',
label: <Trans i18nKey="com.affine.today" />,
match: item => withinDaysAgo(new Date(item[key] ?? item.createDate), 1),
},
{
id: 'yesterday',
label: <Trans i18nKey="com.affine.yesterday" />,
match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 1, 2),
},
{
id: 'last7Days',
label: <Trans i18nKey="com.affine.last7Days" />,
match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 2, 7),
},
{
id: 'last30Days',
label: <Trans i18nKey="com.affine.last30Days" />,
match: item =>
betweenDaysAgo(new Date(item[key] ?? item.createDate), 7, 30),
},
{
id: 'moreThan30Days',
label: <Trans i18nKey="com.affine.moreThan30Days" />,
match: item => !withinDaysAgo(new Date(item[key] ?? item.createDate), 30),
},
];
const pageGroupDefinitions = {
createDate: getDateGroupDefinitions('createDate'),
updatedDate: getDateGroupDefinitions('updatedDate'),
// add more here later
// todo: some page group definitions maybe dynamic
};
export function pagesToPageGroups(
pages: PageMeta[],
key?: DateKey
): PageGroupProps[] {
if (!key) {
return [
{
id: 'all',
items: pages,
allItems: pages,
},
];
}
// assume pages are already sorted, we will use the page order to determine the group order
const groupDefs = pageGroupDefinitions[key];
const groups: PageGroupProps[] = [];
for (const page of pages) {
// for a single page, there could be multiple groups that it belongs to
const matchedGroups = groupDefs.filter(def => def.match(page));
for (const groupDef of matchedGroups) {
const group = groups.find(g => g.id === groupDef.id);
if (group) {
group.items.push(page);
} else {
const label =
typeof groupDef.label === 'function'
? groupDef.label()
: groupDef.label;
groups.push({
id: groupDef.id,
label: label,
items: [page],
allItems: pages,
});
}
}
}
return groups;
}

View File

@@ -4,7 +4,7 @@ import { atom } from 'jotai';
import { selectAtom } from 'jotai/utils';
import { createIsolation } from 'jotai-scope';
import { pagesToPageGroups } from './page-group';
import { pagesToPageGroups } from './pages-to-page-group';
import type {
PageListProps,
PageMetaRecord,

View File

@@ -1,13 +1,6 @@
import {
createEmptyCollection,
useEditCollectionName,
} from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { SaveIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import { Modal } from '@toeverything/components/modal';
import { nanoid } from 'nanoid';
import { useCallback, useMemo, useState } from 'react';
import Input from '../../../ui/input';
@@ -127,40 +120,3 @@ export const CreateCollection = ({
</div>
);
};
interface SaveAsCollectionButtonProps {
onConfirm: (collection: Collection) => Promise<void>;
}
export const SaveAsCollectionButton = ({
onConfirm,
}: SaveAsCollectionButtonProps) => {
const t = useAFFiNEI18N();
const { open, node } = useEditCollectionName({
title: t['com.affine.editCollection.saveCollection'](),
showTips: true,
});
const handleClick = useCallback(() => {
open('')
.then(name => {
return onConfirm(createEmptyCollection(nanoid(), { name }));
})
.catch(err => {
console.error(err);
});
}, [open, onConfirm]);
return (
<>
<Button
onClick={handleClick}
data-testid="save-as-collection"
icon={<SaveIcon />}
size="large"
style={{ padding: '7px 8px' }}
>
{t['com.affine.editCollection.saveCollection']()}
</Button>
{node}
</>
);
};

View File

@@ -6,7 +6,7 @@ import { Button } from '@toeverything/components/button';
import { Modal } from '@toeverything/components/modal';
import { type ReactNode, useCallback, useMemo, useState } from 'react';
import { RadioButton, RadioButtonGroup } from '../../../../index';
import { RadioButton, RadioButtonGroup } from '../../../../ui/button';
import * as styles from './edit-collection.css';
import { PagesMode } from './pages-mode';
import { RulesMode } from './rules-mode';

View File

@@ -1,12 +1,7 @@
import {
type AllPageListConfig,
filterPageByRules,
} from '@affine/component/page-list';
import type { Filter } from '@affine/env/filter';
import type { PageMeta } from '@blocksuite/store';
import { Modal } from '@toeverything/components/modal';
import { type MouseEvent, useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import type { AllPageListConfig } from './edit-collection';
import { SelectPage } from './select-page';
export const useSelectPage = ({
allPageListConfig,
@@ -60,47 +55,3 @@ export const useSelectPage = ({
}),
};
};
export const useFilter = (list: PageMeta[]) => {
const [filters, changeFilters] = useState<Filter[]>([]);
const [showFilter, setShowFilter] = useState(false);
const clickFilter = useCallback(
(e: MouseEvent) => {
if (showFilter || filters.length !== 0) {
e.stopPropagation();
e.preventDefault();
setShowFilter(!showFilter);
}
},
[filters.length, showFilter]
);
const onCreateFilter = useCallback(
(filter: Filter) => {
changeFilters([...filters, filter]);
setShowFilter(true);
},
[filters]
);
return {
showFilter,
filters,
updateFilters: changeFilters,
clickFilter,
createFilter: onCreateFilter,
filteredList: list.filter(v => {
if (v.trash) {
return false;
}
return filterPageByRules(filters, [], v);
}),
};
};
export const useSearch = (list: PageMeta[]) => {
const [value, onChange] = useState('');
return {
searchText: value,
updateSearchText: onChange,
searchedList: value
? list.filter(v => v.title.toLowerCase().includes(value.toLowerCase()))
: list,
};
};

View File

@@ -1,8 +1,3 @@
import {
type AllPageListConfig,
FilterList,
VirtualizedPageList,
} from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { FilterIcon } from '@blocksuite/icons';
@@ -11,10 +6,14 @@ import { Menu } from '@toeverything/components/menu';
import clsx from 'clsx';
import { type ReactNode, useCallback } from 'react';
import { FilterList } from '../../filter/filter-list';
import { VariableSelect } from '../../filter/vars';
import { VirtualizedPageList } from '../../virtualized-page-list';
import type { AllPageListConfig } from './edit-collection';
import * as styles from './edit-collection.css';
import { useFilter, useSearch } from './hooks';
import { EmptyList } from './select-page';
import { useFilter } from './use-filter';
import { useSearch } from './use-search';
export const PagesMode = ({
switchMode,

View File

@@ -6,13 +6,15 @@ import { Menu } from '@toeverything/components/menu';
import clsx from 'clsx';
import { useCallback, useState } from 'react';
import { VirtualizedPageList } from '../..';
import { FilterList } from '../../filter';
import { VariableSelect } from '../../filter/vars';
import { VirtualizedPageList } from '../../virtualized-page-list';
import { AffineShapeIcon } from '../affine-shape';
import type { AllPageListConfig } from './edit-collection';
import * as styles from './edit-collection.css';
import { useFilter, useSearch } from './hooks';
import { useFilter } from './use-filter';
import { useSearch } from './use-search';
export const SelectPage = ({
allPageListConfig,
init,

View File

@@ -0,0 +1,40 @@
import type { Filter } from '@affine/env/filter';
import type { PageMeta } from '@blocksuite/store';
import { type MouseEvent, useCallback, useState } from 'react';
import { filterPageByRules } from '../../use-collection-manager';
export const useFilter = (list: PageMeta[]) => {
const [filters, changeFilters] = useState<Filter[]>([]);
const [showFilter, setShowFilter] = useState(false);
const clickFilter = useCallback(
(e: MouseEvent) => {
if (showFilter || filters.length !== 0) {
e.stopPropagation();
e.preventDefault();
setShowFilter(!showFilter);
}
},
[filters.length, showFilter]
);
const onCreateFilter = useCallback(
(filter: Filter) => {
changeFilters([...filters, filter]);
setShowFilter(true);
},
[filters]
);
return {
showFilter,
filters,
updateFilters: changeFilters,
clickFilter,
createFilter: onCreateFilter,
filteredList: list.filter(v => {
if (v.trash) {
return false;
}
return filterPageByRules(filters, [], v);
}),
};
};

View File

@@ -0,0 +1,13 @@
import type { PageMeta } from '@blocksuite/store';
import { useState } from 'react';
export const useSearch = (list: PageMeta[]) => {
const [value, onChange] = useState('');
return {
searchText: value,
updateSearchText: onChange,
searchedList: value
? list.filter(v => v.title.toLowerCase().includes(value.toLowerCase()))
: list,
};
};

View File

@@ -4,4 +4,5 @@ export * from './collection-list';
export * from './collection-operations';
export * from './create-collection';
export * from './edit-collection/edit-collection';
export * from './save-as-collection-button';
export * from './use-edit-collection';

View File

@@ -0,0 +1,46 @@
import type { Collection } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { SaveIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { createEmptyCollection } from '../use-collection-manager';
import { useEditCollectionName } from './use-edit-collection';
interface SaveAsCollectionButtonProps {
onConfirm: (collection: Collection) => Promise<void>;
}
export const SaveAsCollectionButton = ({
onConfirm,
}: SaveAsCollectionButtonProps) => {
const t = useAFFiNEI18N();
const { open, node } = useEditCollectionName({
title: t['com.affine.editCollection.saveCollection'](),
showTips: true,
});
const handleClick = useCallback(() => {
open('')
.then(name => {
return onConfirm(createEmptyCollection(nanoid(), { name }));
})
.catch(err => {
console.error(err);
});
}, [open, onConfirm]);
return (
<>
<Button
onClick={handleClick}
data-testid="save-as-collection"
icon={<SaveIcon />}
size="large"
style={{ padding: '7px 8px' }}
>
{t['com.affine.editCollection.saveCollection']()}
</Button>
{node}
</>
);
};

View File

@@ -1,12 +1,13 @@
import {
type AllPageListConfig,
CreateCollectionModal,
EditCollectionModal,
type EditCollectionMode,
} from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { useCallback, useState } from 'react';
import { CreateCollectionModal } from './create-collection';
import {
type AllPageListConfig,
EditCollectionModal,
type EditCollectionMode,
} from './edit-collection/edit-collection';
export const useEditCollection = (config: AllPageListConfig) => {
const [data, setData] = useState<{
collection: Collection;

View File

@@ -29,7 +29,7 @@ export const Skeleton = ({
const style = {
width,
height,
...(_style || {}),
..._style,
};
return (