From d5121ebddc6d8cdd02a2bec05c3c4f0f440cd936 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 03:23:08 +0000 Subject: [PATCH 1/2] =?UTF-8?q?refactor(api):=20knowledge=20=E2=86=92=20sr?= =?UTF-8?q?c/api=20modules=20(Effort=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical move of the knowledge feature from src/hooks/queries/knowledge to src/api/knowledge, adding queryOptions factories for the query hooks. No behavior change. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K513rsQz6Lg1HbbfYiafrE --- src/api/knowledge/types.ts | 4 + src/api/knowledge/useKnowledge.ts | 34 ++++++++ .../knowledge/useKnowledgeToggleMutation.ts} | 79 +------------------ src/api/knowledge/useUserKnowledgeQuery.ts | 38 +++++++++ 4 files changed, 78 insertions(+), 77 deletions(-) create mode 100644 src/api/knowledge/types.ts create mode 100644 src/api/knowledge/useKnowledge.ts rename src/{hooks/queries/knowledge/useKnowledge.ts => api/knowledge/useKnowledgeToggleMutation.ts} (51%) create mode 100644 src/api/knowledge/useUserKnowledgeQuery.ts diff --git a/src/api/knowledge/types.ts b/src/api/knowledge/types.ts new file mode 100644 index 00000000..294b7c3a --- /dev/null +++ b/src/api/knowledge/types.ts @@ -0,0 +1,4 @@ +export const knowledgeKeys = { + all: ["knowledge"] as const, + user: (userId: string) => [...knowledgeKeys.all, "user", userId] as const, +}; diff --git a/src/api/knowledge/useKnowledge.ts b/src/api/knowledge/useKnowledge.ts new file mode 100644 index 00000000..20e6846c --- /dev/null +++ b/src/api/knowledge/useKnowledge.ts @@ -0,0 +1,34 @@ +import { useAuth } from "@/contexts/AuthContext"; +import { useUserKnowledgeQuery } from "./useUserKnowledgeQuery"; +import { useKnowledgeToggleMutation } from "./useKnowledgeToggleMutation"; + +export function useKnowledge() { + const { user } = useAuth(); + const { data: userKnowledge = {} } = useUserKnowledgeQuery(user?.id); + const knowledgeToggleMutation = useKnowledgeToggleMutation(); + + async function handleKnowledgeToggle(artistId: string) { + if (!user) { + return { requiresAuth: true }; + } + + const isKnown = userKnowledge[artistId]; + + try { + await knowledgeToggleMutation.mutateAsync({ + artistId, + userId: user.id, + isKnown, + }); + return { requiresAuth: false }; + } catch (error) { + console.error("failed toggling knowledge", error); + return { requiresAuth: false }; + } + } + + return { + userKnowledge, + handleKnowledgeToggle, + }; +} diff --git a/src/hooks/queries/knowledge/useKnowledge.ts b/src/api/knowledge/useKnowledgeToggleMutation.ts similarity index 51% rename from src/hooks/queries/knowledge/useKnowledge.ts rename to src/api/knowledge/useKnowledgeToggleMutation.ts index 80e81a3a..af4ed689 100644 --- a/src/hooks/queries/knowledge/useKnowledge.ts +++ b/src/api/knowledge/useKnowledgeToggleMutation.ts @@ -1,35 +1,7 @@ -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useToast } from "@/hooks/use-toast"; import { supabase } from "@/integrations/supabase/client"; -import { useAuth } from "@/contexts/AuthContext"; - -// Query key factory -export const knowledgeKeys = { - all: ["knowledge"] as const, - user: (userId: string) => [...knowledgeKeys.all, "user", userId] as const, -}; - -// Business logic functions -async function fetchUserKnowledge( - userId: string, -): Promise> { - const { data, error } = await supabase - .from("artist_knowledge") - .select("artist_id") - .eq("user_id", userId); - - if (error) { - throw new Error("Failed to fetch user knowledge"); - } - - return (data || []).reduce( - (acc, knowledge) => { - acc[knowledge.artist_id] = true; - return acc; - }, - {} as Record, - ); -} +import { knowledgeKeys } from "./types"; async function toggleKnowledge(variables: { artistId: string; @@ -58,15 +30,6 @@ async function toggleKnowledge(variables: { } } -// Hooks -export function useUserKnowledgeQuery(userId: string | undefined) { - return useQuery({ - queryKey: knowledgeKeys.user(userId || ""), - queryFn: () => fetchUserKnowledge(userId!), - enabled: !!userId, - }); -} - export function useKnowledgeToggleMutation() { const queryClient = useQueryClient(); const { toast } = useToast(); @@ -76,17 +39,14 @@ export function useKnowledgeToggleMutation() { onMutate: async (variables) => { const { artistId, userId, isKnown } = variables; - // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: knowledgeKeys.user(userId), }); - // Snapshot the previous value const previousKnowledge = queryClient.getQueryData< Record >(knowledgeKeys.user(userId)); - // Optimistically update to the new value queryClient.setQueryData>( knowledgeKeys.user(userId), (old) => { @@ -94,10 +54,8 @@ export function useKnowledgeToggleMutation() { const newKnowledge = { ...old }; if (isKnown) { - // Remove knowledge delete newKnowledge[artistId]; } else { - // Add knowledge newKnowledge[artistId] = true; } @@ -108,7 +66,6 @@ export function useKnowledgeToggleMutation() { return { previousKnowledge, userId }; }, onError: (_error, _variables, context) => { - // If the mutation fails, use the context returned from onMutate to roll back if (context?.previousKnowledge) { queryClient.setQueryData( knowledgeKeys.user(context.userId), @@ -123,41 +80,9 @@ export function useKnowledgeToggleMutation() { }); }, onSettled: (_data, _error, variables) => { - // Always refetch after error or success to ensure consistency queryClient.invalidateQueries({ queryKey: knowledgeKeys.user(variables.userId), }); }, }); } - -export function useKnowledge() { - const { user } = useAuth(); - const { data: userKnowledge = {} } = useUserKnowledgeQuery(user?.id); - const knowledgeToggleMutation = useKnowledgeToggleMutation(); - - async function handleKnowledgeToggle(artistId: string) { - if (!user) { - return { requiresAuth: true }; - } - - const isKnown = userKnowledge[artistId]; - - try { - await knowledgeToggleMutation.mutateAsync({ - artistId, - userId: user.id, - isKnown, - }); - return { requiresAuth: false }; - } catch (error) { - console.error("failed toggling knowledge", error); - return { requiresAuth: false }; - } - } - - return { - userKnowledge, - handleKnowledgeToggle, - }; -} diff --git a/src/api/knowledge/useUserKnowledgeQuery.ts b/src/api/knowledge/useUserKnowledgeQuery.ts new file mode 100644 index 00000000..cf02e5e1 --- /dev/null +++ b/src/api/knowledge/useUserKnowledgeQuery.ts @@ -0,0 +1,38 @@ +import { queryOptions, useQuery } from "@tanstack/react-query"; +import { supabase } from "@/integrations/supabase/client"; +import { knowledgeKeys } from "./types"; + +async function fetchUserKnowledge( + userId: string, +): Promise> { + const { data, error } = await supabase + .from("artist_knowledge") + .select("artist_id") + .eq("user_id", userId); + + if (error) { + throw new Error("Failed to fetch user knowledge"); + } + + return (data || []).reduce( + (acc, knowledge) => { + acc[knowledge.artist_id] = true; + return acc; + }, + {} as Record, + ); +} + +export function userKnowledgeQuery(userId: string) { + return queryOptions({ + queryKey: knowledgeKeys.user(userId), + queryFn: () => fetchUserKnowledge(userId), + }); +} + +export function useUserKnowledgeQuery(userId: string | undefined) { + return useQuery({ + ...userKnowledgeQuery(userId!), + enabled: !!userId, + }); +} From ec524452bae0974c4dd4b6fac97ee3a5d79dbe89 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 03:43:18 +0000 Subject: [PATCH 2/2] fix(api): preserve empty-string key normalization in useUserKnowledgeQuery The mechanical move changed knowledgeKeys.user(userId || "") to userKnowledgeQuery(userId!), shifting the disabled-state cache key from ["...", ""] to ["...", undefined]. Restore ?? "" to keep the move behavior-neutral, matching main. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K513rsQz6Lg1HbbfYiafrE --- src/api/knowledge/useUserKnowledgeQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/knowledge/useUserKnowledgeQuery.ts b/src/api/knowledge/useUserKnowledgeQuery.ts index cf02e5e1..4bf26d37 100644 --- a/src/api/knowledge/useUserKnowledgeQuery.ts +++ b/src/api/knowledge/useUserKnowledgeQuery.ts @@ -32,7 +32,7 @@ export function userKnowledgeQuery(userId: string) { export function useUserKnowledgeQuery(userId: string | undefined) { return useQuery({ - ...userKnowledgeQuery(userId!), + ...userKnowledgeQuery(userId ?? ""), enabled: !!userId, }); }