1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

menu.ts: refactoring and fixes, reuse route paths from views (renderer)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-22 17:10:37 +03:00
parent 117ad584d3
commit a4bcfc1e52
7 changed files with 118 additions and 139 deletions

View File

@ -69,16 +69,24 @@ export function handleIpc(channel: IpcChannel, handler: IpcMessageHandler, optio
export interface IpcPairOptions { export interface IpcPairOptions {
channel: IpcChannel channel: IpcChannel
handle?: IpcMessageHandler handle?: IpcMessageHandler
options?: IpcHandleOpts autoBind?: boolean;
timeout?: number;
} }
export function createIpcChannel({ channel, ...initOpts }: IpcPairOptions) { // todo: improve api
export function createIpcChannel({ channel, autoBind, ...initOpts }: IpcPairOptions) {
const bindHandler = (opts: { handler?: IpcMessageHandler, options?: IpcHandleOpts } = {}) => {
const handler = opts.handler || initOpts.handle || Function;
const options = opts.options || { timeout: initOpts.timeout };
handleIpc(channel, handler, options);
};
if (autoBind) {
bindHandler();
}
return { return {
handleInMain: (opts: Partial<Omit<IpcPairOptions, "channel">> = {}) => { channel: channel,
const { handle = initOpts.handle, options = initOpts.options } = opts; handleInMain: bindHandler,
return handleIpc(channel, handle, options); invokeFromRenderer(...args: any[]) {
},
invokeFromRenderer: (...args: any[]) => {
return invokeIpc(channel, ...args); return invokeIpc(channel, ...args);
}, },
} }

View File

@ -5,7 +5,7 @@ import "../common/prometheus-providers"
import { app, dialog } from "electron" import { app, dialog } from "electron"
import { appName, appProto, staticDir, staticProto } from "../common/vars"; import { appName, appProto, staticDir, staticProto } from "../common/vars";
import path from "path" import path from "path"
import initMenu from "./menu" import { initMenu } from "./menu"
import { LensProxy } from "./lens-proxy" import { LensProxy } from "./lens-proxy"
import { WindowManager } from "./window-manager"; import { WindowManager } from "./window-manager";
import { ClusterManager } from "./cluster-manager"; import { ClusterManager } from "./cluster-manager";
@ -41,7 +41,6 @@ async function main() {
const updater = new AppUpdater() const updater = new AppUpdater()
updater.start(); updater.start();
initMenu();
registerFileProtocol(appProto, app.getPath("userData")); registerFileProtocol(appProto, app.getPath("userData"));
registerFileProtocol(staticProto, staticDir); registerFileProtocol(staticProto, staticDir);
@ -76,6 +75,7 @@ async function main() {
// create window manager and open app // create window manager and open app
windowManager = new WindowManager(proxyPort); windowManager = new WindowManager(proxyPort);
windowManager.showSplash(); windowManager.showSplash();
initMenu(windowManager);
} }
app.on("ready", main); app.on("ready", main);

View File

@ -1,113 +1,71 @@
import type { WindowManager } from "./window-manager";
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell, webContents } from "electron" import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell, webContents } from "electron"
import { broadcastIpc } from "../common/ipc";
import { appName, isMac, issuesTrackerUrl, isWindows, slackUrl } from "../common/vars"; import { appName, isMac, issuesTrackerUrl, isWindows, slackUrl } from "../common/vars";
import { clusterStore } from "../common/cluster-store";
import { addClusterURL } from "../renderer/components/+add-cluster/add-cluster.route";
import { preferencesURL } from "../renderer/components/+preferences/preferences.route";
import { whatsNewURL } from "../renderer/components/+whats-new/whats-new.route";
import { clusterSettingsURL } from "../renderer/components/+cluster-settings/cluster-settings.route";
// todo: refactor + split menu sections to separated files, e.g. menus/file.menu.ts export function initMenu(windowManager: WindowManager) {
const menuItems: MenuItemConstructorOptions[] = [];
export interface MenuOptions { function navigate(url: string) {
logoutHook: any; const activeClusterId = clusterStore.activeClusterId;
addClusterHook: any; const view = windowManager.getView(activeClusterId);
clusterSettingsHook: any; if (view) {
showWhatsNewHook: any; broadcastIpc({
showPreferencesHook: any; channel: "menu:navigate",
// all the above are really () => void type functions webContentId: view.id,
} args: [url],
});
}
}
function setClusterSettingsEnabled(enabled: boolean) { function macOnly(menuItems: MenuItemConstructorOptions[]): MenuItemConstructorOptions[] {
const menuIndex = isMac ? 1 : 0 if (!isMac) return [];
Menu.getApplicationMenu().items[menuIndex].submenu.items[1].enabled = enabled return menuItems;
} }
export function showAbout(_menuitem: MenuItem, browserWindow: BrowserWindow) { // "File" submenu
const appInfo = [ menuItems.push({
`${appName}: ${app.getVersion()}`, label: isMac ? app.getName() : "File",
`Electron: ${process.versions.electron}`,
`Chrome: ${process.versions.chrome}`,
`Copyright 2020 Lakend Labs, Inc.`,
]
dialog.showMessageBoxSync(browserWindow, {
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
type: "info",
buttons: ["Close"],
message: `Lens`,
detail: appInfo.join("\r\n")
})
}
/**
* Constructs the menu based on the example at: https://electronjs.org/docs/api/menu#main-process
* Menu items are constructed piece-by-piece to have slightly better control on individual sub-menus
*/
export default function initMenu(opts: Partial<MenuOptions> = {}) {
const mt: MenuItemConstructorOptions[] = [];
const macAppMenu: MenuItemConstructorOptions = {
label: app.getName(),
submenu: [ submenu: [
{ {
label: "About Lens", label: 'Add Cluster',
click: showAbout click() {
navigate(addClusterURL())
}
},
{
label: 'Cluster Settings',
click() {
navigate(clusterSettingsURL())
}
}, },
{ type: 'separator' }, { type: 'separator' },
{ {
label: 'Preferences', label: 'Preferences',
click: opts.showPreferencesHook, click() {
enabled: true navigate(preferencesURL())
}
}, },
{ type: 'separator' }, ...macOnly([
{ role: 'services' }, { type: 'separator' },
{ type: 'separator' }, { role: 'services' },
{ role: 'hide' }, { type: 'separator' },
{ role: 'hideOthers' }, { role: 'hide' },
{ role: 'unhide' }, { role: 'hideOthers' },
{ role: 'unhide' },
]),
{ type: 'separator' }, { type: 'separator' },
{ role: 'quit' } { role: 'quit' }
] ]
}; });
if (isMac) {
mt.push(macAppMenu);
}
let fileMenu: MenuItemConstructorOptions; // "Edit" submenu
if (isMac) { menuItems.push({
fileMenu = {
label: 'File',
submenu: [{
label: 'Add Cluster...',
click: opts.addClusterHook,
},
{
label: 'Cluster Settings',
click: opts.clusterSettingsHook,
enabled: false
}
]
}
} else {
fileMenu = {
label: 'File',
submenu: [
{
label: 'Add Cluster...',
click: opts.addClusterHook,
},
{
label: 'Cluster Settings',
click: opts.clusterSettingsHook,
enabled: false
},
{ type: 'separator' },
{
label: 'Preferences',
click: opts.showPreferencesHook,
enabled: true
},
{ type: 'separator' },
{ role: 'quit' }
]
}
}
mt.push(fileMenu);
const editMenu: MenuItemConstructorOptions = {
label: 'Edit', label: 'Edit',
submenu: [ submenu: [
{ role: 'undo' }, { role: 'undo' },
@ -120,10 +78,10 @@ export default function initMenu(opts: Partial<MenuOptions> = {}) {
{ type: 'separator' }, { type: 'separator' },
{ role: 'selectAll' }, { role: 'selectAll' },
] ]
}; });
mt.push(editMenu);
const viewMenu: MenuItemConstructorOptions = { // "View" submenu
menuItems.push({
label: 'View', label: 'View',
submenu: [ submenu: [
{ {
@ -155,20 +113,26 @@ export default function initMenu(opts: Partial<MenuOptions> = {}) {
{ type: 'separator' }, { type: 'separator' },
{ role: 'togglefullscreen' } { role: 'togglefullscreen' }
] ]
}; })
mt.push(viewMenu);
const helpMenu: MenuItemConstructorOptions = { // "Help" submenu
menuItems.push({
role: 'help', role: 'help',
submenu: [ submenu: [
{ {
label: 'License', label: "What's new?",
click() {
navigate(whatsNewURL())
},
},
{
label: "License",
click: async () => { click: async () => {
shell.openExternal('https://lakendlabs.com/licenses/lens-eula.md'); shell.openExternal('https://lakendlabs.com/licenses/lens-eula.md');
}, },
}, },
{ {
label: 'Community Slack', label: "Community Slack",
click: async () => { click: async () => {
shell.openExternal(slackUrl); shell.openExternal(slackUrl);
}, },
@ -180,27 +144,26 @@ export default function initMenu(opts: Partial<MenuOptions> = {}) {
}, },
}, },
{ {
label: "What's new?",
click: opts.showWhatsNewHook,
},
...(!isMac ? [{
label: "About Lens", label: "About Lens",
click: showAbout click(menuItem: MenuItem, browserWindow: BrowserWindow) {
} as MenuItemConstructorOptions] : []) const appInfo = [
`${appName}: ${app.getVersion()}`,
`Electron: ${process.versions.electron}`,
`Chrome: ${process.versions.chrome}`,
`Copyright 2020 Lakend Labs, Inc.`,
]
dialog.showMessageBoxSync(browserWindow, {
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
type: "info",
buttons: ["Close"],
message: `Lens`,
detail: appInfo.join("\r\n")
})
}
}
] ]
}; });
mt.push(helpMenu);
const menu = Menu.buildFromTemplate(mt); const menu = Menu.buildFromTemplate(menuItems);
Menu.setApplicationMenu(menu); Menu.setApplicationMenu(menu);
// const promiseIpc = new PromiseIpc({ timeout: 2000 })
//
// promiseIpc.on("enableClusterSettingsMenuItem", (clusterId: string) => {
// setClusterSettingsEnabled(true)
// });
//
// promiseIpc.on("disableClusterSettingsMenuItem", () => {
// setClusterSettingsEnabled(false)
// });
} }

View File

@ -33,7 +33,7 @@ export class WhatsNew extends React.Component {
</div> </div>
<div className="bottom"> <div className="bottom">
<Button <Button
primary primary autoFocus
label={<Trans>Ok, got it!</Trans>} label={<Trans>Ok, got it!</Trans>}
onClick={this.ok} onClick={this.ok}
/> />

View File

@ -5,7 +5,7 @@ import { createPortal } from "react-dom";
import { cssNames, noop } from "../../utils"; import { cssNames, noop } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Animate, AnimateName } from "../animate"; import { Animate, AnimateName } from "../animate";
import { browserHistory } from "../../navigation"; import { history } from "../../navigation";
import { themeStore } from "../../theme.store"; import { themeStore } from "../../theme.store";
export interface DrawerProps { export interface DrawerProps {
@ -36,7 +36,7 @@ export class Drawer extends React.Component<DrawerProps> {
private scrollElem: HTMLElement private scrollElem: HTMLElement
private scrollPos = new Map<string, number>(); private scrollPos = new Map<string, number>();
private stopListenLocation = browserHistory.listen(() => { private stopListenLocation = history.listen(() => {
this.restoreScrollPos(); this.restoreScrollPos();
}); });
@ -55,13 +55,13 @@ export class Drawer extends React.Component<DrawerProps> {
saveScrollPos = () => { saveScrollPos = () => {
if (!this.scrollElem) return; if (!this.scrollElem) return;
const key = browserHistory.location.key; const key = history.location.key;
this.scrollPos.set(key, this.scrollElem.scrollTop); this.scrollPos.set(key, this.scrollElem.scrollTop);
} }
restoreScrollPos = () => { restoreScrollPos = () => {
if (!this.scrollElem) return; if (!this.scrollElem) return;
const key = browserHistory.location.key; const key = history.location.key;
this.scrollElem.scrollTop = this.scrollPos.get(key) || 0; this.scrollElem.scrollTop = this.scrollPos.get(key) || 0;
} }

View File

@ -7,7 +7,7 @@ import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store"; import { workspaceStore } from "../common/workspace-store";
import { clusterStore } from "../common/cluster-store"; import { clusterStore } from "../common/cluster-store";
import { I18nProvider } from "@lingui/react"; import { I18nProvider } from "@lingui/react";
import { browserHistory } from "./navigation"; import { history } from "./navigation";
import { isMac } from "../common/vars"; import { isMac } from "../common/vars";
import { _i18n } from "./i18n"; import { _i18n } from "./i18n";
import { ClusterManager } from "./components/cluster-manager"; import { ClusterManager } from "./components/cluster-manager";
@ -32,7 +32,7 @@ class LensApp extends React.Component {
render() { render() {
return ( return (
<I18nProvider i18n={_i18n}> <I18nProvider i18n={_i18n}>
<Router history={browserHistory}> <Router history={history}>
<ErrorBoundary> <ErrorBoundary>
<Switch> <Switch>
{userStore.isNewVersion && <Route component={WhatsNew}/>} {userStore.isNewVersion && <Route component={WhatsNew}/>}

View File

@ -1,11 +1,19 @@
// Navigation helpers // Navigation helpers
import { ipcRenderer } from "electron";
import { compile } from "path-to-regexp" import { compile } from "path-to-regexp"
import { createBrowserHistory, Location, LocationDescriptor } from "history"; import { createBrowserHistory, createMemoryHistory, Location, LocationDescriptor } from "history";
import { createObservableHistory } from "mobx-observable-history"; import { createObservableHistory } from "mobx-observable-history";
export const browserHistory = createBrowserHistory(); export const history = typeof window !== "undefined" ? createBrowserHistory() : createMemoryHistory();
export const navigation = createObservableHistory(browserHistory); export const navigation = createObservableHistory(history);
if (ipcRenderer) {
// subscribe for navigation via menu.ts
ipcRenderer.on("menu:navigate", (event, path: string) => {
navigate(path);
});
}
export function navigate(location: LocationDescriptor) { export function navigate(location: LocationDescriptor) {
navigation.location = location as Location; navigation.location = location as Location;