mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
fix(core): add favorite folder in menu, adjust empty-page new page button (#7730)
close AF-1150, AF-1128, AF-1131 - Replace favorite migration related copy - Adjust empty page's "New Page" button  - Add toggle favorite to folder menu  - Adjust `Button` - add `withoutHover` state - remove cursor: not-allowed when disabled
This commit is contained in:
@@ -107,7 +107,8 @@ const NameWorkspaceContent = ({
|
||||
confirmText={t['com.affine.nameWorkspace.button.create']()}
|
||||
confirmButtonOptions={{
|
||||
variant: 'primary',
|
||||
disabled: !workspaceName || loading,
|
||||
loading,
|
||||
disabled: !workspaceName,
|
||||
['data-testid' as string]: 'create-workspace-create-button',
|
||||
}}
|
||||
closeButtonOptions={{
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type FolderNode,
|
||||
OrganizeService,
|
||||
} from '@affine/core/modules/organize';
|
||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||
import { Unreachable } from '@affine/env/constant';
|
||||
@@ -43,6 +44,7 @@ import { ExplorerDocNode } from '../doc';
|
||||
import { ExplorerTagNode } from '../tag';
|
||||
import type { GenericExplorerNode } from '../types';
|
||||
import { FolderEmpty } from './empty';
|
||||
import { FavoriteFolderOperation } from './operations';
|
||||
|
||||
export const ExplorerFolderNode = ({
|
||||
nodeId,
|
||||
@@ -164,6 +166,7 @@ export const ExplorerFolderNodeFolder = ({
|
||||
const { docsService, workbenchService } = useServices({
|
||||
DocsService,
|
||||
WorkbenchService,
|
||||
CompatibleFavoriteItemsAdapter,
|
||||
});
|
||||
const openDocsSelector = useSelectDoc();
|
||||
const openTagsSelector = useSelectTag();
|
||||
@@ -710,6 +713,17 @@ export const ExplorerFolderNodeFolder = ({
|
||||
</MenuSub>
|
||||
),
|
||||
},
|
||||
|
||||
// favorite, only new favorite available
|
||||
...(runtimeConfig.enableNewFavorite && node.id
|
||||
? [
|
||||
{
|
||||
index: 200,
|
||||
view: <FavoriteFolderOperation id={node.id} />,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
index: 9999,
|
||||
view: <MenuSeparator key="menu-separator" />,
|
||||
@@ -731,7 +745,14 @@ export const ExplorerFolderNodeFolder = ({
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [handleAddToFolder, handleCreateSubfolder, handleDelete, handleNewDoc, t]);
|
||||
}, [
|
||||
handleAddToFolder,
|
||||
handleCreateSubfolder,
|
||||
handleDelete,
|
||||
handleNewDoc,
|
||||
node.id,
|
||||
t,
|
||||
]);
|
||||
|
||||
const finalOperations = useMemo(() => {
|
||||
if (additionalOperations) {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { MenuIcon, MenuItem } from '@affine/component';
|
||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const FavoriteFolderOperation = ({ id }: { id: string }) => {
|
||||
const t = useI18n();
|
||||
const compatibleFavoriteItemsAdapter = useService(
|
||||
CompatibleFavoriteItemsAdapter
|
||||
);
|
||||
|
||||
const favorite = useLiveData(
|
||||
useMemo(() => {
|
||||
return compatibleFavoriteItemsAdapter.isFavorite$(id, 'folder');
|
||||
}, [compatibleFavoriteItemsAdapter, id])
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
{favorite ? (
|
||||
<FavoritedIcon style={{ color: cssVar('primaryColor') }} />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)}
|
||||
</MenuIcon>
|
||||
}
|
||||
onClick={() => compatibleFavoriteItemsAdapter.toggle(id, 'folder')}
|
||||
>
|
||||
{favorite
|
||||
? t['com.affine.rootAppSidebar.organize.folder-rm-favorite']()
|
||||
: t['com.affine.rootAppSidebar.organize.folder-add-favorite']()}
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { LiveData, Service } from '@toeverything/infra';
|
||||
import { defaultsDeep } from 'lodash-es';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import type { FavoriteService } from '../../favorite';
|
||||
import type { FavoriteService, FavoriteSupportType } from '../../favorite';
|
||||
import {
|
||||
PagePropertyType,
|
||||
PageSystemPropertyId,
|
||||
@@ -305,6 +305,10 @@ export class FavoriteItemsAdapter extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
type CompatibleFavoriteSupportType =
|
||||
| WorkspaceFavoriteItem['type']
|
||||
| FavoriteSupportType;
|
||||
|
||||
/**
|
||||
* A service written for compatibility,with the same API as FavoriteItemsAdapter.
|
||||
* When `runtimeConfig.enableNewFavorite` is false, it operates FavoriteItemsAdapter,
|
||||
@@ -318,24 +322,29 @@ export class CompatibleFavoriteItemsAdapter extends Service {
|
||||
super();
|
||||
}
|
||||
|
||||
toggle(id: string, type: WorkspaceFavoriteItem['type']) {
|
||||
if (runtimeConfig.enableNewFavorite) {
|
||||
// old adapter only supports doc and collection
|
||||
private isNewType(type: CompatibleFavoriteSupportType) {
|
||||
return type !== 'doc' && type !== 'collection';
|
||||
}
|
||||
|
||||
toggle(id: string, type: CompatibleFavoriteSupportType) {
|
||||
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
|
||||
this.favoriteService.favoriteList.toggle(type, id);
|
||||
} else {
|
||||
this.favoriteItemsAdapter.toggle(id, type);
|
||||
}
|
||||
}
|
||||
|
||||
isFavorite$(id: string, type: WorkspaceFavoriteItem['type']) {
|
||||
if (runtimeConfig.enableNewFavorite) {
|
||||
isFavorite$(id: string, type: CompatibleFavoriteSupportType) {
|
||||
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
|
||||
return this.favoriteService.favoriteList.isFavorite$(type, id);
|
||||
} else {
|
||||
return this.favoriteItemsAdapter.isFavorite$(id, type);
|
||||
}
|
||||
}
|
||||
|
||||
isFavorite(id: string, type: WorkspaceFavoriteItem['type']) {
|
||||
if (runtimeConfig.enableNewFavorite) {
|
||||
isFavorite(id: string, type: CompatibleFavoriteSupportType) {
|
||||
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
|
||||
return this.favoriteService.favoriteList.isFavorite$(type, id).value;
|
||||
} else {
|
||||
return this.favoriteItemsAdapter.isFavorite(id, type);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export const pageListEmptyStyle = style({
|
||||
height: 'calc(100% - 52px)',
|
||||
@@ -23,3 +24,15 @@ export const emptyDescKbd = style([
|
||||
cursor: 'text',
|
||||
},
|
||||
]);
|
||||
|
||||
export const plusButton = style({
|
||||
borderWidth: 1,
|
||||
borderColor: cssVarV2('layer/border'),
|
||||
boxShadow: 'none',
|
||||
cursor: 'default',
|
||||
});
|
||||
export const descWrapper = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
});
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
import { Empty } from '@affine/component';
|
||||
import { Empty, IconButton } from '@affine/component';
|
||||
import { Trans, useI18n } from '@affine/i18n';
|
||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||
import type { DocCollection } from '@blocksuite/store';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils';
|
||||
import * as styles from './page-list-empty.css';
|
||||
|
||||
export const EmptyPageList = ({
|
||||
type,
|
||||
docCollection,
|
||||
heading,
|
||||
}: {
|
||||
type: 'all' | 'trash' | 'shared' | 'public';
|
||||
docCollection: DocCollection;
|
||||
heading?: ReactNode;
|
||||
}) => {
|
||||
const { createPage } = usePageHelper(docCollection);
|
||||
const t = useI18n();
|
||||
const onCreatePage = useCallback(() => {
|
||||
createPage?.();
|
||||
}, [createPage]);
|
||||
|
||||
const getEmptyDescription = () => {
|
||||
if (type === 'all') {
|
||||
const createNewPageButton = (
|
||||
<button className={styles.emptyDescButton} onClick={onCreatePage}>
|
||||
{t['New Page']()}
|
||||
</button>
|
||||
<IconButton
|
||||
withoutHover
|
||||
className={styles.plusButton}
|
||||
variant="solid"
|
||||
icon={<PlusIcon />}
|
||||
/>
|
||||
);
|
||||
if (environment.isDesktop) {
|
||||
const shortcut = environment.isMacOs ? '⌘ + N' : 'Ctrl + N';
|
||||
@@ -61,7 +58,9 @@ export const EmptyPageList = ({
|
||||
{heading && <div>{heading}</div>}
|
||||
<Empty
|
||||
title={t['com.affine.emptyDesc']()}
|
||||
description={getEmptyDescription()}
|
||||
description={
|
||||
<span className={styles.descWrapper}>{getEmptyDescription()}</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user