feat: open combined collections directly from any video view

This commit is contained in:
2026-10-11 21:28:20 +02:00
parent a9fef33e51
commit d70a077076
3 changed files with 353 additions and 28 deletions
+59 -7
View File
@@ -127,12 +127,14 @@ actions! {
ChannelTags, "channel.tags", "Edit the selected channel's category tags", VIEW, ["s"];
CategoryTags, "category.tags", "Edit category tags for all channels in this category", VIEW, ["S"];
WatchLaterSave, "video.watch_later.save", "Save the selected video to watch later", VIEW, ["w"];
WatchLaterBrowse, "view.watch_later.browse", "Browse this category's watch later, or the combined collection elsewhere", &[Context::Normal, Context::CollectionNormal], ["W"];
WatchLaterBrowse, "view.watch_later.browse", "Browse watch later across all categories", VIEW, ["W"];
WatchLaterBrowseCategory, "view.watch_later.category", "Browse the current category's watch later", VIEW, ["alt+w"];
WatchLaterFilter, "view.watch_later.filter", "Cycle watch-later filter: all, unwatched, watched", &[Context::Normal, Context::CollectionNormal], ["f"];
CollectionRemoveSelected, "view.collection.remove.selected", "Remove the selected video from the current collection (also in its previews)", COLLECTION, ["x"];
CollectionRemoveBulk, "view.collection.remove.bulk", "Confirm removal of the current collection subset", COLLECTION, ["X"];
SavedVideoSave, "video.saved.save", "Save the selected video (also in previews)", VIEW, ["v"];
SavedVideoBrowse, "view.saved.browse", "Browse this category's saved videos, or the combined collection elsewhere", &[Context::Normal, Context::CollectionNormal], ["V"];
SavedVideoBrowse, "view.saved.browse", "Browse saved videos across all categories", VIEW, ["V"];
SavedVideoBrowseCategory, "view.saved.category", "Browse the current category's saved videos", VIEW, ["alt+v"];
SelectorNext, "selector.select.next", "Select the next category or channel", &[Context::SelectorNormal], ["j", "down"];
SelectorPrevious, "selector.select.previous", "Select the previous category or channel", &[Context::SelectorNormal], ["k", "up"];
SelectorInputNext, "selector.input.next", "Select the next match while editing the filter", &[Context::SelectorInput], ["down", "ctrl+j"];
@@ -492,7 +494,11 @@ impl Bindings {
(&[WatchLaterSave], "Save the selected video"),
(
&[WatchLaterBrowse],
"Browse category watch later; combined elsewhere",
"Browse watch later across all categories",
),
(
&[WatchLaterBrowseCategory],
"Browse current category's watch later",
),
(&[WatchLaterFilter], "Cycle all / unwatched / watched"),
],
@@ -506,7 +512,11 @@ impl Bindings {
),
(
&[SavedVideoBrowse],
"Browse category saved videos; combined elsewhere",
"Browse saved videos across all categories",
),
(
&[SavedVideoBrowseCategory],
"Browse current category's saved videos",
),
],
),
@@ -1147,10 +1157,52 @@ mod tests {
}
}
#[test]
fn collection_browse_shortcuts_are_distinct_and_view_scoped() {
let defaults = Bindings::default();
let overrides = configured(
"[keys.view.watch_later]\ncategory = ['OpTiOn+b', 'alt+b']\n[keys]\n'view.saved.category' = []\n",
)
.unwrap();
for &context in ALL {
for (key, modifiers, action) in [
('W', KeyModifiers::NONE, Action::WatchLaterBrowse),
('w', KeyModifiers::SHIFT, Action::WatchLaterBrowse),
('V', KeyModifiers::NONE, Action::SavedVideoBrowse),
('v', KeyModifiers::SHIFT, Action::SavedVideoBrowse),
('w', KeyModifiers::ALT, Action::WatchLaterBrowseCategory),
('v', KeyModifiers::ALT, Action::SavedVideoBrowseCategory),
] {
assert_eq!(
press(&defaults, context, KeyCode::Char(key), modifiers),
VIEW.contains(&context).then_some(action),
);
}
assert_eq!(
press(&overrides, context, KeyCode::Char('b'), KeyModifiers::ALT),
VIEW.contains(&context)
.then_some(Action::WatchLaterBrowseCategory),
);
for key in ['w', 'v'] {
assert_eq!(
press(&overrides, context, KeyCode::Char(key), KeyModifiers::ALT),
None,
);
}
for key in ['W', 'V'] {
assert_eq!(
press(&defaults, context, KeyCode::Char(key), KeyModifiers::ALT),
None,
);
}
}
assert_eq!(overrides.label(Action::WatchLaterBrowseCategory), "Alt+b");
}
#[test]
fn collection_browsing_and_save_overrides_retain_contexts() {
let bindings = configured(
"[keys.video.watch_later]\nsave = 'alt+w'\n[keys.video.saved]\nsave = 'alt+v'\n[keys.view.watch_later]\nbrowse = 'shift+x'\nfilter = 'ctrl+f'\n[keys.view.saved]\nbrowse = 'alt+b'\n[keys.view.collection.remove]\nbulk = []\n",
"[keys.video.watch_later]\nsave = 'alt+w'\n[keys.video.saved]\nsave = 'alt+v'\n[keys.view.watch_later]\nbrowse = 'shift+x'\ncategory = []\nfilter = 'ctrl+f'\n[keys.view.saved]\nbrowse = 'alt+b'\ncategory = []\n[keys.view.collection.remove]\nbulk = []\n",
).unwrap();
for &context in ALL {
for (key, modifiers, action, enabled) in [
@@ -1170,7 +1222,7 @@ mod tests {
'x',
KeyModifiers::SHIFT,
Action::WatchLaterBrowse,
matches!(context, Context::Normal | Context::CollectionNormal),
VIEW.contains(&context),
),
(
'f',
@@ -1182,7 +1234,7 @@ mod tests {
'b',
KeyModifiers::ALT,
Action::SavedVideoBrowse,
matches!(context, Context::Normal | Context::CollectionNormal),
VIEW.contains(&context),
),
] {
assert_eq!(