fix(chat): make cached media load on safari
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
isBrowserLocalChatImageSource,
|
||||
@@ -39,11 +39,13 @@ export function FullscreenImageViewer({
|
||||
onUnlockImagePaywall,
|
||||
onClose,
|
||||
}: FullscreenImageViewerProps) {
|
||||
const { mediaUrl } = useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: imageUrl,
|
||||
kind: "image",
|
||||
});
|
||||
const { mediaUrl, isUsingCachedMedia, reportMediaError } =
|
||||
useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: imageUrl,
|
||||
kind: "image",
|
||||
});
|
||||
const [imageErrorSrc, setImageErrorSrc] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
@@ -55,6 +57,15 @@ export function FullscreenImageViewer({
|
||||
|
||||
const src = normalizeChatImageSource(mediaUrl || imageUrl);
|
||||
const shouldUseNativeImage = isBrowserLocalChatImageSource(src);
|
||||
const hasImageError = imageErrorSrc === src;
|
||||
|
||||
const handleImageError = () => {
|
||||
if (isUsingCachedMedia) {
|
||||
reportMediaError();
|
||||
return;
|
||||
}
|
||||
setImageErrorSrc(src);
|
||||
};
|
||||
|
||||
if (imagePaywalled) {
|
||||
return (
|
||||
@@ -68,6 +79,7 @@ export function FullscreenImageViewer({
|
||||
src={src}
|
||||
alt=""
|
||||
className={`${styles.nativePaywallImage} ${styles.paywallImage}`}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
@@ -76,6 +88,7 @@ export function FullscreenImageViewer({
|
||||
fill
|
||||
sizes="(max-width: 540px) 100vw, 540px"
|
||||
className={styles.paywallImage}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.paywallScrim} aria-hidden="true" />
|
||||
@@ -108,16 +121,14 @@ export function FullscreenImageViewer({
|
||||
role="dialog"
|
||||
aria-label="Fullscreen image"
|
||||
>
|
||||
{shouldUseNativeImage ? (
|
||||
{hasImageError ? (
|
||||
<div className="error">🖼</div>
|
||||
) : shouldUseNativeImage ? (
|
||||
<img
|
||||
src={src}
|
||||
alt=""
|
||||
className={styles.viewerImage}
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = "none";
|
||||
(e.currentTarget.parentElement as HTMLElement).innerHTML =
|
||||
'<div class="error">🖼</div>';
|
||||
}}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
@@ -126,11 +137,7 @@ export function FullscreenImageViewer({
|
||||
width={800}
|
||||
height={800}
|
||||
className={styles.viewerImage}
|
||||
onError={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = "none";
|
||||
(e.currentTarget.parentElement as HTMLElement).innerHTML =
|
||||
'<div class="error">🖼</div>';
|
||||
}}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -34,16 +34,26 @@ export function ImageBubble({
|
||||
imagePaywalled = false,
|
||||
}: ImageBubbleProps) {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState(false);
|
||||
const { mediaUrl } = useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: imageUrl,
|
||||
kind: "image",
|
||||
});
|
||||
const [errorSrc, setErrorSrc] = useState<string | null>(null);
|
||||
const { mediaUrl, isUsingCachedMedia, reportMediaError } =
|
||||
useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: imageUrl,
|
||||
kind: "image",
|
||||
});
|
||||
|
||||
const src = normalizeChatImageSource(mediaUrl || imageUrl);
|
||||
const canOpen = Boolean(messageId);
|
||||
const shouldUseNativeImage = isBrowserLocalChatImageSource(src);
|
||||
const error = errorSrc === src;
|
||||
|
||||
const handleImageError = () => {
|
||||
if (isUsingCachedMedia) {
|
||||
reportMediaError();
|
||||
return;
|
||||
}
|
||||
setErrorSrc(src);
|
||||
};
|
||||
|
||||
const openImage = () => {
|
||||
if (!messageId) return;
|
||||
@@ -73,7 +83,7 @@ export function ImageBubble({
|
||||
className={styles.image}
|
||||
src={src}
|
||||
alt=""
|
||||
onError={() => setError(true)}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
@@ -82,7 +92,7 @@ export function ImageBubble({
|
||||
alt=""
|
||||
width={240}
|
||||
height={240}
|
||||
onError={() => setError(true)}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
)}
|
||||
{!error && imagePaywalled ? (
|
||||
|
||||
@@ -27,12 +27,13 @@ export function VoiceBubble({
|
||||
}: VoiceBubbleProps) {
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const { mediaUrl } = useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: audioUrl,
|
||||
kind: "audio",
|
||||
});
|
||||
const [errorMediaUrl, setErrorMediaUrl] = useState<string | null>(null);
|
||||
const { mediaUrl, isUsingCachedMedia, reportMediaError } =
|
||||
useCachedChatMediaUrl({
|
||||
messageId,
|
||||
remoteUrl: audioUrl,
|
||||
kind: "audio",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current;
|
||||
@@ -42,7 +43,11 @@ export function VoiceBubble({
|
||||
const handlePause = () => setIsPlaying(false);
|
||||
const handlePlay = () => setIsPlaying(true);
|
||||
const handleError = () => {
|
||||
setHasError(true);
|
||||
if (isUsingCachedMedia) {
|
||||
reportMediaError();
|
||||
return;
|
||||
}
|
||||
setErrorMediaUrl(mediaUrl);
|
||||
setIsPlaying(false);
|
||||
};
|
||||
|
||||
@@ -56,7 +61,9 @@ export function VoiceBubble({
|
||||
audio.removeEventListener("play", handlePlay);
|
||||
audio.removeEventListener("error", handleError);
|
||||
};
|
||||
}, []);
|
||||
}, [isUsingCachedMedia, mediaUrl, reportMediaError]);
|
||||
|
||||
const hasError = errorMediaUrl === mediaUrl;
|
||||
|
||||
const handleToggle = () => {
|
||||
if (locked) return;
|
||||
@@ -70,7 +77,7 @@ export function VoiceBubble({
|
||||
}
|
||||
|
||||
void audio.play().catch(() => {
|
||||
setHasError(true);
|
||||
setErrorMediaUrl(mediaUrl);
|
||||
setIsPlaying(false);
|
||||
});
|
||||
};
|
||||
@@ -125,7 +132,9 @@ export function VoiceBubble({
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
{!locked ? <audio ref={audioRef} src={mediaUrl} preload="metadata" /> : null}
|
||||
{!locked ? (
|
||||
<audio ref={audioRef} src={mediaUrl} preload="metadata" />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user