{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-reaction-toggle",
  "type": "registry:hook",
  "title": "Use Reaction Toggle",
  "description": "A hook to toggle reactions (like/unlike) on Lens posts using the Lens React SDK.",
  "dependencies": [
    "@lens-protocol/react@0.0.0-canary-20250820102520",
    "@lens-protocol/types@0.0.0-canary-20250820102520"
  ],
  "files": [
    {
      "path": "registry/new-york/hooks/use-reaction-toggle.ts",
      "content": "import {\n  AddReactionMutation,\n  AddReactionRequest,\n  AnyPost,\n  invariant,\n  PostReactionType,\n  PublicClient,\n  SessionClient,\n  UndoReactionMutation,\n  UndoReactionRequest,\n} from \"@lens-protocol/react\";\nimport { UseAsyncTask, useAsyncTask } from \"@/registry/new-york/lib/tasks\";\nimport { ResultAsync } from \"@lens-protocol/types\";\n\nexport type ReactionToggleArgs = {\n  session: SessionClient | PublicClient;\n  post: AnyPost;\n};\n\ntype ReactionToggleResult = ResultAsync<void, unknown>;\n\nexport const hasUpvoted = (post: AnyPost): boolean => \"operations\" in post && post.operations?.hasUpvoted === true;\n\nexport const useReactionToggle = (): UseAsyncTask<ReactionToggleArgs, void, unknown> => {\n  return useAsyncTask((args: ReactionToggleArgs): ReactionToggleResult => {\n    invariant(args.session?.isSessionClient(), \"You must be authenticated to use this operation\");\n\n    return ResultAsync.fromPromise(\n      (async () => {\n        const post = args.post;\n        if (hasUpvoted(post)) {\n          const request: UndoReactionRequest = { post: post.id, reaction: PostReactionType.Upvote };\n          args.session?.mutation(UndoReactionMutation, { request });\n        } else {\n          const request: AddReactionRequest = { post: post.id, reaction: PostReactionType.Upvote };\n          args.session?.mutation(AddReactionMutation, { request });\n        }\n      })(),\n      error => {\n        console.error(\"Error toggling reaction:\", error);\n        return error;\n      },\n    );\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"
    }
  ]
}
