From e5490df56c9a411ceee50c500fc0dd6781d425fd Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 20 Mar 2023 19:22:52 -0700 Subject: [PATCH] big checkpoint on cmd screen migration --- src/emain.ts | 8 +++ src/linecomps.tsx | 4 +- src/main.tsx | 95 +++++++++++++++++++++++++++++-- src/model.ts | 126 ++++++++++++++++------------------------- src/preload.js | 1 + src/sh2.less | 24 +++++++- src/simplerenderer.tsx | 3 +- src/term.ts | 2 +- src/types.ts | 15 +++-- 9 files changed, 183 insertions(+), 95 deletions(-) diff --git a/src/emain.ts b/src/emain.ts index 083482da..c4c1adb5 100644 --- a/src/emain.ts +++ b/src/emain.ts @@ -387,6 +387,14 @@ electron.ipcMain.on("restart-server", (event) => { return; }); +electron.ipcMain.on("reload-window", (event) => { + if (MainWindow != null) { + MainWindow.reload(); + } + event.returnValue = true; + return; +}); + function getContextMenu() : any { let menu = new electron.Menu(); let menuItem = new electron.MenuItem({label: "Testing", click: () => console.log("click testing!")}); diff --git a/src/linecomps.tsx b/src/linecomps.tsx index ef4bd8f4..c6195425 100644 --- a/src/linecomps.tsx +++ b/src/linecomps.tsx @@ -29,7 +29,7 @@ function isBlank(s : string) : boolean { } function getLineId(line : LineType) : string { - return sprintf("%s-%s-%s", line.sessionid, line.screenid, line.lineid); + return sprintf("%s-%s", line.screenid, line.lineid); } function makeFullRemoteRef(ownerName : string, remoteRef : string, name : string) : string { @@ -179,7 +179,7 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT } let {line} = this.props; this.rtnStateDiffFetched = true; - let usp = new URLSearchParams({sessionid: line.sessionid, cmdid: line.cmdid}); + let usp = new URLSearchParams({screenid: line.screenid, cmdid: line.cmdid}); let url = GlobalModel.getBaseHostPort() + "/api/rtnstate?" + usp.toString(); let fetchHeaders = GlobalModel.getFetchHeaders(); fetch(url, {headers: fetchHeaders}).then((resp) => { diff --git a/src/main.tsx b/src/main.tsx index ac99a350..8be36268 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,7 @@ import cn from "classnames"; import {debounce, throttle} from "throttle-debounce"; import {v4 as uuidv4} from "uuid"; import dayjs from "dayjs"; -import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts, RemoteEditType, FeStateType, ContextMenuOpts, BookmarkType, RenderModeType} from "./types"; +import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts, RemoteEditType, FeStateType, ContextMenuOpts, BookmarkType, RenderModeType, ClientMigrationInfo} from "./types"; import type * as T from "./types"; import localizedFormat from 'dayjs/plugin/localizedFormat'; import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, riToRPtr, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols, TabColors, RemoteColors} from "./model"; @@ -1642,7 +1642,7 @@ function getLineDateStr(todayDate : string, yesterdayDate : string, ts : number) } @mobxReact.observer -class LinesView extends React.Component<{screen : Screen, width : number, lines : LineType[], renderMode : RenderModeType}, {}> { +class LinesView extends React.Component<{sessionId : string, screen : Screen, width : number, lines : LineType[], renderMode : RenderModeType}, {}> { rszObs : any; linesRef : React.RefObject; staticRender : OV = mobx.observable.box(true, {name: "static-render"}); @@ -2158,7 +2158,7 @@ class ScreenWindowView extends React.Component<{screen : Screen}, {}> { 0}> - +
@@ -2701,6 +2701,56 @@ class DisconnectedModal extends React.Component<{}, {}> { } } +@mobxReact.observer +class ClientStopModal extends React.Component<{}, {}> { + @boundMethod + refreshClient() { + GlobalModel.refreshClient(); + } + + render() { + let model = GlobalModel; + let cdata = model.clientData.get(); + let mdata : ClientMigrationInfo = (cdata != null ? cdata.migration : null); + let title = "Client Not Ready"; + if (mdata != null) { + title = "Migrating Data"; + } + return ( +
+
+
+
+
[prompt] {title}
+
+
+ +
Cannot get client data.
+
+ +
Client database is being migrated to the latest version, please wait.
+ +
+ {mdata.migrationpos} +
+
{mdata.migrationpos}/{mdata.migrationtotal}
+
+
+
+
+ +
+
+
+ ); + } +} + @mobxReact.observer class LoadingSpinner extends React.Component<{}, {}> { render() { @@ -2856,6 +2906,8 @@ class WelcomeModal extends React.Component<{}, {}> { @mobxReact.observer class Main extends React.Component<{}, {}> { + dcWait : OV = mobx.observable.box(false, {name: "dcWait"}); + constructor(props : any) { super(props); } @@ -2887,10 +2939,44 @@ class Main extends React.Component<{}, {}> { } } + @boundMethod + updateDcWait(val : boolean) : void { + mobx.action(() => { + this.dcWait.set(val); + })(); + } + render() { let screenSettingsModal = GlobalModel.screenSettingsModal.get(); let sessionSettingsModal = GlobalModel.sessionSettingsModal.get(); let lineSettingsModal = GlobalModel.lineSettingsModal.get(); + let disconnected = !GlobalModel.ws.open.get() || !GlobalModel.localServerRunning.get(); + let hasClientStop = GlobalModel.getHasClientStop(); + let dcWait = this.dcWait.get(); + if (disconnected || hasClientStop) { + if (!dcWait) { + setTimeout(() => this.updateDcWait(true), 1500); + } + return ( +
+
+ +
+
+ + + + + + + + +
+ ); + } + if (dcWait) { + setTimeout(() => this.updateDcWait(false), 0); + } return (
@@ -2899,9 +2985,6 @@ class Main extends React.Component<{}, {}> {
- - - diff --git a/src/model.ts b/src/model.ts index aa6e090b..35f1af8f 100644 --- a/src/model.ts +++ b/src/model.ts @@ -5,7 +5,7 @@ import {debounce} from "throttle-debounce"; import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeDataMap, genMergeSimpleData, boundInt, isModKeyPress} from "./util"; import {TermWrap} from "./term"; import {v4 as uuidv4} from "uuid"; -import type {SessionDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize, RemoteInputPacketType, FeStateType, ContextMenuOpts, RendererContext, RendererModel, PtyDataType, BookmarkType, ClientDataType, HistoryViewDataType, AlertMessageType, HistorySearchParams, FocusTypeStrs, ScreenLinesType, HistoryTypeStrs, RendererPluginType, WindowSize} from "./types"; +import type {SessionDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize, RemoteInputPacketType, FeStateType, ContextMenuOpts, RendererContext, RendererModel, PtyDataType, BookmarkType, ClientDataType, HistoryViewDataType, AlertMessageType, HistorySearchParams, FocusTypeStrs, ScreenLinesType, HistoryTypeStrs, RendererPluginType, WindowSize, ClientMigrationInfo} from "./types"; import {WSControl} from "./ws"; import {measureText, getMonoFontSize} from "./textmeasure"; import dayjs from "dayjs"; @@ -69,7 +69,6 @@ function getRendererType(line : LineType) : "terminal" | "plugin" { function getRendererContext(line : LineType) : RendererContext { return { - sessionId: line.sessionid, screenId: line.screenid, cmdId: line.cmdid, lineId: line.lineid, @@ -156,6 +155,7 @@ type ElectronApi = { getAuthKey : () => string, getLocalServerStatus : () => boolean, restartLocalServer : () => boolean, + reloadWindow : () => void, onTCmd : (callback : (mods : KeyModsType) => void) => void, onICmd : (callback : (mods : KeyModsType) => void) => void, onLCmd : (callback : (mods : KeyModsType) => void) => void, @@ -175,10 +175,6 @@ function getApi() : ElectronApi { return (window as any).api; } -function getLineId(line : LineType) : string { - return sprintf("%s-%s-%s", line.sessionid, line.screenid, line.lineid); -} - // clean empty string function ces(s : string) { if (s == "") { @@ -188,7 +184,6 @@ function ces(s : string) { } class Cmd { - sessionId : string; screenId : string; remote : RemotePtrType; remoteId : string; @@ -196,7 +191,6 @@ class Cmd { data : OV; constructor(cmd : CmdDataType) { - this.sessionId = cmd.sessionid; this.screenId = cmd.screenid; this.cmdId = cmd.cmdid; this.remote = cmd.remote; @@ -295,7 +289,7 @@ class Cmd { handleInputChunk(data : string) : void { let inputPacket : FeInputPacketType = { type: "feinput", - ck: this.sessionId + "/" + this.cmdId, + ck: this.screenId + "/" + this.cmdId, remote: this.remote, inputdata64: btoa(data), }; @@ -1745,7 +1739,7 @@ class SpecialHistoryViewLineContainer { if (line.contentheight != null && line.contentheight != -1) { usedRows = line.contentheight; } - let termContext = {sessionId: line.sessionid, screenId: line.screenid, cmdId: cmdId, lineId : line.lineid, lineNum: line.linenum}; + let termContext = {screenId: line.screenid, cmdId: cmdId, lineId : line.lineid, lineNum: line.linenum}; termWrap = new TermWrap(elem, { termContext: termContext, usedRows: usedRows, @@ -2371,6 +2365,7 @@ class Model { bookmarksModel : BookmarksModel; historyViewModel : HistoryViewModel; clientData : OV = mobx.observable.box(null, {name: "clientData"}); + clientMigrationInfo : OV = mobx.observable.box(null, {name: "clientMigrationInfo"}); constructor() { this.clientId = getApi().getId(); @@ -2413,6 +2408,10 @@ class Model { setTimeout(() => this.getClientDataLoop(1), 10); } + refreshClient() : void { + getApi().reloadWindow(); + } + registerRendererPlugin(plugin : RendererPluginType) { if (isBlank(plugin.name)) { throw new Error("invalid plugin, no name"); @@ -2427,6 +2426,17 @@ class Model { this.rendererPlugins.push(plugin); } + getHasClientStop() : boolean { + if (this.clientData.get() == null) { + return true; + } + let cdata = this.clientData.get(); + if (cdata.cmdstoretype == "session") { + return true; + } + return false; + } + getRendererPluginByName(name : string) : RendererPluginType { for (let i=0; i s.sessionId, (sdata : SessionDataType) => sdata.sessionid, (sdata : SessionDataType) => new Session(sdata), (s : Session) => s.sessionIdx.get()); - if (!("activesessionid" in update)) { - let newActiveScreen = this.getActiveScreen(); - if (oldActiveScreen != newActiveScreen) { - if (newActiveScreen == null) { - this._activateScreen(this.activeSessionId.get(), null, oldActiveScreen); - } - else { - this._activateScreen(newActiveScreen.sessionId, newActiveScreen.screenId, oldActiveScreen); - } + if ("activesessionid" in update) { + let newSessionId = update.activesessionid; + if (this.activeSessionId.get() != newSessionId) { + this.activeSessionId.set(newSessionId); } } - } - if ("activesessionid" in update) { - this._activateSession(update.activesessionid); + let [newActiveSessionId, newActiveScreenId] = this.getActiveIds(); + if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) { + this.activeMainView.set("session"); + this.deactivateScreenLines(); + this.ws.watchScreen(newActiveSessionId, newActiveScreenId); + } } if ("line" in update) { this.addLineCmd(update.line, update.cmd, interactive); @@ -2946,17 +2954,18 @@ class Model { getClientDataLoop(loopNum : number) : void { this.getClientData(); - if (this.clientData.get() != null) { + let clientStop = this.getHasClientStop() + if (this.clientData.get() != null && !clientStop) { return; } let timeoutMs = 1000; - if (loopNum > 5) { + if (!clientStop && loopNum > 5) { timeoutMs = 3000; } - if (loopNum > 10) { + if (!clientStop && loopNum > 10) { timeoutMs = 10000; } - if (loopNum > 15) { + if (!clientStop && loopNum > 15) { timeoutMs = 30000; } setTimeout(() => this.getClientDataLoop(loopNum+1), timeoutMs); @@ -2969,6 +2978,7 @@ class Model { mobx.action(() => { let clientData = data.data; this.clientData.set(clientData); + this.clientMigrationInfo.set(clientData.migration); })(); }).catch((err) => { this.errorHandler("calling get-client-data", err, true); @@ -3032,45 +3042,14 @@ class Model { return this.submitCommandPacket(pk, interactive) } - _activateSession(sessionId : string) { - mobx.action(() => { - this.activeMainView.set("session"); - })(); - let oldActiveSession = this.getActiveSession(); - if (oldActiveSession != null && oldActiveSession.sessionId == sessionId) { - return; - } - let newSession = this.getSessionById(sessionId); - if (newSession == null) { - return; - } - this._activateScreen(sessionId, newSession.activeScreenId.get()); - } - - _activateScreen(sessionId : string, screenId : string, oldActiveScreen? : Screen) { - mobx.action(() => { - this.activeMainView.set("session"); - })(); - if (!oldActiveScreen) { - oldActiveScreen = this.getActiveScreen(); - } - if (oldActiveScreen && oldActiveScreen.sessionId == sessionId && oldActiveScreen.screenId == screenId) { - return; - } - mobx.action(() => { - this.deactivateScreenLines(); - let curSessionId = this.activeSessionId.get(); - if (curSessionId != sessionId) { - this.activeSessionId.set(sessionId); - } - this.getActiveSession().activeScreenId.set(screenId); - })(); - let curScreen = this.getActiveScreen(); - if (curScreen == null) { - this.ws.watchScreen(sessionId, null); - return; - } - this.ws.watchScreen(curScreen.sessionId, curScreen.screenId); + // returns [sessionId, screenId] + getActiveIds() : [string, string] { + let activeSession = this.getActiveSession(); + let activeScreen = this.getActiveScreen(); + return [ + (activeSession == null ? null : activeSession.sessionId), + (activeScreen == null ? null : activeScreen.screenId), + ]; } _loadScreenLinesAsync(newWin : ScreenLines) { @@ -3130,10 +3109,6 @@ class Model { } getCmd(line : LineType) : Cmd { - let session = this.getSessionById(line.sessionid); - if (session == null) { - return null; - } let slines = this.getScreenLinesById(line.screenid); if (slines == null) { return null; @@ -3338,7 +3313,6 @@ class CommandRunner { setTermUsedRows(termContext : RendererContext, height : number) { let kwargs : Record = {}; - kwargs["session"] = termContext.sessionId; kwargs["screen"] = termContext.screenId; kwargs["hohist"] = "1"; let posargs = [String(termContext.lineNum), String(height)]; @@ -3466,8 +3440,8 @@ function _getPtyDataFromUrl(url : string) : Promise { }); } -function getPtyData(sessionId : string, cmdId : string) : Promise { - let url = sprintf(GlobalModel.getBaseHostPort() + "/api/ptyout?sessionid=%s&cmdid=%s", sessionId, cmdId); +function getPtyData(screenId : string, cmdId : string) : Promise { + let url = sprintf(GlobalModel.getBaseHostPort() + "/api/ptyout?screenid=%s&cmdid=%s", screenId, cmdId); return _getPtyDataFromUrl(url); } diff --git a/src/preload.js b/src/preload.js index 75f6b8b3..546afb77 100644 --- a/src/preload.js +++ b/src/preload.js @@ -6,6 +6,7 @@ contextBridge.exposeInMainWorld("api", { getAuthKey: () => ipcRenderer.sendSync("get-authkey"), getLocalServerStatus: () => ipcRenderer.sendSync("local-server-status"), restartLocalServer: () => ipcRenderer.sendSync("restart-server"), + reloadWindow: () => ipcRenderer.sendSync("reload-window"), onTCmd: (callback) => ipcRenderer.on("t-cmd", callback), onICmd: (callback) => ipcRenderer.on("i-cmd", callback), onLCmd: (callback) => ipcRenderer.on("l-cmd", callback), diff --git a/src/sh2.less b/src/sh2.less index 7c535923..91fd39fd 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -1746,7 +1746,7 @@ body .xterm .xterm-viewport { } .line.line-invalid { - color: #000; + color: @term-white; margin-left: 5px; } @@ -2421,9 +2421,7 @@ input[type=checkbox] { } } } -} -.disconnected-modal { .inner-content { .ws-log { padding: 5px; @@ -2439,6 +2437,26 @@ input[type=checkbox] { } } +.modal.prompt-modal.client-stop-modal { + footer { + justify-content: center; + } + + .inner-content { + display: flex; + flex-direction: column; + padding: 20px; + .progress-container { + margin-top: 20px; + } + .progress-text { + color: @term-white; + align-self: center; + font-size: 12px; + } + } +} + .remote-status { font-size: 8px; margin-right: 5px; diff --git a/src/simplerenderer.tsx b/src/simplerenderer.tsx index 5f9b3af6..c483fdd5 100644 --- a/src/simplerenderer.tsx +++ b/src/simplerenderer.tsx @@ -72,7 +72,7 @@ class SimpleBlobRendererModel { mobx.action(() => { this.loading.set(true); })(); - let rtnp = getPtyData(this.context.sessionId, this.context.cmdId); + let rtnp = getPtyData(this.context.screenId, this.context.cmdId); rtnp.then((ptydata) => { setTimeout(() => { this.ptyData = ptydata; @@ -96,7 +96,6 @@ class SimpleBlobRendererModel { function contextFromLine(line : LineType) : RendererContext { return { - sessionId: line.sessionid, screenId: line.screenid, cmdId: line.cmdid, lineId: line.lineid, diff --git a/src/term.ts b/src/term.ts index 8d56a010..86300f01 100644 --- a/src/term.ts +++ b/src/term.ts @@ -278,7 +278,7 @@ class TermWrap { } else { let termContext = this.getRendererContext(); - rtnp = getPtyData(termContext.sessionId, termContext.cmdId); + rtnp = getPtyData(termContext.screenId, termContext.cmdId); } rtnp.then((ptydata) => { setTimeout(() => { diff --git a/src/types.ts b/src/types.ts index 3f142649..90bb49a1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,7 +23,6 @@ type SessionDataType = { }; type LineType = { - sessionid : string, screenid : string, userid : string, lineid : string, @@ -217,7 +216,6 @@ type CmdDoneInfoType = { }; type CmdDataType = { - sessionid : string, screenid : string, cmdid : string, remote : RemotePtrType, @@ -234,7 +232,6 @@ type CmdDataType = { }; type PtyDataUpdateType = { - sessionid : string, screenid : string, cmdid : string, remoteid : string, @@ -344,7 +341,6 @@ type ContextMenuOpts = { type UpdateMessage = PtyDataUpdateType | ModelUpdateType; type RendererContext = { - sessionId : string, screenId : string, cmdId : string, lineId : string, @@ -423,6 +419,15 @@ type ClientDataType = { clientid : string, userid : string, feopts : FeOptsType; + cmdstoretype : "session" | "screen"; + migration? : ClientMigrationInfo; +}; + +type ClientMigrationInfo = { + migrationtype : string, + migrationpos : number, + migrationtotal : number, + migrationdone : boolean, }; type PlaybookType = { @@ -463,4 +468,4 @@ type HistorySearchParams = { type RenderModeType = "normal" | "collapsed"; -export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, FeStateType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate}; +export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, FeStateType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate, ClientMigrationInfo};