UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

158 lines (157 loc) 6.25 kB
"use client"; import { useCallback } from "react"; /** * Hook for detecting and parsing MetaLens contexts * * Based on patterns from: * - MetaLens browser extension context detection * - Various context type implementations * * @example * ```tsx * const { detectContext, parseContext } = useMetaLensContext(); * * // Detect context from current page * const context = detectContext(); * * // Parse a specific identifier * const parsed = parseContext('https://example.com/article'); * // Returns: { type: 'url', value: 'https://example.com/article' } * ``` */ export function useMetaLensContext() { const detectContext = useCallback(() => { if (typeof window === "undefined" || !document) { return null; } // Check meta tags for bitcoin transaction const txMeta = document.querySelector('meta[name="bitcoin-tx"], meta[property="bitcoin:tx"]'); if (txMeta?.getAttribute("content")) { return { type: "tx", value: txMeta.getAttribute("content") || "" }; } // Check for UPC in meta tags or structured data const upcMeta = document.querySelector('meta[property="product:upc"], meta[name="upc"]'); if (upcMeta?.getAttribute("content")) { return { type: "upc", value: upcMeta.getAttribute("content") || "" }; } // Check for ISBN in structured data const isbnElement = document.querySelector('[itemprop="isbn"], meta[property="book:isbn"]'); if (isbnElement) { const isbn = isbnElement.getAttribute("content") || isbnElement.textContent || ""; if (isbn) { return { type: "isbn", value: isbn.trim() }; } } // Check URL patterns for specific contexts const url = window.location.href; // Transaction explorers const txExplorerPattern = /(?:whatsonchain\.com|blockchair\.com|blockstream\.info)\/.*\/tx\/([a-fA-F0-9]{64})/; const txMatch = url.match(txExplorerPattern); if (txMatch?.[1]) { return { type: "tx", value: txMatch[1] }; } // IPFS gateways const ipfsPattern = /(?:ipfs\.io|gateway\.ipfs\.io|cloudflare-ipfs\.com)\/ipfs\/(Qm[a-zA-Z0-9]+)/; const ipfsMatch = url.match(ipfsPattern); if (ipfsMatch?.[1]) { return { type: "ipfs", value: ipfsMatch[1] }; } // Default to current URL return { type: "url", value: window.location.href }; }, []); const parseContext = useCallback((identifier) => { if (!identifier || typeof identifier !== "string") { return { type: "custom", value: identifier || "" }; } // Trim whitespace const trimmedIdentifier = identifier.trim(); // Check if it's a transaction ID (64 hex chars) if (/^[a-fA-F0-9]{64}$/.test(trimmedIdentifier)) { return { type: "tx", value: trimmedIdentifier }; } // Check if it's a UPC (12-14 digits) if (/^\d{12,14}$/.test(trimmedIdentifier)) { return { type: "upc", value: trimmedIdentifier }; } // Check if it's an ISBN (10 or 13 digits, possibly with dashes) const cleanIsbn = trimmedIdentifier.replace(/[-\s]/g, ""); if (/^(?:\d{9}[\dX]|\d{13})$/.test(cleanIsbn)) { return { type: "isbn", value: trimmedIdentifier }; } // Check if it's a geohash (alphanumeric, typically 4-12 chars) // Basic validation - real geohash validation is more complex if (/^[0-9a-z]{4,12}$/.test(trimmedIdentifier)) { // Additional check: geohashes use base32 (no 'a', 'i', 'l', 'o') if (!/[ailo]/i.test(trimmedIdentifier)) { return { type: "geohash", value: trimmedIdentifier }; } } // Check if it's an IPFS hash if (trimmedIdentifier.startsWith("Qm") && trimmedIdentifier.length === 46) { return { type: "ipfs", value: trimmedIdentifier }; } if (trimmedIdentifier.startsWith("ipfs://")) { return { type: "ipfs", value: trimmedIdentifier.replace("ipfs://", ""), }; } // Check if it's a DOI if (/^10\.\d{4,}\/[-._;()\/:a-zA-Z0-9]+$/.test(trimmedIdentifier)) { return { type: "doi", value: trimmedIdentifier }; } // Check if it's a URL try { const _url = new URL(trimmedIdentifier); // Valid URL return { type: "url", value: trimmedIdentifier }; } catch { // Not a valid URL, check if it looks like a URL without protocol if (trimmedIdentifier.includes(".") && !trimmedIdentifier.includes(" ")) { try { new URL(`https://${trimmedIdentifier}`); return { type: "url", value: `https://${trimmedIdentifier}` }; } catch { // Still not valid } } } // Default to custom return { type: "custom", value: trimmedIdentifier }; }, []); const formatContextDisplay = useCallback((type, value) => { switch (type) { case "url": try { const url = new URL(value); return url.hostname + (url.pathname !== "/" ? url.pathname : ""); } catch { return value; } case "tx": return `${value.substring(0, 8)}...${value.substring(56)}`; case "upc": return `Product ${value}`; case "geohash": return `Location ${value.substring(0, 6)}`; case "isbn": return `Book ISBN ${value}`; case "doi": return `Paper ${value}`; case "ipfs": return `IPFS ${value.substring(0, 12)}...`; default: return value.length > 20 ? `${value.substring(0, 20)}...` : value; } }, []); return { detectContext, parseContext, formatContextDisplay, }; }