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:
@@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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),
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user