refactor(electron): tab title/icon update logic (#7675)
fix AF-1122 fix AF-1136
This commit is contained in:
@@ -7,21 +7,8 @@ import {
|
|||||||
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
||||||
import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls';
|
import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { DesktopStateSynchronizer } from '@affine/core/modules/workbench/services/desktop-state-synchronizer';
|
|
||||||
import type { WorkbenchMeta } from '@affine/electron-api';
|
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
import {
|
import { CloseIcon, PlusIcon, RightSidebarIcon } from '@blocksuite/icons/rc';
|
||||||
AllDocsIcon,
|
|
||||||
CloseIcon,
|
|
||||||
DeleteIcon,
|
|
||||||
EdgelessIcon,
|
|
||||||
PageIcon,
|
|
||||||
PlusIcon,
|
|
||||||
RightSidebarIcon,
|
|
||||||
TagIcon,
|
|
||||||
TodayIcon,
|
|
||||||
ViewLayersIcon,
|
|
||||||
} from '@blocksuite/icons/rc';
|
|
||||||
import {
|
import {
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
@@ -38,25 +25,14 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import { iconNameToIcon } from '../../workbench/constants';
|
||||||
|
import { DesktopStateSynchronizer } from '../../workbench/services/desktop-state-synchronizer';
|
||||||
import {
|
import {
|
||||||
AppTabsHeaderService,
|
AppTabsHeaderService,
|
||||||
type TabStatus,
|
type TabStatus,
|
||||||
} from '../services/app-tabs-header-service';
|
} from '../services/app-tabs-header-service';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
type ModuleName = NonNullable<WorkbenchMeta['views'][0]['moduleName']>;
|
|
||||||
|
|
||||||
const moduleNameToIcon = {
|
|
||||||
all: <AllDocsIcon />,
|
|
||||||
collection: <ViewLayersIcon />,
|
|
||||||
doc: <PageIcon />,
|
|
||||||
page: <PageIcon />,
|
|
||||||
edgeless: <EdgelessIcon />,
|
|
||||||
journal: <TodayIcon />,
|
|
||||||
tag: <TagIcon />,
|
|
||||||
trash: <DeleteIcon />,
|
|
||||||
} satisfies Record<ModuleName, ReactNode>;
|
|
||||||
|
|
||||||
const WorkbenchTab = ({
|
const WorkbenchTab = ({
|
||||||
workbench,
|
workbench,
|
||||||
active: tabActive,
|
active: tabActive,
|
||||||
@@ -66,6 +42,7 @@ const WorkbenchTab = ({
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
tabsLength: number;
|
tabsLength: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
useServiceOptional(DesktopStateSynchronizer);
|
||||||
const tabsHeaderService = useService(AppTabsHeaderService);
|
const tabsHeaderService = useService(AppTabsHeaderService);
|
||||||
const activeViewIndex = workbench.activeViewIndex ?? 0;
|
const activeViewIndex = workbench.activeViewIndex ?? 0;
|
||||||
const onContextMenu = useAsyncCallback(
|
const onContextMenu = useAsyncCallback(
|
||||||
@@ -115,7 +92,7 @@ const WorkbenchTab = ({
|
|||||||
>
|
>
|
||||||
<div className={styles.labelIcon}>
|
<div className={styles.labelIcon}>
|
||||||
{workbench.ready || !workbench.loaded ? (
|
{workbench.ready || !workbench.loaded ? (
|
||||||
moduleNameToIcon[view.moduleName ?? 'all']
|
iconNameToIcon[view.iconName ?? 'allDocs']
|
||||||
) : (
|
) : (
|
||||||
<Loading />
|
<Loading />
|
||||||
)}
|
)}
|
||||||
@@ -194,8 +171,6 @@ export const AppTabsHeader = ({
|
|||||||
await tabsHeaderService.onToggleRightSidebar();
|
await tabsHeaderService.onToggleRightSidebar();
|
||||||
}, [tabsHeaderService]);
|
}, [tabsHeaderService]);
|
||||||
|
|
||||||
useServiceOptional(DesktopStateSynchronizer);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mode === 'app') {
|
if (mode === 'app') {
|
||||||
apis?.ui.pingAppLayoutReady().catch(console.error);
|
apis?.ui.pingAppLayoutReady().catch(console.error);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export const tab = style({
|
|||||||
boxShadow: cssVar('shadow1'),
|
boxShadow: cssVar('shadow1'),
|
||||||
},
|
},
|
||||||
'&[data-pinned="false"]': {
|
'&[data-pinned="false"]': {
|
||||||
paddingRight: 8,
|
paddingRight: 20,
|
||||||
},
|
},
|
||||||
'&[data-pinned="true"]': {
|
'&[data-pinned="true"]': {
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
|||||||
23
packages/frontend/core/src/modules/workbench/constants.tsx
Normal file
23
packages/frontend/core/src/modules/workbench/constants.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import {
|
||||||
|
AllDocsIcon,
|
||||||
|
DeleteIcon,
|
||||||
|
EdgelessIcon,
|
||||||
|
PageIcon,
|
||||||
|
TagIcon,
|
||||||
|
TodayIcon,
|
||||||
|
ViewLayersIcon,
|
||||||
|
} from '@blocksuite/icons/rc';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const iconNameToIcon = {
|
||||||
|
allDocs: <AllDocsIcon />,
|
||||||
|
collection: <ViewLayersIcon />,
|
||||||
|
doc: <PageIcon />,
|
||||||
|
page: <PageIcon />,
|
||||||
|
edgeless: <EdgelessIcon />,
|
||||||
|
journal: <TodayIcon />,
|
||||||
|
tag: <TagIcon />,
|
||||||
|
trash: <DeleteIcon />,
|
||||||
|
} satisfies Record<string, ReactNode>;
|
||||||
|
|
||||||
|
export type ViewIconName = keyof typeof iconNameToIcon;
|
||||||
@@ -3,12 +3,15 @@ import type { Location, To } from 'history';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { createNavigableHistory } from '../../../utils/navigable-history';
|
import { createNavigableHistory } from '../../../utils/navigable-history';
|
||||||
|
import type { ViewIconName } from '../constants';
|
||||||
import { ViewScope } from '../scopes/view';
|
import { ViewScope } from '../scopes/view';
|
||||||
import { SidebarTab } from './sidebar-tab';
|
import { SidebarTab } from './sidebar-tab';
|
||||||
|
|
||||||
export class View extends Entity<{
|
export class View extends Entity<{
|
||||||
id: string;
|
id: string;
|
||||||
defaultLocation?: To | undefined;
|
defaultLocation?: To | undefined;
|
||||||
|
title?: string;
|
||||||
|
icon?: ViewIconName;
|
||||||
}> {
|
}> {
|
||||||
scope = this.framework.createScope(ViewScope, {
|
scope = this.framework.createScope(ViewScope, {
|
||||||
view: this as View,
|
view: this as View,
|
||||||
@@ -70,6 +73,10 @@ export class View extends Entity<{
|
|||||||
|
|
||||||
size$ = new LiveData(100);
|
size$ = new LiveData(100);
|
||||||
|
|
||||||
|
title$ = new LiveData(this.props.title ?? '');
|
||||||
|
|
||||||
|
icon$ = new LiveData(this.props.icon ?? 'allDocs');
|
||||||
|
|
||||||
push(path: To) {
|
push(path: To) {
|
||||||
this.history.push(path);
|
this.history.push(path);
|
||||||
}
|
}
|
||||||
@@ -105,4 +112,12 @@ export class View extends Entity<{
|
|||||||
activeSidebarTab(id: string | null) {
|
activeSidebarTab(id: string | null) {
|
||||||
this._activeSidebarTabId$.next(id);
|
this._activeSidebarTabId$.next(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTitle(title: string) {
|
||||||
|
this.title$.next(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIcon(icon: ViewIconName) {
|
||||||
|
this.icon$.next(icon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,16 @@ export { ViewScope } from './scopes/view';
|
|||||||
export { WorkbenchService } from './services/workbench';
|
export { WorkbenchService } from './services/workbench';
|
||||||
export { useIsActiveView } from './view/use-is-active-view';
|
export { useIsActiveView } from './view/use-is-active-view';
|
||||||
export { ViewBody, ViewHeader, ViewSidebarTab } from './view/view-islands';
|
export { ViewBody, ViewHeader, ViewSidebarTab } from './view/view-islands';
|
||||||
|
export { ViewIcon, ViewTitle } from './view/view-meta';
|
||||||
export { WorkbenchLink } from './view/workbench-link';
|
export { WorkbenchLink } from './view/workbench-link';
|
||||||
export { WorkbenchRoot } from './view/workbench-root';
|
export { WorkbenchRoot } from './view/workbench-root';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DocsService,
|
|
||||||
type Framework,
|
type Framework,
|
||||||
GlobalStateService,
|
GlobalStateService,
|
||||||
WorkspaceScope,
|
WorkspaceScope,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
import { WorkspacePropertiesAdapter } from '../properties';
|
|
||||||
import { SidebarTab } from './entities/sidebar-tab';
|
import { SidebarTab } from './entities/sidebar-tab';
|
||||||
import { View } from './entities/view';
|
import { View } from './entities/view';
|
||||||
import { Workbench } from './entities/workbench';
|
import { Workbench } from './entities/workbench';
|
||||||
@@ -52,9 +51,5 @@ export function configureDesktopWorkbenchModule(services: Framework) {
|
|||||||
.impl(WorkbenchDefaultState, DesktopWorkbenchDefaultState, [
|
.impl(WorkbenchDefaultState, DesktopWorkbenchDefaultState, [
|
||||||
GlobalStateService,
|
GlobalStateService,
|
||||||
])
|
])
|
||||||
.service(DesktopStateSynchronizer, [
|
.service(DesktopStateSynchronizer, [WorkbenchService]);
|
||||||
WorkbenchService,
|
|
||||||
WorkspacePropertiesAdapter,
|
|
||||||
DocsService,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,13 @@
|
|||||||
import {
|
import { apis, appInfo, events } from '@affine/electron-api';
|
||||||
apis,
|
import { LiveData, Service } from '@toeverything/infra';
|
||||||
appInfo,
|
|
||||||
events,
|
|
||||||
type WorkbenchViewMeta,
|
|
||||||
} from '@affine/electron-api';
|
|
||||||
import { I18n, type I18nKeys, i18nTime } from '@affine/i18n';
|
|
||||||
import type { DocsService } from '@toeverything/infra';
|
|
||||||
import { Service } from '@toeverything/infra';
|
|
||||||
import { combineLatest, filter, map, of, switchMap } from 'rxjs';
|
|
||||||
|
|
||||||
import { resolveRouteLinkMeta } from '../../navigation';
|
|
||||||
import type { RouteModulePath } from '../../navigation/utils';
|
|
||||||
import type { WorkspacePropertiesAdapter } from '../../properties';
|
|
||||||
import type { WorkbenchService } from '../../workbench';
|
import type { WorkbenchService } from '../../workbench';
|
||||||
|
|
||||||
const routeModuleToI18n = {
|
|
||||||
all: 'All pages',
|
|
||||||
collection: 'Collections',
|
|
||||||
tag: 'Tags',
|
|
||||||
trash: 'Trash',
|
|
||||||
} satisfies Record<RouteModulePath, I18nKeys>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize workbench state with state stored in main process
|
* Synchronize workbench state with state stored in main process
|
||||||
*/
|
*/
|
||||||
export class DesktopStateSynchronizer extends Service {
|
export class DesktopStateSynchronizer extends Service {
|
||||||
constructor(
|
constructor(private readonly workbenchService: WorkbenchService) {
|
||||||
private readonly workbenchService: WorkbenchService,
|
|
||||||
private readonly workspaceProperties: WorkspacePropertiesAdapter,
|
|
||||||
private readonly docsService: DocsService
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.startSync();
|
this.startSync();
|
||||||
}
|
}
|
||||||
@@ -80,70 +58,31 @@ export class DesktopStateSynchronizer extends Service {
|
|||||||
|
|
||||||
// sync workbench state with main process
|
// sync workbench state with main process
|
||||||
// also fill tab view meta with title & moduleName
|
// also fill tab view meta with title & moduleName
|
||||||
this.workspaceProperties.workspace.engine.rootDocState$
|
LiveData.computed(get => {
|
||||||
.pipe(
|
return get(workbench.views$).map(view => {
|
||||||
filter(v => v.ready),
|
const location = get(view.location$);
|
||||||
switchMap(() => workbench.views$),
|
return {
|
||||||
switchMap(views => {
|
id: view.id,
|
||||||
return combineLatest(
|
title: get(view.title$),
|
||||||
views.map(view =>
|
iconName: get(view.icon$),
|
||||||
view.location$.map(location => {
|
path: {
|
||||||
return {
|
pathname: location.pathname,
|
||||||
view,
|
search: location.search,
|
||||||
location,
|
hash: location.hash,
|
||||||
};
|
},
|
||||||
})
|
};
|
||||||
)
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
map(viewLocations => {
|
|
||||||
if (!apis || !appInfo?.viewId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewMetas = viewLocations.map(({ view, location }) => {
|
|
||||||
return {
|
|
||||||
id: view.id,
|
|
||||||
path: location,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return viewMetas.map(viewMeta => this.fillTabViewMeta(viewMeta));
|
|
||||||
}),
|
|
||||||
filter(v => !!v),
|
|
||||||
switchMap(viewMetas => {
|
|
||||||
return this.docsService.list.docs$.pipe(
|
|
||||||
switchMap(docs => {
|
|
||||||
return combineLatest(
|
|
||||||
viewMetas.map(vm => {
|
|
||||||
return (
|
|
||||||
docs
|
|
||||||
.find(doc => doc.id === vm.docId)
|
|
||||||
?.mode$.asObservable() ?? of('page')
|
|
||||||
).pipe(
|
|
||||||
map(mode => ({
|
|
||||||
...vm,
|
|
||||||
moduleName:
|
|
||||||
vm.moduleName === 'page' ? mode : vm.moduleName,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe(viewMetas => {
|
|
||||||
if (!apis || !appInfo?.viewId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
apis.ui
|
|
||||||
.updateWorkbenchMeta(appInfo.viewId, {
|
|
||||||
views: viewMetas,
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
});
|
});
|
||||||
|
}).subscribe(views => {
|
||||||
|
if (!apis || !appInfo?.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
apis.ui
|
||||||
|
.updateWorkbenchMeta(appInfo.viewId, {
|
||||||
|
views,
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
});
|
||||||
|
|
||||||
workbench.activeViewIndex$.subscribe(activeViewIndex => {
|
workbench.activeViewIndex$.subscribe(activeViewIndex => {
|
||||||
if (!apis || !appInfo?.viewId) {
|
if (!apis || !appInfo?.viewId) {
|
||||||
@@ -169,59 +108,4 @@ export class DesktopStateSynchronizer extends Service {
|
|||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private toFullUrl(
|
|
||||||
basename: string,
|
|
||||||
location: { hash?: string; pathname?: string; search?: string }
|
|
||||||
) {
|
|
||||||
return basename + location.pathname + location.search + location.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill tab view meta with title & moduleName
|
|
||||||
private fillTabViewMeta(
|
|
||||||
view: WorkbenchViewMeta
|
|
||||||
): WorkbenchViewMeta & { docId?: string } {
|
|
||||||
if (!view.path) {
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = this.toFullUrl(
|
|
||||||
this.workbenchService.workbench.basename$.value,
|
|
||||||
view.path
|
|
||||||
);
|
|
||||||
const linkMeta = resolveRouteLinkMeta(url);
|
|
||||||
|
|
||||||
if (!linkMeta) {
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
const journalString =
|
|
||||||
linkMeta.moduleName === 'doc'
|
|
||||||
? this.workspaceProperties.getJournalPageDateString(linkMeta.docId)
|
|
||||||
: undefined;
|
|
||||||
const isJournal = !!journalString;
|
|
||||||
|
|
||||||
const title = (() => {
|
|
||||||
// todo: resolve more module types like collections?
|
|
||||||
if (linkMeta?.moduleName === 'doc') {
|
|
||||||
if (journalString) {
|
|
||||||
return i18nTime(journalString, { absolute: { accuracy: 'day' } });
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
this.workspaceProperties.workspace.docCollection.meta.getDocMeta(
|
|
||||||
linkMeta.docId
|
|
||||||
)?.title || I18n['Untitled']()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return I18n[routeModuleToI18n[linkMeta.moduleName]]();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...view,
|
|
||||||
title: title,
|
|
||||||
docId: linkMeta.docId,
|
|
||||||
moduleName: isJournal ? 'journal' : linkMeta.moduleName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export function useBindWorkbenchToDesktopRouter(
|
|||||||
basename: string
|
basename: string
|
||||||
) {
|
) {
|
||||||
const browserLocation = useLocation();
|
const browserLocation = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newLocation = browserLocationToViewLocation(
|
const newLocation = browserLocationToViewLocation(
|
||||||
browserLocation,
|
browserLocation,
|
||||||
@@ -36,6 +37,7 @@ export function useBindWorkbenchToDesktopRouter(
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
workbench.open(newLocation);
|
workbench.open(newLocation);
|
||||||
}, [basename, browserLocation, workbench]);
|
}, [basename, browserLocation, workbench]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { useServiceOptional } from '@toeverything/infra';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import type { ViewIconName } from '../constants';
|
||||||
|
import { ViewService } from '../services/view';
|
||||||
|
|
||||||
|
export const ViewTitle = ({ title }: { title: string }) => {
|
||||||
|
const view = useServiceOptional(ViewService)?.view;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (view) {
|
||||||
|
view.setTitle(title);
|
||||||
|
}
|
||||||
|
}, [title, view]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewIcon = ({ icon }: { icon: ViewIconName }) => {
|
||||||
|
const view = useServiceOptional(ViewService)?.view;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (view) {
|
||||||
|
view.setIcon(icon);
|
||||||
|
}
|
||||||
|
}, [icon, view]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -6,6 +6,10 @@ import {
|
|||||||
VirtualizedCollectionList,
|
VirtualizedCollectionList,
|
||||||
} from '@affine/core/components/page-list';
|
} from '@affine/core/components/page-list';
|
||||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||||
|
import {
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
|
} from '@affine/core/modules/workbench/view/view-meta';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@@ -55,6 +59,8 @@ export const AllCollection = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={t['Collections']()} />
|
||||||
|
<ViewIcon icon="collection" />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<AllCollectionHeader
|
<AllCollectionHeader
|
||||||
showCreateNew={!hideHeaderCreateNew}
|
showCreateNew={!hideHeaderCreateNew}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { performanceRenderLogger } from '@affine/core/shared';
|
import { performanceRenderLogger } from '@affine/core/shared';
|
||||||
import type { Filter } from '@affine/env/filter';
|
import type { Filter } from '@affine/env/filter';
|
||||||
|
import { useI18n } from '@affine/i18n';
|
||||||
import {
|
import {
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
useService,
|
useService,
|
||||||
@@ -17,6 +18,8 @@ import {
|
|||||||
useIsActiveView,
|
useIsActiveView,
|
||||||
ViewBody,
|
ViewBody,
|
||||||
ViewHeader,
|
ViewHeader,
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
} from '../../../modules/workbench';
|
} from '../../../modules/workbench';
|
||||||
import { EmptyPageList } from '../page-list-empty';
|
import { EmptyPageList } from '../page-list-empty';
|
||||||
import * as styles from './all-page.css';
|
import * as styles from './all-page.css';
|
||||||
@@ -47,8 +50,12 @@ export const AllPage = () => {
|
|||||||
return;
|
return;
|
||||||
}, [globalContext, isActiveView]);
|
}, [globalContext, isActiveView]);
|
||||||
|
|
||||||
|
const t = useI18n();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={t['All pages']()} />
|
||||||
|
<ViewIcon icon="allDocs" />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<AllPageHeader
|
<AllPageHeader
|
||||||
showCreateNew={!hideHeaderCreateNew}
|
showCreateNew={!hideHeaderCreateNew}
|
||||||
|
|||||||
@@ -5,10 +5,16 @@ import {
|
|||||||
import { CreateOrEditTag } from '@affine/core/components/page-list/tags/create-tag';
|
import { CreateOrEditTag } from '@affine/core/components/page-list/tags/create-tag';
|
||||||
import type { TagMeta } from '@affine/core/components/page-list/types';
|
import type { TagMeta } from '@affine/core/components/page-list/types';
|
||||||
import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag';
|
import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag';
|
||||||
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { ViewBody, ViewHeader } from '../../../modules/workbench';
|
import {
|
||||||
|
ViewBody,
|
||||||
|
ViewHeader,
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
|
} from '../../../modules/workbench';
|
||||||
import { EmptyTagList } from '../page-list-empty';
|
import { EmptyTagList } from '../page-list-empty';
|
||||||
import * as styles from './all-tag.css';
|
import * as styles from './all-tag.css';
|
||||||
import { AllTagHeader } from './header';
|
import { AllTagHeader } from './header';
|
||||||
@@ -54,8 +60,12 @@ export const AllTag = () => {
|
|||||||
[setOpen, setSelectedTagIds]
|
[setOpen, setSelectedTagIds]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const t = useI18n();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={t['Tags']()} />
|
||||||
|
<ViewIcon icon="tag" />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<AllTagHeader />
|
<AllTagHeader />
|
||||||
</ViewHeader>
|
</ViewHeader>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import {
|
|||||||
useIsActiveView,
|
useIsActiveView,
|
||||||
ViewBody,
|
ViewBody,
|
||||||
ViewHeader,
|
ViewHeader,
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
} from '../../../modules/workbench';
|
} from '../../../modules/workbench';
|
||||||
import { WorkspaceSubPath } from '../../../shared';
|
import { WorkspaceSubPath } from '../../../shared';
|
||||||
import * as styles from './collection.css';
|
import * as styles from './collection.css';
|
||||||
@@ -155,6 +157,8 @@ const Placeholder = ({ collection }: { collection: Collection }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={collection.name} />
|
||||||
|
<ViewIcon icon="collection" />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -13,9 +13,16 @@ import { PageHeaderMenuButton } from '@affine/core/components/blocksuite/block-s
|
|||||||
import { DetailPageHeaderPresentButton } from '@affine/core/components/blocksuite/block-suite-header/present/detail-header-present-button';
|
import { DetailPageHeaderPresentButton } from '@affine/core/components/blocksuite/block-suite-header/present/detail-header-present-button';
|
||||||
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||||
import { useRegisterCopyLinkCommands } from '@affine/core/hooks/affine/use-register-copy-link-commands';
|
import { useRegisterCopyLinkCommands } from '@affine/core/hooks/affine/use-register-copy-link-commands';
|
||||||
|
import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
||||||
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
||||||
|
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
import { type Workspace } from '@toeverything/infra';
|
import {
|
||||||
|
DocService,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
type Workspace,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
@@ -68,8 +75,11 @@ export function JournalPageHeader({ page, workspace }: PageHeaderProps) {
|
|||||||
|
|
||||||
const { hideShare, hideToday } =
|
const { hideShare, hideToday } =
|
||||||
useDetailPageHeaderResponsive(containerWidth);
|
useDetailPageHeaderResponsive(containerWidth);
|
||||||
|
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
||||||
return (
|
return (
|
||||||
<Header className={styles.header} ref={containerRef}>
|
<Header className={styles.header} ref={containerRef}>
|
||||||
|
<ViewTitle title={title} />
|
||||||
|
<ViewIcon icon="journal" />
|
||||||
<EditorModeSwitch
|
<EditorModeSwitch
|
||||||
docCollection={workspace.docCollection}
|
docCollection={workspace.docCollection}
|
||||||
pageId={page?.id}
|
pageId={page?.id}
|
||||||
@@ -115,8 +125,15 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) {
|
|||||||
const onRename = useCallback(() => {
|
const onRename = useCallback(() => {
|
||||||
setTimeout(() => titleInputHandleRef.current?.triggerEdit());
|
setTimeout(() => titleInputHandleRef.current?.triggerEdit());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
||||||
|
const doc = useService(DocService).doc;
|
||||||
|
const currentMode = useLiveData(doc.mode$);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header className={styles.header} ref={containerRef}>
|
<Header className={styles.header} ref={containerRef}>
|
||||||
|
<ViewTitle title={title} />
|
||||||
|
<ViewIcon icon={currentMode ?? 'page'} />
|
||||||
<EditorModeSwitch
|
<EditorModeSwitch
|
||||||
docCollection={workspace.docCollection}
|
docCollection={workspace.docCollection}
|
||||||
pageId={page?.id}
|
pageId={page?.id}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
useIsActiveView,
|
useIsActiveView,
|
||||||
ViewBody,
|
ViewBody,
|
||||||
ViewHeader,
|
ViewHeader,
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
} from '@affine/core/modules/workbench';
|
} from '@affine/core/modules/workbench';
|
||||||
import {
|
import {
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
@@ -39,6 +41,7 @@ export const TagDetail = ({ tagId }: { tagId?: string }) => {
|
|||||||
}, [pageIds, pageMetas]);
|
}, [pageIds, pageMetas]);
|
||||||
|
|
||||||
const isActiveView = useIsActiveView();
|
const isActiveView = useIsActiveView();
|
||||||
|
const tagName = useLiveData(currentTag?.value$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActiveView && currentTag) {
|
if (isActiveView && currentTag) {
|
||||||
@@ -59,6 +62,8 @@ export const TagDetail = ({ tagId }: { tagId?: string }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={tagName ?? 'Untitled'} />
|
||||||
|
<ViewIcon icon="tag" />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<TagDetailHeader />
|
<TagDetailHeader />
|
||||||
</ViewHeader>
|
</ViewHeader>
|
||||||
|
|||||||
@@ -14,7 +14,13 @@ import {
|
|||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { useIsActiveView, ViewBody, ViewHeader } from '../../modules/workbench';
|
import {
|
||||||
|
useIsActiveView,
|
||||||
|
ViewBody,
|
||||||
|
ViewHeader,
|
||||||
|
ViewIcon,
|
||||||
|
ViewTitle,
|
||||||
|
} from '../../modules/workbench';
|
||||||
import { EmptyPageList } from './page-list-empty';
|
import { EmptyPageList } from './page-list-empty';
|
||||||
import * as styles from './trash-page.css';
|
import * as styles from './trash-page.css';
|
||||||
|
|
||||||
@@ -56,8 +62,11 @@ export const TrashPage = () => {
|
|||||||
return;
|
return;
|
||||||
}, [globalContextService.globalContext.isTrash, isActiveView]);
|
}, [globalContextService.globalContext.isTrash, isActiveView]);
|
||||||
|
|
||||||
|
const t = useI18n();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ViewTitle title={t['Trash']()} />
|
||||||
|
<ViewIcon icon={'trash'} />
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<TrashHeader />
|
<TrashHeader />
|
||||||
</ViewHeader>
|
</ViewHeader>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import '@affine/component/theme/theme.css';
|
|||||||
import { NotificationCenter } from '@affine/component';
|
import { NotificationCenter } from '@affine/component';
|
||||||
import { AffineContext } from '@affine/component/context';
|
import { AffineContext } from '@affine/component/context';
|
||||||
import { GlobalLoading } from '@affine/component/global-loading';
|
import { GlobalLoading } from '@affine/component/global-loading';
|
||||||
import { registerAffineCommand } from '@affine/core/commands';
|
|
||||||
import { AppFallback } from '@affine/core/components/affine/app-container';
|
import { AppFallback } from '@affine/core/components/affine/app-container';
|
||||||
import { configureCommonModules } from '@affine/core/modules';
|
import { configureCommonModules } from '@affine/core/modules';
|
||||||
import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header';
|
import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header';
|
||||||
@@ -22,7 +21,6 @@ import {
|
|||||||
import { Telemetry } from '@affine/core/telemetry';
|
import { Telemetry } from '@affine/core/telemetry';
|
||||||
import createEmotionCache from '@affine/core/utils/create-emotion-cache';
|
import createEmotionCache from '@affine/core/utils/create-emotion-cache';
|
||||||
import { createI18n, setUpLanguage } from '@affine/i18n';
|
import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||||
import { SettingsIcon } from '@blocksuite/icons/rc';
|
|
||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import {
|
import {
|
||||||
Framework,
|
Framework,
|
||||||
@@ -127,14 +125,3 @@ export function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAffineCommand({
|
|
||||||
id: 'affine:reload',
|
|
||||||
category: 'affine:general',
|
|
||||||
label: 'Reload current tab',
|
|
||||||
icon: <SettingsIcon />,
|
|
||||||
keyBinding: '$mod+R',
|
|
||||||
run() {
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
addTab,
|
addTab,
|
||||||
closeTab,
|
closeTab,
|
||||||
initAndShowMainWindow,
|
initAndShowMainWindow,
|
||||||
|
reloadView,
|
||||||
showDevTools,
|
showDevTools,
|
||||||
showMainWindow,
|
showMainWindow,
|
||||||
undoCloseTab,
|
undoCloseTab,
|
||||||
@@ -92,8 +93,13 @@ export function createApplicationMenu() {
|
|||||||
{
|
{
|
||||||
label: 'View',
|
label: 'View',
|
||||||
submenu: [
|
submenu: [
|
||||||
{ role: 'reload' },
|
{
|
||||||
{ role: 'forceReload' },
|
label: 'Reload',
|
||||||
|
accelerator: 'CommandOrControl+R',
|
||||||
|
click() {
|
||||||
|
reloadView().catch(console.error);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Open devtools',
|
label: 'Open devtools',
|
||||||
accelerator: isMac ? 'Cmd+Option+I' : 'Ctrl+Shift+I',
|
accelerator: isMac ? 'Cmd+Option+I' : 'Ctrl+Shift+I',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
showTab,
|
showTab,
|
||||||
showTabContextMenu,
|
showTabContextMenu,
|
||||||
updateWorkbenchMeta,
|
updateWorkbenchMeta,
|
||||||
|
updateWorkbenchViewMeta,
|
||||||
} from '../windows-manager';
|
} from '../windows-manager';
|
||||||
import { getChallengeResponse } from './challenge';
|
import { getChallengeResponse } from './challenge';
|
||||||
import { uiSubjects } from './subject';
|
import { uiSubjects } from './subject';
|
||||||
@@ -168,6 +169,12 @@ export const uiHandlers = {
|
|||||||
) => {
|
) => {
|
||||||
return updateWorkbenchMeta(...args);
|
return updateWorkbenchMeta(...args);
|
||||||
},
|
},
|
||||||
|
updateWorkbenchViewMeta: async (
|
||||||
|
_,
|
||||||
|
...args: Parameters<typeof updateWorkbenchViewMeta>
|
||||||
|
) => {
|
||||||
|
return updateWorkbenchViewMeta(...args);
|
||||||
|
},
|
||||||
getTabViewsMeta: async () => {
|
getTabViewsMeta: async () => {
|
||||||
return getTabViewsMeta();
|
return getTabViewsMeta();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const workbenchViewModuleSchema = z.enum([
|
export const workbenchViewIconNameSchema = z.enum([
|
||||||
'trash',
|
'trash',
|
||||||
'all',
|
'allDocs',
|
||||||
'collection',
|
'collection',
|
||||||
'tag',
|
'tag',
|
||||||
'doc', // refers to a doc whose mode is not yet being resolved
|
'doc', // refers to a doc whose mode is not yet being resolved
|
||||||
@@ -22,7 +22,7 @@ export const workbenchViewMetaSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
// todo: move title/module to cached stated
|
// todo: move title/module to cached stated
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
moduleName: workbenchViewModuleSchema.optional(),
|
iconName: workbenchViewIconNameSchema.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workbenchMetaSchema = z.object({
|
export const workbenchMetaSchema = z.object({
|
||||||
@@ -42,4 +42,4 @@ export const TabViewsMetaKey = 'tabViewsMetaSchema';
|
|||||||
export type TabViewsMetaSchema = z.infer<typeof tabViewsMetaSchema>;
|
export type TabViewsMetaSchema = z.infer<typeof tabViewsMetaSchema>;
|
||||||
export type WorkbenchMeta = z.infer<typeof workbenchMetaSchema>;
|
export type WorkbenchMeta = z.infer<typeof workbenchMetaSchema>;
|
||||||
export type WorkbenchViewMeta = z.infer<typeof workbenchViewMetaSchema>;
|
export type WorkbenchViewMeta = z.infer<typeof workbenchViewMetaSchema>;
|
||||||
export type WorkbenchViewModule = z.infer<typeof workbenchViewModuleSchema>;
|
export type WorkbenchViewModule = z.infer<typeof workbenchViewIconNameSchema>;
|
||||||
|
|||||||
@@ -258,15 +258,50 @@ export class WebContentViewsManager {
|
|||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const workbench = workbenches[index];
|
||||||
const newWorkbenches = workbenches.toSpliced(index, 1, {
|
const newWorkbenches = workbenches.toSpliced(index, 1, {
|
||||||
...workbenches[index],
|
...workbench,
|
||||||
...patch,
|
...patch,
|
||||||
|
views: patch.views
|
||||||
|
? patch.views.map(v => {
|
||||||
|
const existing = workbench.views.find(e => e.id === v.id);
|
||||||
|
return {
|
||||||
|
...existing,
|
||||||
|
...v,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: workbench.views,
|
||||||
});
|
});
|
||||||
this.patchTabViewsMeta({
|
this.patchTabViewsMeta({
|
||||||
workbenches: newWorkbenches,
|
workbenches: newWorkbenches,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateWorkbenchViewMeta = (
|
||||||
|
workbenchId: string,
|
||||||
|
viewId: string,
|
||||||
|
patch: Partial<WorkbenchViewMeta>
|
||||||
|
) => {
|
||||||
|
const workbench = this.tabViewsMeta.workbenches.find(
|
||||||
|
w => w.id === workbenchId
|
||||||
|
);
|
||||||
|
if (!workbench) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const views = workbench.views;
|
||||||
|
const viewIndex = views.findIndex(v => v.id === viewId);
|
||||||
|
if (viewIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newViews = views.toSpliced(viewIndex, 1, {
|
||||||
|
...views[viewIndex],
|
||||||
|
...patch,
|
||||||
|
});
|
||||||
|
this.updateWorkbenchMeta(workbenchId, {
|
||||||
|
views: newViews,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
isActiveTab = (id: string) => {
|
isActiveTab = (id: string) => {
|
||||||
return this.activeWorkbenchId === id;
|
return this.activeWorkbenchId === id;
|
||||||
};
|
};
|
||||||
@@ -345,10 +380,18 @@ export class WebContentViewsManager {
|
|||||||
|
|
||||||
addTab = async (option?: AddTabOption) => {
|
addTab = async (option?: AddTabOption) => {
|
||||||
if (!option) {
|
if (!option) {
|
||||||
|
const activeWorkbench = this.activeWorkbenchMeta;
|
||||||
|
const basename = (activeWorkbench?.basename ?? '') + '/';
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
basename: '/',
|
basename,
|
||||||
view: {
|
view: {
|
||||||
title: 'New Tab',
|
title: 'New Tab',
|
||||||
|
path: basename.startsWith('/workspace')
|
||||||
|
? {
|
||||||
|
pathname: 'all',
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -393,18 +436,18 @@ export class WebContentViewsManager {
|
|||||||
let view = this.tabViewsMap.get(id);
|
let view = this.tabViewsMap.get(id);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
view = await this.createAndAddView('app', id);
|
view = await this.createAndAddView('app', id);
|
||||||
const workbench = this.tabViewsMeta.workbenches.find(w => w.id === id);
|
}
|
||||||
const viewMeta = workbench?.views[workbench.activeViewIndex];
|
const workbench = this.tabViewsMeta.workbenches.find(w => w.id === id);
|
||||||
if (workbench && viewMeta) {
|
const viewMeta = workbench?.views[workbench.activeViewIndex];
|
||||||
const url = new URL(
|
if (workbench && viewMeta) {
|
||||||
workbench.basename + (viewMeta.path?.pathname ?? ''),
|
const url = new URL(
|
||||||
mainWindowOrigin
|
workbench.basename + (viewMeta.path?.pathname ?? ''),
|
||||||
);
|
mainWindowOrigin
|
||||||
url.hash = viewMeta.path?.hash ?? '';
|
);
|
||||||
url.search = viewMeta.path?.search ?? '';
|
url.hash = viewMeta.path?.hash ?? '';
|
||||||
logger.info(`loading tab ${id} at ${url.href}`);
|
url.search = viewMeta.path?.search ?? '';
|
||||||
view.webContents.loadURL(url.href).catch(logger.error);
|
logger.info(`loading tab ${id} at ${url.href}`);
|
||||||
}
|
view.webContents.loadURL(url.href).catch(logger.error);
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
};
|
};
|
||||||
@@ -835,6 +878,19 @@ export const updateWorkbenchMeta = (
|
|||||||
) => {
|
) => {
|
||||||
WebContentViewsManager.instance.updateWorkbenchMeta(id, meta);
|
WebContentViewsManager.instance.updateWorkbenchMeta(id, meta);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateWorkbenchViewMeta = (
|
||||||
|
workbenchId: string,
|
||||||
|
viewId: string,
|
||||||
|
meta: Partial<WorkbenchViewMeta>
|
||||||
|
) => {
|
||||||
|
WebContentViewsManager.instance.updateWorkbenchViewMeta(
|
||||||
|
workbenchId,
|
||||||
|
viewId,
|
||||||
|
meta
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const getWorkbenchMeta = (id: string) => {
|
export const getWorkbenchMeta = (id: string) => {
|
||||||
return TabViewsMetaState.value.workbenches.find(w => w.id === id);
|
return TabViewsMetaState.value.workbenches.find(w => w.id === id);
|
||||||
};
|
};
|
||||||
@@ -851,6 +907,13 @@ export const closeTab = WebContentViewsManager.instance.closeTab;
|
|||||||
export const undoCloseTab = WebContentViewsManager.instance.undoCloseTab;
|
export const undoCloseTab = WebContentViewsManager.instance.undoCloseTab;
|
||||||
export const activateView = WebContentViewsManager.instance.activateView;
|
export const activateView = WebContentViewsManager.instance.activateView;
|
||||||
|
|
||||||
|
export const reloadView = async () => {
|
||||||
|
const id = WebContentViewsManager.instance.activeWorkbenchId;
|
||||||
|
if (id) {
|
||||||
|
await WebContentViewsManager.instance.loadTab(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const onTabAction = (fn: (event: TabAction) => void) => {
|
export const onTabAction = (fn: (event: TabAction) => void) => {
|
||||||
const { unsubscribe } =
|
const { unsubscribe } =
|
||||||
WebContentViewsManager.instance.tabAction$.subscribe(fn);
|
WebContentViewsManager.instance.tabAction$.subscribe(fn);
|
||||||
|
|||||||
Reference in New Issue
Block a user