Reading history entries to record.
export function recordReadingHistory(entries: ReadingHistoryEntry[]): void {
if (!entries.length) return;
const history = getReadingHistory();
const now = Date.now();
// Create a map of existing entries by mangaId and date for deduplication (using local date)
const existingMap = new Map<string, ReadingHistoryEntry>();
for (const entry of history.entries) {
const entryDate = getLocalDateString(entry.timestamp);
const key = `${entry.mangaId}_${entryDate}`;
existingMap.set(key, entry);
}
// Add or update entries
for (const entry of entries) {
const entryDate = getLocalDateString(entry.timestamp);
const key = `${entry.mangaId}_${entryDate}`;
// Only update if chapters changed or it's a new entry
const existing = existingMap.get(key);
if (!existing || existing.chaptersRead !== entry.chaptersRead) {
existingMap.set(key, entry);
}
}
// Convert back to array and sort by timestamp (newest first)
const allEntries = Array.from(existingMap.values()).sort(
(a, b) => b.timestamp - a.timestamp,
);
// Enforce maximum entries limit per manga to avoid disproportionate truncation
const entriesByManga = new Map<string | number, ReadingHistoryEntry[]>();
for (const entry of allEntries) {
if (!entriesByManga.has(entry.mangaId)) {
entriesByManga.set(entry.mangaId, []);
}
entriesByManga.get(entry.mangaId)!.push(entry);
}
// Trim each manga's history to max entries, keeping newest
const maxPerManga = 365;
const trimmedEntries: ReadingHistoryEntry[] = [];
for (const mangaEntries of entriesByManga.values()) {
if (mangaEntries.length > maxPerManga) {
trimmedEntries.push(...mangaEntries.slice(0, maxPerManga));
} else {
trimmedEntries.push(...mangaEntries);
}
}
// Sort final list by timestamp (newest first)
trimmedEntries.sort((a, b) => b.timestamp - a.timestamp);
const updatedHistory: ReadingHistory = {
entries: trimmedEntries,
lastUpdated: now,
version: 1,
};
saveReadingHistory(updatedHistory);
}
Records reading history snapshots with deduplication and retention limits.