Skip to content

Commit

Permalink
fix: layout state restore not work (#3941)
Browse files Browse the repository at this point in the history
  • Loading branch information
bytemain authored Sep 23, 2024
1 parent e50e715 commit 6e2ecab
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,8 @@ export class FileAndContentUpdateTimeContribution extends WithEventBus {
// additional edits for file-participants
this._traceConfig = !!this.preferenceService.get<boolean>(TRACE_LOG_FLAG);
this.addDispose(
this.preferenceService.onPreferenceChanged((e) => {
if (e.preferenceName === TRACE_LOG_FLAG) {
this._traceConfig = !!e.newValue;
}
this.preferenceService.onSpecificPreferenceChange(TRACE_LOG_FLAG, (e) => {
this._traceConfig = !!e.newValue;
}),
);
}
Expand Down
19 changes: 11 additions & 8 deletions packages/core-browser/src/bootstrap/app.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,25 @@ export type IAppRenderer = (app: (props: any) => JSX.Element) => void;

const defaultAppRender =
(dom: HTMLElement): IAppRenderer =>
(IDEApp: (props) => JSX.Element) => {
(IDEApp: (props: any) => JSX.Element) => {
const root = ReactDom.createRoot(dom);
root.render(<IDEApp />);
};

const debugLogger = getDebugLogger();

export function renderClientApp(app: IClientApp, container: HTMLElement | IAppRenderer) {
const Layout = app.config.layoutComponent || DefaultLayout;
const overlayComponents = app.browserModules
.filter((module) => module.isOverlay)
.map((module) => {
if (!module.component) {
getDebugLogger().warn('Overlay module does not have component', module);
return () => <></>;
.filter((mod) => mod.isOverlay)
.map((mod) => {
if (!mod.component) {
debugLogger.warn('Overlay module does not have component', mod);
return null;
}
return module.component;
});
return mod.component;
})
.filter(Boolean) as React.ComponentType[];

const IdeApp = (props) => <App {...props} app={app} main={Layout} overlays={overlayComponents} />;

Expand Down
22 changes: 20 additions & 2 deletions packages/core-browser/src/components/layout/default-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BoxPanel } from './box-panel';
import { SplitPanel } from './split-panel';

export interface ILayoutConfigCache {
[key: string]: { size: number; currentId: string };
[key: string]: { size?: number; currentId?: string };
}

export const getStorageValue = () => {
Expand All @@ -26,7 +26,7 @@ export const getStorageValue = () => {
} catch (err) {}

return {
layout: savedLayout,
layout: fixLayout(savedLayout),
colors: savedColors,
};
};
Expand Down Expand Up @@ -75,3 +75,21 @@ export function ToolbarActionBasedLayout(
</BoxPanel>
);
}

/**
* if layout has currentId, but its size is zero
* we cannot acknowledge the currentId, so we should remove it
*/
export function fixLayout(layout: ILayoutConfigCache) {
const newLayout = { ...layout };
for (const key in layout) {
if (!layout[key]) {
continue;
}

if (!layout[key].size) {
newLayout[key].currentId = '';
}
}
return newLayout;
}
6 changes: 2 additions & 4 deletions packages/core-browser/src/layout/layout-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ export class LayoutState {
await this.preferenceService.ready;
this.saveLayoutWithWorkspace = this.preferenceService.get<boolean>('view.saveLayoutWithWorkspace') || false;
this.disposableCollection.push(
this.preferenceService.onPreferenceChanged((e) => {
if (e.preferenceName === 'view.saveLayoutWithWorkspace') {
this.saveLayoutWithWorkspace = e.newValue;
}
this.preferenceService.onSpecificPreferenceChange('view.saveLayoutWithWorkspace', (e) => {
this.saveLayoutWithWorkspace = e.newValue;
}),
);
}
Expand Down
15 changes: 9 additions & 6 deletions packages/main-layout/src/browser/accordion/accordion.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import debounce from 'lodash/debounce';
import isEqual from 'lodash/isEqual';
import { action, makeObservable, observable, runInAction } from 'mobx';

import { Autowired, Injectable } from '@opensumi/di';
Expand Down Expand Up @@ -83,7 +84,7 @@ export class AccordionService extends WithEventBus {
@Autowired(IContextKeyService)
private contextKeyService: IContextKeyService;

@Autowired()
@Autowired(LayoutState)
private layoutState: LayoutState;

@Autowired(IProgressService)
Expand Down Expand Up @@ -119,16 +120,18 @@ export class AccordionService extends WithEventBus {
private topViewKey: IContextKey<string>;
private scopedCtxKeyService: IScopedContextKeyService;

private didChangeViewTitleEmitter: Emitter<AccordionViewChangeEvent> = new Emitter<AccordionViewChangeEvent>();
private didChangeViewTitleEmitter: Emitter<AccordionViewChangeEvent> = this.registerDispose(
new Emitter<AccordionViewChangeEvent>(),
);
public onDidChangeViewTitle: Event<AccordionViewChangeEvent> = this.didChangeViewTitleEmitter.event;

private beforeAppendViewEmitter = new Emitter<string>();
private beforeAppendViewEmitter = this.registerDispose(new Emitter<string>());
public onBeforeAppendViewEvent = this.beforeAppendViewEmitter.event;

private afterAppendViewEmitter = new Emitter<string>();
private afterAppendViewEmitter = this.registerDispose(new Emitter<string>());
public onAfterAppendViewEvent = this.afterAppendViewEmitter.event;

private afterDisposeViewEmitter = new Emitter<string>();
private afterDisposeViewEmitter = this.registerDispose(new Emitter<string>());
public onAfterDisposeViewEvent = this.afterDisposeViewEmitter.event;

constructor(public containerId: string, private noRestore?: boolean) {
Expand Down Expand Up @@ -222,7 +225,7 @@ export class AccordionService extends WithEventBus {
const defaultState: { [containerId: string]: SectionState } = {};
this.visibleViews.forEach((view) => (defaultState[view.id] = { collapsed: false, hidden: false }));
const restoredState = this.layoutState.getState(LAYOUT_STATE.getContainerSpace(this.containerId), defaultState);
if (restoredState !== defaultState) {
if (!isEqual(restoredState, defaultState)) {
this.state = restoredState;
}
this.popViewKeyIfOnlyOneViewVisible();
Expand Down
7 changes: 2 additions & 5 deletions packages/main-layout/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IMainLayoutService, IViewsRegistry, MainLayoutContribution } from '../c
import { AccordionServiceFactory } from './accordion/accordion.service';
import { LayoutService } from './layout.service';
import { MainLayoutModuleContribution } from './main-layout.contribution';
import { TabbarServiceFactory } from './tabbar/tabbar.service';
import { TabbarServiceFactory, TabbarServiceFactoryFn } from './tabbar/tabbar.service';
import { ViewsRegistry } from './views-registry';

@Injectable()
Expand All @@ -23,10 +23,7 @@ export class MainLayoutModule extends BrowserModule {
},
{
token: TabbarServiceFactory,
useFactory: (injector: Injector) => (location: string) => {
const manager: IMainLayoutService = injector.get(IMainLayoutService);
return manager.getTabbarService(location);
},
useFactory: TabbarServiceFactoryFn,
},
{
token: AccordionServiceFactory,
Expand Down
101 changes: 56 additions & 45 deletions packages/main-layout/src/browser/layout.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
WithEventBus,
slotRendererRegistry,
} from '@opensumi/ide-core-browser';
import { fixLayout } from '@opensumi/ide-core-browser/lib/components';
import { LAYOUT_STATE, LayoutState } from '@opensumi/ide-core-browser/lib/layout/layout-state';
import { ComponentRegistryInfo } from '@opensumi/ide-core-browser/lib/layout/layout.interface';
import {
Expand All @@ -41,6 +42,22 @@ import { AccordionService } from './accordion/accordion.service';
import { TabbarService } from './tabbar/tabbar.service';
import { TabBarHandler } from './tabbar-handler';

const defaultLayoutState = {
[SlotLocation.left]: {
currentId: undefined,
size: undefined,
},
[SlotLocation.right]: {
// 依照下面的恢复逻辑,这里设置为 `''` 时,就不会恢复右侧的 TabBar 的状态(即选中相应的 viewContainer)
currentId: '',
size: undefined,
},
[SlotLocation.bottom]: {
currentId: undefined,
size: undefined,
},
};

@Injectable()
export class LayoutService extends WithEventBus implements IMainLayoutService {
@Autowired(INJECTOR_TOKEN)
Expand Down Expand Up @@ -154,21 +171,7 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
}

restoreTabbarService = async (service: TabbarService) => {
this.state = this.layoutState.getState(LAYOUT_STATE.MAIN, {
[SlotLocation.left]: {
currentId: undefined,
size: undefined,
},
[SlotLocation.right]: {
// 依照下面的恢复逻辑,这里设置为 `''` 时,就不会恢复右侧的 TabBar 的状态(即选中相应的 viewContainer)
currentId: '',
size: undefined,
},
[SlotLocation.bottom]: {
currentId: undefined,
size: undefined,
},
});
this.state = fixLayout(this.layoutState.getState(LAYOUT_STATE.MAIN, defaultLayoutState));

const { currentId, size } = this.state[service.location] || {};
service.prevSize = size;
Expand Down Expand Up @@ -264,30 +267,32 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
getTabbarService(location: string) {
const service = this.tabbarServices.get(location) || this.injector.get(TabbarService, [location]);
if (!this.tabbarServices.get(location)) {
service.onCurrentChange(({ currentId }) => {
this.storeState(service, currentId);
// onView 也支持监听 containerId
this.eventBus.fire(new ExtensionActivateEvent({ topic: 'onView', data: currentId }));
if (currentId && SUPPORT_ACCORDION_LOCATION.has(service.location)) {
const accordionService = this.getAccordionService(currentId);
accordionService?.tryUpdateResize();
accordionService?.expandedViews.forEach((view) => {
this.eventBus.fire(new ExtensionActivateEvent({ topic: 'onView', data: view.id }));
});
}
});
service.addDispose(
service.onCurrentChange(({ currentId }) => {
this.storeState(service, currentId);
// onView 也支持监听 containerId
this.eventBus.fire(new ExtensionActivateEvent({ topic: 'onView', data: currentId }));
if (currentId && SUPPORT_ACCORDION_LOCATION.has(service.location)) {
const accordionService = this.getAccordionService(currentId);
accordionService?.tryUpdateResize();
accordionService?.expandedViews.forEach((view) => {
this.eventBus.fire(new ExtensionActivateEvent({ topic: 'onView', data: view.id }));
});
}
}),
);
service.viewReady.promise
.then(() => service.restoreState())
.then(() => this.restoreTabbarService(service))
.catch((err) => {
this.logger.error(`[TabbarService:${location}] restore state error`, err);
});
const debouncedStoreState = debounce(() => this.storeState(service, service.currentContainerId), 100);
service.onSizeChange(debouncedStoreState);
service.addDispose(service.onSizeChange(debouncedStoreState));
if (location === SlotLocation.bottom) {
// use this getter's side effect to set bottomExpanded contextKey
const debouncedUpdate = debounce(() => void this.bottomExpanded, 100);
service.onSizeChange(() => debouncedUpdate);
service.addDispose(service.onSizeChange(() => debouncedUpdate));
}
this.tabbarServices.set(location, service);
}
Expand Down Expand Up @@ -372,17 +377,21 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
}
const service = this.getAccordionService(options.containerId);
// 如果 append view 时尝试注册 holdTabbarComponent
service.onBeforeAppendViewEvent(() => {
this.tryUpdateTabbar(options.containerId);
});
service.onAfterDisposeViewEvent(() => {
// 如果没有其他 view ,则 remove 掉 container
if (service.views.length === 0) {
this.disposeContainer(options.containerId);
// 重新注册到 holdTabbarComponent ,以便再次 append 时能注册传上去
this.holdTabbarComponent.set(options.containerId, { views, options, side });
}
});
service.addDispose(
service.onBeforeAppendViewEvent(() => {
this.tryUpdateTabbar(options.containerId);
}),
);
service.addDispose(
service.onAfterDisposeViewEvent(() => {
// 如果没有其他 view ,则 remove 掉 container
if (service.views.length === 0) {
this.disposeContainer(options.containerId);
// 重新注册到 holdTabbarComponent ,以便再次 append 时能注册传上去
this.holdTabbarComponent.set(options.containerId, { views, options, side });
}
}),
);
return options.containerId;
}
const tabbarService = this.getTabbarService(side);
Expand Down Expand Up @@ -443,11 +452,13 @@ export class LayoutService extends WithEventBus implements IMainLayoutService {
const viewReady = new Deferred<void>();
const accordionService = this.getAccordionService(containerId);
if (!accordionService.visibleViews.find((view) => view.id === viewId)) {
accordionService.onAfterAppendViewEvent((id) => {
if (id === viewId) {
viewReady.resolve();
}
});
accordionService.addDispose(
accordionService.onAfterAppendViewEvent((id) => {
if (id === viewId) {
viewReady.resolve();
}
}),
);
} else {
viewReady.resolve();
}
Expand Down
7 changes: 6 additions & 1 deletion packages/main-layout/src/browser/tabbar/tabbar.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import debounce from 'lodash/debounce';
import { action, makeObservable, observable, reaction, runInAction } from 'mobx';

import { Autowired, Injectable } from '@opensumi/di';
import { Autowired, Injectable, Injector } from '@opensumi/di';
import {
CommandRegistry,
ComponentRegistryInfo,
Expand Down Expand Up @@ -47,6 +47,11 @@ import { IMainLayoutService, SUPPORT_ACCORDION_LOCATION, TabBarRegistrationEvent
import { EXPAND_BOTTOM_PANEL, RETRACT_BOTTOM_PANEL, TOGGLE_BOTTOM_PANEL_COMMAND } from '../main-layout.contribution';

export const TabbarServiceFactory = Symbol('TabbarServiceFactory');
export const TabbarServiceFactoryFn = (injector: Injector) => (location: string) => {
const manager: IMainLayoutService = injector.get(IMainLayoutService);
return manager.getTabbarService(location);
};

export interface TabState {
hidden: boolean;
// 排序位置,数字越小优先级越高
Expand Down

0 comments on commit 6e2ecab

Please sign in to comment.