bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
17 lines • 7.11 kB
JSON
{
"name": "social-hooks-usefollowuser",
"type": "registry:hook",
"dependencies": [
"@tanstack/react-query"
],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "components/social/hooks/useFollowUser.ts",
"type": "registry:hook",
"content": "// useFollowUser Hook - Follow/unfollow users by BAP ID\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { broadcastSocialTransaction } from \"../../../lib/broadcast\";\nimport { SocialActions } from \"../../../lib/protocol.js\";\nimport type {\n\tFollowMutationData,\n\tUseFollowUserResult,\n\tUseUnfollowUserResult,\n} from \"../types/hooks.js\";\nimport type {\n\tAuthUserWithSigning,\n\tBroadcastResult,\n\tSocialError,\n} from \"../types/social.js\";\n\ninterface UseFollowUserOptions {\n\tapp?: string;\n\tonSuccess?: (\n\t\tresult: BroadcastResult & { action: \"follow\" | \"unfollow\" },\n\t) => void;\n\tonError?: (error: SocialError) => void;\n}\n\nexport function useFollowUser(\n\toptions: UseFollowUserOptions = {},\n): UseFollowUserResult {\n\tconst { app: _app = \"bigblocks\", onSuccess, onError } = options;\n\tconst { user } = useBitcoinAuth();\n\n\tconst followMutation = useMutation<\n\t\tBroadcastResult,\n\t\tSocialError,\n\t\tFollowMutationData\n\t>({\n\t\tmutationFn: async ({ idKey }) => {\n\t\t\tif (!user) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\t\t\tmessage: \"User must be authenticated to follow others\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\tif (!idKey) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"INVALID_BAP_ID\",\n\t\t\t\t\tmessage: \"BAP ID is required to follow a user\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\t// Validate BAP ID format (basic validation)\n\t\t\tif (typeof idKey !== \"string\" || idKey.trim().length === 0) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"INVALID_BAP_ID\",\n\t\t\t\t\tmessage: \"Invalid BAP ID format\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\t// Prevent self-following\n\t\t\tif (user.idKey === idKey) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"INVALID_CONTENT\",\n\t\t\t\t\tmessage: \"Cannot follow yourself\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst signingKey = (user as AuthUserWithSigning).signingKey;\n\t\t\t\tif (!signingKey) {\n\t\t\t\t\tthrow {\n\t\t\t\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\t\t\t\tmessage: \"No signing key available\",\n\t\t\t\t\t} as SocialError;\n\t\t\t\t}\n\n\t\t\t\t// Build follow transaction\n\t\t\t\tconst signedTransaction = await SocialActions.follow(idKey);\n\n\t\t\t\t// Broadcast to Bitcoin network\n\t\t\t\tconst result = await broadcastSocialTransaction(signedTransaction);\n\n\t\t\t\tif (!result.success) {\n\t\t\t\t\tthrow {\n\t\t\t\t\t\tcode: \"BROADCAST_FAILED\",\n\t\t\t\t\t\tmessage: result.error || \"Failed to broadcast follow\",\n\t\t\t\t\t} as SocialError;\n\t\t\t\t}\n\n\t\t\t\treturn { ...result, action: \"follow\" as const };\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as SocialError).code) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"TRANSACTION_FAILED\",\n\t\t\t\t\tmessage: \"Failed to create follow transaction\",\n\t\t\t\t\tdetails: error,\n\t\t\t\t} as SocialError;\n\t\t\t}\n\t\t},\n\t\tonSuccess: (data) => {\n\t\t\tonSuccess?.({ ...data, action: \"follow\" as const });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tconsole.error(\"Follow user failed:\", error);\n\t\t\tonError?.(error);\n\t\t},\n\t});\n\n\treturn {\n\t\tmutate: followMutation.mutate,\n\t\tmutateAsync: followMutation.mutateAsync,\n\t\tisLoading: followMutation.isPending,\n\t\tisError: followMutation.isError,\n\t\tisSuccess: followMutation.isSuccess,\n\t\terror: followMutation.error,\n\t\tdata: followMutation.data,\n\t\treset: followMutation.reset,\n\t} as UseFollowUserResult;\n}\n\n// Unfollow hook\nexport function useUnfollowUser(options: UseFollowUserOptions = {}) {\n\tconst { app: _app = \"bigblocks\", onSuccess, onError } = options;\n\tconst { user } = useBitcoinAuth();\n\n\tconst unfollowMutation = useMutation<\n\t\tBroadcastResult,\n\t\tSocialError,\n\t\tFollowMutationData\n\t>({\n\t\tmutationFn: async ({ idKey }) => {\n\t\t\tif (!user) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\t\t\tmessage: \"User must be authenticated to unfollow others\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\tif (!idKey) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"INVALID_BAP_ID\",\n\t\t\t\t\tmessage: \"BAP ID is required to unfollow a user\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\tif (typeof idKey !== \"string\" || idKey.trim().length === 0) {\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"INVALID_BAP_ID\",\n\t\t\t\t\tmessage: \"Invalid BAP ID format\",\n\t\t\t\t} as SocialError;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst signingKey = (user as AuthUserWithSigning).signingKey;\n\t\t\t\tif (!signingKey) {\n\t\t\t\t\tthrow {\n\t\t\t\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\t\t\t\tmessage: \"No signing key available\",\n\t\t\t\t\t} as SocialError;\n\t\t\t\t}\n\n\t\t\t\t// Build unfollow transaction\n\t\t\t\tconst signedTransaction = await SocialActions.unfollow(idKey);\n\n\t\t\t\t// Broadcast to Bitcoin network\n\t\t\t\tconst result = await broadcastSocialTransaction(signedTransaction);\n\n\t\t\t\tif (!result.success) {\n\t\t\t\t\tthrow {\n\t\t\t\t\t\tcode: \"BROADCAST_FAILED\",\n\t\t\t\t\t\tmessage: result.error || \"Failed to broadcast unfollow\",\n\t\t\t\t\t} as SocialError;\n\t\t\t\t}\n\n\t\t\t\treturn { ...result, action: \"unfollow\" as const };\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as SocialError).code) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\tthrow {\n\t\t\t\t\tcode: \"TRANSACTION_FAILED\",\n\t\t\t\t\tmessage: \"Failed to create unfollow transaction\",\n\t\t\t\t\tdetails: error,\n\t\t\t\t} as SocialError;\n\t\t\t}\n\t\t},\n\t\tonSuccess: (data) => {\n\t\t\tonSuccess?.({ ...data, action: \"unfollow\" as const });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tconsole.error(\"Unfollow user failed:\", error);\n\t\t\tonError?.(error);\n\t\t},\n\t});\n\n\treturn {\n\t\tmutate: unfollowMutation.mutate,\n\t\tmutateAsync: unfollowMutation.mutateAsync,\n\t\tisLoading: unfollowMutation.isPending,\n\t\tisError: unfollowMutation.isError,\n\t\tisSuccess: unfollowMutation.isSuccess,\n\t\terror: unfollowMutation.error,\n\t\tdata: unfollowMutation.data,\n\t\treset: unfollowMutation.reset,\n\t} as UseUnfollowUserResult;\n}\n\n// Combined hook for toggling follow status\nexport function useToggleFollow(options: UseFollowUserOptions = {}) {\n\tconst followUser = useFollowUser(options);\n\tconst unfollowUser = useUnfollowUser(options);\n\n\treturn {\n\t\tfollow: followUser,\n\t\tunfollow: unfollowUser,\n\t\ttoggle: (idKey: string, currentlyFollowing: boolean) => {\n\t\t\tif (currentlyFollowing) {\n\t\t\t\treturn unfollowUser.mutate({ idKey });\n\t\t\t}\n\t\t\treturn followUser.mutate({ idKey });\n\t\t},\n\t\tisLoading: followUser.isLoading || unfollowUser.isLoading,\n\t\terror: followUser.error || unfollowUser.error,\n\t};\n}\n",
"target": "<%- config.aliases.hooks %>/usefollowuser.ts"
}
]
}