UNPKG

bigblocks

Version:

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

15 lines 10.6 kB
{ "name": "profile-management-profilepublisher", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/profile-management/ProfilePublisher.tsx", "type": "registry:component", "content": "\"use client\";\n\nimport {\n\tCheckCircledIcon,\n\tCrossCircledIcon,\n\tInfoCircledIcon,\n\tRocketIcon,\n} from \"@radix-ui/react-icons\";\nimport { useState } from \"react\";\nimport { CONTAINER_WIDTHS } from \"../../lib/layout-constants.js\";\nimport type { ProfileInfo } from \"../../lib/types.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { LoadingButton } from \"../ui-components/LoadingButton.js\";\nimport { Alert, AlertDescription } from \"../ui/alert.js\";\nimport { Badge } from \"../ui/badge.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card, CardContent } from \"../ui/card.js\";\nimport {\n\tDialog,\n\tDialogClose,\n\tDialogContent,\n\tDialogTitle,\n} from \"../ui/dialog.js\";\nimport { Progress } from \"../ui/progress.js\";\nimport { ProfileCard } from \"./ProfileCard.js\";\n\nexport interface ProfilePublisherProps {\n\tprofile: ProfileInfo;\n\tonPublish: (profileId: string) => Promise<{ txid: string }>;\n\tonSuccess?: (txid: string) => void;\n\tonError?: (error: string) => void;\n\testimatedCost?: string;\n\tclassName?: string;\n}\n\n/**\n * ProfilePublisher - Publish BAP profiles to the Bitcoin blockchain\n *\n * Features:\n * - Profile preview before publishing\n * - Cost estimation display\n * - Publishing progress indication\n * - Transaction ID display on success\n * - Error handling with retry\n * - Confirmation dialog\n */\nexport function ProfilePublisher({\n\tprofile,\n\tonPublish,\n\tonSuccess,\n\tonError,\n\testimatedCost = \"~0.0001 BSV\",\n\tclassName = \"\",\n}: ProfilePublisherProps) {\n\tconst [showConfirmDialog, setShowConfirmDialog] = useState(false);\n\tconst [isPublishing, setIsPublishing] = useState(false);\n\tconst [publishProgress, setPublishProgress] = useState(0);\n\tconst [result, setResult] = useState<{\n\t\tsuccess: boolean;\n\t\ttxid?: string;\n\t\terror?: string;\n\t} | null>(null);\n\n\tconst handlePublish = async () => {\n\t\tsetIsPublishing(true);\n\t\tsetPublishProgress(10);\n\t\tsetResult(null);\n\n\t\ttry {\n\t\t\t// Simulate progress\n\t\t\tsetPublishProgress(30);\n\n\t\t\t// Call the publish function\n\t\t\tconst { txid } = await onPublish(profile.id);\n\n\t\t\tsetPublishProgress(80);\n\n\t\t\t// Small delay for UX\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t\t\tsetPublishProgress(100);\n\t\t\tsetResult({ success: true, txid });\n\t\t\tonSuccess?.(txid);\n\n\t\t\t// Close dialog after success\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetShowConfirmDialog(false);\n\t\t\t\tsetIsPublishing(false);\n\t\t\t\tsetPublishProgress(0);\n\t\t\t}, 2000);\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : \"Failed to publish profile\";\n\t\t\tsetResult({ success: false, error: errorMessage });\n\t\t\tonError?.(errorMessage);\n\t\t\tsetIsPublishing(false);\n\t\t\tsetPublishProgress(0);\n\t\t}\n\t};\n\n\tif (profile.isPublished) {\n\t\treturn (\n\t\t\t<Card\n\t\t\t\tclassName={cn(className)}\n\t\t\t\tstyle={{ maxWidth: CONTAINER_WIDTHS.CARD_MEDIUM }}\n\t\t\t>\n\t\t\t\t<CardContent className=\"flex flex-col gap-3 items-center py-8\">\n\t\t\t\t\t<CheckCircledIcon className=\"h-12 w-12 text-green-600\" />\n\t\t\t\t\t<p className=\"text-base font-medium\">Profile Already Published</p>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\tThis profile is already on the blockchain\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<>\n\t\t\t<Card\n\t\t\t\tclassName={cn(className)}\n\t\t\t\tstyle={{ maxWidth: CONTAINER_WIDTHS.CARD_MEDIUM }}\n\t\t\t>\n\t\t\t\t<CardContent className=\"p-6\">\n\t\t\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t<p className=\"text-lg font-bold\">Publish Profile</p>\n\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\tclassName=\"bg-orange-100 text-orange-700\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tUnpublished\n\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Alert>\n\t\t\t\t\t\t\t<InfoCircledIcon className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t<AlertDescription>\n\t\t\t\t\t\t\t\tPublishing your profile to the blockchain makes it permanent and\n\t\t\t\t\t\t\t\tdiscoverable by others. This action cannot be undone.\n\t\t\t\t\t\t\t</AlertDescription>\n\t\t\t\t\t\t</Alert>\n\n\t\t\t\t\t\t{/* Profile Preview */}\n\t\t\t\t\t\t<ProfileCard profile={profile} showActions={false} compact />\n\n\t\t\t\t\t\t{/* Cost Estimation */}\n\t\t\t\t\t\t<Card>\n\t\t\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Estimated Cost</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-mono\">{estimatedCost}</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t</Card>\n\n\t\t\t\t\t\t{/* Publish Button */}\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tsize=\"lg\"\n\t\t\t\t\t\t\tonClick={() => setShowConfirmDialog(true)}\n\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<RocketIcon className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\tPublish to Blockchain\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{/* Confirmation Dialog */}\n\t\t\t<Dialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>\n\t\t\t\t<DialogContent className=\"max-w-[450px]\">\n\t\t\t\t\t<DialogTitle>Confirm Profile Publishing</DialogTitle>\n\n\t\t\t\t\t<div className=\"flex flex-col gap-4 mt-4\">\n\t\t\t\t\t\t{!result ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tYou are about to publish your profile to the Bitcoin\n\t\t\t\t\t\t\t\t\tblockchain. This will make your profile information permanent\n\t\t\t\t\t\t\t\t\tand publicly visible.\n\t\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t\t<Card>\n\t\t\t\t\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Profile Details</p>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\tName: {profile.name || \"Unnamed\"}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\tAddress: {profile.address.slice(0, 8)}...\n\t\t\t\t\t\t\t\t\t\t\t\t{profile.address.slice(-6)}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{profile.description && (\n\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tBio: {profile.description.slice(0, 50)}...\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t\t\t</Card>\n\n\t\t\t\t\t\t\t\t<Card>\n\t\t\t\t\t\t\t\t\t<CardContent className=\"p-4\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Transaction Cost</p>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-bold font-mono\">\n\t\t\t\t\t\t\t\t\t\t\t\t{estimatedCost}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t\t\t</Card>\n\n\t\t\t\t\t\t\t\t{isPublishing && <Progress value={publishProgress} />}\n\n\t\t\t\t\t\t\t\t<div className=\"flex gap-3 justify-end\">\n\t\t\t\t\t\t\t\t\t<DialogClose asChild>\n\t\t\t\t\t\t\t\t\t\t<Button variant=\"secondary\" disabled={isPublishing}>\n\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</DialogClose>\n\t\t\t\t\t\t\t\t\t<LoadingButton loading={isPublishing} onClick={handlePublish}>\n\t\t\t\t\t\t\t\t\t\t<RocketIcon className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t{isPublishing ? \"Publishing...\" : \"Confirm & Publish\"}\n\t\t\t\t\t\t\t\t\t</LoadingButton>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{result.success ? (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-3 items-center py-4\">\n\t\t\t\t\t\t\t\t\t\t<CheckCircledIcon className=\"h-12 w-12 text-green-600\" />\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-base font-medium text-center\">\n\t\t\t\t\t\t\t\t\t\t\tProfile Published Successfully!\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\t\t\t\t\t\tYour profile is now permanently stored on the blockchain.\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{result.txid && (\n\t\t\t\t\t\t\t\t\t\t\t<Card className=\"w-full\">\n\t\t\t\t\t\t\t\t\t\t\t\t<CardContent className=\"p-3\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tTransaction ID\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs font-mono break-all\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{result.txid}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t\t\t\t\t\t</Card>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-3 items-center py-4\">\n\t\t\t\t\t\t\t\t\t\t<CrossCircledIcon className=\"h-12 w-12 text-destructive\" />\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-base font-medium text-center\">\n\t\t\t\t\t\t\t\t\t\t\tPublishing Failed\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\t\t\t\t\t\t{result.error ||\n\t\t\t\t\t\t\t\t\t\t\t\t\"An error occurred while publishing your profile.\"}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex gap-3 justify-end\">\n\t\t\t\t\t\t\t\t\t\t\t<DialogClose asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t<Button variant=\"secondary\">Close</Button>\n\t\t\t\t\t\t\t\t\t\t\t</DialogClose>\n\t\t\t\t\t\t\t\t\t\t\t<Button onClick={handlePublish}>Try Again</Button>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</DialogContent>\n\t\t\t</Dialog>\n\t\t</>\n\t);\n}\n", "target": "<%- config.aliases.components %>/profilepublisher.tsx" } ] }