UNPKG

bigblocks

Version:

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

17 lines 10.8 kB
{ "name": "droplit-components-datapushbutton", "type": "registry:component", "dependencies": [ "@radix-ui/react-icons" ], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/droplit/components/DataPushButton.tsx", "type": "registry:component", "content": "\"use client\";\n\nimport { CheckIcon } from \"@radix-ui/react-icons\";\nimport { useState } from \"react\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { PROTOCOL_IDS } from \"../../social/types/protocol.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Card, CardContent } from \"../../ui/card.js\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"../../ui/select.js\";\nimport { Textarea } from \"../../ui/textarea.js\";\n\nexport interface DataTemplate {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\tprotocol: string;\n\tcategory: \"social\" | \"market\" | \"identity\" | \"protocol\";\n\texample: string[];\n}\n\nexport interface DataPushButtonProps {\n\tonSuccess?: (result: {\n\t\ttxid: string;\n\t\tdata: string[];\n\t\ttemplate: string;\n\t}) => void;\n\tonError?: (error: Error) => void;\n\tdata?: string[]; // Pre-built data array - if provided, skips form rendering\n\ttemplate?: DataTemplate;\n\tbuttonText?: string;\n\tvariant?: \"default\" | \"secondary\" | \"outline\" | \"ghost\";\n\tsize?: \"sm\" | \"default\" | \"lg\";\n\tshowTemplateSelector?: boolean;\n\tshowPreview?: boolean;\n\tclassName?: string;\n\tcolor?: \"blue\" | \"green\" | \"orange\" | \"red\" | \"purple\" | \"gray\";\n\trequireAuth?: boolean;\n\tdisabled?: boolean;\n}\n\n// Bitcoin protocol templates for droplit data transactions\nconst dataTemplates: DataTemplate[] = [\n\t{\n\t\tid: \"b-post\",\n\t\tname: \"B:// Post\",\n\t\tdescription: \"Create content using B:// protocol\",\n\t\tprotocol: \"B\",\n\t\tcategory: \"social\",\n\t\texample: [\n\t\t\tPROTOCOL_IDS.B, // B protocol prefix\n\t\t\t\"Hello Bitcoin world! 🚀\", // Content\n\t\t\t\"text/plain\", // Content type\n\t\t\t\"utf-8\", // Encoding\n\t\t\t\"post.txt\", // Filename\n\t\t],\n\t},\n\t{\n\t\tid: \"map-social\",\n\t\tname: \"MAP Social Post\",\n\t\tdescription: \"Social post with MAP protocol metadata\",\n\t\tprotocol: \"MAP\",\n\t\tcategory: \"social\",\n\t\texample: [\n\t\t\tPROTOCOL_IDS.MAP, // MAP protocol prefix\n\t\t\t\"SET\", // Action\n\t\t\t\"app\",\n\t\t\t\"bigblocks.social\", // App identifier\n\t\t\t\"type\",\n\t\t\t\"post\", // Post type\n\t\t],\n\t},\n\t{\n\t\tid: \"bap-identity\",\n\t\tname: \"BAP Identity\",\n\t\tdescription: \"Bitcoin Attestation Protocol identity claim\",\n\t\tprotocol: \"BAP\",\n\t\tcategory: \"identity\",\n\t\texample: [\n\t\t\tPROTOCOL_IDS.BAP, // BAP protocol prefix\n\t\t\t\"ID\", // Identity claim\n\t\t\t\"name\",\n\t\t\t\"Satoshi Nakamoto\", // Name\n\t\t\t\"bio\",\n\t\t\t\"Creator of Bitcoin\", // Bio\n\t\t],\n\t},\n\t{\n\t\tid: \"custom-protocol\",\n\t\tname: \"Custom Protocol\",\n\t\tdescription: \"Your own protocol data\",\n\t\tprotocol: \"CUSTOM\",\n\t\tcategory: \"protocol\",\n\t\texample: [\n\t\t\t\"my_protocol_v1\", // Protocol identifier\n\t\t\t\"action\",\n\t\t\t\"store\", // Action\n\t\t\t\"data\",\n\t\t\t\"Custom data here\", // Data\n\t\t],\n\t},\n];\n\n/**\n * DataPushButton - Push data to Bitcoin blockchain via droplit with protocol templates\n *\n * This component allows users to push structured data to the Bitcoin blockchain\n * using predefined protocol templates. It supports social posts, market listings,\n * identity claims, and more.\n *\n * Can be used in two modes:\n * 1. Form mode (no data prop) - renders template selector and content input\n * 2. Button-only mode (with data prop) - just renders the push button\n *\n * @example\n * // Form mode - renders full form interface\n * ```tsx\n * <DataPushButton\n * onSuccess={(result) => console.log('Data pushed:', result)}\n * onError={(error) => console.error('Error:', error)}\n * showTemplateSelector={true}\n * showPreview={true}\n * />\n * ```\n *\n * @example\n * // Button-only mode - provide your own data\n * ```tsx\n * <DataPushButton\n * data={['B_SOCIAL', 'POST', 'My custom post content', Date.now().toString()]}\n * onSuccess={(result) => console.log('Data pushed:', result)}\n * buttonText=\"Post to Bitcoin\"\n * showPreview={true}\n * />\n * ```\n */\nexport function DataPushButton({\n\tonSuccess,\n\tonError,\n\tdata,\n\ttemplate,\n\tbuttonText = \"Push Data\",\n\tvariant = \"default\",\n\tsize = \"default\",\n\tshowTemplateSelector = true,\n\tshowPreview = true,\n\tclassName = \"\",\n\tcolor = \"orange\",\n\trequireAuth = false,\n\tdisabled = false,\n}: DataPushButtonProps) {\n\tconst [selectedTemplate, setSelectedTemplate] = useState<DataTemplate>(() => {\n\t\tif (template) return template;\n\t\tconst defaultTemplate = dataTemplates[0];\n\t\tif (!defaultTemplate) {\n\t\t\tthrow new Error(\"No data templates available\");\n\t\t}\n\t\treturn defaultTemplate;\n\t});\n\tconst [customContent, setCustomContent] = useState(\"\");\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [pushCount, setPushCount] = useState(0);\n\tconst [isAuthenticated] = useState(true); // Demo: assume authenticated\n\n\t// Determine if we should show form elements (only if no data is provided)\n\tconst showForm = !data;\n\n\tconst handlePushData = async () => {\n\t\tif (requireAuth && !isAuthenticated) {\n\t\t\tonError?.(new Error(\"Authentication required\"));\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsLoading(true);\n\n\t\ttry {\n\t\t\t// Simulate droplit data push (no BSV cost)\n\t\t\t// In production, this would call the actual droplit API\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 1500));\n\n\t\t\t// Use provided data or build from template/custom content\n\t\t\tlet dataArray: string[];\n\t\t\tif (data) {\n\t\t\t\t// Use provided data directly\n\t\t\t\tdataArray = [...data];\n\t\t\t} else if (customContent) {\n\t\t\t\t// Parse custom content as line-separated values\n\t\t\t\tdataArray = customContent.split(\"\\n\").filter((line) => line.trim());\n\t\t\t} else {\n\t\t\t\t// Use template default\n\t\t\t\tdataArray = [...selectedTemplate.example];\n\t\t\t}\n\n\t\t\tconst newCount = pushCount + 1;\n\t\t\tsetPushCount(newCount);\n\n\t\t\tconst result = {\n\t\t\t\ttxid: `droplit-tx-${Date.now()}-${newCount}`,\n\t\t\t\tdata: dataArray,\n\t\t\t\ttemplate: data ? \"custom\" : selectedTemplate.id,\n\t\t\t};\n\n\t\t\tonSuccess?.(result);\n\t\t} catch (error) {\n\t\t\tonError?.(error as Error);\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t};\n\n\tif (requireAuth && !isAuthenticated) {\n\t\treturn (\n\t\t\t<Card>\n\t\t\t\t<CardContent className=\"flex items-center gap-2 p-4\">\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t🔐 Authentication required for droplit data transactions\n\t\t\t\t\t</p>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className={cn(\"flex flex-col gap-3\", className)}>\n\t\t\t{showForm && showTemplateSelector && (\n\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t<p className=\"text-sm font-medium\">Protocol Template</p>\n\t\t\t\t\t<Select\n\t\t\t\t\t\tvalue={selectedTemplate.id}\n\t\t\t\t\t\tonValueChange={(value) => {\n\t\t\t\t\t\t\tconst foundTemplate = dataTemplates.find((t) => t.id === value);\n\t\t\t\t\t\t\tif (foundTemplate) {\n\t\t\t\t\t\t\t\tsetSelectedTemplate(foundTemplate);\n\t\t\t\t\t\t\t\t// Reset custom content to show the new template\n\t\t\t\t\t\t\t\tsetCustomContent(\"\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<SelectTrigger>\n\t\t\t\t\t\t\t<SelectValue />\n\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t{dataTemplates.map((template) => (\n\t\t\t\t\t\t\t\t<SelectItem key={template.id} value={template.id}>\n\t\t\t\t\t\t\t\t\t{template.name}\n\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t</Select>\n\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t{selectedTemplate.description}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showForm && (\n\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t<p className=\"text-sm font-medium\">Data Array Content</p>\n\t\t\t\t\t<Textarea\n\t\t\t\t\t\tvalue={customContent || selectedTemplate.example.join(\"\\n\")}\n\t\t\t\t\t\tonChange={(e) => setCustomContent(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Enter data array items (one per line)\"\n\t\t\t\t\t\trows={Math.max(3, selectedTemplate.example.length)}\n\t\t\t\t\t/>\n\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\tEdit the data array above. Each line becomes a separate string in\n\t\t\t\t\t\tthe array.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showPreview && (\n\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t<p className=\"text-sm font-medium\">Data Preview</p>\n\t\t\t\t\t<Card className=\"bg-muted\">\n\t\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t\t<code className=\"text-xs block whitespace-pre overflow-x-auto\">\n\t\t\t\t\t\t\t\t{JSON.stringify(\n\t\t\t\t\t\t\t\t\tdata ||\n\t\t\t\t\t\t\t\t\t\t(customContent\n\t\t\t\t\t\t\t\t\t\t\t? customContent.split(\"\\n\").filter((line) => line.trim())\n\t\t\t\t\t\t\t\t\t\t\t: selectedTemplate.example),\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<Button\n\t\t\t\tonClick={handlePushData}\n\t\t\t\tdisabled={isLoading || disabled}\n\t\t\t\tvariant={variant}\n\t\t\t\tsize={size}\n\t\t\t\tclassName={cn(\n\t\t\t\t\tcolor === \"orange\" && \"bg-orange-600 hover:bg-orange-700\",\n\t\t\t\t\tcolor === \"blue\" && \"bg-blue-600 hover:bg-blue-700\",\n\t\t\t\t\tcolor === \"green\" && \"bg-green-600 hover:bg-green-700\",\n\t\t\t\t\tcolor === \"red\" && \"bg-red-600 hover:bg-red-700\",\n\t\t\t\t\tcolor === \"purple\" && \"bg-purple-600 hover:bg-purple-700\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{isLoading ? \"Pushing to Droplit...\" : buttonText}\n\t\t\t</Button>\n\n\t\t\t{pushCount > 0 && (\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<CheckIcon className=\"text-green-600\" />\n\t\t\t\t\t<Badge\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tclassName=\"text-xs bg-green-100 text-green-700\"\n\t\t\t\t\t>\n\t\t\t\t\t\tData pushed {pushCount} time{pushCount !== 1 ? \"s\" : \"\"}\n\t\t\t\t\t</Badge>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t💧 Droplit: Data transactions with no BSV costs\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n", "target": "<%- config.aliases.components %>/datapushbutton.tsx" } ] }