implement recently visited memory index, add new global menu section

This commit is contained in:
yggverse 2025-01-12 15:59:30 +02:00
parent 0618283d84
commit 38594c0aa8
5 changed files with 113 additions and 20 deletions

View File

@ -33,8 +33,8 @@ GTK 4 / Libadwaita client written in Rust
* [ ] History
* [ ] Browser window
* [x] Page navigation
* [ ] Recently visited
* [x] Recently closed tabs
* [x] Recently visited
* [x] Recently closed
* [ ] Proxy
* [ ] Session
* [ ] Window

View File

@ -11,6 +11,7 @@ use std::rc::Rc;
const LABEL_MAX_LENGTH: usize = 32;
const RECENT_BOOKMARKS: usize = 50;
const RECENTLY_CLOSED: usize = 50;
const RECENT_REQUESTS: usize = 50;
pub struct Menu {
pub menu_button: MenuButton,
@ -137,8 +138,13 @@ impl Menu {
// Main > History > Recently closed
// * menu items dynamically generated using profile memory pool and `set_create_popup_func`
let main_history_closed = gio::Menu::new();
main_history.append_submenu(Some("Closed tabs"), &main_history_closed);
let main_history_tab = gio::Menu::new();
main_history.append_submenu(Some("Recently closed"), &main_history_tab);
// Main > History > Recent requests
// * menu items dynamically generated using profile memory pool and `set_create_popup_func`
let main_history_request = gio::Menu::new();
main_history.append_section(None, &main_history_request);
main.append_submenu(Some("History"), &main_history);
@ -204,8 +210,8 @@ impl Menu {
// @TODO
// }
// History
main_history_closed.remove_all();
// Recently closed history
main_history_tab.remove_all();
for item in profile.history.memory.tab.recent(RECENTLY_CLOSED) {
let item_request = item.page.navigation.request.widget.entry.text(); // @TODO restore entire `Item`
let menu_item = gio::MenuItem::new(Some(&label(&item_request, LABEL_MAX_LENGTH)), None);
@ -215,7 +221,20 @@ impl Menu {
window_action.open.simple_action.name()
)), Some(&item_request.to_variant()));
main_history_closed.append_item(&menu_item);
main_history_tab.append_item(&menu_item);
}
// Recently visited history
main_history_request.remove_all();
for request in profile.history.memory.request.recent(RECENT_REQUESTS) {
let menu_item = gio::MenuItem::new(Some(&label(&request, LABEL_MAX_LENGTH)), None);
menu_item.set_action_and_target_value(Some(&format!(
"{}.{}",
window_action.id,
window_action.open.simple_action.name()
)), Some(&request.to_variant()));
main_history_request.append_item(&menu_item);
}
}
});

View File

@ -28,7 +28,7 @@ use gtk::{
gdk::Texture,
gdk_pixbuf::Pixbuf,
gio::SocketClientEvent,
glib::{gformat, GString, Priority, Uri, UriFlags, UriHideFlags},
glib::{gformat, DateTime, GString, Priority, Uri, UriFlags, UriHideFlags},
prelude::{EditableExt, FileExt, SocketClientExt},
};
use sqlite::Transaction;
@ -234,7 +234,7 @@ impl Page {
scheme => {
// Add history record
if is_history {
snap_history(self.navigation.clone());
snap_history(&self.profile, &self.navigation);
}
// Update widget
@ -322,7 +322,7 @@ impl Page {
self.navigation.restore(transaction, &record.id)?;
// Make initial page history snap using `navigation` values restored
// * just to have back/forward navigation ability
snap_history(self.navigation.clone());
snap_history(&self.profile, &self.navigation);
}
}
Err(e) => return Err(e.to_string()),
@ -391,11 +391,12 @@ impl Page {
let browser_action = self.browser_action.clone();
let cancellable = self.client.cancellable();
let content = self.content.clone();
let search = self.search.clone();
let id = self.id.clone();
let input = self.input.clone();
let meta = self.meta.clone();
let navigation = self.navigation.clone();
let profile = self.profile.clone();
let search = self.search.clone();
let tab_action = self.tab_action.clone();
let window_action = self.window_action.clone();
@ -477,7 +478,7 @@ impl Page {
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-20
response::meta::Status::Success => {
if is_history {
snap_history(navigation.clone());
snap_history(&profile, &navigation);
}
if is_download {
// Init download widget
@ -805,7 +806,7 @@ impl Page {
response::meta::Status::CertificateInvalid => {
// Add history record
if is_history {
snap_history(navigation.clone());
snap_history(&profile, &navigation);
}
// Update widget
@ -830,7 +831,7 @@ impl Page {
_ => {
// Add history record
if is_history {
snap_history(navigation.clone());
snap_history(&profile, &navigation);
}
// Update widget
@ -853,7 +854,7 @@ impl Page {
Err(e) => {
// Add history record
if is_history {
snap_history(navigation.clone());
snap_history(&profile, &navigation);
}
// Update widget
@ -920,15 +921,21 @@ fn is_external_uri(subject: &Uri, base: &Uri) -> bool {
/// Make new history record for given `navigation` object
/// * applies on shared conditions match only
fn snap_history(navigation: Rc<Navigation>) {
fn snap_history(profile: &Profile, navigation: &Navigation) {
let request = navigation.request.widget.entry.text();
// Apply additional filters
// Add new record into the global memory index (used in global menu)
profile
.history
.memory
.request
.set(request.clone(), DateTime::now_local().unwrap().to_unix());
// Add new record into the page navigation history
if match navigation.history.current() {
Some(current) => current != request,
Some(current) => current != request, // apply additional filters
None => true,
} {
// Add new record match conditions
navigation.history.add(request, true)
}
}

View File

@ -1,8 +1,12 @@
mod request;
mod tab;
use request::Request;
use tab::Tab;
/// Reduce disk usage by cache Bookmarks index in memory
pub struct Memory {
pub request: Request,
pub tab: Tab,
}
@ -17,6 +21,9 @@ impl Memory {
/// Create new `Self`
pub fn new() -> Self {
Self { tab: Tab::new() }
Self {
request: Request::new(),
tab: Tab::new(),
}
}
}

View File

@ -0,0 +1,60 @@
use gtk::glib::GString;
use itertools::Itertools;
use std::{cell::RefCell, collections::HashMap};
pub struct Value {
pub unix_timestamp: i64,
}
/// Recent request history
pub struct Request {
index: RefCell<HashMap<GString, Value>>,
}
impl Default for Request {
fn default() -> Self {
Self::new()
}
}
impl Request {
// Constructors
/// Create new `Self`
pub fn new() -> Self {
Self {
index: RefCell::new(HashMap::new()),
}
}
// Actions
/// Add new record with `request` as key and `unix_timestamp` as value
/// * replace with new value if `request` already exists
pub fn set(&self, request: GString, unix_timestamp: i64) {
self.index
.borrow_mut()
.insert(request, Value { unix_timestamp });
}
/// Get recent requests vector
/// * sorted by `unix_timestamp` DESC
pub fn recent(&self, limit: usize) -> Vec<GString> {
let mut recent: Vec<GString> = Vec::new();
for (request, _) in self
.index
.borrow()
.iter()
.sorted_by(|a, b| Ord::cmp(&b.1.unix_timestamp, &a.1.unix_timestamp))
.take(limit)
{
recent.push(request.clone())
}
recent
}
/// Get records total
pub fn total(&self) -> usize {
self.index.borrow().len()
}
}