From a8329360547a039214ddb472365e0f8856728d00 Mon Sep 17 00:00:00 2001 From: yggverse Date: Wed, 26 Mar 2025 21:50:07 +0200 Subject: [PATCH] implement Redirect struct as the redirection wrapper with additional API features --- .../window/tab/item/client/driver/gemini.rs | 5 +- .../tab/item/page/navigation/request/info.rs | 17 ++++-- .../page/navigation/request/info/dialog.rs | 56 ++++++++++++++----- .../page/navigation/request/info/redirect.rs | 26 +++++++++ .../request/info/redirect/method.rs | 25 +++++++++ 5 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 src/app/browser/window/tab/item/page/navigation/request/info/redirect.rs create mode 100644 src/app/browser/window/tab/item/page/navigation/request/info/redirect/method.rs diff --git a/src/app/browser/window/tab/item/client/driver/gemini.rs b/src/app/browser/window/tab/item/client/driver/gemini.rs index e588c978..35f754f2 100644 --- a/src/app/browser/window/tab/item/client/driver/gemini.rs +++ b/src/app/browser/window/tab/item/client/driver/gemini.rs @@ -566,7 +566,10 @@ fn handle( .set_size(None) .commit(); - page.navigation.request.info.replace(i.into_redirect()); + page.navigation.request.info.replace(match redirect { + Redirect::Permanent { .. } => i.into_permanent_redirect(), + Redirect::Temporary { .. } => i.into_temporary_redirect(), + }); } page.item_action.load.activate(Some(&t), false); } diff --git a/src/app/browser/window/tab/item/page/navigation/request/info.rs b/src/app/browser/window/tab/item/page/navigation/request/info.rs index d457501f..b11f657f 100644 --- a/src/app/browser/window/tab/item/page/navigation/request/info.rs +++ b/src/app/browser/window/tab/item/page/navigation/request/info.rs @@ -1,11 +1,13 @@ mod dialog; mod event; +mod redirect; mod socket; use super::Profile; use dialog::Dialog; use event::Event; use gtk::{gio::SocketAddress, prelude::IsA}; +use redirect::Redirect; use socket::Socket; /// Common, shared `Page` information holder @@ -24,7 +26,7 @@ pub struct Info { mime: Option, /// Hold redirections chain with handled details /// * the `referrer` member name is reserved for other protocols - redirect: Option>, + redirect: Option>, /// Key to relate data collected with the specific request request: Option, /// Hold size info @@ -74,13 +76,20 @@ impl Info { /// Take `Self`, convert it into the redirect member, /// then, return new `Self` back - /// * tip: use on driver redirection events - pub fn into_redirect(self) -> Self { + pub fn into_redirect(self, method: redirect::Method) -> Self { let mut this = Self::new(); - this.redirect = Some(Box::new(self)); + this.redirect = Some(Box::new(Redirect { info: self, method })); this } + pub fn into_permanent_redirect(self) -> Self { + self.into_redirect(redirect::Method::Permanent) + } + + pub fn into_temporary_redirect(self) -> Self { + self.into_redirect(redirect::Method::Temporary) + } + pub fn add_event(&mut self, name: String) -> &mut Self { self.event.push(Event::now(name)); self diff --git a/src/app/browser/window/tab/item/page/navigation/request/info/dialog.rs b/src/app/browser/window/tab/item/page/navigation/request/info/dialog.rs index 102586ea..711caf60 100644 --- a/src/app/browser/window/tab/item/page/navigation/request/info/dialog.rs +++ b/src/app/browser/window/tab/item/page/navigation/request/info/dialog.rs @@ -185,36 +185,60 @@ impl Dialog for PreferencesDialog { .icon_name("insert-link-symbolic") .build(); p.add(&{ - // Collect redirections into the buffer, - // to reverse chain before add its members to widget - // * capacity optimized for Gemini protocol (as default) - let mut b = Vec::with_capacity(5); + use gtk::Button; + /// Common suffix widget pattern + fn suffix( + icon_name: impl Into, + tooltip_text: impl Into, + ) -> Button { + Button::builder() + .css_classes(["flat"]) + .icon_name(icon_name) + .tooltip_text(tooltip_text) + .sensitive(false) + .valign(Align::Center) + .halign(Align::Center) + .build() + } /// Recursively collect redirection members into the given vector fn chain<'a>(b: &mut Vec<&'a Info>, i: &'a Info) { b.push(i); if let Some(ref r) = i.redirect { - chain(b, r) + chain(b, &r.info) } } + // Collect redirections into the buffer, + // to reverse chain before add its members to widget + // * capacity optimized for Gemini protocol (as default) + let mut b = Vec::with_capacity(5); chain(&mut b, info); b.reverse(); let l = b.len(); // calculate once let t = b[0].event[0].time(); // first event time to count from for (i, r) in b.iter().enumerate() { g.add(&{ + let is_external = r + .redirect + .as_ref() + .is_some_and(|this| this.is_external(r).is_some_and(|v| v)); let a = ActionRow::builder() .css_classes(["property"]) .subtitle_selectable(true) .title_selectable(true) .title(r.request().unwrap()) .build(); - // show redirections counter a.add_prefix(&{ let c = i + 1; - gtk::Button::builder() + Button::builder() .css_classes([ "circular", - if c == l { "success" } else { "accent" }, + if is_external { + "warning" + } else if c == l { + "success" + } else { + "accent" + }, ]) .label(c.to_string()) .sensitive(false) @@ -222,6 +246,15 @@ impl Dialog for PreferencesDialog { .halign(Align::Center) .build() }); + if let Some(ref redirect) = r.redirect { + a.add_suffix(&suffix( + redirect.method.icon_name(), + redirect.method.to_string(), + )) + } + if is_external { + a.add_suffix(&suffix("application-exit-symbolic", "External")) // @TODO links contain ⇖ text label indication + } // show total redirection time in ms a.set_subtitle(&if i == 0 { t.format_iso8601().unwrap() @@ -237,7 +270,7 @@ impl Dialog for PreferencesDialog { ) }); a - }); + }) } g }); @@ -266,10 +299,7 @@ impl Dialog for PreferencesDialog { ); a.add_suffix( &Label::builder() - .css_classes([ - "flat", - if c == 0 { "success" } else { "warning" }, - ]) + .css_classes([if c == 0 { "success" } else { "warning" }]) .halign(Align::End) .label(if c > 0 { format!("+{c} ms") diff --git a/src/app/browser/window/tab/item/page/navigation/request/info/redirect.rs b/src/app/browser/window/tab/item/page/navigation/request/info/redirect.rs new file mode 100644 index 00000000..b60c1b1c --- /dev/null +++ b/src/app/browser/window/tab/item/page/navigation/request/info/redirect.rs @@ -0,0 +1,26 @@ +pub mod method; +pub use method::Method; + +use super::Info; + +/// Unified redirection info wrapper for the application page +pub struct Redirect { + pub info: Info, + pub method: Method, +} + +impl Redirect { + // Getters + + /// Check redirection has external target + /// * return `None` when at least one request value could not be parsed to + /// the valid [Uri](https://docs.gtk.org/glib/struct.Uri.html) host + pub fn is_external(&self, cmp: &Info) -> Option { + fn parse(info: &Info) -> Option { + gtk::glib::Uri::parse(info.request.as_ref()?, gtk::glib::UriFlags::NONE) + .ok()? + .host() + } + Some(parse(&self.info)? != parse(cmp)?) + } +} diff --git a/src/app/browser/window/tab/item/page/navigation/request/info/redirect/method.rs b/src/app/browser/window/tab/item/page/navigation/request/info/redirect/method.rs new file mode 100644 index 00000000..bcb31475 --- /dev/null +++ b/src/app/browser/window/tab/item/page/navigation/request/info/redirect/method.rs @@ -0,0 +1,25 @@ +/// Common redirection type enumeration for different protocol drivers +pub enum Method { + Permanent, + Temporary, +} + +impl Method { + pub fn icon_name(&self) -> &str { + match self { + Self::Permanent => "network-transmit-symbolic", + Self::Temporary => "network-transmit-receive-symbolic", + } + } +} + +impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Permanent => write!(f, "Permanent"), + Self::Temporary => { + write!(f, "Temporary") + } + } + } +}