fix uploaded videos don't load on safari (#1593)
* fix uploaded videos don't load on safari * fix safari loading video as image, min-content restored * refinements for ssr and skip load check for images --------- Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: k00b <k00b@stacker.news>
This commit is contained in:
parent
83e72e21cc
commit
c08088bdbe
|
@ -1,5 +1,5 @@
|
||||||
import styles from './text.module.css'
|
import styles from './text.module.css'
|
||||||
import { useState, useEffect, useMemo, useCallback, memo } from 'react'
|
import { useState, useEffect, useMemo, useCallback, memo, useRef } from 'react'
|
||||||
import { decodeProxyUrl, IMGPROXY_URL_REGEXP, MEDIA_DOMAIN_REGEXP } from '@/lib/url'
|
import { decodeProxyUrl, IMGPROXY_URL_REGEXP, MEDIA_DOMAIN_REGEXP } from '@/lib/url'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import { UNKNOWN_LINK_REL } from '@/lib/constants'
|
import { UNKNOWN_LINK_REL } from '@/lib/constants'
|
||||||
|
@ -23,13 +23,31 @@ const Media = memo(function Media ({
|
||||||
src, bestResSrc, srcSet, sizes, width,
|
src, bestResSrc, srcSet, sizes, width,
|
||||||
height, onClick, onError, style, className, video
|
height, onClick, onError, style, className, video
|
||||||
}) {
|
}) {
|
||||||
|
const [loaded, setLoaded] = useState(!video)
|
||||||
|
const ref = useRef(null)
|
||||||
|
|
||||||
|
const handleLoadedMedia = () => {
|
||||||
|
setLoaded(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// events are not fired on elements during hydration
|
||||||
|
// https://github.com/facebook/react/issues/15446
|
||||||
|
useEffect(() => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.src = src
|
||||||
|
}
|
||||||
|
}, [ref.current, src])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(className, styles.mediaContainer)}
|
// will set min-content ONLY after the media is loaded
|
||||||
|
// due to safari video bug
|
||||||
|
className={classNames(className, styles.mediaContainer, { [styles.loaded]: loaded })}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
{video
|
{video
|
||||||
? <video
|
? <video
|
||||||
|
ref={ref}
|
||||||
src={src}
|
src={src}
|
||||||
preload={bestResSrc !== src ? 'metadata' : undefined}
|
preload={bestResSrc !== src ? 'metadata' : undefined}
|
||||||
controls
|
controls
|
||||||
|
@ -37,8 +55,10 @@ const Media = memo(function Media ({
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
|
onLoadedMetadata={handleLoadedMedia}
|
||||||
/>
|
/>
|
||||||
: <img
|
: <img
|
||||||
|
ref={ref}
|
||||||
src={src}
|
src={src}
|
||||||
srcSet={srcSet}
|
srcSet={srcSet}
|
||||||
sizes={sizes}
|
sizes={sizes}
|
||||||
|
@ -46,6 +66,7 @@ const Media = memo(function Media ({
|
||||||
height={height}
|
height={height}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
|
onLoad={handleLoadedMedia}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -101,21 +122,28 @@ export const useMediaHelper = ({ src, srcSet: srcSetIntital, topLevel, tab }) =>
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// don't load the video at all if user doesn't want these
|
// don't load the video at all if user doesn't want these
|
||||||
if (!showMedia || isVideo || isImage) return
|
if (!showMedia || isVideo || isImage) return
|
||||||
// make sure it's not a false negative by trying to load URL as <img>
|
|
||||||
const img = new window.Image()
|
// check if it's a video by trying to load it
|
||||||
img.onload = () => setIsImage(true)
|
|
||||||
img.src = src
|
|
||||||
const video = document.createElement('video')
|
const video = document.createElement('video')
|
||||||
video.onloadeddata = () => setIsVideo(true)
|
video.onloadedmetadata = () => {
|
||||||
|
setIsVideo(true)
|
||||||
|
setIsImage(false)
|
||||||
|
}
|
||||||
|
video.onerror = () => {
|
||||||
|
// hack
|
||||||
|
// if it's not a video it will throw an error, so we can assume it's an image
|
||||||
|
const img = new window.Image()
|
||||||
|
img.onload = () => setIsImage(true)
|
||||||
|
img.src = src
|
||||||
|
}
|
||||||
video.src = src
|
video.src = src
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
img.onload = null
|
video.onloadedmetadata = null
|
||||||
img.src = ''
|
video.onerror = null
|
||||||
video.onloadeddata = null
|
|
||||||
video.src = ''
|
video.src = ''
|
||||||
}
|
}
|
||||||
}, [src, setIsImage, setIsVideo, showMedia, isVideo])
|
}, [src, setIsImage, setIsVideo, showMedia, isImage])
|
||||||
|
|
||||||
const srcSet = useMemo(() => {
|
const srcSet = useMemo(() => {
|
||||||
if (Object.keys(srcSetObj).length === 0) return undefined
|
if (Object.keys(srcSetObj).length === 0) return undefined
|
||||||
|
|
|
@ -184,10 +184,14 @@
|
||||||
.p:has(> .mediaContainer) .mediaContainer
|
.p:has(> .mediaContainer) .mediaContainer
|
||||||
{
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
width: min-content;
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p:has(> .mediaContainer) .mediaContainer.loaded
|
||||||
|
{
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
.p:has(> .mediaContainer) .mediaContainer img,
|
.p:has(> .mediaContainer) .mediaContainer img,
|
||||||
.p:has(> .mediaContainer) .mediaContainer video
|
.p:has(> .mediaContainer) .mediaContainer video
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue