mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
fix: resolve cycle imports and prevent it by oxlint (#5103)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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';
|
||||
|
||||
@@ -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}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user