250 lines
9.0 KiB
Rust
250 lines
9.0 KiB
Rust
use gtk::prelude::*;
|
|
use lemmy_api_common::lemmy_db_views::structs::PostView;
|
|
use relm4::prelude::*;
|
|
use relm4_components::web_image::WebImage;
|
|
|
|
use crate::{api, util::get_web_image_url};
|
|
use crate::{settings, util};
|
|
|
|
use super::voting_row::{VotingRowModel, VotingStats};
|
|
|
|
#[derive(Debug)]
|
|
pub struct PostRow {
|
|
post: PostView,
|
|
author_image: Controller<WebImage>,
|
|
community_image: Controller<WebImage>,
|
|
thumbnail: Controller<WebImage>,
|
|
voting_row: Controller<VotingRowModel>,
|
|
image_size: i32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum PostRowMsg {
|
|
OpenPost,
|
|
OpenCommunity,
|
|
OpenPerson,
|
|
ToggleSaved,
|
|
ToggleRead,
|
|
DeletePost,
|
|
UpdateSaved(bool),
|
|
UpdateRead(bool),
|
|
}
|
|
|
|
#[relm4::factory(pub)]
|
|
impl FactoryComponent for PostRow {
|
|
type Init = PostView;
|
|
type Input = PostRowMsg;
|
|
type Output = crate::AppMsg;
|
|
type CommandOutput = ();
|
|
type ParentInput = crate::AppMsg;
|
|
type ParentWidget = gtk::Box;
|
|
|
|
view! {
|
|
root = gtk::Box {
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_margin_end: 10,
|
|
set_margin_start: 10,
|
|
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Horizontal,
|
|
set_spacing: 10,
|
|
|
|
#[local_ref]
|
|
thumbnail -> gtk::Box {
|
|
set_visible: self.post.post.thumbnail_url.is_some(),
|
|
set_size_request: (self.image_size, self.image_size),
|
|
set_margin_start: 10,
|
|
set_margin_end: 10,
|
|
set_hexpand: false,
|
|
set_valign: gtk::Align::Center,
|
|
},
|
|
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_valign: gtk::Align::Center,
|
|
set_spacing: 10,
|
|
set_hexpand: true,
|
|
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Horizontal,
|
|
set_margin_top: 10,
|
|
set_spacing: 10,
|
|
set_vexpand: false,
|
|
|
|
#[local_ref]
|
|
community_image -> gtk::Box {
|
|
set_hexpand: false,
|
|
set_visible: self.post.community.icon.clone().is_some(),
|
|
},
|
|
|
|
gtk::Button {
|
|
set_label: &self.post.community.title,
|
|
connect_clicked => PostRowMsg::OpenCommunity,
|
|
},
|
|
|
|
#[local_ref]
|
|
author_image -> gtk::Box {
|
|
set_hexpand: false,
|
|
set_margin_start: 10,
|
|
set_visible: self.post.creator.avatar.clone().is_some(),
|
|
},
|
|
|
|
gtk::Button {
|
|
set_label: &self.post.creator.name,
|
|
connect_clicked => PostRowMsg::OpenPerson,
|
|
},
|
|
|
|
gtk::Label {
|
|
set_margin_start: 10,
|
|
set_label: &util::format_elapsed_time(self.post.post.published),
|
|
}
|
|
},
|
|
|
|
gtk::Label {
|
|
set_halign: gtk::Align::Start,
|
|
set_text: &self.post.post.name,
|
|
set_wrap: true,
|
|
add_css_class: "font-bold",
|
|
add_controller = gtk::GestureClick {
|
|
connect_pressed[sender] => move |_, _, _, _| {
|
|
sender.input(PostRowMsg::OpenPost);
|
|
}
|
|
},
|
|
},
|
|
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Horizontal,
|
|
#[local_ref]
|
|
voting_row -> gtk::Box {
|
|
set_margin_end: 10,
|
|
},
|
|
gtk::Label {
|
|
set_halign: gtk::Align::Start,
|
|
set_text: &format!("{} comments", self.post.counts.comments.clone()),
|
|
},
|
|
gtk::ToggleButton {
|
|
set_icon_name: "bookmark-new",
|
|
set_margin_start: 10,
|
|
connect_clicked => PostRowMsg::ToggleSaved,
|
|
set_visible: settings::get_current_account().jwt.is_some(),
|
|
#[watch]
|
|
set_active: self.post.saved,
|
|
},
|
|
gtk::ToggleButton {
|
|
set_icon_name: "mail-mark-read",
|
|
set_margin_start: 10,
|
|
connect_clicked => PostRowMsg::ToggleRead,
|
|
set_visible: settings::get_current_account().jwt.is_some(),
|
|
#[watch]
|
|
set_active: self.post.read,
|
|
},
|
|
gtk::Button {
|
|
set_icon_name: "edit-delete",
|
|
connect_clicked => PostRowMsg::DeletePost,
|
|
set_margin_start: 10,
|
|
#[watch]
|
|
set_visible: self.post.creator.id.0 == settings::get_current_account().id,
|
|
}
|
|
},
|
|
}
|
|
},
|
|
|
|
gtk::Separator {
|
|
set_margin_top: 10,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> {
|
|
Some(output)
|
|
}
|
|
|
|
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
|
|
let thumbnail = WebImage::builder()
|
|
.launch(get_web_image_url(value.post.thumbnail_url.clone()))
|
|
.detach();
|
|
let author_image = WebImage::builder()
|
|
.launch(get_web_image_url(value.creator.avatar.clone()))
|
|
.detach();
|
|
let community_image = WebImage::builder()
|
|
.launch(get_web_image_url(value.community.icon.clone()))
|
|
.detach();
|
|
let voting_row = VotingRowModel::builder()
|
|
.launch(VotingStats::from_post(value.counts.clone(), value.my_vote))
|
|
.detach();
|
|
|
|
Self {
|
|
post: value,
|
|
author_image,
|
|
community_image,
|
|
voting_row,
|
|
thumbnail,
|
|
image_size: 1500,
|
|
}
|
|
}
|
|
|
|
fn init_widgets(
|
|
&mut self,
|
|
_index: &Self::Index,
|
|
root: &Self::Root,
|
|
_returned_widget: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
|
sender: FactorySender<Self>,
|
|
) -> Self::Widgets {
|
|
match root.widget_ref().toplevel_window() {
|
|
Some(window) => {
|
|
self.image_size = window.allocated_width() / 8;
|
|
}
|
|
None => unreachable!(),
|
|
}
|
|
let thumbnail = self.thumbnail.widget();
|
|
let author_image = self.author_image.widget();
|
|
let community_image = self.community_image.widget();
|
|
let voting_row = self.voting_row.widget();
|
|
let widgets = view_output!();
|
|
widgets
|
|
}
|
|
|
|
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
|
|
match message {
|
|
PostRowMsg::OpenCommunity => {
|
|
sender.output(crate::AppMsg::OpenCommunity(self.post.community.id))
|
|
}
|
|
PostRowMsg::OpenPerson => {
|
|
sender.output(crate::AppMsg::OpenPerson(self.post.creator.id))
|
|
}
|
|
PostRowMsg::OpenPost => sender.output(crate::AppMsg::OpenPost(self.post.post.id)),
|
|
PostRowMsg::ToggleSaved => {
|
|
let post_id = self.post.post.id;
|
|
let new_state = !self.post.saved;
|
|
std::thread::spawn(move || match api::post::save_post(post_id, new_state) {
|
|
Ok(_) => sender.input(PostRowMsg::UpdateSaved(new_state)),
|
|
Err(err) => println!("{}", err),
|
|
});
|
|
}
|
|
PostRowMsg::ToggleRead => {
|
|
let post_id = self.post.post.id;
|
|
let new_state = !self.post.read;
|
|
std::thread::spawn(move || {
|
|
match api::post::mark_post_as_read(post_id, new_state) {
|
|
Ok(_) => sender.input(PostRowMsg::UpdateRead(new_state)),
|
|
Err(err) => println!("{}", err),
|
|
}
|
|
});
|
|
}
|
|
PostRowMsg::UpdateSaved(is_saved) => {
|
|
self.post.saved = is_saved;
|
|
}
|
|
PostRowMsg::UpdateRead(is_read) => {
|
|
self.post.read = is_read;
|
|
}
|
|
PostRowMsg::DeletePost => {
|
|
let post_id = self.post.post.id;
|
|
std::thread::spawn(move || {
|
|
let _ = api::post::delete_post(post_id);
|
|
sender.output_sender().emit(crate::AppMsg::OpenPosts);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|