fix: ensure watch history is sorted by most recent first

- Enforce descending month order in `js/nostr/watchHistory.js` by explicitly sorting keys before flattening buckets.
- Update `js/feedEngine/watchHistoryFeed.js` and `js/historyView.js` to use `video.created_at` as a fallback sort key when `watchedAt` is missing or equal.
- Verified logic with `tests/verify_sort.mjs`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-07 00:28:39 +00:00
1 parent ad961cd401
commit f2f55830e8
3 files changed
+55 -8

No files matched your search

+26 -4
View File
@@ -195,8 +195,19 @@ function createWatchHistorySource({ service = watchHistoryService } = {}) {
}
results.sort((a, b) => {
if (a.watchedAt !== b.watchedAt) {
return b.watchedAt - a.watchedAt;
const watchedA = a?.watchedAt || 0;
const watchedB = b?.watchedAt || 0;
if (watchedA !== watchedB) {
return watchedB - watchedA;
}
const createdA = Number.isFinite(a?.video?.created_at)
? a.video.created_at
: 0;
const createdB = Number.isFinite(b?.video?.created_at)
? b.video.created_at
: 0;
if (createdA !== createdB) {
return createdB - createdA;
}
return a.pointerKey.localeCompare(b.pointerKey);
});
@@ -488,8 +499,19 @@ function createWatchHistorySorter() {
}
const copy = [...items];
copy.sort((a, b) => {
if (a?.watchedAt !== b?.watchedAt) {
return (b?.watchedAt || 0) - (a?.watchedAt || 0);
const watchedA = a?.watchedAt || 0;
const watchedB = b?.watchedAt || 0;
if (watchedA !== watchedB) {
return watchedB - watchedA;
}
const createdA = Number.isFinite(a?.video?.created_at)
? a.video.created_at
: 0;
const createdB = Number.isFinite(b?.video?.created_at)
? b.video.created_at
: 0;
if (createdA !== createdB) {
return createdB - createdA;
}
const aKey = typeof a?.pointerKey === "string" ? a.pointerKey : "";
const bKey = typeof b?.pointerKey === "string" ? b.pointerKey : "";
+9
View File
@@ -240,6 +240,15 @@ function normalizeHistoryItems(rawItems) {
if (a.watchedAt !== b.watchedAt) {
return b.watchedAt - a.watchedAt;
}
const createdA = Number.isFinite(a?.video?.created_at)
? a.video.created_at
: 0;
const createdB = Number.isFinite(b?.video?.created_at)
? b.video.created_at
: 0;
if (createdA !== createdB) {
return createdB - createdA;
}
return a.pointerKey.localeCompare(b.pointerKey);
});
return normalized;
+20 -4
View File
@@ -526,7 +526,11 @@ async function computeWatchHistoryFingerprintForItems(itemsOrBuckets) {
if (Array.isArray(itemsOrBuckets)) {
flatItems = itemsOrBuckets;
} else if (itemsOrBuckets && typeof itemsOrBuckets === "object") {
flatItems = Object.values(itemsOrBuckets).flat();
flatItems = Object.keys(itemsOrBuckets)
.sort()
.reverse()
.map((k) => itemsOrBuckets[k])
.flat();
}
// Sort for deterministic fingerprint
@@ -1617,7 +1621,11 @@ class WatchHistoryManager {
// Existing items - try to get records, fallback to items
let existingItems = [];
if (cachedEntry.records && typeof cachedEntry.records === "object") {
existingItems = Object.values(cachedEntry.records).flat();
existingItems = Object.keys(cachedEntry.records)
.sort()
.reverse()
.map((k) => cachedEntry.records[k])
.flat();
} else if (Array.isArray(cachedEntry.items)) {
existingItems = cachedEntry.items;
}
@@ -1959,7 +1967,11 @@ class WatchHistoryManager {
mergedItems,
WATCH_HISTORY_MAX_ITEMS,
);
const flatItems = Object.values(records).flat();
const flatItems = Object.keys(records)
.sort()
.reverse()
.map((k) => records[k])
.flat();
const fingerprint = await this.getFingerprint(
resolvedActor,
@@ -2038,7 +2050,11 @@ class WatchHistoryManager {
merged.items || [],
batchLimit,
);
const flatItems = Object.values(records).flat();
const flatItems = Object.keys(records)
.sort()
.reverse()
.map((k) => records[k])
.flat();
const fingerprint = await this.getFingerprint(
resolvedActor,