Add support for viewing the inbox (mentions, replies)
This commit is contained in:
parent
b56a8247e1
commit
1ebfa7776f
|
@ -1,4 +1,4 @@
|
|||
use lemmy_api_common::{person::{GetPersonDetailsResponse, GetPersonDetails}};
|
||||
use lemmy_api_common::{person::{GetPersonDetailsResponse, GetPersonDetails, GetPersonMentionsResponse, GetRepliesResponse, MarkAllAsRead, GetReplies, GetPersonMentions}, lemmy_db_schema::CommentSortType};
|
||||
|
||||
use crate::settings;
|
||||
|
||||
|
@ -15,4 +15,33 @@ pub fn get_user(username: String, page: i64) -> std::result::Result<GetPersonDet
|
|||
|
||||
pub fn default_person() -> GetPersonDetailsResponse {
|
||||
serde_json::from_str(include_str!("../examples/person.json")).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mentions(page: i64, unread_only: bool) -> std::result::Result<GetPersonMentionsResponse, reqwest::Error> {
|
||||
let params = GetPersonMentions {
|
||||
auth: settings::get_current_account().jwt.unwrap(),
|
||||
unread_only: Some(unread_only),
|
||||
page: Some(page),
|
||||
sort: Some(CommentSortType::New),
|
||||
..Default::default()
|
||||
};
|
||||
super::get("/user/mentions", ¶ms)
|
||||
}
|
||||
|
||||
pub fn get_replies(page: i64, unread_only: bool) -> std::result::Result<GetRepliesResponse, reqwest::Error> {
|
||||
let params = GetReplies {
|
||||
auth: settings::get_current_account().jwt.unwrap(),
|
||||
page: Some(page),
|
||||
unread_only: Some(unread_only),
|
||||
sort: Some(CommentSortType::New),
|
||||
..Default::default()
|
||||
};
|
||||
super::get("/user/replies", ¶ms)
|
||||
}
|
||||
|
||||
pub fn mark_all_as_read() -> std::result::Result<GetRepliesResponse, reqwest::Error> {
|
||||
let params = MarkAllAsRead {
|
||||
auth: settings::get_current_account().jwt.unwrap(),
|
||||
};
|
||||
super::post("/user/mark_all_as_read", ¶ms)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
use lemmy_api_common::lemmy_db_views_actor::structs::CommentReplyView;
|
||||
use relm4::{prelude::*, factory::FactoryVecDeque};
|
||||
use gtk::prelude::*;
|
||||
|
||||
use crate::api;
|
||||
|
||||
use super::mention_row::MentionRow;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InboxType {
|
||||
Mentions,
|
||||
Replies,
|
||||
}
|
||||
|
||||
pub struct InboxPage {
|
||||
mentions: FactoryVecDeque<MentionRow>,
|
||||
page: i64,
|
||||
unread_only: bool,
|
||||
type_: InboxType
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InboxInput {
|
||||
UpdateType(InboxType),
|
||||
ToggleUnreadState,
|
||||
FetchInbox,
|
||||
UpdateInbox(Vec<CommentReplyView>)
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for InboxPage {
|
||||
type Init = ();
|
||||
type Input = InboxInput;
|
||||
type Output = crate::AppMsg;
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_margin_all: 15,
|
||||
set_spacing: 10,
|
||||
gtk::Button {
|
||||
set_label: "Mentions",
|
||||
connect_clicked => InboxInput::UpdateType(InboxType::Mentions),
|
||||
},
|
||||
gtk::Button {
|
||||
set_label: "Replies",
|
||||
connect_clicked => InboxInput::UpdateType(InboxType::Replies),
|
||||
},
|
||||
gtk::ToggleButton {
|
||||
set_active: false,
|
||||
set_label: "Show unread only",
|
||||
connect_clicked => InboxInput::ToggleUnreadState,
|
||||
},
|
||||
},
|
||||
gtk::ScrolledWindow {
|
||||
#[local_ref]
|
||||
mentions -> gtk::Box {
|
||||
set_vexpand: true,
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
_init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let mentions = FactoryVecDeque::new(gtk::Box::default(), sender.output_sender());
|
||||
let model = Self { mentions, page: 1, unread_only: false, type_: InboxType::Mentions };
|
||||
let mentions = model.mentions.widget();
|
||||
let widgets = view_output!();
|
||||
sender.input(InboxInput::FetchInbox);
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||
match message {
|
||||
InboxInput::FetchInbox => {
|
||||
let type_ = self.type_.clone();
|
||||
let page = self.page.clone();
|
||||
let unread_only = self.unread_only.clone();
|
||||
std::thread::spawn(move || {
|
||||
let comments = match type_ {
|
||||
InboxType::Mentions => {
|
||||
if let Ok(response) = api::user::get_mentions(page, unread_only) {
|
||||
let serialised = serde_json::to_string(&response.mentions).unwrap();
|
||||
serde_json::from_str(&serialised).ok()
|
||||
} else { None }
|
||||
}
|
||||
InboxType::Replies => {
|
||||
if let Ok(response) = api::user::get_replies(page, unread_only) {
|
||||
Some(response.replies)
|
||||
} else { None }
|
||||
}
|
||||
};
|
||||
if let Some(comments) = comments {
|
||||
sender.input(InboxInput::UpdateInbox(comments))
|
||||
};
|
||||
});
|
||||
}
|
||||
InboxInput::UpdateType(type_) => {
|
||||
self.type_ = type_;
|
||||
sender.input(InboxInput::FetchInbox);
|
||||
}
|
||||
InboxInput::ToggleUnreadState => {
|
||||
self.unread_only = !self.unread_only;
|
||||
sender.input(InboxInput::FetchInbox);
|
||||
}
|
||||
InboxInput::UpdateInbox(comments) => {
|
||||
for comment in comments {
|
||||
self.mentions.guard().push_back(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
use lemmy_api_common::lemmy_db_views_actor::structs::CommentReplyView;
|
||||
use relm4::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4_components::web_image::WebImage;
|
||||
|
||||
use crate::util::get_web_image_url;
|
||||
use crate::util::markdown_to_pango_markup;
|
||||
|
||||
use super::voting_row::VotingRowModel;
|
||||
use super::voting_row::VotingStats;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MentionRow {
|
||||
comment: CommentReplyView,
|
||||
creator_image: Controller<WebImage>,
|
||||
community_image: Controller<WebImage>,
|
||||
voting_row: Controller<VotingRowModel>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MentionRowMsg {
|
||||
OpenPerson,
|
||||
OpenPost,
|
||||
OpenCommunity,
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl FactoryComponent for MentionRow {
|
||||
type Init = CommentReplyView;
|
||||
type Input = MentionRowMsg;
|
||||
type Output = crate::AppMsg;
|
||||
type CommandOutput = ();
|
||||
type Widgets = PostViewWidgets;
|
||||
type ParentInput = crate::AppMsg;
|
||||
type ParentWidget = gtk::Box;
|
||||
|
||||
view! {
|
||||
root = gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 10,
|
||||
set_margin_end: 10,
|
||||
set_margin_start: 10,
|
||||
set_margin_top: 10,
|
||||
|
||||
gtk::Label {
|
||||
set_label: &self.comment.post.name,
|
||||
add_css_class: "font-bold",
|
||||
set_halign: gtk::Align::Start,
|
||||
},
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
gtk::Label {
|
||||
set_label: "in",
|
||||
},
|
||||
if self.comment.community.icon.clone().is_some() {
|
||||
gtk::Box {
|
||||
set_hexpand: false,
|
||||
set_margin_start: 10,
|
||||
set_margin_end: 10,
|
||||
#[local_ref]
|
||||
community_image -> gtk::Box {}
|
||||
}
|
||||
} else {
|
||||
gtk::Box {}
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &self.comment.community.title,
|
||||
connect_clicked => MentionRowMsg::OpenCommunity,
|
||||
},
|
||||
},
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 10,
|
||||
set_margin_top: 10,
|
||||
set_vexpand: false,
|
||||
|
||||
if self.comment.creator.avatar.is_some() {
|
||||
gtk::Box {
|
||||
set_hexpand: false,
|
||||
#[local_ref]
|
||||
creator_image -> gtk::Box {}
|
||||
}
|
||||
} else {
|
||||
gtk::Box {}
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &self.comment.creator.name,
|
||||
connect_clicked => MentionRowMsg::OpenPerson,
|
||||
},
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
#[watch]
|
||||
set_markup: &markdown_to_pango_markup(self.comment.comment.content.clone()),
|
||||
set_halign: gtk::Align::Start,
|
||||
set_use_markup: true,
|
||||
set_selectable: true,
|
||||
},
|
||||
|
||||
#[local_ref]
|
||||
voting_row -> gtk::Box {},
|
||||
|
||||
gtk::Separator {}
|
||||
}
|
||||
}
|
||||
|
||||
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 creator_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_comment(value.counts.clone(), value.my_vote)).detach();
|
||||
|
||||
Self { comment: value, creator_image, community_image, voting_row }
|
||||
}
|
||||
|
||||
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 {
|
||||
let creator_image = self.creator_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 {
|
||||
MentionRowMsg::OpenPerson => {
|
||||
sender.output(crate::AppMsg::OpenPerson(self.comment.creator.name.clone()));
|
||||
}
|
||||
MentionRowMsg::OpenPost => {
|
||||
sender.output(crate::AppMsg::OpenPost(self.comment.post.id.clone()));
|
||||
}
|
||||
MentionRowMsg::OpenCommunity => {
|
||||
sender.output(crate::AppMsg::OpenCommunity(self.comment.community.name.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,3 +5,5 @@ pub mod community_page;
|
|||
pub mod post_page;
|
||||
pub mod comment_row;
|
||||
pub mod voting_row;
|
||||
pub mod inbox_page;
|
||||
pub mod mention_row;
|
28
src/main.rs
28
src/main.rs
|
@ -5,7 +5,7 @@ pub mod util;
|
|||
pub mod dialogs;
|
||||
|
||||
use api::{user::default_person, community::default_community, post::default_post};
|
||||
use components::{post_row::PostRow, community_row::CommunityRow, profile_page::{ProfilePage, self}, community_page::{CommunityPage, self}, post_page::{PostPage, self}};
|
||||
use components::{post_row::PostRow, community_row::CommunityRow, profile_page::{ProfilePage, self}, community_page::{CommunityPage, self}, post_page::{PostPage, self}, inbox_page::InboxPage};
|
||||
use gtk::prelude::*;
|
||||
use lemmy_api_common::{lemmy_db_views_actor::structs::CommunityView, lemmy_db_views::structs::PostView, person::GetPersonDetailsResponse, lemmy_db_schema::{newtypes::PostId, ListingType}, post::GetPostResponse, community::GetCommunityResponse};
|
||||
use relm4::{prelude::*, factory::FactoryVecDeque, set_global_css, actions::{RelmAction, RelmActionGroup}};
|
||||
|
@ -22,7 +22,8 @@ enum AppState {
|
|||
Person,
|
||||
Post,
|
||||
Login,
|
||||
Message
|
||||
Message,
|
||||
Inbox
|
||||
}
|
||||
|
||||
struct App {
|
||||
|
@ -33,7 +34,8 @@ struct App {
|
|||
communities: FactoryVecDeque<CommunityRow>,
|
||||
profile_page: Controller<ProfilePage>,
|
||||
community_page: Controller<CommunityPage>,
|
||||
post_page: Controller<PostPage>
|
||||
post_page: Controller<PostPage>,
|
||||
inbox_page: Controller<InboxPage>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -54,7 +56,8 @@ pub enum AppMsg {
|
|||
OpenPerson(String),
|
||||
DoneFetchPerson(GetPersonDetailsResponse),
|
||||
OpenPost(PostId),
|
||||
DoneFetchPost(GetPostResponse)
|
||||
DoneFetchPost(GetPostResponse),
|
||||
OpenInbox
|
||||
}
|
||||
|
||||
#[relm4::component]
|
||||
|
@ -83,6 +86,10 @@ impl SimpleComponent for App {
|
|||
set_label: "Subscribed",
|
||||
connect_clicked => AppMsg::StartFetchPosts(Some(ListingType::Subscribed)),
|
||||
},
|
||||
pack_start = >k::Button {
|
||||
set_label: "Inbox",
|
||||
connect_clicked => AppMsg::OpenInbox,
|
||||
},
|
||||
pack_start = >k::Button {
|
||||
set_label: "Communities",
|
||||
connect_clicked => AppMsg::ViewCommunities(None),
|
||||
|
@ -247,6 +254,12 @@ impl SimpleComponent for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
AppState::Inbox => {
|
||||
gtk::ScrolledWindow {
|
||||
#[local_ref]
|
||||
inbox_page -> gtk::Box {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,8 +288,9 @@ impl SimpleComponent for App {
|
|||
let profile_page = ProfilePage::builder().launch(default_person()).forward(sender.input_sender(), |msg| msg);
|
||||
let community_page = CommunityPage::builder().launch(default_community().community_view).forward(sender.input_sender(), |msg| msg);
|
||||
let post_page = PostPage::builder().launch(default_post()).forward(sender.input_sender(), |msg| msg);
|
||||
let inbox_page = InboxPage::builder().launch(()).forward(sender.input_sender(), |msg| msg);
|
||||
|
||||
let model = App { state, posts, communities, profile_page, community_page, post_page, message: None, latest_action: None };
|
||||
let model = App { state, posts, communities, profile_page, community_page, post_page, inbox_page, message: None, latest_action: None };
|
||||
|
||||
// fetch posts if that's the initial page
|
||||
if !instance_url.is_empty() { sender.input(AppMsg::StartFetchPosts(None)) };
|
||||
|
@ -287,6 +301,7 @@ impl SimpleComponent for App {
|
|||
let profile_page = model.profile_page.widget();
|
||||
let community_page = model.community_page.widget();
|
||||
let post_page = model.post_page.widget();
|
||||
let inbox_page = model.inbox_page.widget();
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
|
@ -447,6 +462,9 @@ impl SimpleComponent for App {
|
|||
AppMsg::Retry => {
|
||||
sender.input(self.latest_action.clone().unwrap_or(AppMsg::StartFetchPosts(None)));
|
||||
}
|
||||
AppMsg::OpenInbox => {
|
||||
self.state = AppState::Inbox;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue