bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
17 lines • 5.91 kB
JSON
{
"name": "social-components-compactpostbutton",
"type": "registry:component",
"dependencies": [
"@radix-ui/react-icons"
],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "components/social/components/CompactPostButton.tsx",
"type": "registry:component",
"content": "import { PaperPlaneIcon, Pencil1Icon } from \"@radix-ui/react-icons\";\nimport React, { useState } from \"react\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { CONTAINER_WIDTHS } from \"../../../lib/layout-constants.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../ui/popover.js\";\nimport { Textarea } from \"../../ui/textarea.js\";\nimport { useFetchPost } from \"../hooks/useFetchPost.js\";\nimport { useSocialPost } from \"../hooks/useSocialPost.js\";\nimport type { PostButtonProps } from \"../types/social.js\";\n\nexport interface CompactPostButtonProps extends PostButtonProps {\n\tplaceholder?: string;\n\tmaxLength?: number;\n\talign?: \"start\" | \"center\" | \"end\";\n\tside?: \"top\" | \"right\" | \"bottom\" | \"left\";\n\ticonSize?: number;\n}\n\nexport function CompactPostButton({\n\tonPost,\n\tdefaultContent = \"\",\n\tplaceholder = \"What's on your mind?\",\n\tmaxLength = 280,\n\talign = \"end\",\n\tside = \"bottom\",\n\ticonSize = 16,\n\tclassName,\n\tvariant = \"ghost\",\n\tsize = \"default\",\n\tdisabled,\n\t...props\n}: CompactPostButtonProps) {\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [content, setContent] = useState(defaultContent);\n\tconst [pendingTxid, setPendingTxid] = useState<string | null>(null);\n\n\tconst { isAuthenticated } = useBitcoinAuth();\n\n\t// Fetch the full post after creation\n\tconst { post: fetchedPost } = useFetchPost({\n\t\ttxid: pendingTxid || \"\",\n\t\tenabled: !!pendingTxid,\n\t});\n\n\t// When we get the full post, call the callback\n\tReact.useEffect(() => {\n\t\tif (fetchedPost?.post && onPost) {\n\t\t\tonPost(fetchedPost.post);\n\t\t\tsetPendingTxid(null); // Clear pending state\n\t\t}\n\t}, [fetchedPost, onPost]);\n\n\tconst {\n\t\tmutate: createPost,\n\t\tisLoading,\n\t\terror,\n\t} = useSocialPost({\n\t\tonSuccess: (result) => {\n\t\t\tsetIsOpen(false);\n\t\t\tsetContent(\"\");\n\t\t\tif (result.txid) {\n\t\t\t\tsetPendingTxid(result.txid); // Trigger fetch of full post\n\t\t\t}\n\t\t},\n\t});\n\n\tconst handleSubmit = () => {\n\t\tif (!content.trim()) return;\n\n\t\tcreatePost({\n\t\t\tcontent: content.trim(),\n\t\t\tcontentType: \"text/plain\",\n\t\t});\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n\t\t\te.preventDefault();\n\t\t\thandleSubmit();\n\t\t}\n\t};\n\n\tconst remainingChars = maxLength - content.length;\n\tconst isOverLimit = remainingChars < 0;\n\tconst canSubmit = content.trim().length > 0 && !isOverLimit && !isLoading;\n\n\tif (!isAuthenticated) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\tsize={size}\n\t\t\t\tdisabled\n\t\t\t\ttitle=\"Sign in to post\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"p-0\",\n\t\t\t\t\tsize === \"sm\" ? \"h-8 w-8\" : \"h-10 w-10\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<Pencil1Icon width={iconSize} height={iconSize} />\n\t\t\t</Button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Popover open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t<PopoverTrigger asChild>\n\t\t\t\t<Button\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\ttitle=\"Create post\"\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"p-0\",\n\t\t\t\t\t\tsize === \"sm\" ? \"h-8 w-8\" : \"h-10 w-10\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t<Pencil1Icon width={iconSize} height={iconSize} />\n\t\t\t\t</Button>\n\t\t\t</PopoverTrigger>\n\n\t\t\t<PopoverContent\n\t\t\t\talign={align}\n\t\t\t\tside={side}\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: CONTAINER_WIDTHS.POPOVER_SMALL,\n\t\t\t\t}}\n\t\t\t\tclassName=\"p-3\"\n\t\t\t>\n\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t{/* Compact Header */}\n\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t<p className=\"text-sm font-bold\">Quick Post</p>\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"text-xs\",\n\t\t\t\t\t\t\t\tisOverLimit\n\t\t\t\t\t\t\t\t\t? \"text-red-600\"\n\t\t\t\t\t\t\t\t\t: remainingChars < 20\n\t\t\t\t\t\t\t\t\t\t? \"text-orange-600\"\n\t\t\t\t\t\t\t\t\t\t: \"text-muted-foreground\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{remainingChars}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Content Input */}\n\t\t\t\t\t<Textarea\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\tvalue={content}\n\t\t\t\t\t\tonChange={(e) => setContent(e.target.value)}\n\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\trows={3}\n\t\t\t\t\t\tclassName=\"text-sm resize-none\"\n\t\t\t\t\t\tautoFocus\n\t\t\t\t\t/>\n\n\t\t\t\t\t{/* Error Display */}\n\t\t\t\t\t{error && <p className=\"text-xs text-red-600\">{error.message}</p>}\n\n\t\t\t\t\t{/* Actions */}\n\t\t\t\t\t<div className=\"flex gap-2 justify-between items-center\">\n\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">Cmd+Enter to send</p>\n\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={handleSubmit}\n\t\t\t\t\t\t\tdisabled={!canSubmit || isLoading}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{isLoading ? (\n\t\t\t\t\t\t\t\t\"Posting...\"\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<PaperPlaneIcon className=\"w-3 h-3 mr-1\" />\n\t\t\t\t\t\t\t\t\tPost\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</PopoverContent>\n\t\t</Popover>\n\t);\n}\n",
"target": "<%- config.aliases.components %>/compactpostbutton.tsx"
}
]
}