File size: 1,390 Bytes
dbb1bf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * #4923: pure "new since your last visit" partition, extracted from
 * NewsPanel's first render so the behavior is unit-testable (review
 * finding on PR #4926 — a source-regex test cannot catch broken wiring).
 *
 * SEMANTIC DECISION (explicit, revisit deliberately): a cluster counts as
 * new-since-away when its `firstSeen` — the EARLIEST article in the
 * cluster — postdates the previous visit. Genuinely new stories flag NEW;
 * a long-running story that merely gained an update while away does NOT
 * (using lastUpdated would keep rolling stories perpetually NEW on every
 * return, which trains users to ignore the signal).
 *
 * prevVisitAt <= 0 means "no known previous visit" — everything is seen,
 * matching the pre-#4923 first-render behavior.
 */

export interface NewSinceVisitCluster {
  id: string;
  firstSeen: Date | string | number;
}

export function computeNewSinceVisit(
  clusters: readonly NewSinceVisitCluster[],
  prevVisitAt: number,
): { newIds: string[]; seenIds: string[] } {
  const newIds: string[] = [];
  const seenIds: string[] = [];
  for (const cluster of clusters) {
    const firstSeenMs = new Date(cluster.firstSeen).getTime();
    if (prevVisitAt > 0 && Number.isFinite(firstSeenMs) && firstSeenMs > prevVisitAt) {
      newIds.push(cluster.id);
    } else {
      seenIds.push(cluster.id);
    }
  }
  return { newIds, seenIds };
}