functional-tab: Split ‘name’ into ‘page’ and ‘label’.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2024-12-02 13:53:08 -08:00
parent 105e7e93a1
commit 467e7b11c5
7 changed files with 39 additions and 28 deletions

View File

@@ -20,9 +20,11 @@ export type ServerConfig = {
}; };
export type TabRole = "server" | "function"; export type TabRole = "server" | "function";
export type TabPage = "Settings" | "About";
export type TabData = { export type TabData = {
role: TabRole; role: TabRole;
name: string; page?: TabPage;
label: string;
index: number; index: number;
}; };

View File

@@ -428,7 +428,7 @@ ${error}`,
AppMenu.setMenu(properties); AppMenu.setMenu(properties);
if (properties.activeTabIndex !== undefined) { if (properties.activeTabIndex !== undefined) {
const activeTab = properties.tabs[properties.activeTabIndex]; const activeTab = properties.tabs[properties.activeTabIndex];
mainWindow.setTitle(`Zulip - ${activeTab.name}`); mainWindow.setTitle(`Zulip - ${activeTab.label}`);
} }
}); });

View File

@@ -318,12 +318,12 @@ function getWindowSubmenu(
if (tab === undefined) continue; if (tab === undefined) continue;
// Do not add functional tab settings to list of windows in menu bar // Do not add functional tab settings to list of windows in menu bar
if (tab.role === "function" && tab.name === "Settings") { if (tab.role === "function" && tab.page === "Settings") {
continue; continue;
} }
initialSubmenu.push({ initialSubmenu.push({
label: tab.name, label: tab.label,
accelerator: accelerator:
tab.role === "function" ? "" : `${shortcutKey} + ${tab.index + 1}`, tab.role === "function" ? "" : `${shortcutKey} + ${tab.index + 1}`,
checked: tab.index === activeTabIndex, checked: tab.index === activeTabIndex,

View File

@@ -1,10 +1,12 @@
import {type Html, html} from "../../../common/html.js"; import {type Html, html} from "../../../common/html.js";
import type {TabPage} from "../../../common/types.js";
import {generateNodeFromHtml} from "./base.js"; import {generateNodeFromHtml} from "./base.js";
import Tab, {type TabProperties} from "./tab.js"; import Tab, {type TabProperties} from "./tab.js";
export type FunctionalTabProperties = { export type FunctionalTabProperties = {
$view: Element; $view: Element;
page: TabPage;
} & TabProperties; } & TabProperties;
export default class FunctionalTab extends Tab { export default class FunctionalTab extends Tab {
@@ -17,7 +19,7 @@ export default class FunctionalTab extends Tab {
this.$view = $view; this.$view = $view;
this.$el = generateNodeFromHtml(this.templateHtml()); this.$el = generateNodeFromHtml(this.templateHtml());
if (this.properties.name !== "Settings") { if (properties.page !== "Settings") {
this.properties.$root.append(this.$el); this.properties.$root.append(this.$el);
this.$closeButton = this.$el.querySelector(".server-tab-badge")!; this.$closeButton = this.$el.querySelector(".server-tab-badge")!;
this.registerListeners(); this.registerListeners();

View File

@@ -49,7 +49,7 @@ export default class ServerTab extends Tab {
return html` return html`
<div class="tab" data-tab-id="${this.properties.tabIndex}"> <div class="tab" data-tab-id="${this.properties.tabIndex}">
<div class="server-tooltip" style="display:none"> <div class="server-tooltip" style="display:none">
${this.properties.name} ${this.properties.label}
</div> </div>
<div class="server-tab-badge"></div> <div class="server-tab-badge"></div>
<div class="server-tab"> <div class="server-tab">
@@ -60,9 +60,9 @@ export default class ServerTab extends Tab {
`; `;
} }
setName(name: string): void { setLabel(label: string): void {
this.properties.name = name; this.properties.label = label;
this.$name.textContent = name; this.$name.textContent = label;
} }
setIcon(icon: string): void { setIcon(icon: string): void {

View File

@@ -1,9 +1,10 @@
import type {TabRole} from "../../../common/types.js"; import type {TabPage, TabRole} from "../../../common/types.js";
export type TabProperties = { export type TabProperties = {
role: TabRole; role: TabRole;
page?: TabPage;
icon?: string; icon?: string;
name: string; label: string;
$root: Element; $root: Element;
onClick: () => void; onClick: () => void;
index: number; index: number;

View File

@@ -20,6 +20,7 @@ import type {
NavigationItem, NavigationItem,
ServerConfig, ServerConfig,
TabData, TabData,
TabPage,
} from "../../common/types.js"; } from "../../common/types.js";
import defaultIcon from "../img/icon.png"; import defaultIcon from "../img/icon.png";
@@ -81,7 +82,7 @@ export class ServerManagerView {
loading: Set<string>; loading: Set<string>;
activeTabIndex: number; activeTabIndex: number;
tabs: ServerOrFunctionalTab[]; tabs: ServerOrFunctionalTab[];
functionalTabs: Map<string, number>; functionalTabs: Map<TabPage, number>;
tabIndex: number; tabIndex: number;
presetOrgs: string[]; presetOrgs: string[];
preferenceView?: PreferenceView; preferenceView?: PreferenceView;
@@ -333,7 +334,7 @@ export class ServerManagerView {
server.url, server.url,
i, i,
); );
tab.setName(serverConfig.alias); tab.setLabel(serverConfig.alias);
tab.setIcon(DomainUtil.iconAsUrl(serverConfig.icon)); tab.setIcon(DomainUtil.iconAsUrl(serverConfig.icon));
(await tab.webview).setUnsupportedMessage( (await tab.webview).setUnsupportedMessage(
DomainUtil.getUnsupportedMessage(serverConfig), DomainUtil.getUnsupportedMessage(serverConfig),
@@ -376,7 +377,7 @@ export class ServerManagerView {
const tab = new ServerTab({ const tab = new ServerTab({
role: "server", role: "server",
icon: DomainUtil.iconAsUrl(server.icon), icon: DomainUtil.iconAsUrl(server.icon),
name: server.alias, label: server.alias,
$root: this.$tabsContainer, $root: this.$tabsContainer,
onClick: this.activateLastTab.bind(this, index), onClick: this.activateLastTab.bind(this, index),
index, index,
@@ -558,18 +559,19 @@ export class ServerManagerView {
} }
async openFunctionalTab(tabProperties: { async openFunctionalTab(tabProperties: {
name: string; label: string;
page: TabPage;
materialIcon: string; materialIcon: string;
makeView: () => Promise<Element>; makeView: () => Promise<Element>;
destroyView: () => void; destroyView: () => void;
}): Promise<void> { }): Promise<void> {
if (this.functionalTabs.has(tabProperties.name)) { if (this.functionalTabs.has(tabProperties.page)) {
await this.activateTab(this.functionalTabs.get(tabProperties.name)!); await this.activateTab(this.functionalTabs.get(tabProperties.page)!);
return; return;
} }
const index = this.tabs.length; const index = this.tabs.length;
this.functionalTabs.set(tabProperties.name, index); this.functionalTabs.set(tabProperties.page, index);
const tabIndex = this.getTabIndex(); const tabIndex = this.getTabIndex();
const $view = await tabProperties.makeView(); const $view = await tabProperties.makeView();
@@ -579,13 +581,14 @@ export class ServerManagerView {
new FunctionalTab({ new FunctionalTab({
role: "function", role: "function",
materialIcon: tabProperties.materialIcon, materialIcon: tabProperties.materialIcon,
name: tabProperties.name, label: tabProperties.label,
page: tabProperties.page,
$root: this.$tabsContainer, $root: this.$tabsContainer,
index, index,
tabIndex, tabIndex,
onClick: this.activateTab.bind(this, index), onClick: this.activateTab.bind(this, index),
onDestroy: async () => { onDestroy: async () => {
await this.destroyTab(tabProperties.name, index); await this.destroyFunctionalTab(tabProperties.page, index);
tabProperties.destroyView(); tabProperties.destroyView();
}, },
$view, $view,
@@ -596,14 +599,15 @@ export class ServerManagerView {
// closed when the functional tab DOM is ready, handled in webview.js // closed when the functional tab DOM is ready, handled in webview.js
this.$webviewsContainer.classList.remove("loaded"); this.$webviewsContainer.classList.remove("loaded");
await this.activateTab(this.functionalTabs.get(tabProperties.name)!); await this.activateTab(this.functionalTabs.get(tabProperties.page)!);
} }
async openSettings( async openSettings(
navigationItem: NavigationItem = "General", navigationItem: NavigationItem = "General",
): Promise<void> { ): Promise<void> {
await this.openFunctionalTab({ await this.openFunctionalTab({
name: "Settings", page: "Settings",
label: "Settings",
materialIcon: "settings", materialIcon: "settings",
makeView: async () => { makeView: async () => {
this.preferenceView = await PreferenceView.create(); this.preferenceView = await PreferenceView.create();
@@ -622,7 +626,8 @@ export class ServerManagerView {
async openAbout(): Promise<void> { async openAbout(): Promise<void> {
let aboutView: AboutView; let aboutView: AboutView;
await this.openFunctionalTab({ await this.openFunctionalTab({
name: "About", page: "About",
label: "About",
materialIcon: "sentiment_very_satisfied", materialIcon: "sentiment_very_satisfied",
async makeView() { async makeView() {
aboutView = await AboutView.create(); aboutView = await AboutView.create();
@@ -660,7 +665,8 @@ export class ServerManagerView {
get tabsForIpc(): TabData[] { get tabsForIpc(): TabData[] {
return this.tabs.map((tab) => ({ return this.tabs.map((tab) => ({
role: tab.properties.role, role: tab.properties.role,
name: tab.properties.name, page: tab.properties.page,
label: tab.properties.label,
index: tab.properties.index, index: tab.properties.index,
})); }));
} }
@@ -680,7 +686,7 @@ export class ServerManagerView {
// If old tab is functional tab Settings, remove focus from the settings icon at sidebar bottom // If old tab is functional tab Settings, remove focus from the settings icon at sidebar bottom
if ( if (
this.tabs[this.activeTabIndex].properties.role === "function" && this.tabs[this.activeTabIndex].properties.role === "function" &&
this.tabs[this.activeTabIndex].properties.name === "Settings" this.tabs[this.activeTabIndex].properties.page === "Settings"
) { ) {
this.$settingsButton.classList.remove("active"); this.$settingsButton.classList.remove("active");
} }
@@ -722,7 +728,7 @@ export class ServerManagerView {
this.$loadingIndicator.classList.toggle("hidden", !loading); this.$loadingIndicator.classList.toggle("hidden", !loading);
} }
async destroyTab(name: string, index: number): Promise<void> { async destroyFunctionalTab(page: TabPage, index: number): Promise<void> {
const tab = this.tabs[index]; const tab = this.tabs[index];
if (tab instanceof ServerTab && (await tab.webview).loading) { if (tab instanceof ServerTab && (await tab.webview).loading) {
return; return;
@@ -731,7 +737,7 @@ export class ServerManagerView {
await tab.destroy(); await tab.destroy();
delete this.tabs[index]; // eslint-disable-line @typescript-eslint/no-array-delete delete this.tabs[index]; // eslint-disable-line @typescript-eslint/no-array-delete
this.functionalTabs.delete(name); this.functionalTabs.delete(page);
// Issue #188: If the functional tab was not focused, do not activate another tab. // Issue #188: If the functional tab was not focused, do not activate another tab.
if (this.activeTabIndex === index) { if (this.activeTabIndex === index) {
@@ -1053,7 +1059,7 @@ export class ServerManagerView {
for (const [index, domain] of DomainUtil.getDomains().entries()) { for (const [index, domain] of DomainUtil.getDomains().entries()) {
if (domain.url === serverURL) { if (domain.url === serverURL) {
const tab = this.tabs[index]; const tab = this.tabs[index];
if (tab instanceof ServerTab) tab.setName(realmName); if (tab instanceof ServerTab) tab.setLabel(realmName);
domain.alias = realmName; domain.alias = realmName;
DomainUtil.updateDomain(index, domain); DomainUtil.updateDomain(index, domain);
// Update the realm name also on the Window menu // Update the realm name also on the Window menu