Add idle tab auto archive policy

This commit is contained in:
2026-05-08 02:27:29 -04:00
parent 15cf65be62
commit 7f479ff9e1
7 changed files with 229 additions and 10 deletions
+16 -5
View File
@@ -1,12 +1,14 @@
use ely_domain::{CommandIntent, CommandScope, ProfileId, ProfileKind, SpaceId};
use std::time::SystemTime;
use ely_domain::{ArchivePolicy, CommandIntent, CommandScope, ProfileId, ProfileKind, SpaceId};
use crate::{
CoreError,
navigation::{
about_url, archive_url, bookmarks_url, downloads_url, history_url, move_tab_space_name,
new_private_profile_name, new_profile_name, new_space_name, plugin_detail_url, plugins_url,
reading_list_url, search_url, settings_page_url, settings_url, space_icon,
switch_profile_name, sync_status_url, task_manager_url,
about_url, archive_idle_days, archive_url, bookmarks_url, downloads_url, history_url,
move_tab_space_name, new_private_profile_name, new_profile_name, new_space_name,
plugin_detail_url, plugins_url, reading_list_url, search_url, settings_page_url,
settings_url, space_icon, switch_profile_name, sync_status_url, task_manager_url,
},
};
@@ -115,6 +117,11 @@ impl BrowserCore {
self.select_profile(&profile_id)?;
return Ok(true);
}
if let Some(idle_days) = archive_idle_days(command) {
self.set_active_space_archive_policy(ArchivePolicy::IdleDays(idle_days))?;
self.archive_idle_tabs(SystemTime::now())?;
return Ok(true);
}
match command.to_ascii_lowercase().as_str() {
"new-tab" => {
@@ -185,6 +192,10 @@ impl BrowserCore {
"close-split-view" | "close split view" => {
Ok(self.close_active_saved_split_view()?.is_some())
}
"archive-idle-tabs" | "archive idle tabs" => {
self.archive_idle_tabs(SystemTime::now())?;
Ok(true)
}
"favorite" | "toggle-favorite" => {
self.toggle_active_tab_favorite()?;
Ok(true)
+81 -1
View File
@@ -1,4 +1,8 @@
use ely_domain::{ArchiveSource, ArchivedTab, BrowserTab, ProfileId, SpaceId, TabId, UrlText};
use std::time::{Duration, SystemTime};
use ely_domain::{
ArchivePolicy, ArchiveSource, ArchivedTab, BrowserTab, ProfileId, SpaceId, TabId, UrlText,
};
use crate::{
CoreError,
@@ -168,6 +172,23 @@ impl BrowserCore {
self.restore_archived_tab_at_index(index).map(Some)
}
pub fn archive_idle_tabs(&mut self, now: SystemTime) -> Result<usize, CoreError> {
let ArchivePolicy::IdleDays(idle_days) = self.active_space()?.archive_policy() else {
return Ok(0);
};
let active_space_id = self.active_space_id.clone();
let idle_after = Duration::from_secs(u64::from(*idle_days) * 86_400);
let tab_ids = self.idle_archive_candidates(&active_space_id, now, idle_after);
let archived_count = tab_ids.len();
for tab_id in tab_ids {
self.archive_idle_tab(&tab_id)?;
}
Ok(archived_count)
}
pub fn select_tab(&mut self, tab_id: &TabId) -> Result<(), CoreError> {
let tab = self
.tabs
@@ -184,6 +205,7 @@ impl BrowserCore {
self.active_tabs_by_space_profile
.insert((active_space_id.clone(), active_profile_id), active_tab_id.clone());
self.active_tabs_by_space.insert(active_space_id, active_tab_id);
self.record_tab_activity(tab_id, SystemTime::now());
Ok(())
}
@@ -348,4 +370,62 @@ impl BrowserCore {
},
)
}
fn idle_archive_candidates(
&self,
space_id: &SpaceId,
now: SystemTime,
idle_after: Duration,
) -> Vec<TabId> {
self.tabs
.iter()
.filter(|tab| tab.space_id() == space_id)
.filter(|tab| tab.id() != &self.active_tab_id)
.filter(|tab| !tab.flags().favorite && !tab.flags().pinned)
.filter(|tab| tab.split_id().is_none())
.filter(|tab| tab_is_idle(tab, now, idle_after))
.map(|tab| tab.id().clone())
.collect()
}
fn archive_idle_tab(&mut self, tab_id: &TabId) -> Result<(), CoreError> {
let tab_index = self
.tabs
.iter()
.position(|tab| tab.id() == tab_id)
.ok_or_else(|| CoreError::TabNotFound { id: tab_id.clone() })?;
let mut tab = self.tabs.remove(tab_index);
let space_id = tab.space_id().clone();
let profile_id = tab.profile_id().clone();
tab.clear_split_id();
self.archived_tabs.push(ArchivedTab::new(tab, ArchiveSource::AutoArchive));
self.refresh_space_profile_active_tab(&space_id, &profile_id);
Ok(())
}
fn refresh_space_profile_active_tab(&mut self, space_id: &SpaceId, profile_id: &ProfileId) {
let key = (space_id.clone(), profile_id.clone());
if self.active_tabs_by_space_profile.get(&key).is_some_and(|tab_id| {
self.tabs.iter().any(|tab| {
tab.id() == tab_id && tab.space_id() == space_id && tab.profile_id() == profile_id
})
}) {
return;
}
self.active_tabs_by_space_profile.remove(&key);
if let Some(tab_id) = self
.tabs
.iter()
.find(|tab| tab.space_id() == space_id && tab.profile_id() == profile_id)
.map(|tab| tab.id().clone())
{
self.active_tabs_by_space_profile.insert(key, tab_id);
}
}
}
fn tab_is_idle(tab: &BrowserTab, now: SystemTime, idle_after: Duration) -> bool {
now.duration_since(tab.last_active_at()).is_ok_and(|idle_time| idle_time >= idle_after)
}