summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs208
-rw-r--r--src/main.rs17
2 files changed, 149 insertions, 76 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 302c63a..f2436f8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,4 @@
-use std::sync::Arc;
+use std::{borrow::Borrow, sync::Arc};
 
 use adw::prelude::*;
 use relm4::{gtk, prelude::{AsyncComponent, AsyncComponentParts, ComponentController, Controller}, AsyncComponentSender, Component, RelmWidgetExt};
@@ -29,84 +29,134 @@ pub const VISIBILITY: [&str; 2] = ["public", "private"];
 
 #[derive(Debug)]
 pub struct App {
-    micropub: Arc<micropub::Client>,
-    submit_busy_guard: Option<gtk::gio::ApplicationBusyGuard>,
-
-    post_editor: Controller<components::PostEditor<micropub::Error>>
+    state: AuthState
+}
+#[derive(Debug)]
+enum AuthState {
+    LoggedOut(gtk::EntryBuffer),
+    LoggedIn {
+        submit_busy_guard: Option<gtk::gio::ApplicationBusyGuard>,
+        post_editor: Controller<components::PostEditor<micropub::Error>>,
+        micropub: micropub::Client
+    }
 }
 
 #[derive(Debug)]
 #[doc(hidden)]
 pub enum Input {
     SubmitButtonPressed,
+    Authorize,
     PostEditor(Option<Post>)
 }
 
-#[relm4::component(pub async)]
+#[derive(Default, Debug)]
+pub struct AppRootWidgets {
+    root: adw::ApplicationWindow,
+    toolbar_view: adw::ToolbarView,
+    top_bar: adw::HeaderBar,
+    top_bar_btn: gtk::Button,
+
+    login_box: gtk::Box,
+    login_label: gtk::Label,
+    login_button: gtk::Button,
+}
+
+//#[relm4::component(pub async)]
 impl AsyncComponent for App {
     /// The type of the messages that this component can receive.
     type Input = Input;
     /// The type of the messages that this component can send.
     type Output = ();
     /// The type of data with which this component will be initialized.
-    type Init = micropub::Client;
+    type Init = ();
     /// The type of the command outputs that this component can receive.
     type CommandOutput = ();
- 
-    view! {
-        #[root]
-        adw::ApplicationWindow {
-            set_title: Some("Create post"),
-            set_width_request: 360,
-            set_height_request: 294,
-
-            adw::ToolbarView {
-                add_top_bar: &{
-                    relm4::view! {
-                        send_button = gtk::Button {
-                            set_icon_name: "document-send-symbolic",
-                            set_tooltip: "Send post",
-
-                            connect_clicked => Self::Input::SubmitButtonPressed,
-                            #[watch]
-                            set_sensitive: model.submit_busy_guard.is_none(),
-                        },
 
-                        bar = adw::HeaderBar::new() {
-                            pack_end: &send_button,
-                        },
-                    }
+    type Widgets = AppRootWidgets;
 
-                    bar
-                },
+    type Root = adw::ApplicationWindow;
 
-                model.post_editor.widget(),
-            }
-        }
-                
+    fn init_root() -> Self::Root {
+        let window = Self::Root::default();
+        window.set_size_request(360, 294);
+        #[cfg(debug_assertions)]
+        window.add_css_class("devel");
+
+        window
     }
+
     /// Initialize the UI and model.
     async fn init(
-        init: Self::Init,
+        _init: Self::Init,
         window: Self::Root,
         sender: AsyncComponentSender<Self>,
     ) -> AsyncComponentParts<Self> {
         let model = App {
-            submit_busy_guard: None,
-            micropub: Arc::new(init),
-            post_editor: components::PostEditor::builder()
-                .launch(None)
-                .forward(sender.input_sender(), Self::Input::PostEditor),
+            state: AuthState::LoggedOut(Default::default())
         };
 
-        let widgets = view_output!();
+        let mut widgets = Self::Widgets {
+            root: window,
+            ..Self::Widgets::default()
+        };
 
-        #[cfg(debug_assertions)]
-        window.add_css_class("devel");
+        widgets.toolbar_view.add_top_bar(&widgets.top_bar);
+
+        widgets.top_bar.pack_end(&widgets.top_bar_btn);
+
+        widgets.top_bar_btn.set_icon_name("document-send-symbolic");
+        widgets.top_bar_btn.set_tooltip("Send post");
+        widgets.top_bar_btn.connect_clicked(glib::clone!(
+            #[strong] sender,
+            move |_button| sender.input(Self::Input::SubmitButtonPressed)
+        ));
+
+        widgets.login_box.set_orientation(gtk::Orientation::Vertical);
+        widgets.login_box.append(&widgets.login_label);
+        widgets.login_box.append(&widgets.login_button);
+
+        widgets.login_label.set_text("You need to authorize first.");
+
+        widgets.login_button.set_label("Pretend to authorize");
+        widgets.login_button.set_tooltip("(check MICROPUB_URI and MICROPUB_TOKEN environment variables)");
+        widgets.login_button.connect_clicked(glib::clone!(
+            #[strong] sender,
+            move |_button| sender.input(Self::Input::Authorize)
+        ));
+
+        widgets.root.set_content(Some(&widgets.toolbar_view));
+
+        // Separate component choosing logic from initialization. We
+        // already have all the parts here, might as well use them.
+        model.update_view(&mut widgets, sender);
 
         AsyncComponentParts { model, widgets }
     }
 
+
+    fn update_view(&self, widgets: &mut Self::Widgets, _sender: AsyncComponentSender<Self>) {
+        // Bind the child component, if any, here.
+        match &self.state {
+            AuthState::LoggedOut(_entry_buffer) => {
+                widgets.root.set_title(Some("Sign in with your website"));
+                widgets.toolbar_view.set_content(None::<&gtk::Box>);
+                widgets.top_bar_btn.set_visible(false);
+                widgets.toolbar_view.set_content(Some(&widgets.login_box));
+            },
+            AuthState::LoggedIn {
+                post_editor,
+                submit_busy_guard,
+                ..
+            } => {
+                widgets.root.set_title(Some("Create post"));
+                widgets.toolbar_view.set_content(Some(post_editor.widget()));
+                widgets.top_bar_btn.set_sensitive(submit_busy_guard.is_none());
+                widgets.top_bar_btn.set_visible(true);
+            }
+        }        
+    }
+
+
     async fn update(
         &mut self,
         message: Self::Input,
@@ -114,30 +164,64 @@ impl AsyncComponent for App {
         _root: &Self::Root
     ) {
         match message {
+            Input::Authorize => {
+                self.state = AuthState::LoggedIn {
+                    post_editor: components::PostEditor::builder()
+                        .launch(None)
+                        .forward(_sender.clone().input_sender(), Self::Input::PostEditor),
+                    micropub: std::env::var("MICROPUB_TOKEN").ok()
+                        .and_then(|token| {
+                            Some((token, glib::Uri::parse(
+                                &std::env::var("MICROPUB_URI").ok()?,
+                                glib::UriFlags::NONE
+                            ).ok()?))
+                        })
+                        .map(|(token, uri)| crate::micropub::Client::new(
+                            uri, token
+                        ))
+                        .unwrap(),
+                    submit_busy_guard: None
+                };
+            },
             Input::SubmitButtonPressed => {
-                self.submit_busy_guard = Some(relm4::main_adw_application().mark_busy());
-                self.post_editor.sender().send(components::PostEditorInput::Submit).unwrap();
+                if let AuthState::LoggedIn {
+                    ref mut submit_busy_guard,
+                    ref post_editor,
+                    ..
+                } = &mut self.state {
+                    *submit_busy_guard = Some(relm4::main_adw_application().mark_busy());
+                    post_editor.sender().emit(components::PostEditorInput::Submit);
+                };
             },
             Input::PostEditor(None) => {
-                self.submit_busy_guard = None;
+                if let AuthState::LoggedIn {
+                    ref mut submit_busy_guard,
+                    ..
+                } = &mut self.state {
+                    *submit_busy_guard = None;
+                }
             }
             Input::PostEditor(Some(post)) => {
-                let mf2 = post.into();
-                log::debug!("Submitting post: {:#}", serde_json::to_string(&mf2).unwrap());
-                match self.micropub.send_post(mf2).await {
-                    Ok(location) => {
-                        self.post_editor.sender()
-                            .send(components::PostEditorInput::SubmitDone(location))
-                            .unwrap();
-                    },
-                    Err(err) => {
-                        log::warn!("Error sending post: {}", err);
-                        self.post_editor.sender()
-                            .send(components::PostEditorInput::SubmitError(err))
-                            .unwrap();
+                if let AuthState::LoggedIn {
+                    ref mut submit_busy_guard,
+                    ref post_editor,
+                    ref micropub,
+                } = &mut self.state {
+                    let mf2 = post.into();
+                    log::debug!("Submitting post: {:#}", serde_json::to_string(&mf2).unwrap());
+                    match micropub.send_post(mf2).await {
+                        Ok(uri) => {
+                            post_editor.sender()
+                                .emit(components::PostEditorInput::SubmitDone(uri));
+                        },
+                        Err(err) => {
+                            log::warn!("Error sending post: {}", err);
+                            post_editor.sender()
+                                .emit(components::PostEditorInput::SubmitError(err));
+                        }
                     }
+                    *submit_busy_guard = None;
                 }
-                self.submit_busy_guard = None;
             },
         }
     }
diff --git a/src/main.rs b/src/main.rs
index 31d7d24..a12fe60 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,3 @@
-use adw::prelude::GtkWindowExt;
-use relm4::{ComponentParts, ComponentSender, RelmApp, Component, ComponentController};
-
-use bowl::App;
-
-use bowl::APPLICATION_ID;
-
 static GLIB_LOGGER: glib::GlibLogger = glib::GlibLogger::new(
     glib::GlibLoggerFormat::Plain,
     glib::GlibLoggerDomain::CrateTarget,
@@ -14,11 +7,7 @@ fn main() {
     log::set_logger(&GLIB_LOGGER).unwrap();
     log::set_max_level(log::LevelFilter::Debug);
 
-    let app = RelmApp::new(APPLICATION_ID);
-    app.run_async::<App>(
-        bowl::micropub::Client::new(
-            glib::Uri::parse(&std::env::var("MICROPUB_URI").unwrap(), glib::UriFlags::NONE).unwrap(),
-            std::env::var("MICROPUB_TOKEN").unwrap(),
-        )
-    );
+    let app = relm4::RelmApp::new(bowl::APPLICATION_ID);
+
+    app.run_async::<bowl::App>(());
 }