feat: allow collections to be added to favorites (#6288)
This commit is contained in:
@@ -40,13 +40,12 @@ export const root = style({
|
|||||||
paddingLeft: '4px',
|
paddingLeft: '4px',
|
||||||
paddingRight: '4px',
|
paddingRight: '4px',
|
||||||
},
|
},
|
||||||
'&[data-type="collection-list-item"][data-collapsible="false"][data-active="true"],&[data-type="reference-page"][data-collapsible="false"][data-active="true"], &[data-type="reference-page"][data-collapsible="false"]:hover, &[data-type="collection-list-item"][data-collapsible="false"]:hover':
|
'&[data-collapsible="false"]:is([data-active="true"], :hover)': {
|
||||||
{
|
width: 'calc(100% + 8px)',
|
||||||
width: 'calc(100% + 8px)',
|
transform: 'translateX(-8px)',
|
||||||
transform: 'translateX(-8px)',
|
paddingLeft: '20px',
|
||||||
paddingLeft: '20px',
|
paddingRight: '12px',
|
||||||
paddingRight: '12px',
|
},
|
||||||
},
|
|
||||||
[`${linkItemRoot}:first-of-type &`]: {
|
[`${linkItemRoot}:first-of-type &`]: {
|
||||||
marginTop: '0px',
|
marginTop: '0px',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { Button, FlexWrapper, Menu } from '@affine/component';
|
import { Button, FlexWrapper, Menu } from '@affine/component';
|
||||||
import type {
|
import type { Collection, Filter, PropertiesMeta } from '@affine/env/filter';
|
||||||
Collection,
|
|
||||||
DeleteCollectionInfo,
|
|
||||||
Filter,
|
|
||||||
PropertiesMeta,
|
|
||||||
} from '@affine/env/filter';
|
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { FilterIcon } from '@blocksuite/icons';
|
import { FilterIcon } from '@blocksuite/icons';
|
||||||
|
|
||||||
@@ -16,20 +11,14 @@ import type { AllPageListConfig } from './edit-collection/edit-collection';
|
|||||||
export const CollectionPageListOperationsMenu = ({
|
export const CollectionPageListOperationsMenu = ({
|
||||||
collection,
|
collection,
|
||||||
allPageListConfig,
|
allPageListConfig,
|
||||||
userInfo,
|
|
||||||
}: {
|
}: {
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
allPageListConfig: AllPageListConfig;
|
allPageListConfig: AllPageListConfig;
|
||||||
userInfo: DeleteCollectionInfo;
|
|
||||||
}) => {
|
}) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
return (
|
return (
|
||||||
<FlexWrapper alignItems="center">
|
<FlexWrapper alignItems="center">
|
||||||
<CollectionOperations
|
<CollectionOperations collection={collection} config={allPageListConfig}>
|
||||||
info={userInfo}
|
|
||||||
collection={collection}
|
|
||||||
config={allPageListConfig}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
className={styles.filterMenuTrigger}
|
className={styles.filterMenuTrigger}
|
||||||
type="default"
|
type="default"
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import type { MenuItemProps } from '@affine/component';
|
import type { MenuItemProps } from '@affine/component';
|
||||||
import { Menu, MenuIcon, MenuItem } from '@affine/component';
|
import { Menu, MenuIcon, MenuItem } from '@affine/component';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
|
import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info';
|
||||||
import { Workbench } from '@affine/core/modules/workbench';
|
import { Workbench } from '@affine/core/modules/workbench';
|
||||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
import { FavoriteItemsAdapter } from '@affine/core/modules/workspace';
|
||||||
|
import type { Collection } from '@affine/env/filter';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import {
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
EditIcon,
|
EditIcon,
|
||||||
|
FavoritedIcon,
|
||||||
|
FavoriteIcon,
|
||||||
FilterIcon,
|
FilterIcon,
|
||||||
SplitViewIcon,
|
SplitViewIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
@@ -25,15 +29,14 @@ import {
|
|||||||
export const CollectionOperations = ({
|
export const CollectionOperations = ({
|
||||||
collection,
|
collection,
|
||||||
config,
|
config,
|
||||||
info,
|
|
||||||
openRenameModal,
|
openRenameModal,
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<{
|
}: PropsWithChildren<{
|
||||||
info: DeleteCollectionInfo;
|
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
config: AllPageListConfig;
|
config: AllPageListConfig;
|
||||||
openRenameModal?: () => void;
|
openRenameModal?: () => void;
|
||||||
}>) => {
|
}>) => {
|
||||||
|
const deleteInfo = useDeleteCollectionInfo();
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const service = useService(CollectionService);
|
const service = useService(CollectionService);
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
@@ -76,6 +79,19 @@ export const CollectionOperations = ({
|
|||||||
workbench.openCollection(collection.id, { at: 'tail' });
|
workbench.openCollection(collection.id, { at: 'tail' });
|
||||||
}, [collection.id, workbench]);
|
}, [collection.id, workbench]);
|
||||||
|
|
||||||
|
const favAdapter = useService(FavoriteItemsAdapter);
|
||||||
|
|
||||||
|
const onToggleFavoritePage = useCallback(() => {
|
||||||
|
favAdapter.toggle(collection.id, 'collection');
|
||||||
|
}, [favAdapter, collection.id]);
|
||||||
|
|
||||||
|
const favorite = useLiveData(
|
||||||
|
useMemo(
|
||||||
|
() => favAdapter.isFavorite$(collection.id, 'collection'),
|
||||||
|
[collection.id, favAdapter]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const actions = useMemo<
|
const actions = useMemo<
|
||||||
Array<
|
Array<
|
||||||
| {
|
| {
|
||||||
@@ -109,6 +125,21 @@ export const CollectionOperations = ({
|
|||||||
name: t['com.affine.collection.menu.edit'](),
|
name: t['com.affine.collection.menu.edit'](),
|
||||||
click: showEdit,
|
click: showEdit,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<MenuIcon>
|
||||||
|
{favorite ? (
|
||||||
|
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
|
||||||
|
) : (
|
||||||
|
<FavoriteIcon />
|
||||||
|
)}
|
||||||
|
</MenuIcon>
|
||||||
|
),
|
||||||
|
name: favorite
|
||||||
|
? t['com.affine.favoritePageOperation.remove']()
|
||||||
|
: t['com.affine.favoritePageOperation.add'](),
|
||||||
|
click: onToggleFavoritePage,
|
||||||
|
},
|
||||||
...(appSettings.enableMultiView
|
...(appSettings.enableMultiView
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -133,7 +164,7 @@ export const CollectionOperations = ({
|
|||||||
),
|
),
|
||||||
name: t['Delete'](),
|
name: t['Delete'](),
|
||||||
click: () => {
|
click: () => {
|
||||||
service.deleteCollection(info, collection.id);
|
service.deleteCollection(deleteInfo, collection.id);
|
||||||
},
|
},
|
||||||
type: 'danger',
|
type: 'danger',
|
||||||
},
|
},
|
||||||
@@ -142,10 +173,12 @@ export const CollectionOperations = ({
|
|||||||
t,
|
t,
|
||||||
showEditName,
|
showEditName,
|
||||||
showEdit,
|
showEdit,
|
||||||
|
favorite,
|
||||||
|
onToggleFavoritePage,
|
||||||
appSettings.enableMultiView,
|
appSettings.enableMultiView,
|
||||||
openCollectionSplitView,
|
openCollectionSplitView,
|
||||||
service,
|
service,
|
||||||
info,
|
deleteInfo,
|
||||||
collection.id,
|
collection.id,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import {
|
|||||||
} from '@affine/core/components/page-list';
|
} from '@affine/core/components/page-list';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { FavoriteItemsAdapter } from '@affine/core/modules/workspace';
|
import { FavoriteItemsAdapter } from '@affine/core/modules/workspace';
|
||||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
import type { Collection } from '@affine/env/filter';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons';
|
import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons';
|
||||||
import type { DocCollection, DocMeta } from '@blocksuite/store';
|
import type { DocCollection } from '@blocksuite/store';
|
||||||
import { useDroppable } from '@dnd-kit/core';
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -27,17 +27,16 @@ import type { CollectionsListProps } from '../index';
|
|||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
const CollectionRenderer = ({
|
export const CollectionSidebarNavItem = ({
|
||||||
collection,
|
collection,
|
||||||
pages,
|
|
||||||
docCollection,
|
docCollection,
|
||||||
info,
|
className,
|
||||||
}: {
|
}: {
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
pages: DocMeta[];
|
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
info: DeleteCollectionInfo;
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const pages = useBlockSuiteDocMeta(docCollection);
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
@@ -112,7 +111,12 @@ const CollectionRenderer = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible.Root open={!collapsed} ref={setNodeRef}>
|
<Collapsible.Root
|
||||||
|
open={!collapsed}
|
||||||
|
ref={setNodeRef}
|
||||||
|
data-testid="collection-"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
<SidebarMenuLinkItem
|
<SidebarMenuLinkItem
|
||||||
data-testid="collection-item"
|
data-testid="collection-item"
|
||||||
data-type="collection-list-item"
|
data-type="collection-list-item"
|
||||||
@@ -127,7 +131,6 @@ const CollectionRenderer = ({
|
|||||||
style={{ display: 'flex', alignItems: 'center' }}
|
style={{ display: 'flex', alignItems: 'center' }}
|
||||||
>
|
>
|
||||||
<CollectionOperations
|
<CollectionOperations
|
||||||
info={info}
|
|
||||||
collection={collection}
|
collection={collection}
|
||||||
config={config}
|
config={config}
|
||||||
openRenameModal={handleOpen}
|
openRenameModal={handleOpen}
|
||||||
@@ -174,12 +177,11 @@ const CollectionRenderer = ({
|
|||||||
};
|
};
|
||||||
export const CollectionsList = ({
|
export const CollectionsList = ({
|
||||||
docCollection: workspace,
|
docCollection: workspace,
|
||||||
info,
|
|
||||||
onCreate,
|
onCreate,
|
||||||
}: CollectionsListProps) => {
|
}: CollectionsListProps) => {
|
||||||
const metas = useBlockSuiteDocMeta(workspace);
|
|
||||||
const collections = useLiveData(useService(CollectionService).collections$);
|
const collections = useLiveData(useService(CollectionService).collections$);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
if (collections.length === 0) {
|
if (collections.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.emptyCollectionWrapper}>
|
<div className={styles.emptyCollectionWrapper}>
|
||||||
@@ -204,11 +206,9 @@ export const CollectionsList = ({
|
|||||||
<div data-testid="collections" className={styles.wrapper}>
|
<div data-testid="collections" className={styles.wrapper}>
|
||||||
{collections.map(view => {
|
{collections.map(view => {
|
||||||
return (
|
return (
|
||||||
<CollectionRenderer
|
<CollectionSidebarNavItem
|
||||||
info={info}
|
|
||||||
key={view.id}
|
key={view.id}
|
||||||
collection={view}
|
collection={view}
|
||||||
pages={metas}
|
|
||||||
docCollection={workspace}
|
docCollection={workspace}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { FavoriteItemsAdapter } from '@affine/core/modules/workspace';
|
import { FavoriteItemsAdapter } from '@affine/core/modules/workspace';
|
||||||
import type { DocMeta } from '@blocksuite/store';
|
import type { DocMeta } from '@blocksuite/store';
|
||||||
import { useDroppable } from '@dnd-kit/core';
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
@@ -6,6 +7,7 @@ import { useLiveData, useService } from '@toeverything/infra';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { getDropItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
import { getDropItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
||||||
|
import { CollectionSidebarNavItem } from '../collections';
|
||||||
import type { FavoriteListProps } from '../index';
|
import type { FavoriteListProps } from '../index';
|
||||||
import EmptyItem from './empty-item';
|
import EmptyItem from './empty-item';
|
||||||
import { FavouritePage } from './favourite-page';
|
import { FavouritePage } from './favourite-page';
|
||||||
@@ -18,18 +20,10 @@ export const FavoriteList = ({
|
|||||||
}: FavoriteListProps) => {
|
}: FavoriteListProps) => {
|
||||||
const metas = useBlockSuiteDocMeta(workspace);
|
const metas = useBlockSuiteDocMeta(workspace);
|
||||||
const favAdapter = useService(FavoriteItemsAdapter);
|
const favAdapter = useService(FavoriteItemsAdapter);
|
||||||
|
const collections = useLiveData(useService(CollectionService).collections$);
|
||||||
const dropItemId = getDropItemId('favorites');
|
const dropItemId = getDropItemId('favorites');
|
||||||
|
|
||||||
const favourites = useLiveData(
|
const docMetaMapping = useMemo(
|
||||||
favAdapter.favorites$.map(favourites =>
|
|
||||||
favourites.filter(fav => {
|
|
||||||
const meta = metas.find(m => m.id === fav.id);
|
|
||||||
return meta && !meta.trash;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const metaMapping = useMemo(
|
|
||||||
() =>
|
() =>
|
||||||
metas.reduce(
|
metas.reduce(
|
||||||
(acc, meta) => {
|
(acc, meta) => {
|
||||||
@@ -41,6 +35,17 @@ export const FavoriteList = ({
|
|||||||
[metas]
|
[metas]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const favourites = useLiveData(
|
||||||
|
favAdapter.orderedFavorites$.map(favs => {
|
||||||
|
return favs.filter(fav => {
|
||||||
|
if (fav.type === 'doc') {
|
||||||
|
return !!docMetaMapping[fav.id] && !docMetaMapping[fav.id].trash;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const { setNodeRef, isOver } = useDroppable({
|
const { setNodeRef, isOver } = useDroppable({
|
||||||
id: dropItemId,
|
id: dropItemId,
|
||||||
});
|
});
|
||||||
@@ -52,17 +57,32 @@ export const FavoriteList = ({
|
|||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
data-over={isOver}
|
data-over={isOver}
|
||||||
>
|
>
|
||||||
{favourites.map((pageMeta, index) => {
|
{favourites.map(item => {
|
||||||
return (
|
if (item.type === 'collection') {
|
||||||
<FavouritePage
|
const collection = collections.find(c => c.id === item.id);
|
||||||
key={`${pageMeta}-${index}`}
|
if (collection) {
|
||||||
metaMapping={metaMapping}
|
return (
|
||||||
pageId={pageMeta.id}
|
<CollectionSidebarNavItem
|
||||||
// memo?
|
key={item.id}
|
||||||
parentIds={emptyPageIdSet}
|
className={styles.favItemWrapper}
|
||||||
docCollection={workspace}
|
docCollection={workspace}
|
||||||
/>
|
collection={collection}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (item.type === 'doc' && !docMetaMapping[item.id].trash) {
|
||||||
|
return (
|
||||||
|
<FavouritePage
|
||||||
|
key={item.id}
|
||||||
|
metaMapping={docMetaMapping}
|
||||||
|
pageId={item.id}
|
||||||
|
// memo?
|
||||||
|
parentIds={emptyPageIdSet}
|
||||||
|
docCollection={workspace}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
})}
|
})}
|
||||||
{favourites.length === 0 && <EmptyItem />}
|
{favourites.length === 0 && <EmptyItem />}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { DeleteCollectionInfo } from '@affine/env/filter';
|
|
||||||
import type { DocCollection } from '@blocksuite/store';
|
import type { DocCollection } from '@blocksuite/store';
|
||||||
|
|
||||||
export type FavoriteListProps = {
|
export type FavoriteListProps = {
|
||||||
@@ -7,6 +6,5 @@ export type FavoriteListProps = {
|
|||||||
|
|
||||||
export type CollectionsListProps = {
|
export type CollectionsListProps = {
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
info: DeleteCollectionInfo;
|
|
||||||
onCreate?: () => void;
|
onCreate?: () => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import type { HTMLAttributes, ReactElement } from 'react';
|
|||||||
import { forwardRef, useCallback, useEffect } from 'react';
|
import { forwardRef, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
|
||||||
import { useDeleteCollectionInfo } from '../../hooks/affine/use-delete-collection-info';
|
|
||||||
import { getDropItemId } from '../../hooks/affine/use-sidebar-drag';
|
import { getDropItemId } from '../../hooks/affine/use-sidebar-drag';
|
||||||
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
|
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
|
||||||
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
|
||||||
@@ -163,7 +162,6 @@ export const RootAppSidebar = ({
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}, [docCollection.id, collection, navigateHelper, open]);
|
}, [docCollection.id, collection, navigateHelper, open]);
|
||||||
const userInfo = useDeleteCollectionInfo();
|
|
||||||
|
|
||||||
const allPageActive = currentPath === '/all';
|
const allPageActive = currentPath === '/all';
|
||||||
|
|
||||||
@@ -226,7 +224,6 @@ export const RootAppSidebar = ({
|
|||||||
</CategoryDivider>
|
</CategoryDivider>
|
||||||
<CollectionsList
|
<CollectionsList
|
||||||
docCollection={docCollection}
|
docCollection={docCollection}
|
||||||
info={userInfo}
|
|
||||||
onCreate={handleCreateCollection}
|
onCreate={handleCreateCollection}
|
||||||
/>
|
/>
|
||||||
<CategoryDivider label={t['com.affine.rootAppSidebar.others']()} />
|
<CategoryDivider label={t['com.affine.rootAppSidebar.others']()} />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { DeleteCollectionInfo } from '@affine/env/filter';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { useSession } from './use-current-user';
|
import { useSession } from './use-current-user';
|
||||||
@@ -5,7 +6,7 @@ import { useSession } from './use-current-user';
|
|||||||
export const useDeleteCollectionInfo = () => {
|
export const useDeleteCollectionInfo = () => {
|
||||||
const { user } = useSession();
|
const { user } = useSession();
|
||||||
|
|
||||||
return useMemo(
|
return useMemo<DeleteCollectionInfo | null>(
|
||||||
() => (user ? { userName: user.name, userId: user.id } : null),
|
() => (user ? { userName: user.name, userId: user.id } : null),
|
||||||
[user]
|
[user]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -168,8 +168,22 @@ export class FavoriteItemsAdapter {
|
|||||||
this.getItems().filter(i => i.value)
|
this.getItems().filter(i => i.value)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
orderedFavorites$ = this.adapter.properties$.map(() => {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
return this.sorter.getOrderedItems().filter(item => {
|
||||||
|
const key = FavoriteItemsAdapter.getFavItemKey(item.id, item.type);
|
||||||
|
if (seen.has(key) || !item.value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
seen.add(key);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
getItems() {
|
getItems() {
|
||||||
return Object.values(this.adapter.favorites ?? {});
|
return Object.entries(this.adapter.favorites ?? {})
|
||||||
|
.filter(([k]) => k.includes(':'))
|
||||||
|
.map(([, v]) => v);
|
||||||
}
|
}
|
||||||
|
|
||||||
get favorites() {
|
get favorites() {
|
||||||
|
|||||||
Reference in New Issue
Block a user