chat
Gitea Actions Demo / Deploy (push) Successful in 36s

This commit is contained in:
vadim committed 2026-10-02 21:43:19 +03:00
1 parent cdd7147d8e
commit b80e88b121
9 files changed
+660 -12

No files matched your search

+153
View File
@@ -0,0 +1,153 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
type Comment,
type CommentEventType,
type CreateCommentInput,
itemCreate,
itemDelete,
itemsList,
itemUpdate,
type UpdateCommentInput,
} from '@/api/comment.api.ts'
const PAGE_SIZE = 20
/**
* Comments of the currently opened issue, oldest first. Loaded chat-style:
* the latest page first, older pages are prepended on demand.
*/
export const useCommentStore = defineStore('comment', () => {
const issueId = ref<number | null>(null)
const comments = ref<Comment[]>([])
/** id of the oldest loaded comment while older ones exist on the server */
const olderCursor = ref<string | null>(null)
const loading = ref(false)
const loadingOlder = ref(false)
const error = ref<string | null>(null)
/** Inserts or replaces a comment by id. New comments are always the newest — append. */
function upsert(comment: Comment) {
const index = comments.value.findIndex((c) => c.id === comment.id)
if (index !== -1) comments.value[index] = comment
else comments.value.push(comment)
}
async function load(id: number) {
issueId.value = id
comments.value = []
olderCursor.value = null
loadingOlder.value = false
loading.value = true
try {
const res = await itemsList(id, { limit: PAGE_SIZE, direction: 'backward' })
// The issue may have changed while the request was in flight
if (issueId.value !== id) return
comments.value = res.list
olderCursor.value = res.nextCursor
} catch (e) {
error.value = e instanceof Error ? e.message : 'error get list of comments'
} finally {
if (issueId.value === id) loading.value = false
}
}
async function loadOlder() {
const id = issueId.value
if (id === null || !olderCursor.value || loading.value || loadingOlder.value) return
loadingOlder.value = true
try {
const res = await itemsList(id, { limit: PAGE_SIZE, cursor: olderCursor.value, direction: 'backward' })
if (issueId.value !== id) return
const known = new Set(comments.value.map((c) => c.id))
comments.value.unshift(...res.list.filter((c) => !known.has(c.id)))
olderCursor.value = res.nextCursor
} catch (e) {
error.value = e instanceof Error ? e.message : 'error get list of comments'
} finally {
if (issueId.value === id) loadingOlder.value = false
}
}
/** Fetches comments newer than the last loaded one — e.g. after an SSE reconnect. */
async function loadNewer() {
const id = issueId.value
if (id === null || loading.value) return
let cursor = comments.value.at(-1)?.id
if (!cursor) return load(id)
try {
do {
const res = await itemsList(id, { limit: PAGE_SIZE, cursor, direction: 'forward' })
if (issueId.value !== id) return
res.list.forEach(upsert)
cursor = res.nextCursor ?? undefined
} while (cursor)
} catch (e) {
error.value = e instanceof Error ? e.message : 'error get list of comments'
}
}
async function create(input: CreateCommentInput) {
if (issueId.value === null) return
try {
const comment = await itemCreate(issueId.value, input)
// The SSE event for the same comment may come before or after — upsert dedupes
if (comment.issueId === issueId.value) upsert(comment)
return comment
} catch (e) {
error.value = e instanceof Error ? e.message : 'error creating comment'
}
}
async function update(id: string, input: UpdateCommentInput) {
try {
const comment = await itemUpdate(id, input)
if (comment.issueId === issueId.value) upsert(comment)
return comment
} catch (e) {
error.value = e instanceof Error ? e.message : 'error updating comment'
}
}
async function remove(id: string) {
try {
return await itemDelete(id)
} catch (e) {
error.value = e instanceof Error ? e.message : 'error deleting comment'
return false
}
}
/** Applies a server-sent event. Deleted comments stay in the list as placeholders. */
function applyEvent(type: CommentEventType, comment: Comment) {
if (comment.issueId !== issueId.value) return
// Updates of comments older than the loaded window are irrelevant
if (type !== 'comment.created' && !comments.value.some((c) => c.id === comment.id)) return
upsert(comment)
}
function reset() {
issueId.value = null
comments.value = []
olderCursor.value = null
loading.value = false
loadingOlder.value = false
}
return {
issueId,
comments,
olderCursor,
loading,
loadingOlder,
error,
load,
loadOlder,
loadNewer,
create,
update,
remove,
applyEvent,
reset,
}
})