From cfbeadb93bb048a13245f29778f480813502a225 Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Fri, 5 Dec 2025 16:36:17 +0100 Subject: [PATCH] refactor: remove ImageWithFallback component and replace with Next.js Image in various components --- .../src/components/ImageWithFallback.tsx | 19 ------- .../src/components/annuncio_card.tsx | 10 ++-- .../area-riservata/documenti_personali.tsx | 4 +- .../src/components/servizio/annuncio_card.tsx | 4 +- apps/infoalloggi/src/hooks/staleImage.ts | 50 +++++++++++++++++++ .../src/middlewares/api_middleware.ts | 47 ++++++++++++++--- apps/infoalloggi/src/pages/annuncio/[cod].tsx | 2 + 7 files changed, 102 insertions(+), 34 deletions(-) delete mode 100644 apps/infoalloggi/src/components/ImageWithFallback.tsx create mode 100644 apps/infoalloggi/src/hooks/staleImage.ts diff --git a/apps/infoalloggi/src/components/ImageWithFallback.tsx b/apps/infoalloggi/src/components/ImageWithFallback.tsx deleted file mode 100644 index 83427d5..0000000 --- a/apps/infoalloggi/src/components/ImageWithFallback.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Image from "next/image"; -import type { ComponentProps } from "react"; - -const fallbackImage = "/fallback-image.png"; -export const ImageFlbk = (props: ComponentProps) => { - return ( - { - e.currentTarget.src = fallbackImage; - }} - onLoad={(e) => { - if (e.currentTarget.naturalWidth === 0) { - e.currentTarget.src = fallbackImage; - } - }} - /> - ); -}; diff --git a/apps/infoalloggi/src/components/annuncio_card.tsx b/apps/infoalloggi/src/components/annuncio_card.tsx index dc37568..e7b53c5 100644 --- a/apps/infoalloggi/src/components/annuncio_card.tsx +++ b/apps/infoalloggi/src/components/annuncio_card.tsx @@ -8,10 +8,10 @@ import { Star, TrafficCone, } from "lucide-react"; +import Image from "next/image"; import Link from "next/link"; import { type RefObject, useEffect, useRef, useState } from "react"; import { useIntersection } from "react-use"; -import { ImageFlbk } from "~/components/ImageWithFallback"; import { Carousel, CarouselContent, @@ -216,7 +216,7 @@ export const CardAnnuncio = ({ {images?.map((img, idx) => ( - {images && images.length > 0 && images[0] ? ( - - {immagini?.map((img, idx) => ( - ) : ( -
- { + useEffect(() => { + // Check for stale resource cookie + const checkStaleCookie = () => { + const hasStale = document.cookie.includes(`${STALE_KEY}=1`); + + if (hasStale) { + console.log("Stale resource cookie detected"); + // Clear the cookie + deleteCookie(STALE_KEY); + attemptReload(); + } + }; + + // Check on mount + checkStaleCookie(); + + // Also check periodically for dynamically loaded images + const interval = setInterval(checkStaleCookie, 1000); + + return () => clearInterval(interval); + }, []); +}; + +function attemptReload() { + const lastAttempt = sessionStorage.getItem(RELOAD_STORAGE_KEY); + const now = Date.now(); + + if (!lastAttempt) { + sessionStorage.setItem(RELOAD_STORAGE_KEY, now.toString()); + window.location.reload(); + return; + } + + const timeSinceLastAttempt = now - parseInt(lastAttempt, 10); + + if (timeSinceLastAttempt > RELOAD_COOLDOWN_MS) { + sessionStorage.setItem(RELOAD_STORAGE_KEY, now.toString()); + window.location.reload(); + } +} diff --git a/apps/infoalloggi/src/middlewares/api_middleware.ts b/apps/infoalloggi/src/middlewares/api_middleware.ts index 477988d..4f8d666 100644 --- a/apps/infoalloggi/src/middlewares/api_middleware.ts +++ b/apps/infoalloggi/src/middlewares/api_middleware.ts @@ -3,6 +3,7 @@ import { env } from "~/env"; import { TOKEN_CONFIG } from "~/server/auth/configs"; import { verifyToken } from "~/server/auth/jwt"; +const STALE_KEY = "stale-resource"; export const apisMiddleware = async (req: NextRequest) => { const { pathname, searchParams } = req.nextUrl; @@ -68,16 +69,28 @@ export const apisMiddleware = async (req: NextRequest) => { // Handle failed responses if (!response.ok) { - console.error( - `Storage API error: ${response.status} for ${storageUrl}, referrer: ${req.referrer}, url: ${req.url}`, - ); + // console.error( + // `Storage API error: ${response.status} for ${storageUrl}, referrer: ${req.referrer}, url: ${req.url}`, + // ); + // 404 = File not found (likely stale URL after media update) + // 410 = Gone (explicitly removed) + const isStaleResource = + response.status === 404 || response.status === 410; // Return fallback for image requests if (isImageRequest || isNextImageRequest) { const url = req.nextUrl.clone(); url.pathname = "/fallback-image.png"; url.search = ""; - return NextResponse.rewrite(url); + const response = NextResponse.rewrite(url); + if (isStaleResource) { + response.cookies.set(STALE_KEY, "1", { + maxAge: 5, + path: "/", + }); + } + + return response; } // Return fallback for video requests @@ -85,10 +98,32 @@ export const apisMiddleware = async (req: NextRequest) => { const url = req.nextUrl.clone(); url.pathname = "/fallback-video.png"; url.search = ""; - return NextResponse.rewrite(url); + const response = NextResponse.rewrite(url); + if (isStaleResource) { + response.cookies.set(STALE_KEY, "1", { + maxAge: 5, + path: "/", + }); + } + + return response; } - return new NextResponse("Storage API error", { status: response.status }); + // For non-media requests (e.g., documents), return JSON with reload hint + if (isStaleResource) { + return NextResponse.json( + { message: "Stale resource, please reload." }, + { status: 410 }, + ); + } + return new NextResponse(`Storage API error: ${response.status}`, { + status: response.status, + }); + } + + //ignore if 204 no content, is the favicon request + if (response.status === 204) { + return new NextResponse(null, { status: 200 }); } // Get response data diff --git a/apps/infoalloggi/src/pages/annuncio/[cod].tsx b/apps/infoalloggi/src/pages/annuncio/[cod].tsx index 4c30024..acac54d 100644 --- a/apps/infoalloggi/src/pages/annuncio/[cod].tsx +++ b/apps/infoalloggi/src/pages/annuncio/[cod].tsx @@ -51,6 +51,7 @@ import { type CaratteristicheFiltered, filteredCaratteristiche, } from "~/hooks/schedaAnnuncioUtils"; +import { useStaleImageReload } from "~/hooks/staleImage"; import { useMediaQuery } from "~/hooks/use-media-query"; import { handleConsegna } from "~/lib/annuncio_details"; import { replaceWithBr } from "~/lib/newlineToBr"; @@ -106,6 +107,7 @@ const AnnuncioDettaglio: NextPage = ({ const AnnuncioView = ({ cod, flag }: Omit) => { const session = useSession(); const { locale } = useTranslation(); + useStaleImageReload(); const { data, isLoading } = api.annunci.getAnnuncio.useQuery( {