forked from Mirrors/gomuks
web/all: add more error boundaries
This commit is contained in:
parent
8ba9279cc7
commit
2461cad4f2
5 changed files with 77 additions and 27 deletions
|
@ -16,6 +16,7 @@
|
||||||
import { JSX, use } from "react"
|
import { JSX, use } from "react"
|
||||||
import type { UserID } from "@/api/types"
|
import type { UserID } from "@/api/types"
|
||||||
import MainScreenContext from "../MainScreenContext.ts"
|
import MainScreenContext from "../MainScreenContext.ts"
|
||||||
|
import ErrorBoundary from "../util/ErrorBoundary.tsx"
|
||||||
import MemberList from "./MemberList.tsx"
|
import MemberList from "./MemberList.tsx"
|
||||||
import PinnedMessages from "./PinnedMessages.tsx"
|
import PinnedMessages from "./PinnedMessages.tsx"
|
||||||
import UserInfo from "./UserInfo.tsx"
|
import UserInfo from "./UserInfo.tsx"
|
||||||
|
@ -76,7 +77,9 @@ const RightPanel = (props: RightPanelProps) => {
|
||||||
<button onClick={mainScreen.closeRightPanel}><CloseIcon/></button>
|
<button onClick={mainScreen.closeRightPanel}><CloseIcon/></button>
|
||||||
</div>
|
</div>
|
||||||
<div className={`right-panel-content ${props.type}`}>
|
<div className={`right-panel-content ${props.type}`}>
|
||||||
|
<ErrorBoundary thing="right panel content">
|
||||||
{renderRightPanelContent(props)}
|
{renderRightPanelContent(props)}
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,13 @@ div.room-view {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> div.room-view-error {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div#mobile-event-menu-container {
|
div#mobile-event-menu-container {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import MessageComposer from "../composer/MessageComposer.tsx"
|
||||||
import TypingNotifications from "../composer/TypingNotifications.tsx"
|
import TypingNotifications from "../composer/TypingNotifications.tsx"
|
||||||
import RightPanel, { RightPanelProps } from "../rightpanel/RightPanel.tsx"
|
import RightPanel, { RightPanelProps } from "../rightpanel/RightPanel.tsx"
|
||||||
import TimelineView from "../timeline/TimelineView.tsx"
|
import TimelineView from "../timeline/TimelineView.tsx"
|
||||||
|
import ErrorBoundary from "../util/ErrorBoundary.tsx"
|
||||||
import RoomViewHeader from "./RoomViewHeader.tsx"
|
import RoomViewHeader from "./RoomViewHeader.tsx"
|
||||||
import { RoomContext, RoomContextData } from "./roomcontext.ts"
|
import { RoomContext, RoomContextData } from "./roomcontext.ts"
|
||||||
import "./RoomView.css"
|
import "./RoomView.css"
|
||||||
|
@ -49,11 +50,13 @@ const RoomView = ({ room, rightPanelResizeHandle, rightPanel }: RoomViewProps) =
|
||||||
}
|
}
|
||||||
return <RoomContext value={roomContextData}>
|
return <RoomContext value={roomContextData}>
|
||||||
<div className="room-view" onClick={onClick}>
|
<div className="room-view" onClick={onClick}>
|
||||||
|
<ErrorBoundary thing="room view" wrapperClassName="room-view-error">
|
||||||
<div id="mobile-event-menu-container"/>
|
<div id="mobile-event-menu-container"/>
|
||||||
<RoomViewHeader room={room}/>
|
<RoomViewHeader room={room}/>
|
||||||
<TimelineView/>
|
<TimelineView/>
|
||||||
<MessageComposer/>
|
<MessageComposer/>
|
||||||
<TypingNotifications/>
|
<TypingNotifications/>
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
{rightPanelResizeHandle}
|
{rightPanelResizeHandle}
|
||||||
{rightPanel && <RightPanel {...rightPanel}/>}
|
{rightPanel && <RightPanel {...rightPanel}/>}
|
||||||
|
|
|
@ -14,27 +14,12 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import ErrorBoundary from "@/ui/util/ErrorBoundary.tsx"
|
||||||
|
|
||||||
export default class ContentErrorBoundary extends React.Component<{ children: React.ReactNode }, { error?: Error }> {
|
export default class ContentErrorBoundary extends ErrorBoundary {
|
||||||
constructor(props: { children: React.ReactNode }) {
|
renderError(message: string): React.JSX.Element {
|
||||||
super(props)
|
|
||||||
this.state = { error: undefined }
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDerivedStateFromError(error: unknown) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
error = new Error(`${error}`)
|
|
||||||
}
|
|
||||||
return { error }
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.error) {
|
|
||||||
return <div className="render-error-body">
|
return <div className="render-error-body">
|
||||||
Failed to render event: {this.state.error.message.replace(/^Error: /, "")}
|
Failed to render event: {message}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.children
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
52
web/src/ui/util/ErrorBoundary.tsx
Normal file
52
web/src/ui/util/ErrorBoundary.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// gomuks - A Matrix client written in Go.
|
||||||
|
// Copyright (C) 2024 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export interface ErrorBoundaryProps {
|
||||||
|
thing?: string
|
||||||
|
wrapperClassName?: string
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, { error?: string }> {
|
||||||
|
constructor(props: ErrorBoundaryProps) {
|
||||||
|
super(props)
|
||||||
|
this.state = { error: undefined }
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error: unknown) {
|
||||||
|
return {
|
||||||
|
error: `${error}`.replace(/^Error: /, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderError(message: string) {
|
||||||
|
const inner = <>
|
||||||
|
Failed to render {this.props.thing ?? "component"}: {message}
|
||||||
|
</>
|
||||||
|
if (this.props.wrapperClassName) {
|
||||||
|
return <div className={this.props.wrapperClassName}>{inner}</div>
|
||||||
|
}
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.error) {
|
||||||
|
return this.renderError(this.state.error)
|
||||||
|
}
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue