👁️
5
fork

Configure Feed

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

optimistic update for backlinks

+124 -1
+62
src/lib/collection-list-queries.ts
··· 34 34 removeDeckFromList, 35 35 type SaveItem, 36 36 } from "./collection-list-types"; 37 + import { 38 + type BacklinksResponse, 39 + COLLECTION_LIST_NSID, 40 + } from "./constellation-client"; 37 41 import { getConstellationQueryKeys } from "./constellation-queries"; 38 42 import { getPdsForDid } from "./identity"; 39 43 import type { ComDeckbelcherCollectionList } from "./lexicons/index"; ··· 509 513 await queryClient.cancelQueries({ 510 514 queryKey: constellationKeys.saveCount, 511 515 }); 516 + await queryClient.cancelQueries({ 517 + queryKey: constellationKeys.savers, 518 + }); 512 519 513 520 const previousSaved = queryClient.getQueryData<boolean>( 514 521 constellationKeys.userSaved, ··· 516 523 const previousCount = queryClient.getQueryData<number>( 517 524 constellationKeys.saveCount, 518 525 ); 526 + const previousSavers = queryClient.getQueryData< 527 + InfiniteData<BacklinksResponse> 528 + >(constellationKeys.savers); 519 529 520 530 queryClient.setQueryData<boolean>(constellationKeys.userSaved, !isSaved); 521 531 queryClient.setQueryData<number>(constellationKeys.saveCount, (old) => 522 532 isSaved ? Math.max(0, (old ?? 1) - 1) : (old ?? 0) + 1, 523 533 ); 524 534 535 + // Optimistically update savers list 536 + queryClient.setQueryData<InfiniteData<BacklinksResponse>>( 537 + constellationKeys.savers, 538 + (old) => { 539 + if (isSaved) { 540 + // Remove list from savers 541 + if (!old) return old; 542 + return { 543 + ...old, 544 + pages: old.pages.map((page, i) => 545 + i === 0 546 + ? { 547 + ...page, 548 + total: Math.max(0, page.total - 1), 549 + records: page.records.filter( 550 + (r) => !(r.did === did && r.rkey === rkey), 551 + ), 552 + } 553 + : page, 554 + ), 555 + }; 556 + } 557 + // Add list to savers - seed cache if empty 558 + const newRecord = { did, collection: COLLECTION_LIST_NSID, rkey }; 559 + if (!old) { 560 + return { 561 + pages: [{ records: [newRecord], total: 1 }], 562 + pageParams: [undefined], 563 + }; 564 + } 565 + return { 566 + ...old, 567 + pages: old.pages.map((page, i) => 568 + i === 0 569 + ? { 570 + ...page, 571 + total: page.total + 1, 572 + records: [newRecord, ...page.records], 573 + } 574 + : page, 575 + ), 576 + }; 577 + }, 578 + ); 579 + 525 580 return { 526 581 previousList, 527 582 previousLists, 528 583 previousSaved, 529 584 previousCount, 585 + previousSavers, 530 586 constellationKeys, 531 587 isSaved, 532 588 }; ··· 558 614 context.constellationKeys.saveCount, 559 615 context.previousCount, 560 616 ); 617 + if (context.previousSavers) { 618 + queryClient.setQueryData<InfiniteData<BacklinksResponse>>( 619 + context.constellationKeys.savers, 620 + context.previousSavers, 621 + ); 622 + } 561 623 }, 562 624 onSuccess: (data, { list, itemName, item }) => { 563 625 const what = itemName ?? (item.type === "card" ? "Card" : "Deck");
+3
src/lib/constellation-queries.ts
··· 224 224 saveCount: ["constellation", "saveCount", itemUri] as const, 225 225 userLiked: ["constellation", "userLiked", itemUri, userDid] as const, 226 226 likeCount: ["constellation", "likeCount", itemUri] as const, 227 + likers: ["constellation", "likers", itemUri] as const, 228 + savers: ["constellation", "savers", itemUri] as const, 229 + deckBacklinks: ["constellation", "deckBacklinks", itemUri] as const, 227 230 }; 228 231 } 229 232
+59 -1
src/lib/like-queries.ts
··· 3 3 */ 4 4 5 5 import type { ResourceUri } from "@atcute/lexicons"; 6 + import type { InfiniteData } from "@tanstack/react-query"; 6 7 import { useQueryClient } from "@tanstack/react-query"; 7 8 import { toast } from "sonner"; 8 9 import { createLikeRecord, deleteLikeRecord } from "./atproto-client"; 9 10 import type { SaveItem } from "./collection-list-types"; 11 + import { type BacklinksResponse, LIKE_NSID } from "./constellation-client"; 10 12 import { getConstellationQueryKeys } from "./constellation-queries"; 11 13 import type { ComDeckbelcherSocialLike } from "./lexicons/index"; 12 14 import type { OracleId, ScryfallId } from "./scryfall-types"; ··· 101 103 102 104 await queryClient.cancelQueries({ queryKey: keys.userLiked }); 103 105 await queryClient.cancelQueries({ queryKey: keys.likeCount }); 106 + await queryClient.cancelQueries({ queryKey: keys.likers }); 104 107 105 108 const previousLiked = queryClient.getQueryData<boolean>(keys.userLiked); 106 109 const previousCount = queryClient.getQueryData<number>(keys.likeCount); 110 + const previousLikers = queryClient.getQueryData< 111 + InfiniteData<BacklinksResponse> 112 + >(keys.likers); 107 113 108 114 queryClient.setQueryData<boolean>(keys.userLiked, !params.isLiked); 109 115 queryClient.setQueryData<number>(keys.likeCount, (old) => 110 116 params.isLiked ? Math.max(0, (old ?? 1) - 1) : (old ?? 0) + 1, 111 117 ); 112 118 113 - return { previousLiked, previousCount, keys }; 119 + // Optimistically update likers list 120 + if (userDid) { 121 + queryClient.setQueryData<InfiniteData<BacklinksResponse>>( 122 + keys.likers, 123 + (old) => { 124 + if (params.isLiked) { 125 + // Remove user from likers 126 + if (!old) return old; 127 + return { 128 + ...old, 129 + pages: old.pages.map((page, i) => 130 + i === 0 131 + ? { 132 + ...page, 133 + total: Math.max(0, page.total - 1), 134 + records: page.records.filter((r) => r.did !== userDid), 135 + } 136 + : page, 137 + ), 138 + }; 139 + } 140 + // Add user to likers - seed cache if empty 141 + // WARN: rkey unknown until mutation completes 142 + const newRecord = { did: userDid, collection: LIKE_NSID, rkey: "" }; 143 + if (!old) { 144 + return { 145 + pages: [{ records: [newRecord], total: 1 }], 146 + pageParams: [undefined], 147 + }; 148 + } 149 + return { 150 + ...old, 151 + pages: old.pages.map((page, i) => 152 + i === 0 153 + ? { 154 + ...page, 155 + total: page.total + 1, 156 + records: [newRecord, ...page.records], 157 + } 158 + : page, 159 + ), 160 + }; 161 + }, 162 + ); 163 + } 164 + 165 + return { previousLiked, previousCount, previousLikers, keys }; 114 166 }, 115 167 onError: (_err, _params, context) => { 116 168 if (!context) return; ··· 123 175 context.keys.likeCount, 124 176 context.previousCount, 125 177 ); 178 + if (context.previousLikers) { 179 + queryClient.setQueryData<InfiniteData<BacklinksResponse>>( 180 + context.keys.likers, 181 + context.previousLikers, 182 + ); 183 + } 126 184 }, 127 185 onSuccess: (data, params) => { 128 186 const what =