mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-27 11:34:14 +00:00
implement recently visited memory index, add new global menu section
This commit is contained in:
parent
0618283d84
commit
38594c0aa8
@ -33,8 +33,8 @@ GTK 4 / Libadwaita client written in Rust
|
|||||||
* [ ] History
|
* [ ] History
|
||||||
* [ ] Browser window
|
* [ ] Browser window
|
||||||
* [x] Page navigation
|
* [x] Page navigation
|
||||||
* [ ] Recently visited
|
* [x] Recently visited
|
||||||
* [x] Recently closed tabs
|
* [x] Recently closed
|
||||||
* [ ] Proxy
|
* [ ] Proxy
|
||||||
* [ ] Session
|
* [ ] Session
|
||||||
* [ ] Window
|
* [ ] Window
|
||||||
|
@ -11,6 +11,7 @@ use std::rc::Rc;
|
|||||||
const LABEL_MAX_LENGTH: usize = 32;
|
const LABEL_MAX_LENGTH: usize = 32;
|
||||||
const RECENT_BOOKMARKS: usize = 50;
|
const RECENT_BOOKMARKS: usize = 50;
|
||||||
const RECENTLY_CLOSED: usize = 50;
|
const RECENTLY_CLOSED: usize = 50;
|
||||||
|
const RECENT_REQUESTS: usize = 50;
|
||||||
|
|
||||||
pub struct Menu {
|
pub struct Menu {
|
||||||
pub menu_button: MenuButton,
|
pub menu_button: MenuButton,
|
||||||
@ -137,8 +138,13 @@ impl Menu {
|
|||||||
|
|
||||||
// Main > History > Recently closed
|
// Main > History > Recently closed
|
||||||
// * menu items dynamically generated using profile memory pool and `set_create_popup_func`
|
// * menu items dynamically generated using profile memory pool and `set_create_popup_func`
|
||||||
let main_history_closed = gio::Menu::new();
|
let main_history_tab = gio::Menu::new();
|
||||||
main_history.append_submenu(Some("Closed tabs"), &main_history_closed);
|
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);
|
main.append_submenu(Some("History"), &main_history);
|
||||||
|
|
||||||
@ -204,8 +210,8 @@ impl Menu {
|
|||||||
// @TODO
|
// @TODO
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// History
|
// Recently closed history
|
||||||
main_history_closed.remove_all();
|
main_history_tab.remove_all();
|
||||||
for item in profile.history.memory.tab.recent(RECENTLY_CLOSED) {
|
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 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);
|
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()
|
window_action.open.simple_action.name()
|
||||||
)), Some(&item_request.to_variant()));
|
)), 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,7 @@ use gtk::{
|
|||||||
gdk::Texture,
|
gdk::Texture,
|
||||||
gdk_pixbuf::Pixbuf,
|
gdk_pixbuf::Pixbuf,
|
||||||
gio::SocketClientEvent,
|
gio::SocketClientEvent,
|
||||||
glib::{gformat, GString, Priority, Uri, UriFlags, UriHideFlags},
|
glib::{gformat, DateTime, GString, Priority, Uri, UriFlags, UriHideFlags},
|
||||||
prelude::{EditableExt, FileExt, SocketClientExt},
|
prelude::{EditableExt, FileExt, SocketClientExt},
|
||||||
};
|
};
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
@ -234,7 +234,7 @@ impl Page {
|
|||||||
scheme => {
|
scheme => {
|
||||||
// Add history record
|
// Add history record
|
||||||
if is_history {
|
if is_history {
|
||||||
snap_history(self.navigation.clone());
|
snap_history(&self.profile, &self.navigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update widget
|
// Update widget
|
||||||
@ -322,7 +322,7 @@ impl Page {
|
|||||||
self.navigation.restore(transaction, &record.id)?;
|
self.navigation.restore(transaction, &record.id)?;
|
||||||
// Make initial page history snap using `navigation` values restored
|
// Make initial page history snap using `navigation` values restored
|
||||||
// * just to have back/forward navigation ability
|
// * 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()),
|
Err(e) => return Err(e.to_string()),
|
||||||
@ -391,11 +391,12 @@ impl Page {
|
|||||||
let browser_action = self.browser_action.clone();
|
let browser_action = self.browser_action.clone();
|
||||||
let cancellable = self.client.cancellable();
|
let cancellable = self.client.cancellable();
|
||||||
let content = self.content.clone();
|
let content = self.content.clone();
|
||||||
let search = self.search.clone();
|
|
||||||
let id = self.id.clone();
|
let id = self.id.clone();
|
||||||
let input = self.input.clone();
|
let input = self.input.clone();
|
||||||
let meta = self.meta.clone();
|
let meta = self.meta.clone();
|
||||||
let navigation = self.navigation.clone();
|
let navigation = self.navigation.clone();
|
||||||
|
let profile = self.profile.clone();
|
||||||
|
let search = self.search.clone();
|
||||||
let tab_action = self.tab_action.clone();
|
let tab_action = self.tab_action.clone();
|
||||||
let window_action = self.window_action.clone();
|
let window_action = self.window_action.clone();
|
||||||
|
|
||||||
@ -477,7 +478,7 @@ impl Page {
|
|||||||
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-20
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-20
|
||||||
response::meta::Status::Success => {
|
response::meta::Status::Success => {
|
||||||
if is_history {
|
if is_history {
|
||||||
snap_history(navigation.clone());
|
snap_history(&profile, &navigation);
|
||||||
}
|
}
|
||||||
if is_download {
|
if is_download {
|
||||||
// Init download widget
|
// Init download widget
|
||||||
@ -805,7 +806,7 @@ impl Page {
|
|||||||
response::meta::Status::CertificateInvalid => {
|
response::meta::Status::CertificateInvalid => {
|
||||||
// Add history record
|
// Add history record
|
||||||
if is_history {
|
if is_history {
|
||||||
snap_history(navigation.clone());
|
snap_history(&profile, &navigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update widget
|
// Update widget
|
||||||
@ -830,7 +831,7 @@ impl Page {
|
|||||||
_ => {
|
_ => {
|
||||||
// Add history record
|
// Add history record
|
||||||
if is_history {
|
if is_history {
|
||||||
snap_history(navigation.clone());
|
snap_history(&profile, &navigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update widget
|
// Update widget
|
||||||
@ -853,7 +854,7 @@ impl Page {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Add history record
|
// Add history record
|
||||||
if is_history {
|
if is_history {
|
||||||
snap_history(navigation.clone());
|
snap_history(&profile, &navigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update widget
|
// Update widget
|
||||||
@ -920,15 +921,21 @@ fn is_external_uri(subject: &Uri, base: &Uri) -> bool {
|
|||||||
|
|
||||||
/// Make new history record for given `navigation` object
|
/// Make new history record for given `navigation` object
|
||||||
/// * applies on shared conditions match only
|
/// * 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();
|
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() {
|
if match navigation.history.current() {
|
||||||
Some(current) => current != request,
|
Some(current) => current != request, // apply additional filters
|
||||||
None => true,
|
None => true,
|
||||||
} {
|
} {
|
||||||
// Add new record match conditions
|
|
||||||
navigation.history.add(request, true)
|
navigation.history.add(request, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
mod request;
|
||||||
mod tab;
|
mod tab;
|
||||||
|
|
||||||
|
use request::Request;
|
||||||
use tab::Tab;
|
use tab::Tab;
|
||||||
|
|
||||||
/// Reduce disk usage by cache Bookmarks index in memory
|
/// Reduce disk usage by cache Bookmarks index in memory
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
|
pub request: Request,
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,6 +21,9 @@ impl Memory {
|
|||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { tab: Tab::new() }
|
Self {
|
||||||
|
request: Request::new(),
|
||||||
|
tab: Tab::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
src/profile/history/memory/request.rs
Normal file
60
src/profile/history/memory/request.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user