2026-05-25 13:35:39 +08:00
|
|
|
const STORAGE_KEY = 'x-financial.documents.viewed'
|
|
|
|
|
const SCOPE_STORAGE_KEY = 'x-financial.documents.scope'
|
2026-05-26 09:15:14 +08:00
|
|
|
export const DOCUMENT_VIEWED_KEYS_CHANGE_EVENT = 'x-financial.documents.viewed-change'
|
2026-05-25 13:35:39 +08:00
|
|
|
|
|
|
|
|
function getStorage() {
|
|
|
|
|
return typeof window === 'undefined' ? null : window.localStorage
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolveDocumentNewKey(row) {
|
|
|
|
|
const source = String(row?.source || 'document').trim()
|
|
|
|
|
const id = String(row?.claimId || row?.documentNo || row?.documentKey || row?.id || '').trim()
|
|
|
|
|
return id ? `${source}:${id}` : ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function readViewedDocumentKeys(storage = getStorage()) {
|
|
|
|
|
if (!storage) {
|
|
|
|
|
return new Set()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(storage.getItem(STORAGE_KEY) || '[]')
|
|
|
|
|
return new Set(Array.isArray(parsed) ? parsed.map((item) => String(item || '').trim()).filter(Boolean) : [])
|
|
|
|
|
} catch {
|
|
|
|
|
return new Set()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function writeViewedDocumentKeys(keys, storage = getStorage()) {
|
|
|
|
|
if (!storage) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
storage.setItem(STORAGE_KEY, JSON.stringify(Array.from(keys).filter(Boolean)))
|
2026-05-26 09:15:14 +08:00
|
|
|
|
|
|
|
|
if (typeof window !== 'undefined' && storage === window.localStorage) {
|
|
|
|
|
window.dispatchEvent(new CustomEvent(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT))
|
|
|
|
|
}
|
2026-05-25 13:35:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function readDocumentScope(fallback, allowedScopes = [], storage = getStorage()) {
|
|
|
|
|
if (!storage) {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const storedScope = String(storage.getItem(SCOPE_STORAGE_KEY) || '').trim()
|
|
|
|
|
return allowedScopes.includes(storedScope) ? storedScope : fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function writeDocumentScope(scope, allowedScopes = [], storage = getStorage()) {
|
|
|
|
|
if (!storage || !allowedScopes.includes(scope)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
storage.setItem(SCOPE_STORAGE_KEY, scope)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isNewDocument(row, viewedKeys) {
|
2026-06-02 14:01:51 +08:00
|
|
|
if (row?.isNewDocument === false || row?.archived === true || String(row?.source || '').trim() === 'archive') {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2026-05-25 13:35:39 +08:00
|
|
|
const key = resolveDocumentNewKey(row)
|
|
|
|
|
return Boolean(key) && !viewedKeys.has(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function countNewDocuments(rows, viewedKeys) {
|
|
|
|
|
return rows.filter((row) => isNewDocument(row, viewedKeys)).length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function markDocumentViewed(row, viewedKeys, storage = getStorage()) {
|
|
|
|
|
const key = resolveDocumentNewKey(row)
|
|
|
|
|
if (!key) {
|
|
|
|
|
return viewedKeys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const nextKeys = new Set(viewedKeys)
|
|
|
|
|
nextKeys.add(key)
|
|
|
|
|
writeViewedDocumentKeys(nextKeys, storage)
|
|
|
|
|
return nextKeys
|
|
|
|
|
}
|
2026-06-03 16:46:13 +08:00
|
|
|
|
|
|
|
|
export function markDocumentsViewed(rows, viewedKeys, storage = getStorage()) {
|
|
|
|
|
const nextKeys = new Set(viewedKeys)
|
|
|
|
|
let changed = false
|
|
|
|
|
|
|
|
|
|
;(Array.isArray(rows) ? rows : []).forEach((row) => {
|
|
|
|
|
if (!isNewDocument(row, nextKeys)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const key = resolveDocumentNewKey(row)
|
|
|
|
|
if (key) {
|
|
|
|
|
nextKeys.add(key)
|
|
|
|
|
changed = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
writeViewedDocumentKeys(nextKeys, storage)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nextKeys
|
|
|
|
|
}
|