errorboundary fallback, errorboundary in block frame header. fix workspace error boundary

This commit is contained in:
sawka 2024-09-09 18:19:41 -07:00
parent 639e796296
commit 766a976718
3 changed files with 41 additions and 15 deletions

View File

@ -28,6 +28,7 @@ import {
} from "@/app/store/global"; } from "@/app/store/global";
import * as services from "@/app/store/services"; import * as services from "@/app/store/services";
import { WshServer } from "@/app/store/wshserver"; import { WshServer } from "@/app/store/wshserver";
import { ErrorBoundary } from "@/element/errorboundary";
import { IconButton } from "@/element/iconbutton"; import { IconButton } from "@/element/iconbutton";
import { MagnifyIcon } from "@/element/magnify"; import { MagnifyIcon } from "@/element/magnify";
import { NodeModel } from "@/layout/index"; import { NodeModel } from "@/layout/index";
@ -116,7 +117,7 @@ function computeEndIcons(
onContextMenu: (e: React.MouseEvent<HTMLDivElement>) => void onContextMenu: (e: React.MouseEvent<HTMLDivElement>) => void
): JSX.Element[] { ): JSX.Element[] {
const endIconsElem: JSX.Element[] = []; const endIconsElem: JSX.Element[] = [];
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons); const endIconButtons = util.useAtomValueSafe(viewModel?.endIconButtons);
const magnified = jotai.useAtomValue(nodeModel.isMagnified); const magnified = jotai.useAtomValue(nodeModel.isMagnified);
const numLeafs = jotai.useAtomValue(nodeModel.numLeafs); const numLeafs = jotai.useAtomValue(nodeModel.numLeafs);
const magnifyDisabled = numLeafs <= 1; const magnifyDisabled = numLeafs <= 1;
@ -155,15 +156,16 @@ const BlockFrame_Header = ({
preview, preview,
connBtnRef, connBtnRef,
changeConnModalAtom, changeConnModalAtom,
}: BlockFrameProps & { changeConnModalAtom: jotai.PrimitiveAtom<boolean> }) => { error,
}: BlockFrameProps & { changeConnModalAtom: jotai.PrimitiveAtom<boolean>; error?: Error }) => {
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId)); const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
const viewName = util.useAtomValueSafe(viewModel.viewName) ?? blockViewToName(blockData?.meta?.view); const viewName = util.useAtomValueSafe(viewModel?.viewName) ?? blockViewToName(blockData?.meta?.view);
const showBlockIds = jotai.useAtomValue(useSettingsKeyAtom("blockheader:showblockids")); const showBlockIds = jotai.useAtomValue(useSettingsKeyAtom("blockheader:showblockids"));
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.meta?.view); const viewIconUnion = util.useAtomValueSafe(viewModel?.viewIcon) ?? blockViewToIcon(blockData?.meta?.view);
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton); const preIconButton = util.useAtomValueSafe(viewModel?.preIconButton);
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText); const headerTextUnion = util.useAtomValueSafe(viewModel?.viewText);
const magnified = jotai.useAtomValue(nodeModel.isMagnified); const magnified = jotai.useAtomValue(nodeModel.isMagnified);
const manageConnection = util.useAtomValueSafe(viewModel.manageConnection); const manageConnection = util.useAtomValueSafe(viewModel?.manageConnection);
const dragHandleRef = preview ? null : nodeModel.dragHandleRef; const dragHandleRef = preview ? null : nodeModel.dragHandleRef;
const onContextMenu = React.useCallback( const onContextMenu = React.useCallback(
@ -193,6 +195,19 @@ const BlockFrame_Header = ({
headerTextElems.push(...renderHeaderElements(headerTextUnion, preview)); headerTextElems.push(...renderHeaderElements(headerTextUnion, preview));
} }
headerTextElems.unshift(<ControllerStatusIcon key="connstatus" blockId={nodeModel.blockId} />); headerTextElems.unshift(<ControllerStatusIcon key="connstatus" blockId={nodeModel.blockId} />);
if (error != null) {
const copyHeaderErr = () => {
navigator.clipboard.writeText(error.message + "\n" + error.stack);
};
headerTextElems.push(
<div className="iconbutton disabled" key="controller-status" onClick={copyHeaderErr}>
<i
className="fa-sharp fa-solid fa-triangle-exclamation"
title={"Error Rendering View Header: " + error.message}
/>
</div>
);
}
return ( return (
<div className="block-frame-default-header" ref={dragHandleRef} onContextMenu={onContextMenu}> <div className="block-frame-default-header" ref={dragHandleRef} onContextMenu={onContextMenu}>
@ -377,9 +392,9 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
const { nodeModel, viewModel, blockModel, preview, numBlocksInTab, children } = props; const { nodeModel, viewModel, blockModel, preview, numBlocksInTab, children } = props;
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId)); const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
const isFocused = jotai.useAtomValue(nodeModel.isFocused); const isFocused = jotai.useAtomValue(nodeModel.isFocused);
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.meta?.view); const viewIconUnion = util.useAtomValueSafe(viewModel?.viewIcon) ?? blockViewToIcon(blockData?.meta?.view);
const customBg = util.useAtomValueSafe(viewModel.blockBg); const customBg = util.useAtomValueSafe(viewModel?.blockBg);
const manageConnection = util.useAtomValueSafe(viewModel.manageConnection); const manageConnection = util.useAtomValueSafe(viewModel?.manageConnection);
const changeConnModalAtom = useBlockAtom(nodeModel.blockId, "changeConn", () => { const changeConnModalAtom = useBlockAtom(nodeModel.blockId, "changeConn", () => {
return jotai.atom(false); return jotai.atom(false);
}) as jotai.PrimitiveAtom<boolean>; }) as jotai.PrimitiveAtom<boolean>;
@ -429,6 +444,10 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
} }
} }
const previewElem = <div className="block-frame-preview">{viewIconElem}</div>; const previewElem = <div className="block-frame-preview">{viewIconElem}</div>;
const headerElem = (
<BlockFrame_Header {...props} connBtnRef={connBtnRef} changeConnModalAtom={changeConnModalAtom} />
);
const headerElemNoView = React.cloneElement(headerElem, { viewModel: null });
return ( return (
<div <div
className={clsx("block", "block-frame-default", "block-" + nodeModel.blockId, { className={clsx("block", "block-frame-default", "block-" + nodeModel.blockId, {
@ -442,7 +461,7 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
ref={blockModel?.blockRef} ref={blockModel?.blockRef}
> >
<BlockMask nodeModel={nodeModel} /> <BlockMask nodeModel={nodeModel} />
{preview ? null : ( {preview || viewModel == null ? null : (
<ConnStatusOverlay <ConnStatusOverlay
nodeModel={nodeModel} nodeModel={nodeModel}
viewModel={viewModel} viewModel={viewModel}
@ -450,10 +469,10 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
/> />
)} )}
<div className="block-frame-default-inner" style={innerStyle}> <div className="block-frame-default-inner" style={innerStyle}>
<BlockFrame_Header {...props} connBtnRef={connBtnRef} changeConnModalAtom={changeConnModalAtom} /> <ErrorBoundary fallback={headerElemNoView}>{headerElem}</ErrorBoundary>
{preview ? previewElem : children} {preview ? previewElem : children}
</div> </div>
{preview || !connModalOpen ? null : ( {preview || viewModel == null || !connModalOpen ? null : (
<ChangeConnectionBlockModal <ChangeConnectionBlockModal
blockId={nodeModel.blockId} blockId={nodeModel.blockId}
nodeModel={nodeModel} nodeModel={nodeModel}

View File

@ -3,7 +3,10 @@
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
export class ErrorBoundary extends React.Component<{ children: ReactNode }, { error: Error }> { export class ErrorBoundary extends React.Component<
{ children: ReactNode; fallback?: React.ReactElement & { error?: Error } },
{ error: Error }
> {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { error: null }; this.state = { error: null };
@ -14,8 +17,12 @@ export class ErrorBoundary extends React.Component<{ children: ReactNode }, { er
} }
render() { render() {
const { fallback } = this.props;
const { error } = this.state; const { error } = this.state;
if (error) { if (error) {
if (fallback != null) {
return React.cloneElement(fallback as any, { error });
}
const errorMsg = `Error: ${error?.message}\n\n${error?.stack}`; const errorMsg = `Error: ${error?.message}\n\n${error?.stack}`;
return <pre className="error-boundary">{errorMsg}</pre>; return <pre className="error-boundary">{errorMsg}</pre>;
} else { } else {

View File

@ -108,7 +108,7 @@ const WorkspaceElem = React.memo(() => {
<div className="workspace"> <div className="workspace">
<TabBar key={ws.oid} workspace={ws} /> <TabBar key={ws.oid} workspace={ws} />
<div className="workspace-tabcontent"> <div className="workspace-tabcontent">
<ErrorBoundary> <ErrorBoundary key={activeTabId}>
{activeTabId == "" ? ( {activeTabId == "" ? (
<CenteredDiv>No Active Tab</CenteredDiv> <CenteredDiv>No Active Tab</CenteredDiv>
) : ( ) : (