{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "follow-button",
  "type": "registry:component",
  "title": "Lens Follow Button",
  "description": "A button component to follow or unfollow a Lens Account, displaying the current follow state.",
  "dependencies": [
    "@lens-protocol/react@0.0.0-canary-20250820102520",
    "@lens-protocol/client@0.0.0-canary-20250820102520",
    "viem@2.33.0"
  ],
  "registryDependencies": ["button", "spinner"],
  "files": [
    {
      "path": "registry/new-york/components/account/lens-follow-button.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport { Account, SessionClient, TxHash, UnauthenticatedError } from \"@lens-protocol/react\";\nimport { follow, unfollow } from \"@lens-protocol/client/actions\";\nimport { handleOperationWith } from \"@lens-protocol/client/viem\";\nimport { WalletClient } from \"viem\";\nimport { Spinner } from \"@/registry/new-york/ui/spinner\";\nimport { isResult, Result } from \"@/registry/new-york/lib/result\";\n\ntype Props = {\n  account: Account | Result<Account>;\n  session: Result<SessionClient>;\n  wallet?: { data: WalletClient | undefined | null; isLoading?: boolean; error?: unknown };\n  onFollowSuccess?: (account: Account, txHash: TxHash) => void;\n  onUnfollowSuccess?: (account: Account, txHash: TxHash) => void;\n  onFollowError?: (account: Account, error: Error) => void;\n};\n\nexport const LensFollowButton = ({ ...props }: Props) => {\n  const { account: accountRes, session, wallet, onFollowSuccess, onUnfollowSuccess, onFollowError } = props;\n  const [isSubmitting, setIsSubmitting] = useState(false);\n\n  const account = isResult(accountRes) ? accountRes?.data : accountRes;\n  const sessionClient = session?.data;\n  const walletClient = wallet?.data;\n\n  const accountLoading = accountRes && \"loading\" in accountRes ? accountRes.loading : false;\n\n  const [optimisticIsFollowed, setOptimisticIsFollowed] = useState<boolean>(\n    account?.operations?.isFollowedByMe ?? false,\n  );\n\n  useEffect(() => {\n    setOptimisticIsFollowed(account?.operations?.isFollowedByMe ?? false);\n  }, [account?.operations]);\n\n  const handleUnfollow = async () => {\n    if (!account || !sessionClient) return;\n\n    if (!walletClient) {\n      onFollowError?.(account, new Error(\"Wallet client is not available\"));\n      return;\n    }\n\n    const res = await unfollow(sessionClient, {\n      account: account.address,\n    })\n      .andThen(handleOperationWith(walletClient))\n      .andThen(sessionClient.waitForTransaction);\n\n    if (res.isErr()) {\n      onFollowError?.(account, res.error);\n      return;\n    }\n\n    setOptimisticIsFollowed(false);\n    onUnfollowSuccess?.(account, res.value);\n  };\n\n  const handleFollow = async () => {\n    if (!account || !sessionClient) return;\n\n    if (!walletClient) {\n      onFollowError?.(account, new Error(\"Wallet client is not available\"));\n      return;\n    }\n\n    const res = await follow(sessionClient, {\n      account: account.address,\n    })\n      .andThen(handleOperationWith(walletClient))\n      .andThen(sessionClient.waitForTransaction);\n\n    if (res.isErr()) {\n      onFollowError?.(account, res.error);\n      return;\n    }\n\n    setOptimisticIsFollowed(true);\n    onFollowSuccess?.(account, res.value);\n  };\n\n  const onFollowButtonClick = async () => {\n    if (!account) return;\n\n    if (!session) {\n      onFollowError?.(account, new UnauthenticatedError());\n      return;\n    }\n\n    setIsSubmitting(true);\n\n    try {\n      if (optimisticIsFollowed) {\n        await handleUnfollow();\n      } else {\n        await handleFollow();\n      }\n    } catch (e) {\n      if (e instanceof Error) {\n        onFollowError?.(account, e);\n      }\n    } finally {\n      setIsSubmitting(false);\n    }\n  };\n\n  return (\n    <Button\n      variant={optimisticIsFollowed ? \"outline\" : \"default\"}\n      className=\"group hover:border-destructive\"\n      onClick={onFollowButtonClick}\n      disabled={isSubmitting || accountLoading || session?.loading || wallet?.isLoading}\n    >\n      {isSubmitting ? (\n        <>\n          <Spinner /> {optimisticIsFollowed ? \"Unfollowing...\" : \"Following...\"}\n        </>\n      ) : (\n        <>\n          <span className=\"hidden group-hover:inline\">{optimisticIsFollowed ? \"Unfollow\" : \"Follow\"}</span>\n          <span className=\"inline group-hover:hidden\">{optimisticIsFollowed ? \"Following\" : \"Follow\"}</span>\n        </>\n      )}\n    </Button>\n  );\n};\n",
      "type": "registry:component"
    },
    {
      "path": "registry/new-york/lib/result.ts",
      "content": "export type Result<T, E = never> = {\n  data: T | null | undefined;\n  error: E | null | undefined;\n  loading: boolean;\n};\n\nexport const isResult = <T, E = never>(result: any): result is Result<T, E> => {\n  return \"data\" in result && \"error\" in result && \"loading\" in result;\n};\n\nexport const isLoading = (result: any): boolean => {\n  if (isResult(result)) {\n    return result.loading;\n  }\n  return false;\n};\n",
      "type": "registry:lib"
    }
  ]
}
