Refactor and Move TileErrorBoundary to Shared Components (#32793)
* creation of stories and view in shared-components * migrate EventTile error fallback to shared TileErrorView MVVM * Fix lint errors and unused import * Update tests because of the refactoring * Update snapshots + stories * removal of mxEvent since it never changes in timeline * Update packages/shared-components/src/message-body/TileErrorView/TileErrorView.stories.tsx Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * Update apps/web/src/viewmodels/message-body/TileErrorViewModel.ts Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * Update apps/web/src/viewmodels/message-body/TileErrorViewModel.ts Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * docs: add TileErrorView tsdoc * docs: add TileErrorViewModel tsdoc * docs: add view source label tsdoc * refactor: move tile error layout into vm * docs: add TileErrorView story view docs * docs: move tile error story list wrapper * refactor: remove unused tile error event setter * Update packages/shared-components/src/message-body/TileErrorView/TileErrorView.stories.tsx Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * docs: add tsdoc for event tile error fallback props * refactor: rely on snapshot merge no-op checks * remove unessecery if statment * test: restore EventTile mocks in afterEach * test(shared-components): move TileErrorView baselines --------- Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MouseEventHandler } from "react";
|
||||
import { type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
BaseViewModel,
|
||||
type TileErrorViewLayout,
|
||||
type TileErrorViewSnapshot as TileErrorViewSnapshotInterface,
|
||||
type TileErrorViewModel as TileErrorViewModelInterface,
|
||||
} from "@element-hq/web-shared-components";
|
||||
|
||||
import { _t } from "../../languageHandler";
|
||||
import Modal from "../../Modal";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import { BugReportEndpointURLLocal } from "../../IConfigOptions";
|
||||
import ViewSource from "../../components/structures/ViewSource";
|
||||
import BugReportDialog from "../../components/views/dialogs/BugReportDialog";
|
||||
|
||||
const TILE_ERROR_BUG_REPORT_LABEL = "react-tile-soft-crash";
|
||||
|
||||
export interface TileErrorViewModelProps {
|
||||
/**
|
||||
* Layout variant used by the host timeline.
|
||||
*/
|
||||
layout: TileErrorViewLayout;
|
||||
/**
|
||||
* Event whose tile failed to render.
|
||||
*/
|
||||
mxEvent: MatrixEvent;
|
||||
/**
|
||||
* Render error captured by the boundary.
|
||||
*/
|
||||
error: Error;
|
||||
/**
|
||||
* Whether developer mode is enabled, which controls the view-source action.
|
||||
*/
|
||||
developerMode: boolean;
|
||||
}
|
||||
|
||||
function getBugReportCtaLabel(): string | undefined {
|
||||
const bugReportUrl = SdkConfig.get().bug_report_endpoint_url;
|
||||
|
||||
if (!bugReportUrl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return bugReportUrl === BugReportEndpointURLLocal
|
||||
? _t("bug_reporting|download_logs")
|
||||
: _t("bug_reporting|submit_debug_logs");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized view-source action label when developer mode is enabled.
|
||||
*/
|
||||
function getViewSourceCtaLabel(developerMode: boolean): string | undefined {
|
||||
return developerMode ? _t("action|view_source") : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewModel for the tile error fallback, providing the snapshot shown when a tile fails to render.
|
||||
*
|
||||
* The snapshot includes the host timeline layout, the fallback message, the event type,
|
||||
* and optional bug-report and view-source action labels. The view model also exposes
|
||||
* click handlers for those actions, opening the bug-report or view-source dialog when
|
||||
* available.
|
||||
*/
|
||||
export class TileErrorViewModel
|
||||
extends BaseViewModel<TileErrorViewSnapshotInterface, TileErrorViewModelProps>
|
||||
implements TileErrorViewModelInterface
|
||||
{
|
||||
private static readonly computeSnapshot = (props: TileErrorViewModelProps): TileErrorViewSnapshotInterface => ({
|
||||
layout: props.layout,
|
||||
message: _t("timeline|error_rendering_message"),
|
||||
eventType: props.mxEvent.getType(),
|
||||
bugReportCtaLabel: getBugReportCtaLabel(),
|
||||
viewSourceCtaLabel: getViewSourceCtaLabel(props.developerMode),
|
||||
});
|
||||
|
||||
public constructor(props: TileErrorViewModelProps) {
|
||||
super(props, TileErrorViewModel.computeSnapshot(props));
|
||||
}
|
||||
|
||||
public setLayout(layout: TileErrorViewLayout): void {
|
||||
this.props.layout = layout;
|
||||
this.snapshot.merge({ layout });
|
||||
}
|
||||
|
||||
public setError(error: Error): void {
|
||||
this.props.error = error;
|
||||
}
|
||||
|
||||
public setDeveloperMode(developerMode: boolean): void {
|
||||
this.props.developerMode = developerMode;
|
||||
|
||||
const nextViewSourceCtaLabel = getViewSourceCtaLabel(developerMode);
|
||||
this.snapshot.merge({ viewSourceCtaLabel: nextViewSourceCtaLabel });
|
||||
}
|
||||
|
||||
public onBugReportClick: MouseEventHandler<HTMLButtonElement> = () => {
|
||||
if (!this.snapshot.current.bugReportCtaLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.createDialog(BugReportDialog, {
|
||||
label: TILE_ERROR_BUG_REPORT_LABEL,
|
||||
error: this.props.error,
|
||||
});
|
||||
};
|
||||
|
||||
public onViewSourceClick: MouseEventHandler<HTMLButtonElement> = () => {
|
||||
if (!this.snapshot.current.viewSourceCtaLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.createDialog(
|
||||
ViewSource,
|
||||
{
|
||||
mxEvent: this.props.mxEvent,
|
||||
},
|
||||
"mx_Dialog_viewsource",
|
||||
);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user