feat(journal): live AI status updates in entry cards via Postgres NOTIFY trigger + SSE ai_status events
This commit is contained in:
+32
-4
@@ -66,7 +66,7 @@ function aiBadge(e) {
|
||||
const st = e.ai_status;
|
||||
const label = AI_STATUS_LABELS[st] || `ИИ: ${st}`;
|
||||
const icon = AI_STATUS_ICONS[st] || 'sparkles';
|
||||
return `<span class="ai-badge ai-badge-ic ai-badge-${esc(st)}" title="${esc(label)}" role="img" aria-label="${esc(label)}"><i data-lucide="${icon}"></i></span>`;
|
||||
return `<span class="ai-badge ai-badge-ic ai-badge-${esc(st)}" data-ai-badge="${e.id}" title="${esc(label)}" role="img" aria-label="${esc(label)}"><i data-lucide="${icon}"></i></span>`;
|
||||
}
|
||||
|
||||
function findFile(id) {
|
||||
@@ -236,6 +236,27 @@ function goPage(p) {
|
||||
|
||||
let liveSource = null;
|
||||
let liveReconnectTimer = null;
|
||||
let aiRefreshTimer = null;
|
||||
let entriesRefreshTimer = null;
|
||||
function applyAiUpdate(update) {
|
||||
const e = currentEntries.find(x => x.id === update.id);
|
||||
if (!e) return;
|
||||
e.ai_status = update.ai_status;
|
||||
e.ai_error = update.ai_error || null;
|
||||
if (update.ai_status === 'done') {
|
||||
if (aiRefreshTimer) return;
|
||||
aiRefreshTimer = setTimeout(() => { aiRefreshTimer = null; loadEntries(); }, 300);
|
||||
return;
|
||||
}
|
||||
document.querySelectorAll(`[data-ai-badge="${update.id}"]`).forEach(badgeEl => {
|
||||
badgeEl.outerHTML = aiBadge(e);
|
||||
});
|
||||
renderIcons();
|
||||
const editBadge = document.getElementById('editAiStatus');
|
||||
if (editId === update.id && editBadge && editBadge.style.display !== 'none') {
|
||||
renderEditAiStatus(e);
|
||||
}
|
||||
}
|
||||
function connectLiveUpdates() {
|
||||
if (liveSource) { liveSource.close(); liveSource = null; }
|
||||
const connect = () => {
|
||||
@@ -243,9 +264,16 @@ function connectLiveUpdates() {
|
||||
if (!t) return;
|
||||
liveSource = new EventSource(`${API}/api/events?token=${encodeURIComponent(t)}`);
|
||||
liveSource.addEventListener('entries_changed', () => {
|
||||
showToast('Поступила новая запись — список обновлён');
|
||||
page = 1;
|
||||
loadEntries();
|
||||
if (entriesRefreshTimer) return;
|
||||
entriesRefreshTimer = setTimeout(() => {
|
||||
entriesRefreshTimer = null;
|
||||
showToast('Поступила новая запись — список обновлён');
|
||||
page = 1;
|
||||
loadEntries();
|
||||
}, 300);
|
||||
});
|
||||
liveSource.addEventListener('ai_status', ev => {
|
||||
try { applyAiUpdate(JSON.parse(ev.data)); } catch (err) {}
|
||||
});
|
||||
liveSource.onerror = () => {
|
||||
if (liveSource) { liveSource.close(); liveSource = null; }
|
||||
|
||||
Reference in New Issue
Block a user