diff --git a/src/assets/images/broken-image-fallback.png b/src/assets/images/broken-image-fallback.png new file mode 100644 index 00000000..84e828fb Binary files /dev/null and b/src/assets/images/broken-image-fallback.png differ diff --git a/src/assets/symbols.svg b/src/assets/symbols.svg index 87d63361..c9fda729 100644 --- a/src/assets/symbols.svg +++ b/src/assets/symbols.svg @@ -170,8 +170,11 @@ - - + + + + + diff --git a/src/shared/components/common/pictrs-image.tsx b/src/shared/components/common/pictrs-image.tsx index 9a5f450e..d942aba6 100644 --- a/src/shared/components/common/pictrs-image.tsx +++ b/src/shared/components/common/pictrs-image.tsx @@ -1,9 +1,10 @@ import classNames from "classnames"; -import { Component } from "inferno"; +import { Component, linkEvent } from "inferno"; import { UserService } from "../../services"; import { setIsoData } from "@utils/app"; import { IsoData } from "../../interfaces"; +import { getStaticDir } from "@utils/env"; const iconThumbnailSize = 96; const thumbnailSize = 256; @@ -20,13 +21,35 @@ interface PictrsImageProps { cardTop?: boolean; } -export class PictrsImage extends Component { +interface PictrsImageState { + src: string; +} + +function handleImgLoadError(i: PictrsImage) { + i.setState({ + src: `${getStaticDir()}/assets/images/broken-image-fallback.png`, + }); +} + +export class PictrsImage extends Component { private readonly isoData: IsoData = setIsoData(this.context); + state: PictrsImageState = { + src: this.props.src, + }; + + componentDidUpdate(prevProps: PictrsImageProps) { + if (prevProps.src !== this.props.src) { + this.setState({ src: this.props.src }); + } + } + render() { - const { src, icon, iconOverlay, banner, thumbnail, nsfw, pushup, cardTop } = + const { icon, iconOverlay, banner, thumbnail, nsfw, pushup, cardTop } = this.props; + const { src } = this.state; + const blurImage = nsfw && (UserService.Instance.myUserInfo?.local_user_view.local_user.blur_nsfw ?? @@ -58,6 +81,7 @@ export class PictrsImage extends Component { "avatar-pushup": pushup, "card-img-top": cardTop, })} + onError={linkEvent(this, handleImgLoadError)} /> ) @@ -70,14 +94,14 @@ export class PictrsImage extends Component { let url: URL | undefined; try { - url = new URL(this.props.src); + url = new URL(this.state.src); } catch { - return this.props.src; + return this.state.src; } // If there's no match, then it's not a pictrs image if (!url.pathname.includes("/pictrs/image/")) { - return this.props.src; + return this.state.src; } // Keeps original search params. Could probably do `url.search = ""` here.