mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
fix(core): cleanup layout when switch page (#3794)
This commit is contained in:
@@ -106,6 +106,11 @@ const deleteLayoutAtom = atom<null, [string], void>(null, (_, set, id) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// clean up plugin windows when switching to other pages
|
||||||
|
rootStore.sub(currentPageAtom, () => {
|
||||||
|
rootStore.set(contentLayoutAtom, 'editor');
|
||||||
|
});
|
||||||
|
|
||||||
// module -> importName -> updater[]
|
// module -> importName -> updater[]
|
||||||
export const _rootImportsMap = new Map<string, Map<string, any>>();
|
export const _rootImportsMap = new Map<string, Map<string, any>>();
|
||||||
const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
|
const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
|
||||||
|
|||||||
@@ -17,7 +17,15 @@ import { contentLayoutAtom, rootStore } from '@toeverything/infra/atom';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import type { CSSProperties, ReactElement } from 'react';
|
import type { CSSProperties, ReactElement } from 'react';
|
||||||
import { memo, startTransition, Suspense, useCallback, useMemo } from 'react';
|
import {
|
||||||
|
memo,
|
||||||
|
startTransition,
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||||
|
|
||||||
import { pageSettingFamily } from '../atoms';
|
import { pageSettingFamily } from '../atoms';
|
||||||
@@ -134,27 +142,42 @@ interface PluginContentAdapterProps {
|
|||||||
|
|
||||||
const PluginContentAdapter = memo<PluginContentAdapterProps>(
|
const PluginContentAdapter = memo<PluginContentAdapterProps>(
|
||||||
function PluginContentAdapter({ windowItem, pluginName }) {
|
function PluginContentAdapter({ windowItem, pluginName }) {
|
||||||
return (
|
const rootRef = useRef<HTMLDivElement | null>(null);
|
||||||
<div
|
useEffect(() => {
|
||||||
className={pluginContainer}
|
const abortController = new AbortController();
|
||||||
ref={useCallback(
|
const root = rootRef.current;
|
||||||
(ref: HTMLDivElement | null) => {
|
if (root) {
|
||||||
if (ref) {
|
startTransition(() => {
|
||||||
startTransition(() => {
|
if (abortController.signal.aborted) {
|
||||||
const div = document.createElement('div');
|
return;
|
||||||
const cleanup = windowItem(div);
|
}
|
||||||
ref.appendChild(div);
|
const div = document.createElement('div');
|
||||||
addCleanup(pluginName, () => {
|
const cleanup = windowItem(div);
|
||||||
cleanup();
|
root.appendChild(div);
|
||||||
ref.removeChild(div);
|
if (abortController.signal.aborted) {
|
||||||
});
|
cleanup();
|
||||||
|
root.removeChild(div);
|
||||||
|
} else {
|
||||||
|
const cl = () => {
|
||||||
|
cleanup();
|
||||||
|
root.removeChild(div);
|
||||||
|
};
|
||||||
|
const dispose = addCleanup(pluginName, cl);
|
||||||
|
abortController.signal.addEventListener('abort', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
dispose();
|
||||||
|
cl();
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
[pluginName, windowItem]
|
});
|
||||||
)}
|
return () => {
|
||||||
/>
|
abortController.abort();
|
||||||
);
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}, [pluginName, windowItem]);
|
||||||
|
return <div className={pluginContainer} ref={rootRef} />;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { CallbackMap } from '@affine/sdk/entry';
|
import type { CallbackMap } from '@affine/sdk/entry';
|
||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
import { atom } from 'jotai/vanilla';
|
import { atom } from 'jotai/vanilla';
|
||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
@@ -14,13 +15,21 @@ export const builtinPluginPaths = new Set([
|
|||||||
'/plugins/outline',
|
'/plugins/outline',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pluginCleanupMap = new Map<string, (() => void)[]>();
|
const pluginCleanupMap = new Map<string, Set<() => void>>();
|
||||||
|
|
||||||
export function addCleanup(pluginName: string, cleanup: () => void) {
|
export function addCleanup(
|
||||||
|
pluginName: string,
|
||||||
|
cleanup: () => void
|
||||||
|
): () => void {
|
||||||
if (!pluginCleanupMap.has(pluginName)) {
|
if (!pluginCleanupMap.has(pluginName)) {
|
||||||
pluginCleanupMap.set(pluginName, []);
|
pluginCleanupMap.set(pluginName, new Set());
|
||||||
}
|
}
|
||||||
pluginCleanupMap.get(pluginName)?.push(cleanup);
|
const cleanupSet = pluginCleanupMap.get(pluginName);
|
||||||
|
assertExists(cleanupSet);
|
||||||
|
cleanupSet.add(cleanup);
|
||||||
|
return () => {
|
||||||
|
cleanupSet.delete(cleanup);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function invokeCleanup(pluginName: string) {
|
export function invokeCleanup(pluginName: string) {
|
||||||
|
|||||||
@@ -1,42 +1,30 @@
|
|||||||
import { Tooltip } from '@affine/component';
|
import { Tooltip } from '@affine/component';
|
||||||
import { deleteLayoutAtom, pushLayoutAtom } from '@affine/sdk/entry';
|
import {
|
||||||
|
currentPageAtom,
|
||||||
|
deleteLayoutAtom,
|
||||||
|
pushLayoutAtom,
|
||||||
|
} from '@affine/sdk/entry';
|
||||||
import { TOCNotesPanel } from '@blocksuite/blocks';
|
import { TOCNotesPanel } from '@blocksuite/blocks';
|
||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { RightSidebarIcon } from '@blocksuite/icons';
|
import { RightSidebarIcon } from '@blocksuite/icons';
|
||||||
import type { Page } from '@blocksuite/store';
|
|
||||||
import { IconButton } from '@toeverything/components/button';
|
import { IconButton } from '@toeverything/components/button';
|
||||||
import { useAtom, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import type { ComponentType, PropsWithChildren } from 'react';
|
import type { ComponentType, PropsWithChildren } from 'react';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { blocksuiteRootAtom } from './atom';
|
|
||||||
|
|
||||||
const Outline = () => {
|
const Outline = () => {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const tocPanelRef = useRef<TOCNotesPanel | null>(null);
|
const tocPanelRef = useRef<TOCNotesPanel | null>(null);
|
||||||
const [blocksuite] = useAtom(blocksuiteRootAtom);
|
const currentPage = useAtomValue(currentPageAtom);
|
||||||
|
|
||||||
if (!tocPanelRef.current) {
|
if (!tocPanelRef.current) {
|
||||||
tocPanelRef.current = new TOCNotesPanel();
|
tocPanelRef.current = new TOCNotesPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocksuite?.page !== tocPanelRef.current?.page) {
|
if (currentPage !== tocPanelRef.current?.page) {
|
||||||
(tocPanelRef.current as TOCNotesPanel).page = blocksuite?.page as Page;
|
(tocPanelRef.current as TOCNotesPanel).page = currentPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ref.current || !tocPanelRef.current) return;
|
|
||||||
|
|
||||||
const container = ref.current;
|
|
||||||
const tocPanel = tocPanelRef.current as TOCNotesPanel;
|
|
||||||
|
|
||||||
container.appendChild(tocPanel);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
container.removeChild(tocPanel);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`outline-wrapper`}
|
className={`outline-wrapper`}
|
||||||
@@ -44,7 +32,12 @@ const Outline = () => {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
borderLeft: `1px solid var(--affine-border-color)`,
|
borderLeft: `1px solid var(--affine-border-color)`,
|
||||||
}}
|
}}
|
||||||
ref={ref}
|
ref={useCallback((container: HTMLDivElement | null) => {
|
||||||
|
if (container) {
|
||||||
|
assertExists(tocPanelRef.current);
|
||||||
|
container.appendChild(tocPanelRef.current);
|
||||||
|
}
|
||||||
|
}, [])}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { atom } from 'jotai';
|
|
||||||
|
|
||||||
export const blocksuiteRootAtom = atom(() =>
|
|
||||||
document.querySelector('block-suite-root')
|
|
||||||
);
|
|
||||||
Reference in New Issue
Block a user