From 15a4719b17122fccdacbbbbbd2eb84367f675778 Mon Sep 17 00:00:00 2001 From: Red J Adaya Date: Wed, 1 Nov 2023 13:24:36 +0800 Subject: [PATCH] About Modal (#39) * menu item and send event to renderer * wire up everything * update content * modal content * add mising text * minor fix * style fixes * figma design init * about header, content, and general styles * css refactor * more on refactor * reuse styles from app.less in about modal * dynamic status * external linkn * finish about modal * update links * update license link --- src/app/app.tsx | 5 +- src/app/assets/icons/close.svg | 5 + src/app/assets/waveterm-logo-with-bg.svg | 29 +++ src/app/common/common.less | 2 - src/app/common/modals/modals.less | 279 +++++++++++++++++++---- src/app/common/modals/modals.tsx | 124 +++++++++- src/electron/emain.ts | 10 +- src/electron/preload.js | 1 + src/model/model.ts | 15 ++ 9 files changed, 415 insertions(+), 55 deletions(-) create mode 100644 src/app/assets/icons/close.svg create mode 100644 src/app/assets/waveterm-logo-with-bg.svg diff --git a/src/app/app.tsx b/src/app/app.tsx index f60e6da2..96ca195f 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -24,7 +24,7 @@ import { import { RemotesModal } from "./connections/connections"; import { TosModal } from "./common/modals/modals"; import { MainSideBar } from "./sidebar/MainSideBar"; -import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal } from "./common/modals/modals"; +import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal, AboutModal } from "./common/modals/modals"; import { ErrorBoundary } from "./common/error/errorboundary"; import "./app.less"; @@ -127,6 +127,9 @@ class App extends React.Component<{}, {}> { + + + + + + + diff --git a/src/app/assets/waveterm-logo-with-bg.svg b/src/app/assets/waveterm-logo-with-bg.svg new file mode 100644 index 00000000..0c137c9d --- /dev/null +++ b/src/app/assets/waveterm-logo-with-bg.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/common/common.less b/src/app/common/common.less index 5685fe03..e91ae465 100644 --- a/src/app/common/common.less +++ b/src/app/common/common.less @@ -258,14 +258,12 @@ } .button.is-wave-green { - margin-top: 16px; display: flex; padding: 6px 16px !important; color: @term-bright-white !important; align-items: center; gap: 4px; border-radius: 6px !important; - font-size: 14px !important; height: auto !important; background: @term-green !important; box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5), diff --git a/src/app/common/modals/modals.less b/src/app/common/modals/modals.less index 5c687214..57fe495d 100644 --- a/src/app/common/modals/modals.less +++ b/src/app/common/modals/modals.less @@ -184,10 +184,9 @@ .modal.wave-modal { .modal-content { display: flex; - padding: 32px 48px; flex-direction: column; align-items: flex-start; - gap: 8px; + gap: 16px; border-radius: 10px; background: var(--olive-dark-1, #151715); box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.35), 0px 10px 24px 0px rgba(0, 0, 0, 0.45), @@ -201,28 +200,26 @@ width: 100%; header { - display: flex; - flex-direction: column; - align-items: center; - gap: var(--sizing-sm, 12px); width: 100%; + display: flex; + align-items: center; + padding: 12px 20px; + justify-content: space-between; + line-height: 20px; + border-bottom: 1px solid rgba(250, 250, 250, 0.1); .modal-title { color: @term-bright-white; - text-align: center; - font-size: 20px; font-style: normal; - font-weight: 300; line-height: 20px; + font-size: 13px; } - .modal-subtitle { - color: @term-white; - text-align: center; - - font-style: normal; - font-weight: 300; - line-height: 20px; + .close-icon-wrapper { + display: flex; + padding: 4px; + align-items: center; + gap: 8px; } } @@ -230,40 +227,77 @@ display: flex; flex-direction: column; align-items: flex-start; - gap: 32px; + gap: 24px; + width: 87%; + } + } + } +} + +.modal.tos-modal { + .modal-content.tos-modal-content { + padding: 32px 48px; + gap: 8px; + + header.tos-header { + flex-direction: column; + gap: var(--sizing-sm, 12px); + border-bottom: none; + padding: 0; + + .modal-title { + text-align: center; + font-size: 20px; + font-weight: 300; + } + + .modal-subtitle { + color: @term-white; + text-align: center; + + font-style: normal; + font-weight: 300; + line-height: 20px; + } + } + + .content.tos-content { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 32px; + width: 100%; + margin-bottom: 0; + + .item { + display: flex; width: 100%; - margin-bottom: 0; + align-items: center; + gap: 16px; - .item { + .item-inner { display: flex; - width: 100%; - align-items: center; - gap: 16px; + flex-direction: column; + align-items: flex-start; + gap: 4px; + flex: 1 0 0; - .item-inner { + .item-title { + color: @term-bright-white; + font-style: normal; + line-height: 20px; + } + + .item-text { + color: @term-white; + font-style: normal; + line-height: 20px; + } + + .item-field { display: flex; - flex-direction: column; - align-items: flex-start; - gap: 4px; - flex: 1 0 0; - - .item-title { - color: @term-bright-white; - font-style: normal; - line-height: 20px; - } - - .item-text { - color: @term-white; - font-style: normal; - line-height: 20px; - } - - .item-field { - display: flex; - align-items: center; - gap: 8px; - } + align-items: center; + gap: 8px; } } } @@ -276,6 +310,11 @@ align-items: center; justify-content: center; + button { + font-size: 12.5px !important; + margin-top: 16px; + } + button.disabled-button { cursor: default; } @@ -284,6 +323,156 @@ } } +.modal.about-modal { + .modal-content.about-modal-content { + width: 401px; + + .about-content { + margin-bottom: 0; + + section { + .logo-wrapper { + width: 72px; + height: 72px; + flex-shrink: 0; + + img { + border-radius: 10px; + } + } + + .text-wrapper { + display: flex; + align-items: flex-start; + flex-direction: column; + gap: 4px; + align-self: stretch; + font-style: normal; + line-height: 20px; + + div:first-child { + color: @term-bright-white; + font-size: 14.5px; + } + + div:last-child { + color: @term-white; + text-align: left; + } + } + + .status { + div { + display: flex; + align-items: center; + margin-bottom: 5px; + + i { + font-size: 16px; + margin-right: 10px; + } + } + + div:first-child + div { + color: @term-white; + } + } + + .status.updated { + div { + display: flex; + align-items: center; + margin-bottom: 5px; + + i { + color: @term-green; + } + } + } + + .status.outdated { + div { + i { + color: @term-yellow; + } + } + + button { + margin-top: 5px; + } + } + } + + section:nth-child(3) { + display: flex; + align-items: flex-start; + gap: 10px; + + .button-link { + display: flex; + align-items: center; + + i { + font-size: 16px; + } + } + } + + section:last-child { + margin-bottom: 24px; + color: @term-white; + } + } + } +} + +.button { + display: flex; + padding: 6px 16px; + align-items: center; + gap: var(--sizing-2-xs, 4px); + border-radius: 6px; + height: auto; +} + +.button.color-green { + color: @term-bright-white; + background: @term-green !important; // !important is needed to override the default button color + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5), + 0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset; + + &:hover { + color: @term-bright-white; + } +} + +.button.color-standard { + color: @term-white; + background: var(--overlays-white-6, rgba(255, 255, 255, 0.12)); + + &:hover { + color: @term-white; + } +} + +.button-link { + display: flex; + padding: 6px 16px; + align-items: center; + gap: var(--sizing-2-xs, 4px); + border-radius: 6px; + background: var(--overlays-white-6, rgba(255, 255, 255, 0.12)); + cursor: pointer; +} + +section { + display: flex; + align-items: center; + gap: 16px; + align-self: stretch; + width: 100%; +} + .modal.welcome-modal { footer { .prev-button { diff --git a/src/app/common/modals/modals.tsx b/src/app/common/modals/modals.tsx index 5d3427ed..3b5d261a 100644 --- a/src/app/common/modals/modals.tsx +++ b/src/app/common/modals/modals.tsx @@ -15,14 +15,18 @@ import * as util from "../../../util/util"; import { Toggle, Checkbox } from "../common"; import { ClientDataType } from "../../../types/types"; -import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg"; +import close from "../../assets/icons/close.svg"; import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg"; import shield from "../../assets/icons/shield_check.svg"; import help from "../../assets/icons/help_filled.svg"; import github from "../../assets/icons/github.svg"; +import logo from "../../assets/waveterm-logo-with-bg.svg"; dayjs.extend(localizedFormat); +// @ts-ignore +const VERSION = __PROMPT_VERSION__; + type OV = mobx.IObservableValue; @mobxReact.observer @@ -269,7 +273,7 @@ class WelcomeModal extends React.Component<{}, {}> {
-
welcome to [prompt]
+
About
@@ -346,13 +350,13 @@ class TosModal extends React.Component<{}, {}> { return (
-
+
-
+
Welcome to Wave Terminal!
Lets set everything for you
-
+
Privacy
@@ -429,4 +433,112 @@ class TosModal extends React.Component<{}, {}> { } } -export { WelcomeModal, LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal }; +@mobxReact.observer +class AboutModal extends React.Component<{}, {}> { + @boundMethod + closeModal(): void { + mobx.action(() => { + GlobalModel.aboutModalOpen.set(false); + })(); + } + + @boundMethod + isUpToDate(): boolean { + return true; + } + + @boundMethod + updateApp(): void { + // GlobalCommandRunner.updateApp(); + } + + @boundMethod + getStatus(isUpToDate: boolean): JSX.Element { + if (isUpToDate) { + return ( +
+
+ + Up to Date +
+
Client Version v0.4.0 20231016-110014
+
+ ); + } + return ( +
+
+ + Outdated Version +
+
Client Version v0.4.0 20231016-110014
+
+ +
+
+ ); + } + + render() { + return ( +
+
+
+
+
+
About
+
+ Close (Escape) +
+
+
+
+
+ logo +
+
+
Wave Terminal
+
Modern Terminal for Seamless Workflow
+
+
+
{this.getStatus(this.isUpToDate())}
+
+ + + Github + + + + Website + + + + License + +
+
Copyright © 2023 Command Line Inc.
+
+
+
+
+ ); + } +} + +export { WelcomeModal, LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal, AboutModal }; diff --git a/src/electron/emain.ts b/src/electron/emain.ts index 527bad30..317e6bd4 100644 --- a/src/electron/emain.ts +++ b/src/electron/emain.ts @@ -146,6 +146,14 @@ function readAuthKey() { let menuTemplate = [ { role: "appMenu", + submenu: [ + { + label: 'About Wave Terminal', + click: () => { + MainWindow?.webContents.send('menu-item-about'); + } + } + ], }, { label: "File", @@ -165,7 +173,7 @@ let menuTemplate = [ let menu = electron.Menu.buildFromTemplate(menuTemplate); electron.Menu.setApplicationMenu(menu); -let MainWindow = null; +let MainWindow: Electron.BrowserWindow | null = null; function getMods(input: any) { return { meta: input.meta, shift: input.shift, ctrl: input.control, alt: input.alt }; diff --git a/src/electron/preload.js b/src/electron/preload.js index 701d7ee3..2b07a1e9 100644 --- a/src/electron/preload.js +++ b/src/electron/preload.js @@ -19,6 +19,7 @@ contextBridge.exposeInMainWorld("api", { onMetaPageDown: (callback) => ipcRenderer.on("meta-pagedown", callback), onBracketCmd: (callback) => ipcRenderer.on("bracket-cmd", callback), onDigitCmd: (callback) => ipcRenderer.on("digit-cmd", callback), + onMenuItemAbout: (callback) => ipcRenderer.on("menu-item-about", callback), contextScreen: (screenOpts, position) => ipcRenderer.send("context-screen", screenOpts, position), contextEditMenu: (position, opts) => ipcRenderer.send("context-editmenu", position, opts), onWaveSrvStatusChange: (callback) => ipcRenderer.on("wavesrv-status-change", callback), diff --git a/src/model/model.ts b/src/model/model.ts index 3fcae67b..ed19ee0f 100644 --- a/src/model/model.ts +++ b/src/model/model.ts @@ -180,6 +180,7 @@ type ElectronApi = { onICmd: (callback: (mods: KeyModsType) => void) => void; onLCmd: (callback: (mods: KeyModsType) => void) => void; onHCmd: (callback: (mods: KeyModsType) => void) => void; + onMenuItemAbout: (callback: () => void) => void; onMetaArrowUp: (callback: () => void) => void; onMetaArrowDown: (callback: () => void) => void; onMetaPageUp: (callback: () => void) => void; @@ -2699,6 +2700,9 @@ class Model { welcomeModalOpen: OV = mobx.observable.box(false, { name: "welcomeModalOpen", }); + aboutModalOpen: OV = mobx.observable.box(false, { + name: "aboutModalOpen", + }); screenSettingsModal: OV<{ sessionId: string; screenId: string }> = mobx.observable.box(null, { name: "screenSettingsModal", }); @@ -2759,6 +2763,7 @@ class Model { getApi().onICmd(this.onICmd.bind(this)); getApi().onLCmd(this.onLCmd.bind(this)); getApi().onHCmd(this.onHCmd.bind(this)); + getApi().onMenuItemAbout(this.onMenuItemAbout.bind(this)); getApi().onMetaArrowUp(this.onMetaArrowUp.bind(this)); getApi().onMetaArrowDown(this.onMetaArrowDown.bind(this)); getApi().onMetaPageUp(this.onMetaPageUp.bind(this)); @@ -2977,6 +2982,10 @@ class Model { GlobalModel.welcomeModalOpen.set(false); didSomething = true; } + if (GlobalModel.welcomeModalOpen.get()) { + GlobalModel.welcomeModalOpen.set(false); + didSomething = true; + } })(); return didSomething; } @@ -3106,6 +3115,12 @@ class Model { } } + onMenuItemAbout(): void { + mobx.action(() => { + this.aboutModalOpen.set(true); + })(); + } + onMetaPageUp(): void { GlobalCommandRunner.screenSelectLine("-1"); }