import { defineStore } from 'pinia' import { shallowRef, reactive } from 'vue' import type { ComAtprotoRepoStrongRef } from '@atcute/atproto' import { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/bluesky' import { ok } from '@atcute/client' import { useAuthStore } from './auth' type PostView = AppBskyFeedDefs.PostView export const usePostStore = defineStore('posts', () => { const auth = useAuthStore() const posts = shallowRef(new Map()) function mergePost(post: PostView): PostView { const existing = posts.value.get(post.uri) if (existing) { Object.assign(existing, post) return existing } else { const reactivePost = reactive(post) posts.value.set(post.uri, reactivePost) return reactivePost } } async function toggleLike(post: PostView) { if (!auth.isAuthenticated) return if (!auth.session) return const uri = post.uri const cid = post.cid const originalLike = post.viewer?.like const originalCount = post.likeCount || 0 if (!post.viewer) post.viewer = {} if (originalLike) { post.viewer.like = undefined post.likeCount = originalCount - 1 } else { post.viewer.like = 'at://did:plc:pending/like' post.likeCount = originalCount + 1 } try { const rpc = auth.getRpc() if (originalLike) { await rpc.post('com.atproto.repo.deleteRecord', { input: { collection: 'app.bsky.feed.like', repo: auth.session?.info.sub, rkey: originalLike.split('/').pop()!, }, }) } else { const data = ok( await rpc.post('com.atproto.repo.createRecord', { input: { collection: 'app.bsky.feed.like', repo: auth.session?.info.sub, record: { $type: 'app.bsky.feed.like', subject: { uri, cid }, createdAt: new Date().toISOString(), }, }, }), ) const storedPost = posts.value.get(uri) if (storedPost && storedPost.viewer) { storedPost.viewer.like = data.uri } } } catch (err) { console.error('Failed to toggle like', err) const storedPost = posts.value.get(uri) if (storedPost && storedPost.viewer) { storedPost.viewer.like = originalLike storedPost.likeCount = originalCount } } } async function toggleBookmark(post: PostView, silent = false) { if (!auth.isAuthenticated) return if (!auth.session) return const uri = post.uri const cid = post.cid const originalBookmarked = post.viewer?.bookmarked const originalCount = post.bookmarkCount || 0 if (!post.viewer) post.viewer = {} if (originalBookmarked) { post.viewer.bookmarked = false post.bookmarkCount = originalCount - 1 } else { post.viewer.bookmarked = true post.bookmarkCount = originalCount + 1 } try { const rpc = auth.getRpc() if (originalBookmarked) { const res = await rpc.post('app.bsky.bookmark.deleteBookmark', { input: { uri: uri, }, as: null, }) } else { const res = await rpc.post('app.bsky.bookmark.createBookmark', { input: { cid: cid, uri: uri, }, as: null, }) } } catch (err) { console.error('Failed to toggle bookmark', err) const storedPost = posts.value.get(uri) if (storedPost && storedPost.viewer) { storedPost.viewer.bookmarked = originalBookmarked storedPost.bookmarkCount = originalCount } } } async function toggleRepost(post: PostView) { if (!auth.isAuthenticated) return const uri = post.uri const cid = post.cid const originalRepost = post.viewer?.repost const originalCount = post.repostCount || 0 if (!post.viewer) post.viewer = {} if (originalRepost) { post.viewer.repost = undefined post.repostCount = originalCount - 1 } else { post.viewer.repost = 'at://did:plc:pending/repost' post.repostCount = originalCount + 1 } try { const rpc = auth.getRpc() if (originalRepost) { await ok( rpc.post('com.atproto.repo.deleteRecord', { input: { collection: 'app.bsky.feed.repost', repo: auth.session!.info.sub, rkey: originalRepost.split('/').pop()!, }, }), ) } else { const data = ok( await rpc.post('com.atproto.repo.createRecord', { input: { collection: 'app.bsky.feed.repost', repo: auth.session!.info.sub, record: { $type: 'app.bsky.feed.repost', subject: { uri, cid }, createdAt: new Date().toISOString(), }, }, }), ) const storedPost = posts.value.get(uri) if (storedPost && storedPost.viewer) { storedPost.viewer.repost = data.uri } } } catch (err) { console.error('Failed to toggle repost', err) const storedPost = posts.value.get(uri) if (storedPost && storedPost.viewer) { storedPost.viewer.repost = originalRepost storedPost.repostCount = originalCount } } } async function createPost(args: { text: string embeds?: AppBskyFeedPost.Main['embed'] reply?: { parent: ComAtprotoRepoStrongRef.Main root: ComAtprotoRepoStrongRef.Main } }) { const { reply, text, embeds } = args if (!auth.isAuthenticated || !auth.session) throw new Error('Not authenticated') const rpc = auth.getRpc() const record: AppBskyFeedPost.Main = { $type: 'app.bsky.feed.post', text, createdAt: new Date().toISOString(), embed: embeds, reply: reply, } const data = await ok( rpc.post('com.atproto.repo.createRecord', { input: { collection: 'app.bsky.feed.post', repo: auth.session.info.sub, record, }, }), ) return data } return { posts, mergePost, createPost, toggleLike, toggleBookmark, toggleRepost, } })