Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/api/knowledge/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const knowledgeKeys = {
all: ["knowledge"] as const,
user: (userId: string) => [...knowledgeKeys.all, "user", userId] as const,
};
34 changes: 34 additions & 0 deletions src/api/knowledge/useKnowledge.ts
Original file line number Diff line number Diff line change
@@ -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 };
}
Comment on lines +10 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. handleknowledgetoggle uses mutateasync try/catch 📘 Rule violation ⚙ Maintainability

handleKnowledgeToggle wraps knowledgeToggleMutation.mutateAsync(...) in a try/catch to handle
success/error paths. This violates the requirement to use callback-based `mutate(variables, {
onSuccess, onError })` for mutation handling.
Agent Prompt
## Issue description
`handleKnowledgeToggle` currently uses `mutateAsync` with a surrounding `try/catch`. The mutation should instead be invoked via `mutate` with `onSuccess`/`onError` callbacks.

## Issue Context
This hook currently returns `{ requiresAuth: boolean }` for both success and failure; preserve that behavior while moving handling into callbacks.

## Fix Focus Areas
- src/api/knowledge/useKnowledge.ts[10-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

}

return {
userKnowledge,
handleKnowledgeToggle,
};
}
Original file line number Diff line number Diff line change
@@ -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<Record<string, boolean>> {
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<string, boolean>,
);
}
import { knowledgeKeys } from "./types";

async function toggleKnowledge(variables: {
artistId: string;
Expand Down Expand Up @@ -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();
Expand All @@ -76,28 +39,23 @@ 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<string, boolean>
>(knowledgeKeys.user(userId));

// Optimistically update to the new value
queryClient.setQueryData<Record<string, boolean>>(
knowledgeKeys.user(userId),
(old) => {
if (!old) return {};
const newKnowledge = { ...old };

if (isKnown) {
// Remove knowledge
delete newKnowledge[artistId];
} else {
// Add knowledge
newKnowledge[artistId] = true;
}

Expand All @@ -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),
Expand All @@ -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,
};
}
38 changes: 38 additions & 0 deletions src/api/knowledge/useUserKnowledgeQuery.ts
Original file line number Diff line number Diff line change
@@ -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<Record<string, boolean>> {
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<string, boolean>,
);
}

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,
});
}
Loading