From 3312133c902ed4a5ca110296ca36311fde9c1056 Mon Sep 17 00:00:00 2001 From: Don Date: Thu, 25 Jan 2024 14:27:59 -0500 Subject: [PATCH] feat: added shortcut for copying version info and added browser/os to info (#1739) - Adds a shortcut for copying version info to clipboard to make it easier when making tickets. - Adds a new package for parsing UA strings - Adds browser/os to version info for copying --- package-lock.json | 12 +++++ packages/code-studio/package.json | 1 + .../code-studio/src/main/AppMainContainer.tsx | 20 +++++++- .../code-studio/src/settings/SettingsMenu.tsx | 20 +++++--- .../src/settings/SettingsUtils.test.tsx | 51 +++++++++++++++++++ .../src/settings/SettingsUtils.tsx | 26 ++++++++++ .../src/shortcuts/GlobalShortcuts.ts | 8 +++ 7 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 packages/code-studio/src/settings/SettingsUtils.test.tsx diff --git a/package-lock.json b/package-lock.json index 3b05a62cc8..32e34130c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10509,6 +10509,11 @@ "popper.js": "^1.16.1" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "license": "MIT", @@ -27936,6 +27941,7 @@ "@deephaven/utils": "file:../utils", "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/react-fontawesome": "^0.2.0", + "bowser": "^2.11.0", "classnames": "^2.3.1", "event-target-shim": "^6.0.2", "jszip": "3.10.1", @@ -30036,6 +30042,7 @@ "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/react-fontawesome": "^0.2.0", "autoprefixer": "^10.4.8", + "bowser": "^2.11.0", "classnames": "^2.3.1", "event-target-shim": "^6.0.2", "jszip": "3.10.1", @@ -36696,6 +36703,11 @@ "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "requires": {} }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "brace-expansion": { "version": "1.1.11", "requires": { diff --git a/packages/code-studio/package.json b/packages/code-studio/package.json index 9e5a51e727..91d3f15f7e 100644 --- a/packages/code-studio/package.json +++ b/packages/code-studio/package.json @@ -39,6 +39,7 @@ "@deephaven/utils": "file:../utils", "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/react-fontawesome": "^0.2.0", + "bowser": "^2.11.0", "classnames": "^2.3.1", "event-target-shim": "^6.0.2", "jszip": "3.10.1", diff --git a/packages/code-studio/src/main/AppMainContainer.tsx b/packages/code-studio/src/main/AppMainContainer.tsx index 3eb433e969..5482a1de96 100644 --- a/packages/code-studio/src/main/AppMainContainer.tsx +++ b/packages/code-studio/src/main/AppMainContainer.tsx @@ -78,7 +78,11 @@ import { ServerConfigValues, CustomizableWorkspace, } from '@deephaven/redux'; -import { bindAllMethods, PromiseUtils } from '@deephaven/utils'; +import { + bindAllMethods, + copyToClipboard, + PromiseUtils, +} from '@deephaven/utils'; import GoldenLayout from '@deephaven/golden-layout'; import type { ItemConfigType } from '@deephaven/golden-layout'; import { @@ -97,6 +101,7 @@ import WidgetList, { WindowMouseEvent } from './WidgetList'; import EmptyDashboard from './EmptyDashboard'; import UserLayoutUtils from './UserLayoutUtils'; import LayoutStorage from '../storage/LayoutStorage'; +import { getFormattedVersionInfo } from '../settings/SettingsUtils'; const log = Log.module('AppMainContainer'); @@ -186,6 +191,19 @@ export class AppMainContainer extends Component< this.state = { contextActions: [ + { + action: () => { + // Copies the version info to the clipboard for easy pasting into a ticket + const { serverConfigValues } = this.props; + const versionInfo = getFormattedVersionInfo(serverConfigValues); + const versionInfoText = Object.entries(versionInfo) + .map(([key, value]) => `${key}: ${value}`) + .join('\n'); + copyToClipboard(versionInfoText); + }, + shortcut: GLOBAL_SHORTCUTS.COPY_VERSION_INFO, + isGlobal: true, + }, { action: () => { this.handleToolSelect(ToolType.LINKER); diff --git a/packages/code-studio/src/settings/SettingsMenu.tsx b/packages/code-studio/src/settings/SettingsMenu.tsx index 2fdfa56c4a..b06576322a 100644 --- a/packages/code-studio/src/settings/SettingsMenu.tsx +++ b/packages/code-studio/src/settings/SettingsMenu.tsx @@ -13,6 +13,7 @@ import { import { Button, CopyButton, + GLOBAL_SHORTCUTS, Logo, ThemeContext, ThemePicker, @@ -32,6 +33,7 @@ import ShortcutSectionContent from './ShortcutsSectionContent'; import { exportLogs } from '../log/LogExport'; import './SettingsMenu.scss'; import ColumnSpecificSectionContent from './ColumnSpecificSectionContent'; +import { getFormattedVersionInfo } from './SettingsUtils'; interface SettingsMenuProps { serverConfigValues: ServerConfigValues; @@ -128,14 +130,13 @@ export class SettingsMenu extends Component< } render(): ReactElement { - const uiVersion = import.meta.env.npm_package_version; const supportLink = import.meta.env.VITE_SUPPORT_LINK; const docsLink = import.meta.env.VITE_DOCS_LINK; const { serverConfigValues, user } = this.props; - const barrageVersion = serverConfigValues.get('barrage.version'); - const javaVersion = serverConfigValues.get('java.version'); + const versionInfo = getFormattedVersionInfo(serverConfigValues); const deephavenVersion = serverConfigValues.get('deephaven.version'); + const copyShortcut = GLOBAL_SHORTCUTS.COPY_VERSION_INFO.getDisplayText(); const getRow = (text: string, ver?: string): JSX.Element => ( <> @@ -327,16 +328,19 @@ export class SettingsMenu extends Component< {deephavenVersion}
- {getRow('Engine Version', deephavenVersion)} - {getRow('Web UI Version', uiVersion)} - {getRow('Java Version', javaVersion)} - {getRow('Barrage Version', barrageVersion)} + {Object.entries(versionInfo).map(([key, value]) => + getRow(key, value) + )}
`${key}: ${value}`) + .join('\n')} > Copy Versions + ({copyShortcut})
diff --git a/packages/code-studio/src/settings/SettingsUtils.test.tsx b/packages/code-studio/src/settings/SettingsUtils.test.tsx new file mode 100644 index 0000000000..6786a42ddb --- /dev/null +++ b/packages/code-studio/src/settings/SettingsUtils.test.tsx @@ -0,0 +1,51 @@ +import { getFormattedVersionInfo } from './SettingsUtils'; + +describe('getFormattedVersionInfo', () => { + it('should return the formatted version information', () => { + const serverConfigValues = new Map(); + serverConfigValues.set('deephaven.version', '1.0.0'); + serverConfigValues.set('java.version', '11.0.1'); + serverConfigValues.set('barrage.version', '2.3.4'); + + Object.defineProperty(window, 'navigator', { + value: { + userAgent: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', + }, + writable: true, + }); + + const result = getFormattedVersionInfo(serverConfigValues); + + expect(result).toEqual({ + 'Engine Version': '1.0.0', + 'Web UI Version': '0.0.1', + 'Java Version': '11.0.1', + 'Barrage Version': '2.3.4', + 'Browser Name': 'Chrome 96', + 'OS Name': 'Windows NT 10.0', + }); + }); + + it('should return "Unknown" for missing version information', () => { + const serverConfigValues = new Map(); + + Object.defineProperty(window, 'navigator', { + value: { + userAgent: 'Garbage User Agent String', + }, + writable: true, + }); + + const result = getFormattedVersionInfo(serverConfigValues); + + expect(result).toEqual({ + 'Engine Version': 'Unknown', + 'Web UI Version': '0.0.1', + 'Java Version': 'Unknown', + 'Barrage Version': 'Unknown', + 'Browser Name': 'Unknown', + 'OS Name': 'Unknown', + }); + }); +}); diff --git a/packages/code-studio/src/settings/SettingsUtils.tsx b/packages/code-studio/src/settings/SettingsUtils.tsx index e0a137f4e0..251e0dae54 100644 --- a/packages/code-studio/src/settings/SettingsUtils.tsx +++ b/packages/code-studio/src/settings/SettingsUtils.tsx @@ -8,6 +8,8 @@ import { } from '@deephaven/jsapi-utils'; import type { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; +import type { ServerConfigValues } from '@deephaven/redux'; +import Bowser from 'bowser'; const log = Log.module('SettingsUtils'); @@ -32,6 +34,30 @@ function isFormatStringFormat( ); } +/** + * Get an object containing all version information formatted for display + * @param serverConfigValues The server config values + * @returns The formatted version info or "Unknown" for any value not available + */ +export function getFormattedVersionInfo( + serverConfigValues: ServerConfigValues +): Record { + const ua = Bowser.parse(window.navigator.userAgent); + const browser = `${ua.browser.name ?? ''} ${ + // use only the major version and minor version, rest is usually empty 120.1.0.0 -> 120.1 + Number(parseFloat(ua.browser.version ?? '')) || '' + }`; + const os = `${ua.os.name ?? ''} ${ua.os.version ?? ''}`; + return { + 'Engine Version': serverConfigValues.get('deephaven.version') ?? 'Unknown', + 'Web UI Version': import.meta.env.npm_package_version ?? 'Unknown', + 'Java Version': serverConfigValues.get('java.version') ?? 'Unknown', + 'Barrage Version': serverConfigValues.get('barrage.version') ?? 'Unknown', + 'Browser Name': browser.trim() || 'Unknown', + 'OS Name': os.trim() || 'Unknown', + }; +} + export function focusFirstInputInContainer( container: HTMLElement | null ): void { diff --git a/packages/components/src/shortcuts/GlobalShortcuts.ts b/packages/components/src/shortcuts/GlobalShortcuts.ts index eeead7de69..f61455f00f 100644 --- a/packages/components/src/shortcuts/GlobalShortcuts.ts +++ b/packages/components/src/shortcuts/GlobalShortcuts.ts @@ -43,6 +43,14 @@ const GLOBAL_SHORTCUTS = { macShortcut: [KEY.ESCAPE], isEditable: false, }), + COPY_VERSION_INFO: ShortcutRegistry.createAndAdd({ + id: 'GLOBAL.COPY_VERSION_INFO', + name: 'Copy Version Info', + // alt vs shift to not be the devtools shortcut on each platform + shortcut: [MODIFIER.CTRL, MODIFIER.ALT, KEY.I], + macShortcut: [MODIFIER.CMD, MODIFIER.SHIFT, KEY.I], + isEditable: true, + }), NEXT: ShortcutRegistry.createAndAdd({ id: 'GLOBAL.NEXT', name: 'Next',