Files
jacuzzi/frontend/js/state.js
Simon d4ed9dce5d Browse favorites like a listing, sorted, and page them as you scroll
Favorites now carry `favoriteDate`. Ones saved before this had no way of
knowing when they were saved, so they are all stamped with the moment the
client first reads them -- they sort together as one batch, at the point
favorites learned to keep dates. An import brings the date the other client
recorded instead, so a restored library keeps its history.

The bar used to build a card per favorite, which an import of several
hundred made an expensive way to open the app. It now renders a screenful
and appends more as the strip is scrolled.

"Browse all" turns the whole grid into favorites: the same cards, the same
virtualized masonry, the same infinite scroll and reels mode as a channel
listing -- App.videos.loadVideos simply pages out of localStorage instead of
the server while that view is open. Sort applies to the bar and the grid
together: recently added (default), oldest, title, longest, shortest, and a
shuffle for rediscovering a long list.

Tests (scratchpad): dates backfilled onto undated favorites, the bar paging
as it scrolls rather than building every card, the grid paging to the full
list with zero server calls, each sort order reordering it, and the way back
to the channel listing. Also measured: 515 imported favorites load with the
page responsive and 24 bar cards built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBDkEXP4htyXTCZUwMLphd
2026-09-06 10:46:47 +00:00

49 lines
1.5 KiB
JavaScript

window.App = window.App || {};
// Centralized runtime state for pagination, player, and UI behavior.
App.state = {
currentPage: 1,
perPage: 12,
renderedVideoIds: new Set(),
hasNextPage: true,
isLoading: false,
hlsPlayer: null,
currentLoadController: null,
errorToastTimer: null,
loadedVideos: [],
feedOpen: false,
feedMuted: true,
feedActiveIndex: -1,
feedActiveVideoId: null,
groupCursors: null
};
// Local storage keys used across modules.
App.constants = {
FAVORITES_KEY: 'favorites',
FAVORITES_VISIBILITY_KEY: 'favoritesVisible',
FAVORITES_SORT_KEY: 'favoritesSort',
PREFERRED_QUALITY_KEY: 'preferredQuality',
FEED_END_BEHAVIOR_KEY: 'feedEndBehavior'
};
// Lazily injects hls.js the first time a stream actually needs it. Sessions
// that only browse thumbnails, or that play native/MP4, never download it.
// Resolves with window.Hls (or null if loading failed).
App.ensureHls = function() {
if (window.Hls) return Promise.resolve(window.Hls);
if (App._hlsPromise) return App._hlsPromise;
App._hlsPromise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/hls.js@1.5';
script.async = true;
script.onload = () => resolve(window.Hls || null);
script.onerror = () => {
App._hlsPromise = null;
reject(new Error('Failed to load hls.js'));
};
document.head.appendChild(script);
});
return App._hlsPromise;
};