use std::sync::Arc; use adw::prelude::*; use relm4::{gtk, prelude::{AsyncComponent, AsyncComponentParts, ComponentController, Controller}, AsyncComponentSender, Component, RelmWidgetExt}; pub mod components { pub(crate) mod smart_summary; pub(crate) use smart_summary::{ SmartSummaryButton, Output as SmartSummaryOutput, Input as SmartSummaryInput }; pub(crate) mod post_editor; pub(crate) use post_editor::{ PostEditor, Input as PostEditorInput }; } use components::post_editor::{Post, Visibility}; pub mod secrets; pub mod micropub; pub mod util; pub const APPLICATION_ID: &str = "xyz.vikanezrimaya.kittybox.Bowl"; pub const VISIBILITY: [&str; 2] = ["public", "private"]; #[derive(Debug)] pub struct App { micropub: Arc, submit_busy_guard: Option, post_editor: Controller> } #[derive(Debug)] #[doc(hidden)] pub enum Input { SubmitButtonPressed, PostEditor(Option) } #[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; /// 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: 480, 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, }, } bar }, model.post_editor.widget(), } } } /// Initialize the UI and model. async fn init( init: Self::Init, window: Self::Root, sender: AsyncComponentSender, ) -> AsyncComponentParts { 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), }; let widgets = view_output!(); #[cfg(debug_assertions)] window.add_css_class("devel"); AsyncComponentParts { model, widgets } } async fn update( &mut self, message: Self::Input, _sender: AsyncComponentSender, _root: &Self::Root ) { match message { Input::SubmitButtonPressed => { self.submit_busy_guard = Some(relm4::main_adw_application().mark_busy()); self.post_editor.sender().send(components::PostEditorInput::Submit).unwrap(); }, Input::PostEditor(None) => { self.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(); } } self.submit_busy_guard = None; }, } } }