mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-02 04:02:13 +01:00
error boundary for screens (#45)
* error boundary for screens * error boundary for plugins * remove changes.diff * move load-error-text class to app.less * implement mikes suggestions * apply error boundary to workspace view * fix minor issues
This commit is contained in:
parent
49f18a3e94
commit
7e9e76f089
@ -373,6 +373,15 @@ a.a-block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.load-error-text {
|
||||||
|
color: @term-red;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-error {
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes loader-ring {
|
@keyframes loader-ring {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
@ -24,6 +24,7 @@ import { TosModal } from "./common/modals/modals";
|
|||||||
import { WorkspaceView } from "../app/workspace/workspaceview";
|
import { WorkspaceView } from "../app/workspace/workspaceview";
|
||||||
import { MainSideBar } from "./sidebar/MainSideBar";
|
import { MainSideBar } from "./sidebar/MainSideBar";
|
||||||
import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal } from "./common/modals/modals";
|
import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal } from "./common/modals/modals";
|
||||||
|
import { ErrorBoundary } from "./common/error/errorboundary";
|
||||||
import "./app.less";
|
import "./app.less";
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
@ -110,9 +111,11 @@ class App extends React.Component<{}, {}> {
|
|||||||
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
|
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
|
||||||
<div className="main-content">
|
<div className="main-content">
|
||||||
<MainSideBar />
|
<MainSideBar />
|
||||||
<WorkspaceView />
|
<ErrorBoundary>
|
||||||
<HistoryView />
|
<WorkspaceView />
|
||||||
<BookmarksView />
|
<HistoryView />
|
||||||
|
<BookmarksView />
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
<AlertModal />
|
<AlertModal />
|
||||||
<If condition={GlobalModel.needsTos()}>
|
<If condition={GlobalModel.needsTos()}>
|
||||||
|
65
src/app/common/error/errorboundary.tsx
Normal file
65
src/app/common/error/errorboundary.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React, { Component, ReactNode } from "react";
|
||||||
|
import { RendererContext } from "../../../plugins/types/types";
|
||||||
|
import cn from "classnames";
|
||||||
|
|
||||||
|
interface ErrorBoundaryState {
|
||||||
|
hasError: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorBoundaryProps {
|
||||||
|
children: ReactNode;
|
||||||
|
plugin?: string;
|
||||||
|
lineContext?: RendererContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||||
|
state: ErrorBoundaryState = {
|
||||||
|
hasError: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||||
|
return { hasError: true, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
||||||
|
const { plugin, lineContext } = this.props;
|
||||||
|
|
||||||
|
if (plugin && lineContext) {
|
||||||
|
console.log(`Plugin Name: ${plugin}\n`);
|
||||||
|
console.log(`Line Context: \n`);
|
||||||
|
console.log(`${JSON.stringify(lineContext, null, 4)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetErrorBoundary = (): void => {
|
||||||
|
this.setState({ hasError: false, error: null });
|
||||||
|
};
|
||||||
|
|
||||||
|
renderFallback() {
|
||||||
|
const { error } = this.state;
|
||||||
|
const { plugin } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("load-error-text", { "view-error": !plugin })}>
|
||||||
|
<div>{`${error?.message}`}</div>
|
||||||
|
{plugin && <div>An error occurred in the {plugin} plugin</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hasError } = this.state;
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
return this.renderFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ErrorBoundary };
|
@ -38,6 +38,7 @@ import { isBlank } from "../../util/util";
|
|||||||
import { PluginModel } from "../../plugins/plugins";
|
import { PluginModel } from "../../plugins/plugins";
|
||||||
import { Prompt } from "../common/prompt/prompt";
|
import { Prompt } from "../common/prompt/prompt";
|
||||||
import * as lineutil from "./lineutil";
|
import * as lineutil from "./lineutil";
|
||||||
|
import { ErrorBoundary } from "../../app/common/error/errorboundary";
|
||||||
|
|
||||||
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||||
import { ReactComponent as CommentIcon } from "../assets/icons/line/comment.svg";
|
import { ReactComponent as CommentIcon } from "../assets/icons/line/comment.svg";
|
||||||
@ -594,7 +595,6 @@ class LineCmd extends React.Component<
|
|||||||
let isRunning = cmd.isRunning();
|
let isRunning = cmd.isRunning();
|
||||||
let isExpanded = this.isCmdExpanded.get();
|
let isExpanded = this.isCmdExpanded.get();
|
||||||
let rsdiff = this.rtnStateDiff.get();
|
let rsdiff = this.rtnStateDiff.get();
|
||||||
// console.log("render", "#" + line.linenum, termHeight, usedRows, cmd.getStatus(), (this.rtnStateDiff.get() != null), (!cmd.isRunning() ? "cmd-done" : "running"));
|
|
||||||
let mainDivCn = cn(
|
let mainDivCn = cn(
|
||||||
"line",
|
"line",
|
||||||
"line-cmd",
|
"line-cmd",
|
||||||
@ -664,39 +664,41 @@ class LineCmd extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<If condition={!this.isMinimised.get()}>
|
<If condition={!this.isMinimised.get()}>
|
||||||
<If condition={rendererPlugin == null && !isNoneRenderer}>
|
<ErrorBoundary plugin={rendererPlugin?.name} lineContext={lineutil.getRendererContext(line)}>
|
||||||
<TerminalRenderer
|
<If condition={rendererPlugin == null && !isNoneRenderer}>
|
||||||
screen={screen}
|
<TerminalRenderer
|
||||||
line={line}
|
screen={screen}
|
||||||
width={width}
|
line={line}
|
||||||
staticRender={staticRender}
|
width={width}
|
||||||
visible={visible}
|
staticRender={staticRender}
|
||||||
onHeightChange={this.handleHeightChange}
|
visible={visible}
|
||||||
collapsed={false}
|
onHeightChange={this.handleHeightChange}
|
||||||
/>
|
collapsed={false}
|
||||||
</If>
|
/>
|
||||||
<If condition={rendererPlugin != null && rendererPlugin.rendererType == "simple"}>
|
</If>
|
||||||
<SimpleBlobRenderer
|
<If condition={rendererPlugin != null && rendererPlugin.rendererType == "simple"}>
|
||||||
rendererContainer={screen}
|
<SimpleBlobRenderer
|
||||||
lineId={line.lineid}
|
rendererContainer={screen}
|
||||||
plugin={rendererPlugin}
|
lineId={line.lineid}
|
||||||
onHeightChange={this.handleHeightChange}
|
plugin={rendererPlugin}
|
||||||
initParams={this.makeRendererModelInitializeParams()}
|
onHeightChange={this.handleHeightChange}
|
||||||
scrollToBringIntoViewport={this.scrollToBringIntoViewport}
|
initParams={this.makeRendererModelInitializeParams()}
|
||||||
isSelected={isSelected}
|
scrollToBringIntoViewport={this.scrollToBringIntoViewport}
|
||||||
shouldFocus={shouldCmdFocus}
|
isSelected={isSelected}
|
||||||
/>
|
shouldFocus={shouldCmdFocus}
|
||||||
</If>
|
/>
|
||||||
<If condition={rendererPlugin != null && rendererPlugin.rendererType == "full"}>
|
</If>
|
||||||
<IncrementalRenderer
|
<If condition={rendererPlugin != null && rendererPlugin.rendererType == "full"}>
|
||||||
rendererContainer={screen}
|
<IncrementalRenderer
|
||||||
lineId={line.lineid}
|
rendererContainer={screen}
|
||||||
plugin={rendererPlugin}
|
lineId={line.lineid}
|
||||||
onHeightChange={this.handleHeightChange}
|
plugin={rendererPlugin}
|
||||||
initParams={this.makeRendererModelInitializeParams()}
|
onHeightChange={this.handleHeightChange}
|
||||||
isSelected={isSelected}
|
initParams={this.makeRendererModelInitializeParams()}
|
||||||
/>
|
isSelected={isSelected}
|
||||||
</If>
|
/>
|
||||||
|
</If>
|
||||||
|
</ErrorBoundary>
|
||||||
<If condition={cmd.getRtnState()}>
|
<If condition={cmd.getRtnState()}>
|
||||||
<div
|
<div
|
||||||
key="rtnstate"
|
key="rtnstate"
|
||||||
|
@ -11,6 +11,7 @@ import { GlobalModel } from "../../model/model";
|
|||||||
import { CmdInput } from "./cmdinput/cmdinput";
|
import { CmdInput } from "./cmdinput/cmdinput";
|
||||||
import { ScreenView } from "./screen/screenview";
|
import { ScreenView } from "./screen/screenview";
|
||||||
import { ScreenTabs } from "./screen/tabs";
|
import { ScreenTabs } from "./screen/tabs";
|
||||||
|
import { ErrorBoundary } from "../../app/common/error/errorboundary";
|
||||||
import "./workspace.less";
|
import "./workspace.less";
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
@ -31,12 +32,15 @@ class WorkspaceView extends React.Component<{}, {}> {
|
|||||||
cmdInputHeight = 110;
|
cmdInputHeight = 110;
|
||||||
}
|
}
|
||||||
let isHidden = GlobalModel.activeMainView.get() != "session";
|
let isHidden = GlobalModel.activeMainView.get() != "session";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("session-view", { "is-hidden": isHidden })} data-sessionid={session.sessionId}>
|
<div className={cn("session-view", { "is-hidden": isHidden })} data-sessionid={session.sessionId}>
|
||||||
<ScreenTabs session={session} />
|
<ScreenTabs session={session} />
|
||||||
<ScreenView screen={activeScreen} />
|
<ErrorBoundary>
|
||||||
<div style={{ height: cmdInputHeight }}></div>
|
<ScreenView screen={activeScreen} />
|
||||||
<CmdInput />
|
<div style={{ height: cmdInputHeight }}></div>
|
||||||
|
<CmdInput />
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user