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
  ![CleanShot 2024-08-05 at 15.16.06@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/1cf7d75a-a33a-4eec-9dc1-87d50d9526f1.png)
- Add toggle favorite to folder menu
  ![CleanShot 2024-08-05 at 15.17.50@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/af6116b5-47d1-49a6-9660-41c0d7cd8fd3.png)
- Adjust `Button`
  - add `withoutHover` state
  - remove cursor: not-allowed when disabled
This commit is contained in:
CatsJuice
2024-08-05 09:15:16 +00:00
parent 73a6723d15
commit 6d253c0600
9 changed files with 121 additions and 29 deletions

View File

@@ -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={{

View File

@@ -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) {

View File

@@ -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>
);
};

View File

@@ -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);

View File

@@ -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,
});

View File

@@ -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>
);