From daa539031f74f38629a150bb67a275291f35acd3 Mon Sep 17 00:00:00 2001 From: Lorenzo B Gomez Date: Fri, 30 Jun 2023 09:57:18 -0500 Subject: [PATCH] List federated instances in instance selector (#8) Co-authored-by: Bnyro --- src/api/instances.rs | 29 ++++++++++++ src/api/mod.rs | 3 +- src/components/instance_row.rs | 87 ++++++++++++++++++++++++++++++++++ src/components/mod.rs | 2 + src/main.rs | 35 ++++++-------- 5 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 src/api/instances.rs create mode 100644 src/components/instance_row.rs diff --git a/src/api/instances.rs b/src/api/instances.rs new file mode 100644 index 0000000..5718d81 --- /dev/null +++ b/src/api/instances.rs @@ -0,0 +1,29 @@ +use crate::settings; +use lemmy_api_common::{ + lemmy_db_schema::source::instance::Instance, + site::{GetFederatedInstances, GetFederatedInstancesResponse}, +}; + +pub fn fetch_instances() -> std::result::Result, reqwest::Error> { + // TODO: Update code to use the Instance views from lemmy 0.18.0 + let params = GetFederatedInstances { + auth: settings::get_current_account().jwt, + }; + + // we fetch the instances from the official instance because the instance is likely unset on first startup + let instances = super::CLIENT + .get("https://lemmy.ml/api/v3/federated_instances".to_owned()) + .query(¶ms) + .send()? + .json::()?; + + match instances.federated_instances { + Some(instances) => Ok(instances + .linked + .iter() + .filter(|instance| instance.software == Some("lemmy".to_owned())) + .map(|instance| instance.clone()) + .collect::>()), + None => Ok(vec![]), + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 098194d..2afeb98 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -6,13 +6,14 @@ 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 private_message; pub mod search; pub mod site; +pub mod image; +pub mod instances; pub mod user; static API_VERSION: &str = "v3"; diff --git a/src/components/instance_row.rs b/src/components/instance_row.rs new file mode 100644 index 0000000..b4418ff --- /dev/null +++ b/src/components/instance_row.rs @@ -0,0 +1,87 @@ +use gtk::prelude::*; +use lemmy_api_common::lemmy_db_schema::source::instance::Instance; +use relm4::prelude::*; + +#[derive(Debug)] +pub struct InstanceRow { + instance: Instance, +} + +#[derive(Debug)] +pub enum InstanceRowMsg { + OpenInstance, +} + +#[relm4::factory(pub)] +impl FactoryComponent for InstanceRow { + type Init = Instance; + type Input = InstanceRowMsg; + type Output = crate::AppMsg; + type CommandOutput = (); + type Widgets = PostViewWidgets; + type ParentInput = crate::AppMsg; + type ParentWidget = gtk::Box; + + view! { + root = gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_spacing: 10, + set_margin_end: 10, + set_margin_start: 10, + set_vexpand: false, + + add_controller = gtk::GestureClick { + connect_pressed[sender] => move |_, _, _, _| { + sender.input(InstanceRowMsg::OpenInstance); + } + + }, + + gtk::Box { + set_orientation: gtk::Orientation::Horizontal, + set_spacing: 10, + + gtk::Label { + set_label: &self.instance.domain + }, + + gtk::Box { + set_hexpand: true, + }, + + }, + + gtk::Separator {} + } + } + + fn forward_to_parent(output: Self::Output) -> Option { + Some(output) + } + + fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> Self { + Self { instance: value } + } + + fn init_widgets( + &mut self, + _index: &Self::Index, + root: &Self::Root, + _returned_widget: &::ReturnedWidget, + sender: FactorySender, + ) -> Self::Widgets { + let widgets = view_output!(); + widgets + } + + fn update(&mut self, message: Self::Input, sender: FactorySender) { + match message { + InstanceRowMsg::OpenInstance => { + let instance_address = format!("https://{}", self.instance.domain); + sender.output(crate::AppMsg::DoneChoosingInstance( + instance_address.clone(), + )) + } + } + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 3db4823..4c624c5 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,6 +2,8 @@ pub mod comment_row; pub mod community_page; pub mod community_row; pub mod inbox_page; +pub mod instance_row; +pub mod instances_page; pub mod mention_row; pub mod post_page; pub mod post_row; diff --git a/src/main.rs b/src/main.rs index 73edaa6..fcdbe67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use components::{ community_page::{self, CommunityPage}, community_row::CommunityRow, inbox_page::{InboxInput, InboxPage}, + instances_page::{InstancePageInput, InstancesPage}, post_page::{self, PostPage}, post_row::PostRow, profile_page::{self, ProfilePage}, @@ -55,6 +56,7 @@ struct App { back_queue: Vec, posts: FactoryVecDeque, communities: FactoryVecDeque, + instances_page: Controller, profile_page: Controller, community_page: Controller, post_page: Controller, @@ -179,27 +181,8 @@ impl SimpleComponent for App { }, }, AppState::ChooseInstance => gtk::Box { - set_hexpand: true, - set_orientation: gtk::Orientation::Vertical, - set_spacing: 12, - set_margin_all: 20, - set_valign: gtk::Align::Center, - set_halign: gtk::Align::Center, - gtk::Label { - set_text: "Please enter the URL of a valid lemmy instance", - }, - #[name(instance_url)] - gtk::Entry { - set_tooltip_text: Some("Instance"), - }, - gtk::Button { - set_label: "Done", - connect_clicked[sender, instance_url] => move |_| { - let text = instance_url.text().as_str().to_string(); - instance_url.set_text(""); - sender.input(AppMsg::DoneChoosingInstance(text)); - }, - } + #[local_ref] + instances_page -> gtk::Box {} }, AppState::Login => gtk::Box { set_hexpand: true, @@ -287,6 +270,7 @@ impl SimpleComponent for App { } } } + AppState::Person => { gtk::Box { #[local_ref] @@ -357,6 +341,9 @@ impl SimpleComponent for App { // 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 instances_page = InstancesPage::builder() + .launch(()) + .forward(sender.input_sender(), |msg| msg); let profile_page = ProfilePage::builder() .launch(default_person()) .forward(sender.input_sender(), |msg| msg); @@ -380,6 +367,7 @@ impl SimpleComponent for App { logged_in, posts, communities, + instances_page, profile_page, community_page, post_page, @@ -401,6 +389,7 @@ impl SimpleComponent for App { // setup all widgets and different stack pages let posts_box = model.posts.widget(); let communities_box = model.communities.widget(); + let instances_page = model.instances_page.widget(); let profile_page = model.profile_page.widget(); let community_page = model.community_page.widget(); let post_page = model.post_page.widget(); @@ -486,6 +475,9 @@ impl SimpleComponent for App { } AppMsg::ChooseInstance => { self.state = AppState::ChooseInstance; + self.instances_page + .sender() + .emit(InstancePageInput::FetchInstances); } AppMsg::StartFetchPosts(type_, remove_previous) => { self.current_posts_type = type_; @@ -536,6 +528,7 @@ impl SimpleComponent for App { sender.input(message); }); } + AppMsg::DoneFetchCommunities(communities) => { self.state = AppState::Communities; if self.current_communities_page == 1 {