// What a person has read in the Reader: which chapters (how many times, and when last) and on which days. // Merging two copies never loses anything, so two devices can both add to it: each chapter keeps the larger // count and the later date, and the days are combined. The data comes from the browser, so it is rebuilt field by field. const KEY = /^[1-3A-Z]{3}\.\d{1,3}$/; const DATE = /^\d{4}-\d{2}-\d{2}$/; const MAX_DAYS = 5000; export const emptyHistory = () => ({ chapters: {}, days: [] }); export function cleanHistory(raw) { const chapters = {}; if (raw?.chapters && typeof raw.chapters === 'object' && !Array.isArray(raw.chapters)) { for (const [key, v] of Object.entries(raw.chapters).slice(0, 1300)) { if (!KEY.test(key) || !v || typeof v !== 'object') continue; const n = Number.isInteger(v.n) ? Math.min(9999, Math.max(1, v.n)) : 1; chapters[key] = { n, last: DATE.test(v.last ?? '') ? v.last : '' }; } } const days = Array.isArray(raw?.days) ? [...new Set(raw.days.filter((d) => typeof d === 'string' && DATE.test(d)))].sort().slice(-MAX_DAYS) : []; return { chapters, days }; } export function mergeHistory(a, b) { const x = cleanHistory(a); const y = cleanHistory(b); const chapters = { ...x.chapters }; for (const [key, v] of Object.entries(y.chapters)) { const mine = chapters[key]; chapters[key] = mine ? { n: Math.max(mine.n, v.n), last: mine.last > v.last ? mine.last : v.last } : v; } return { chapters, days: [...new Set([...x.days, ...y.days])].sort().slice(-MAX_DAYS) }; }