diff --git a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx index 1eca66c88c2..c93e8e98786 100644 --- a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx +++ b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx @@ -34,7 +34,7 @@ import { Icon } from "src/components/Shared/Icon"; import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; import { ConfigurationContext } from "src/hooks/Config"; import { IUIConfig } from "src/core/config"; -import ImageUtils from "src/utils/image"; +import { DetailImage } from "src/components/Shared/DetailImage"; import { useRatingKeybinds } from "src/hooks/keybinds"; import { useLoadStickyHeader } from "src/hooks/detailsPanel"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; @@ -232,11 +232,7 @@ const MoviePage: React.FC = ({ movie }) => { if (image && defaultImage) { return (
- Front Cover +
); } else if (image) { @@ -246,11 +242,7 @@ const MoviePage: React.FC = ({ movie }) => { variant="link" onClick={() => showLightbox()} > - Front Cover + ); } @@ -273,11 +265,7 @@ const MoviePage: React.FC = ({ movie }) => { variant="link" onClick={() => showLightbox(index - 1)} > - Back Cover + ); } diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx index 1182112c7b6..015789fe173 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx @@ -42,7 +42,7 @@ import { import { faInstagram, faTwitter } from "@fortawesome/free-brands-svg-icons"; import { IUIConfig } from "src/core/config"; import { useRatingKeybinds } from "src/hooks/keybinds"; -import ImageUtils from "src/utils/image"; +import { DetailImage } from "src/components/Shared/DetailImage"; import { useLoadStickyHeader } from "src/hooks/detailsPanel"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; @@ -206,11 +206,10 @@ const PerformerPage: React.FC = ({ performer, tabKey }) => { if (activeImage) { return ( ); diff --git a/ui/v2.5/src/components/Shared/DetailImage.tsx b/ui/v2.5/src/components/Shared/DetailImage.tsx new file mode 100644 index 00000000000..fc16d081378 --- /dev/null +++ b/ui/v2.5/src/components/Shared/DetailImage.tsx @@ -0,0 +1,38 @@ +import { useLayoutEffect, useRef } from "react"; + +const DEFAULT_WIDTH = "200"; + +// Props used by the element +type IDetailImageProps = JSX.IntrinsicElements["img"]; + +export const DetailImage = (props: IDetailImageProps) => { + const imgRef = useRef(null); + + function fixWidth() { + const img = imgRef.current; + if (!img) return; + + // prevent SVG's w/o intrinsic size from rendering as 0x0 + if (img.naturalWidth === 0) { + // If the naturalWidth is zero, it means the image either hasn't loaded yet + // or we're on Firefox and it is an SVG w/o an intrinsic size. + // So set the width to our fallback width. + img.setAttribute("width", DEFAULT_WIDTH); + } else { + // If we have a `naturalWidth`, this could either be the actual intrinsic width + // of the image, or the image is an SVG w/o an intrinsic size and we're on Chrome or Safari, + // which seem to return a size calculated in some browser-specific way. + // Worse yet, once rendered, Safari will then return the value of `img.width` as `img.naturalWidth`, + // so we need to clone the image to disconnect it from the DOM, and then get the `naturalWidth` of the clone, + // in order to always return the same `naturalWidth` for a given src. + const i = img.cloneNode() as HTMLImageElement; + img.setAttribute("width", (i.naturalWidth || DEFAULT_WIDTH).toString()); + } + } + + useLayoutEffect(() => { + fixWidth(); + }, [props.src]); + + return fixWidth()} {...props} />; +}; diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx index 1b169e63aeb..ae7ce9d85ad 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx @@ -41,7 +41,7 @@ import { import { IUIConfig } from "src/core/config"; import TextUtils from "src/utils/text"; import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; -import ImageUtils from "src/utils/image"; +import { DetailImage } from "src/components/Shared/DetailImage"; import { useRatingKeybinds } from "src/hooks/keybinds"; import { useLoadStickyHeader } from "src/hooks/detailsPanel"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; @@ -237,12 +237,7 @@ const StudioPage: React.FC = ({ studio, tabKey }) => { if (studioImage) { return ( - {studio.name} + ); } } diff --git a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx index 8ef7c4adb06..372f0168f0c 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx @@ -38,7 +38,7 @@ import { faTrashAlt, } from "@fortawesome/free-solid-svg-icons"; import { IUIConfig } from "src/core/config"; -import ImageUtils from "src/utils/image"; +import { DetailImage } from "src/components/Shared/DetailImage"; import { useLoadStickyHeader } from "src/hooks/detailsPanel"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; @@ -274,14 +274,7 @@ const TagPage: React.FC = ({ tag, tabKey }) => { } if (tagImage) { - return ( - {tag.name} - ); + return ; } } diff --git a/ui/v2.5/src/utils/image.tsx b/ui/v2.5/src/utils/image.tsx index 53443c0b3eb..b31387e830f 100644 --- a/ui/v2.5/src/utils/image.tsx +++ b/ui/v2.5/src/utils/image.tsx @@ -70,17 +70,6 @@ const ImageUtils = { onImageChange, usePasteImage, imageToDataURL, - verifyImageSize, }; -function verifyImageSize(e: React.UIEvent) { - const img = e.target as HTMLImageElement; - // set width = 200px if zero-sized image (SVG w/o intrinsic size) - if (img.width === 0 && img.height === 0) { - img.setAttribute("width", "200"); - } else { - img.removeAttribute("width"); - } -} - export default ImageUtils;