{
  "name": "metalens-hooks-usemetalensstream",
  "type": "registry:hook",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/metalens/hooks/useMetaLensStream.ts",
      "type": "registry:hook",
      "content": "\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useMetaLensProvider } from \"../components/MetaLensProvider.js\";\nimport type {\n\tMetaLensComment,\n\tMetaLensContextType,\n\tMetaLensReactionData,\n\tStreamOptions,\n\tUseMetaLensStreamReturn,\n} from \"../types/index.js\";\nimport { useMetaLens } from \"./useMetaLens.js\";\n\n/**\n * Hook for real-time MetaLens comment streaming\n *\n * Based on patterns from:\n * - MetaLens browser extension EventSource implementation\n * - /Users/satchmo/code/metalens.app/CLAUDE.md (real-time features)\n *\n * @example\n * ```tsx\n * const { comments, loading, error } = useMetaLensStream('url', 'https://example.com');\n *\n * // Comments will update in real-time as new ones are posted\n * ```\n */\nexport function useMetaLensStream(\n\tcontext: MetaLensContextType,\n\tvalue: string,\n\toptions?: StreamOptions,\n): UseMetaLensStreamReturn {\n\tconst [comments, setComments] = useState<MetaLensComment[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst { getComments, subscribeToComments } = useMetaLens();\n\tconst { config } = useMetaLensProvider();\n\tconst mountedRef = useRef(true);\n\n\t// Helper function to update comment reactions\n\tconst updateCommentReaction = useCallback(\n\t\t(\n\t\t\tcomments: MetaLensComment[],\n\t\t\treaction: MetaLensReactionData,\n\t\t): MetaLensComment[] => {\n\t\t\treturn comments.map((comment) => {\n\t\t\t\tif (comment.txid === reaction.commentId) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...comment,\n\t\t\t\t\t\treactions: {\n\t\t\t\t\t\t\t...comment.reactions,\n\t\t\t\t\t\t\t[reaction.emoji]:\n\t\t\t\t\t\t\t\t(comment.reactions?.[reaction.emoji] || 0) +\n\t\t\t\t\t\t\t\t(reaction.removed ? -1 : 1),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t// Check replies recursively\n\t\t\t\tif (comment.replies) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...comment,\n\t\t\t\t\t\treplies: updateCommentReaction(comment.replies, reaction),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn comment;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\t// Helper function to add reply to comment tree\n\tconst addReplyToComment = useCallback(\n\t\t(\n\t\t\tcomments: MetaLensComment[],\n\t\t\treply: MetaLensComment,\n\t\t): MetaLensComment[] => {\n\t\t\treturn comments.map((comment) => {\n\t\t\t\tif (comment.txid === reply.metadata?.thread) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...comment,\n\t\t\t\t\t\treplies: [...(comment.replies || []), reply],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t// Check replies recursively\n\t\t\t\tif (comment.replies) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...comment,\n\t\t\t\t\t\treplies: addReplyToComment(comment.replies, reply),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn comment;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\tuseEffect(() => {\n\t\tmountedRef.current = true;\n\n\t\t// Fetch initial comments\n\t\tconst fetchInitialComments = async () => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\t\t\t\tsetError(null);\n\t\t\t\tconst initialComments = await getComments(context, value);\n\t\t\t\tif (mountedRef.current) {\n\t\t\t\t\tsetComments(initialComments);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (mountedRef.current) {\n\t\t\t\t\tsetError(err as Error);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (mountedRef.current) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Set up real-time subscription\n\t\tlet unsubscribe: (() => void) | null = null;\n\n\t\tif (config.enableRealTime && options?.autoConnect !== false) {\n\t\t\t// Subscribe to new comments\n\t\t\tunsubscribe = subscribeToComments(context, value, (newComment) => {\n\t\t\t\tif (mountedRef.current) {\n\t\t\t\t\tsetComments((prev) => {\n\t\t\t\t\t\t// Check if it's a reply\n\t\t\t\t\t\tif (newComment.metadata?.thread) {\n\t\t\t\t\t\t\treturn addReplyToComment(prev, newComment);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// New root comment - add to top\n\t\t\t\t\t\treturn [newComment, ...prev];\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Fetch initial data if autoConnect is true (default)\n\t\tif (options?.autoConnect !== false) {\n\t\t\tfetchInitialComments();\n\t\t}\n\n\t\t// Cleanup\n\t\treturn () => {\n\t\t\tmountedRef.current = false;\n\t\t\tif (unsubscribe) {\n\t\t\t\tunsubscribe();\n\t\t\t}\n\t\t};\n\t}, [\n\t\tcontext,\n\t\tvalue,\n\t\tgetComments,\n\t\tsubscribeToComments,\n\t\tconfig.enableRealTime,\n\t\toptions?.autoConnect,\n\t\taddReplyToComment,\n\t]);\n\n\tconst refetch = useCallback(async () => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst freshComments = await getComments(context, value);\n\t\t\tsetComments(freshComments);\n\t\t} catch (err) {\n\t\t\tsetError(err as Error);\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, [context, value, getComments]);\n\n\treturn {\n\t\tcomments,\n\t\tloading,\n\t\terror,\n\t\trefetch,\n\t};\n}\n",
      "target": "<%- config.aliases.hooks %>/usemetalensstream.ts"
    }
  ]
}