page controller contains lot of code, begin components reorganization before implement new features

This commit is contained in:
yggverse 2025-01-15 03:47:21 +02:00
parent 2cab9e4287
commit a35d86a630
6 changed files with 153 additions and 80 deletions

View File

@ -5,7 +5,6 @@ mod error;
mod input;
mod mode;
mod navigation;
mod redirect;
mod search;
mod status;
mod widget;
@ -16,7 +15,6 @@ use error::Error;
use input::Input;
use mode::Mode;
use navigation::Navigation;
use redirect::Redirect;
use search::Search;
use status::Status;
use widget::Widget;
@ -38,7 +36,6 @@ pub struct Page {
profile: Rc<Profile>,
status: Rc<RefCell<Status>>,
title: Rc<RefCell<GString>>,
redirect: Rc<Redirect>,
// Actions
browser_action: Rc<BrowserAction>,
tab_action: Rc<TabAction>,
@ -90,7 +87,6 @@ impl Page {
Self {
id: id.clone(),
profile: profile.clone(),
redirect: Rc::new(Redirect::new()),
status: Rc::new(RefCell::new(Status::New)),
title: Rc::new(RefCell::new(gformat!("New page"))),
// Actions
@ -170,10 +166,6 @@ impl Page {
/// Main loader for different protocols, that routed by scheme
/// * every protocol has it own (children) method implementation
pub fn load(&self, is_history: bool) {
/// Global limit to prevent infinitive redirects (ALL protocols)
/// * every protocol implementation has own value checker, according to specification
const DEFAULT_MAX_REDIRECT_COUNT: usize = 10;
// Move focus out from navigation entry
self.browser_action
.escape
@ -186,27 +178,22 @@ impl Page {
self.search.unset();
self.input.unset();
// Prevent infinitive redirection
if self.redirect.count() > DEFAULT_MAX_REDIRECT_COUNT {
todo!()
}
// Try redirect request
let request = if let Some(redirect) = self.redirect.last() {
let request = if let Some(redirect) = self.client.redirect.last() {
// Gemini protocol may provide background (temporarily) redirects
if redirect.is_foreground {
self.navigation
.request
.widget
.entry
.set_text(&redirect.request);
.set_text(&redirect.request.to_string());
}
// Return value from redirection holder
Mode::from(&redirect.request, redirect.referrer.as_ref())
Mode::from(&redirect.request.to_str(), None /*redirect.referrer*/) // @TODO
} else {
// Reset redirect counter as request value taken from user input
self.redirect.clear();
self.client.redirect.clear();
// Return value from navigation entry
Mode::from(&self.navigation.request.widget.entry.text(), None)
@ -424,7 +411,7 @@ impl Page {
let search = self.search.clone();
let tab_action = self.tab_action.clone();
let window_action = self.window_action.clone();
let redirect = self.redirect.clone();
let redirect = self.client.redirect.clone();
// Listen for connection status updates
self.client.gemini.socket.connect_event({
@ -775,11 +762,11 @@ impl Page {
redirect.add(
// skip query and fragment by protocol requirements
// @TODO review fragment specification
resolved_uri.to_string_partial(
Uri::parse(&resolved_uri.to_string_partial(
UriHideFlags::FRAGMENT | UriHideFlags::QUERY
),
), UriFlags::NONE).unwrap(), // @TODO handle
// referrer
Some(navigation.request.widget.entry.text()),
Some(uri.clone()),
// set follow policy based on status code
matches!(response.meta.status, response::meta::Status::PermanentRedirect),
);

View File

@ -1,5 +1,14 @@
mod redirect;
mod status;
use redirect::Redirect;
use status::Status;
use gtk::{gio::Cancellable, prelude::CancellableExt};
use std::cell::Cell;
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
/// Multi-client holder for single `Page` object
///
@ -10,7 +19,11 @@ use std::cell::Cell;
pub struct Client {
// Shared reference to cancel async operations
cancellable: Cell<Cancellable>,
// Clients
// Redirects resolver for different protocols
pub redirect: Rc<Redirect>,
// Track update status
status: Rc<RefCell<Status>>,
// Drivers
pub gemini: gemini::Client,
// other clients..
}
@ -28,6 +41,8 @@ impl Client {
pub fn new() -> Self {
Self {
cancellable: Cell::new(Cancellable::new()),
redirect: Rc::new(Redirect::new()),
status: Rc::new(RefCell::new(Status::Cancellable)),
gemini: gemini::Client::new(),
}
}
@ -41,9 +56,23 @@ impl Client {
let previous = self.cancellable.replace(cancellable.clone());
if !previous.is_cancelled() {
previous.cancel();
self.status.replace(Status::Cancelled);
} else {
self.status.replace(Status::Cancellable);
}
// Done
cancellable
}
pub fn request(&self, query: &str) {
self.status.replace(Status::Request(query.to_string()));
// Forcefully prevent infinitive redirection
// * this condition just to make sure that client will never stuck by driver implementation issue
if self.redirect.count() > redirect::LIMIT {
self.status.replace(Status::RedirectLimit(redirect::LIMIT));
// @TODO return;
}
}
}

View File

@ -0,0 +1,67 @@
mod item;
use item::Item;
use gtk::glib::Uri;
use std::cell::{Cell, RefCell};
/// Global limit to prevent infinitive redirection issues
/// * defined value is globally applicable to ALL drivers
/// * every driver implement its own value, according to protocol specification
/// * the `Client` will forcefully break redirection loop when iteration reach this value
pub const LIMIT: usize = 10; // @TODO make optional
pub struct Redirect {
chain: RefCell<Vec<Item>>,
}
impl Default for Redirect {
fn default() -> Self {
Self::new()
}
}
impl Redirect {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
Self {
chain: RefCell::new(Vec::new()),
}
}
// Actions
/// Register new redirect in chain
pub fn add(&self, request: Uri, referrer: Option<Uri>, is_foreground: bool) -> &Self {
self.chain.borrow_mut().push(Item {
request,
referrer,
is_foreground,
is_processed: Cell::new(false),
});
self
}
/// Clear redirect chain
pub fn clear(&self) {
self.chain.borrow_mut().clear()
}
// Getters
/// Get total redirects count in chain
pub fn count(&self) -> usize {
self.chain.borrow().len() + 1
}
/// Get last redirection `Item` copy
pub fn last(&self) -> Option<Item> {
if let Some(redirect) = self.chain.borrow().last() {
if !redirect.is_processed.replace(true) {
return Some(redirect.clone());
}
}
None
}
}

View File

@ -0,0 +1,11 @@
use gtk::glib::Uri;
use std::cell::Cell;
/// Single redirect `Item`
#[derive(Clone)]
pub struct Item {
pub is_foreground: bool,
pub is_processed: Cell<bool>,
pub referrer: Option<Uri>,
pub request: Uri,
}

View File

@ -0,0 +1,36 @@
use std::fmt::{Display, Formatter, Result};
/// Local `Client` status
/// * not same as the Gemini status!
pub enum Status {
/// Ready to use (or cancel from outside)
Cancellable,
/// Operation cancelled, new `Cancellable` required to continue
Cancelled,
/// Redirection count limit reached by protocol driver or global settings
RedirectLimit(usize),
/// New `request` begin
Request(String),
}
impl Display for Status {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
Self::Cancellable => {
write!(f, "Ready to use (or cancel from outside)")
}
Self::Cancelled => {
write!(
f,
"Operation cancelled, new `Cancellable` required to continue"
)
}
Self::RedirectLimit(count) => {
write!(f, "Redirection count limit ({count}) reached by protocol driver or global settings")
}
Self::Request(value) => {
write!(f, "Request `{value}`...")
}
}
}
}

View File

@ -1,57 +0,0 @@
mod item;
use item::Item;
use gtk::glib::GString;
use std::cell::{Cell, RefCell};
pub struct Redirect {
index: RefCell<Vec<Item>>,
}
impl Default for Redirect {
fn default() -> Self {
Self::new()
}
}
impl Redirect {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
Self {
index: RefCell::new(Vec::new()),
}
}
// Actions
pub fn add(&self, request: GString, referrer: Option<GString>, is_foreground: bool) -> &Self {
self.index.borrow_mut().push(Item {
request,
referrer,
is_foreground,
is_processed: Cell::new(false),
});
self
}
pub fn clear(&self) {
self.index.borrow_mut().clear()
}
// Getters
pub fn count(&self) -> usize {
self.index.borrow().len() + 1
}
pub fn last(&self) -> Option<Item> {
if let Some(redirect) = self.index.borrow().last() {
if !redirect.is_processed.replace(true) {
return Some(redirect.clone());
}
}
None
}
}