UNPKG

@funkit/connect

Version:

Funkit Connect SDK elevates DeFi apps via web2 sign-ins and one-click checkouts.

231 lines (225 loc) 6.21 kB
"use client"; import { Box } from "./chunk-LUHEVXPD.js"; import { logger } from "./chunk-AHYOV7AV.js"; // src/components/AsyncImage/useAsyncImage.ts import { useEffect, useReducer } from "react"; var cachedUrls = /* @__PURE__ */ new Map(); var cachedRequestPromises = /* @__PURE__ */ new Map(); async function loadAsyncImage(asyncImage) { const cachedRequestPromise = cachedRequestPromises.get(asyncImage); if (cachedRequestPromise) { return cachedRequestPromise; } const load = async () => asyncImage().then(async (url) => { cachedUrls.set(asyncImage, url); return url; }); const requestPromise = load().catch(async () => { return load().catch(() => { cachedRequestPromises.delete(asyncImage); return void 0; }); }); cachedRequestPromises.set(asyncImage, requestPromise); return requestPromise; } function useForceUpdate() { const [, forceUpdate] = useReducer((x) => x + 1, 0); return forceUpdate; } function useAsyncImage(url) { const cachedUrl = typeof url === "function" ? cachedUrls.get(url) : void 0; const forceUpdate = useForceUpdate(); useEffect(() => { if (typeof url === "function" && !cachedUrl) { loadAsyncImage(url).then(forceUpdate); } }, [url, cachedUrl, forceUpdate]); return typeof url === "function" ? cachedUrl : url; } var REMOTE_IMG_CACHE = /* @__PURE__ */ new Set(); function preloadRemoteImages(...imageUrls) { for (const imageUrl of imageUrls) { try { const { protocol } = new URL(imageUrl); if (protocol !== "https:" && protocol !== "http:") { logger.warn("preloadRemoteImages:invalidUrl", { message: "Invalid protocol", imageUrl }); continue; } if (!REMOTE_IMG_CACHE.has(imageUrl)) { REMOTE_IMG_CACHE.add(imageUrl); const preloadImage = new Image(); preloadImage.src = imageUrl; } } catch (error) { logger.warn("preloadRemoteImages:error", { message: "Unable to preload image", imageUrl, error }); } } } // src/components/AsyncImage/AsyncImage.tsx import React, { useMemo, useReducer as useReducer2, useState } from "react"; // src/utils/sanitizeUrl.ts var SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["https:", "http:"]); function sanitizeUrl(url) { if (!url) { return void 0; } try { const parsed = new URL(url); if (SAFE_URL_PROTOCOLS.has(parsed.protocol)) { return parsed.href; } return void 0; } catch { return void 0; } } function sanitizeCssUrl(url) { const safeUrl = sanitizeUrl(url); if (!safeUrl) { return void 0; } return safeUrl.replace(/['()\\]/g, (char) => { return `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`; }); } // src/components/AsyncImage/AsyncImage.tsx function useTriggeredOnce() { return useReducer2(() => true, false); } function AsyncImage({ alt, background, borderColor, borderRadius, boxShadow, height, useAsImage, src: srcProp, fallbackSrc, fallbackElement, width, testId }) { const src = useAsyncImage(srcProp || fallbackSrc); const isRemoteImage = src !== void 0 && /^(https?|data):/.test(src); const [isRemoteImageLoaded, setRemoteImageLoaded] = useTriggeredOnce(); const [hasError, setHasError] = useState( srcProp === void 0 && fallbackSrc === void 0 ); const { asyncStyle, imgProps } = useMemo(() => { const asyncStyle2 = !useAsImage ? getRemoteImageStyles(isRemoteImage, isRemoteImageLoaded, src) : {}; if (useAsImage || isRemoteImage) { const imgProps2 = { as: "img", src, "aria-hidden": true, onError: ({ currentTarget }) => { currentTarget.onerror = null; if (fallbackSrc !== void 0) { currentTarget.src = fallbackSrc; } else { setHasError(true); } }, onLoad: isRemoteImage ? setRemoteImageLoaded : void 0 }; return { imgProps: imgProps2, asyncStyle: asyncStyle2 }; } return { imgProps: { backgroundSize: "cover" }, asyncStyle: { ...asyncStyle2, // has to be added as a inline style because vanilla-extract doesn't recognize this as a valid style backgroundPosition: "center" } }; }, [ fallbackSrc, isRemoteImage, isRemoteImageLoaded, src, useAsImage, setRemoteImageLoaded ]); const mainElement = hasError && fallbackElement || /* @__PURE__ */ React.createElement( Box, { ...imgProps, height: "full", position: "absolute", style: { WebkitTouchCallout: "none", transition: "opacity .15s linear", userSelect: "none", ...asyncStyle }, width: "full" } ); return /* @__PURE__ */ React.createElement( Box, { "aria-label": alt, borderRadius, boxShadow, height: typeof height === "string" ? height : void 0, overflow: "hidden", position: "relative", role: "img", style: { background, height: typeof height === "number" ? height : void 0, width: typeof width === "number" ? width : void 0 }, width: typeof width === "string" ? width : void 0, testId }, mainElement, borderColor ? /* @__PURE__ */ React.createElement( Box, { ...typeof borderColor === "object" && "custom" in borderColor ? { style: { borderColor: borderColor.custom } } : { borderColor }, borderRadius, borderStyle: "solid", borderWidth: "1", height: "full", position: "relative", width: "full" } ) : null ); } var getRemoteImageStyles = (isRemoteImage, isRemoteImageLoaded, src) => { if (isRemoteImage) { return { opacity: isRemoteImageLoaded ? 1 : 0 }; } const safeSrc = sanitizeCssUrl(src); return { // note the url must have quotation, see https://github.com/evanw/esbuild/issues/1843#issuecomment-1370108070 backgroundImage: safeSrc ? `url('${safeSrc}')` : void 0, backgroundRepeat: "no-repeat", opacity: src ? 1 : 0 }; }; export { preloadRemoteImages, AsyncImage };