mirror of
https://github.com/dergigi/boris.git
synced 2026-01-17 05:44:24 +01:00
84 lines
3.0 KiB
TypeScript
84 lines
3.0 KiB
TypeScript
import { ReadItem } from '../services/readsService'
|
|
|
|
/**
|
|
* Merges a ReadItem into a state map, returning whether the state changed.
|
|
* Uses most recent reading activity to determine precedence.
|
|
*/
|
|
export function mergeReadItem(
|
|
stateMap: Map<string, ReadItem>,
|
|
incoming: ReadItem
|
|
): boolean {
|
|
const existing = stateMap.get(incoming.id)
|
|
|
|
if (!existing) {
|
|
stateMap.set(incoming.id, incoming)
|
|
return true
|
|
}
|
|
|
|
// Always merge if incoming has reading progress data
|
|
const hasNewProgress = incoming.readingProgress !== undefined &&
|
|
(existing.readingProgress === undefined || existing.readingProgress !== incoming.readingProgress)
|
|
|
|
const hasNewMarkedAsRead = incoming.markedAsRead !== undefined && existing.markedAsRead === undefined
|
|
|
|
// Merge by taking the most recent reading activity
|
|
const existingTime = existing.readingTimestamp || existing.markedAt || 0
|
|
const incomingTime = incoming.readingTimestamp || incoming.markedAt || 0
|
|
|
|
if (incomingTime > existingTime || hasNewProgress || hasNewMarkedAsRead) {
|
|
// Keep existing data, but update with newer reading metadata
|
|
stateMap.set(incoming.id, {
|
|
...existing,
|
|
...incoming,
|
|
// Preserve event data if incoming doesn't have it
|
|
event: incoming.event || existing.event,
|
|
title: incoming.title || existing.title,
|
|
summary: incoming.summary || existing.summary,
|
|
image: incoming.image || existing.image,
|
|
published: incoming.published || existing.published,
|
|
author: incoming.author || existing.author,
|
|
// Always take reading progress if available
|
|
readingProgress: incoming.readingProgress !== undefined ? incoming.readingProgress : existing.readingProgress,
|
|
readingTimestamp: incomingTime > existingTime ? incoming.readingTimestamp : existing.readingTimestamp
|
|
})
|
|
return true
|
|
}
|
|
|
|
// If timestamps are equal but incoming has additional data, merge it
|
|
if (incomingTime === existingTime && (!existing.event && incoming.event || !existing.title && incoming.title)) {
|
|
stateMap.set(incoming.id, {
|
|
...existing,
|
|
...incoming,
|
|
event: incoming.event || existing.event,
|
|
title: incoming.title || existing.title,
|
|
summary: incoming.summary || existing.summary,
|
|
image: incoming.image || existing.image,
|
|
published: incoming.published || existing.published,
|
|
author: incoming.author || existing.author
|
|
})
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Extracts a readable title from a URL when no title is available.
|
|
* Removes protocol, www, and shows domain + path.
|
|
*/
|
|
export function fallbackTitleFromUrl(url: string): string {
|
|
try {
|
|
const parsed = new URL(url)
|
|
let title = parsed.hostname.replace(/^www\./, '')
|
|
if (parsed.pathname && parsed.pathname !== '/') {
|
|
const path = parsed.pathname.slice(0, 40)
|
|
title += path.length < parsed.pathname.length ? path + '...' : path
|
|
}
|
|
return title
|
|
} catch {
|
|
// If URL parsing fails, just return the URL truncated
|
|
return url.length > 50 ? url.slice(0, 47) + '...' : url
|
|
}
|
|
}
|
|
|