diff --git a/src/app/[name]/(Main)/components/CategoryScroll.tsx b/src/app/[name]/(Main)/components/CategoryScroll.tsx index 548fb73..eb47ef8 100644 --- a/src/app/[name]/(Main)/components/CategoryScroll.tsx +++ b/src/app/[name]/(Main)/components/CategoryScroll.tsx @@ -1,5 +1,6 @@ 'use client'; +import { usePathname } from "next/navigation"; import Image from "next/image"; import clsx from "clsx"; import HorizontalScrollView from "@/components/listview/HorizontalScrollView"; @@ -7,6 +8,74 @@ import CategoryItemRenderer from "@/components/listview/CategoryItemRenderer"; import CategorySmallItemRenderer from "@/components/listview/CategorySmallItemRenderer"; import { Category } from "@/app/[name]/(Main)/types/Types"; +const SVG_MASK_STYLE = { + maskSize: "contain", + maskRepeat: "no-repeat", + maskPosition: "center", + WebkitMaskSize: "contain", + WebkitMaskRepeat: "no-repeat", + WebkitMaskPosition: "center", +} as const; + +function CategoryImage({ + src, + size, + alt, + proxyBase, +}: { + src: string; + size: number; + alt: string; + proxyBase: string | null; +}) { + const isSvg = src.endsWith(".svg"); + + if (isSvg) { + const isSameOrigin = + src.startsWith("/") || + (typeof window !== "undefined" && + new URL(src, window.location.href).origin === window.location.origin); + + const maskUrl = isSameOrigin + ? src + : proxyBase + ? `${proxyBase}/api/proxy-svg?url=${encodeURIComponent(src)}` + : null; + + if (maskUrl) { + return ( +
+ ); + } + + return ( + // eslint-disable-next-line @next/next/no-img-element + {alt} + ); + } + + return ( + {alt} + ); +} + type Variant = "large" | "small"; const variantConfig: Record< @@ -41,6 +110,8 @@ const CategoryScroll = ({ variant = "large", className, }: Props) => { + const segment = usePathname()?.split("/").filter(Boolean)[0]; + const proxyBase = segment != null ? `/${segment}` : null; const { renderer: Renderer, imageSize } = variantConfig[variant]; const handleSelect = (categoryId: string) => () => onSelect(categoryId); @@ -76,12 +147,11 @@ const CategoryScroll = ({ className={clsx(isSelected && "bg-container!")} onClick={handleSelect(item.id)} > - category image {item.title} diff --git a/src/app/[name]/api/proxy-svg/route.ts b/src/app/[name]/api/proxy-svg/route.ts new file mode 100644 index 0000000..f57e2cc --- /dev/null +++ b/src/app/[name]/api/proxy-svg/route.ts @@ -0,0 +1,46 @@ +import { NextRequest, NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; +export const revalidate = 0; + +const CACHE_MAX_AGE = "public, max-age=3600"; + +export async function GET(request: NextRequest) { + const url = request.nextUrl.searchParams.get("url"); + if (!url) { + return NextResponse.json({ error: "url is required" }, { status: 400 }); + } + try { + const parsed = new URL(url); + if (!["http:", "https:"].includes(parsed.protocol)) { + return NextResponse.json({ error: "Invalid URL" }, { status: 400 }); + } + const res = await fetch(url, { + headers: { Accept: "image/svg+xml, text/xml, text/plain" }, + }); + if (!res.ok) { + return NextResponse.json( + { error: "Failed to fetch SVG" }, + { status: res.status } + ); + } + const text = await res.text(); + if (!text.trim().toLowerCase().includes("