Improve Watchlist page handling items being watchlisted/unwatchlisted within a given session

This commit is contained in:
HighImKevin committed 2026-10-17 07:03:45 -04:00
1 parent a9b055e0e7
commit 0ac2c6415e
3 files changed
+103 -18

No files matched your search

+75 -18
View File
@@ -3297,6 +3297,37 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
});
}
/**
* Keep paint-snapshot membership + header counts in sync on like/unlike without re-rendering sections.
* Ghost cards may remain in the DOM; counts reflect liked membership.
*/
function patchWatchlistItemsByTypeMembership(itemId, itemType, isAdded) {
if (!itemId) return;
const removeFromType = (type) => {
const list = watchlistItemsByType[type];
if (!Array.isArray(list) || !list.length) return false;
const next = list.filter((item) => item?.Id !== itemId);
if (next.length === list.length) return false;
watchlistItemsByType[type] = next;
return true;
};
if (isAdded) {
const type = itemType && isWatchlistSupportedType(itemType) ? itemType : null;
if (!type) return;
const list = watchlistItemsByType[type] || (watchlistItemsByType[type] = []);
if (list.some((item) => item?.Id === itemId)) return;
list.push({ Id: itemId });
} else if (itemType && isWatchlistSupportedType(itemType)) {
removeFromType(itemType);
} else {
WATCHLIST_SECTION_TYPES.forEach(({ type }) => removeFromType(type));
}
updateWatchlistHeaderStats();
}
function getWatchlistProgressiveHost() {
const watchlistSection = getWatchlistSection();
if (!watchlistSection) return null;
@@ -3330,9 +3361,11 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
throw new Error('apiHelper.getWatchlistQuery unavailable');
}
// Same Fields as apiHelper.buildWatchlistQueryDef so invalidate hits this fetch URL
const queryOptions = {
IncludeItemTypes: itemType,
Fields: 'PrimaryImageAspectRatio,DateCreated,ProductionYear,PremiereDate,ProviderIds,UserData'
Fields: apiHelper.WATCHLIST_PROGRESSIVE_FIELDS
|| 'PrimaryImageAspectRatio,DateCreated,ProductionYear,PremiereDate,ProviderIds,UserData'
};
const queryResult = await apiHelper.getWatchlistQuery(queryOptions, {
ttl: WATCHLIST_CACHE_TTL,
@@ -4974,31 +5007,52 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
alert(`Watchlist cleared: ${cleared} item(s) removed.`);
}
// Invalidate progressive watchlist cache and re-render when membership changes
/**
* Membership changed: update liked-Id cache + invalidate Likes query URL.
* Do not full-rebuild the Watchlist tab (avoids scroll jump / stale re-paint races).
* Cards stay as ghosts until next visit; cardBuilder owns overlay button UI via UserDataChanged.
*/
async function updateWatchlistCacheOnToggle(itemId, itemType, isAdded) {
try {
LOG(`Watchlist membership changed for ${itemType} ${itemId} (added=${isAdded}); refreshing progressive sections`);
LOG(`Watchlist membership changed for ${itemType || 'unknown'} ${itemId} (added=${isAdded}); invalidating query cache`);
setLikedIdInCache(itemId, isAdded === true);
syncOverlayWatchlistButtons();
patchWatchlistItemsByTypeMembership(itemId, itemType, isAdded === true);
if (itemType) {
if (itemType && isWatchlistSupportedType(itemType)) {
await window.apiHelper?.invalidateWatchlistQueries?.({ IncludeItemTypes: itemType });
} else {
await invalidateAllWatchlistQueries();
}
const watchlistSection = getWatchlistSection();
const watchlistVisible = !!watchlistSection
&& !watchlistSection.closest('.libraryPage.hide')
&& !!watchlistSection.querySelector('div[data-tab="watchlist"]:not([style*="display: none"])');
if (watchlistVisible || tabStates.watchlist.isDataFetched) {
await initWatchlistTab(true);
} else {
tabStates.watchlist.isDataFetched = false;
}
tabStates.watchlist.isDataFetched = false;
} catch (err) {
ERR('Error refreshing watchlist after toggle:', err);
ERR('Error refreshing watchlist cache after toggle:', err);
}
}
/**
* Sync liked-Id cache + invalidate progressive Likes queries from UserData.Likes.
* Idempotent for the Set when cardBuilder already applied Likes; always invalidate
* so a remote/Library like is visible on the next Watchlist visit.
*/
async function handleItemWatchlistChange(itemId, isLiked, itemType) {
if (!itemId || typeof isLiked !== 'boolean') return;
if (isItemInLikedIdCache(itemId) !== isLiked) {
setLikedIdInCache(itemId, isLiked);
}
// Keep header counts aligned even if liked-Id Set was already updated by cardBuilder
patchWatchlistItemsByTypeMembership(itemId, itemType, isLiked);
try {
if (itemType && isWatchlistSupportedType(itemType)) {
await window.apiHelper?.invalidateWatchlistQueries?.({ IncludeItemTypes: itemType });
} else {
await invalidateAllWatchlistQueries();
}
tabStates.watchlist.isDataFetched = false;
} catch (err) {
ERR('Error invalidating watchlist after Likes change:', err);
}
}
@@ -5022,8 +5076,11 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
for (const userData of entries) {
if (!userData?.ItemId) continue;
LOG(`Detected UserDataChanged for item: ${userData.ItemId}, Played: ${userData.Played}`);
handleItemWatchedStatusChange(userData.ItemId, userData.Played, userData.Likes).catch(err => {
LOG(`Detected UserDataChanged for item: ${userData.ItemId}, Played: ${userData.Played}, Likes: ${userData.Likes}`);
(async () => {
await handleItemWatchlistChange(userData.ItemId, userData.Likes);
await handleItemWatchedStatusChange(userData.ItemId, userData.Played, userData.Likes);
})().catch(err => {
ERR('Error handling UserDataChanged event:', err);
});
}