diff --git a/src/main.tsx b/src/main.tsx index 97a66a5a0..b3d01c571 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,75 +6,8 @@ import {boundMethod} from "autobind-decorator"; import * as dayjs from 'dayjs' import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components"; import cn from "classnames" -import {GlobalWS} from "./ws"; -import {TermWrap, getOrCreateTermWrap} from "./term"; - -type LineType = { - sessionid : string, - windowid : string, - lineid : number, - ts : number, - userid : string, - linetype : string, - text : string, - cmdid : string, - cmdtext : string, - isnew : boolean, -}; - -type SessionType = { - sessionid : string; - name : string; - windows : WindowType[]; -}; - -type WindowType = { - windowid : string; - name : string; - lines : LineType[]; -}; - -var GlobalUser = "sawka"; -var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1"; -var GWindowId = "1"; - -var GlobalLines = mobx.observable.box([ - {sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"}, - {sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"}, -]); - -function fetchJsonData(resp : any, ctErr : boolean) : Promise { - let contentType = resp.headers.get("Content-Type"); - if (contentType != null && contentType.startsWith("application/json")) { - return resp.text().then((textData) => { - try { - return JSON.parse(textData); - } - catch (err) { - let errMsg = sprintf("Unparseable JSON: " + err.message); - let rtnErr = new Error(errMsg); - throw rtnErr; - } - }); - } - if (ctErr) { - throw new Error("non-json content-type"); - } -} - -function handleJsonFetchResponse(url : URL, resp : any) : Promise { - if (!resp.ok) { - let errData = fetchJsonData(resp, false); - if (errData && errData["error"]) { - throw new Error(errData["error"]) - } - let errMsg = sprintf("Bad status code response from fetch '%s': %d %s", url.toString(), resp.status, resp.statusText); - let rtnErr = new Error(errMsg); - throw rtnErr; - } - let rtnData = fetchJsonData(resp, true); - return rtnData; -} +import {TermWrap} from "./term"; +import {getDefaultSession} from "./session"; @mobxReact.observer class LineMeta extends React.Component<{line : LineType}, {}> { @@ -91,13 +24,13 @@ class LineMeta extends React.Component<{line : LineType}, {}> { } @mobxReact.observer -class LineText extends React.Component<{line : LineType}, {}> { +class LineText extends React.Component<{line : LineType, session : Session}, {}> { render() { let line = this.props.line; return (
- M + S
@@ -114,20 +47,16 @@ class LineText extends React.Component<{line : LineType}, {}> { } @mobxReact.observer -class LineCmd extends React.Component<{line : LineType}, {}> { - termWrap : TermWrap; - +class LineCmd extends React.Component<{line : LineType, session : Session}, {}> { constructor(props) { super(props); - let {line} = this.props; - this.termWrap = new TermWrap(line.sessionid, line.cmdid, line.windowid, line.lineid); } componentDidMount() { - let {line} = this.props; + let {session, line} = this.props; let termElem = document.getElementById(this.getId()); - this.termWrap.connectToElem(termElem); - this.termWrap.reloadTerminal(0); + let termWrap = session.getTermWrap(line); + termWrap.connectToElem(termElem); if (line.isnew) { setTimeout(() => { let lineElem = document.getElementById("line-" + this.getId()); @@ -137,7 +66,7 @@ class LineCmd extends React.Component<{line : LineType}, {}> { })(); }, 100); setTimeout(() => { - this.termWrap.reloadTerminal(0); + termWrap.reloadTerminal(0); }, 1000); } } @@ -149,7 +78,9 @@ class LineCmd extends React.Component<{line : LineType}, {}> { @boundMethod doRefresh() { - this.termWrap.reloadTerminal(500); + let {session, line} = this.props; + let termWrap = session.getTermWrap(line); + termWrap.reloadTerminal(500); } @boundMethod @@ -169,14 +100,15 @@ class LineCmd extends React.Component<{line : LineType}, {}> { } render() { - let {line} = this.props; + let {session, line} = this.props; let lineid = line.lineid.toString(); let running = false; let rows = 0; let cols = 0; - let renderVersion = this.termWrap.getRenderVersion(); - this.termWrap.resizeToContent(); - let termSize = this.termWrap.getSize(); + let termWrap = session.getTermWrap(line); + let renderVersion = termWrap.getRenderVersion(); + termWrap.resizeToContent(); + let termSize = termWrap.getSize(); return (
= 5}, {"running": running})}> @@ -186,10 +118,10 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
{line.userid}
{dayjs(line.ts).format("hh:mm:ss a")}
-
{line.cmdid} 0}>({termSize.rows}x{termSize.cols}) {this.termWrap.ptyPos} bytes, v{renderVersion}
+
{line.cmdid} 0}>({termSize.rows}x{termSize.cols}) {termWrap.ptyPos} bytes, v{renderVersion}
> {this.singleLineCmdText(line.cmdtext)}
-
+
@@ -202,7 +134,7 @@ class LineCmd extends React.Component<{line : LineType}, {}> { } @mobxReact.observer -class Line extends React.Component<{line : LineType}, {}> { +class Line extends React.Component<{line : LineType, session : Session}, {}> { render() { let line = this.props.line; if (line.linetype == "text") { @@ -216,7 +148,7 @@ class Line extends React.Component<{line : LineType}, {}> { } @mobxReact.observer -class CmdInput extends React.Component<{sessionid : string, windowid : string}, {}> { +class CmdInput extends React.Component<{session : Session, windowid : string}, {}> { curLine : mobx.IObservableValue = mobx.observable("", {name: "command-line"}); @mobx.action @boundMethod @@ -241,22 +173,12 @@ class CmdInput extends React.Component<{sessionid : string, windowid : string}, @boundMethod doSubmitCmd() { - let {sessionid, windowid} = this.props; + let {session, windowid} = this.props; let commandStr = this.curLine.get(); mobx.action(() => { this.curLine.set(""); })(); - let url = sprintf("http://localhost:8080/api/run-command"); - let data = {sessionid: sessionid, windowid: windowid, command: commandStr, userid: GlobalUser}; - fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { - mobx.action(() => { - let lines = GlobalLines.get(); - data.data.line.isnew = true; - lines.push(data.data.line); - })(); - }).catch((err) => { - console.log("error calling run-command", err) - }); + session.submitCommand(windowid, commandStr); } render() { @@ -291,16 +213,16 @@ class CmdInput extends React.Component<{sessionid : string, windowid : string}, class SessionView extends React.Component<{session : SessionType}, {}> { render() { let session = this.props.session; - let window = session.windows[0]; + let window = session.getActiveWindow(); let lines = window.lines; return (
- +
- +
); } @@ -313,14 +235,7 @@ class Main extends React.Component<{}, {}> { } render() { - let windowLines = GlobalLines.get(); - let session : SessionType = { - sessionid: GSessionId, - name: "default", - windows: [ - {windowid: GWindowId, name: "default", lines: windowLines}, - ], - }; + let session = getDefaultSession(); return (

diff --git a/src/session.ts b/src/session.ts new file mode 100644 index 000000000..3323b4291 --- /dev/null +++ b/src/session.ts @@ -0,0 +1,99 @@ +import * as mobx from "mobx"; +import {sprintf} from "sprintf-js"; +import {boundMethod} from "autobind-decorator"; +import {handleJsonFetchResponse} from "./util"; +import {GlobalWS} from "./ws"; +import {TermWrap, makeTermKey} from "./term"; + +var GlobalUser = "sawka"; +var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1"; +var GWindowId = "1"; + +var GlobalLines = mobx.observable.box([ + {sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"}, + {sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"}, +]); + +type LineType = { + sessionid : string, + windowid : string, + lineid : number, + ts : number, + userid : string, + linetype : string, + text : string, + cmdid : string, + cmdtext : string, + isnew : boolean, +}; + +type WindowType = { + sessionid : string; + windowid : string; + name : string; + lines : LineType[]; +}; + +class Session { + sessionid : string; + name : string; + windows : WindowType[]; + activeWindowId : string; + termMap : Record = {}; + + submitCommand(windowid : string, commandStr : string) { + let url = sprintf("http://localhost:8080/api/run-command"); + let data = {sessionid: this.sessionid, windowid: windowid, command: commandStr, userid: GlobalUser}; + fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { + mobx.action(() => { + let lines = GlobalLines.get(); + data.data.line.isnew = true; + lines.push(data.data.line); + })(); + }).catch((err) => { + console.log("error calling run-command", err) + }); + } + + getTermWrap(line : LineType) { + let termKey = makeTermKey(line.sessionid, line.cmdid, line.windowid, line.lineid); + let termWrap = this.termMap[termKey]; + if (termWrap != null) { + return termWrap; + } + termWrap = new TermWrap(line.sessionid, line.cmdid, line.windowid, line.lineid); + this.termMap[termKey] = termWrap; + termWrap.initialized = true; + termWrap.reloadTerminal(0); + return termWrap; + } + + getActiveWindow() : WindowType { + if (this.windows == null) { + return null; + } + for (let i=0; i; - -function getOrCreateTermWrap(sessionId : string, cmdId : string, windowId : string, lineid : number) : TermWrap { - let termKey = makeTermKey(sessionId, cmdId, windowId, lineid); - let termWrap = TermMap[termKey]; - if (termWrap != null) { - return termWrap; - } - termWrap = new TermWrap(sessionId, cmdId, windowId, lineid); - return termWrap; -} - function makeTermKey(sessionId : string, cmdId : string, windowId : string, lineid : number) : string { return sprintf("%s/%s/%s/%s", sessionId, cmdId, windowId, lineid); } @@ -57,7 +44,6 @@ class TermWrap { this.windowId = windowId; this.lineid = lineid; this.terminal = new Terminal({rows: 2, cols: 80}); - TermMap[cmdId] = this; } destroy() { @@ -150,9 +136,4 @@ class TermWrap { } } -if (window.TermMap == null) { - TermMap = {}; - window.TermMap = TermMap; -} - -export {TermWrap, TermMap, makeTermKey, getOrCreateTermWrap}; +export {TermWrap, makeTermKey}; diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 000000000..71b106608 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,34 @@ +function fetchJsonData(resp : any, ctErr : boolean) : Promise { + let contentType = resp.headers.get("Content-Type"); + if (contentType != null && contentType.startsWith("application/json")) { + return resp.text().then((textData) => { + try { + return JSON.parse(textData); + } + catch (err) { + let errMsg = sprintf("Unparseable JSON: " + err.message); + let rtnErr = new Error(errMsg); + throw rtnErr; + } + }); + } + if (ctErr) { + throw new Error("non-json content-type"); + } +} + +function handleJsonFetchResponse(url : URL, resp : any) : Promise { + if (!resp.ok) { + let errData = fetchJsonData(resp, false); + if (errData && errData["error"]) { + throw new Error(errData["error"]) + } + let errMsg = sprintf("Bad status code response from fetch '%s': %d %s", url.toString(), resp.status, resp.statusText); + let rtnErr = new Error(errMsg); + throw rtnErr; + } + let rtnData = fetchJsonData(resp, true); + return rtnData; +} + +export {handleJsonFetchResponse};