Read-it-later social network
12
fork

Configure Feed

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

implement subscribe button

authored by

zeudev and committed by tangled.org 663a6e98 0b4c47ae

+79 -17
+71 -16
src/lib/components/PublicationCard.svelte
··· 1 1 <script lang="ts"> 2 2 import { getContext } from "svelte"; 3 3 import { createQuery } from "@tanstack/svelte-query"; 4 - import { resolveHandle, type MiniDoc, type PublicationNode, type StandardSiteThemeColorRGB } from "$lib/utils"; 5 4 import type { QuicksliceClient } from "quickslice-client-js"; 5 + import { parseAtUri, resolveHandle, type MiniDoc, type PublicationNode } from "$lib/utils"; 6 6 7 7 const user = getContext("user") as MiniDoc; 8 8 const atclient = getContext("atclient") as QuicksliceClient; 9 9 10 10 let { publication, showEmpty = false }: { publication: PublicationNode, showEmpty?: boolean } = $props(); 11 11 12 + let disableSubscribeButton = $state(false); 12 13 let isSubscribeButtonHovered = $state(false); 13 14 14 15 const miniDocQuery = createQuery(() => ({ ··· 40 41 const subscribers = Number(data.links["site.standard.graph.subscription"]?.[".publication"]?.records) || 0; 41 42 42 43 return { documents, subscribers } 43 - } 44 + }, 44 45 })); 45 46 46 - const isSubscribedQuery = createQuery(() => ({ 47 + const subscriptionQuery = createQuery(() => ({ 47 48 queryKey: ["isSubscribed", publication.uri, user.did], 48 49 queryFn: async () => { 49 50 const constellationUrl = new URL("https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks"); ··· 57 58 }); 58 59 59 60 60 - const json = await response.json() as { total: number }; 61 + const json = await response.json() as { records: { did: string, collection: string, rkey: string }[] }; 61 62 return json; 62 63 }, 64 + select: (data) => data.records[0] && data.records[0].rkey 63 65 })); 64 66 65 67 let documents = $derived(countQuery.data?.documents || 0); 66 68 let subscribers = $derived(countQuery.data?.subscribers || 0); 67 - let isSubscribed = $derived((isSubscribedQuery.data?.total ?? 0) === 1); 69 + let subscriptionRkey = $derived(subscriptionQuery.data); 68 70 let blobSyncUrl = $derived((`${miniDocQuery.data?.pds}/xrpc/com.atproto.sync.getBlob?did=${publication.did}&cid=${publication.value.icon?.ref.$link}`)); 69 71 const theme = publication.value.basicTheme || { 70 72 $type: "site.standard.theme.basic", ··· 94 96 }, 95 97 }; 96 98 97 - // TODO: update with `site.standard.graph.subscription` create or delete on click with auth 98 - function toggleSubscribe() { 99 - const past = isSubscribed; 100 - isSubscribed = !isSubscribed; 101 - if (subscribers) { 102 - if (past) { 103 - subscribers--; 99 + async function toggleSubscribe() { 100 + disableSubscribeButton = true; 101 + 102 + const pastRkey = subscriptionRkey; 103 + if (pastRkey) { 104 + subscribers--; 105 + subscriptionRkey = undefined; 106 + } 107 + else { 108 + subscribers++; 109 + subscriptionRkey = "placeholder_rkey"; 110 + } 111 + 112 + try { 113 + if (pastRkey) { 114 + const mutation = ` 115 + mutation { 116 + deleteSiteStandardGraphSubscription(rkey: "${pastRkey}") { 117 + uri 118 + } 119 + } 120 + `; 121 + await atclient.mutate(mutation) as { createSiteStandardGraphSubscription: { uri: string }}; 122 + subscriptionRkey = undefined; 104 123 } 105 124 else { 125 + const mutation = ` 126 + mutation { 127 + createSiteStandardGraphSubscription(input: { 128 + publication: "${publication.uri}" 129 + }) { 130 + uri 131 + } 132 + } 133 + `; 134 + const result = await atclient.mutate(mutation) as { createSiteStandardGraphSubscription: { uri: string }}; 135 + const { rkey } = parseAtUri(result.createSiteStandardGraphSubscription.uri); 136 + subscriptionRkey = rkey; 137 + } 138 + 139 + disableSubscribeButton = false; 140 + } 141 + catch (e) { 142 + console.log(e); 143 + // rollback initial changes 144 + if (pastRkey) { 106 145 subscribers++; 146 + subscriptionRkey = pastRkey; 107 147 } 148 + else { 149 + subscribers--; 150 + subscriptionRkey = undefined; 151 + } 152 + 153 + disableSubscribeButton = false; 108 154 } 109 155 } 110 156 </script> ··· 159 205 </div> 160 206 <button 161 207 onclick={toggleSubscribe} 208 + disabled={disableSubscribeButton} 162 209 onmouseenter={() => isSubscribeButtonHovered = true} 163 210 onmouseleave={() => isSubscribeButtonHovered = false} 164 - class={["flex flex-1 flex-col items-center justify-center gap-1 p-4 hover:cursor-pointer transition-all duration-150 hover:bg-green-500", isSubscribed && "bg-green-500 hover:bg-red-400"]}> 165 - <span class="text-2xl font-bold"> 211 + class={[ 212 + "flex flex-1 flex-col items-center justify-center gap-1 p-4 hover:cursor-pointer transition-all duration-150 hover:bg-green-500", 213 + subscriptionRkey && "bg-green-500 hover:bg-red-400" 214 + ]} 215 + > 216 + <span class="gap-[0.5rem] text-2xl font-bold"> 166 217 {subscribers} 167 218 </span> 168 - <span class="text-xs uppercase tracking-wide"> 169 - {#if isSubscribed} 219 + <span class="text-xs uppercase tracking-wide flex"> 220 + {#if subscriptionRkey} 170 221 {#if isSubscribeButtonHovered} 171 222 Unsubscribe? 172 223 {:else} ··· 178 229 {:else} 179 230 Subscribers 180 231 {/if} 232 + {/if} 233 + 234 + {#if disableSubscribeButton} 235 + <p class="animate-spin">◝</p> 181 236 {/if} 182 237 </span> 183 238 </button>
+7
src/lib/utils.ts
··· 9 9 } 10 10 } 11 11 12 + export function createAtUri({ did, collection, rkey }: { did: string, collection: string, rkey: string }) { 13 + if (did && collection && rkey) { 14 + return `at://${did}/${collection}/${rkey}`; 15 + } 16 + return undefined; 17 + } 18 + 12 19 export type MiniDoc = { 13 20 did: string; 14 21 handle: string;
+1 -1
src/routes/+page.svelte
··· 1 1 <script lang="ts"> 2 - import PublicationCard from '$lib/components/PublicationCard.svelte'; 3 2 import type { PublicationNode } from '$lib/utils'; 4 3 import { createInfiniteQuery } from '@tanstack/svelte-query'; 4 + import PublicationCard from '$lib/components/PublicationCard.svelte'; 5 5 6 6 let { data } = $props(); 7 7 let { atclient, user } = data;