bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
158 lines (157 loc) • 6.25 kB
JavaScript
"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,
};
}