Allow clicking on home section labels to open corresponding page

My media opens collections page
Latest X opens that collection's page
This commit is contained in:
Avery
2024-02-01 12:46:06 -05:00
parent 1f6d96601c
commit a8b9391f3c
7 changed files with 94 additions and 58 deletions

View File

@@ -16,15 +16,10 @@ pub struct HomeSectionContinueWatching {
_media_grid: AsyncController<MediaList>,
}
#[derive(Debug)]
pub enum HomeSectionContinueWatchingInput {
Empty,
}
#[relm4::component(pub)]
impl Component for HomeSectionContinueWatching {
type Init = Arc<ApiClient>;
type Input = HomeSectionContinueWatchingInput;
type Input = MediaListOutput;
type Output = ();
type CommandOutput = ();
@@ -47,8 +42,9 @@ impl Component for HomeSectionContinueWatching {
api_client,
list_type: MediaListType::ContinueWatching,
label: tr!("library-section-title.continue-watching").to_string(),
label_clickable: false,
})
.forward(sender.input_sender(), |o| o.into());
.forward(sender.input_sender(), |o| o);
root.append(media_grid.widget());
let model = HomeSectionContinueWatching {
@@ -66,15 +62,8 @@ impl Component for HomeSectionContinueWatching {
root: &Self::Root,
) {
match message {
HomeSectionContinueWatchingInput::Empty => root.set_visible(false),
}
}
}
impl From<MediaListOutput> for HomeSectionContinueWatchingInput {
fn from(value: MediaListOutput) -> Self {
match value {
MediaListOutput::Empty(_) => HomeSectionContinueWatchingInput::Empty,
MediaListOutput::Empty(_) => root.set_visible(false),
MediaListOutput::LabelClicked(_) => {}
}
}
}

View File

@@ -1,4 +1,4 @@
use std::{collections::HashMap, convert::identity, sync::Arc};
use std::{collections::HashMap, convert::identity, sync::Arc, unreachable};
use gtk::prelude::*;
use relm4::{
@@ -10,6 +10,7 @@ use tracing::warn;
use uuid::Uuid;
use crate::{
app::{AppInput, APP_BROKER},
jellyfin_api::{
api_client::ApiClient,
models::{
@@ -24,13 +25,13 @@ use crate::{
};
pub struct HomeSectionLatest {
rows: HashMap<String, Controller<LatestRow>>,
rows: HashMap<Uuid, (UserView, Controller<LatestRow>)>,
}
#[derive(Debug)]
pub enum HomeSectionLatestInput {
Empty(Uuid),
None,
ShowCollection(Uuid),
}
#[relm4::component(pub)]
@@ -68,7 +69,7 @@ impl Component for HomeSectionLatest {
.launch((api_client.clone(), view.clone()))
.forward(sender.input_sender(), identity);
root.append(row.widget());
model.rows.insert(view.id().to_string(), row);
model.rows.insert(view.id(), (view, row));
}
ComponentParts { model, widgets }
@@ -77,11 +78,15 @@ impl Component for HomeSectionLatest {
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>, _root: &Self::Root) {
match message {
HomeSectionLatestInput::Empty(id) => {
if let Some(row) = self.rows.get(&id.to_string()) {
row.widget().set_visible(false);
if let Some(row) = self.rows.get(&id) {
row.1.widget().set_visible(false);
}
}
HomeSectionLatestInput::ShowCollection(id) => {
if let Some(collection) = self.rows.get(&id) {
APP_BROKER.send(AppInput::ShowCollection(collection.0.clone().into()));
}
}
HomeSectionLatestInput::None => {}
}
}
}
@@ -90,7 +95,8 @@ impl From<MediaListOutput> for HomeSectionLatestInput {
fn from(value: MediaListOutput) -> Self {
match value {
MediaListOutput::Empty(Some(id)) => HomeSectionLatestInput::Empty(id),
_ => HomeSectionLatestInput::None,
MediaListOutput::LabelClicked(Some(id)) => HomeSectionLatestInput::ShowCollection(id),
_ => unreachable!(),
}
}
}
@@ -136,6 +142,7 @@ impl SimpleComponent for LatestRow {
api_client,
list_type: MediaListType::Latest(MediaListTypeLatestParams { view_id: view.id() }),
label: title_text.to_string(),
label_clickable: true,
})
.forward(sender.input_sender(), |o| o.into());
root.append(media_list.widget());

View File

@@ -8,7 +8,10 @@ use crate::{
api_client::ApiClient,
models::user_view::{FilterSupported, UserView},
},
library::media_list::{MediaList, MediaListInit, MediaListOutput, MediaListType},
library::{
media_list::{MediaList, MediaListInit, MediaListOutput, MediaListType},
LibraryInput, LIBRARY_BROKER,
},
tr,
};
@@ -56,6 +59,7 @@ impl Component for HomeSectionMyMedia {
api_client,
list_type: MediaListType::MyMedia { user_views, small },
label: tr!("library-section-title.my-media").to_string(),
label_clickable: true,
})
.forward(sender.input_sender(), |m| m);
@@ -68,7 +72,12 @@ impl Component for HomeSectionMyMedia {
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>, root: &Self::Root) {
match message {
MediaListOutput::Empty(_) => root.set_visible(false),
MediaListOutput::Empty(_) => {
root.set_visible(false);
}
MediaListOutput::LabelClicked(_) => {
LIBRARY_BROKER.send(LibraryInput::ShowCollections);
}
}
}
}

View File

@@ -16,15 +16,10 @@ pub struct HomeSectionNextUp {
_media_grid: AsyncController<MediaList>,
}
#[derive(Debug)]
pub enum HomeSectionNextUpInput {
Empty,
}
#[relm4::component(pub)]
impl Component for HomeSectionNextUp {
type Init = Arc<ApiClient>;
type Input = HomeSectionNextUpInput;
type Input = MediaListOutput;
type Output = ();
type CommandOutput = ();
@@ -47,8 +42,9 @@ impl Component for HomeSectionNextUp {
api_client,
list_type: MediaListType::NextUp,
label: tr!("library-section-title.next-up").to_string(),
label_clickable: false,
})
.forward(sender.input_sender(), |o| o.into());
.forward(sender.input_sender(), |o| o);
root.append(media_grid.widget());
let model = HomeSectionNextUp {
@@ -66,15 +62,8 @@ impl Component for HomeSectionNextUp {
root: &Self::Root,
) {
match message {
HomeSectionNextUpInput::Empty => root.set_visible(false),
}
}
}
impl From<MediaListOutput> for HomeSectionNextUpInput {
fn from(value: MediaListOutput) -> Self {
match value {
MediaListOutput::Empty(_) => HomeSectionNextUpInput::Empty,
MediaListOutput::Empty(_) => root.set_visible(false),
MediaListOutput::LabelClicked(_) => {}
}
}
}

View File

@@ -45,6 +45,7 @@ pub(crate) struct MediaCarouselInit {
pub(crate) carousel_type: MediaCarouselType,
pub(crate) api_client: Arc<ApiClient>,
pub(crate) label: String,
pub(crate) label_clickable: bool,
}
#[derive(Debug)]
@@ -54,6 +55,11 @@ pub(crate) enum MediaCarouselInput {
Right,
}
#[derive(Debug)]
pub(crate) enum MediaCarouselOutput {
LabelClicked,
}
impl MediaTileDisplay {
fn min_height(&self, pages: &Vec<gtk::Box>) -> i32 {
match pages.len() {
@@ -67,7 +73,7 @@ impl MediaTileDisplay {
impl Component for MediaCarousel {
type Init = MediaCarouselInit;
type Input = MediaCarouselInput;
type Output = ();
type Output = MediaCarouselOutput;
type CommandOutput = ();
view! {
@@ -78,11 +84,32 @@ impl Component for MediaCarousel {
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
#[name = "title"]
gtk::Label {
set_label: label.as_str(),
add_css_class: "title-2",
set_halign: gtk::Align::Start,
gtk::Box {
set_spacing: 4,
set_cursor_from_name: if label_clickable { Some("pointer") } else { None },
gtk::Label {
set_label: label.as_str(),
add_css_class: "title-2",
set_halign: gtk::Align::Start,
},
gtk::Image::from_icon_name("right") {
set_visible: label_clickable,
},
add_controller?: if label_clickable {
let controller = gtk::GestureClick::new();
controller.connect_released({
let sender = sender.clone();
move |_, _, _, _| {
sender.output(MediaCarouselOutput::LabelClicked).unwrap();
}
});
Some(controller)
} else {
None
},
},
gtk::Box {
@@ -167,6 +194,7 @@ impl Component for MediaCarousel {
media_tile_display,
carousel_type,
label,
label_clickable,
} = init;
let media_tiles = media

View File

@@ -13,7 +13,7 @@ use crate::jellyfin_api::{
};
use super::{
media_carousel::{MediaCarousel, MediaCarouselInit, MediaCarouselType},
media_carousel::{MediaCarousel, MediaCarouselInit, MediaCarouselOutput, MediaCarouselType},
media_tile::MediaTileDisplay,
};
@@ -46,11 +46,13 @@ pub struct MediaListInit {
pub list_type: MediaListType,
pub api_client: Arc<ApiClient>,
pub label: String,
pub label_clickable: bool,
}
#[derive(Debug)]
pub enum MediaListOutput {
Empty(Option<Uuid>),
LabelClicked(Option<Uuid>),
}
#[relm4::component(pub async)]
@@ -86,6 +88,9 @@ impl MediaList {
let api_client = Arc::clone(&init.api_client);
let list_type = &init.list_type;
let label = init.label.clone();
let label_clickable = init.label_clickable;
let view_id = list_type.view_id();
let media = match list_type {
MediaListType::ContinueWatching => api_client
@@ -111,9 +116,7 @@ impl MediaList {
.collect(),
};
if media.is_empty() {
sender
.output(MediaListOutput::Empty(get_view_id(list_type)))
.unwrap();
sender.output(MediaListOutput::Empty(view_id)).unwrap();
}
let media_tile_display = match list_type {
@@ -136,8 +139,11 @@ impl MediaList {
carousel_type,
api_client,
label,
label_clickable,
})
.detach();
.forward(sender.output_sender(), move |msg| match msg {
MediaCarouselOutput::LabelClicked => MediaListOutput::LabelClicked(view_id),
});
root.append(carousel.widget());
MediaListContents::Carousel(carousel)
};
@@ -148,11 +154,13 @@ impl MediaList {
}
}
fn get_view_id(list_type: &MediaListType) -> Option<Uuid> {
match list_type {
MediaListType::ContinueWatching | MediaListType::NextUp | MediaListType::MyMedia { .. } => {
None
impl MediaListType {
fn view_id(&self) -> Option<Uuid> {
match self {
MediaListType::ContinueWatching
| MediaListType::NextUp
| MediaListType::MyMedia { .. } => None,
MediaListType::Latest(params) => Some(params.view_id),
}
MediaListType::Latest(params) => Some(params.view_id),
}
}

View File

@@ -84,6 +84,7 @@ pub enum LibraryInput {
SearchChanged(String),
SearchingChanged(bool),
ShowSearch,
ShowCollections,
}
#[derive(Debug)]
@@ -432,6 +433,11 @@ impl Component for Library {
self.searching.set_value(true);
}
}
LibraryInput::ShowCollections => {
if let Some(collections) = &self.collections {
widgets.view_stack.set_visible_child(collections.widget());
}
}
}
self.update_view(widgets, sender);