summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/post_editor.rs36
-rw-r--r--src/components/preferences.rs61
-rw-r--r--src/lib.rs14
-rw-r--r--src/meson.build1
4 files changed, 90 insertions, 22 deletions
diff --git a/src/components/post_editor.rs b/src/components/post_editor.rs
index 25069be..863e515 100644
--- a/src/components/post_editor.rs
+++ b/src/components/post_editor.rs
@@ -32,40 +32,52 @@ pub struct Post {
     pub visibility: Visibility
 }
 
-impl From<Post> for microformats::types::Item {
-    fn from(post: Post) -> Self {
-        use microformats::types::{Item, Class, KnownClass, PropertyValue};
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct PostConversionSettings {
+    pub send_html_directly: bool,
+}
+
+impl Post {
+    pub fn into_mf2(self, settings: PostConversionSettings) -> microformats::types::Item {
+        use microformats::types::{Item, Class, KnownClass, PropertyValue, Fragment};
         let mut mf2 = Item::new(vec![Class::Known(KnownClass::Entry)]);
 
-        if let Some(name) = post.name {
+        if let Some(name) = self.name {
             mf2.properties.insert(
                 "name".to_owned(), vec![PropertyValue::Plain(name)]
             );
         }
 
-        if let Some(summary) = post.summary {
+        if let Some(summary) = self.summary {
             mf2.properties.insert(
                 "summary".to_owned(),
                 vec![PropertyValue::Plain(summary)]
             );
         }
 
-        if !post.tags.is_empty() {
+        if !self.tags.is_empty() {
             mf2.properties.insert(
                 "category".to_string(),
-                post.tags.into_iter().map(PropertyValue::Plain).collect()
+                self.tags.into_iter().map(PropertyValue::Plain).collect()
             );
         }
 
         mf2.properties.insert(
             "visibility".to_string(),
-            vec![PropertyValue::Plain(post.visibility.to_string())]
+            vec![PropertyValue::Plain(self.visibility.to_string())]
         );
 
-        mf2.properties.insert(
-            "content".to_string(),
-            vec![PropertyValue::Plain(post.content)]
-        );
+        let content = if settings.send_html_directly {
+            PropertyValue::Fragment(Fragment {
+                html: self.content.clone(),
+                value: self.content,
+                lang: None
+            })
+        } else {
+            PropertyValue::Plain(self.content)
+        };
+
+        mf2.properties.insert("content".to_string(), vec![content]);
 
         mf2
     }
diff --git a/src/components/preferences.rs b/src/components/preferences.rs
index f1f37f9..67075a2 100644
--- a/src/components/preferences.rs
+++ b/src/components/preferences.rs
@@ -1,3 +1,5 @@
+use gettextrs::*;
+
 use gio::prelude::*;
 use adw::prelude::*;
 use relm4::prelude::*;
@@ -6,6 +8,52 @@ pub struct Preferences {
     settings: gio::Settings,
 }
 
+#[allow(dead_code)]
+struct ComposerPreferencesWidgets {
+    page: adw::PreferencesPage,
+    general_group: adw::PreferencesGroup,
+    send_html_directly: adw::SwitchRow,
+}
+
+impl ComposerPreferencesWidgets {
+    fn new(settings: &gio::Settings) -> Self {
+        let page = adw::PreferencesPage::builder()
+            .title(gettext("Post composer"))
+            .description(gettext("Settings for composing new posts and editing existing ones."))
+            .icon_name("editor-symbolic")
+            .build();
+        let general_group = adw::PreferencesGroup::builder()
+            .title(gettext("General"))
+            .build();
+        let send_html_directly = adw::SwitchRow::new();
+        general_group.add(&send_html_directly);
+        page.add(&general_group);
+
+        let widgets = Self {
+            page,
+            general_group,
+            send_html_directly
+        };
+
+        let schema = settings.settings_schema().unwrap();
+
+        #[expect(clippy::single_element_loop)]
+        for (row, key, property) in [
+            (widgets.send_html_directly.upcast_ref::<adw::PreferencesRow>(), "send-html-directly", "active"),
+        ] {
+            let key_data = schema.key(key);
+            settings.bind(key, row, property)
+                .get()
+                .set()
+                .build();
+            row.set_title(&gettext(key_data.summary().unwrap()));
+            row.set_tooltip_markup(key_data.description().map(gettext).as_deref());
+        }
+
+        widgets
+    }
+}
+
 #[cfg(feature = "smart-summary")]
 #[allow(dead_code)]
 struct LanguageModelPreferencesWidgets {
@@ -25,10 +73,8 @@ struct LanguageModelPreferencesWidgets {
 #[cfg(feature = "smart-summary")]
 impl LanguageModelPreferencesWidgets {
     fn new(settings: &gio::Settings) -> Self {
-        use gettextrs::*;
-
         let page = adw::PreferencesPage::builder()
-            .title(gettext("Language Models"))
+            .title(gettext("Language models"))
             .description(gettext("Settings for the language model integrations."))
             .icon_name("brain-augemnted") // sic!
             .build();
@@ -79,11 +125,13 @@ impl LanguageModelPreferencesWidgets {
             (widgets.smart_summary_prompt_prefix.upcast_ref::<_>(), "smart-summary-prompt-prefix", "text"),
             (widgets.smart_summary_prompt_suffix.upcast_ref::<_>(), "smart-summary-prompt-suffix", "text"),
         ] {
+            let key_data = schema.key(key);
             settings.bind(key, row, property)
                 .get()
                 .set()
                 .build();
-            row.set_title(&gettext(schema.key(key).summary().unwrap()));
+            row.set_title(&gettext(key_data.summary().unwrap()));
+            row.set_tooltip_markup(key_data.description().map(gettext).as_deref());
         }
 
         widgets
@@ -91,8 +139,9 @@ impl LanguageModelPreferencesWidgets {
 }
 
 pub struct PreferencesWidgets {
+    composer: ComposerPreferencesWidgets,
     #[cfg(feature = "smart-summary")]
-    llm: LanguageModelPreferencesWidgets
+    llm: LanguageModelPreferencesWidgets,
 }
 
 impl Component for Preferences {
@@ -119,9 +168,11 @@ impl Component for Preferences {
         model.settings.delay();
 
         let widgets = PreferencesWidgets {
+            composer: ComposerPreferencesWidgets::new(&model.settings),
             #[cfg(feature = "smart-summary")]
             llm: LanguageModelPreferencesWidgets::new(&model.settings),
         };
+        root.add(&widgets.composer.page);
         #[cfg(feature = "smart-summary")]
         root.add(&widgets.llm.page);
 
diff --git a/src/lib.rs b/src/lib.rs
index bdc3682..fd4b51c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,13 +29,13 @@ pub mod components {
     pub use preferences::Preferences;
 }
 
-use components::{post_editor::Post, PostEditorInput};
+use components::{post_editor::{Post, PostConversionSettings}, PostEditorInput};
 use soup::prelude::SessionExt;
 
 pub mod secrets;
 pub mod micropub;
 pub mod util;
-pub const APPLICATION_ID: &str = "xyz.vikanezrimaya.kittybox.Bowl";
+pub const APPLICATION_ID: &str = env!("APP_ID");
 pub const CLIENT_ID_STR: &str = "https://kittybox.fireburn.ru/bowl/";
 
 #[derive(Debug)]
@@ -43,6 +43,7 @@ pub struct App {
     secret_schema: libsecret::Schema,
     http: soup::Session,
     submit_busy_guard: Option<gtk::gio::ApplicationBusyGuard>,
+    settings: gio::Settings,
 
     // TODO: make this support multiple users
     micropub: Option<micropub::Client>,
@@ -415,10 +416,10 @@ impl AsyncComponent for App {
                         set_popover = &gtk::PopoverMenu::from_model(Some(&main_menu)) {},
                         #[watch]
                         set_visible: model.micropub.is_some(),
-                        set_icon_name: "menu",
+                        set_icon_name: "menu-symbolic",
                     },
                     pack_end = &gtk::Button {
-                        set_icon_name: "document-send-symbolic",
+                        set_icon_name: "paper-plane",
                         set_tooltip: &gettext("Publish"),
                         #[watch]
                         set_visible: model.micropub.is_some(),
@@ -473,6 +474,7 @@ impl AsyncComponent for App {
             http: http.clone(),
             micropub: state,
             secret_schema,
+            settings: gio::Settings::new(crate::APPLICATION_ID),
 
             post_editor: {
                 #[cfg(feature = "smart-summary")]
@@ -561,7 +563,9 @@ impl AsyncComponent for App {
             }
             Input::PostEditor(Some(post)) => {
                 if let Some(micropub) = self.micropub.as_ref() {
-                    let mf2 = post.into();
+                    let mf2 = post.into_mf2(PostConversionSettings {
+                        send_html_directly: self.settings.get("send-html-directly")
+                    });
                     log::debug!("Submitting post: {:#}", serde_json::to_string(&mf2).unwrap());
                     match micropub.send_post(mf2).await {
                         Ok(uri) => {
diff --git a/src/meson.build b/src/meson.build
index 3bc0c3f..d9adfb7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,6 +20,7 @@ cargo_env = [
     'CARGO_HOME=' + meson.project_build_root() / 'cargo-home',
     'PKGDATADIR=' + pkgdatadir,
     'LOCALEDIR=' + localedir,
+    'APP_ID=' + application_id,
 ]
 
 cargo_build = custom_target(