bluesky client without react native baggage written in sveltekit
Something went wrong. Try again.
4.3 kB · 144 lines
Svelte
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145<script lang="ts"> import RichText from './RichText.svelte'; import Avatar from './Avatar.svelte'; import { getClient } from '$lib/atproto'; import { getUserContext } from '$lib/context'; import type { PostView } from '@atcute/bluesky/types/app/feed/defs';
let { post }: { post: PostView } = $props(); const user = getUserContext(); let liked = $state(!!post.viewer?.like); let reposted = $state(!!post.viewer?.repost); let likeCount = $derived(post.viewer?.like ? (post.likeCount ?? 0) - 1 : post.likeCount); let repostCount = $derived(post.viewer?.repost ? (post.repostCount ?? 0) - 1 : post.repostCount); const isAuthenticated = !!user.profile?.did;
async function likePost() { liked = true; const client = await getClient();
if (!user.profile?.did) { liked = false; throw new Error('you must be authenticated to do this action'); }
const { ok } = await client.post('com.atproto.repo.createRecord', { input: { collection: 'app.bsky.feed.like', record: { $type: 'app.bsky.feed.like', createdAt: new Date().toISOString(), subject: { cid: post.cid, uri: post.uri } }, repo: user.profile.did } });
if (!ok) { liked = false; throw new Error('failed to like the post'); } }
async function unlikePost() { liked = false; const client = await getClient();
if (!user.profile?.did) { liked = true; throw new Error('you must be authenticated to do this action (how did you even like this)'); } const rkey = post.uri.split('/').at(-1); if (!rkey) { liked = true; throw new Error("couldn't properly extract rkey"); } const { ok } = await client.post('com.atproto.repo.deleteRecord', { input: { collection: 'app.bsky.feed.like', rkey, repo: user.profile.did } });
if (!ok) { liked = true; throw new Error('failed to unlike the post'); } console.log('liked post!'); }</script>
<article class="flex border-x border-b border-post-border bg-body-background pt-2 pr-4 pb-2 pl-2.5 hover:bg-item-hover"> <div class="mr-2.5 ml-2 shrink-0"> <Avatar user={post.author} /> </div> <div> <div class="mb-1"> <a href="#"> <b>{post.author.displayName || post.author.handle}</b> <span class="text-secondary-text">@{post.author.handle}</span> </a> </div> <RichText text={post.record.text} facets={post.record.facets} /> {#if post.embed} {#if post.embed.$type === 'app.bsky.embed.images#view'} {#each post.embed.images as image (image.thumb)} <img class="aspect-[1.23151 / 1] my-2 rounded-xl" src={image.thumb} alt={image.alt} /> {/each} {/if} {/if} <div class="mt-0.5 flex w-full"> <div class="flex w-[320px] max-w-[320px] justify-between"> <div class="grow"> <button class="flex items-center gap-1 py-1.25 pr-1.25" ><span class="text-4.5 icon-[boxicons--message-reply] h-4.5 w-4.5"></span> {post.replyCount}</button > </div> {#if reposted} <div class="flex grow items-center gap-1"> <span class="text-4.5 icon-[mdi--repost] h-4.5 w-4.5 text-green-500"></span> {(repostCount ?? 0) + 1} </div> {:else} <div class="flex grow items-center gap-1"> <span class="text-4.5 icon-[mdi--repost] h-4.5 w-4.5"></span> {repostCount} </div> {/if} {#if !isAuthenticated} <div class="flex grow items-center gap-1"> <span class="text-4.5 icon-[icon-park-solid--like] h-4.5 w-4.5 text-gray-400"></span> <span aria-hidden={true}>{likeCount}</span> </div> {:else if liked} <button aria-label={`Unlike this post, {likeCount+1} likes`} aria-pressed={true} class="flex grow items-center gap-1 hover:cursor-pointer" onclick={unlikePost} > <span class="text-4.5 icon-[icon-park-solid--like] h-4.5 w-4.5 text-red-500"></span> <span aria-hidden={true}>{(likeCount ?? 0) + 1}</span> </button> {:else} <button aria-label={`Like this post, {likeCount} likes`} aria-pressed={false} class="flex grow items-center gap-1 hover:cursor-pointer" onclick={likePost} > <span class="text-4.5 icon-[icon-park-outline--like] h-4.5 w-4.5"></span> <span aria-hidden={true}>{likeCount}</span> </button> {/if} </div> </div> </div></article>