👁️
5
fork

Configure Feed

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

show if card is in users decks, new icon for decks

+59 -8
+2 -2
src/components/social/BacklinkModal.tsx
··· 1 1 import { useInfiniteQuery } from "@tanstack/react-query"; 2 2 import { useVirtualizer } from "@tanstack/react-virtual"; 3 - import { Bookmark, Heart, Layers, Loader2, X } from "lucide-react"; 3 + import { Bookmark, Heart, Loader2, Rows3, X } from "lucide-react"; 4 4 import { useEffect, useId, useRef } from "react"; 5 5 import { 6 6 type CardItemUri, ··· 37 37 }, 38 38 decks: { 39 39 title: "In decks", 40 - icon: Layers, 40 + icon: Rows3, 41 41 iconBg: "bg-purple-100 dark:bg-purple-900/30", 42 42 iconColor: "text-purple-600 dark:text-purple-400", 43 43 },
+17 -5
src/components/social/SocialStats.tsx
··· 1 - import { Bookmark, Heart, Layers } from "lucide-react"; 1 + import { Bookmark, Heart, Rows3 } from "lucide-react"; 2 2 import { useState } from "react"; 3 3 import type { SaveItem } from "@/lib/collection-list-types"; 4 4 import type { SocialItemUri } from "@/lib/constellation-queries"; ··· 40 40 isLikedByUser, 41 41 likeCount, 42 42 isLikeLoading, 43 + isInUserDeck, 43 44 deckCount, 44 45 isDeckCountLoading, 45 46 } = useItemSocialStats(itemUri, item.type); ··· 152 153 {item.type === "card" && showCount && ( 153 154 <div className="flex items-center"> 154 155 <div 155 - className={`${statBase} text-gray-600 dark:text-gray-400`} 156 - title="Decks containing this card" 156 + className={`${statBase} ${ 157 + isInUserDeck 158 + ? "text-purple-500 dark:text-purple-400" 159 + : "text-gray-600 dark:text-gray-400" 160 + }`} 161 + title={isInUserDeck ? "In your deck" : "Decks containing this card"} 157 162 > 158 - <Layers className="w-5 h-5" /> 163 + <Rows3 164 + className="w-5 h-5" 165 + fill={isInUserDeck ? "currentColor" : "none"} 166 + /> 159 167 </div> 160 168 <button 161 169 type="button" 162 170 onClick={() => deckCount > 0 && setOpenModal("decks")} 163 171 disabled={deckCount === 0} 164 - className={`text-sm tabular-nums px-1 py-2 rounded text-gray-600 dark:text-gray-400 ${isDeckCountLoading ? "opacity-50" : ""} ${deckCount > 0 ? "hover:underline cursor-pointer" : "cursor-default"}`} 172 + className={`text-sm tabular-nums px-1 py-2 rounded ${isDeckCountLoading ? "opacity-50" : ""} ${ 173 + isInUserDeck 174 + ? "text-purple-500 dark:text-purple-400" 175 + : "text-gray-600 dark:text-gray-400" 176 + } ${deckCount > 0 ? "hover:underline cursor-pointer" : "cursor-default"}`} 165 177 title={deckCount > 0 ? "See decks with this card" : undefined} 166 178 > 167 179 {deckCount}
+40 -1
src/lib/constellation-queries.ts
··· 102 102 } 103 103 104 104 /** 105 + * Query options for checking if current user has any deck containing a card 106 + */ 107 + export function userDeckContainsCardQueryOptions( 108 + itemUri: CardItemUri, 109 + userDid: Did | undefined, 110 + ) { 111 + return queryOptions({ 112 + queryKey: ["constellation", "userDeckContains", itemUri, userDid] as const, 113 + queryFn: async (): Promise<boolean> => { 114 + if (!userDid) return false; 115 + 116 + const result = await getBacklinks({ 117 + subject: itemUri, 118 + source: buildSource(DECK_LIST_NSID, DECK_LIST_CARD_PATH), 119 + did: userDid, 120 + limit: 1, 121 + }); 122 + 123 + if (!result.success) { 124 + throw result.error; 125 + } 126 + 127 + return result.data.records.length > 0; 128 + }, 129 + enabled: !!userDid, 130 + staleTime: 30 * 1000, 131 + }); 132 + } 133 + 134 + /** 105 135 * Query options for getting count of decks containing a card (cards only) 106 136 */ 107 137 export function cardDeckCountQueryOptions(itemUri: CardItemUri) { ··· 131 161 isLikedByUser: boolean; 132 162 likeCount: number; 133 163 isLikeLoading: boolean; 164 + isInUserDeck: boolean; 134 165 deckCount: number; 135 166 isDeckCountLoading: boolean; 136 167 } ··· 154 185 ); 155 186 const likeCountQuery = useQuery(itemLikeCountQueryOptions(itemUri, itemType)); 156 187 157 - // Deck count only applies to cards 188 + // Deck queries only apply to cards 189 + const userDeckQuery = useQuery({ 190 + ...userDeckContainsCardQueryOptions( 191 + itemUri as CardItemUri, 192 + session?.info.sub, 193 + ), 194 + enabled: itemType === "card" && !!session, 195 + }); 158 196 const deckCountQuery = useQuery({ 159 197 ...cardDeckCountQueryOptions(itemUri as CardItemUri), 160 198 enabled: itemType === "card", ··· 167 205 isLikedByUser: likedQuery.data ?? false, 168 206 likeCount: likeCountQuery.data ?? 0, 169 207 isLikeLoading: likedQuery.isLoading || likeCountQuery.isLoading, 208 + isInUserDeck: userDeckQuery.data ?? false, 170 209 deckCount: deckCountQuery.data ?? 0, 171 210 isDeckCountLoading: itemType === "card" && deckCountQuery.isLoading, 172 211 };