diff --git a/web/electron/paths.ts b/web/electron/paths.ts index 94ab17fb56..37d0a319db 100644 --- a/web/electron/paths.ts +++ b/web/electron/paths.ts @@ -17,3 +17,8 @@ export const iconPath = path.join( app.getAppPath(), 'dist/octant/assets/icons/icon.png' ); + +export const greyIconPath = path.join( + app.getAppPath(), + 'dist/octant/assets/icons/icon-grey.png' +); diff --git a/web/electron/tray-menu.ts b/web/electron/tray-menu.ts index 22813599f8..b651ae7734 100644 --- a/web/electron/tray-menu.ts +++ b/web/electron/tray-menu.ts @@ -4,47 +4,109 @@ * */ -import { app, BrowserWindow, Menu, Tray, nativeImage, shell } from 'electron'; -import { errLogPath, iconPath } from './paths'; +import { app, BrowserWindow, Menu, Tray, nativeImage, shell, MenuItem } from 'electron'; +import { errLogPath, iconPath, greyIconPath } from './paths'; +import * as WebSocket from 'ws'; +import * as open from 'open'; export class TrayMenu { public readonly tray: Tray; + public wsConn : WebSocket; + private menuState : { + contexts: string[], + namespaces: string[], + currentContext: (string | null), + buildInfo: {version: string, commit: string, time: string} + } - constructor(public window: BrowserWindow) { + constructor(public window: BrowserWindow, public websocketUrl: string) { this.tray = new Tray(this.createNativeImage()); - this.tray.setContextMenu(this.createMenu(window)); - } + this.wsConn = new WebSocket(this.websocketUrl); + this.menuState = { contexts: [], namespaces: [], currentContext: null, + buildInfo: {version: '', commit: '', time: ''} + } + this.setMenu(); + this.startOctantEventListener(); + } + createNativeImage(): Electron.NativeImage { - const image = nativeImage.createFromPath(iconPath); + const image = nativeImage.createFromPath(greyIconPath); image.setTemplateImage(true); return image.resize({ width: 16, height: 16 }); } - createMenu(win: BrowserWindow): Menu { - const menu = Menu.buildFromTemplate([ - { - label: 'Open Octant', - type: 'normal', - click: () => { - win.show(); - }, - }, - { - label: 'View Logs', - type: 'normal', - click: () => { - shell.showItemInFolder(errLogPath); - }, - }, - { - label: 'Quit', - type: 'normal', - click: () => { - app.quit(); - }, - }, - ]); - return menu; + startOctantEventListener() { + this.wsConn.on('message', (msg) => { + const config = JSON.parse(`${msg}`); + + switch (config.type) { + case 'event.octant.dev/kubeConfig': + this.menuState.contexts = config.data.contexts.map(c => c.name); + this.menuState.currentContext = config.data.currentContext; + this.setMenu(); + break; + case 'event.octant.dev/buildInfo': + this.menuState.buildInfo = config.data; + this.setAboutOptions(); + this.setMenu(); + break; + } + }); + } + + setMenu() { + let menu = new Menu(); + menu.append(new MenuItem({label: 'Open Octant', type: 'normal', click: () => this.window.show()})); + menu.append(new MenuItem({label: 'View Logs', type: 'normal', click: () => shell.showItemInFolder(errLogPath)})); + + menu.append(this.contextSubMenu()); + menu.append(this.aboutOctantSubMenu()); + menu.append(this.openIssueMenuItem()); + menu.append(this.octantDocsMenuItem()); + + menu.append(new MenuItem({label: 'Quit', type: 'normal', click: () => app.quit()})) + this.tray.setContextMenu(menu); + } + + contextSubMenu() : MenuItem { + let contextMenu = new Menu(); + const cItems = this.menuState.contexts.forEach((context) => { + contextMenu.append(new MenuItem({ + label: context, + type: 'checkbox', + checked: context === this.menuState.currentContext, + click: () => { this.wsConn.send(JSON.stringify({ + type: 'action.octant.dev/setContext', + payload: { requestedContext: context } + })) } + })); + }); + + + return new MenuItem({label: 'Contexts', type: 'submenu', submenu: contextMenu}); + } + + aboutOctantSubMenu() : MenuItem { + return new MenuItem({label: 'About Octant', role: 'about'}); + } + + openIssueMenuItem() : MenuItem { + const newIssueLink = 'https://github.com/vmware-tanzu/octant/issues/new/choose'; + return new MenuItem({label: 'Open an Issue/Provide Feedback', type: 'normal', click: () => open(newIssueLink)}); + } + + octantDocsMenuItem() : MenuItem { + const docsLink = 'https://octant.dev/'; + return new MenuItem({label: 'Octant Documentation', type: 'normal', click: () => open(docsLink)}); + } + + setAboutOptions() : void { + app.setAboutPanelOptions({ + applicationName: 'Octant', + applicationVersion: `Version: ${this.menuState.buildInfo.version}\nCommit: ${this.menuState.buildInfo.commit}\nBuilt: ${this.menuState.buildInfo.time}`, + website: 'https://octant.dev', + iconPath: iconPath + }) } } diff --git a/web/main.ts b/web/main.ts index cad0b2cedd..ccbcc6ca83 100644 --- a/web/main.ts +++ b/web/main.ts @@ -235,12 +235,13 @@ try { w.webContents.send('port-message', port); }); - tray = new TrayMenu(win); + const websocketUrl = `ws://localhost:${port}/api/v1/stream` + tray = new TrayMenu(win, websocketUrl); // In event of a black background issue: https://github.com/electron/electron/issues/15947 // setTimeout(createWindow, 400); session.defaultSession.webRequest.onBeforeSendHeaders( - { urls: ['ws://localhost:' + port + '/api/v1/stream'] }, + { urls: [websocketUrl] }, (details, callback) => { details.requestHeaders['Origin'] = null; callback({ cancel: false, requestHeaders: details.requestHeaders }); diff --git a/web/package-lock.json b/web/package-lock.json index 1551124e03..7bad3b744d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -353,6 +353,16 @@ "debug": "4" } }, + "open": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.0.tgz", + "integrity": "sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -659,6 +669,16 @@ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, + "open": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.0.tgz", + "integrity": "sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, "resolve": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", @@ -4870,7 +4890,7 @@ "loglevel": "^1.6.4", "loglevel-plugin-prefix": "^0.8.4", "lunr": "^2.3.6", - "marked": "2.0.3", + "marked": "^0.7.0", "minimist": "^1.2.0", "opencollective-postinstall": "^2.0.2", "os-name": "^3.1.0", @@ -11401,6 +11421,18 @@ "dev": true, "requires": { "open": "^7.0.3" + }, + "dependencies": { + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + } } }, "big.js": { @@ -14611,6 +14643,11 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -18727,10 +18764,9 @@ "dev": true }, "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "dev": true + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, "is-dom": { "version": "1.1.0", @@ -19002,7 +19038,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "requires": { "is-docker": "^2.0.0" } @@ -22630,13 +22665,13 @@ } }, "open": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.0.tgz", - "integrity": "sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA==", - "dev": true, + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/open/-/open-8.0.7.tgz", + "integrity": "sha512-qoyG0kpdaWVoL5MiwTRQWujSdivwBOgfLadVEdpsZNHOK1+kBvmVtLYdgWr8G4cgBpG9zaxezn6jz6PPdQW5xg==", "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, "opencollective-postinstall": { @@ -25829,6 +25864,21 @@ } } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -32792,4 +32842,4 @@ "dev": true } } -} \ No newline at end of file +} diff --git a/web/package.json b/web/package.json index ada39675ab..a27f887fa9 100644 --- a/web/package.json +++ b/web/package.json @@ -86,6 +86,7 @@ "lodash-es": "^4.17.15", "monaco-editor": "^0.21.2", "ngx-highlightjs": "^4.1.2", + "open": "^8.0.7", "overlayscrollbars-ngx": "^0.2.2", "rxjs": "^6.6.3", "serialize-javascript": "^5.0.1", diff --git a/web/src/assets/icons/icon-grey.png b/web/src/assets/icons/icon-grey.png new file mode 100644 index 0000000000..192e0d6ee2 Binary files /dev/null and b/web/src/assets/icons/icon-grey.png differ