blob: 0df6284bf96f090ac51800bf5f6d49981b975f7d [file] [log] [blame]
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {Disposable} from '../base/disposable';
import {DetailsPanel, LegacyDetailsPanel, TabDescriptor} from '../public';
export interface ResolvedTab {
uri: string;
tab?: TabDescriptor;
}
/**
* Stores tab & current selection section registries.
* Keeps track of tab lifecycles.
*/
export class TabManager implements Disposable {
private _registry = new Map<string, TabDescriptor>();
private _defaultTabs = new Set<string>();
private _legacyDetailsPanelRegistry = new Set<LegacyDetailsPanel>();
private _detailsPanelRegistry = new Set<DetailsPanel>();
private _currentTabs = new Map<string, TabDescriptor>();
dispose(): void {
// Dispose of all tabs that are currently alive
for (const tab of this._currentTabs.values()) {
this.disposeTab(tab);
}
this._currentTabs.clear();
}
registerTab(desc: TabDescriptor): Disposable {
this._registry.set(desc.uri, desc);
return {
dispose: () => this._registry.delete(desc.uri),
};
}
addDefaultTab(uri: string): Disposable {
this._defaultTabs.add(uri);
return {
dispose: () => this._defaultTabs.delete(uri),
};
}
registerLegacyDetailsPanel(section: LegacyDetailsPanel): Disposable {
this._legacyDetailsPanelRegistry.add(section);
return {
dispose: () => this._legacyDetailsPanelRegistry.delete(section),
};
}
registerDetailsPanel(section: DetailsPanel): Disposable {
this._detailsPanelRegistry.add(section);
return {
dispose: () => this._detailsPanelRegistry.delete(section),
};
}
resolveTab(uri: string): TabDescriptor | undefined {
return this._registry.get(uri);
}
get tabs(): TabDescriptor[] {
return Array.from(this._registry.values());
}
get defaultTabs(): string[] {
return Array.from(this._defaultTabs);
}
get legacyDetailsPanels(): LegacyDetailsPanel[] {
return Array.from(this._legacyDetailsPanelRegistry);
}
get detailsPanels(): DetailsPanel[] {
return Array.from(this._detailsPanelRegistry);
}
/**
* Resolves a list of URIs to tabs and manages tab lifecycles.
* @param tabUris List of tabs.
* @return List of resolved tabs.
*/
resolveTabs(tabUris: string[]): ResolvedTab[] {
// Refresh the list of old tabs
const newTabs = new Map<string, TabDescriptor>();
const tabs: ResolvedTab[] = [];
tabUris.forEach((uri) => {
const newTab = this._registry.get(uri);
tabs.push({uri, tab: newTab});
if (newTab) {
newTabs.set(uri, newTab);
}
});
// Call onShow() on any new tabs.
for (const [uri, tab] of newTabs) {
const oldTab = this._currentTabs.get(uri);
if (!oldTab) {
this.initTab(tab);
}
}
// Call onHide() on any tabs that have been removed.
for (const [uri, tab] of this._currentTabs) {
const newTab = newTabs.get(uri);
if (!newTab) {
this.disposeTab(tab);
}
}
this._currentTabs = newTabs;
return tabs;
}
/**
* Call onShow() on this tab.
* @param tab The tab to initialize.
*/
private initTab(tab: TabDescriptor): void {
tab.onShow?.();
}
/**
* Call onHide() and maybe remove from registry if tab is ephemeral.
* @param tab The tab to dispose.
*/
private disposeTab(tab: TabDescriptor): void {
// Attempt to call onHide
tab.onHide?.();
// If ephemeral, also unregister the tab
if (tab.isEphemeral) {
this._registry.delete(tab.uri);
}
}
}