mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
web/timeline: add error boundary for event content rendering
This commit is contained in:
parent
b67095f0fd
commit
f2f89b728f
5 changed files with 54 additions and 4 deletions
|
@ -18,7 +18,7 @@ import { getAvatarURL } from "@/api/media.ts"
|
||||||
import { RoomStateStore, useRoomEvent } from "@/api/statestore"
|
import { RoomStateStore, useRoomEvent } from "@/api/statestore"
|
||||||
import type { EventID, MemDBEvent, MemberEventContent } from "@/api/types"
|
import type { EventID, MemDBEvent, MemberEventContent } from "@/api/types"
|
||||||
import { ClientContext } from "../ClientContext.ts"
|
import { ClientContext } from "../ClientContext.ts"
|
||||||
import getBodyType from "./content"
|
import getBodyType, { ContentErrorBoundary } from "./content"
|
||||||
import CloseButton from "@/icons/close.svg?react"
|
import CloseButton from "@/icons/close.svg?react"
|
||||||
import "./ReplyBody.css"
|
import "./ReplyBody.css"
|
||||||
|
|
||||||
|
@ -83,6 +83,8 @@ export const ReplyBody = ({ room, event, onClose }: ReplyBodyProps) => {
|
||||||
<span className="event-sender">{memberEvtContent?.displayname || event.sender}</span>
|
<span className="event-sender">{memberEvtContent?.displayname || event.sender}</span>
|
||||||
{onClose && <button className="close-reply" onClick={onClose}><CloseButton/></button>}
|
{onClose && <button className="close-reply" onClick={onClose}><CloseButton/></button>}
|
||||||
</div>
|
</div>
|
||||||
<BodyType room={room} event={event}/>
|
<ContentErrorBoundary>
|
||||||
|
<BodyType room={room} event={event}/>
|
||||||
|
</ContentErrorBoundary>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,11 @@ div.date-separator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.render-error-body {
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
div.decryption-error-body {
|
div.decryption-error-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { isEventID } from "@/util/validation.ts"
|
||||||
import { ClientContext } from "../ClientContext.ts"
|
import { ClientContext } from "../ClientContext.ts"
|
||||||
import { LightboxContext } from "../Lightbox.tsx"
|
import { LightboxContext } from "../Lightbox.tsx"
|
||||||
import { ReplyIDBody } from "./ReplyBody.tsx"
|
import { ReplyIDBody } from "./ReplyBody.tsx"
|
||||||
import getBodyType, { EventContentProps, HiddenEvent, MemberBody } from "./content"
|
import getBodyType, { ContentErrorBoundary, EventContentProps, HiddenEvent, MemberBody } from "./content"
|
||||||
import ErrorIcon from "../../icons/error.svg?react"
|
import ErrorIcon from "../../icons/error.svg?react"
|
||||||
import PendingIcon from "../../icons/pending.svg?react"
|
import PendingIcon from "../../icons/pending.svg?react"
|
||||||
import SentIcon from "../../icons/sent.svg?react"
|
import SentIcon from "../../icons/sent.svg?react"
|
||||||
|
@ -111,7 +111,9 @@ const TimelineEvent = ({ room, evt, prevEvt, setReplyToRef }: TimelineEventProps
|
||||||
</div>
|
</div>
|
||||||
<div className="event-content">
|
<div className="event-content">
|
||||||
{isEventID(replyTo) && BodyType !== HiddenEvent ? <ReplyIDBody room={room} eventID={replyTo}/> : null}
|
{isEventID(replyTo) && BodyType !== HiddenEvent ? <ReplyIDBody room={room} eventID={replyTo}/> : null}
|
||||||
<BodyType room={room} sender={memberEvt} event={evt}/>
|
<ContentErrorBoundary>
|
||||||
|
<BodyType room={room} sender={memberEvt} event={evt}/>
|
||||||
|
</ContentErrorBoundary>
|
||||||
{evt.reactions ? <EventReactions reactions={evt.reactions}/> : null}
|
{evt.reactions ? <EventReactions reactions={evt.reactions}/> : null}
|
||||||
</div>
|
</div>
|
||||||
{evt.sender === client.userID && evt.transaction_id ? <EventSendStatus evt={evt}/> : null}
|
{evt.sender === client.userID && evt.transaction_id ? <EventSendStatus evt={evt}/> : null}
|
||||||
|
|
40
web/src/ui/timeline/content/ContentErrorBoundary.tsx
Normal file
40
web/src/ui/timeline/content/ContentErrorBoundary.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// 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 default class ContentErrorBoundary extends React.Component<{ children: React.ReactNode }, { error?: Error }> {
|
||||||
|
constructor(props: { children: React.ReactNode }) {
|
||||||
|
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">
|
||||||
|
Failed to render event: {this.state.error.message.replace(/^Error: /, "")}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import TextMessageBody from "./TextMessageBody.tsx"
|
||||||
import UnknownMessageBody from "./UnknownMessageBody.tsx"
|
import UnknownMessageBody from "./UnknownMessageBody.tsx"
|
||||||
import EventContentProps from "./props.ts"
|
import EventContentProps from "./props.ts"
|
||||||
|
|
||||||
|
export { default as ContentErrorBoundary } from "./ContentErrorBoundary.tsx"
|
||||||
export { default as EncryptedBody } from "./EncryptedBody.tsx"
|
export { default as EncryptedBody } from "./EncryptedBody.tsx"
|
||||||
export { default as HiddenEvent } from "./HiddenEvent.tsx"
|
export { default as HiddenEvent } from "./HiddenEvent.tsx"
|
||||||
export { default as MediaMessageBody } from "./MediaMessageBody.tsx"
|
export { default as MediaMessageBody } from "./MediaMessageBody.tsx"
|
||||||
|
|
Loading…
Add table
Reference in a new issue