feat: 添加全局 Error Boundary 和餐厅图片加载失败 fallback
- error.tsx: 路由级错误边界,提供重试和返回首页操作 - global-error.tsx: 根布局级兜底,纯内联样式避免依赖加载 - RestaurantImage: 可复用图片组件,加载失败显示餐具占位图标 - 替换 RestaurantCard、MatchResult、profile 中所有餐厅图片
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
import { fireCelebration, playChime } from "@/lib/celebrate";
|
||||
import { isRegistered } from "@/lib/userId";
|
||||
import ShareCardModal from "@/components/ShareCardModal";
|
||||
import RestaurantImage from "@/components/RestaurantImage";
|
||||
import AuthModal from "@/components/AuthModal";
|
||||
|
||||
interface MatchResultProps {
|
||||
@@ -145,11 +146,10 @@ function RunnerUpCard({
|
||||
className="flex gap-3 rounded-xl bg-surface/80 p-2.5 ring-1 ring-border/50 backdrop-blur-sm transition-colors hover:bg-elevated/80"
|
||||
>
|
||||
{restaurant.images?.[0] && (
|
||||
<img
|
||||
<RestaurantImage
|
||||
src={restaurant.images[0]}
|
||||
alt={restaurant.name}
|
||||
className="h-16 w-16 shrink-0 rounded-lg object-cover"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
)}
|
||||
<div className="flex min-w-0 flex-1 flex-col justify-center">
|
||||
@@ -397,11 +397,10 @@ export default function MatchResult({
|
||||
</motion.button>
|
||||
)}
|
||||
{restaurant.images?.[0] && (
|
||||
<img
|
||||
<RestaurantImage
|
||||
src={restaurant.images[0]}
|
||||
alt={restaurant.name}
|
||||
className="h-44 w-full object-cover"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
)}
|
||||
<div className="p-4">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCallback, useState, useEffect } from "react";
|
||||
import { Star, MapPin, Clock, ExternalLink, Flame, Bookmark, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { Restaurant } from "@/types";
|
||||
import { getUserId, isRegistered } from "@/lib/userId";
|
||||
import RestaurantImage from "@/components/RestaurantImage";
|
||||
|
||||
interface RestaurantCardProps {
|
||||
restaurant: Restaurant;
|
||||
@@ -57,16 +58,15 @@ function ImageGallery({ images, name }: { images: string[]; name: string }) {
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full" onClick={handleTap} onPointerDown={stopAll}>
|
||||
<img
|
||||
<RestaurantImage
|
||||
src={images[idx]}
|
||||
alt={name}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
draggable={false}
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
|
||||
{fadingOut !== null && (
|
||||
<img
|
||||
<RestaurantImage
|
||||
key={fadingOut}
|
||||
src={images[fadingOut]}
|
||||
alt=""
|
||||
@@ -74,7 +74,6 @@ function ImageGallery({ images, name }: { images: string[]; name: string }) {
|
||||
style={{ animation: "img-fade-out 280ms ease-out forwards" }}
|
||||
onAnimationEnd={() => setFadingOut(null)}
|
||||
draggable={false}
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import { UtensilsCrossed } from "lucide-react";
|
||||
|
||||
interface RestaurantImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
className?: string;
|
||||
draggable?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
onAnimationEnd?: () => void;
|
||||
}
|
||||
|
||||
export default function RestaurantImage({
|
||||
src,
|
||||
alt,
|
||||
className = "",
|
||||
draggable,
|
||||
style,
|
||||
onAnimationEnd,
|
||||
}: RestaurantImageProps) {
|
||||
const [failed, setFailed] = useState(false);
|
||||
|
||||
const handleError = useCallback(() => setFailed(true), []);
|
||||
|
||||
if (failed) {
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center justify-center bg-elevated ${className}`}
|
||||
style={style}
|
||||
>
|
||||
<UtensilsCrossed className="h-1/3 w-1/3 max-h-8 max-w-8 text-muted/40" strokeWidth={1.5} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={className}
|
||||
draggable={draggable}
|
||||
style={style}
|
||||
referrerPolicy="no-referrer"
|
||||
onError={handleError}
|
||||
onAnimationEnd={onAnimationEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user