Newer
Older
navi-1 / webclient / src / stores / sessions.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy on 15 May 4 KB Add self-recall (scheduled callback) system
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
  }

  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
  }
})