Reformat project and change package id
This commit is contained in:
parent
d71bbcd3f3
commit
fbf4b8dfa5
|
@ -1,3 +1,7 @@
|
|||
/target
|
||||
/.cargo
|
||||
/_build
|
||||
/_build
|
||||
/builddir
|
||||
/.flatpak
|
||||
/.flatpak-builder
|
||||
/.vscode
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"id": "com.lemmy-gtk.lemoa",
|
||||
"id": "com.lemmygtk.lemoa",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "44",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use lemmy_api_common::{person::Login, sensitive::Sensitive};
|
||||
|
||||
pub fn login(username_or_email: String, password: String, totp_token: Option<String>) -> std::result::Result<lemmy_api_common::person::LoginResponse, reqwest::Error> {
|
||||
pub fn login(
|
||||
username_or_email: String,
|
||||
password: String,
|
||||
totp_token: Option<String>,
|
||||
) -> std::result::Result<lemmy_api_common::person::LoginResponse, reqwest::Error> {
|
||||
let params = Login {
|
||||
username_or_email: Sensitive::new(username_or_email),
|
||||
password: Sensitive::new(password),
|
||||
|
|
|
@ -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<CommentResponse
|
|||
super::post("/comment/like", ¶ms)
|
||||
}
|
||||
|
||||
pub fn edit_comment(
|
||||
body: String,
|
||||
comment_id: i32
|
||||
) -> Result<CommentResponse, reqwest::Error> {
|
||||
pub fn edit_comment(body: String, comment_id: i32) -> Result<CommentResponse, reqwest::Error> {
|
||||
let params = EditComment {
|
||||
content: Some(body),
|
||||
comment_id: CommentId(comment_id),
|
||||
|
|
|
@ -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<String>,listing_type: Option<ListingType>) -> std::result::Result<Vec<CommunityView>, reqwest::Error> {
|
||||
pub fn fetch_communities(
|
||||
page: i64,
|
||||
query: Option<String>,
|
||||
listing_type: Option<ListingType>,
|
||||
) -> std::result::Result<Vec<CommunityView>, reqwest::Error> {
|
||||
if query.is_none() || query.clone().unwrap().trim().is_empty() {
|
||||
let params = ListCommunities {
|
||||
type_: listing_type,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<String, reqwest::Error> {
|
||||
pub fn upload_image(image: std::path::PathBuf) -> Result<String, reqwest::Error> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Client> = Lazy::new(|| {
|
||||
Client::new()
|
||||
});
|
||||
pub static CLIENT: Lazy<Client> = 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<T, Params>(path: &str, params: &Params) -> Result<T, reqwest::Error>
|
||||
|
@ -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<T, Params>(path: &str, params: &Params) -> Result<T, reqwest::Error>
|
||||
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<GetPostResponse, reqwest::Error> {
|
|||
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<reqwest::Url>,
|
||||
body: String,
|
||||
post_id: i32
|
||||
post_id: i32,
|
||||
) -> Result<PostResponse, reqwest::Error> {
|
||||
let params = EditPost {
|
||||
name: Some(name),
|
||||
|
|
|
@ -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<String>, listing_type: Option<ListingType>) -> std::result::Result<Vec<PostView>, reqwest::Error> {
|
||||
pub fn list_posts(
|
||||
page: i64,
|
||||
community_name: Option<String>,
|
||||
listing_type: Option<ListingType>,
|
||||
) -> std::result::Result<Vec<PostView>, reqwest::Error> {
|
||||
let params = GetPosts {
|
||||
page: Some(page),
|
||||
type_: listing_type,
|
||||
|
|
|
@ -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<SearchType>) -> std::result::Result<SearchResponse, reqwest::Error> {
|
||||
pub fn fetch_search(
|
||||
page: i64,
|
||||
query: String,
|
||||
search_type: Option<SearchType>,
|
||||
) -> std::result::Result<SearchResponse, reqwest::Error> {
|
||||
let params = Search {
|
||||
q: query,
|
||||
sort: Some(SortType::TopMonth),
|
||||
|
@ -13,4 +20,4 @@ pub fn fetch_search(page: i64, query: String, search_type: Option<SearchType>) -
|
|||
};
|
||||
|
||||
super::get("/search", ¶ms)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use lemmy_api_common::site::{GetSiteResponse, GetSite};
|
||||
use lemmy_api_common::site::{GetSite, GetSiteResponse};
|
||||
|
||||
use crate::settings;
|
||||
|
||||
|
|
|
@ -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<GetPersonDetailsResponse, reqwest::Error> {
|
||||
pub fn get_user(
|
||||
id: PersonId,
|
||||
page: i64,
|
||||
) -> std::result::Result<GetPersonDetailsResponse, reqwest::Error> {
|
||||
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<GetPersonMentionsResponse, reqwest::Error> {
|
||||
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),
|
||||
|
@ -28,7 +40,10 @@ pub fn get_mentions(page: i64, unread_only: bool) -> std::result::Result<GetPers
|
|||
super::get("/user/mention", ¶ms)
|
||||
}
|
||||
|
||||
pub fn get_replies(page: i64, unread_only: bool) -> std::result::Result<GetRepliesResponse, reqwest::Error> {
|
||||
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),
|
||||
|
|
|
@ -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<WebImage>,
|
||||
voting_row: Controller<VotingRowModel>
|
||||
voting_row: Controller<VotingRowModel>,
|
||||
}
|
||||
|
||||
#[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>) -> 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: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> Self::Widgets {
|
||||
&mut self,
|
||||
_index: &Self::Index,
|
||||
root: &Self::Root,
|
||||
_returned_widget: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> 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<Self>) {
|
||||
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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PostRow>,
|
||||
#[allow(dead_code)]
|
||||
create_post_dialog: Controller<EditorDialog>,
|
||||
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) => {
|
||||
|
|
|
@ -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>) -> 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: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> Self::Widgets {
|
||||
&mut self,
|
||||
_index: &Self::Index,
|
||||
root: &Self::Root,
|
||||
_returned_widget: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> 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<Self>) {
|
||||
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(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MentionRow>,
|
||||
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<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
_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 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 {
|
||||
|
|
|
@ -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<WebImage>,
|
||||
community_image: Controller<WebImage>,
|
||||
voting_row: Controller<VotingRowModel>
|
||||
voting_row: Controller<VotingRowModel>,
|
||||
}
|
||||
|
||||
#[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>) -> 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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
pub mod mention_row;
|
||||
pub mod post_page;
|
||||
pub mod post_row;
|
||||
pub mod profile_page;
|
||||
pub mod voting_row;
|
||||
|
|
|
@ -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<DialogMsg> = MessageBroker::new();
|
||||
|
||||
|
@ -17,7 +28,7 @@ pub struct PostPage {
|
|||
comments: FactoryVecDeque<CommentRow>,
|
||||
#[allow(dead_code)]
|
||||
create_comment_dialog: Controller<EditorDialog>,
|
||||
voting_row: Controller<VotingRowModel>
|
||||
voting_row: Controller<VotingRowModel>,
|
||||
}
|
||||
|
||||
#[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) => {
|
||||
|
|
|
@ -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<WebImage>,
|
||||
community_image: Controller<WebImage>,
|
||||
voting_row: Controller<VotingRowModel>
|
||||
voting_row: Controller<VotingRowModel>,
|
||||
}
|
||||
|
||||
#[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<Self::ParentInput> { Some(output) }
|
||||
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 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: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> Self::Widgets {
|
||||
&mut self,
|
||||
_index: &Self::Index,
|
||||
root: &Self::Root,
|
||||
_returned_widget: &<Self::ParentWidget as relm4::factory::FactoryView>::ReturnedWidget,
|
||||
sender: FactorySender<Self>,
|
||||
) -> 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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<WebImage>,
|
||||
posts: FactoryVecDeque<PostRow>
|
||||
posts: FactoryVecDeque<PostRow>,
|
||||
}
|
||||
|
||||
#[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<Self> {
|
||||
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);
|
||||
|
|
|
@ -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<i32>,
|
||||
comment_id: Option<i32>
|
||||
comment_id: Option<i32>,
|
||||
}
|
||||
|
||||
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) => {
|
||||
|
|
|
@ -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<reqwest::Url>,
|
||||
pub id: Option<i32>
|
||||
pub id: Option<i32>,
|
||||
}
|
||||
|
||||
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<i32>,
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub mod editor;
|
||||
pub mod editor;
|
||||
|
|
194
src/main.rs
194
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<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
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<ChangeInstanceAction> = RelmAction::new_stateless(move |_| {
|
||||
instance_sender.input(AppMsg::ChooseInstance);
|
||||
});
|
||||
let instance_action: RelmAction<ChangeInstanceAction> =
|
||||
RelmAction::new_stateless(move |_| {
|
||||
instance_sender.input(AppMsg::ChooseInstance);
|
||||
});
|
||||
let profile_sender = sender.clone();
|
||||
let profile_action: RelmAction<ProfileAction> = 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<LoginAction> = RelmAction::new_stateless(move |_| {
|
||||
|
@ -380,15 +434,20 @@ impl SimpleComponent for App {
|
|||
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
|
||||
// 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::<App>(());
|
||||
}
|
||||
|
|
|
@ -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<Account>,
|
||||
pub current_account_index: u32
|
||||
pub current_account_index: u32,
|
||||
}
|
||||
|
||||
pub fn data_path() -> PathBuf {
|
||||
|
|
12
src/util.rs
12
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<DbUrl>) -> WebImageMsg {
|
||||
pub fn get_web_image_msg(url: Option<DbUrl>) -> 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<DbUrl>) -> 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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue