@adland/react 0.15.4 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +64 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -358,6 +358,60 @@ var adCardLabel = {
|
|
|
358
358
|
};
|
|
359
359
|
var adlandApiUrl = process.env.NODE_ENV === "development" ? "http://localhost:3069" : "https://api.adland.space";
|
|
360
360
|
|
|
361
|
+
// src/utils/tracking.ts
|
|
362
|
+
var UMAMI_URL = "https://umami.api.0xslots.org";
|
|
363
|
+
var WEBSITE_ID = "b02a0a73-70c1-44cd-b4ae-cd70fa3d632f";
|
|
364
|
+
var tracked = /* @__PURE__ */ new Set();
|
|
365
|
+
function sendEvent(eventName, data) {
|
|
366
|
+
if (typeof window === "undefined") return;
|
|
367
|
+
const url = window.location.href;
|
|
368
|
+
const referrer = document.referrer || void 0;
|
|
369
|
+
const hostname = window.location.hostname;
|
|
370
|
+
try {
|
|
371
|
+
fetch(`${UMAMI_URL}/api/send`, {
|
|
372
|
+
method: "POST",
|
|
373
|
+
headers: { "Content-Type": "application/json" },
|
|
374
|
+
body: JSON.stringify({
|
|
375
|
+
type: "event",
|
|
376
|
+
payload: {
|
|
377
|
+
website: WEBSITE_ID,
|
|
378
|
+
url,
|
|
379
|
+
referrer,
|
|
380
|
+
hostname,
|
|
381
|
+
name: eventName,
|
|
382
|
+
data
|
|
383
|
+
}
|
|
384
|
+
}),
|
|
385
|
+
keepalive: true
|
|
386
|
+
}).catch(() => {
|
|
387
|
+
});
|
|
388
|
+
} catch {
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function trackImpression(element, data) {
|
|
392
|
+
if (!element || typeof window === "undefined") return;
|
|
393
|
+
const key = `${data.slot}:${window.location.href}`;
|
|
394
|
+
if (tracked.has(key)) return;
|
|
395
|
+
const observer = new IntersectionObserver(
|
|
396
|
+
(entries) => {
|
|
397
|
+
for (const entry of entries) {
|
|
398
|
+
if (entry.isIntersecting && !tracked.has(key)) {
|
|
399
|
+
tracked.add(key);
|
|
400
|
+
sendEvent("impression", data);
|
|
401
|
+
observer.disconnect();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{ threshold: 0.5 }
|
|
406
|
+
// 50% visible
|
|
407
|
+
);
|
|
408
|
+
observer.observe(element);
|
|
409
|
+
return () => observer.disconnect();
|
|
410
|
+
}
|
|
411
|
+
function trackClick(eventName, data) {
|
|
412
|
+
sendEvent(eventName, data);
|
|
413
|
+
}
|
|
414
|
+
|
|
361
415
|
// src/components/Ad.tsx
|
|
362
416
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
363
417
|
function Ad({
|
|
@@ -383,21 +437,30 @@ function Ad({
|
|
|
383
437
|
async () => {
|
|
384
438
|
if (!client || !slot) throw new Error("NO_AD" /* NO_AD */);
|
|
385
439
|
const uri = await fetchMetadataURI(client, slot);
|
|
386
|
-
if (!uri)
|
|
440
|
+
if (!uri) {
|
|
441
|
+
console.info("[Ad] no metadata URI found for slot", slot);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
387
444
|
return fetchAdFromURI(uri);
|
|
388
445
|
},
|
|
389
446
|
{ enabled: !!slot && !staticData }
|
|
390
447
|
);
|
|
391
448
|
const adData = staticData ?? fetchedData;
|
|
392
449
|
const isEmpty = !adData && !isLoading && (error instanceof Error ? error.message === "NO_AD" /* NO_AD */ : !error);
|
|
450
|
+
(0, import_react3.useEffect)(() => {
|
|
451
|
+
if (!adData || !slot) return;
|
|
452
|
+
return trackImpression(ref.current, { slot, chainId });
|
|
453
|
+
}, [adData, slot, chainId]);
|
|
393
454
|
const onClick = (0, import_react3.useCallback)(
|
|
394
455
|
(e) => {
|
|
395
456
|
const target = e.target;
|
|
396
457
|
const isInteractive = target.tagName === "A" || target.tagName === "BUTTON" || target.closest("a") !== null || target.closest("button") !== null;
|
|
397
458
|
if (isInteractive) return;
|
|
398
459
|
if (adData) {
|
|
460
|
+
if (slot) trackClick("click", { slot, chainId });
|
|
399
461
|
performAdAction(adData);
|
|
400
462
|
} else if (isEmpty && slot) {
|
|
463
|
+
trackClick("click-empty", { slot, chainId });
|
|
401
464
|
performEmptyAdAction(slot, chainId, baseLinkUrl);
|
|
402
465
|
}
|
|
403
466
|
},
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/Ad.tsx","../src/fetch.ts","../src/hooks/useAdContext.ts","../src/hooks/useFetch.ts","../src/utils/fetchCache.ts","../src/utils/ad-actions.ts","../src/utils/ad-fields.ts","../src/utils/constants.ts"],"sourcesContent":["// Compound Ad components\nexport {\n Ad,\n AdBadge,\n AdDescription,\n AdEmpty,\n AdError,\n AdImage,\n AdLabel,\n AdLoaded,\n AdLoading,\n AdTitle,\n} from \"./components/Ad\";\n\nexport type {\n AdBadgeProps,\n AdDescriptionProps,\n AdImageProps,\n AdLabelProps,\n AdStatusProps,\n AdTitleProps,\n} from \"./components/Ad\";\n\n// Context hook\nexport { useAd } from \"./hooks/useAdContext\";\nexport type { AdContextValue } from \"./hooks/useAdContext\";\n\n// Field helpers\nexport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"./utils/ad-fields\";\n\n// Types\nexport type { AdProps, AdDataQueryError } from \"./types\";\n\n// Constants\nexport { adCardIcon, adCardLabel } from \"./utils/constants\";\n","import { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { useCallback, useMemo, useRef } from \"react\";\n\nimport { createReadClient, fetchAdFromURI, fetchMetadataURI } from \"../fetch\";\nimport { AdContext, useAd } from \"../hooks/useAdContext\";\nimport { useFetch } from \"../hooks/useFetch\";\nimport { AdDataQueryError, type AdProps } from \"../types\";\nimport { performAdAction, performEmptyAdAction } from \"../utils/ad-actions\";\nimport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"../utils/ad-fields\";\nimport { adCardIcon, adCardLabel } from \"../utils/constants\";\n\n// ─── Root component ──────────────────────────────────────────────────────────\n\n/**\n * Root Ad component — compound pattern.\n *\n * @example\n * ```tsx\n * <Ad slot=\"0xabc...123\" className=\"rounded-md border p-3\">\n * <AdImage className=\"size-10 rounded-md\" />\n * <AdTitle className=\"text-sm font-medium\" />\n * <AdDescription className=\"text-xs text-muted-foreground\" />\n * <AdBadge />\n * </Ad>\n * ```\n */\nexport function Ad({\n slot,\n data: staticData,\n chainId = SlotsChain.BASE,\n rpcUrl,\n baseLinkUrl = \"https://app.0xslots.org\",\n children,\n ...props\n}: AdProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n const client = useMemo(\n () => (slot ? createReadClient(chainId, rpcUrl) : null),\n [slot, chainId, rpcUrl],\n );\n\n const {\n data: fetchedData,\n isLoading,\n error,\n } = useFetch<AdData>(\n `ad-data-${slot}`,\n async () => {\n if (!client || !slot) throw new Error(AdDataQueryError.NO_AD);\n const uri = await fetchMetadataURI(client, slot);\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n return fetchAdFromURI(uri);\n },\n { enabled: !!slot && !staticData },\n );\n\n const adData = staticData ?? fetchedData;\n\n const isEmpty =\n !adData &&\n !isLoading &&\n (error instanceof Error\n ? error.message === AdDataQueryError.NO_AD\n : !error);\n\n const onClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n const isInteractive =\n target.tagName === \"A\" ||\n target.tagName === \"BUTTON\" ||\n target.closest(\"a\") !== null ||\n target.closest(\"button\") !== null;\n if (isInteractive) return;\n\n if (adData) {\n performAdAction(adData);\n } else if (isEmpty && slot) {\n performEmptyAdAction(slot, chainId, baseLinkUrl);\n }\n },\n [adData, isEmpty, slot, chainId, baseLinkUrl],\n );\n\n return (\n <AdContext.Provider\n value={{\n data: adData ?? null,\n isLoading: !!slot && !staticData && isLoading,\n error,\n isEmpty,\n slot,\n baseLinkUrl,\n chainId,\n }}\n >\n <div ref={ref} onClick={onClick} {...props}>\n {children}\n </div>\n </AdContext.Provider>\n );\n}\n\n// ─── Sub-components ──────────────────────────────────────────────────────────\n\nexport interface AdImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdImage({ fallback, ...props }: AdImageProps) {\n const { data } = useAd();\n const src = getAdImage(data);\n if (!src) return fallback ? <>{fallback}</> : null;\n return <img src={src} alt=\"\" {...props} />;\n}\n\nexport interface AdTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdTitle({ fallback, children, ...props }: AdTitleProps) {\n const { data } = useAd();\n const title = getAdTitle(data);\n if (!title) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? title}</p>;\n}\n\nexport interface AdDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdDescription({ fallback, children, ...props }: AdDescriptionProps) {\n const { data } = useAd();\n const description = getAdDescription(data);\n if (!description) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? description}</p>;\n}\n\nexport interface AdBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdBadge({ children, ...props }: AdBadgeProps) {\n const { data } = useAd();\n const type = getAdType(data);\n if (!type) return null;\n const Icon = adCardIcon[type];\n const label = adCardLabel[type];\n return (\n <span {...props}>\n {children ?? (\n <>\n {Icon && <Icon className=\"size-3\" />}\n {label}\n </>\n )}\n </span>\n );\n}\n\nexport interface AdLabelProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdLabel({ children, ...props }: AdLabelProps) {\n return <span {...props}>{children ?? \"AD\"}</span>;\n}\n\n// ─── State components ────────────────────────────────────────────────────────\n\nexport interface AdStatusProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode;\n}\n\nexport function AdLoading({ children, ...props }: AdStatusProps) {\n const { isLoading } = useAd();\n if (!isLoading) return null;\n return <div {...props}>{children ?? \"Loading...\"}</div>;\n}\n\nexport function AdEmpty({ children, ...props }: AdStatusProps) {\n const { isEmpty } = useAd();\n if (!isEmpty) return null;\n return <div {...props}>{children ?? \"Your ad here\"}</div>;\n}\n\nexport function AdError({ children, ...props }: AdStatusProps) {\n const { error, isEmpty } = useAd();\n if (!error || isEmpty) return null;\n return <div {...props}>{children ?? \"Error loading ad\"}</div>;\n}\n\nexport function AdLoaded({ children, ...props }: AdStatusProps) {\n const { data } = useAd();\n if (!data) return null;\n return <div {...props}>{children}</div>;\n}\n","import { SlotsClient, type SlotsChain } from \"@0xslots/sdk\";\nimport { type Address, createPublicClient, http } from \"viem\";\nimport { base, baseSepolia } from \"viem/chains\";\n\nimport { AdDataQueryError } from \"./types\";\n\nconst IPFS_GATEWAY = \"https://amethyst-representative-mandrill-369.mypinata.cloud/ipfs/\";\n\nconst viemChains: Record<number, typeof base> = {\n 8453: base,\n 84532: baseSepolia,\n};\n\n/**\n * Create a read-only SlotsClient for a given chain.\n */\nexport function createReadClient(\n chainId: SlotsChain,\n rpcUrl?: string,\n): SlotsClient {\n const chain = viemChains[chainId];\n if (!chain) throw new Error(`Unsupported chain: ${chainId}`);\n\n const publicClient = createPublicClient({\n chain,\n transport: http(rpcUrl),\n });\n\n return new SlotsClient({ chainId, publicClient });\n}\n\n/**\n * Fetch ad content from a metadata URI (IPFS or HTTP)\n */\nexport const fetchAdFromURI = async (uri: string) => {\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n\n const url = uri.startsWith(\"ipfs://\")\n ? `${IPFS_GATEWAY}${uri.slice(7)}`\n : uri;\n\n const res = await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!res.ok) {\n if (res.status === 404) throw new Error(AdDataQueryError.NO_AD);\n throw new Error(AdDataQueryError.ERROR);\n }\n\n const data = await res.json();\n if (data.error) throw new Error(data.error);\n\n return data;\n};\n\n/**\n * Fetch the metadata URI for a slot using the SDK.\n */\nexport const fetchMetadataURI = async (\n client: SlotsClient,\n slotAddress: string,\n): Promise<string> => {\n const info = await client.getSlotInfo(slotAddress as Address);\n const moduleAddress = (info as { module: Address }).module;\n\n if (\n !moduleAddress ||\n moduleAddress === \"0x0000000000000000000000000000000000000000\"\n ) {\n return \"\";\n }\n\n return client.modules.metadata.getURI(\n moduleAddress,\n slotAddress as Address,\n );\n};\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { createContext, useContext } from \"react\";\n\nexport interface AdContextValue {\n data: AdData | null;\n isLoading: boolean;\n error: unknown;\n isEmpty: boolean;\n slot?: string;\n baseLinkUrl: string;\n chainId: SlotsChain;\n}\n\nexport const AdContext = createContext<AdContextValue | null>(null);\n\nexport function useAd(): AdContextValue {\n const ctx = useContext(AdContext);\n if (!ctx) throw new Error(\"useAd must be used within an <Ad> component\");\n return ctx;\n}\n","import { useEffect, useState } from \"react\";\nimport FetchCache from \"../utils/fetchCache\";\n\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nconst globalCache = new FetchCache();\n\nexport const fetchCache = {\n clear: () => globalCache.clear(),\n};\n\nexport function useFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n opts?: {\n enabled?: boolean;\n ttl?: number; // ms\n },\n) {\n const { enabled = true, ttl = 0 } = opts ?? {};\n\n // Get cached data from global cache\n const getCachedData = (): T | null => {\n return globalCache.get<T>(key, ttl || undefined);\n };\n\n const hasCachedData = () => {\n return globalCache.has(key, ttl || undefined);\n };\n\n // Check if there's an active fetch for this key (dedupe concurrent requests)\n const getActiveFetch = (): Promise<T> | undefined => {\n return globalCache.getActiveFetch<T>(key);\n };\n\n const cachedData = getCachedData();\n const [data, setData] = useState<T | null>(cachedData);\n const [error, setError] = useState<unknown>(null);\n // If we have cached data, start with success status to avoid showing loader\n const [status, setStatus] = useState<Status>(cachedData ? \"success\" : \"idle\");\n\n const refetch = async () => {\n // ALWAYS check cache first - never show loading if we have valid cached data\n const cached = getCachedData();\n if (cached) {\n // Already have valid cached data, ensure state is correct\n setData(cached);\n setStatus(\"success\");\n return cached;\n }\n\n // Check if there's already an active fetch for this key (dedupe)\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n try {\n const res = await activeFetch;\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n }\n\n // Only set loading if we actually need to fetch\n setStatus(\"loading\");\n setError(null);\n\n try {\n // Create fetch promise and store it for deduplication\n const fetchPromise = fetcher();\n globalCache.setActiveFetch(key, fetchPromise);\n\n const res = await fetchPromise;\n globalCache.set(key, res);\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n const cached = getCachedData();\n\n if (cached) {\n setData(cached);\n setStatus(\"success\");\n return;\n }\n\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n setStatus(\"loading\");\n activeFetch\n .then((res) => {\n setData(res);\n setStatus(\"success\");\n })\n .catch((e) => {\n setError(e);\n setStatus(\"error\");\n });\n return;\n }\n\n if (status !== \"loading\" && status !== \"success\") {\n refetch();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, enabled]);\n\n const hasValidCache = hasCachedData();\n const isLoading = status === \"loading\" && !hasValidCache;\n\n return {\n data,\n error,\n status,\n isIdle: status === \"idle\",\n isLoading,\n isSuccess: status === \"success\",\n isError: status === \"error\",\n refetch,\n };\n}\n","class FetchCache {\n private cache = new Map<string, { data: unknown; ts: number }>();\n private activeFetches = new Map<string, Promise<unknown>>();\n\n get<T>(key: string, ttl?: number): T | null {\n const cached = this.cache.get(key);\n if (!cached) return null;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n // Cache expired, remove it\n this.cache.delete(key);\n return null;\n }\n\n return cached.data as T;\n }\n\n has(key: string, ttl?: number): boolean {\n const cached = this.cache.get(key);\n if (!cached) return false;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n set<T>(key: string, data: T): void {\n this.cache.set(key, { data, ts: Date.now() });\n }\n\n getActiveFetch<T>(key: string): Promise<T> | undefined {\n return this.activeFetches.get(key) as Promise<T> | undefined;\n }\n\n setActiveFetch<T>(key: string, promise: Promise<T>): void {\n this.activeFetches.set(key, promise);\n promise.finally(() => {\n this.activeFetches.delete(key);\n });\n }\n\n clear(): void {\n this.cache.clear();\n this.activeFetches.clear();\n }\n}\n\nexport default FetchCache;\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport sdk from \"@farcaster/miniapp-sdk\";\nimport { getAddress } from \"viem\";\n\n// Cache the miniapp check at module level — resolved once, sync thereafter\nlet _isMiniApp: boolean | null = null;\nconst _miniAppPromise = sdk\n .isInMiniApp()\n .then((v) => {\n _isMiniApp = v;\n return v;\n })\n .catch(() => {\n _isMiniApp = false;\n return false;\n });\n\nasync function isMiniApp(): Promise<boolean> {\n if (_isMiniApp !== null) return _isMiniApp;\n return _miniAppPromise;\n}\n\nexport function performAdAction(adData: AdData) {\n try {\n switch (adData.type) {\n case \"link\":\n sdk.actions.openUrl(adData.data.url);\n break;\n case \"cast\":\n sdk.actions.viewCast({ hash: adData.data.hash });\n break;\n case \"miniapp\":\n sdk.actions.openMiniApp({ url: adData.data.url });\n break;\n case \"token\": {\n const address = adData.data.address;\n const chainId = adData.data.chainId;\n const buyToken = `eip155:${chainId}/erc20:${getAddress(address)}`;\n sdk.actions.swapToken({ buyToken });\n break;\n }\n case \"farcasterProfile\":\n sdk.actions.viewProfile({\n fid: Number.parseInt(adData.data.fid, 10),\n });\n break;\n }\n } catch (err) {\n // Fallback for web (non-miniapp) context\n if (adData.type === \"link\" || adData.type === \"miniapp\") {\n window.open(adData.data.url, \"_blank\");\n } else {\n console.error(\"[@adland/react] Failed to perform ad action:\", err);\n }\n }\n}\n\nexport async function performEmptyAdAction(\n slot: string,\n chainId: SlotsChain,\n baseLinkUrl: string,\n) {\n const url = `${baseLinkUrl}/slots/${slot}?chain=${chainId}`;\n if (await isMiniApp()) {\n sdk.actions.openMiniApp({ url });\n } else {\n window.open(url, \"_blank\");\n }\n}\n","import type { AdData, AdType } from \"@adland/data\";\n\nconst IMAGE_KEYS = [\"image\", \"icon\", \"pfpUrl\", \"logoURI\", \"imageUrl\"] as const;\nconst TITLE_KEYS = [\"title\", \"displayName\", \"username\", \"name\", \"symbol\"] as const;\nconst DESC_KEYS = [\"description\", \"bio\", \"text\", \"name\"] as const;\n\nfunction flatFields(data: AdData): Record<string, unknown> {\n return { ...data.data, ...(data.metadata ?? {}) };\n}\n\nexport function getAdImage(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of IMAGE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdTitle(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of TITLE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdDescription(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n const title = getAdTitle(data);\n for (const key of DESC_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v && v !== title) return v;\n }\n return null;\n}\n\nexport function getAdType(data: AdData | null): AdType | null {\n if (!data) return null;\n return data.type as AdType;\n}\n","import { AdType } from \"@adland/data\";\nimport { ForwardRefExoticComponent, RefAttributes } from \"react\";\nimport {\n Link,\n MessageCircle,\n LayoutGrid,\n LucideProps,\n Coins,\n User,\n} from \"lucide-react\";\n\nexport const adCardIcon: Record<\n AdType,\n ForwardRefExoticComponent<\n Omit<LucideProps, \"ref\"> & RefAttributes<SVGSVGElement>\n >\n> = {\n link: Link,\n cast: MessageCircle,\n miniapp: LayoutGrid,\n token: Coins,\n farcasterProfile: User,\n};\n\nexport const adCardLabel: Record<AdType, string> = {\n link: \"Link\",\n cast: \"Cast\",\n miniapp: \"Miniapp\",\n token: \"Token\",\n farcasterProfile: \"Profile\",\n};\n\nexport const adlandApiUrl =\n process.env.NODE_ENV === \"development\"\n ? \"http://localhost:3069\"\n : \"https://api.adland.space\";\n"],"mappings":"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","names":["import_sdk","import_react","import_react","import_viem","sdk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/Ad.tsx","../src/fetch.ts","../src/hooks/useAdContext.ts","../src/hooks/useFetch.ts","../src/utils/fetchCache.ts","../src/utils/ad-actions.ts","../src/utils/ad-fields.ts","../src/utils/constants.ts","../src/utils/tracking.ts"],"sourcesContent":["// Compound Ad components\nexport {\n Ad,\n AdBadge,\n AdDescription,\n AdEmpty,\n AdError,\n AdImage,\n AdLabel,\n AdLoaded,\n AdLoading,\n AdTitle,\n} from \"./components/Ad\";\n\nexport type {\n AdBadgeProps,\n AdDescriptionProps,\n AdImageProps,\n AdLabelProps,\n AdStatusProps,\n AdTitleProps,\n} from \"./components/Ad\";\n\n// Context hook\nexport { useAd } from \"./hooks/useAdContext\";\nexport type { AdContextValue } from \"./hooks/useAdContext\";\n\n// Field helpers\nexport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"./utils/ad-fields\";\n\n// Types\nexport type { AdProps, AdDataQueryError } from \"./types\";\n\n// Constants\nexport { adCardIcon, adCardLabel } from \"./utils/constants\";\n","import { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nimport { createReadClient, fetchAdFromURI, fetchMetadataURI } from \"../fetch\";\nimport { AdContext, useAd } from \"../hooks/useAdContext\";\nimport { useFetch } from \"../hooks/useFetch\";\nimport { AdDataQueryError, type AdProps } from \"../types\";\nimport { performAdAction, performEmptyAdAction } from \"../utils/ad-actions\";\nimport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"../utils/ad-fields\";\nimport { adCardIcon, adCardLabel } from \"../utils/constants\";\nimport { trackImpression, trackClick } from \"../utils/tracking\";\n\n// ─── Root component ──────────────────────────────────────────────────────────\n\n/**\n * Root Ad component — compound pattern.\n *\n * @example\n * ```tsx\n * <Ad slot=\"0xabc...123\" className=\"rounded-md border p-3\">\n * <AdImage className=\"size-10 rounded-md\" />\n * <AdTitle className=\"text-sm font-medium\" />\n * <AdDescription className=\"text-xs text-muted-foreground\" />\n * <AdBadge />\n * </Ad>\n * ```\n */\nexport function Ad({\n slot,\n data: staticData,\n chainId = SlotsChain.BASE,\n rpcUrl,\n baseLinkUrl = \"https://app.0xslots.org\",\n children,\n ...props\n}: AdProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n const client = useMemo(\n () => (slot ? createReadClient(chainId, rpcUrl) : null),\n [slot, chainId, rpcUrl],\n );\n\n const {\n data: fetchedData,\n isLoading,\n error,\n } = useFetch<AdData>(\n `ad-data-${slot}`,\n async () => {\n if (!client || !slot) throw new Error(AdDataQueryError.NO_AD);\n const uri = await fetchMetadataURI(client, slot);\n if (!uri) {\n console.info(\"[Ad] no metadata URI found for slot\", slot);\n return null;\n }\n return fetchAdFromURI(uri);\n },\n { enabled: !!slot && !staticData },\n );\n\n const adData = staticData ?? fetchedData;\n\n const isEmpty =\n !adData &&\n !isLoading &&\n (error instanceof Error\n ? error.message === AdDataQueryError.NO_AD\n : !error);\n\n // Track impression when ad is visible in viewport (once per slot per page load)\n useEffect(() => {\n if (!adData || !slot) return;\n return trackImpression(ref.current, { slot, chainId });\n }, [adData, slot, chainId]);\n\n const onClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n const isInteractive =\n target.tagName === \"A\" ||\n target.tagName === \"BUTTON\" ||\n target.closest(\"a\") !== null ||\n target.closest(\"button\") !== null;\n if (isInteractive) return;\n\n if (adData) {\n if (slot) trackClick(\"click\", { slot, chainId });\n performAdAction(adData);\n } else if (isEmpty && slot) {\n trackClick(\"click-empty\", { slot, chainId });\n performEmptyAdAction(slot, chainId, baseLinkUrl);\n }\n },\n [adData, isEmpty, slot, chainId, baseLinkUrl],\n );\n\n return (\n <AdContext.Provider\n value={{\n data: adData ?? null,\n isLoading: !!slot && !staticData && isLoading,\n error,\n isEmpty,\n slot,\n baseLinkUrl,\n chainId,\n }}\n >\n <div ref={ref} onClick={onClick} {...props}>\n {children}\n </div>\n </AdContext.Provider>\n );\n}\n\n// ─── Sub-components ──────────────────────────────────────────────────────────\n\nexport interface AdImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdImage({ fallback, ...props }: AdImageProps) {\n const { data } = useAd();\n const src = getAdImage(data);\n if (!src) return fallback ? <>{fallback}</> : null;\n return <img src={src} alt=\"\" {...props} />;\n}\n\nexport interface AdTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdTitle({ fallback, children, ...props }: AdTitleProps) {\n const { data } = useAd();\n const title = getAdTitle(data);\n if (!title) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? title}</p>;\n}\n\nexport interface AdDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdDescription({ fallback, children, ...props }: AdDescriptionProps) {\n const { data } = useAd();\n const description = getAdDescription(data);\n if (!description) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? description}</p>;\n}\n\nexport interface AdBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdBadge({ children, ...props }: AdBadgeProps) {\n const { data } = useAd();\n const type = getAdType(data);\n if (!type) return null;\n const Icon = adCardIcon[type];\n const label = adCardLabel[type];\n return (\n <span {...props}>\n {children ?? (\n <>\n {Icon && <Icon className=\"size-3\" />}\n {label}\n </>\n )}\n </span>\n );\n}\n\nexport interface AdLabelProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdLabel({ children, ...props }: AdLabelProps) {\n return <span {...props}>{children ?? \"AD\"}</span>;\n}\n\n// ─── State components ────────────────────────────────────────────────────────\n\nexport interface AdStatusProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode;\n}\n\nexport function AdLoading({ children, ...props }: AdStatusProps) {\n const { isLoading } = useAd();\n if (!isLoading) return null;\n return <div {...props}>{children ?? \"Loading...\"}</div>;\n}\n\nexport function AdEmpty({ children, ...props }: AdStatusProps) {\n const { isEmpty } = useAd();\n if (!isEmpty) return null;\n return <div {...props}>{children ?? \"Your ad here\"}</div>;\n}\n\nexport function AdError({ children, ...props }: AdStatusProps) {\n const { error, isEmpty } = useAd();\n if (!error || isEmpty) return null;\n return <div {...props}>{children ?? \"Error loading ad\"}</div>;\n}\n\nexport function AdLoaded({ children, ...props }: AdStatusProps) {\n const { data } = useAd();\n if (!data) return null;\n return <div {...props}>{children}</div>;\n}\n","import { SlotsClient, type SlotsChain } from \"@0xslots/sdk\";\nimport { type Address, createPublicClient, http } from \"viem\";\nimport { base, baseSepolia } from \"viem/chains\";\n\nimport { AdDataQueryError } from \"./types\";\n\nconst IPFS_GATEWAY = \"https://amethyst-representative-mandrill-369.mypinata.cloud/ipfs/\";\n\nconst viemChains: Record<number, typeof base> = {\n 8453: base,\n 84532: baseSepolia,\n};\n\n/**\n * Create a read-only SlotsClient for a given chain.\n */\nexport function createReadClient(\n chainId: SlotsChain,\n rpcUrl?: string,\n): SlotsClient {\n const chain = viemChains[chainId];\n if (!chain) throw new Error(`Unsupported chain: ${chainId}`);\n\n const publicClient = createPublicClient({\n chain,\n transport: http(rpcUrl),\n });\n\n return new SlotsClient({ chainId, publicClient });\n}\n\n/**\n * Fetch ad content from a metadata URI (IPFS or HTTP)\n */\nexport const fetchAdFromURI = async (uri: string) => {\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n\n const url = uri.startsWith(\"ipfs://\")\n ? `${IPFS_GATEWAY}${uri.slice(7)}`\n : uri;\n\n const res = await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!res.ok) {\n if (res.status === 404) throw new Error(AdDataQueryError.NO_AD);\n throw new Error(AdDataQueryError.ERROR);\n }\n\n const data = await res.json();\n if (data.error) throw new Error(data.error);\n\n return data;\n};\n\n/**\n * Fetch the metadata URI for a slot using the SDK.\n */\nexport const fetchMetadataURI = async (\n client: SlotsClient,\n slotAddress: string,\n): Promise<string> => {\n const info = await client.getSlotInfo(slotAddress as Address);\n const moduleAddress = (info as { module: Address }).module;\n\n if (\n !moduleAddress ||\n moduleAddress === \"0x0000000000000000000000000000000000000000\"\n ) {\n return \"\";\n }\n\n return client.modules.metadata.getURI(\n moduleAddress,\n slotAddress as Address,\n );\n};\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { createContext, useContext } from \"react\";\n\nexport interface AdContextValue {\n data: AdData | null;\n isLoading: boolean;\n error: unknown;\n isEmpty: boolean;\n slot?: string;\n baseLinkUrl: string;\n chainId: SlotsChain;\n}\n\nexport const AdContext = createContext<AdContextValue | null>(null);\n\nexport function useAd(): AdContextValue {\n const ctx = useContext(AdContext);\n if (!ctx) throw new Error(\"useAd must be used within an <Ad> component\");\n return ctx;\n}\n","import { useEffect, useState } from \"react\";\nimport FetchCache from \"../utils/fetchCache\";\n\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nconst globalCache = new FetchCache();\n\nexport const fetchCache = {\n clear: () => globalCache.clear(),\n};\n\nexport function useFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n opts?: {\n enabled?: boolean;\n ttl?: number; // ms\n },\n) {\n const { enabled = true, ttl = 0 } = opts ?? {};\n\n // Get cached data from global cache\n const getCachedData = (): T | null => {\n return globalCache.get<T>(key, ttl || undefined);\n };\n\n const hasCachedData = () => {\n return globalCache.has(key, ttl || undefined);\n };\n\n // Check if there's an active fetch for this key (dedupe concurrent requests)\n const getActiveFetch = (): Promise<T> | undefined => {\n return globalCache.getActiveFetch<T>(key);\n };\n\n const cachedData = getCachedData();\n const [data, setData] = useState<T | null>(cachedData);\n const [error, setError] = useState<unknown>(null);\n // If we have cached data, start with success status to avoid showing loader\n const [status, setStatus] = useState<Status>(cachedData ? \"success\" : \"idle\");\n\n const refetch = async () => {\n // ALWAYS check cache first - never show loading if we have valid cached data\n const cached = getCachedData();\n if (cached) {\n // Already have valid cached data, ensure state is correct\n setData(cached);\n setStatus(\"success\");\n return cached;\n }\n\n // Check if there's already an active fetch for this key (dedupe)\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n try {\n const res = await activeFetch;\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n }\n\n // Only set loading if we actually need to fetch\n setStatus(\"loading\");\n setError(null);\n\n try {\n // Create fetch promise and store it for deduplication\n const fetchPromise = fetcher();\n globalCache.setActiveFetch(key, fetchPromise);\n\n const res = await fetchPromise;\n globalCache.set(key, res);\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n const cached = getCachedData();\n\n if (cached) {\n setData(cached);\n setStatus(\"success\");\n return;\n }\n\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n setStatus(\"loading\");\n activeFetch\n .then((res) => {\n setData(res);\n setStatus(\"success\");\n })\n .catch((e) => {\n setError(e);\n setStatus(\"error\");\n });\n return;\n }\n\n if (status !== \"loading\" && status !== \"success\") {\n refetch();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, enabled]);\n\n const hasValidCache = hasCachedData();\n const isLoading = status === \"loading\" && !hasValidCache;\n\n return {\n data,\n error,\n status,\n isIdle: status === \"idle\",\n isLoading,\n isSuccess: status === \"success\",\n isError: status === \"error\",\n refetch,\n };\n}\n","class FetchCache {\n private cache = new Map<string, { data: unknown; ts: number }>();\n private activeFetches = new Map<string, Promise<unknown>>();\n\n get<T>(key: string, ttl?: number): T | null {\n const cached = this.cache.get(key);\n if (!cached) return null;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n // Cache expired, remove it\n this.cache.delete(key);\n return null;\n }\n\n return cached.data as T;\n }\n\n has(key: string, ttl?: number): boolean {\n const cached = this.cache.get(key);\n if (!cached) return false;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n set<T>(key: string, data: T): void {\n this.cache.set(key, { data, ts: Date.now() });\n }\n\n getActiveFetch<T>(key: string): Promise<T> | undefined {\n return this.activeFetches.get(key) as Promise<T> | undefined;\n }\n\n setActiveFetch<T>(key: string, promise: Promise<T>): void {\n this.activeFetches.set(key, promise);\n promise.finally(() => {\n this.activeFetches.delete(key);\n });\n }\n\n clear(): void {\n this.cache.clear();\n this.activeFetches.clear();\n }\n}\n\nexport default FetchCache;\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport sdk from \"@farcaster/miniapp-sdk\";\nimport { getAddress } from \"viem\";\n\n// Cache the miniapp check at module level — resolved once, sync thereafter\nlet _isMiniApp: boolean | null = null;\nconst _miniAppPromise = sdk\n .isInMiniApp()\n .then((v) => {\n _isMiniApp = v;\n return v;\n })\n .catch(() => {\n _isMiniApp = false;\n return false;\n });\n\nasync function isMiniApp(): Promise<boolean> {\n if (_isMiniApp !== null) return _isMiniApp;\n return _miniAppPromise;\n}\n\nexport function performAdAction(adData: AdData) {\n try {\n switch (adData.type) {\n case \"link\":\n sdk.actions.openUrl(adData.data.url);\n break;\n case \"cast\":\n sdk.actions.viewCast({ hash: adData.data.hash });\n break;\n case \"miniapp\":\n sdk.actions.openMiniApp({ url: adData.data.url });\n break;\n case \"token\": {\n const address = adData.data.address;\n const chainId = adData.data.chainId;\n const buyToken = `eip155:${chainId}/erc20:${getAddress(address)}`;\n sdk.actions.swapToken({ buyToken });\n break;\n }\n case \"farcasterProfile\":\n sdk.actions.viewProfile({\n fid: Number.parseInt(adData.data.fid, 10),\n });\n break;\n }\n } catch (err) {\n // Fallback for web (non-miniapp) context\n if (adData.type === \"link\" || adData.type === \"miniapp\") {\n window.open(adData.data.url, \"_blank\");\n } else {\n console.error(\"[@adland/react] Failed to perform ad action:\", err);\n }\n }\n}\n\nexport async function performEmptyAdAction(\n slot: string,\n chainId: SlotsChain,\n baseLinkUrl: string,\n) {\n const url = `${baseLinkUrl}/slots/${slot}?chain=${chainId}`;\n if (await isMiniApp()) {\n sdk.actions.openMiniApp({ url });\n } else {\n window.open(url, \"_blank\");\n }\n}\n","import type { AdData, AdType } from \"@adland/data\";\n\nconst IMAGE_KEYS = [\"image\", \"icon\", \"pfpUrl\", \"logoURI\", \"imageUrl\"] as const;\nconst TITLE_KEYS = [\"title\", \"displayName\", \"username\", \"name\", \"symbol\"] as const;\nconst DESC_KEYS = [\"description\", \"bio\", \"text\", \"name\"] as const;\n\nfunction flatFields(data: AdData): Record<string, unknown> {\n return { ...data.data, ...(data.metadata ?? {}) };\n}\n\nexport function getAdImage(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of IMAGE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdTitle(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of TITLE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdDescription(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n const title = getAdTitle(data);\n for (const key of DESC_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v && v !== title) return v;\n }\n return null;\n}\n\nexport function getAdType(data: AdData | null): AdType | null {\n if (!data) return null;\n return data.type as AdType;\n}\n","import { AdType } from \"@adland/data\";\nimport { ForwardRefExoticComponent, RefAttributes } from \"react\";\nimport {\n Link,\n MessageCircle,\n LayoutGrid,\n LucideProps,\n Coins,\n User,\n} from \"lucide-react\";\n\nexport const adCardIcon: Record<\n AdType,\n ForwardRefExoticComponent<\n Omit<LucideProps, \"ref\"> & RefAttributes<SVGSVGElement>\n >\n> = {\n link: Link,\n cast: MessageCircle,\n miniapp: LayoutGrid,\n token: Coins,\n farcasterProfile: User,\n};\n\nexport const adCardLabel: Record<AdType, string> = {\n link: \"Link\",\n cast: \"Cast\",\n miniapp: \"Miniapp\",\n token: \"Token\",\n farcasterProfile: \"Profile\",\n};\n\nexport const adlandApiUrl =\n process.env.NODE_ENV === \"development\"\n ? \"http://localhost:3069\"\n : \"https://api.adland.space\";\n","const UMAMI_URL = \"https://umami.api.0xslots.org\";\nconst WEBSITE_ID = \"b02a0a73-70c1-44cd-b4ae-cd70fa3d632f\";\n\ninterface TrackingPayload {\n slot: string;\n chainId: number;\n [key: string]: string | number | boolean | undefined;\n}\n\n/** Track which slot+url combos have already been counted this session */\nconst tracked = new Set<string>();\n\n/**\n * Send an event to Umami's collect endpoint.\n * Fires and forgets — never blocks rendering.\n */\nfunction sendEvent(eventName: string, data: TrackingPayload): void {\n if (typeof window === \"undefined\") return;\n\n const url = window.location.href;\n const referrer = document.referrer || undefined;\n const hostname = window.location.hostname;\n\n try {\n fetch(`${UMAMI_URL}/api/send`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n type: \"event\",\n payload: {\n website: WEBSITE_ID,\n url,\n referrer,\n hostname,\n name: eventName,\n data,\n },\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {}\n}\n\n/**\n * Track a unique impression (once per slot per page load).\n * Returns a cleanup function for the IntersectionObserver.\n */\nexport function trackImpression(\n element: HTMLElement | null,\n data: TrackingPayload,\n): (() => void) | undefined {\n if (!element || typeof window === \"undefined\") return;\n\n const key = `${data.slot}:${window.location.href}`;\n if (tracked.has(key)) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && !tracked.has(key)) {\n tracked.add(key);\n sendEvent(\"impression\", data);\n observer.disconnect();\n }\n }\n },\n { threshold: 0.5 }, // 50% visible\n );\n\n observer.observe(element);\n\n return () => observer.disconnect();\n}\n\n/**\n * Track a click event (no dedup — every click counts).\n */\nexport function trackClick(eventName: string, data: TrackingPayload): void {\n sendEvent(eventName, data);\n}\n"],"mappings":"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","names":["import_sdk","import_react","import_react","import_viem","sdk"]}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/components/Ad.tsx
|
|
2
2
|
import { SlotsChain } from "@0xslots/sdk";
|
|
3
|
-
import { useCallback, useMemo, useRef } from "react";
|
|
3
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef } from "react";
|
|
4
4
|
|
|
5
5
|
// src/fetch.ts
|
|
6
6
|
import { SlotsClient } from "@0xslots/sdk";
|
|
@@ -312,6 +312,60 @@ var adCardLabel = {
|
|
|
312
312
|
};
|
|
313
313
|
var adlandApiUrl = process.env.NODE_ENV === "development" ? "http://localhost:3069" : "https://api.adland.space";
|
|
314
314
|
|
|
315
|
+
// src/utils/tracking.ts
|
|
316
|
+
var UMAMI_URL = "https://umami.api.0xslots.org";
|
|
317
|
+
var WEBSITE_ID = "b02a0a73-70c1-44cd-b4ae-cd70fa3d632f";
|
|
318
|
+
var tracked = /* @__PURE__ */ new Set();
|
|
319
|
+
function sendEvent(eventName, data) {
|
|
320
|
+
if (typeof window === "undefined") return;
|
|
321
|
+
const url = window.location.href;
|
|
322
|
+
const referrer = document.referrer || void 0;
|
|
323
|
+
const hostname = window.location.hostname;
|
|
324
|
+
try {
|
|
325
|
+
fetch(`${UMAMI_URL}/api/send`, {
|
|
326
|
+
method: "POST",
|
|
327
|
+
headers: { "Content-Type": "application/json" },
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
type: "event",
|
|
330
|
+
payload: {
|
|
331
|
+
website: WEBSITE_ID,
|
|
332
|
+
url,
|
|
333
|
+
referrer,
|
|
334
|
+
hostname,
|
|
335
|
+
name: eventName,
|
|
336
|
+
data
|
|
337
|
+
}
|
|
338
|
+
}),
|
|
339
|
+
keepalive: true
|
|
340
|
+
}).catch(() => {
|
|
341
|
+
});
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function trackImpression(element, data) {
|
|
346
|
+
if (!element || typeof window === "undefined") return;
|
|
347
|
+
const key = `${data.slot}:${window.location.href}`;
|
|
348
|
+
if (tracked.has(key)) return;
|
|
349
|
+
const observer = new IntersectionObserver(
|
|
350
|
+
(entries) => {
|
|
351
|
+
for (const entry of entries) {
|
|
352
|
+
if (entry.isIntersecting && !tracked.has(key)) {
|
|
353
|
+
tracked.add(key);
|
|
354
|
+
sendEvent("impression", data);
|
|
355
|
+
observer.disconnect();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{ threshold: 0.5 }
|
|
360
|
+
// 50% visible
|
|
361
|
+
);
|
|
362
|
+
observer.observe(element);
|
|
363
|
+
return () => observer.disconnect();
|
|
364
|
+
}
|
|
365
|
+
function trackClick(eventName, data) {
|
|
366
|
+
sendEvent(eventName, data);
|
|
367
|
+
}
|
|
368
|
+
|
|
315
369
|
// src/components/Ad.tsx
|
|
316
370
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
317
371
|
function Ad({
|
|
@@ -337,21 +391,30 @@ function Ad({
|
|
|
337
391
|
async () => {
|
|
338
392
|
if (!client || !slot) throw new Error("NO_AD" /* NO_AD */);
|
|
339
393
|
const uri = await fetchMetadataURI(client, slot);
|
|
340
|
-
if (!uri)
|
|
394
|
+
if (!uri) {
|
|
395
|
+
console.info("[Ad] no metadata URI found for slot", slot);
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
341
398
|
return fetchAdFromURI(uri);
|
|
342
399
|
},
|
|
343
400
|
{ enabled: !!slot && !staticData }
|
|
344
401
|
);
|
|
345
402
|
const adData = staticData ?? fetchedData;
|
|
346
403
|
const isEmpty = !adData && !isLoading && (error instanceof Error ? error.message === "NO_AD" /* NO_AD */ : !error);
|
|
404
|
+
useEffect2(() => {
|
|
405
|
+
if (!adData || !slot) return;
|
|
406
|
+
return trackImpression(ref.current, { slot, chainId });
|
|
407
|
+
}, [adData, slot, chainId]);
|
|
347
408
|
const onClick = useCallback(
|
|
348
409
|
(e) => {
|
|
349
410
|
const target = e.target;
|
|
350
411
|
const isInteractive = target.tagName === "A" || target.tagName === "BUTTON" || target.closest("a") !== null || target.closest("button") !== null;
|
|
351
412
|
if (isInteractive) return;
|
|
352
413
|
if (adData) {
|
|
414
|
+
if (slot) trackClick("click", { slot, chainId });
|
|
353
415
|
performAdAction(adData);
|
|
354
416
|
} else if (isEmpty && slot) {
|
|
417
|
+
trackClick("click-empty", { slot, chainId });
|
|
355
418
|
performEmptyAdAction(slot, chainId, baseLinkUrl);
|
|
356
419
|
}
|
|
357
420
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Ad.tsx","../src/fetch.ts","../src/hooks/useAdContext.ts","../src/hooks/useFetch.ts","../src/utils/fetchCache.ts","../src/utils/ad-actions.ts","../src/utils/ad-fields.ts","../src/utils/constants.ts"],"sourcesContent":["import { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { useCallback, useMemo, useRef } from \"react\";\n\nimport { createReadClient, fetchAdFromURI, fetchMetadataURI } from \"../fetch\";\nimport { AdContext, useAd } from \"../hooks/useAdContext\";\nimport { useFetch } from \"../hooks/useFetch\";\nimport { AdDataQueryError, type AdProps } from \"../types\";\nimport { performAdAction, performEmptyAdAction } from \"../utils/ad-actions\";\nimport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"../utils/ad-fields\";\nimport { adCardIcon, adCardLabel } from \"../utils/constants\";\n\n// ─── Root component ──────────────────────────────────────────────────────────\n\n/**\n * Root Ad component — compound pattern.\n *\n * @example\n * ```tsx\n * <Ad slot=\"0xabc...123\" className=\"rounded-md border p-3\">\n * <AdImage className=\"size-10 rounded-md\" />\n * <AdTitle className=\"text-sm font-medium\" />\n * <AdDescription className=\"text-xs text-muted-foreground\" />\n * <AdBadge />\n * </Ad>\n * ```\n */\nexport function Ad({\n slot,\n data: staticData,\n chainId = SlotsChain.BASE,\n rpcUrl,\n baseLinkUrl = \"https://app.0xslots.org\",\n children,\n ...props\n}: AdProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n const client = useMemo(\n () => (slot ? createReadClient(chainId, rpcUrl) : null),\n [slot, chainId, rpcUrl],\n );\n\n const {\n data: fetchedData,\n isLoading,\n error,\n } = useFetch<AdData>(\n `ad-data-${slot}`,\n async () => {\n if (!client || !slot) throw new Error(AdDataQueryError.NO_AD);\n const uri = await fetchMetadataURI(client, slot);\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n return fetchAdFromURI(uri);\n },\n { enabled: !!slot && !staticData },\n );\n\n const adData = staticData ?? fetchedData;\n\n const isEmpty =\n !adData &&\n !isLoading &&\n (error instanceof Error\n ? error.message === AdDataQueryError.NO_AD\n : !error);\n\n const onClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n const isInteractive =\n target.tagName === \"A\" ||\n target.tagName === \"BUTTON\" ||\n target.closest(\"a\") !== null ||\n target.closest(\"button\") !== null;\n if (isInteractive) return;\n\n if (adData) {\n performAdAction(adData);\n } else if (isEmpty && slot) {\n performEmptyAdAction(slot, chainId, baseLinkUrl);\n }\n },\n [adData, isEmpty, slot, chainId, baseLinkUrl],\n );\n\n return (\n <AdContext.Provider\n value={{\n data: adData ?? null,\n isLoading: !!slot && !staticData && isLoading,\n error,\n isEmpty,\n slot,\n baseLinkUrl,\n chainId,\n }}\n >\n <div ref={ref} onClick={onClick} {...props}>\n {children}\n </div>\n </AdContext.Provider>\n );\n}\n\n// ─── Sub-components ──────────────────────────────────────────────────────────\n\nexport interface AdImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdImage({ fallback, ...props }: AdImageProps) {\n const { data } = useAd();\n const src = getAdImage(data);\n if (!src) return fallback ? <>{fallback}</> : null;\n return <img src={src} alt=\"\" {...props} />;\n}\n\nexport interface AdTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdTitle({ fallback, children, ...props }: AdTitleProps) {\n const { data } = useAd();\n const title = getAdTitle(data);\n if (!title) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? title}</p>;\n}\n\nexport interface AdDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdDescription({ fallback, children, ...props }: AdDescriptionProps) {\n const { data } = useAd();\n const description = getAdDescription(data);\n if (!description) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? description}</p>;\n}\n\nexport interface AdBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdBadge({ children, ...props }: AdBadgeProps) {\n const { data } = useAd();\n const type = getAdType(data);\n if (!type) return null;\n const Icon = adCardIcon[type];\n const label = adCardLabel[type];\n return (\n <span {...props}>\n {children ?? (\n <>\n {Icon && <Icon className=\"size-3\" />}\n {label}\n </>\n )}\n </span>\n );\n}\n\nexport interface AdLabelProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdLabel({ children, ...props }: AdLabelProps) {\n return <span {...props}>{children ?? \"AD\"}</span>;\n}\n\n// ─── State components ────────────────────────────────────────────────────────\n\nexport interface AdStatusProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode;\n}\n\nexport function AdLoading({ children, ...props }: AdStatusProps) {\n const { isLoading } = useAd();\n if (!isLoading) return null;\n return <div {...props}>{children ?? \"Loading...\"}</div>;\n}\n\nexport function AdEmpty({ children, ...props }: AdStatusProps) {\n const { isEmpty } = useAd();\n if (!isEmpty) return null;\n return <div {...props}>{children ?? \"Your ad here\"}</div>;\n}\n\nexport function AdError({ children, ...props }: AdStatusProps) {\n const { error, isEmpty } = useAd();\n if (!error || isEmpty) return null;\n return <div {...props}>{children ?? \"Error loading ad\"}</div>;\n}\n\nexport function AdLoaded({ children, ...props }: AdStatusProps) {\n const { data } = useAd();\n if (!data) return null;\n return <div {...props}>{children}</div>;\n}\n","import { SlotsClient, type SlotsChain } from \"@0xslots/sdk\";\nimport { type Address, createPublicClient, http } from \"viem\";\nimport { base, baseSepolia } from \"viem/chains\";\n\nimport { AdDataQueryError } from \"./types\";\n\nconst IPFS_GATEWAY = \"https://amethyst-representative-mandrill-369.mypinata.cloud/ipfs/\";\n\nconst viemChains: Record<number, typeof base> = {\n 8453: base,\n 84532: baseSepolia,\n};\n\n/**\n * Create a read-only SlotsClient for a given chain.\n */\nexport function createReadClient(\n chainId: SlotsChain,\n rpcUrl?: string,\n): SlotsClient {\n const chain = viemChains[chainId];\n if (!chain) throw new Error(`Unsupported chain: ${chainId}`);\n\n const publicClient = createPublicClient({\n chain,\n transport: http(rpcUrl),\n });\n\n return new SlotsClient({ chainId, publicClient });\n}\n\n/**\n * Fetch ad content from a metadata URI (IPFS or HTTP)\n */\nexport const fetchAdFromURI = async (uri: string) => {\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n\n const url = uri.startsWith(\"ipfs://\")\n ? `${IPFS_GATEWAY}${uri.slice(7)}`\n : uri;\n\n const res = await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!res.ok) {\n if (res.status === 404) throw new Error(AdDataQueryError.NO_AD);\n throw new Error(AdDataQueryError.ERROR);\n }\n\n const data = await res.json();\n if (data.error) throw new Error(data.error);\n\n return data;\n};\n\n/**\n * Fetch the metadata URI for a slot using the SDK.\n */\nexport const fetchMetadataURI = async (\n client: SlotsClient,\n slotAddress: string,\n): Promise<string> => {\n const info = await client.getSlotInfo(slotAddress as Address);\n const moduleAddress = (info as { module: Address }).module;\n\n if (\n !moduleAddress ||\n moduleAddress === \"0x0000000000000000000000000000000000000000\"\n ) {\n return \"\";\n }\n\n return client.modules.metadata.getURI(\n moduleAddress,\n slotAddress as Address,\n );\n};\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { createContext, useContext } from \"react\";\n\nexport interface AdContextValue {\n data: AdData | null;\n isLoading: boolean;\n error: unknown;\n isEmpty: boolean;\n slot?: string;\n baseLinkUrl: string;\n chainId: SlotsChain;\n}\n\nexport const AdContext = createContext<AdContextValue | null>(null);\n\nexport function useAd(): AdContextValue {\n const ctx = useContext(AdContext);\n if (!ctx) throw new Error(\"useAd must be used within an <Ad> component\");\n return ctx;\n}\n","import { useEffect, useState } from \"react\";\nimport FetchCache from \"../utils/fetchCache\";\n\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nconst globalCache = new FetchCache();\n\nexport const fetchCache = {\n clear: () => globalCache.clear(),\n};\n\nexport function useFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n opts?: {\n enabled?: boolean;\n ttl?: number; // ms\n },\n) {\n const { enabled = true, ttl = 0 } = opts ?? {};\n\n // Get cached data from global cache\n const getCachedData = (): T | null => {\n return globalCache.get<T>(key, ttl || undefined);\n };\n\n const hasCachedData = () => {\n return globalCache.has(key, ttl || undefined);\n };\n\n // Check if there's an active fetch for this key (dedupe concurrent requests)\n const getActiveFetch = (): Promise<T> | undefined => {\n return globalCache.getActiveFetch<T>(key);\n };\n\n const cachedData = getCachedData();\n const [data, setData] = useState<T | null>(cachedData);\n const [error, setError] = useState<unknown>(null);\n // If we have cached data, start with success status to avoid showing loader\n const [status, setStatus] = useState<Status>(cachedData ? \"success\" : \"idle\");\n\n const refetch = async () => {\n // ALWAYS check cache first - never show loading if we have valid cached data\n const cached = getCachedData();\n if (cached) {\n // Already have valid cached data, ensure state is correct\n setData(cached);\n setStatus(\"success\");\n return cached;\n }\n\n // Check if there's already an active fetch for this key (dedupe)\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n try {\n const res = await activeFetch;\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n }\n\n // Only set loading if we actually need to fetch\n setStatus(\"loading\");\n setError(null);\n\n try {\n // Create fetch promise and store it for deduplication\n const fetchPromise = fetcher();\n globalCache.setActiveFetch(key, fetchPromise);\n\n const res = await fetchPromise;\n globalCache.set(key, res);\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n const cached = getCachedData();\n\n if (cached) {\n setData(cached);\n setStatus(\"success\");\n return;\n }\n\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n setStatus(\"loading\");\n activeFetch\n .then((res) => {\n setData(res);\n setStatus(\"success\");\n })\n .catch((e) => {\n setError(e);\n setStatus(\"error\");\n });\n return;\n }\n\n if (status !== \"loading\" && status !== \"success\") {\n refetch();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, enabled]);\n\n const hasValidCache = hasCachedData();\n const isLoading = status === \"loading\" && !hasValidCache;\n\n return {\n data,\n error,\n status,\n isIdle: status === \"idle\",\n isLoading,\n isSuccess: status === \"success\",\n isError: status === \"error\",\n refetch,\n };\n}\n","class FetchCache {\n private cache = new Map<string, { data: unknown; ts: number }>();\n private activeFetches = new Map<string, Promise<unknown>>();\n\n get<T>(key: string, ttl?: number): T | null {\n const cached = this.cache.get(key);\n if (!cached) return null;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n // Cache expired, remove it\n this.cache.delete(key);\n return null;\n }\n\n return cached.data as T;\n }\n\n has(key: string, ttl?: number): boolean {\n const cached = this.cache.get(key);\n if (!cached) return false;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n set<T>(key: string, data: T): void {\n this.cache.set(key, { data, ts: Date.now() });\n }\n\n getActiveFetch<T>(key: string): Promise<T> | undefined {\n return this.activeFetches.get(key) as Promise<T> | undefined;\n }\n\n setActiveFetch<T>(key: string, promise: Promise<T>): void {\n this.activeFetches.set(key, promise);\n promise.finally(() => {\n this.activeFetches.delete(key);\n });\n }\n\n clear(): void {\n this.cache.clear();\n this.activeFetches.clear();\n }\n}\n\nexport default FetchCache;\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport sdk from \"@farcaster/miniapp-sdk\";\nimport { getAddress } from \"viem\";\n\n// Cache the miniapp check at module level — resolved once, sync thereafter\nlet _isMiniApp: boolean | null = null;\nconst _miniAppPromise = sdk\n .isInMiniApp()\n .then((v) => {\n _isMiniApp = v;\n return v;\n })\n .catch(() => {\n _isMiniApp = false;\n return false;\n });\n\nasync function isMiniApp(): Promise<boolean> {\n if (_isMiniApp !== null) return _isMiniApp;\n return _miniAppPromise;\n}\n\nexport function performAdAction(adData: AdData) {\n try {\n switch (adData.type) {\n case \"link\":\n sdk.actions.openUrl(adData.data.url);\n break;\n case \"cast\":\n sdk.actions.viewCast({ hash: adData.data.hash });\n break;\n case \"miniapp\":\n sdk.actions.openMiniApp({ url: adData.data.url });\n break;\n case \"token\": {\n const address = adData.data.address;\n const chainId = adData.data.chainId;\n const buyToken = `eip155:${chainId}/erc20:${getAddress(address)}`;\n sdk.actions.swapToken({ buyToken });\n break;\n }\n case \"farcasterProfile\":\n sdk.actions.viewProfile({\n fid: Number.parseInt(adData.data.fid, 10),\n });\n break;\n }\n } catch (err) {\n // Fallback for web (non-miniapp) context\n if (adData.type === \"link\" || adData.type === \"miniapp\") {\n window.open(adData.data.url, \"_blank\");\n } else {\n console.error(\"[@adland/react] Failed to perform ad action:\", err);\n }\n }\n}\n\nexport async function performEmptyAdAction(\n slot: string,\n chainId: SlotsChain,\n baseLinkUrl: string,\n) {\n const url = `${baseLinkUrl}/slots/${slot}?chain=${chainId}`;\n if (await isMiniApp()) {\n sdk.actions.openMiniApp({ url });\n } else {\n window.open(url, \"_blank\");\n }\n}\n","import type { AdData, AdType } from \"@adland/data\";\n\nconst IMAGE_KEYS = [\"image\", \"icon\", \"pfpUrl\", \"logoURI\", \"imageUrl\"] as const;\nconst TITLE_KEYS = [\"title\", \"displayName\", \"username\", \"name\", \"symbol\"] as const;\nconst DESC_KEYS = [\"description\", \"bio\", \"text\", \"name\"] as const;\n\nfunction flatFields(data: AdData): Record<string, unknown> {\n return { ...data.data, ...(data.metadata ?? {}) };\n}\n\nexport function getAdImage(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of IMAGE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdTitle(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of TITLE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdDescription(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n const title = getAdTitle(data);\n for (const key of DESC_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v && v !== title) return v;\n }\n return null;\n}\n\nexport function getAdType(data: AdData | null): AdType | null {\n if (!data) return null;\n return data.type as AdType;\n}\n","import { AdType } from \"@adland/data\";\nimport { ForwardRefExoticComponent, RefAttributes } from \"react\";\nimport {\n Link,\n MessageCircle,\n LayoutGrid,\n LucideProps,\n Coins,\n User,\n} from \"lucide-react\";\n\nexport const adCardIcon: Record<\n AdType,\n ForwardRefExoticComponent<\n Omit<LucideProps, \"ref\"> & RefAttributes<SVGSVGElement>\n >\n> = {\n link: Link,\n cast: MessageCircle,\n miniapp: LayoutGrid,\n token: Coins,\n farcasterProfile: User,\n};\n\nexport const adCardLabel: Record<AdType, string> = {\n link: \"Link\",\n cast: \"Cast\",\n miniapp: \"Miniapp\",\n token: \"Token\",\n farcasterProfile: \"Profile\",\n};\n\nexport const adlandApiUrl =\n process.env.NODE_ENV === \"development\"\n ? \"http://localhost:3069\"\n : \"https://api.adland.space\";\n"],"mappings":";AAAA,SAAS,kBAAkB;AAE3B,SAAS,aAAa,SAAS,cAAc;;;ACF7C,SAAS,mBAAoC;AAC7C,SAAuB,oBAAoB,YAAY;AACvD,SAAS,MAAM,mBAAmB;AAIlC,IAAM,eAAe;AAErB,IAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AACT;AAKO,SAAS,iBACd,SACA,QACa;AACb,QAAM,QAAQ,WAAW,OAAO;AAChC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,OAAO,EAAE;AAE3D,QAAM,eAAe,mBAAmB;AAAA,IACtC;AAAA,IACA,WAAW,KAAK,MAAM;AAAA,EACxB,CAAC;AAED,SAAO,IAAI,YAAY,EAAE,SAAS,aAAa,CAAC;AAClD;AAKO,IAAM,iBAAiB,OAAO,QAAgB;AACnD,MAAI,CAAC,IAAK,OAAM,IAAI,yBAA4B;AAEhD,QAAM,MAAM,IAAI,WAAW,SAAS,IAChC,GAAG,YAAY,GAAG,IAAI,MAAM,CAAC,CAAC,KAC9B;AAEJ,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS,EAAE,QAAQ,mBAAmB;AAAA,EACxC,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,IAAK,OAAM,IAAI,yBAA4B;AAC9D,UAAM,IAAI,yBAA4B;AAAA,EACxC;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KAAK,MAAO,OAAM,IAAI,MAAM,KAAK,KAAK;AAE1C,SAAO;AACT;AAKO,IAAM,mBAAmB,OAC9B,QACA,gBACoB;AACpB,QAAM,OAAO,MAAM,OAAO,YAAY,WAAsB;AAC5D,QAAM,gBAAiB,KAA6B;AAEpD,MACE,CAAC,iBACD,kBAAkB,8CAClB;AACA,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,QAAQ,SAAS;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AACF;;;AC5EA,SAAS,eAAe,kBAAkB;AAYnC,IAAM,YAAY,cAAqC,IAAI;AAE3D,SAAS,QAAwB;AACtC,QAAM,MAAM,WAAW,SAAS;AAChC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,6CAA6C;AACvE,SAAO;AACT;;;ACpBA,SAAS,WAAW,gBAAgB;;;ACApC,IAAM,aAAN,MAAiB;AAAA,EACP,QAAQ,oBAAI,IAA2C;AAAA,EACvD,gBAAgB,oBAAI,IAA8B;AAAA,EAE1D,IAAO,KAAa,KAAwB;AAC1C,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK;AAEvC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,IAAI,KAAa,KAAuB;AACtC,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK;AACvC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,IAAO,KAAa,MAAe;AACjC,SAAK,MAAM,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,CAAC;AAAA,EAC9C;AAAA,EAEA,eAAkB,KAAqC;AACrD,WAAO,KAAK,cAAc,IAAI,GAAG;AAAA,EACnC;AAAA,EAEA,eAAkB,KAAa,SAA2B;AACxD,SAAK,cAAc,IAAI,KAAK,OAAO;AACnC,YAAQ,QAAQ,MAAM;AACpB,WAAK,cAAc,OAAO,GAAG;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AACjB,SAAK,cAAc,MAAM;AAAA,EAC3B;AACF;AAEA,IAAO,qBAAQ;;;AD7Cf,IAAM,cAAc,IAAI,mBAAW;AAM5B,SAAS,SACd,KACA,SACA,MAIA;AACA,QAAM,EAAE,UAAU,MAAM,MAAM,EAAE,IAAI,QAAQ,CAAC;AAG7C,QAAM,gBAAgB,MAAgB;AACpC,WAAO,YAAY,IAAO,KAAK,OAAO,MAAS;AAAA,EACjD;AAEA,QAAM,gBAAgB,MAAM;AAC1B,WAAO,YAAY,IAAI,KAAK,OAAO,MAAS;AAAA,EAC9C;AAGA,QAAM,iBAAiB,MAA8B;AACnD,WAAO,YAAY,eAAkB,GAAG;AAAA,EAC1C;AAEA,QAAM,aAAa,cAAc;AACjC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmB,UAAU;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkB,IAAI;AAEhD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAiB,aAAa,YAAY,MAAM;AAE5E,QAAM,UAAU,YAAY;AAE1B,UAAM,SAAS,cAAc;AAC7B,QAAI,QAAQ;AAEV,cAAQ,MAAM;AACd,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,eAAe;AACnC,QAAI,aAAa;AACf,UAAI;AACF,cAAM,MAAM,MAAM;AAClB,gBAAQ,GAAG;AACX,kBAAU,SAAS;AACnB,eAAO;AAAA,MACT,SAAS,GAAG;AACV,iBAAS,CAAC;AACV,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAAA,IACF;AAGA,cAAU,SAAS;AACnB,aAAS,IAAI;AAEb,QAAI;AAEF,YAAM,eAAe,QAAQ;AAC7B,kBAAY,eAAe,KAAK,YAAY;AAE5C,YAAM,MAAM,MAAM;AAClB,kBAAY,IAAI,KAAK,GAAG;AACxB,cAAQ,GAAG;AACX,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT,SAAS,GAAG;AACV,eAAS,CAAC;AACV,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,UAAM,SAAS,cAAc;AAE7B,QAAI,QAAQ;AACV,cAAQ,MAAM;AACd,gBAAU,SAAS;AACnB;AAAA,IACF;AAEA,UAAM,cAAc,eAAe;AACnC,QAAI,aAAa;AACf,gBAAU,SAAS;AACnB,kBACG,KAAK,CAAC,QAAQ;AACb,gBAAQ,GAAG;AACX,kBAAU,SAAS;AAAA,MACrB,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,iBAAS,CAAC;AACV,kBAAU,OAAO;AAAA,MACnB,CAAC;AACH;AAAA,IACF;AAEA,QAAI,WAAW,aAAa,WAAW,WAAW;AAChD,cAAQ;AAAA,IACV;AAAA,EAEF,GAAG,CAAC,KAAK,OAAO,CAAC;AAEjB,QAAM,gBAAgB,cAAc;AACpC,QAAM,YAAY,WAAW,aAAa,CAAC;AAE3C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB;AAAA,IACA,WAAW,WAAW;AAAA,IACtB,SAAS,WAAW;AAAA,IACpB;AAAA,EACF;AACF;;;AElIA,OAAO,SAAS;AAChB,SAAS,kBAAkB;AAG3B,IAAI,aAA6B;AACjC,IAAM,kBAAkB,IACrB,YAAY,EACZ,KAAK,CAAC,MAAM;AACX,eAAa;AACb,SAAO;AACT,CAAC,EACA,MAAM,MAAM;AACX,eAAa;AACb,SAAO;AACT,CAAC;AAEH,eAAe,YAA8B;AAC3C,MAAI,eAAe,KAAM,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,gBAAgB,QAAgB;AAC9C,MAAI;AACF,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,YAAI,QAAQ,QAAQ,OAAO,KAAK,GAAG;AACnC;AAAA,MACF,KAAK;AACH,YAAI,QAAQ,SAAS,EAAE,MAAM,OAAO,KAAK,KAAK,CAAC;AAC/C;AAAA,MACF,KAAK;AACH,YAAI,QAAQ,YAAY,EAAE,KAAK,OAAO,KAAK,IAAI,CAAC;AAChD;AAAA,MACF,KAAK,SAAS;AACZ,cAAM,UAAU,OAAO,KAAK;AAC5B,cAAM,UAAU,OAAO,KAAK;AAC5B,cAAM,WAAW,UAAU,OAAO,UAAU,WAAW,OAAO,CAAC;AAC/D,YAAI,QAAQ,UAAU,EAAE,SAAS,CAAC;AAClC;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,QAAQ,YAAY;AAAA,UACtB,KAAK,OAAO,SAAS,OAAO,KAAK,KAAK,EAAE;AAAA,QAC1C,CAAC;AACD;AAAA,IACJ;AAAA,EACF,SAAS,KAAK;AAEZ,QAAI,OAAO,SAAS,UAAU,OAAO,SAAS,WAAW;AACvD,aAAO,KAAK,OAAO,KAAK,KAAK,QAAQ;AAAA,IACvC,OAAO;AACL,cAAQ,MAAM,gDAAgD,GAAG;AAAA,IACnE;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,MACA,SACA,aACA;AACA,QAAM,MAAM,GAAG,WAAW,UAAU,IAAI,UAAU,OAAO;AACzD,MAAI,MAAM,UAAU,GAAG;AACrB,QAAI,QAAQ,YAAY,EAAE,IAAI,CAAC;AAAA,EACjC,OAAO;AACL,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC3B;AACF;;;ACnEA,IAAM,aAAa,CAAC,SAAS,QAAQ,UAAU,WAAW,UAAU;AACpE,IAAM,aAAa,CAAC,SAAS,eAAe,YAAY,QAAQ,QAAQ;AACxE,IAAM,YAAY,CAAC,eAAe,OAAO,QAAQ,MAAM;AAEvD,SAAS,WAAW,MAAuC;AACzD,SAAO,EAAE,GAAG,KAAK,MAAM,GAAI,KAAK,YAAY,CAAC,EAAG;AAClD;AAEO,SAAS,WAAW,MAAoC;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,aAAW,OAAO,YAAY;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEO,SAAS,WAAW,MAAoC;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,aAAW,OAAO,YAAY;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAoC;AACnE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,QAAM,QAAQ,WAAW,IAAI;AAC7B,aAAW,OAAO,WAAW;AAC3B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,KAAK,MAAM,MAAO,QAAO;AAAA,EACxD;AACA,SAAO;AACT;AAEO,SAAS,UAAU,MAAoC;AAC5D,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK;AACd;;;AC1CA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AAEA,IAAM,aAKT;AAAA,EACF,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,kBAAkB;AACpB;AAEO,IAAM,cAAsC;AAAA,EACjD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,kBAAkB;AACpB;AAEO,IAAM,eACX,QAAQ,IAAI,aAAa,gBACrB,0BACA;;;AP+DA,SAgBwB,UAhBxB,KAqDE,YArDF;AAvEC,SAAS,GAAG;AAAA,EACjB;AAAA,EACA,MAAM;AAAA,EACN,UAAU,WAAW;AAAA,EACrB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAY;AACV,QAAM,MAAM,OAAuB,IAAI;AAEvC,QAAM,SAAS;AAAA,IACb,MAAO,OAAO,iBAAiB,SAAS,MAAM,IAAI;AAAA,IAClD,CAAC,MAAM,SAAS,MAAM;AAAA,EACxB;AAEA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF,WAAW,IAAI;AAAA,IACf,YAAY;AACV,UAAI,CAAC,UAAU,CAAC,KAAM,OAAM,IAAI,yBAA4B;AAC5D,YAAM,MAAM,MAAM,iBAAiB,QAAQ,IAAI;AAC/C,UAAI,CAAC,IAAK,OAAM,IAAI,yBAA4B;AAChD,aAAO,eAAe,GAAG;AAAA,IAC3B;AAAA,IACA,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,WAAW;AAAA,EACnC;AAEA,QAAM,SAAS,cAAc;AAE7B,QAAM,UACJ,CAAC,UACD,CAAC,cACA,iBAAiB,QACd,MAAM,kCACN,CAAC;AAEP,QAAM,UAAU;AAAA,IACd,CAAC,MAAwC;AACvC,YAAM,SAAS,EAAE;AACjB,YAAM,gBACJ,OAAO,YAAY,OACnB,OAAO,YAAY,YACnB,OAAO,QAAQ,GAAG,MAAM,QACxB,OAAO,QAAQ,QAAQ,MAAM;AAC/B,UAAI,cAAe;AAEnB,UAAI,QAAQ;AACV,wBAAgB,MAAM;AAAA,MACxB,WAAW,WAAW,MAAM;AAC1B,6BAAqB,MAAM,SAAS,WAAW;AAAA,MACjD;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,SAAS,MAAM,SAAS,WAAW;AAAA,EAC9C;AAEA,SACE;AAAA,IAAC,UAAU;AAAA,IAAV;AAAA,MACC,OAAO;AAAA,QACL,MAAM,UAAU;AAAA,QAChB,WAAW,CAAC,CAAC,QAAQ,CAAC,cAAc;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEA,8BAAC,SAAI,KAAU,SAAmB,GAAG,OAClC,UACH;AAAA;AAAA,EACF;AAEJ;AAQO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,MAAM,WAAW,IAAI;AAC3B,MAAI,CAAC,IAAK,QAAO,WAAW,gCAAG,oBAAS,IAAM;AAC9C,SAAO,oBAAC,SAAI,KAAU,KAAI,IAAI,GAAG,OAAO;AAC1C;AAMO,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAG,MAAM,GAAiB;AACtE,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,QAAQ,WAAW,IAAI;AAC7B,MAAI,CAAC,MAAO,QAAO,WAAW,gCAAG,oBAAS,IAAM;AAChD,SAAO,oBAAC,OAAG,GAAG,OAAQ,sBAAY,OAAM;AAC1C;AAMO,SAAS,cAAc,EAAE,UAAU,UAAU,GAAG,MAAM,GAAuB;AAClF,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,cAAc,iBAAiB,IAAI;AACzC,MAAI,CAAC,YAAa,QAAO,WAAW,gCAAG,oBAAS,IAAM;AACtD,SAAO,oBAAC,OAAG,GAAG,OAAQ,sBAAY,aAAY;AAChD;AAIO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,OAAO,UAAU,IAAI;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,WAAW,IAAI;AAC5B,QAAM,QAAQ,YAAY,IAAI;AAC9B,SACE,oBAAC,UAAM,GAAG,OACP,sBACC,iCACG;AAAA,YAAQ,oBAAC,QAAK,WAAU,UAAS;AAAA,IACjC;AAAA,KACH,GAEJ;AAEJ;AAIO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,SAAO,oBAAC,UAAM,GAAG,OAAQ,sBAAY,MAAK;AAC5C;AAQO,SAAS,UAAU,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC/D,QAAM,EAAE,UAAU,IAAI,MAAM;AAC5B,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,cAAa;AACnD;AAEO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC7D,QAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,gBAAe;AACrD;AAEO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC7D,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAM;AACjC,MAAI,CAAC,SAAS,QAAS,QAAO;AAC9B,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,oBAAmB;AACzD;AAEO,SAAS,SAAS,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC9D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,oBAAC,SAAK,GAAG,OAAQ,UAAS;AACnC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/components/Ad.tsx","../src/fetch.ts","../src/hooks/useAdContext.ts","../src/hooks/useFetch.ts","../src/utils/fetchCache.ts","../src/utils/ad-actions.ts","../src/utils/ad-fields.ts","../src/utils/constants.ts","../src/utils/tracking.ts"],"sourcesContent":["import { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nimport { createReadClient, fetchAdFromURI, fetchMetadataURI } from \"../fetch\";\nimport { AdContext, useAd } from \"../hooks/useAdContext\";\nimport { useFetch } from \"../hooks/useFetch\";\nimport { AdDataQueryError, type AdProps } from \"../types\";\nimport { performAdAction, performEmptyAdAction } from \"../utils/ad-actions\";\nimport { getAdDescription, getAdImage, getAdTitle, getAdType } from \"../utils/ad-fields\";\nimport { adCardIcon, adCardLabel } from \"../utils/constants\";\nimport { trackImpression, trackClick } from \"../utils/tracking\";\n\n// ─── Root component ──────────────────────────────────────────────────────────\n\n/**\n * Root Ad component — compound pattern.\n *\n * @example\n * ```tsx\n * <Ad slot=\"0xabc...123\" className=\"rounded-md border p-3\">\n * <AdImage className=\"size-10 rounded-md\" />\n * <AdTitle className=\"text-sm font-medium\" />\n * <AdDescription className=\"text-xs text-muted-foreground\" />\n * <AdBadge />\n * </Ad>\n * ```\n */\nexport function Ad({\n slot,\n data: staticData,\n chainId = SlotsChain.BASE,\n rpcUrl,\n baseLinkUrl = \"https://app.0xslots.org\",\n children,\n ...props\n}: AdProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n const client = useMemo(\n () => (slot ? createReadClient(chainId, rpcUrl) : null),\n [slot, chainId, rpcUrl],\n );\n\n const {\n data: fetchedData,\n isLoading,\n error,\n } = useFetch<AdData>(\n `ad-data-${slot}`,\n async () => {\n if (!client || !slot) throw new Error(AdDataQueryError.NO_AD);\n const uri = await fetchMetadataURI(client, slot);\n if (!uri) {\n console.info(\"[Ad] no metadata URI found for slot\", slot);\n return null;\n }\n return fetchAdFromURI(uri);\n },\n { enabled: !!slot && !staticData },\n );\n\n const adData = staticData ?? fetchedData;\n\n const isEmpty =\n !adData &&\n !isLoading &&\n (error instanceof Error\n ? error.message === AdDataQueryError.NO_AD\n : !error);\n\n // Track impression when ad is visible in viewport (once per slot per page load)\n useEffect(() => {\n if (!adData || !slot) return;\n return trackImpression(ref.current, { slot, chainId });\n }, [adData, slot, chainId]);\n\n const onClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n const isInteractive =\n target.tagName === \"A\" ||\n target.tagName === \"BUTTON\" ||\n target.closest(\"a\") !== null ||\n target.closest(\"button\") !== null;\n if (isInteractive) return;\n\n if (adData) {\n if (slot) trackClick(\"click\", { slot, chainId });\n performAdAction(adData);\n } else if (isEmpty && slot) {\n trackClick(\"click-empty\", { slot, chainId });\n performEmptyAdAction(slot, chainId, baseLinkUrl);\n }\n },\n [adData, isEmpty, slot, chainId, baseLinkUrl],\n );\n\n return (\n <AdContext.Provider\n value={{\n data: adData ?? null,\n isLoading: !!slot && !staticData && isLoading,\n error,\n isEmpty,\n slot,\n baseLinkUrl,\n chainId,\n }}\n >\n <div ref={ref} onClick={onClick} {...props}>\n {children}\n </div>\n </AdContext.Provider>\n );\n}\n\n// ─── Sub-components ──────────────────────────────────────────────────────────\n\nexport interface AdImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdImage({ fallback, ...props }: AdImageProps) {\n const { data } = useAd();\n const src = getAdImage(data);\n if (!src) return fallback ? <>{fallback}</> : null;\n return <img src={src} alt=\"\" {...props} />;\n}\n\nexport interface AdTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdTitle({ fallback, children, ...props }: AdTitleProps) {\n const { data } = useAd();\n const title = getAdTitle(data);\n if (!title) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? title}</p>;\n}\n\nexport interface AdDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {\n fallback?: React.ReactNode;\n}\n\nexport function AdDescription({ fallback, children, ...props }: AdDescriptionProps) {\n const { data } = useAd();\n const description = getAdDescription(data);\n if (!description) return fallback ? <>{fallback}</> : null;\n return <p {...props}>{children ?? description}</p>;\n}\n\nexport interface AdBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdBadge({ children, ...props }: AdBadgeProps) {\n const { data } = useAd();\n const type = getAdType(data);\n if (!type) return null;\n const Icon = adCardIcon[type];\n const label = adCardLabel[type];\n return (\n <span {...props}>\n {children ?? (\n <>\n {Icon && <Icon className=\"size-3\" />}\n {label}\n </>\n )}\n </span>\n );\n}\n\nexport interface AdLabelProps extends React.HTMLAttributes<HTMLSpanElement> {}\n\nexport function AdLabel({ children, ...props }: AdLabelProps) {\n return <span {...props}>{children ?? \"AD\"}</span>;\n}\n\n// ─── State components ────────────────────────────────────────────────────────\n\nexport interface AdStatusProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode;\n}\n\nexport function AdLoading({ children, ...props }: AdStatusProps) {\n const { isLoading } = useAd();\n if (!isLoading) return null;\n return <div {...props}>{children ?? \"Loading...\"}</div>;\n}\n\nexport function AdEmpty({ children, ...props }: AdStatusProps) {\n const { isEmpty } = useAd();\n if (!isEmpty) return null;\n return <div {...props}>{children ?? \"Your ad here\"}</div>;\n}\n\nexport function AdError({ children, ...props }: AdStatusProps) {\n const { error, isEmpty } = useAd();\n if (!error || isEmpty) return null;\n return <div {...props}>{children ?? \"Error loading ad\"}</div>;\n}\n\nexport function AdLoaded({ children, ...props }: AdStatusProps) {\n const { data } = useAd();\n if (!data) return null;\n return <div {...props}>{children}</div>;\n}\n","import { SlotsClient, type SlotsChain } from \"@0xslots/sdk\";\nimport { type Address, createPublicClient, http } from \"viem\";\nimport { base, baseSepolia } from \"viem/chains\";\n\nimport { AdDataQueryError } from \"./types\";\n\nconst IPFS_GATEWAY = \"https://amethyst-representative-mandrill-369.mypinata.cloud/ipfs/\";\n\nconst viemChains: Record<number, typeof base> = {\n 8453: base,\n 84532: baseSepolia,\n};\n\n/**\n * Create a read-only SlotsClient for a given chain.\n */\nexport function createReadClient(\n chainId: SlotsChain,\n rpcUrl?: string,\n): SlotsClient {\n const chain = viemChains[chainId];\n if (!chain) throw new Error(`Unsupported chain: ${chainId}`);\n\n const publicClient = createPublicClient({\n chain,\n transport: http(rpcUrl),\n });\n\n return new SlotsClient({ chainId, publicClient });\n}\n\n/**\n * Fetch ad content from a metadata URI (IPFS or HTTP)\n */\nexport const fetchAdFromURI = async (uri: string) => {\n if (!uri) throw new Error(AdDataQueryError.NO_AD);\n\n const url = uri.startsWith(\"ipfs://\")\n ? `${IPFS_GATEWAY}${uri.slice(7)}`\n : uri;\n\n const res = await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!res.ok) {\n if (res.status === 404) throw new Error(AdDataQueryError.NO_AD);\n throw new Error(AdDataQueryError.ERROR);\n }\n\n const data = await res.json();\n if (data.error) throw new Error(data.error);\n\n return data;\n};\n\n/**\n * Fetch the metadata URI for a slot using the SDK.\n */\nexport const fetchMetadataURI = async (\n client: SlotsClient,\n slotAddress: string,\n): Promise<string> => {\n const info = await client.getSlotInfo(slotAddress as Address);\n const moduleAddress = (info as { module: Address }).module;\n\n if (\n !moduleAddress ||\n moduleAddress === \"0x0000000000000000000000000000000000000000\"\n ) {\n return \"\";\n }\n\n return client.modules.metadata.getURI(\n moduleAddress,\n slotAddress as Address,\n );\n};\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport { createContext, useContext } from \"react\";\n\nexport interface AdContextValue {\n data: AdData | null;\n isLoading: boolean;\n error: unknown;\n isEmpty: boolean;\n slot?: string;\n baseLinkUrl: string;\n chainId: SlotsChain;\n}\n\nexport const AdContext = createContext<AdContextValue | null>(null);\n\nexport function useAd(): AdContextValue {\n const ctx = useContext(AdContext);\n if (!ctx) throw new Error(\"useAd must be used within an <Ad> component\");\n return ctx;\n}\n","import { useEffect, useState } from \"react\";\nimport FetchCache from \"../utils/fetchCache\";\n\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nconst globalCache = new FetchCache();\n\nexport const fetchCache = {\n clear: () => globalCache.clear(),\n};\n\nexport function useFetch<T>(\n key: string,\n fetcher: () => Promise<T>,\n opts?: {\n enabled?: boolean;\n ttl?: number; // ms\n },\n) {\n const { enabled = true, ttl = 0 } = opts ?? {};\n\n // Get cached data from global cache\n const getCachedData = (): T | null => {\n return globalCache.get<T>(key, ttl || undefined);\n };\n\n const hasCachedData = () => {\n return globalCache.has(key, ttl || undefined);\n };\n\n // Check if there's an active fetch for this key (dedupe concurrent requests)\n const getActiveFetch = (): Promise<T> | undefined => {\n return globalCache.getActiveFetch<T>(key);\n };\n\n const cachedData = getCachedData();\n const [data, setData] = useState<T | null>(cachedData);\n const [error, setError] = useState<unknown>(null);\n // If we have cached data, start with success status to avoid showing loader\n const [status, setStatus] = useState<Status>(cachedData ? \"success\" : \"idle\");\n\n const refetch = async () => {\n // ALWAYS check cache first - never show loading if we have valid cached data\n const cached = getCachedData();\n if (cached) {\n // Already have valid cached data, ensure state is correct\n setData(cached);\n setStatus(\"success\");\n return cached;\n }\n\n // Check if there's already an active fetch for this key (dedupe)\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n try {\n const res = await activeFetch;\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n }\n\n // Only set loading if we actually need to fetch\n setStatus(\"loading\");\n setError(null);\n\n try {\n // Create fetch promise and store it for deduplication\n const fetchPromise = fetcher();\n globalCache.setActiveFetch(key, fetchPromise);\n\n const res = await fetchPromise;\n globalCache.set(key, res);\n setData(res);\n setStatus(\"success\");\n return res;\n } catch (e) {\n setError(e);\n setStatus(\"error\");\n throw e;\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n const cached = getCachedData();\n\n if (cached) {\n setData(cached);\n setStatus(\"success\");\n return;\n }\n\n const activeFetch = getActiveFetch();\n if (activeFetch) {\n setStatus(\"loading\");\n activeFetch\n .then((res) => {\n setData(res);\n setStatus(\"success\");\n })\n .catch((e) => {\n setError(e);\n setStatus(\"error\");\n });\n return;\n }\n\n if (status !== \"loading\" && status !== \"success\") {\n refetch();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, enabled]);\n\n const hasValidCache = hasCachedData();\n const isLoading = status === \"loading\" && !hasValidCache;\n\n return {\n data,\n error,\n status,\n isIdle: status === \"idle\",\n isLoading,\n isSuccess: status === \"success\",\n isError: status === \"error\",\n refetch,\n };\n}\n","class FetchCache {\n private cache = new Map<string, { data: unknown; ts: number }>();\n private activeFetches = new Map<string, Promise<unknown>>();\n\n get<T>(key: string, ttl?: number): T | null {\n const cached = this.cache.get(key);\n if (!cached) return null;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n // Cache expired, remove it\n this.cache.delete(key);\n return null;\n }\n\n return cached.data as T;\n }\n\n has(key: string, ttl?: number): boolean {\n const cached = this.cache.get(key);\n if (!cached) return false;\n\n if (ttl && Date.now() - cached.ts > ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n set<T>(key: string, data: T): void {\n this.cache.set(key, { data, ts: Date.now() });\n }\n\n getActiveFetch<T>(key: string): Promise<T> | undefined {\n return this.activeFetches.get(key) as Promise<T> | undefined;\n }\n\n setActiveFetch<T>(key: string, promise: Promise<T>): void {\n this.activeFetches.set(key, promise);\n promise.finally(() => {\n this.activeFetches.delete(key);\n });\n }\n\n clear(): void {\n this.cache.clear();\n this.activeFetches.clear();\n }\n}\n\nexport default FetchCache;\n","import type { SlotsChain } from \"@0xslots/sdk\";\nimport type { AdData } from \"@adland/data\";\nimport sdk from \"@farcaster/miniapp-sdk\";\nimport { getAddress } from \"viem\";\n\n// Cache the miniapp check at module level — resolved once, sync thereafter\nlet _isMiniApp: boolean | null = null;\nconst _miniAppPromise = sdk\n .isInMiniApp()\n .then((v) => {\n _isMiniApp = v;\n return v;\n })\n .catch(() => {\n _isMiniApp = false;\n return false;\n });\n\nasync function isMiniApp(): Promise<boolean> {\n if (_isMiniApp !== null) return _isMiniApp;\n return _miniAppPromise;\n}\n\nexport function performAdAction(adData: AdData) {\n try {\n switch (adData.type) {\n case \"link\":\n sdk.actions.openUrl(adData.data.url);\n break;\n case \"cast\":\n sdk.actions.viewCast({ hash: adData.data.hash });\n break;\n case \"miniapp\":\n sdk.actions.openMiniApp({ url: adData.data.url });\n break;\n case \"token\": {\n const address = adData.data.address;\n const chainId = adData.data.chainId;\n const buyToken = `eip155:${chainId}/erc20:${getAddress(address)}`;\n sdk.actions.swapToken({ buyToken });\n break;\n }\n case \"farcasterProfile\":\n sdk.actions.viewProfile({\n fid: Number.parseInt(adData.data.fid, 10),\n });\n break;\n }\n } catch (err) {\n // Fallback for web (non-miniapp) context\n if (adData.type === \"link\" || adData.type === \"miniapp\") {\n window.open(adData.data.url, \"_blank\");\n } else {\n console.error(\"[@adland/react] Failed to perform ad action:\", err);\n }\n }\n}\n\nexport async function performEmptyAdAction(\n slot: string,\n chainId: SlotsChain,\n baseLinkUrl: string,\n) {\n const url = `${baseLinkUrl}/slots/${slot}?chain=${chainId}`;\n if (await isMiniApp()) {\n sdk.actions.openMiniApp({ url });\n } else {\n window.open(url, \"_blank\");\n }\n}\n","import type { AdData, AdType } from \"@adland/data\";\n\nconst IMAGE_KEYS = [\"image\", \"icon\", \"pfpUrl\", \"logoURI\", \"imageUrl\"] as const;\nconst TITLE_KEYS = [\"title\", \"displayName\", \"username\", \"name\", \"symbol\"] as const;\nconst DESC_KEYS = [\"description\", \"bio\", \"text\", \"name\"] as const;\n\nfunction flatFields(data: AdData): Record<string, unknown> {\n return { ...data.data, ...(data.metadata ?? {}) };\n}\n\nexport function getAdImage(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of IMAGE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdTitle(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n for (const key of TITLE_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v) return v;\n }\n return null;\n}\n\nexport function getAdDescription(data: AdData | null): string | null {\n if (!data) return null;\n const fields = flatFields(data);\n const title = getAdTitle(data);\n for (const key of DESC_KEYS) {\n const v = fields[key];\n if (typeof v === \"string\" && v && v !== title) return v;\n }\n return null;\n}\n\nexport function getAdType(data: AdData | null): AdType | null {\n if (!data) return null;\n return data.type as AdType;\n}\n","import { AdType } from \"@adland/data\";\nimport { ForwardRefExoticComponent, RefAttributes } from \"react\";\nimport {\n Link,\n MessageCircle,\n LayoutGrid,\n LucideProps,\n Coins,\n User,\n} from \"lucide-react\";\n\nexport const adCardIcon: Record<\n AdType,\n ForwardRefExoticComponent<\n Omit<LucideProps, \"ref\"> & RefAttributes<SVGSVGElement>\n >\n> = {\n link: Link,\n cast: MessageCircle,\n miniapp: LayoutGrid,\n token: Coins,\n farcasterProfile: User,\n};\n\nexport const adCardLabel: Record<AdType, string> = {\n link: \"Link\",\n cast: \"Cast\",\n miniapp: \"Miniapp\",\n token: \"Token\",\n farcasterProfile: \"Profile\",\n};\n\nexport const adlandApiUrl =\n process.env.NODE_ENV === \"development\"\n ? \"http://localhost:3069\"\n : \"https://api.adland.space\";\n","const UMAMI_URL = \"https://umami.api.0xslots.org\";\nconst WEBSITE_ID = \"b02a0a73-70c1-44cd-b4ae-cd70fa3d632f\";\n\ninterface TrackingPayload {\n slot: string;\n chainId: number;\n [key: string]: string | number | boolean | undefined;\n}\n\n/** Track which slot+url combos have already been counted this session */\nconst tracked = new Set<string>();\n\n/**\n * Send an event to Umami's collect endpoint.\n * Fires and forgets — never blocks rendering.\n */\nfunction sendEvent(eventName: string, data: TrackingPayload): void {\n if (typeof window === \"undefined\") return;\n\n const url = window.location.href;\n const referrer = document.referrer || undefined;\n const hostname = window.location.hostname;\n\n try {\n fetch(`${UMAMI_URL}/api/send`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n type: \"event\",\n payload: {\n website: WEBSITE_ID,\n url,\n referrer,\n hostname,\n name: eventName,\n data,\n },\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {}\n}\n\n/**\n * Track a unique impression (once per slot per page load).\n * Returns a cleanup function for the IntersectionObserver.\n */\nexport function trackImpression(\n element: HTMLElement | null,\n data: TrackingPayload,\n): (() => void) | undefined {\n if (!element || typeof window === \"undefined\") return;\n\n const key = `${data.slot}:${window.location.href}`;\n if (tracked.has(key)) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && !tracked.has(key)) {\n tracked.add(key);\n sendEvent(\"impression\", data);\n observer.disconnect();\n }\n }\n },\n { threshold: 0.5 }, // 50% visible\n );\n\n observer.observe(element);\n\n return () => observer.disconnect();\n}\n\n/**\n * Track a click event (no dedup — every click counts).\n */\nexport function trackClick(eventName: string, data: TrackingPayload): void {\n sendEvent(eventName, data);\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAE3B,SAAS,aAAa,aAAAA,YAAW,SAAS,cAAc;;;ACFxD,SAAS,mBAAoC;AAC7C,SAAuB,oBAAoB,YAAY;AACvD,SAAS,MAAM,mBAAmB;AAIlC,IAAM,eAAe;AAErB,IAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AACT;AAKO,SAAS,iBACd,SACA,QACa;AACb,QAAM,QAAQ,WAAW,OAAO;AAChC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,OAAO,EAAE;AAE3D,QAAM,eAAe,mBAAmB;AAAA,IACtC;AAAA,IACA,WAAW,KAAK,MAAM;AAAA,EACxB,CAAC;AAED,SAAO,IAAI,YAAY,EAAE,SAAS,aAAa,CAAC;AAClD;AAKO,IAAM,iBAAiB,OAAO,QAAgB;AACnD,MAAI,CAAC,IAAK,OAAM,IAAI,yBAA4B;AAEhD,QAAM,MAAM,IAAI,WAAW,SAAS,IAChC,GAAG,YAAY,GAAG,IAAI,MAAM,CAAC,CAAC,KAC9B;AAEJ,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS,EAAE,QAAQ,mBAAmB;AAAA,EACxC,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,IAAK,OAAM,IAAI,yBAA4B;AAC9D,UAAM,IAAI,yBAA4B;AAAA,EACxC;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KAAK,MAAO,OAAM,IAAI,MAAM,KAAK,KAAK;AAE1C,SAAO;AACT;AAKO,IAAM,mBAAmB,OAC9B,QACA,gBACoB;AACpB,QAAM,OAAO,MAAM,OAAO,YAAY,WAAsB;AAC5D,QAAM,gBAAiB,KAA6B;AAEpD,MACE,CAAC,iBACD,kBAAkB,8CAClB;AACA,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,QAAQ,SAAS;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AACF;;;AC5EA,SAAS,eAAe,kBAAkB;AAYnC,IAAM,YAAY,cAAqC,IAAI;AAE3D,SAAS,QAAwB;AACtC,QAAM,MAAM,WAAW,SAAS;AAChC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,6CAA6C;AACvE,SAAO;AACT;;;ACpBA,SAAS,WAAW,gBAAgB;;;ACApC,IAAM,aAAN,MAAiB;AAAA,EACP,QAAQ,oBAAI,IAA2C;AAAA,EACvD,gBAAgB,oBAAI,IAA8B;AAAA,EAE1D,IAAO,KAAa,KAAwB;AAC1C,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK;AAEvC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,IAAI,KAAa,KAAuB;AACtC,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK;AACvC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,IAAO,KAAa,MAAe;AACjC,SAAK,MAAM,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,CAAC;AAAA,EAC9C;AAAA,EAEA,eAAkB,KAAqC;AACrD,WAAO,KAAK,cAAc,IAAI,GAAG;AAAA,EACnC;AAAA,EAEA,eAAkB,KAAa,SAA2B;AACxD,SAAK,cAAc,IAAI,KAAK,OAAO;AACnC,YAAQ,QAAQ,MAAM;AACpB,WAAK,cAAc,OAAO,GAAG;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AACjB,SAAK,cAAc,MAAM;AAAA,EAC3B;AACF;AAEA,IAAO,qBAAQ;;;AD7Cf,IAAM,cAAc,IAAI,mBAAW;AAM5B,SAAS,SACd,KACA,SACA,MAIA;AACA,QAAM,EAAE,UAAU,MAAM,MAAM,EAAE,IAAI,QAAQ,CAAC;AAG7C,QAAM,gBAAgB,MAAgB;AACpC,WAAO,YAAY,IAAO,KAAK,OAAO,MAAS;AAAA,EACjD;AAEA,QAAM,gBAAgB,MAAM;AAC1B,WAAO,YAAY,IAAI,KAAK,OAAO,MAAS;AAAA,EAC9C;AAGA,QAAM,iBAAiB,MAA8B;AACnD,WAAO,YAAY,eAAkB,GAAG;AAAA,EAC1C;AAEA,QAAM,aAAa,cAAc;AACjC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmB,UAAU;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkB,IAAI;AAEhD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAiB,aAAa,YAAY,MAAM;AAE5E,QAAM,UAAU,YAAY;AAE1B,UAAM,SAAS,cAAc;AAC7B,QAAI,QAAQ;AAEV,cAAQ,MAAM;AACd,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,eAAe;AACnC,QAAI,aAAa;AACf,UAAI;AACF,cAAM,MAAM,MAAM;AAClB,gBAAQ,GAAG;AACX,kBAAU,SAAS;AACnB,eAAO;AAAA,MACT,SAAS,GAAG;AACV,iBAAS,CAAC;AACV,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAAA,IACF;AAGA,cAAU,SAAS;AACnB,aAAS,IAAI;AAEb,QAAI;AAEF,YAAM,eAAe,QAAQ;AAC7B,kBAAY,eAAe,KAAK,YAAY;AAE5C,YAAM,MAAM,MAAM;AAClB,kBAAY,IAAI,KAAK,GAAG;AACxB,cAAQ,GAAG;AACX,gBAAU,SAAS;AACnB,aAAO;AAAA,IACT,SAAS,GAAG;AACV,eAAS,CAAC;AACV,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,UAAM,SAAS,cAAc;AAE7B,QAAI,QAAQ;AACV,cAAQ,MAAM;AACd,gBAAU,SAAS;AACnB;AAAA,IACF;AAEA,UAAM,cAAc,eAAe;AACnC,QAAI,aAAa;AACf,gBAAU,SAAS;AACnB,kBACG,KAAK,CAAC,QAAQ;AACb,gBAAQ,GAAG;AACX,kBAAU,SAAS;AAAA,MACrB,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,iBAAS,CAAC;AACV,kBAAU,OAAO;AAAA,MACnB,CAAC;AACH;AAAA,IACF;AAEA,QAAI,WAAW,aAAa,WAAW,WAAW;AAChD,cAAQ;AAAA,IACV;AAAA,EAEF,GAAG,CAAC,KAAK,OAAO,CAAC;AAEjB,QAAM,gBAAgB,cAAc;AACpC,QAAM,YAAY,WAAW,aAAa,CAAC;AAE3C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB;AAAA,IACA,WAAW,WAAW;AAAA,IACtB,SAAS,WAAW;AAAA,IACpB;AAAA,EACF;AACF;;;AElIA,OAAO,SAAS;AAChB,SAAS,kBAAkB;AAG3B,IAAI,aAA6B;AACjC,IAAM,kBAAkB,IACrB,YAAY,EACZ,KAAK,CAAC,MAAM;AACX,eAAa;AACb,SAAO;AACT,CAAC,EACA,MAAM,MAAM;AACX,eAAa;AACb,SAAO;AACT,CAAC;AAEH,eAAe,YAA8B;AAC3C,MAAI,eAAe,KAAM,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,gBAAgB,QAAgB;AAC9C,MAAI;AACF,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,YAAI,QAAQ,QAAQ,OAAO,KAAK,GAAG;AACnC;AAAA,MACF,KAAK;AACH,YAAI,QAAQ,SAAS,EAAE,MAAM,OAAO,KAAK,KAAK,CAAC;AAC/C;AAAA,MACF,KAAK;AACH,YAAI,QAAQ,YAAY,EAAE,KAAK,OAAO,KAAK,IAAI,CAAC;AAChD;AAAA,MACF,KAAK,SAAS;AACZ,cAAM,UAAU,OAAO,KAAK;AAC5B,cAAM,UAAU,OAAO,KAAK;AAC5B,cAAM,WAAW,UAAU,OAAO,UAAU,WAAW,OAAO,CAAC;AAC/D,YAAI,QAAQ,UAAU,EAAE,SAAS,CAAC;AAClC;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,QAAQ,YAAY;AAAA,UACtB,KAAK,OAAO,SAAS,OAAO,KAAK,KAAK,EAAE;AAAA,QAC1C,CAAC;AACD;AAAA,IACJ;AAAA,EACF,SAAS,KAAK;AAEZ,QAAI,OAAO,SAAS,UAAU,OAAO,SAAS,WAAW;AACvD,aAAO,KAAK,OAAO,KAAK,KAAK,QAAQ;AAAA,IACvC,OAAO;AACL,cAAQ,MAAM,gDAAgD,GAAG;AAAA,IACnE;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,MACA,SACA,aACA;AACA,QAAM,MAAM,GAAG,WAAW,UAAU,IAAI,UAAU,OAAO;AACzD,MAAI,MAAM,UAAU,GAAG;AACrB,QAAI,QAAQ,YAAY,EAAE,IAAI,CAAC;AAAA,EACjC,OAAO;AACL,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC3B;AACF;;;ACnEA,IAAM,aAAa,CAAC,SAAS,QAAQ,UAAU,WAAW,UAAU;AACpE,IAAM,aAAa,CAAC,SAAS,eAAe,YAAY,QAAQ,QAAQ;AACxE,IAAM,YAAY,CAAC,eAAe,OAAO,QAAQ,MAAM;AAEvD,SAAS,WAAW,MAAuC;AACzD,SAAO,EAAE,GAAG,KAAK,MAAM,GAAI,KAAK,YAAY,CAAC,EAAG;AAClD;AAEO,SAAS,WAAW,MAAoC;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,aAAW,OAAO,YAAY;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEO,SAAS,WAAW,MAAoC;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,aAAW,OAAO,YAAY;AAC5B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAoC;AACnE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,WAAW,IAAI;AAC9B,QAAM,QAAQ,WAAW,IAAI;AAC7B,aAAW,OAAO,WAAW;AAC3B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,YAAY,KAAK,MAAM,MAAO,QAAO;AAAA,EACxD;AACA,SAAO;AACT;AAEO,SAAS,UAAU,MAAoC;AAC5D,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK;AACd;;;AC1CA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AAEA,IAAM,aAKT;AAAA,EACF,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,kBAAkB;AACpB;AAEO,IAAM,cAAsC;AAAA,EACjD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,kBAAkB;AACpB;AAEO,IAAM,eACX,QAAQ,IAAI,aAAa,gBACrB,0BACA;;;ACnCN,IAAM,YAAY;AAClB,IAAM,aAAa;AASnB,IAAM,UAAU,oBAAI,IAAY;AAMhC,SAAS,UAAU,WAAmB,MAA6B;AACjE,MAAI,OAAO,WAAW,YAAa;AAEnC,QAAM,MAAM,OAAO,SAAS;AAC5B,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,WAAW,OAAO,SAAS;AAEjC,MAAI;AACF,UAAM,GAAG,SAAS,aAAa;AAAA,MAC7B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,UACP,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD,WAAW;AAAA,IACb,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,QAAQ;AAAA,EAAC;AACX;AAMO,SAAS,gBACd,SACA,MAC0B;AAC1B,MAAI,CAAC,WAAW,OAAO,WAAW,YAAa;AAE/C,QAAM,MAAM,GAAG,KAAK,IAAI,IAAI,OAAO,SAAS,IAAI;AAChD,MAAI,QAAQ,IAAI,GAAG,EAAG;AAEtB,QAAM,WAAW,IAAI;AAAA,IACnB,CAAC,YAAY;AACX,iBAAW,SAAS,SAAS;AAC3B,YAAI,MAAM,kBAAkB,CAAC,QAAQ,IAAI,GAAG,GAAG;AAC7C,kBAAQ,IAAI,GAAG;AACf,oBAAU,cAAc,IAAI;AAC5B,mBAAS,WAAW;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,IACA,EAAE,WAAW,IAAI;AAAA;AAAA,EACnB;AAEA,WAAS,QAAQ,OAAO;AAExB,SAAO,MAAM,SAAS,WAAW;AACnC;AAKO,SAAS,WAAW,WAAmB,MAA6B;AACzE,YAAU,WAAW,IAAI;AAC3B;;;AR+BM,SAgBwB,UAhBxB,KAqDE,YArDF;AAlFC,SAAS,GAAG;AAAA,EACjB;AAAA,EACA,MAAM;AAAA,EACN,UAAU,WAAW;AAAA,EACrB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAY;AACV,QAAM,MAAM,OAAuB,IAAI;AAEvC,QAAM,SAAS;AAAA,IACb,MAAO,OAAO,iBAAiB,SAAS,MAAM,IAAI;AAAA,IAClD,CAAC,MAAM,SAAS,MAAM;AAAA,EACxB;AAEA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF,WAAW,IAAI;AAAA,IACf,YAAY;AACV,UAAI,CAAC,UAAU,CAAC,KAAM,OAAM,IAAI,yBAA4B;AAC5D,YAAM,MAAM,MAAM,iBAAiB,QAAQ,IAAI;AAC/C,UAAI,CAAC,KAAK;AACR,gBAAQ,KAAK,uCAAuC,IAAI;AACxD,eAAO;AAAA,MACT;AACA,aAAO,eAAe,GAAG;AAAA,IAC3B;AAAA,IACA,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,WAAW;AAAA,EACnC;AAEA,QAAM,SAAS,cAAc;AAE7B,QAAM,UACJ,CAAC,UACD,CAAC,cACA,iBAAiB,QACd,MAAM,kCACN,CAAC;AAGP,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,KAAM;AACtB,WAAO,gBAAgB,IAAI,SAAS,EAAE,MAAM,QAAQ,CAAC;AAAA,EACvD,GAAG,CAAC,QAAQ,MAAM,OAAO,CAAC;AAE1B,QAAM,UAAU;AAAA,IACd,CAAC,MAAwC;AACvC,YAAM,SAAS,EAAE;AACjB,YAAM,gBACJ,OAAO,YAAY,OACnB,OAAO,YAAY,YACnB,OAAO,QAAQ,GAAG,MAAM,QACxB,OAAO,QAAQ,QAAQ,MAAM;AAC/B,UAAI,cAAe;AAEnB,UAAI,QAAQ;AACV,YAAI,KAAM,YAAW,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/C,wBAAgB,MAAM;AAAA,MACxB,WAAW,WAAW,MAAM;AAC1B,mBAAW,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC3C,6BAAqB,MAAM,SAAS,WAAW;AAAA,MACjD;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,SAAS,MAAM,SAAS,WAAW;AAAA,EAC9C;AAEA,SACE;AAAA,IAAC,UAAU;AAAA,IAAV;AAAA,MACC,OAAO;AAAA,QACL,MAAM,UAAU;AAAA,QAChB,WAAW,CAAC,CAAC,QAAQ,CAAC,cAAc;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEA,8BAAC,SAAI,KAAU,SAAmB,GAAG,OAClC,UACH;AAAA;AAAA,EACF;AAEJ;AAQO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,MAAM,WAAW,IAAI;AAC3B,MAAI,CAAC,IAAK,QAAO,WAAW,gCAAG,oBAAS,IAAM;AAC9C,SAAO,oBAAC,SAAI,KAAU,KAAI,IAAI,GAAG,OAAO;AAC1C;AAMO,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAG,MAAM,GAAiB;AACtE,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,QAAQ,WAAW,IAAI;AAC7B,MAAI,CAAC,MAAO,QAAO,WAAW,gCAAG,oBAAS,IAAM;AAChD,SAAO,oBAAC,OAAG,GAAG,OAAQ,sBAAY,OAAM;AAC1C;AAMO,SAAS,cAAc,EAAE,UAAU,UAAU,GAAG,MAAM,GAAuB;AAClF,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,cAAc,iBAAiB,IAAI;AACzC,MAAI,CAAC,YAAa,QAAO,WAAW,gCAAG,oBAAS,IAAM;AACtD,SAAO,oBAAC,OAAG,GAAG,OAAQ,sBAAY,aAAY;AAChD;AAIO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,OAAO,UAAU,IAAI;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,WAAW,IAAI;AAC5B,QAAM,QAAQ,YAAY,IAAI;AAC9B,SACE,oBAAC,UAAM,GAAG,OACP,sBACC,iCACG;AAAA,YAAQ,oBAAC,QAAK,WAAU,UAAS;AAAA,IACjC;AAAA,KACH,GAEJ;AAEJ;AAIO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAiB;AAC5D,SAAO,oBAAC,UAAM,GAAG,OAAQ,sBAAY,MAAK;AAC5C;AAQO,SAAS,UAAU,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC/D,QAAM,EAAE,UAAU,IAAI,MAAM;AAC5B,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,cAAa;AACnD;AAEO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC7D,QAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,gBAAe;AACrD;AAEO,SAAS,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC7D,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAM;AACjC,MAAI,CAAC,SAAS,QAAS,QAAO;AAC9B,SAAO,oBAAC,SAAK,GAAG,OAAQ,sBAAY,oBAAmB;AACzD;AAEO,SAAS,SAAS,EAAE,UAAU,GAAG,MAAM,GAAkB;AAC9D,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,oBAAC,SAAK,GAAG,OAAQ,UAAS;AACnC;","names":["useEffect","useEffect"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adland/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"lucide-react": "0.561.0",
|
|
38
38
|
"tsup": "^8.0.1",
|
|
39
39
|
"viem": "^2.0.0",
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
40
|
+
"@adland/data": "0.14.2",
|
|
41
|
+
"@0xslots/sdk": "0.11.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@typescript-eslint/eslint-plugin": "^7.13.1",
|