{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-tip-post-action",
  "type": "registry:hook",
  "title": "Use Tip Post Action",
  "description": "A hook for tipping Lens Posts using the Lens React SDK.",
  "dependencies": [
    "@lens-protocol/react@0.0.0-canary-20250820102520",
    "@lens-protocol/client@0.0.0-canary-20250820102520",
    "@lens-protocol/types@0.0.0-canary-20250820102520",
    "viem@2.33.0"
  ],
  "registryDependencies": ["https://lensblocks.com/r/utils.json"],
  "files": [
    {
      "path": "registry/new-york/hooks/use-tip-post-action.ts",
      "content": "import {\n  AnyPost,\n  evmAddress,\n  PaymentSource,\n  PostId,\n  SessionClient,\n  SigningError,\n  TransactionIndexingError,\n  TxHash,\n  UnauthenticatedError,\n  UnexpectedError,\n  UseAsyncTask,\n  ValidationError,\n} from \"@lens-protocol/react\";\nimport { executePostAction } from \"@lens-protocol/client/actions\";\nimport { WalletClient } from \"viem\";\nimport { LensChainId, LensChainNativeToken, LensChainTestnetId, toApiError } from \"@/registry/new-york/lib/lens-utils\";\nimport { handleOperationWith } from \"@lens-protocol/client/viem\";\nimport { invariant, ResultAsync } from \"@lens-protocol/types\";\nimport { useAsyncTask } from \"@/registry/new-york/lib/tasks\";\n\nexport type TipPostActionArgs = {\n  post: PostId;\n  source: PaymentSource;\n  amount: string;\n  tokenAddress: string;\n};\n\ntype TipPostActionResult = ResultAsync<\n  TxHash,\n  SigningError | TransactionIndexingError | UnauthenticatedError | UnexpectedError | ValidationError\n>;\n\nexport const hasTipped = (post: AnyPost): boolean => \"operations\" in post && post.operations?.hasTipped === true;\n\nexport const useTipPostAction = ({\n  sessionClient,\n  walletClient,\n  useTestnet,\n}: {\n  sessionClient: SessionClient | null | undefined;\n  walletClient: WalletClient | null | undefined;\n  useTestnet?: boolean;\n}): UseAsyncTask<\n  TipPostActionArgs,\n  TxHash,\n  SigningError | TransactionIndexingError | UnauthenticatedError | UnexpectedError | ValidationError\n> => {\n  return useAsyncTask((args: TipPostActionArgs): TipPostActionResult => {\n    invariant(sessionClient?.isSessionClient(), \"You must be authenticated to use this operation\");\n    invariant(walletClient, \"A wallet must be connected to use this operation\");\n\n    const switchChain = () => walletClient?.switchChain({ id: useTestnet ? LensChainTestnetId : LensChainId });\n\n    return ResultAsync.fromPromise(switchChain(), toApiError)\n      .andThen(() => {\n        return executePostAction(sessionClient, {\n          post: args.post,\n          action: {\n            tipping: {\n              paymentSource: args.source,\n              ...(args.tokenAddress === LensChainNativeToken\n                ? { native: args.amount }\n                : { value: args.amount, token: evmAddress(args.tokenAddress) }),\n            },\n          },\n        });\n      })\n      .andThen(handleOperationWith(walletClient))\n      .andThen(sessionClient.waitForTransaction);\n  });\n};\n",
      "type": "registry:hook"
    },
    {
      "path": "registry/new-york/lib/tasks.ts",
      "content": "import { type ResultAsync, invariant } from \"@lens-protocol/types\";\nimport { useCallback, useState } from \"react\";\n\n/**\n * An deferrable task is a function that can be executed multiple times and that can be in a pending state.\n *\n * @internal\n */\nexport type AsyncTask<TInput, TResult extends ResultAsync<unknown, unknown>> = (input: TInput) => TResult;\n\n/**\n * The initial state of a async task.\n */\nexport type AsyncTaskIdle = {\n  called: boolean;\n  loading: false;\n  data: undefined;\n  error: undefined;\n};\n\n/**\n * The state of a async task during the loading.\n */\nexport type AsyncTaskLoading<TData> = {\n  called: true;\n  loading: true;\n  data: TData | undefined;\n  error: undefined;\n};\n\n/**\n * The state of a async task after a successful call.\n */\nexport type AsyncTaskSuccess<TData> = {\n  called: true;\n  loading: false;\n  data: TData;\n  error: undefined;\n};\n\n/**\n * The state of a async task after a failed call.\n */\nexport type AsyncTaskError<TError> = {\n  called: true;\n  loading: false;\n  data: undefined;\n  error: TError;\n};\n\n/**\n * The possible statuses of a async task.\n */\nexport type AsyncTaskState<TData, TError> =\n  | AsyncTaskIdle\n  | AsyncTaskLoading<TData>\n  | AsyncTaskSuccess<TData>\n  | AsyncTaskError<TError>;\n\nconst AsyncTaskState = {\n  Idle: <TData, TError>(): AsyncTaskState<TData, TError> => ({\n    called: false,\n    loading: false,\n    data: undefined,\n    error: undefined,\n  }),\n  Loading: <TData, TError>(data?: TData): AsyncTaskState<TData, TError> => ({\n    called: true,\n    loading: true,\n    data,\n    error: undefined,\n  }),\n  Success: <TData, TError>(data: TData): AsyncTaskState<TData, TError> => ({\n    called: true,\n    loading: false,\n    data,\n    error: undefined,\n  }),\n  Failed: <TData, TError>(error: TError): AsyncTaskState<TData, TError> => ({\n    called: true,\n    loading: false,\n    data: undefined,\n    error,\n  }),\n};\n\n/**\n * A async task React Hook is a lightweight wrapper for an asynchronous function.\n * It allows tracking of the task's execution status and provides access to the\n * last error that occurred during the task's execution, if any.\n *\n * ```ts\n * const { called, loading, data, error, execute }: UseAsyncTask<TData, TError, TInput> = useAnyAsyncTask();\n *\n * if (!called) {\n *   // data === undefined\n *   // error === undefined\n *   return <p>Click the button to execute the task</p>;\n * }\n *\n * if (loading) {\n *   // data === undefined on first call\n *   // data === TData from previous successful call\n *   // error === undefined\n *   return <Loader />;\n * }\n *\n * if (error) {\n *   // data === undefined\n *   // error === TError\n *   return <p>Something went wrong: {error.message}</p>;\n * }\n *\n * // called === true\n * // data === TData\n * // error === undefined\n * return <p>Task completed: {data}</p>;\n * ```\n */\nexport type UseAsyncTask<TInput, TValue, TError> = AsyncTaskState<TValue, TError> & {\n  execute: AsyncTask<TInput, ResultAsync<TValue, TError>>;\n};\n\n/**\n * @internal\n */\nexport function useAsyncTask<TInput, TValue, TError, TResult extends ResultAsync<TValue, TError>>(\n  handler: AsyncTask<TInput, TResult>,\n): UseAsyncTask<TInput, TValue, TError> {\n  const [state, setState] = useState(AsyncTaskState.Idle<TValue, TError>());\n\n  const execute = useCallback(\n    (input: TInput) => {\n      invariant(!state.loading, \"Cannot execute a task while another is in progress.\");\n\n      setState(({ data }) => {\n        return {\n          called: true,\n          loading: true,\n          data,\n          error: undefined,\n        };\n      });\n\n      const result = handler(input);\n\n      result.match(\n        value => setState(AsyncTaskState.Success(value)),\n        error => setState(AsyncTaskState.Failed(error)),\n      );\n\n      return result;\n    },\n    [handler, state],\n  );\n\n  return {\n    ...state,\n    execute,\n  };\n}\n",
      "type": "registry:lib"
    }
  ]
}
