This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / features / cards / lib / mutations / useAddCard.tsx
3.8 kB 92 lines
1import { useMutation, useQueryClient } from '@tanstack/react-query'; 2import { addUrlToLibrary } from '../dal'; 3import { cardKeys } from '../cardKeys'; 4import { collectionKeys } from '@/features/collections/lib/collectionKeys'; 5import { feedKeys } from '@/features/feeds/lib/feedKeys'; 6import { noteKeys } from '@/features/notes/lib/noteKeys'; 7import { sembleKeys } from '@/features/semble/lib/sembleKeys'; 8import posthog from 'posthog-js'; 9import { 10 CardSaveAnalyticsContext, 11 CardSaveEventProperties, 12} from '@/features/analytics/types'; 13import { shouldCaptureAnalytics } from '@/features/analytics/utils'; 14 15export default function useAddCard( 16 analyticsContext?: CardSaveAnalyticsContext, 17) { 18 const queryClient = useQueryClient(); 19 20 const mutation = useMutation({ 21 mutationFn: (newCard: { 22 url: string; 23 note?: string; 24 collectionIds?: string[]; 25 viaCardId?: string; 26 }) => { 27 return addUrlToLibrary(newCard.url, { 28 note: newCard.note, 29 collectionIds: newCard.collectionIds, 30 viaCardId: newCard.viaCardId, 31 }); 32 }, 33 34 // Do things that are absolutely necessary and logic related (like query invalidation) in the useMutation callbacks 35 // Do UI related things like redirects or showing toast notifications in mutate callbacks. If the user navigated away from the current screen before the mutation finished, those will purposefully not fire 36 // https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire 37 onSuccess: (_data, variables) => { 38 queryClient.invalidateQueries({ queryKey: cardKeys.all() }); 39 queryClient.invalidateQueries({ queryKey: noteKeys.all() }); 40 queryClient.invalidateQueries({ queryKey: feedKeys.all() }); 41 queryClient.invalidateQueries({ queryKey: sembleKeys.all() }); 42 queryClient.invalidateQueries({ queryKey: collectionKeys.mine() }); 43 queryClient.invalidateQueries({ queryKey: collectionKeys.infinite() }); 44 queryClient.invalidateQueries({ queryKey: collectionKeys.all() }); 45 queryClient.invalidateQueries({ 46 queryKey: collectionKeys.bySembleUrl(variables.url), 47 }); 48 // Invalidate URL metadata with stats to update tab counts 49 queryClient.invalidateQueries({ 50 queryKey: cardKeys.urlMetadata(variables.url, { includeStats: true }), 51 }); 52 53 // invalidate each collection query individually 54 variables.collectionIds?.forEach((id) => { 55 queryClient.invalidateQueries({ 56 queryKey: collectionKeys.collection(id), 57 }); 58 queryClient.invalidateQueries({ 59 queryKey: collectionKeys.infinite(id), 60 }); 61 }); 62 63 // Track card save event in PostHog 64 if (shouldCaptureAnalytics() && analyticsContext) { 65 const eventProperties: CardSaveEventProperties = { 66 save_source: analyticsContext.saveSource, 67 is_new_card: true, 68 has_note: !!variables.note, 69 collection_count: variables.collectionIds?.length || 0, 70 active_filters: analyticsContext.activeFilters 71 ? { 72 url_type: analyticsContext.activeFilters.urlType, 73 sort: analyticsContext.activeFilters.sort, 74 search_query: analyticsContext.activeFilters.searchQuery, 75 profile_filter: analyticsContext.activeFilters.profileFilter, 76 } 77 : undefined, 78 via_card_id: variables.viaCardId, 79 page_path: analyticsContext.pagePath, 80 }; 81 82 posthog.capture('card_saved', eventProperties); 83 84 // Clear super properties after capture 85 posthog.unregister('original_save_source'); 86 posthog.unregister('original_active_filters'); 87 } 88 }, 89 }); 90 91 return mutation; 92}