mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
feat(core): drop doc onto split view (#9487)
fix AF-2068, AF-2069, AF-1175, AF-2061, AF-2079, AF-2034, AF-2080, AF-1960, AF-2081 1. replace `dnd-kit` with `@atlaskit/pragmatic-drag-and-drop` 2. allow creating split views by drag & drop the following a. WorkbenchLinks (route links), like journals, trash, all docs b. doc refs c. tags/collection 3. style adjustments to split view 4. remove split view's feature flag and make it GA for electron https://github.com/user-attachments/assets/6a3e4a25-faa2-4215-8eb0-983f44db6e8c
This commit is contained in:
@@ -23,7 +23,9 @@ export const BlocksuiteEditorJournalDocTitle = ({ page }: { page: Blocks }) => {
|
||||
<div className="doc-title-container" data-testid="journal-title">
|
||||
<span data-testid="date">{localizedJournalDate}</span>
|
||||
{isTodayJournal ? (
|
||||
<span className={styles.titleTodayTag}>{t['com.affine.today']()}</span>
|
||||
<span className={styles.titleTodayTag} data-testid="date-today-label">
|
||||
{t['com.affine.today']()}
|
||||
</span>
|
||||
) : (
|
||||
<span className={styles.titleDayTag}>{day}</span>
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,9 @@ export const JournalTodayButton = () => {
|
||||
const journalHelper = useJournalRouteHelper();
|
||||
|
||||
const onToday = useCallback(() => {
|
||||
journalHelper.openToday();
|
||||
journalHelper.openToday({
|
||||
replaceHistory: true,
|
||||
});
|
||||
}, [journalHelper]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
JournalService,
|
||||
type MaybeDate,
|
||||
} from '@affine/core/modules/journal';
|
||||
import type { WorkbenchOpenOptions } from '@affine/core/modules/workbench/entities/workbench';
|
||||
import { i18nTime } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import { useService, useServices } from '@toeverything/infra';
|
||||
@@ -60,9 +61,9 @@ export const useJournalRouteHelper = () => {
|
||||
* open journal by date, create one if not exist
|
||||
*/
|
||||
const openJournal = useCallback(
|
||||
(maybeDate: MaybeDate, newTab?: boolean) => {
|
||||
(maybeDate: MaybeDate, options?: WorkbenchOpenOptions) => {
|
||||
const page = getJournalByDate(maybeDate);
|
||||
workbench.openDoc(page.id, { at: newTab ? 'new-tab' : 'active' });
|
||||
workbench.openDoc(page.id, options);
|
||||
track.$.navigationPanel.journal.navigate({
|
||||
to: 'journal',
|
||||
});
|
||||
@@ -75,9 +76,9 @@ export const useJournalRouteHelper = () => {
|
||||
* open today's journal
|
||||
*/
|
||||
const openToday = useCallback(
|
||||
(newTab?: boolean) => {
|
||||
(options: WorkbenchOpenOptions) => {
|
||||
const date = dayjs().format(JOURNAL_DATE_FORMAT);
|
||||
return openJournal(date, newTab);
|
||||
return openJournal(date, options);
|
||||
},
|
||||
[openJournal]
|
||||
);
|
||||
|
||||
@@ -180,7 +180,8 @@ export const PageListItem = (props: PageListItemProps) => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
[props.draggable, props.pageId]
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[props.draggable, props.pageId, props.selectable]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
CompatibleFavoriteItemsAdapter,
|
||||
FavoriteService,
|
||||
} from '@affine/core/modules/favorite';
|
||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
||||
@@ -65,19 +64,15 @@ export const PageOperationCell = ({
|
||||
}: PageOperationCellProps) => {
|
||||
const t = useI18n();
|
||||
const {
|
||||
featureFlagService,
|
||||
workspaceService,
|
||||
compatibleFavoriteItemsAdapter: favAdapter,
|
||||
workbenchService,
|
||||
} = useServices({
|
||||
FeatureFlagService,
|
||||
WorkspaceService,
|
||||
CompatibleFavoriteItemsAdapter,
|
||||
WorkbenchService,
|
||||
});
|
||||
const enableSplitView = useLiveData(
|
||||
featureFlagService.flags.enable_multi_view.$
|
||||
);
|
||||
|
||||
const currentWorkspace = workspaceService.workspace;
|
||||
const favourite = useLiveData(favAdapter.isFavorite$(page.id, 'doc'));
|
||||
const workbench = workbenchService.workbench;
|
||||
@@ -194,7 +189,7 @@ export const PageOperationCell = ({
|
||||
<MenuItem onClick={onOpenInNewTab} prefixIcon={<OpenInNewIcon />}>
|
||||
{t['com.affine.workbench.tab.page-menu-open']()}
|
||||
</MenuItem>
|
||||
{BUILD_CONFIG.isElectron && enableSplitView ? (
|
||||
{BUILD_CONFIG.isElectron ? (
|
||||
<MenuItem onClick={onOpenInSplitView} prefixIcon={<SplitViewIcon />}>
|
||||
{t['com.affine.workbench.split-view.page-menu-open']()}
|
||||
</MenuItem>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { shallowEqual } from '@affine/component';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import type { Tag } from '@affine/env/filter';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
@@ -34,7 +35,6 @@ import type {
|
||||
TagListItemProps,
|
||||
TagMeta,
|
||||
} from './types';
|
||||
import { shallowEqual } from './utils';
|
||||
|
||||
export const ItemGroupHeader = memo(function ItemGroupHeader<
|
||||
T extends ListItem,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { shallowEqual } from '@affine/component';
|
||||
import { DEFAULT_SORT_KEY } from '@affine/env/constant';
|
||||
import { atom } from 'jotai';
|
||||
import { selectAtom } from 'jotai/utils';
|
||||
@@ -10,7 +11,6 @@ import type {
|
||||
MetaRecord,
|
||||
VirtualizedListProps,
|
||||
} from './types';
|
||||
import { shallowEqual } from './utils';
|
||||
|
||||
// for ease of use in the component tree
|
||||
// note: must use selectAtom to access this atom for efficiency
|
||||
|
||||
@@ -56,38 +56,3 @@ export const betweenDaysAgo = (
|
||||
): boolean => {
|
||||
return !withinDaysAgo(date, days0) && withinDaysAgo(date, days1);
|
||||
};
|
||||
|
||||
// credit: https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/shallowEqual.js
|
||||
export function shallowEqual(objA: any, objB: any) {
|
||||
if (Object.is(objA, objB)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof objA !== 'object' ||
|
||||
objA === null ||
|
||||
typeof objB !== 'object' ||
|
||||
objB === null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keysA = Object.keys(objA);
|
||||
const keysB = Object.keys(objB);
|
||||
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for A's keys different from B.
|
||||
for (const key of keysA) {
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(objB, key) ||
|
||||
!Object.is(objA[key], objB[key])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Menu, MenuItem, usePromptModal } from '@affine/component';
|
||||
import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info';
|
||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import type { Collection } from '@affine/env/filter';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
@@ -36,21 +35,16 @@ export const CollectionOperations = ({
|
||||
const {
|
||||
collectionService: service,
|
||||
workbenchService,
|
||||
featureFlagService,
|
||||
workspaceDialogService,
|
||||
} = useServices({
|
||||
CollectionService,
|
||||
WorkbenchService,
|
||||
FeatureFlagService,
|
||||
WorkspaceDialogService,
|
||||
});
|
||||
const deleteInfo = useDeleteCollectionInfo();
|
||||
const workbench = workbenchService.workbench;
|
||||
const t = useI18n();
|
||||
const { openPromptModal } = usePromptModal();
|
||||
const enableMultiView = useLiveData(
|
||||
featureFlagService.flags.enable_multi_view.$
|
||||
);
|
||||
|
||||
const showEditName = useCallback(() => {
|
||||
// use openRenameModal if it is in the sidebar collection list
|
||||
@@ -150,7 +144,7 @@ export const CollectionOperations = ({
|
||||
name: t['com.affine.workbench.tab.page-menu-open'](),
|
||||
click: openCollectionNewTab,
|
||||
},
|
||||
...(BUILD_CONFIG.isElectron && enableMultiView
|
||||
...(BUILD_CONFIG.isElectron
|
||||
? [
|
||||
{
|
||||
icon: <SplitViewIcon />,
|
||||
@@ -172,7 +166,6 @@ export const CollectionOperations = ({
|
||||
},
|
||||
],
|
||||
[
|
||||
enableMultiView,
|
||||
t,
|
||||
showEditName,
|
||||
showEdit,
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
|
||||
import {
|
||||
useJournalInfoHelper,
|
||||
useJournalRouteHelper,
|
||||
} from '@affine/core/components/hooks/use-journal';
|
||||
import { MenuItem } from '@affine/core/modules/app-sidebar/views';
|
||||
import { MenuLinkItem } from '@affine/core/modules/app-sidebar/views';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import { JournalService } from '@affine/core/modules/journal';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { isNewTabTrigger } from '@affine/core/utils';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { TodayIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { type MouseEvent } from 'react';
|
||||
|
||||
export const AppSidebarJournalButton = () => {
|
||||
const t = useI18n();
|
||||
const docDisplayMetaService = useService(DocDisplayMetaService);
|
||||
const journalService = useService(JournalService);
|
||||
const workbench = useService(WorkbenchService).workbench;
|
||||
const location = useLiveData(workbench.location$);
|
||||
const { openToday } = useJournalRouteHelper();
|
||||
const maybeDocId = location.pathname.split('/')[1];
|
||||
const { isJournal } = useJournalInfoHelper(maybeDocId);
|
||||
|
||||
const handleOpenToday = useCatchEventCallback(
|
||||
(e: MouseEvent) => {
|
||||
openToday(isNewTabTrigger(e));
|
||||
},
|
||||
[openToday]
|
||||
);
|
||||
const isJournal = !!useLiveData(journalService.journalDate$(maybeDocId));
|
||||
|
||||
const JournalIcon = useLiveData(
|
||||
docDisplayMetaService.icon$(maybeDocId, {
|
||||
@@ -36,14 +23,13 @@ export const AppSidebarJournalButton = () => {
|
||||
const Icon = isJournal ? JournalIcon : TodayIcon;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
<MenuLinkItem
|
||||
data-testid="slider-bar-journals-button"
|
||||
active={isJournal}
|
||||
onClick={handleOpenToday}
|
||||
onAuxClick={handleOpenToday}
|
||||
to={'/journals'}
|
||||
icon={<Icon />}
|
||||
>
|
||||
{t['com.affine.journal.app-sidebar-title']()}
|
||||
</MenuItem>
|
||||
</MenuLinkItem>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user