From fbf4b8dfa5ce9fecb3727d8469ba0345076406fb Mon Sep 17 00:00:00 2001 From: Bnyro Date: Mon, 26 Jun 2023 10:58:21 +0200 Subject: [PATCH] Reformat project and change package id --- .gitignore | 6 +- .vscode/settings.json | 15 +- build-aux/com.lemmy-gtk.lemoa.Devel.json | 2 +- meson.build | 2 +- src/api/auth.rs | 6 +- src/api/comment.rs | 10 +- src/api/communities.rs | 14 +- src/api/community.rs | 5 +- src/api/image.rs | 23 +-- src/api/mod.rs | 34 ++-- src/api/moderation.rs | 7 +- src/api/post.rs | 16 +- src/api/posts.rs | 12 +- src/api/search.rs | 13 +- src/api/site.rs | 2 +- src/api/user.rs | 23 ++- src/components/comment_row.rs | 51 +++--- src/components/community_page.rs | 67 +++++--- src/components/community_row.rs | 31 ++-- src/components/inbox_page.rs | 29 ++-- src/components/mention_row.rs | 36 +++-- src/components/mod.rs | 14 +- src/components/post_page.rs | 107 +++++++++---- src/components/post_row.rs | 45 ++++-- src/components/profile_page.rs | 17 +- src/components/voting_row.rs | 61 +++++-- src/dialogs/editor.rs | 66 +++++--- src/dialogs/mod.rs | 2 +- src/main.rs | 194 +++++++++++++++++------ src/settings.rs | 6 +- src/util.rs | 12 +- 31 files changed, 644 insertions(+), 284 deletions(-) diff --git a/.gitignore b/.gitignore index 51b5537..b9bc762 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /target /.cargo -/_build \ No newline at end of file +/_build +/builddir +/.flatpak +/.flatpak-builder +/.vscode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b69029b..440d07b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,16 @@ { - "C_Cpp.default.compileCommands": "builddir/vscode_compile_commands.json" + "C_Cpp.default.compileCommands": "builddir/vscode_compile_commands.json", + "files.watcherExclude": { + "**/.dart_tool": true, + ".flatpak/**": true, + "_build/**": true + }, + "mesonbuild.configureOnOpen": false, + "mesonbuild.buildFolder": "_build", + "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh", + "rust-analyzer.server.path": "${workspaceFolder}/.flatpak/rust-analyzer.sh", + "rust-analyzer.runnables.command": "${workspaceFolder}/.flatpak/cargo.sh", + "rust-analyzer.files.excludeDirs": [ + ".flatpak" + ] } diff --git a/build-aux/com.lemmy-gtk.lemoa.Devel.json b/build-aux/com.lemmy-gtk.lemoa.Devel.json index cd5eb8f..23bedab 100644 --- a/build-aux/com.lemmy-gtk.lemoa.Devel.json +++ b/build-aux/com.lemmy-gtk.lemoa.Devel.json @@ -1,5 +1,5 @@ { - "id": "com.lemmy-gtk.lemoa", + "id": "com.lemmygtk.lemoa", "runtime": "org.gnome.Platform", "runtime-version": "44", "sdk": "org.gnome.Sdk", diff --git a/meson.build b/meson.build index 002a75e..78cf8df 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( gnome = import('gnome') -application_id = 'com.lemmy-gtk.lemoa' +application_id = 'com.lemmygtk.lemoa' dependency('glib-2.0', version: '>= 2.70') dependency('gio-2.0', version: '>= 2.70') diff --git a/src/api/auth.rs b/src/api/auth.rs index 5373ae2..46e759c 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -1,6 +1,10 @@ use lemmy_api_common::{person::Login, sensitive::Sensitive}; -pub fn login(username_or_email: String, password: String, totp_token: Option) -> std::result::Result { +pub fn login( + username_or_email: String, + password: String, + totp_token: Option, +) -> std::result::Result { let params = Login { username_or_email: Sensitive::new(username_or_email), password: Sensitive::new(password), diff --git a/src/api/comment.rs b/src/api/comment.rs index 8771dbd..13e2ca1 100644 --- a/src/api/comment.rs +++ b/src/api/comment.rs @@ -1,4 +1,7 @@ -use lemmy_api_common::{comment::{CommentResponse, CreateComment, CreateCommentLike, DeleteComment, EditComment}, lemmy_db_schema::newtypes::{PostId, CommentId}}; +use lemmy_api_common::{ + comment::{CommentResponse, CreateComment, CreateCommentLike, DeleteComment, EditComment}, + lemmy_db_schema::newtypes::{CommentId, PostId}, +}; use crate::settings; @@ -27,10 +30,7 @@ pub fn like_comment(comment_id: CommentId, score: i16) -> Result Result { +pub fn edit_comment(body: String, comment_id: i32) -> Result { let params = EditComment { content: Some(body), comment_id: CommentId(comment_id), diff --git a/src/api/communities.rs b/src/api/communities.rs index 39ac258..457902a 100644 --- a/src/api/communities.rs +++ b/src/api/communities.rs @@ -1,9 +1,17 @@ -use lemmy_api_common::{community::{ListCommunities, ListCommunitiesResponse}, lemmy_db_schema::{SortType, SearchType, ListingType}, lemmy_db_views_actor::structs::CommunityView}; +use lemmy_api_common::{ + community::{ListCommunities, ListCommunitiesResponse}, + lemmy_db_schema::{ListingType, SearchType, SortType}, + lemmy_db_views_actor::structs::CommunityView, +}; -use crate::settings; use super::search; +use crate::settings; -pub fn fetch_communities(page: i64, query: Option,listing_type: Option) -> std::result::Result, reqwest::Error> { +pub fn fetch_communities( + page: i64, + query: Option, + listing_type: Option, +) -> std::result::Result, reqwest::Error> { if query.is_none() || query.clone().unwrap().trim().is_empty() { let params = ListCommunities { type_: listing_type, diff --git a/src/api/community.rs b/src/api/community.rs index f1ff1e7..1817fac 100644 --- a/src/api/community.rs +++ b/src/api/community.rs @@ -1,4 +1,7 @@ -use lemmy_api_common::{community::{GetCommunity, GetCommunityResponse, CommunityResponse, FollowCommunity}, lemmy_db_schema::newtypes::CommunityId}; +use lemmy_api_common::{ + community::{CommunityResponse, FollowCommunity, GetCommunity, GetCommunityResponse}, + lemmy_db_schema::newtypes::CommunityId, +}; use crate::settings; diff --git a/src/api/image.rs b/src/api/image.rs index 4cd726c..e301aac 100644 --- a/src/api/image.rs +++ b/src/api/image.rs @@ -1,9 +1,9 @@ -use reqwest::blocking::multipart::Part; -use std::io::Read; -use std::fs::File; use crate::settings; -use serde::Deserialize; use rand::distributions::{Alphanumeric, DistString}; +use reqwest::blocking::multipart::Part; +use serde::Deserialize; +use std::fs::File; +use std::io::Read; use super::CLIENT; @@ -21,9 +21,7 @@ struct UploadImageFile { pub delete_token: String, } -pub fn upload_image( - image: std::path::PathBuf, -) -> Result { +pub fn upload_image(image: std::path::PathBuf) -> Result { let mime_type = mime_guess::from_path(image.clone()).first(); let file_name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); @@ -31,17 +29,22 @@ pub fn upload_image( let mut data = Vec::new(); file.read_to_end(&mut data).unwrap(); - let part = Part::bytes(data).file_name(file_name).mime_str(&mime_type.unwrap().essence_str())?; + let part = Part::bytes(data) + .file_name(file_name) + .mime_str(&mime_type.unwrap().essence_str())?; let form = reqwest::blocking::multipart::Form::new().part("images[]", part); let account = settings::get_current_account(); let base_url = account.instance_url; let path = format!("{}/pictrs/image", base_url); let res: UploadImageResponse = CLIENT .post(&path) - .header("cookie", format!("jwt={}", account.jwt.unwrap().to_string())) + .header( + "cookie", + format!("jwt={}", account.jwt.unwrap().to_string()), + ) .multipart(form) .send()? .json()?; Ok(format!("{}/pictrs/image/{}", base_url, res.files[0].file)) -} \ No newline at end of file +} diff --git a/src/api/mod.rs b/src/api/mod.rs index bb964fc..d45d30b 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -2,26 +2,24 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::settings::get_current_account; +pub mod auth; +pub mod comment; pub mod communities; pub mod community; +pub mod image; +pub mod moderation; pub mod post; pub mod posts; pub mod search; -pub mod user; -pub mod auth; -pub mod moderation; -pub mod comment; pub mod site; -pub mod image; +pub mod user; static API_VERSION: &str = "v3"; -use reqwest::blocking::Client; use relm4::once_cell::sync::Lazy; +use reqwest::blocking::Client; -pub static CLIENT: Lazy = Lazy::new(|| { - Client::new() -}); +pub static CLIENT: Lazy = Lazy::new(|| Client::new()); fn get_api_url() -> String { format!("{}/api/{}", get_current_account().instance_url, API_VERSION).to_string() @@ -36,11 +34,7 @@ where T: DeserializeOwned, Params: Serialize + std::fmt::Debug, { - CLIENT - .get(&get_url(path)) - .query(¶ms) - .send()? - .json() + CLIENT.get(&get_url(path)).query(¶ms).send()?.json() } fn post(path: &str, params: &Params) -> Result @@ -48,11 +42,7 @@ where T: DeserializeOwned, Params: Serialize + std::fmt::Debug, { - CLIENT - .post(&get_url(path)) - .json(¶ms) - .send()? - .json() + CLIENT.post(&get_url(path)).json(¶ms).send()?.json() } fn put(path: &str, params: &Params) -> Result @@ -60,9 +50,5 @@ where T: DeserializeOwned, Params: Serialize + std::fmt::Debug, { - CLIENT - .put(&get_url(path)) - .json(¶ms) - .send()? - .json() + CLIENT.put(&get_url(path)).json(¶ms).send()?.json() } diff --git a/src/api/moderation.rs b/src/api/moderation.rs index ad660fd..e9a1a83 100644 --- a/src/api/moderation.rs +++ b/src/api/moderation.rs @@ -1,4 +1,9 @@ -use lemmy_api_common::{lemmy_db_schema::newtypes::{CommentId, PostId}, comment::{RemoveComment, CommentResponse}, sensitive::Sensitive, post::{PostResponse, RemovePost}}; +use lemmy_api_common::{ + comment::{CommentResponse, RemoveComment}, + lemmy_db_schema::newtypes::{CommentId, PostId}, + post::{PostResponse, RemovePost}, + sensitive::Sensitive, +}; pub fn remove_post( post_id: i32, diff --git a/src/api/post.rs b/src/api/post.rs index f31a94b..cff8a4b 100644 --- a/src/api/post.rs +++ b/src/api/post.rs @@ -1,4 +1,14 @@ -use lemmy_api_common::{post::{GetPost, GetPostResponse, PostResponse, CreatePost, CreatePostLike, DeletePost, EditPost}, lemmy_db_schema::{newtypes::{PostId, CommunityId}, CommentSortType, ListingType}, comment::{GetComments, GetCommentsResponse}, lemmy_db_views::structs::CommentView}; +use lemmy_api_common::{ + comment::{GetComments, GetCommentsResponse}, + lemmy_db_schema::{ + newtypes::{CommunityId, PostId}, + CommentSortType, ListingType, + }, + lemmy_db_views::structs::CommentView, + post::{ + CreatePost, CreatePostLike, DeletePost, EditPost, GetPost, GetPostResponse, PostResponse, + }, +}; use crate::settings; @@ -6,7 +16,7 @@ pub fn get_post(id: PostId) -> Result { let params = GetPost { id: Some(id), comment_id: None, - auth: settings::get_current_account().jwt + auth: settings::get_current_account().jwt, }; super::get("/post", ¶ms) @@ -54,7 +64,7 @@ pub fn edit_post( name: String, url: Option, body: String, - post_id: i32 + post_id: i32, ) -> Result { let params = EditPost { name: Some(name), diff --git a/src/api/posts.rs b/src/api/posts.rs index b53dfb2..51710c4 100644 --- a/src/api/posts.rs +++ b/src/api/posts.rs @@ -1,8 +1,16 @@ -use lemmy_api_common::{post::{GetPostsResponse, GetPosts}, lemmy_db_views::structs::PostView, lemmy_db_schema::ListingType}; +use lemmy_api_common::{ + lemmy_db_schema::ListingType, + lemmy_db_views::structs::PostView, + post::{GetPosts, GetPostsResponse}, +}; use crate::settings; -pub fn list_posts(page: i64, community_name: Option, listing_type: Option) -> std::result::Result, reqwest::Error> { +pub fn list_posts( + page: i64, + community_name: Option, + listing_type: Option, +) -> std::result::Result, reqwest::Error> { let params = GetPosts { page: Some(page), type_: listing_type, diff --git a/src/api/search.rs b/src/api/search.rs index 0a0da55..2cdea0b 100644 --- a/src/api/search.rs +++ b/src/api/search.rs @@ -1,8 +1,15 @@ -use lemmy_api_common::{site::{SearchResponse, Search}, lemmy_db_schema::{SortType, SearchType}}; +use lemmy_api_common::{ + lemmy_db_schema::{SearchType, SortType}, + site::{Search, SearchResponse}, +}; use crate::settings; -pub fn fetch_search(page: i64, query: String, search_type: Option) -> std::result::Result { +pub fn fetch_search( + page: i64, + query: String, + search_type: Option, +) -> std::result::Result { let params = Search { q: query, sort: Some(SortType::TopMonth), @@ -13,4 +20,4 @@ pub fn fetch_search(page: i64, query: String, search_type: Option) - }; super::get("/search", ¶ms) -} \ No newline at end of file +} diff --git a/src/api/site.rs b/src/api/site.rs index 7f13d0d..6618f02 100644 --- a/src/api/site.rs +++ b/src/api/site.rs @@ -1,4 +1,4 @@ -use lemmy_api_common::site::{GetSiteResponse, GetSite}; +use lemmy_api_common::site::{GetSite, GetSiteResponse}; use crate::settings; diff --git a/src/api/user.rs b/src/api/user.rs index 7057207..317cf65 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -1,8 +1,17 @@ -use lemmy_api_common::{person::{GetPersonDetailsResponse, GetPersonDetails, GetPersonMentionsResponse, GetRepliesResponse, MarkAllAsRead, GetReplies, GetPersonMentions}, lemmy_db_schema::{CommentSortType, newtypes::PersonId}}; +use lemmy_api_common::{ + lemmy_db_schema::{newtypes::PersonId, CommentSortType}, + person::{ + GetPersonDetails, GetPersonDetailsResponse, GetPersonMentions, GetPersonMentionsResponse, + GetReplies, GetRepliesResponse, MarkAllAsRead, + }, +}; use crate::settings; -pub fn get_user(id: PersonId, page: i64) -> std::result::Result { +pub fn get_user( + id: PersonId, + page: i64, +) -> std::result::Result { let params = GetPersonDetails { page: Some(page), person_id: Some(id), @@ -17,7 +26,10 @@ 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 { +pub fn get_mentions( + page: i64, + unread_only: bool, +) -> std::result::Result { let params = GetPersonMentions { auth: settings::get_current_account().jwt.unwrap(), unread_only: Some(unread_only), @@ -28,7 +40,10 @@ pub fn get_mentions(page: i64, unread_only: bool) -> std::result::Result std::result::Result { +pub fn get_replies( + page: i64, + unread_only: bool, +) -> std::result::Result { let params = GetReplies { auth: settings::get_current_account().jwt.unwrap(), page: Some(page), diff --git a/src/components/comment_row.rs b/src/components/comment_row.rs index 58cd299..a456ad1 100644 --- a/src/components/comment_row.rs +++ b/src/components/comment_row.rs @@ -1,13 +1,13 @@ +use gtk::prelude::*; use lemmy_api_common::lemmy_db_views::structs::CommentView; use relm4::prelude::*; -use gtk::prelude::*; use relm4_components::web_image::WebImage; use crate::api; use crate::dialogs::editor::EditorData; +use crate::settings; use crate::util::get_web_image_url; use crate::util::markdown_to_pango_markup; -use crate::settings; use super::post_page::PostInput; use super::voting_row::VotingRowModel; @@ -17,14 +17,14 @@ use super::voting_row::VotingStats; pub struct CommentRow { pub comment: CommentView, avatar: Controller, - voting_row: Controller + voting_row: Controller, } #[derive(Debug)] pub enum CommentRowMsg { OpenPerson, DeleteComment, - OpenEditCommentDialog + OpenEditCommentDialog, } #[relm4::factory(pub)] @@ -65,7 +65,7 @@ impl FactoryComponent for CommentRow { connect_clicked => CommentRowMsg::OpenPerson, }, }, - + gtk::Label { #[watch] set_markup: &markdown_to_pango_markup(self.comment.comment.content.clone()), @@ -99,7 +99,7 @@ impl FactoryComponent for CommentRow { gtk::Box {} } }, - + gtk::Separator {} } } @@ -109,19 +109,30 @@ impl FactoryComponent for CommentRow { } fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> Self { - let avatar = WebImage::builder().launch(get_web_image_url(value.creator.avatar.clone())).detach(); - let voting_row = VotingRowModel::builder().launch(VotingStats::from_comment(value.counts.clone(), value.my_vote)).detach(); + let avatar = WebImage::builder() + .launch(get_web_image_url(value.creator.avatar.clone())) + .detach(); + let voting_row = VotingRowModel::builder() + .launch(VotingStats::from_comment( + value.counts.clone(), + value.my_vote, + )) + .detach(); - Self { comment: value, avatar, voting_row } + Self { + comment: value, + avatar, + voting_row, + } } fn init_widgets( - &mut self, - _index: &Self::Index, - root: &Self::Root, - _returned_widget: &::ReturnedWidget, - sender: FactorySender, - ) -> Self::Widgets { + &mut self, + _index: &Self::Index, + root: &Self::Root, + _returned_widget: &::ReturnedWidget, + sender: FactorySender, + ) -> Self::Widgets { let community_image = self.avatar.widget(); let voting_row = self.voting_row.widget(); let widgets = view_output!(); @@ -131,13 +142,17 @@ impl FactoryComponent for CommentRow { fn update(&mut self, message: Self::Input, sender: FactorySender) { match message { CommentRowMsg::OpenPerson => { - sender.output(PostInput::PassAppMessage(crate::AppMsg::OpenPerson(self.comment.creator.id.clone()))); + sender.output(PostInput::PassAppMessage(crate::AppMsg::OpenPerson( + self.comment.creator.id.clone(), + ))); } CommentRowMsg::DeleteComment => { let comment_id = self.comment.comment.id; std::thread::spawn(move || { let _ = api::comment::delete_comment(comment_id); - let _ = sender.output(PostInput::PassAppMessage(crate::AppMsg::StartFetchPosts(None, true))); + let _ = sender.output(PostInput::PassAppMessage( + crate::AppMsg::StartFetchPosts(None, true), + )); }); } CommentRowMsg::OpenEditCommentDialog => { @@ -151,4 +166,4 @@ impl FactoryComponent for CommentRow { } } } -} \ No newline at end of file +} diff --git a/src/components/community_page.rs b/src/components/community_page.rs index d0fb694..93780f6 100644 --- a/src/components/community_page.rs +++ b/src/components/community_page.rs @@ -1,10 +1,16 @@ -use crate::{util::markdown_to_pango_markup, dialogs::editor::{EditorDialog, DialogMsg, EditorOutput, EditorType, EditorData}}; -use lemmy_api_common::{lemmy_db_views::structs::PostView, lemmy_db_views_actor::structs::CommunityView, lemmy_db_schema::SubscribedType}; -use relm4::{prelude::*, factory::FactoryVecDeque, MessageBroker}; +use crate::{ + dialogs::editor::{DialogMsg, EditorData, EditorDialog, EditorOutput, EditorType}, + util::markdown_to_pango_markup, +}; use gtk::prelude::*; +use lemmy_api_common::{ + lemmy_db_schema::SubscribedType, lemmy_db_views::structs::PostView, + lemmy_db_views_actor::structs::CommunityView, +}; +use relm4::{factory::FactoryVecDeque, prelude::*, MessageBroker}; use relm4_components::web_image::WebImage; -use crate::{api, util::get_web_image_msg, settings}; +use crate::{api, settings, util::get_web_image_msg}; use super::post_row::PostRow; @@ -16,7 +22,7 @@ pub struct CommunityPage { posts: FactoryVecDeque, #[allow(dead_code)] create_post_dialog: Controller, - current_posts_page: i64 + current_posts_page: i64, } #[derive(Debug)] @@ -29,7 +35,7 @@ pub enum CommunityInput { CreatedPost(PostView), ToggleSubscription, UpdateSubscriptionState(SubscribedType), - None + None, } #[relm4::component(pub)] @@ -46,7 +52,7 @@ impl SimpleComponent for CommunityPage { set_orientation: gtk::Orientation::Vertical, set_vexpand: false, set_margin_all: 10, - + #[local_ref] avatar -> gtk::Box { set_size_request: (100, 100), @@ -137,12 +143,18 @@ impl SimpleComponent for CommunityPage { let dialog = EditorDialog::builder() .transient_for(root) .launch_with_broker(EditorType::Post, &COMMUNITY_PAGE_BROKER) - .forward(sender.input_sender(), |msg| match msg { + .forward(sender.input_sender(), |msg| match msg { EditorOutput::CreateRequest(post, _) => CommunityInput::CreatePostRequest(post), - _ => CommunityInput::None + _ => CommunityInput::None, }); - let model = CommunityPage { info: init, avatar, posts, create_post_dialog: dialog, current_posts_page: 0 }; + let model = CommunityPage { + info: init, + avatar, + posts, + create_post_dialog: dialog, + current_posts_page: 0, + }; let avatar = model.avatar.widget(); let posts = model.posts.widget(); let widgets = view_output!(); @@ -154,10 +166,13 @@ impl SimpleComponent for CommunityPage { match message { CommunityInput::UpdateCommunity(community) => { self.info = community.clone(); - self.avatar.emit(get_web_image_msg(community.community.icon)); + self.avatar + .emit(get_web_image_msg(community.community.icon)); self.posts.guard().clear(); self.current_posts_page = 0; - if community.counts.posts == 0 { return; } + if community.counts.posts == 0 { + return; + } sender.input(CommunityInput::FetchPosts); } CommunityInput::FetchPosts => { @@ -176,9 +191,7 @@ impl SimpleComponent for CommunityPage { self.posts.guard().push_back(post); } } - CommunityInput::OpenCreatePostDialog => { - COMMUNITY_PAGE_BROKER.send(DialogMsg::Show) - } + CommunityInput::OpenCreatePostDialog => COMMUNITY_PAGE_BROKER.send(DialogMsg::Show), CommunityInput::CreatedPost(post) => { self.posts.guard().push_front(post); } @@ -187,23 +200,35 @@ impl SimpleComponent for CommunityPage { std::thread::spawn(move || { let message = match api::post::create_post(post.name, post.body, post.url, id) { Ok(post) => Some(CommunityInput::CreatedPost(post.post_view)), - Err(err) => { println!("{}", err.to_string()); None } + Err(err) => { + println!("{}", err.to_string()); + None + } + }; + if let Some(message) = message { + sender.input(message) }; - if let Some(message) = message { sender.input(message) }; }); } CommunityInput::ToggleSubscription => { let community_id = self.info.community.id.0; let new_state = match self.info.subscribed { SubscribedType::NotSubscribed => true, - _ => false + _ => false, }; std::thread::spawn(move || { let message = match api::community::follow_community(community_id, new_state) { - Ok(community) => Some(CommunityInput::UpdateSubscriptionState(community.community_view.subscribed)), - Err(err) => { println!("{}", err.to_string()); None } + Ok(community) => Some(CommunityInput::UpdateSubscriptionState( + community.community_view.subscribed, + )), + Err(err) => { + println!("{}", err.to_string()); + None + } + }; + if message.is_some() { + sender.input(message.unwrap()) }; - if message.is_some() { sender.input(message.unwrap()) }; }); } CommunityInput::UpdateSubscriptionState(state) => { diff --git a/src/components/community_row.rs b/src/components/community_row.rs index 79be961..dbd75f8 100644 --- a/src/components/community_row.rs +++ b/src/components/community_row.rs @@ -1,6 +1,6 @@ +use gtk::prelude::*; use lemmy_api_common::lemmy_db_views_actor::structs::CommunityView; use relm4::prelude::*; -use gtk::prelude::*; use relm4_components::web_image::WebImage; use crate::util::get_web_image_url; @@ -75,18 +75,23 @@ impl FactoryComponent for CommunityRow { } fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> Self { - let community_image= WebImage::builder().launch(get_web_image_url(value.community.clone().icon)).detach(); + let community_image = WebImage::builder() + .launch(get_web_image_url(value.community.clone().icon)) + .detach(); - Self { community: value, community_image } + Self { + community: value, + community_image, + } } fn init_widgets( - &mut self, - _index: &Self::Index, - root: &Self::Root, - _returned_widget: &::ReturnedWidget, - sender: FactorySender, - ) -> Self::Widgets { + &mut self, + _index: &Self::Index, + root: &Self::Root, + _returned_widget: &::ReturnedWidget, + sender: FactorySender, + ) -> Self::Widgets { let community_image = self.community_image.widget(); let widgets = view_output!(); widgets @@ -94,9 +99,9 @@ impl FactoryComponent for CommunityRow { fn update(&mut self, message: Self::Input, sender: FactorySender) { match message { - CommunityRowMsg::OpenCommunity => { - sender.output(crate::AppMsg::OpenCommunity(self.community.community.id.clone())) - } + CommunityRowMsg::OpenCommunity => sender.output(crate::AppMsg::OpenCommunity( + self.community.community.id.clone(), + )), } } -} \ No newline at end of file +} diff --git a/src/components/inbox_page.rs b/src/components/inbox_page.rs index c3ef5b4..8ff974b 100644 --- a/src/components/inbox_page.rs +++ b/src/components/inbox_page.rs @@ -1,6 +1,6 @@ -use lemmy_api_common::lemmy_db_views_actor::structs::CommentReplyView; -use relm4::{prelude::*, factory::FactoryVecDeque}; use gtk::prelude::*; +use lemmy_api_common::lemmy_db_views_actor::structs::CommentReplyView; +use relm4::{factory::FactoryVecDeque, prelude::*}; use crate::api; @@ -16,7 +16,7 @@ pub struct InboxPage { mentions: FactoryVecDeque, page: i64, unread_only: bool, - type_: InboxType + type_: InboxType, } #[derive(Debug)] @@ -70,12 +70,17 @@ impl SimpleComponent for InboxPage { } fn init( - _init: Self::Init, - root: &Self::Root, - sender: ComponentSender, - ) -> ComponentParts { + _init: Self::Init, + root: &Self::Root, + sender: ComponentSender, + ) -> ComponentParts { let mentions = FactoryVecDeque::new(gtk::Box::default(), sender.output_sender()); - let model = Self { mentions, page: 1, unread_only: false, type_: InboxType::Mentions }; + let model = Self { + mentions, + page: 1, + unread_only: false, + type_: InboxType::Mentions, + }; let mentions = model.mentions.widget(); let widgets = view_output!(); ComponentParts { model, widgets } @@ -94,12 +99,16 @@ impl SimpleComponent for InboxPage { // It's just a different object, but its contents are exactly the same let serialised = serde_json::to_string(&response.mentions).unwrap(); serde_json::from_str(&serialised).ok() - } else { None } + } else { + None + } } InboxType::Replies => { if let Ok(response) = api::user::get_replies(page, unread_only) { Some(response.replies) - } else { None } + } else { + None + } } }; if let Some(comments) = comments { diff --git a/src/components/mention_row.rs b/src/components/mention_row.rs index 4b67e49..cc50aa5 100644 --- a/src/components/mention_row.rs +++ b/src/components/mention_row.rs @@ -1,6 +1,6 @@ +use gtk::prelude::*; 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; @@ -14,7 +14,7 @@ pub struct MentionRow { comment: CommentReplyView, creator_image: Controller, community_image: Controller, - voting_row: Controller + voting_row: Controller, } #[derive(Debug)] @@ -93,7 +93,7 @@ impl FactoryComponent for MentionRow { connect_clicked => MentionRowMsg::OpenPerson, }, }, - + gtk::Label { #[watch] set_markup: &markdown_to_pango_markup(self.comment.comment.content.clone()), @@ -104,7 +104,7 @@ impl FactoryComponent for MentionRow { #[local_ref] voting_row -> gtk::Box {}, - + gtk::Separator {} } } @@ -114,11 +114,25 @@ impl FactoryComponent for MentionRow { } fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> 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(); + 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 } + Self { + comment: value, + creator_image, + community_image, + voting_row, + } } fn init_widgets( @@ -144,8 +158,10 @@ impl FactoryComponent for MentionRow { sender.output(crate::AppMsg::OpenPost(self.comment.post.id.clone())); } MentionRowMsg::OpenCommunity => { - sender.output(crate::AppMsg::OpenCommunity(self.comment.community.id.clone())); + sender.output(crate::AppMsg::OpenCommunity( + self.comment.community.id.clone(), + )); } } } -} \ No newline at end of file +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 5e2446b..395e425 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,9 +1,9 @@ -pub mod post_row; -pub mod community_row; -pub mod profile_page; -pub mod community_page; -pub mod post_page; pub mod comment_row; -pub mod voting_row; +pub mod community_page; +pub mod community_row; pub mod inbox_page; -pub mod mention_row; \ No newline at end of file +pub mod mention_row; +pub mod post_page; +pub mod post_row; +pub mod profile_page; +pub mod voting_row; diff --git a/src/components/post_page.rs b/src/components/post_page.rs index 7403bc2..6fd8cca 100644 --- a/src/components/post_page.rs +++ b/src/components/post_page.rs @@ -1,11 +1,22 @@ -use lemmy_api_common::{lemmy_db_views::structs::{CommentView, PostView}, post::GetPostResponse}; -use relm4::{prelude::*, factory::FactoryVecDeque, MessageBroker}; use gtk::prelude::*; +use lemmy_api_common::{ + lemmy_db_views::structs::{CommentView, PostView}, + post::GetPostResponse, +}; +use relm4::{factory::FactoryVecDeque, prelude::*, MessageBroker}; use relm4_components::web_image::WebImage; -use crate::{api, util::{get_web_image_msg, get_web_image_url, markdown_to_pango_markup}, dialogs::editor::{EditorDialog, EditorOutput, DialogMsg, EditorType, EditorData}, settings}; +use crate::{ + api, + dialogs::editor::{DialogMsg, EditorData, EditorDialog, EditorOutput, EditorType}, + settings, + util::{get_web_image_msg, get_web_image_url, markdown_to_pango_markup}, +}; -use super::{comment_row::CommentRow, voting_row::{VotingRowModel, VotingStats, VotingRowInput}}; +use super::{ + comment_row::CommentRow, + voting_row::{VotingRowInput, VotingRowModel, VotingStats}, +}; pub static POST_PAGE_BROKER: MessageBroker = MessageBroker::new(); @@ -17,7 +28,7 @@ pub struct PostPage { comments: FactoryVecDeque, #[allow(dead_code)] create_comment_dialog: Controller, - voting_row: Controller + voting_row: Controller, } #[derive(Debug)] @@ -52,7 +63,7 @@ impl SimpleComponent for PostPage { set_orientation: gtk::Orientation::Vertical, set_vexpand: false, set_margin_all: 10, - + #[local_ref] image -> gtk::Box { set_height_request: 100, @@ -196,16 +207,26 @@ impl SimpleComponent for PostPage { let dialog = EditorDialog::builder() .transient_for(root) .launch_with_broker(EditorType::Comment, &POST_PAGE_BROKER) - .forward(sender.input_sender(), |msg| match msg { + .forward(sender.input_sender(), |msg| match msg { EditorOutput::CreateRequest(comment, _) => PostInput::CreateCommentRequest(comment), EditorOutput::EditRequest(post, type_) => match type_ { EditorType::Post => PostInput::EditPostRequest(post), - EditorType::Comment => PostInput::EditCommentRequest(post) - } + EditorType::Comment => PostInput::EditCommentRequest(post), + }, }); - let voting_row = VotingRowModel::builder().launch(VotingStats::default()).detach(); - let model = PostPage { info: init, image, comments, creator_avatar, community_avatar, create_comment_dialog: dialog, voting_row }; - + let voting_row = VotingRowModel::builder() + .launch(VotingStats::default()) + .detach(); + let model = PostPage { + info: init, + image, + comments, + creator_avatar, + community_avatar, + create_comment_dialog: dialog, + voting_row, + }; + let image = model.image.widget(); let comments = model.comments.widget(); let creator_avatar = model.creator_avatar.widget(); @@ -220,16 +241,25 @@ impl SimpleComponent for PostPage { match message { PostInput::UpdatePost(post) => { self.info = post.clone(); - - self.image.emit(get_web_image_msg(post.post_view.post.thumbnail_url)); - self.community_avatar.emit(get_web_image_msg(post.community_view.community.icon)); - self.creator_avatar.emit(get_web_image_msg(post.post_view.creator.avatar)); - self.voting_row.emit(VotingRowInput::UpdateStats(VotingStats::from_post(post.post_view.counts.clone(), post.post_view.my_vote))); + self.image + .emit(get_web_image_msg(post.post_view.post.thumbnail_url)); + self.community_avatar + .emit(get_web_image_msg(post.community_view.community.icon)); + self.creator_avatar + .emit(get_web_image_msg(post.post_view.creator.avatar)); + + self.voting_row + .emit(VotingRowInput::UpdateStats(VotingStats::from_post( + post.post_view.counts.clone(), + post.post_view.my_vote, + ))); self.comments.guard().clear(); std::thread::spawn(move || { - if post.post_view.counts.comments == 0 { return; } + if post.post_view.counts.comments == 0 { + return; + } let comments = api::post::get_comments(post.post_view.post.id); if let Ok(comments) = comments { sender.input(PostInput::DoneFetchComments(comments)); @@ -258,7 +288,9 @@ impl SimpleComponent for PostPage { if link.is_empty() { link = get_web_image_url(post.embed_video_url); } - if link.is_empty() { return; } + if link.is_empty() { + return; + } gtk::show_uri(None::<&relm4::gtk::Window>, &link, 0); } PostInput::OpenCreateCommentDialog => { @@ -273,9 +305,14 @@ impl SimpleComponent for PostPage { std::thread::spawn(move || { let message = match api::comment::create_comment(id, post.body, None) { Ok(comment) => Some(PostInput::CreatedComment(comment.comment_view)), - Err(err) => { println!("{}", err.to_string()); None } + Err(err) => { + println!("{}", err.to_string()); + None + } + }; + if let Some(message) = message { + sender.input(message) }; - if let Some(message) = message { sender.input(message) }; }); } PostInput::DeletePost => { @@ -288,11 +325,17 @@ impl SimpleComponent for PostPage { PostInput::OpenEditPostDialog => { let url = match self.info.post_view.post.url.clone() { Some(url) => url.to_string(), - None => String::from("") + None => String::from(""), }; let data = EditorData { name: self.info.post_view.post.name.clone(), - body: self.info.post_view.post.body.clone().unwrap_or(String::from("")), + body: self + .info + .post_view + .post + .body + .clone() + .unwrap_or(String::from("")), url: reqwest::Url::parse(&url).ok(), id: None, }; @@ -305,9 +348,14 @@ impl SimpleComponent for PostPage { std::thread::spawn(move || { let message = match api::post::edit_post(post.name, post.url, post.body, id) { Ok(post) => Some(PostInput::DoneEditPost(post.post_view)), - Err(err) => { println!("{}", err.to_string()); None } + Err(err) => { + println!("{}", err.to_string()); + None + } + }; + if let Some(message) = message { + sender.input(message) }; - if let Some(message) = message { sender.input(message) }; }); } PostInput::DoneEditPost(post) => { @@ -322,9 +370,14 @@ impl SimpleComponent for PostPage { std::thread::spawn(move || { let message = match api::comment::edit_comment(data.body, data.id.unwrap()) { Ok(comment) => Some(PostInput::UpdateComment(comment.comment_view)), - Err(err) => { println!("{}", err.to_string()); None } + Err(err) => { + println!("{}", err.to_string()); + None + } + }; + if let Some(message) = message { + sender.input(message) }; - if let Some(message) = message { sender.input(message) }; }); } PostInput::UpdateComment(comment) => { diff --git a/src/components/post_row.rs b/src/components/post_row.rs index 3a4a155..d7513f3 100644 --- a/src/components/post_row.rs +++ b/src/components/post_row.rs @@ -1,10 +1,10 @@ +use gtk::prelude::*; use lemmy_api_common::lemmy_db_views::structs::PostView; use relm4::prelude::*; -use gtk::prelude::*; use relm4_components::web_image::WebImage; -use crate::{util::get_web_image_url, api}; use crate::settings; +use crate::{api, util::get_web_image_url}; use super::voting_row::{VotingRowModel, VotingStats}; @@ -13,7 +13,7 @@ pub struct PostRow { post: PostView, author_image: Controller, community_image: Controller, - voting_row: Controller + voting_row: Controller, } #[derive(Debug)] @@ -21,7 +21,7 @@ pub enum PostViewMsg { OpenPost, OpenCommunity, OpenPerson, - DeletePost + DeletePost, } #[relm4::factory(pub)] @@ -123,23 +123,36 @@ impl FactoryComponent for PostRow { } } - fn forward_to_parent(output: Self::Output) -> Option { Some(output) } + fn forward_to_parent(output: Self::Output) -> Option { + Some(output) + } fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> Self { - 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(); + 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 } + Self { + post: value, + author_image, + community_image, + voting_row, + } } fn init_widgets( - &mut self, - _index: &Self::Index, - root: &Self::Root, - _returned_widget: &::ReturnedWidget, - sender: FactorySender, - ) -> Self::Widgets { + &mut self, + _index: &Self::Index, + root: &Self::Root, + _returned_widget: &::ReturnedWidget, + sender: FactorySender, + ) -> Self::Widgets { let author_image = self.author_image.widget(); let community_image = self.community_image.widget(); let voting_row = self.voting_row.widget(); @@ -167,4 +180,4 @@ impl FactoryComponent for PostRow { } } } -} \ No newline at end of file +} diff --git a/src/components/profile_page.rs b/src/components/profile_page.rs index ce817b0..fc35cd6 100644 --- a/src/components/profile_page.rs +++ b/src/components/profile_page.rs @@ -1,6 +1,6 @@ -use lemmy_api_common::person::GetPersonDetailsResponse; -use relm4::{prelude::*, factory::FactoryVecDeque}; use gtk::prelude::*; +use lemmy_api_common::person::GetPersonDetailsResponse; +use relm4::{factory::FactoryVecDeque, prelude::*}; use relm4_components::web_image::WebImage; use crate::util::get_web_image_msg; @@ -11,7 +11,7 @@ use super::post_row::PostRow; pub struct ProfilePage { info: GetPersonDetailsResponse, avatar: Controller, - posts: FactoryVecDeque + posts: FactoryVecDeque, } #[derive(Debug)] @@ -31,7 +31,7 @@ impl SimpleComponent for ProfilePage { set_orientation: gtk::Orientation::Vertical, set_vexpand: false, set_margin_all: 10, - + #[local_ref] avatar -> gtk::Box { set_size_request: (100, 100), @@ -84,7 +84,11 @@ impl SimpleComponent for ProfilePage { ) -> relm4::ComponentParts { let avatar = WebImage::builder().launch("".to_string()).detach(); let posts = FactoryVecDeque::new(gtk::Box::default(), sender.output_sender()); - let model = ProfilePage { info: init, avatar, posts }; + let model = ProfilePage { + info: init, + avatar, + posts, + }; let avatar = model.avatar.widget(); let posts = model.posts.widget(); let widgets = view_output!(); @@ -96,7 +100,8 @@ impl SimpleComponent for ProfilePage { match message { ProfileInput::UpdatePerson(person) => { self.info = person.clone(); - self.avatar.emit(get_web_image_msg(person.person_view.person.avatar)); + self.avatar + .emit(get_web_image_msg(person.person_view.person.avatar)); self.posts.guard().clear(); for post in person.posts { self.posts.guard().push_back(post); diff --git a/src/components/voting_row.rs b/src/components/voting_row.rs index f370ca6..9a2a791 100644 --- a/src/components/voting_row.rs +++ b/src/components/voting_row.rs @@ -1,6 +1,9 @@ -use lemmy_api_common::{lemmy_db_schema::{aggregates::structs::{PostAggregates, CommentAggregates}, newtypes::{PostId, CommentId}}}; -use relm4::{SimpleComponent, ComponentParts, gtk}; use gtk::prelude::*; +use lemmy_api_common::lemmy_db_schema::{ + aggregates::structs::{CommentAggregates, PostAggregates}, + newtypes::{CommentId, PostId}, +}; +use relm4::{gtk, ComponentParts, SimpleComponent}; use crate::{api, settings}; @@ -15,7 +18,7 @@ pub struct VotingStats { #[allow(dead_code)] id: i32, post_id: Option, - comment_id: Option + comment_id: Option, } impl VotingStats { @@ -46,7 +49,7 @@ impl VotingStats { #[derive(Debug)] pub struct VotingRowModel { - stats: VotingStats + stats: VotingStats, } #[derive(Debug)] @@ -56,9 +59,7 @@ pub enum VotingRowInput { } #[derive(Debug)] -pub enum VotingRowOutput { - -} +pub enum VotingRowOutput {} #[relm4::component(pub)] impl SimpleComponent for VotingRowModel { @@ -105,24 +106,52 @@ impl SimpleComponent for VotingRowModel { match message { VotingRowInput::Vote(vote) => { let mut score = self.stats.own_vote.unwrap_or(0) + vote; - if score < -1 || score > 1 { score = 0 }; - if settings::get_current_account().jwt.is_none() { return; } + if score < -1 || score > 1 { + score = 0 + }; + if settings::get_current_account().jwt.is_none() { + return; + } let stats = self.stats.clone(); std::thread::spawn(move || { let info = if stats.post_id.is_some() { - let response = api::post::like_post(PostId { 0: stats.post_id.unwrap() }, score); + let response = api::post::like_post( + PostId { + 0: stats.post_id.unwrap(), + }, + score, + ); match response { - Ok(post) => Some(VotingStats::from_post(post.post_view.counts, post.post_view.my_vote)), - Err(err) => { println!("{}", err.to_string()); None } + Ok(post) => Some(VotingStats::from_post( + post.post_view.counts, + post.post_view.my_vote, + )), + Err(err) => { + println!("{}", err.to_string()); + None + } } } else { - let response = api::comment::like_comment(CommentId { 0: stats.comment_id.unwrap() }, score); + let response = api::comment::like_comment( + CommentId { + 0: stats.comment_id.unwrap(), + }, + score, + ); match response { - Ok(comment) => Some(VotingStats::from_comment(comment.comment_view.counts, comment.comment_view.my_vote)), - Err(err) => { println!("{}", err.to_string()); None } + Ok(comment) => Some(VotingStats::from_comment( + comment.comment_view.counts, + comment.comment_view.my_vote, + )), + Err(err) => { + println!("{}", err.to_string()); + None + } } }; - if let Some(info) = info { sender.input(VotingRowInput::UpdateStats(info)) }; + if let Some(info) = info { + sender.input(VotingRowInput::UpdateStats(info)) + }; }); } VotingRowInput::UpdateStats(stats) => { diff --git a/src/dialogs/editor.rs b/src/dialogs/editor.rs index ba8521e..1bd09a3 100644 --- a/src/dialogs/editor.rs +++ b/src/dialogs/editor.rs @@ -1,5 +1,8 @@ -use relm4::{prelude::*, gtk::{ResponseType, FileFilter}}; use gtk::prelude::*; +use relm4::{ + gtk::{FileFilter, ResponseType}, + prelude::*, +}; use crate::api; @@ -8,7 +11,7 @@ pub struct EditorData { pub name: String, pub body: String, pub url: Option, - pub id: Option + pub id: Option, } pub struct EditorDialog { @@ -20,13 +23,13 @@ pub struct EditorDialog { body_buffer: gtk::TextBuffer, // Optional field to temporarily store the post or comment id id: Option, - window: gtk::Window + window: gtk::Window, } #[derive(Debug, Clone, Copy)] pub enum EditorType { Post, - Comment + Comment, } #[derive(Debug)] @@ -38,13 +41,13 @@ pub enum DialogMsg { Okay, ChooseImage, UploadImage(std::path::PathBuf), - AppendBody(String) + AppendBody(String), } #[derive(Debug)] pub enum EditorOutput { CreateRequest(EditorData, EditorType), - EditRequest(EditorData, EditorType) + EditRequest(EditorData, EditorType), } #[relm4::component(pub)] @@ -76,13 +79,13 @@ impl SimpleComponent for EditorDialog { set_label: "Post content", add_css_class: "font-bold" }, - gtk::Entry { + gtk::Entry { set_placeholder_text: Some("Title"), set_margin_top: 10, set_margin_bottom: 10, set_buffer: &model.name_buffer, }, - gtk::Entry { + gtk::Entry { set_placeholder_text: Some("Url"), set_margin_top: 10, set_margin_bottom: 10, @@ -151,7 +154,16 @@ impl SimpleComponent for EditorDialog { let url_buffer = gtk::EntryBuffer::builder().build(); let body_buffer = gtk::TextBuffer::builder().build(); let window = root.toplevel_window().unwrap(); - let model = EditorDialog { type_: init, visible: false, is_new: true, name_buffer, url_buffer, body_buffer, id: None, window }; + let model = EditorDialog { + type_: init, + visible: false, + is_new: true, + name_buffer, + url_buffer, + body_buffer, + id: None, + window, + }; let widgets = view_output!(); ComponentParts { model, widgets } } @@ -164,17 +176,22 @@ impl SimpleComponent for EditorDialog { self.url_buffer.set_text(""); self.body_buffer.set_text(""); self.visible = false; - }, + } DialogMsg::Okay => { let name = self.name_buffer.text().to_string(); let url = self.url_buffer.text().to_string(); let (start, end) = &self.body_buffer.bounds(); let body = self.body_buffer.text(start, end, true).to_string(); let url = reqwest::Url::parse(&url).ok(); - let post = EditorData { name, body, url, id: self.id }; + let post = EditorData { + name, + body, + url, + id: self.id, + }; let message = match self.is_new { true => EditorOutput::CreateRequest(post, self.type_), - false => EditorOutput::EditRequest(post, self.type_) + false => EditorOutput::EditRequest(post, self.type_), }; let _ = sender.output(message); self.visible = false; @@ -185,22 +202,32 @@ impl SimpleComponent for EditorDialog { } DialogMsg::UpdateData(data) => { self.name_buffer.set_text(data.name); - if let Some(url) = data.url { self.url_buffer.set_text(url.to_string()); } + if let Some(url) = data.url { + self.url_buffer.set_text(url.to_string()); + } self.body_buffer.set_text(&data.body.clone()); } DialogMsg::ChooseImage => { - let buttons = [("_Cancel", ResponseType::Cancel), ("_Okay", ResponseType::Accept)]; - let dialog = gtk::FileChooserDialog::new(Some("Upload image"), None::<>k::ApplicationWindow>, gtk::FileChooserAction::Open, &buttons); + let buttons = [ + ("_Cancel", ResponseType::Cancel), + ("_Okay", ResponseType::Accept), + ]; + let dialog = gtk::FileChooserDialog::new( + Some("Upload image"), + None::<>k::ApplicationWindow>, + gtk::FileChooserAction::Open, + &buttons, + ); dialog.set_transient_for(Some(&self.window)); let image_filter = FileFilter::new(); image_filter.add_pattern("image/*"); dialog.add_filter(&image_filter); - dialog.run_async(move |dialog, result | { + dialog.run_async(move |dialog, result| { match result { ResponseType::Accept => { let path = dialog.file().unwrap().path(); sender.input(DialogMsg::UploadImage(path.unwrap())) - }, + } _ => dialog.hide(), } dialog.destroy(); @@ -217,8 +244,9 @@ impl SimpleComponent for EditorDialog { DialogMsg::AppendBody(new_text) => { let (start, end) = &self.body_buffer.bounds(); let body = self.body_buffer.text(start, end, true).to_string(); - self.body_buffer.set_text(&format!("{}\n{}", body, new_text)); + self.body_buffer + .set_text(&format!("{}\n{}", body, new_text)); } } } -} \ No newline at end of file +} diff --git a/src/dialogs/mod.rs b/src/dialogs/mod.rs index 3822e56..0769981 100644 --- a/src/dialogs/mod.rs +++ b/src/dialogs/mod.rs @@ -1 +1 @@ -pub mod editor; \ No newline at end of file +pub mod editor; diff --git a/src/main.rs b/src/main.rs index 650da4d..5f41cda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,39 @@ -pub mod settings; pub mod api; pub mod components; -pub mod util; +pub mod config; pub mod dialogs; +pub mod settings; +pub mod util; -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}, inbox_page::{InboxPage, InboxInput}}; +use api::{community::default_community, post::default_post, user::default_person}; +use components::{ + community_page::{self, CommunityPage}, + community_row::CommunityRow, + inbox_page::{InboxInput, InboxPage}, + post_page::{self, PostPage}, + post_row::PostRow, + profile_page::{self, ProfilePage}, +}; 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, CommunityId, PersonId}, ListingType}, post::GetPostResponse, community::GetCommunityResponse}; -use relm4::{prelude::*, factory::FactoryVecDeque, set_global_css, actions::{RelmAction, RelmActionGroup}}; +use lemmy_api_common::{ + community::GetCommunityResponse, + lemmy_db_schema::{ + newtypes::{CommunityId, PersonId, PostId}, + ListingType, + }, + lemmy_db_views::structs::PostView, + lemmy_db_views_actor::structs::CommunityView, + person::GetPersonDetailsResponse, + post::GetPostResponse, +}; +use relm4::{ + actions::{RelmAction, RelmActionGroup}, + factory::FactoryVecDeque, + prelude::*, + set_global_css, +}; use settings::get_current_account; -static APP_ID: &str = "com.lemmy-gtk.lemoa"; - #[derive(Debug, Clone, Copy)] enum AppState { Loading, @@ -24,7 +45,7 @@ enum AppState { Post, Login, Message, - Inbox + Inbox, } struct App { @@ -65,7 +86,7 @@ pub enum AppMsg { OpenPost(PostId), DoneFetchPost(GetPostResponse), OpenInbox, - PopBackStack + PopBackStack, } #[relm4::component] @@ -123,7 +144,7 @@ impl SimpleComponent for App { match model.state { AppState::Posts => gtk::ScrolledWindow { set_hexpand: true, - + gtk::Box { set_orientation: gtk::Orientation::Vertical, @@ -229,11 +250,11 @@ impl SimpleComponent for App { gtk::ScrolledWindow { set_vexpand: true, set_hexpand: true, - + gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 10, - + gtk::Box { set_margin_all: 10, @@ -322,22 +343,52 @@ impl SimpleComponent for App { sender: ComponentSender, ) -> ComponentParts { let current_account = settings::get_current_account(); - let state = if current_account.instance_url.is_empty() { AppState::ChooseInstance } else { AppState::Loading }; + let state = if current_account.instance_url.is_empty() { + AppState::ChooseInstance + } else { + AppState::Loading + }; let logged_in = current_account.jwt.is_some(); // initialize all controllers and factories let posts = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender()); let communities = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender()); - 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 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 community_search_buffer = gtk::EntryBuffer::builder().build(); - let model = App { state, back_queue: vec![], logged_in, posts, communities, profile_page, community_page, post_page, inbox_page, message: None, current_communities_type: None, current_posts_type: None, current_communities_page: 1, current_posts_page: 1, community_search_buffer }; + let model = App { + state, + back_queue: vec![], + logged_in, + posts, + communities, + profile_page, + community_page, + post_page, + inbox_page, + message: None, + current_communities_type: None, + current_posts_type: None, + current_communities_page: 1, + current_posts_page: 1, + community_search_buffer, + }; // fetch posts if that's the initial page - if !current_account.instance_url.is_empty() { sender.input(AppMsg::StartFetchPosts(None, true)) }; + if !current_account.instance_url.is_empty() { + sender.input(AppMsg::StartFetchPosts(None, true)) + }; // setup all widgets and different stack pages let posts_box = model.posts.widget(); @@ -346,18 +397,21 @@ impl SimpleComponent for App { 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!(); // create the header bar menu and its actions let instance_sender = sender.clone(); - let instance_action: RelmAction = RelmAction::new_stateless(move |_| { - instance_sender.input(AppMsg::ChooseInstance); - }); + let instance_action: RelmAction = + RelmAction::new_stateless(move |_| { + instance_sender.input(AppMsg::ChooseInstance); + }); let profile_sender = sender.clone(); let profile_action: RelmAction = RelmAction::new_stateless(move |_| { let person = settings::get_current_account(); - if !person.name.is_empty() { profile_sender.input(AppMsg::OpenPerson(PersonId(person.id))); } + if !person.name.is_empty() { + profile_sender.input(AppMsg::OpenPerson(PersonId(person.id))); + } }); let login_sender = sender.clone(); let login_action: RelmAction = RelmAction::new_stateless(move |_| { @@ -380,15 +434,20 @@ impl SimpleComponent for App { fn update(&mut self, msg: Self::Input, sender: ComponentSender) { // save the back queue match msg { - AppMsg::DoneFetchCommunities(_) | AppMsg::DoneFetchCommunity(_) | AppMsg::DoneFetchPerson(_) | AppMsg::DoneFetchPost(_) | AppMsg::DoneFetchPosts(_) | AppMsg::ShowMessage(_) => { - self.back_queue.push(msg.clone()) - } - _ => {} + AppMsg::DoneFetchCommunities(_) + | AppMsg::DoneFetchCommunity(_) + | AppMsg::DoneFetchPerson(_) + | AppMsg::DoneFetchPost(_) + | AppMsg::DoneFetchPosts(_) + | AppMsg::ShowMessage(_) => self.back_queue.push(msg.clone()), + _ => {} } match msg { AppMsg::DoneChoosingInstance(instance_url) => { - if instance_url.trim().is_empty() { return; } + if instance_url.trim().is_empty() { + return; + } let mut current_account = settings::get_current_account(); current_account.instance_url = instance_url; settings::update_current_account(current_account); @@ -400,41 +459,58 @@ impl SimpleComponent for App { } AppMsg::StartFetchPosts(type_, remove_previous) => { self.current_posts_type = type_; - let page = if remove_previous { 1 } else { self.current_posts_page + 1 }; + let page = if remove_previous { + 1 + } else { + self.current_posts_page + 1 + }; self.current_posts_page = page; std::thread::spawn(move || { let message = match api::posts::list_posts(page, None, type_) { Ok(posts) => AppMsg::DoneFetchPosts(posts), - Err(err) => AppMsg::ShowMessage(err.to_string()) + Err(err) => AppMsg::ShowMessage(err.to_string()), }; sender.input(message); }); } AppMsg::DoneFetchPosts(posts) => { self.state = AppState::Posts; - if self.current_posts_page == 1 { self.posts.guard().clear(); } + if self.current_posts_page == 1 { + self.posts.guard().clear(); + } for post in posts { self.posts.guard().push_back(post); } } AppMsg::FetchCommunities(listing_type, remove_previous) => { let query_text = self.community_search_buffer.text().as_str().to_owned(); - let query = if query_text.is_empty() { None } else { Some(query_text) }; + let query = if query_text.is_empty() { + None + } else { + Some(query_text) + }; self.state = AppState::Communities; - let page = if remove_previous { 1 } else { self.current_communities_page + 1 }; + let page = if remove_previous { + 1 + } else { + self.current_communities_page + 1 + }; self.current_communities_page = page; self.current_communities_type = listing_type; std::thread::spawn(move || { - let message = match api::communities::fetch_communities(page, query, listing_type) { - Ok(communities) => AppMsg::DoneFetchCommunities(communities), - Err(err) => AppMsg::ShowMessage(err.to_string()) - }; + let message = + match api::communities::fetch_communities(page, query, listing_type) { + Ok(communities) => AppMsg::DoneFetchCommunities(communities), + Err(err) => AppMsg::ShowMessage(err.to_string()), + }; sender.input(message); }); } AppMsg::DoneFetchCommunities(communities) => { self.state = AppState::Communities; - if self.current_communities_page == 1 { self.communities.guard().clear(); } + if self.current_communities_page == 1 { + self.communities.guard().clear(); + } for community in communities { self.communities.guard().push_back(community); } @@ -444,13 +520,15 @@ impl SimpleComponent for App { std::thread::spawn(move || { let message = match api::user::get_user(person_id, 1) { Ok(person) => AppMsg::DoneFetchPerson(person), - Err(err) => AppMsg::ShowMessage(err.to_string()) + Err(err) => AppMsg::ShowMessage(err.to_string()), }; sender.input(message); }); } AppMsg::DoneFetchPerson(person) => { - self.profile_page.sender().emit(profile_page::ProfileInput::UpdatePerson(person)); + self.profile_page + .sender() + .emit(profile_page::ProfileInput::UpdatePerson(person)); self.state = AppState::Person; } AppMsg::OpenCommunity(community_id) => { @@ -458,13 +536,17 @@ impl SimpleComponent for App { std::thread::spawn(move || { let message = match api::community::get_community(community_id) { Ok(community) => AppMsg::DoneFetchCommunity(community), - Err(err) => AppMsg::ShowMessage(err.to_string()) + Err(err) => AppMsg::ShowMessage(err.to_string()), }; sender.input(message); }); } AppMsg::DoneFetchCommunity(community) => { - self.community_page.sender().emit(community_page::CommunityInput::UpdateCommunity(community.community_view)); + self.community_page + .sender() + .emit(community_page::CommunityInput::UpdateCommunity( + community.community_view, + )); self.state = AppState::Community; } AppMsg::OpenPost(post_id) => { @@ -472,21 +554,29 @@ impl SimpleComponent for App { std::thread::spawn(move || { let message = match api::post::get_post(post_id) { Ok(post) => AppMsg::DoneFetchPost(post), - Err(err) => AppMsg::ShowMessage(err.to_string()) + Err(err) => AppMsg::ShowMessage(err.to_string()), }; sender.input(message); }); } AppMsg::DoneFetchPost(post) => { - self.post_page.sender().emit(post_page::PostInput::UpdatePost(post)); + self.post_page + .sender() + .emit(post_page::PostInput::UpdatePost(post)); self.state = AppState::Post; } AppMsg::ShowLogin => { self.state = AppState::Login; } AppMsg::Login(username, password, totp_token) => { - if get_current_account().instance_url.is_empty() { return; } - let token = if totp_token.is_empty() { None } else { Some(totp_token) }; + if get_current_account().instance_url.is_empty() { + return; + } + let token = if totp_token.is_empty() { + None + } else { + Some(totp_token) + }; self.state = AppState::Loading; std::thread::spawn(move || { let message = match api::auth::login(username, password, token) { @@ -506,7 +596,7 @@ impl SimpleComponent for App { AppMsg::ShowMessage("Wrong credentials!".to_string()) } } - Err(err) => AppMsg::ShowMessage(err.to_string()) + Err(err) => AppMsg::ShowMessage(err.to_string()), }; sender.input(message); }); @@ -531,7 +621,9 @@ impl SimpleComponent for App { } AppMsg::PopBackStack => { let action = self.back_queue.get(self.back_queue.len() - 2); - if let Some(action) = action { sender.input(action.clone()); } + if let Some(action) = action { + sender.input(action.clone()); + } for _ in 0..2 { self.back_queue.remove(self.back_queue.len() - 1); } @@ -547,7 +639,7 @@ relm4::new_stateless_action!(LoginAction, WindowActionGroup, "login"); relm4::new_stateless_action!(LogoutAction, WindowActionGroup, "logout"); fn main() { - let app = RelmApp::new(APP_ID); + let app = RelmApp::new(config::APP_ID); set_global_css(include_str!("style.css")); app.run::(()); } diff --git a/src/settings.rs b/src/settings.rs index 12f023b..de07e17 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,8 +1,8 @@ -use std::{fs::File, path::PathBuf}; +use crate::config::APP_ID; use crate::gtk::glib; use lemmy_api_common::sensitive::Sensitive; use serde::{Deserialize, Serialize}; -use crate::APP_ID; +use std::{fs::File, path::PathBuf}; #[derive(Deserialize, Serialize, Default, Clone)] pub struct Account { @@ -15,7 +15,7 @@ pub struct Account { #[derive(Deserialize, Serialize, Default)] pub struct Preferences { pub accounts: Vec, - pub current_account_index: u32 + pub current_account_index: u32, } pub fn data_path() -> PathBuf { diff --git a/src/util.rs b/src/util.rs index 9802177..c24f653 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,18 +1,22 @@ use lemmy_api_common::lemmy_db_schema::newtypes::DbUrl; use relm4_components::web_image::WebImageMsg; -pub fn get_web_image_msg(url: Option) -> WebImageMsg { +pub fn get_web_image_msg(url: Option) -> WebImageMsg { return if let Some(url) = url { WebImageMsg::LoadImage(url.to_string()) - } else { WebImageMsg::Unload }; + } else { + WebImageMsg::Unload + }; } pub fn get_web_image_url(url: Option) -> String { return if let Some(url) = url { url.to_string() - } else { String::from("") } + } else { + String::from("") + }; } pub fn markdown_to_pango_markup(text: String) -> String { - return html2pango::markup_html(&markdown::to_html(&text)).unwrap_or(text) + return html2pango::markup_html(&markdown::to_html(&text)).unwrap_or(text); }