import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import * as api from '@/api/index.js'
export const useSessionsStore = defineStore('sessions', () => {
const pageSize = 30
const sessions = ref([])
const loading = ref(false)
const loadingMore = ref(false)
const hasMore = ref(true)
const nextOffset = ref(0)
const currentProfileId = ref(null)
const searchQuery = ref('')
const searchActive = ref(false)
const searchCache = ref(null)
const hasPendingRecallFilter = ref(false)
async function fetchSessions(profileId = null) {
currentProfileId.value = profileId
loading.value = true
try {
const page = await api.getSessions({
limit: pageSize,
offset: 0,
profileId: searchActive.value ? null : profileId,
search: searchActive.value ? searchQuery.value : null,
})
const items = Array.isArray(page) ? page : page.items
sessions.value = items
hasMore.value = Array.isArray(page) ? false : page.has_more
nextOffset.value = Array.isArray(page) ? items.length : page.next_offset
if (searchActive.value) {
searchCache.value = {
query: searchQuery.value,
items: [...items],
hasMore: hasMore.value,
nextOffset: nextOffset.value
}
}
} finally {
loading.value = false
}
}
async function fetchMoreSessions() {
if (loading.value || loadingMore.value || !hasMore.value) return
loadingMore.value = true
try {
const page = await api.getSessions({
limit: pageSize,
offset: nextOffset.value,
profileId: searchActive.value ? null : currentProfileId.value,
search: searchActive.value ? searchQuery.value : null,
})
const items = Array.isArray(page) ? page : page.items
const known = new Set(sessions.value.map(s => s.session_id))
sessions.value = [
...sessions.value,
...items.filter(s => !known.has(s.session_id)),
]
hasMore.value = Array.isArray(page) ? false : page.has_more
nextOffset.value = Array.isArray(page) ? sessions.value.length : page.next_offset
if (searchActive.value) {
searchCache.value = {
query: searchQuery.value,
items: [...sessions.value],
hasMore: hasMore.value,
nextOffset: nextOffset.value
}
}
} finally {
loadingMore.value = false
}
}
function setSearch(query) {
searchQuery.value = query
searchActive.value = query.length > 0
}
function restoreSearch() {
if (!searchQuery.value) return
searchActive.value = true
if (searchCache.value && searchCache.value.query === searchQuery.value) {
sessions.value = searchCache.value.items
hasMore.value = searchCache.value.hasMore
nextOffset.value = searchCache.value.nextOffset
} else {
fetchSessions()
}
}
function clearSearch() {
searchQuery.value = ''
searchActive.value = false
searchCache.value = null
}
function exitSearch() {
searchActive.value = false
}
async function createSession(profileId) {
const session = await api.createSession(profileId)
// Add a complete placeholder so SessionItem renders correctly immediately
sessions.value.unshift({
session_id: session.session_id,
profile_id: session.profile_id,
created_at: session.created_at,
last_active: session.created_at,
message_count: 0,
preview: '',
pinned: false
})
nextOffset.value += 1
return session
}
async function deleteSession(id) {
await api.deleteSession(id)
// Replace array reference — RecycleScroller reacts to reference changes
sessions.value = sessions.value.filter(s => s.session_id !== id)
nextOffset.value = Math.max(0, nextOffset.value - 1)
}
async function pinSession(id, pinned) {
await api.pinSession(id, pinned)
// Replace array: update item + re-sort pinned to top
const updated = sessions.value.map(s =>
s.session_id === id ? { ...s, pinned } : s
)
updated.sort((a, b) => (b.pinned ? 1 : 0) - (a.pinned ? 1 : 0))
sessions.value = updated
}
function updateRecallStatus(id, hasRecall) {
const session = sessions.value.find(s => s.session_id === id)
if (session) session.has_pending_recall = hasRecall
}
function updatePreview(id, preview) {
const session = sessions.value.find(s => s.session_id === id)
if (session) session.preview = preview
}
function updateName(id, name) {
const session = sessions.value.find(s => s.session_id === id)
if (session) session.name = name
}
const filteredSessions = computed(() => {
if (!hasPendingRecallFilter.value) return sessions.value
return sessions.value.filter(s => s.has_pending_recall)
})
return {
sessions,
filteredSessions,
loading,
loadingMore,
hasMore,
currentProfileId,
searchQuery,
searchActive,
hasPendingRecallFilter,
fetchSessions,
fetchMoreSessions,
createSession,
deleteSession,
pinSession,
updatePreview,
updateName,
updateRecallStatus,
setSearch,
restoreSearch,
clearSearch,
exitSearch,
searchCache
}
})