diff --git a/frontend/js/storage.js b/frontend/js/storage.js index 1ff329f..7fcb679 100644 --- a/frontend/js/storage.js +++ b/frontend/js/storage.js @@ -50,6 +50,11 @@ App.session = App.session || {}; }); }; + // Synthetic filter id used to let a channel group expose its member + // channels as a toggleable multi-select, so the user can browse "All " + // while disabling individual channels. + App.session.GROUP_CHANNELS_OPTION_ID = '__groupChannels'; + // Options/session helpers that power channel selection and filters. App.session.serializeOptions = function(options) { const serialized = {}; @@ -71,9 +76,13 @@ App.session = App.session || {}; const allOptions = optionGroup.options || []; const savedValue = saved[optionGroup.id]; if (optionGroup.multiSelect) { - const selectedIds = Array.isArray(savedValue) ? savedValue : []; - const selected = allOptions.filter((opt) => selectedIds.includes(opt.id)); - hydrated[optionGroup.id] = selected.length > 0 ? selected : allOptions.slice(0, 1); + const fallback = optionGroup.selectAllDefault ? allOptions.slice() : allOptions.slice(0, 1); + if (Array.isArray(savedValue)) { + const selected = allOptions.filter((opt) => savedValue.includes(opt.id)); + hydrated[optionGroup.id] = selected.length > 0 ? selected : fallback; + } else { + hydrated[optionGroup.id] = fallback; + } } else { const selected = allOptions.find((opt) => opt.id === savedValue) || allOptions[0]; if (selected) hydrated[optionGroup.id] = selected; @@ -91,12 +100,26 @@ App.session = App.session || {}; const channelIds = Array.isArray(group.channelIds) ? group.channelIds.filter((id) => knownIds.has(id)) : []; + const channelOptions = channelIds.map((id) => { + const channel = (channels || []).find((ch) => ch.id === id); + return { id: id, title: (channel && (channel.name || channel.id)) || id }; + }); return { id: `group:${group.id}`, name: group.title || group.id, isGroup: true, groupId: group.id, - channelIds: channelIds + channelIds: channelIds, + // Expose member channels as a multi-select filter (all on by + // default) so the user can disable individual channels while + // browsing the whole group. + options: channelOptions.length > 0 ? [{ + id: App.session.GROUP_CHANNELS_OPTION_ID, + title: 'Channels', + multiSelect: true, + selectAllDefault: true, + options: channelOptions + }] : [] }; }; @@ -131,7 +154,9 @@ App.session = App.session || {}; channel.options.forEach((optionGroup) => { if (!optionGroup.options || optionGroup.options.length === 0) return; if (optionGroup.multiSelect) { - selected[optionGroup.id] = [optionGroup.options[0]]; + selected[optionGroup.id] = optionGroup.selectAllDefault ? + optionGroup.options.slice() : + [optionGroup.options[0]]; } else { selected[optionGroup.id] = optionGroup.options[0]; } diff --git a/frontend/js/videos.js b/frontend/js/videos.js index 079f8da..e34b78d 100644 --- a/frontend/js/videos.js +++ b/frontend/js/videos.js @@ -150,11 +150,22 @@ App.videos = App.videos || {}; const searchInput = document.getElementById('search-input'); const query = searchInput ? searchInput.value : ""; - if (!state.groupCursors || state.groupCursors.groupId !== group.id || state.groupCursors.query !== query) { + // Honor the per-group "Channels" multi-select filter so disabled + // channels are skipped. Falls back to the full group when nothing is + // selected (e.g. older sessions without the filter). + const selectedChannels = session.options ? session.options[App.session.GROUP_CHANNELS_OPTION_ID] : null; + const enabledIds = (Array.isArray(selectedChannels) && selectedChannels.length > 0 ? + selectedChannels.map((opt) => opt.id) : + group.channelIds).filter((id) => group.channelIds.includes(id)); + const signature = enabledIds.join(','); + + if (!state.groupCursors || state.groupCursors.groupId !== group.id || + state.groupCursors.query !== query || state.groupCursors.signature !== signature) { state.groupCursors = { groupId: group.id, query: query, - channels: group.channelIds.map((id) => ({ id, page: 1, hasNextPage: true })) + signature: signature, + channels: enabledIds.map((id) => ({ id, page: 1, hasNextPage: true })) }; }