import { defineStore } from 'pinia' import { ref } from 'vue' import { useApi } from '../composables/useApi.js' import { useAsyncAction } from '../composables/useAsyncAction.js' export const usePostsStore = defineStore('posts', () => { const api = useApi() const items = ref([]) const cursor = ref(null) const { loading, error, run } = useAsyncAction() const done = ref(false) const filters = ref({ artist_id: null, platform: null }) // In-context (anchored) view: bidirectional cursors so the feed can load // newer posts on scroll-up and older posts on scroll-down around a post. const cursorOlder = ref(null) const cursorNewer = ref(null) const doneOlder = ref(false) const doneNewer = ref(false) const anchorId = ref(null) function _qs() { const q = {} if (cursor.value) q.cursor = cursor.value if (filters.value.artist_id != null) q.artist_id = filters.value.artist_id if (filters.value.platform) q.platform = filters.value.platform return q } function _reset() { items.value = [] cursor.value = null done.value = false error.value = null } async function loadInitial(newFilters) { filters.value = { artist_id: newFilters?.artist_id ?? null, platform: newFilters?.platform ?? null, } _reset() await loadMore() } async function loadMore() { if (loading.value || done.value) return await run(async () => { const body = await api.get('/api/posts', { params: _qs() }) items.value.push(...body.items) cursor.value = body.next_cursor if (body.next_cursor == null) done.value = true }) } async function getPostFull(id) { // Used by PostCard's "Show more" expand to fetch the full description. return await api.get(`/api/posts/${id}`) } // Filter overlay for the around/older/newer (in-context anchored) // path. Keep this distinct from `filters.value` (the down-only feed) // so a normal-feed filter change doesn't leak into an active anchored // view (or vice versa). Caller of loadAround passes the snapshot; the // subsequent loadOlder/loadNewer use it verbatim. function _aroundParams(extra) { const p = { ...extra } if (filters.value.artist_id != null) p.artist_id = filters.value.artist_id if (filters.value.platform) p.platform = filters.value.platform return p } // Load a window centered on `postId`: newer posts above, the post, older // posts below. Sets both directional cursors for subsequent scrolling. // Accepts the same filter shape as loadInitial so the anchored view // stays artist/platform-scoped (operator-flagged 2026-06-01: clicking a // post title from the modal's Provenance card opens the post in the // posts feed; without this the older/newer scroll loaded unfiltered // global posts instead of staying in the artist's stream). async function loadAround(postId, newFilters) { filters.value = { artist_id: newFilters?.artist_id ?? null, platform: newFilters?.platform ?? null, } loading.value = true error.value = null anchorId.value = null try { const body = await api.get('/api/posts', { params: _aroundParams({ around: postId }), }) items.value = body.items cursorOlder.value = body.cursor_older cursorNewer.value = body.cursor_newer doneOlder.value = body.cursor_older == null doneNewer.value = body.cursor_newer == null anchorId.value = body.anchor_id } catch (e) { error.value = e } finally { loading.value = false } } async function loadOlder() { if (loading.value || doneOlder.value || cursorOlder.value == null) return loading.value = true try { const body = await api.get('/api/posts', { params: _aroundParams({ cursor: cursorOlder.value, direction: 'older', }), }) items.value.push(...body.items) cursorOlder.value = body.next_cursor if (body.next_cursor == null) doneOlder.value = true } catch (e) { error.value = e } finally { loading.value = false } } async function loadNewer() { if (loading.value || doneNewer.value || cursorNewer.value == null) return loading.value = true try { const body = await api.get('/api/posts', { params: _aroundParams({ cursor: cursorNewer.value, direction: 'newer', }), }) items.value.unshift(...body.items) cursorNewer.value = body.next_cursor if (body.next_cursor == null) doneNewer.value = true } catch (e) { error.value = e } finally { loading.value = false } } return { items, cursor, loading, done, error, filters, cursorOlder, cursorNewer, doneOlder, doneNewer, anchorId, loadInitial, loadMore, getPostFull, loadAround, loadOlder, loadNewer, } })