mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-10-31 12:03:39 +00:00 
			
		
		
		
	Move functional tab pages out of separate webviews.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| import type {DNDSettings} from "./dnd-util"; | import type {DNDSettings} from "./dnd-util"; | ||||||
| import type {MenuProps, NavItem, ServerConf} from "./types"; | import type {MenuProps, ServerConf} from "./types"; | ||||||
|  |  | ||||||
| export interface MainMessage { | export interface MainMessage { | ||||||
|   "clear-app-settings": () => void; |   "clear-app-settings": () => void; | ||||||
| @@ -66,7 +66,6 @@ export interface RendererMessage { | |||||||
|   "show-keyboard-shortcuts": () => void; |   "show-keyboard-shortcuts": () => void; | ||||||
|   "show-notification-settings": () => void; |   "show-notification-settings": () => void; | ||||||
|   "switch-server-tab": (index: number) => void; |   "switch-server-tab": (index: number) => void; | ||||||
|   "switch-settings-nav": (navItem: NavItem) => void; |  | ||||||
|   "tab-devtools": () => void; |   "tab-devtools": () => void; | ||||||
|   "toggle-autohide-menubar": ( |   "toggle-autohide-menubar": ( | ||||||
|     autoHideMenubar: boolean, |     autoHideMenubar: boolean, | ||||||
|   | |||||||
| @@ -1,21 +0,0 @@ | |||||||
| <!DOCTYPE html> |  | ||||||
| <html lang="en"> |  | ||||||
|   <head> |  | ||||||
|     <meta charset="UTF-8" /> |  | ||||||
|     <title>Zulip - About</title> |  | ||||||
|     <style> |  | ||||||
|       html, |  | ||||||
|       body, |  | ||||||
|       body > div { |  | ||||||
|         margin: 0; |  | ||||||
|         height: 100%; |  | ||||||
|       } |  | ||||||
|     </style> |  | ||||||
|   </head> |  | ||||||
|   <body> |  | ||||||
|     <script> |  | ||||||
|       const {AboutView} = require("./js/pages/about.js"); |  | ||||||
|       document.body.append(new AboutView().$view); |  | ||||||
|     </script> |  | ||||||
|   </body> |  | ||||||
| </html> |  | ||||||
| @@ -304,27 +304,28 @@ body { | |||||||
|   visibility: hidden; |   visibility: hidden; | ||||||
| } | } | ||||||
|  |  | ||||||
| webview { | webview, | ||||||
|  | .functional-view { | ||||||
|   /* transition: opacity 0.3s ease-in; */ |   /* transition: opacity 0.3s ease-in; */ | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: 100%; |   height: 100%; | ||||||
|   flex-grow: 1; |   flex-grow: 1; | ||||||
|   display: flex; |  | ||||||
|   flex-direction: column; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| webview.onload { | webview.onload { | ||||||
|   transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035); |   transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035); | ||||||
| } | } | ||||||
|  |  | ||||||
| webview.active { | webview.active, | ||||||
|  | .functional-view.active { | ||||||
|   opacity: 1; |   opacity: 1; | ||||||
|   z-index: 1; |   z-index: 1; | ||||||
|   visibility: visible; |   visibility: visible; | ||||||
| } | } | ||||||
|  |  | ||||||
| webview.disabled { | webview.disabled, | ||||||
|  | .functional-view.disabled { | ||||||
|   opacity: 0; |   opacity: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,13 +5,19 @@ import {generateNodeFromHTML} from "./base"; | |||||||
| import type {TabProps} from "./tab"; | import type {TabProps} from "./tab"; | ||||||
| import Tab from "./tab"; | import Tab from "./tab"; | ||||||
|  |  | ||||||
|  | export interface FunctionalTabProps extends TabProps { | ||||||
|  |   $view: Element; | ||||||
|  | } | ||||||
|  |  | ||||||
| export default class FunctionalTab extends Tab { | export default class FunctionalTab extends Tab { | ||||||
|  |   $view: Element; | ||||||
|   $el: Element; |   $el: Element; | ||||||
|   $closeButton?: Element; |   $closeButton?: Element; | ||||||
|  |  | ||||||
|   constructor(props: TabProps) { |   constructor({$view, ...props}: FunctionalTabProps) { | ||||||
|     super(props); |     super(props); | ||||||
|  |  | ||||||
|  |     this.$view = $view; | ||||||
|     this.$el = generateNodeFromHTML(this.templateHTML()); |     this.$el = generateNodeFromHTML(this.templateHTML()); | ||||||
|     if (this.props.name !== "Settings") { |     if (this.props.name !== "Settings") { | ||||||
|       this.props.$root.append(this.$el); |       this.props.$root.append(this.$el); | ||||||
| @@ -20,6 +26,23 @@ export default class FunctionalTab extends Tab { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   override activate(): void { | ||||||
|  |     super.activate(); | ||||||
|  |     this.$view.classList.add("active"); | ||||||
|  |     this.$view.classList.remove("disabled"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override deactivate(): void { | ||||||
|  |     super.deactivate(); | ||||||
|  |     this.$view.classList.add("disabled"); | ||||||
|  |     this.$view.classList.remove("active"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override destroy(): void { | ||||||
|  |     super.destroy(); | ||||||
|  |     this.$view.remove(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   templateHTML(): HTML { |   templateHTML(): HTML { | ||||||
|     return html` |     return html` | ||||||
|       <div class="tab functional-tab" data-tab-id="${this.props.tabIndex}"> |       <div class="tab functional-tab" data-tab-id="${this.props.tabIndex}"> | ||||||
|   | |||||||
| @@ -5,20 +5,42 @@ import {ipcRenderer} from "../typed-ipc-renderer"; | |||||||
| import {generateNodeFromHTML} from "./base"; | import {generateNodeFromHTML} from "./base"; | ||||||
| import type {TabProps} from "./tab"; | import type {TabProps} from "./tab"; | ||||||
| import Tab from "./tab"; | import Tab from "./tab"; | ||||||
|  | import type WebView from "./webview"; | ||||||
|  |  | ||||||
|  | export interface ServerTabProps extends TabProps { | ||||||
|  |   webview: WebView; | ||||||
|  | } | ||||||
|  |  | ||||||
| export default class ServerTab extends Tab { | export default class ServerTab extends Tab { | ||||||
|  |   webview: WebView; | ||||||
|   $el: Element; |   $el: Element; | ||||||
|   $badge: Element; |   $badge: Element; | ||||||
|  |  | ||||||
|   constructor(props: TabProps) { |   constructor({webview, ...props}: ServerTabProps) { | ||||||
|     super(props); |     super(props); | ||||||
|  |  | ||||||
|  |     this.webview = webview; | ||||||
|     this.$el = generateNodeFromHTML(this.templateHTML()); |     this.$el = generateNodeFromHTML(this.templateHTML()); | ||||||
|     this.props.$root.append(this.$el); |     this.props.$root.append(this.$el); | ||||||
|     this.registerListeners(); |     this.registerListeners(); | ||||||
|     this.$badge = this.$el.querySelector(".server-tab-badge")!; |     this.$badge = this.$el.querySelector(".server-tab-badge")!; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   override activate(): void { | ||||||
|  |     super.activate(); | ||||||
|  |     this.webview.load(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override deactivate(): void { | ||||||
|  |     super.deactivate(); | ||||||
|  |     this.webview.hide(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override destroy(): void { | ||||||
|  |     super.destroy(); | ||||||
|  |     this.webview.$el!.remove(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   templateHTML(): HTML { |   templateHTML(): HTML { | ||||||
|     return html` |     return html` | ||||||
|       <div class="tab" data-tab-id="${this.props.tabIndex}"> |       <div class="tab" data-tab-id="${this.props.tabIndex}"> | ||||||
|   | |||||||
| @@ -1,7 +1,5 @@ | |||||||
| import type {TabRole} from "../../../common/types"; | import type {TabRole} from "../../../common/types"; | ||||||
|  |  | ||||||
| import type WebView from "./webview"; |  | ||||||
|  |  | ||||||
| export interface TabProps { | export interface TabProps { | ||||||
|   role: TabRole; |   role: TabRole; | ||||||
|   icon?: string; |   icon?: string; | ||||||
| @@ -12,19 +10,16 @@ export interface TabProps { | |||||||
|   tabIndex: number; |   tabIndex: number; | ||||||
|   onHover?: () => void; |   onHover?: () => void; | ||||||
|   onHoverOut?: () => void; |   onHoverOut?: () => void; | ||||||
|   webview: WebView; |  | ||||||
|   materialIcon?: string; |   materialIcon?: string; | ||||||
|   onDestroy?: () => void; |   onDestroy?: () => void; | ||||||
| } | } | ||||||
|  |  | ||||||
| export default abstract class Tab { | export default abstract class Tab { | ||||||
|   props: TabProps; |   props: TabProps; | ||||||
|   webview: WebView; |  | ||||||
|   abstract $el: Element; |   abstract $el: Element; | ||||||
|  |  | ||||||
|   constructor(props: TabProps) { |   constructor(props: TabProps) { | ||||||
|     this.props = props; |     this.props = props; | ||||||
|     this.webview = this.props.webview; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   registerListeners(): void { |   registerListeners(): void { | ||||||
| @@ -41,16 +36,13 @@ export default abstract class Tab { | |||||||
|  |  | ||||||
|   activate(): void { |   activate(): void { | ||||||
|     this.$el.classList.add("active"); |     this.$el.classList.add("active"); | ||||||
|     this.webview.load(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   deactivate(): void { |   deactivate(): void { | ||||||
|     this.$el.classList.remove("active"); |     this.$el.classList.remove("active"); | ||||||
|     this.webview.hide(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   destroy(): void { |   destroy(): void { | ||||||
|     this.$el.remove(); |     this.$el.remove(); | ||||||
|     this.webview.$el!.remove(); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -107,12 +107,7 @@ export default class WebView { | |||||||
|       this.props.onTitleChange(); |       this.props.onTitleChange(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     this.$el!.addEventListener("did-navigate-in-page", (event) => { |     this.$el!.addEventListener("did-navigate-in-page", () => { | ||||||
|       const isSettingPage = event.url.includes("renderer/preference.html"); |  | ||||||
|       if (isSettingPage) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       this.canGoBackButton(); |       this.canGoBackButton(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
| @@ -170,10 +165,7 @@ export default class WebView { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     this.$el!.addEventListener("did-start-loading", () => { |     this.$el!.addEventListener("did-start-loading", () => { | ||||||
|       const isSettingPage = this.props.url.includes("renderer/preference.html"); |       this.props.switchLoading(true, this.props.url); | ||||||
|       if (!isSettingPage) { |  | ||||||
|         this.props.switchLoading(true, this.props.url); |  | ||||||
|       } |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     this.$el!.addEventListener("did-stop-loading", () => { |     this.$el!.addEventListener("did-stop-loading", () => { | ||||||
|   | |||||||
| @@ -8,12 +8,13 @@ import type {DNDSettings} from "../../common/dnd-util"; | |||||||
| import * as EnterpriseUtil from "../../common/enterprise-util"; | import * as EnterpriseUtil from "../../common/enterprise-util"; | ||||||
| import Logger from "../../common/logger-util"; | import Logger from "../../common/logger-util"; | ||||||
| import * as Messages from "../../common/messages"; | import * as Messages from "../../common/messages"; | ||||||
| import type {RendererMessage} from "../../common/typed-ipc"; |  | ||||||
| import type {NavItem, ServerConf, TabData} from "../../common/types"; | import type {NavItem, ServerConf, TabData} from "../../common/types"; | ||||||
|  |  | ||||||
| import FunctionalTab from "./components/functional-tab"; | import FunctionalTab from "./components/functional-tab"; | ||||||
| import ServerTab from "./components/server-tab"; | import ServerTab from "./components/server-tab"; | ||||||
| import WebView from "./components/webview"; | import WebView from "./components/webview"; | ||||||
|  | import {AboutView} from "./pages/about"; | ||||||
|  | import {PreferenceView} from "./pages/preference/preference"; | ||||||
| import {initializeTray} from "./tray"; | import {initializeTray} from "./tray"; | ||||||
| import {ipcRenderer} from "./typed-ipc-renderer"; | import {ipcRenderer} from "./typed-ipc-renderer"; | ||||||
| import * as DomainUtil from "./utils/domain-util"; | import * as DomainUtil from "./utils/domain-util"; | ||||||
| @@ -41,7 +42,7 @@ const logger = new Logger({ | |||||||
| const rendererDirectory = path.resolve(__dirname, ".."); | const rendererDirectory = path.resolve(__dirname, ".."); | ||||||
| type ServerOrFunctionalTab = ServerTab | FunctionalTab; | type ServerOrFunctionalTab = ServerTab | FunctionalTab; | ||||||
|  |  | ||||||
| class ServerManagerView { | export class ServerManagerView { | ||||||
|   $addServerButton: HTMLButtonElement; |   $addServerButton: HTMLButtonElement; | ||||||
|   $tabsContainer: Element; |   $tabsContainer: Element; | ||||||
|   $reloadButton: HTMLButtonElement; |   $reloadButton: HTMLButtonElement; | ||||||
| @@ -66,6 +67,7 @@ class ServerManagerView { | |||||||
|   functionalTabs: Map<string, number>; |   functionalTabs: Map<string, number>; | ||||||
|   tabIndex: number; |   tabIndex: number; | ||||||
|   presetOrgs: string[]; |   presetOrgs: string[]; | ||||||
|  |   preferenceView?: PreferenceView; | ||||||
|   constructor() { |   constructor() { | ||||||
|     this.$addServerButton = document.querySelector("#add-tab")!; |     this.$addServerButton = document.querySelector("#add-tab")!; | ||||||
|     this.$tabsContainer = document.querySelector("#tabs-container")!; |     this.$tabsContainer = document.querySelector("#tabs-container")!; | ||||||
| @@ -111,7 +113,7 @@ class ServerManagerView { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   async init(): Promise<void> { |   async init(): Promise<void> { | ||||||
|     initializeTray(); |     initializeTray(this); | ||||||
|     await this.loadProxy(); |     await this.loadProxy(); | ||||||
|     this.initDefaultSettings(); |     this.initDefaultSettings(); | ||||||
|     this.initSidebar(); |     this.initSidebar(); | ||||||
| @@ -532,16 +534,20 @@ class ServerManagerView { | |||||||
|   openFunctionalTab(tabProps: { |   openFunctionalTab(tabProps: { | ||||||
|     name: string; |     name: string; | ||||||
|     materialIcon: string; |     materialIcon: string; | ||||||
|     url: string; |     makeView: () => Element; | ||||||
|  |     destroyView: () => void; | ||||||
|   }): void { |   }): void { | ||||||
|     if (this.functionalTabs.has(tabProps.name)) { |     if (this.functionalTabs.has(tabProps.name)) { | ||||||
|       this.activateTab(this.functionalTabs.get(tabProps.name)!); |       this.activateTab(this.functionalTabs.get(tabProps.name)!); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     this.functionalTabs.set(tabProps.name, this.tabs.length); |     const index = this.tabs.length; | ||||||
|  |     this.functionalTabs.set(tabProps.name, index); | ||||||
|  |  | ||||||
|     const tabIndex = this.getTabIndex(); |     const tabIndex = this.getTabIndex(); | ||||||
|  |     const $view = tabProps.makeView(); | ||||||
|  |     this.$webviewsContainer.append($view); | ||||||
|  |  | ||||||
|     this.tabs.push( |     this.tabs.push( | ||||||
|       new FunctionalTab({ |       new FunctionalTab({ | ||||||
| @@ -549,45 +555,14 @@ class ServerManagerView { | |||||||
|         materialIcon: tabProps.materialIcon, |         materialIcon: tabProps.materialIcon, | ||||||
|         name: tabProps.name, |         name: tabProps.name, | ||||||
|         $root: this.$tabsContainer, |         $root: this.$tabsContainer, | ||||||
|         index: this.functionalTabs.get(tabProps.name)!, |         index, | ||||||
|         tabIndex, |         tabIndex, | ||||||
|         onClick: this.activateTab.bind( |         onClick: this.activateTab.bind(this, index), | ||||||
|           this, |         onDestroy: () => { | ||||||
|           this.functionalTabs.get(tabProps.name)!, |           this.destroyTab(tabProps.name, index); | ||||||
|         ), |           tabProps.destroyView(); | ||||||
|         onDestroy: this.destroyTab.bind( |         }, | ||||||
|           this, |         $view, | ||||||
|           tabProps.name, |  | ||||||
|           this.functionalTabs.get(tabProps.name)!, |  | ||||||
|         ), |  | ||||||
|         webview: new WebView({ |  | ||||||
|           $root: this.$webviewsContainer, |  | ||||||
|           index: this.functionalTabs.get(tabProps.name)!, |  | ||||||
|           tabIndex, |  | ||||||
|           url: tabProps.url, |  | ||||||
|           role: "function", |  | ||||||
|           isActive: () => |  | ||||||
|             this.functionalTabs.get(tabProps.name) === this.activeTabIndex, |  | ||||||
|           switchLoading: (loading: boolean, url: string) => { |  | ||||||
|             if (loading) { |  | ||||||
|               this.loading.add(url); |  | ||||||
|             } else { |  | ||||||
|               this.loading.delete(url); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             const tab = this.tabs[this.activeTabIndex]; |  | ||||||
|             this.showLoading( |  | ||||||
|               tab instanceof ServerTab && |  | ||||||
|                 this.loading.has(tab.webview.props.url), |  | ||||||
|             ); |  | ||||||
|           }, |  | ||||||
|           onNetworkError: async (index: number) => { |  | ||||||
|             await this.openNetworkTroubleshooting(index); |  | ||||||
|           }, |  | ||||||
|           onTitleChange: this.updateBadge.bind(this), |  | ||||||
|           nodeIntegration: true, |  | ||||||
|           preload: false, |  | ||||||
|         }), |  | ||||||
|       }), |       }), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -602,20 +577,33 @@ class ServerManagerView { | |||||||
|     this.openFunctionalTab({ |     this.openFunctionalTab({ | ||||||
|       name: "Settings", |       name: "Settings", | ||||||
|       materialIcon: "settings", |       materialIcon: "settings", | ||||||
|       url: `file://${rendererDirectory}/preference.html#${nav}`, |       makeView: () => { | ||||||
|  |         this.preferenceView = new PreferenceView(); | ||||||
|  |         this.preferenceView.$view.classList.add("functional-view"); | ||||||
|  |         return this.preferenceView.$view; | ||||||
|  |       }, | ||||||
|  |       destroyView: () => { | ||||||
|  |         this.preferenceView!.destroy(); | ||||||
|  |         this.preferenceView = undefined; | ||||||
|  |       }, | ||||||
|     }); |     }); | ||||||
|     this.$settingsButton.classList.add("active"); |     this.$settingsButton.classList.add("active"); | ||||||
|     await this.tabs[this.functionalTabs.get("Settings")!].webview.send( |     this.preferenceView!.handleNavigation(nav); | ||||||
|       "switch-settings-nav", |  | ||||||
|       nav, |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   openAbout(): void { |   openAbout(): void { | ||||||
|  |     let aboutView: AboutView; | ||||||
|     this.openFunctionalTab({ |     this.openFunctionalTab({ | ||||||
|       name: "About", |       name: "About", | ||||||
|       materialIcon: "sentiment_very_satisfied", |       materialIcon: "sentiment_very_satisfied", | ||||||
|       url: `file://${rendererDirectory}/about.html`, |       makeView: () => { | ||||||
|  |         aboutView = new AboutView(); | ||||||
|  |         aboutView.$view.classList.add("functional-view"); | ||||||
|  |         return aboutView.$view; | ||||||
|  |       }, | ||||||
|  |       destroyView: () => { | ||||||
|  |         aboutView.destroy(); | ||||||
|  |       }, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -768,16 +756,6 @@ class ServerManagerView { | |||||||
|     ipcRenderer.send("update-badge", messageCountAll); |     ipcRenderer.send("update-badge", messageCountAll); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   updateGeneralSettings<Channel extends keyof RendererMessage>( |  | ||||||
|     channel: Channel, |  | ||||||
|     ...args: Parameters<RendererMessage[Channel]> |  | ||||||
|   ): void { |  | ||||||
|     if (this.getActiveWebview()) { |  | ||||||
|       const webContentsId = this.getActiveWebview().getWebContentsId(); |  | ||||||
|       ipcRenderer.sendTo(webContentsId, channel, ...args); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   toggleSidebar(show: boolean): void { |   toggleSidebar(show: boolean): void { | ||||||
|     if (show) { |     if (show) { | ||||||
|       this.$sidebar.classList.remove("sidebar-hide"); |       this.$sidebar.classList.remove("sidebar-hide"); | ||||||
| @@ -802,12 +780,6 @@ class ServerManagerView { | |||||||
|     return !(url.endsWith("/login/") || tab.webview.loading); |     return !(url.endsWith("/login/") || tab.webview.loading); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getActiveWebview(): Electron.WebviewTag { |  | ||||||
|     const selector = "webview:not(.disabled)"; |  | ||||||
|     const webview: Electron.WebviewTag = document.querySelector(selector)!; |  | ||||||
|     return webview; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   addContextMenu($serverImg: HTMLElement, index: number): void { |   addContextMenu($serverImg: HTMLElement, index: number): void { | ||||||
|     $serverImg.addEventListener("contextmenu", (event) => { |     $serverImg.addEventListener("contextmenu", (event) => { | ||||||
|       event.preventDefault(); |       event.preventDefault(); | ||||||
| @@ -1012,9 +984,6 @@ class ServerManagerView { | |||||||
|     ipcRenderer.on("toggle-sidebar", (event: Event, show: boolean) => { |     ipcRenderer.on("toggle-sidebar", (event: Event, show: boolean) => { | ||||||
|       // Toggle the left sidebar |       // Toggle the left sidebar | ||||||
|       this.toggleSidebar(show); |       this.toggleSidebar(show); | ||||||
|  |  | ||||||
|       // Toggle sidebar switch in the general settings |  | ||||||
|       this.updateGeneralSettings("toggle-sidebar", show); |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     ipcRenderer.on("toggle-silent", (event: Event, state: boolean) => { |     ipcRenderer.on("toggle-silent", (event: Event, state: boolean) => { | ||||||
| @@ -1040,14 +1009,7 @@ class ServerManagerView { | |||||||
|             tabs: this.tabsForIpc, |             tabs: this.tabsForIpc, | ||||||
|             activeTabIndex: this.activeTabIndex, |             activeTabIndex: this.activeTabIndex, | ||||||
|           }); |           }); | ||||||
|           return; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         this.updateGeneralSettings( |  | ||||||
|           "toggle-autohide-menubar", |  | ||||||
|           autoHideMenubar, |  | ||||||
|           updateMenu, |  | ||||||
|         ); |  | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -1060,8 +1022,6 @@ class ServerManagerView { | |||||||
|           "toggle-silent", |           "toggle-silent", | ||||||
|           newSettings.silent ?? false, |           newSettings.silent ?? false, | ||||||
|         ); |         ); | ||||||
|         const webContentsId = this.getActiveWebview().getWebContentsId(); |  | ||||||
|         ipcRenderer.sendTo(webContentsId, "toggle-dnd", state, newSettings); |  | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ export class PreferenceView { | |||||||
|   private readonly $shadow: ShadowRoot; |   private readonly $shadow: ShadowRoot; | ||||||
|   private readonly $settingsContainer: Element; |   private readonly $settingsContainer: Element; | ||||||
|   private readonly nav: Nav; |   private readonly nav: Nav; | ||||||
|  |   private navItem: NavItem = "General"; | ||||||
|  |  | ||||||
|   constructor() { |   constructor() { | ||||||
|     this.$view = document.createElement("div"); |     this.$view = document.createElement("div"); | ||||||
| @@ -49,21 +50,15 @@ export class PreferenceView { | |||||||
|       onItemSelected: this.handleNavigation, |       onItemSelected: this.handleNavigation, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     const navItem = |  | ||||||
|       this.nav.navItems.find( |  | ||||||
|         (navItem) => window.location.hash === `#${navItem}`, |  | ||||||
|       ) ?? "General"; |  | ||||||
|  |  | ||||||
|     this.handleNavigation(navItem); |  | ||||||
|  |  | ||||||
|     ipcRenderer.on("switch-settings-nav", this.handleSwitchSettingsNav); |  | ||||||
|     ipcRenderer.on("toggle-sidebar", this.handleToggleSidebar); |     ipcRenderer.on("toggle-sidebar", this.handleToggleSidebar); | ||||||
|     ipcRenderer.on("toggle-autohide-menubar", this.handleToggleMenubar); |     ipcRenderer.on("toggle-autohide-menubar", this.handleToggleMenubar); | ||||||
|     ipcRenderer.on("toggle-tray", this.handleToggleTray); |  | ||||||
|     ipcRenderer.on("toggle-dnd", this.handleToggleDnd); |     ipcRenderer.on("toggle-dnd", this.handleToggleDnd); | ||||||
|  |  | ||||||
|  |     this.handleNavigation(this.navItem); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleNavigation = (navItem: NavItem): void => { |   handleNavigation = (navItem: NavItem): void => { | ||||||
|  |     this.navItem = navItem; | ||||||
|     this.nav.select(navItem); |     this.nav.select(navItem); | ||||||
|     switch (navItem) { |     switch (navItem) { | ||||||
|       case "AddServer": |       case "AddServer": | ||||||
| @@ -104,11 +99,13 @@ export class PreferenceView { | |||||||
|     window.location.hash = `#${navItem}`; |     window.location.hash = `#${navItem}`; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   handleToggleTray(state: boolean) { | ||||||
|  |     this.handleToggle("tray-option", state); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   destroy(): void { |   destroy(): void { | ||||||
|     ipcRenderer.off("switch-settings-nav", this.handleSwitchSettingsNav); |  | ||||||
|     ipcRenderer.off("toggle-sidebar", this.handleToggleSidebar); |     ipcRenderer.off("toggle-sidebar", this.handleToggleSidebar); | ||||||
|     ipcRenderer.off("toggle-autohide-menubar", this.handleToggleMenubar); |     ipcRenderer.off("toggle-autohide-menubar", this.handleToggleMenubar); | ||||||
|     ipcRenderer.off("toggle-tray", this.handleToggleTray); |  | ||||||
|     ipcRenderer.off("toggle-dnd", this.handleToggleDnd); |     ipcRenderer.off("toggle-dnd", this.handleToggleDnd); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -121,13 +118,6 @@ export class PreferenceView { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private readonly handleSwitchSettingsNav = ( |  | ||||||
|     _event: Event, |  | ||||||
|     navItem: NavItem, |  | ||||||
|   ) => { |  | ||||||
|     this.handleNavigation(navItem); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   private readonly handleToggleSidebar = (_event: Event, state: boolean) => { |   private readonly handleToggleSidebar = (_event: Event, state: boolean) => { | ||||||
|     this.handleToggle("sidebar-option", state); |     this.handleToggle("sidebar-option", state); | ||||||
|   }; |   }; | ||||||
| @@ -136,10 +126,6 @@ export class PreferenceView { | |||||||
|     this.handleToggle("menubar-option", state); |     this.handleToggle("menubar-option", state); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   private readonly handleToggleTray = (_event: Event, state: boolean) => { |  | ||||||
|     this.handleToggle("tray-option", state); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   private readonly handleToggleDnd = ( |   private readonly handleToggleDnd = ( | ||||||
|     _event: Event, |     _event: Event, | ||||||
|     _state: boolean, |     _state: boolean, | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| import type {NativeImage, WebviewTag} from "electron"; | import type {NativeImage} from "electron"; | ||||||
| import {remote} from "electron"; | import {remote} from "electron"; | ||||||
| import path from "path"; | import path from "path"; | ||||||
|  |  | ||||||
| import * as ConfigUtil from "../../common/config-util"; | import * as ConfigUtil from "../../common/config-util"; | ||||||
| import type {RendererMessage} from "../../common/typed-ipc"; | import type {RendererMessage} from "../../common/typed-ipc"; | ||||||
|  |  | ||||||
|  | import type {ServerManagerView} from "./main"; | ||||||
| import {ipcRenderer} from "./typed-ipc-renderer"; | import {ipcRenderer} from "./typed-ipc-renderer"; | ||||||
|  |  | ||||||
| const {Tray, Menu, nativeImage, BrowserWindow} = remote; | const {Tray, Menu, nativeImage, BrowserWindow} = remote; | ||||||
| @@ -168,7 +169,7 @@ const createTray = function (): void { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export function initializeTray() { | export function initializeTray(serverManagerView: ServerManagerView) { | ||||||
|   ipcRenderer.on("destroytray", (_event: Event) => { |   ipcRenderer.on("destroytray", (_event: Event) => { | ||||||
|     if (!tray) { |     if (!tray) { | ||||||
|       return; |       return; | ||||||
| @@ -224,12 +225,7 @@ export function initializeTray() { | |||||||
|       ConfigUtil.setConfigItem("trayIcon", true); |       ConfigUtil.setConfigItem("trayIcon", true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const webview = document.querySelector<WebviewTag>( |     serverManagerView.preferenceView?.handleToggleTray(state); | ||||||
|       "webview:not([class*=disabled])", |  | ||||||
|     ); |  | ||||||
|     if (webview !== null) { |  | ||||||
|       ipcRenderer.sendTo(webview.getWebContentsId(), "toggle-tray", state); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ipcRenderer.on("toggletray", toggleTray); |   ipcRenderer.on("toggletray", toggleTray); | ||||||
|   | |||||||
| @@ -1,24 +0,0 @@ | |||||||
| <!DOCTYPE html> |  | ||||||
| <html lang="en" class="responsive desktop"> |  | ||||||
|   <head> |  | ||||||
|     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |  | ||||||
|     <meta name="viewport" content="width=device-width" /> |  | ||||||
|     <title>Zulip - Settings</title> |  | ||||||
|     <link rel="stylesheet" href="css/fonts.css" /> |  | ||||||
|     <style> |  | ||||||
|       html, |  | ||||||
|       body, |  | ||||||
|       body > div { |  | ||||||
|         margin: 0; |  | ||||||
|         height: 100%; |  | ||||||
|       } |  | ||||||
|     </style> |  | ||||||
|   </head> |  | ||||||
|   <body> |  | ||||||
|     <zd-preference-view></zd-preference-view> |  | ||||||
|     <script> |  | ||||||
|       const {PreferenceView} = require("./js/pages/preference/preference.js"); |  | ||||||
|       document.body.append(new PreferenceView().$view); |  | ||||||
|     </script> |  | ||||||
|   </body> |  | ||||||
| </html> |  | ||||||
| @@ -16,8 +16,7 @@ test("app runs", async (t) => { | |||||||
|     const mainWindow = await take(windows); |     const mainWindow = await take(windows); | ||||||
|     t.equal(await mainWindow.title(), "Zulip"); |     t.equal(await mainWindow.title(), "Zulip"); | ||||||
|  |  | ||||||
|     const mainWebview = await take(windows); |     await mainWindow.waitForSelector("#connect"); | ||||||
|     await mainWebview.waitForSelector("#connect"); |  | ||||||
|   } finally { |   } finally { | ||||||
|     await setup.endTest(app); |     await setup.endTest(app); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -16,9 +16,8 @@ test("add-organization", async (t) => { | |||||||
|     const mainWindow = await take(windows); |     const mainWindow = await take(windows); | ||||||
|     t.equal(await mainWindow.title(), "Zulip"); |     t.equal(await mainWindow.title(), "Zulip"); | ||||||
|  |  | ||||||
|     const mainWebview = await take(windows); |     await mainWindow.fill(".setting-input-value", "chat.zulip.org"); | ||||||
|     await mainWebview.fill(".setting-input-value", "chat.zulip.org"); |     await mainWindow.click("#connect"); | ||||||
|     await mainWebview.click("#connect"); |  | ||||||
|  |  | ||||||
|     const orgWebview = await take(windows); |     const orgWebview = await take(windows); | ||||||
|     await orgWebview.waitForSelector("#id_username"); |     await orgWebview.waitForSelector("#id_username"); | ||||||
|   | |||||||
| @@ -18,8 +18,7 @@ test("new-org-link", async (t) => { | |||||||
|     const mainWindow = await take(windows); |     const mainWindow = await take(windows); | ||||||
|     t.equal(await mainWindow.title(), "Zulip"); |     t.equal(await mainWindow.title(), "Zulip"); | ||||||
|  |  | ||||||
|     const mainWebview = await take(windows); |     await mainWindow.click("#open-create-org-link"); | ||||||
|     await mainWebview.click("#open-create-org-link"); |  | ||||||
|   } finally { |   } finally { | ||||||
|     await setup.endTest(app); |     await setup.endTest(app); | ||||||
|   } |   } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user