Add domain history clear confirmation

This commit is contained in:
2026-05-08 04:21:02 -04:00
parent 02279f8984
commit 71750114a6
7 changed files with 393 additions and 132 deletions
+61
View File
@@ -1,13 +1,36 @@
use ely_domain::{ProfileId, SpaceId};
use gpui::Context;
use super::{ElyShell, ShellState};
#[derive(Clone, Debug)]
pub(super) struct PendingHistoryDomainClear {
profile_id: ProfileId,
space_id: SpaceId,
host: String,
}
impl PendingHistoryDomainClear {
fn new(profile_id: ProfileId, space_id: SpaceId, host: String) -> Self {
Self { profile_id, space_id, host }
}
pub(super) fn host(&self) -> &str {
&self.host
}
pub(super) fn matches_context(&self, profile_id: &ProfileId, space_id: &SpaceId) -> bool {
&self.profile_id == profile_id && &self.space_id == space_id
}
}
impl ElyShell {
pub(super) fn request_clear_history_confirmation(&mut self, cx: &mut Context<Self>) {
if let ShellState::Ready(core) = &mut self.state
&& let Ok(snapshot) = core.snapshot()
{
self.history_clear_confirmation = Some(snapshot.active_profile_id);
self.pending_history_domain_clear = None;
cx.notify();
}
}
@@ -24,6 +47,44 @@ impl ElyShell {
&& core.clear_active_profile_history().is_ok()
{
self.history_clear_confirmation = None;
self.pending_history_domain_clear = None;
cx.notify();
}
}
pub(super) fn request_clear_history_domain_confirmation(
&mut self,
host: String,
cx: &mut Context<Self>,
) {
if let ShellState::Ready(core) = &mut self.state
&& let Ok(snapshot) = core.snapshot()
{
self.pending_history_domain_clear = Some(PendingHistoryDomainClear::new(
snapshot.active_profile_id,
snapshot.active_space_id,
host,
));
cx.notify();
}
}
pub(super) fn cancel_clear_history_domain_confirmation(&mut self, cx: &mut Context<Self>) {
self.pending_history_domain_clear = None;
cx.notify();
}
pub(super) fn clear_active_space_history_for_pending_domain(&mut self, cx: &mut Context<Self>) {
let Some(pending) = self.pending_history_domain_clear.clone() else {
return;
};
if let ShellState::Ready(core) = &mut self.state
&& let Ok(snapshot) = core.snapshot()
&& pending.matches_context(&snapshot.active_profile_id, &snapshot.active_space_id)
&& core.clear_active_space_history_for_host(pending.host()).is_ok()
{
self.pending_history_domain_clear = None;
cx.notify();
}
}
+2 -131
View File
@@ -5,6 +5,7 @@ mod download_labels;
mod download_settings;
mod downloads;
mod general;
mod history;
mod plugin_catalog;
mod plugin_details;
mod plugins;
@@ -23,7 +24,7 @@ mod task_manager;
use ely_browser_core::BrowserSnapshot;
use ely_design_system::{colors, spacing};
use ely_domain::{ArchiveSource, ArchivedTab, BrowserTab, HistoryEntry};
use ely_domain::{ArchiveSource, ArchivedTab, BrowserTab};
use gpui::{
AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString,
StatefulInteractiveElement, Styled, div, px, rgb,
@@ -73,136 +74,6 @@ impl ElyShell {
}
}
fn render_history_page(
&mut self,
snapshot: &BrowserSnapshot,
cx: &mut Context<Self>,
) -> AnyElement {
render_canvas_surface(
div()
.size_full()
.p_8()
.flex()
.flex_col()
.gap_5()
.child(
div()
.flex()
.items_end()
.justify_between()
.child(
div()
.flex()
.flex_col()
.gap_2()
.child(
div()
.text_size(px(26.0))
.text_color(rgb(colors::INK))
.child("History"),
)
.child(div().text_sm().text_color(rgb(colors::MUTED)).child(
format!(
"{} / {}",
snapshot.active_profile_name, snapshot.active_space_name
),
)),
)
.child(
div()
.text_xs()
.text_color(rgb(colors::MUTED))
.child(format!("{} entries", snapshot.history_entries.len())),
),
)
.child(self.render_history_list(snapshot, cx)),
)
}
fn render_history_list(
&mut self,
snapshot: &BrowserSnapshot,
cx: &mut Context<Self>,
) -> AnyElement {
if snapshot.history_entries.is_empty() {
return div()
.flex_1()
.border_t_1()
.border_color(rgb(colors::HAIRLINE))
.pt_5()
.text_sm()
.text_color(rgb(colors::MUTED))
.child("History is empty for this Space and Profile.")
.into_any_element();
}
div()
.flex_1()
.flex()
.flex_col()
.overflow_y_scrollbar()
.border_t_1()
.border_color(rgb(colors::HAIRLINE))
.children(
snapshot
.history_entries
.iter()
.rev()
.enumerate()
.map(|(index, entry)| self.render_history_row(index, entry, cx)),
)
.into_any_element()
}
fn render_history_row(
&mut self,
index: usize,
entry: &HistoryEntry,
cx: &mut Context<Self>,
) -> AnyElement {
let url = entry.url().clone();
div()
.id(SharedString::from(format!("history-{index}")))
.py_3()
.border_b_1()
.border_color(rgb(colors::HAIRLINE))
.flex()
.items_center()
.justify_between()
.gap_4()
.cursor_pointer()
.hover(|style| style.bg(rgb(colors::CANVAS_SOFT)))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(move |shell, _, window, cx| {
shell.open_url(url.clone(), window, cx);
}))
.child(
div()
.min_w_0()
.flex()
.flex_col()
.gap_1()
.child(
div()
.text_sm()
.font_semibold()
.truncate()
.text_color(rgb(colors::INK))
.child(entry.title().to_string()),
)
.child(
div()
.text_xs()
.truncate()
.text_color(rgb(colors::MUTED))
.child(entry.url().display_url()),
),
)
.child(div().text_color(rgb(colors::MUTED_SOFT)).child(IconName::ExternalLink))
.into_any_element()
}
fn render_archive_page(
&mut self,
snapshot: &BrowserSnapshot,
@@ -0,0 +1,231 @@
use ely_browser_core::BrowserSnapshot;
use ely_design_system::colors;
use ely_domain::HistoryEntry;
use gpui::prelude::FluentBuilder;
use gpui::{
AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div,
px, rgb,
};
use gpui_component::{
Sizable, StyledExt,
button::{Button, ButtonVariants},
scroll::ScrollableElement,
};
use super::super::PendingHistoryDomainClear;
use super::{ElyShell, render_canvas_surface};
impl ElyShell {
pub(super) fn render_history_page(
&mut self,
snapshot: &BrowserSnapshot,
cx: &mut Context<Self>,
) -> AnyElement {
let pending_domain_clear = self.pending_history_domain_clear.clone().filter(|pending| {
pending.matches_context(&snapshot.active_profile_id, &snapshot.active_space_id)
});
render_canvas_surface(
div()
.size_full()
.p_8()
.flex()
.flex_col()
.gap_5()
.child(render_history_header(snapshot))
.when_some(pending_domain_clear, |this, pending| {
this.child(render_domain_clear_confirmation(&pending, cx))
})
.child(render_history_list(snapshot, cx)),
)
}
}
fn render_history_header(snapshot: &BrowserSnapshot) -> AnyElement {
div()
.flex()
.items_end()
.justify_between()
.child(
div()
.flex()
.flex_col()
.gap_2()
.child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("History"))
.child(div().text_sm().text_color(rgb(colors::MUTED)).child(format!(
"{} / {}",
snapshot.active_profile_name, snapshot.active_space_name
))),
)
.child(
div()
.text_xs()
.text_color(rgb(colors::MUTED))
.child(format!("{} entries", snapshot.history_entries.len())),
)
.into_any_element()
}
fn render_domain_clear_confirmation(
pending: &PendingHistoryDomainClear,
cx: &mut Context<ElyShell>,
) -> AnyElement {
div()
.rounded_md()
.border_1()
.border_color(rgb(colors::ERROR))
.bg(rgb(colors::CANVAS_SOFT))
.px_4()
.py_3()
.flex()
.items_center()
.justify_between()
.gap_4()
.child(
div()
.min_w_0()
.flex()
.flex_col()
.gap_1()
.child(
div()
.text_sm()
.font_semibold()
.text_color(rgb(colors::INK))
.child(format!("Confirm clearing {}", pending.host())),
)
.child(
div()
.text_xs()
.text_color(rgb(colors::MUTED))
.child("This removes matching history in the current Space."),
),
)
.child(
div()
.flex()
.items_center()
.gap_2()
.child(
Button::new("cancel-clear-history-domain")
.ghost()
.xsmall()
.label("Cancel")
.on_click(cx.listener(|shell, _, _, cx| {
shell.cancel_clear_history_domain_confirmation(cx);
})),
)
.child(
Button::new("confirm-clear-history-domain")
.danger()
.xsmall()
.label("Clear")
.on_click(cx.listener(|shell, _, _, cx| {
shell.clear_active_space_history_for_pending_domain(cx);
})),
),
)
.into_any_element()
}
fn render_history_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) -> AnyElement {
if snapshot.history_entries.is_empty() {
return div()
.flex_1()
.border_t_1()
.border_color(rgb(colors::HAIRLINE))
.pt_5()
.text_sm()
.text_color(rgb(colors::MUTED))
.child("History is empty for this Space and Profile.")
.into_any_element();
}
div()
.flex_1()
.min_h_0()
.flex()
.flex_col()
.overflow_y_scrollbar()
.border_t_1()
.border_color(rgb(colors::HAIRLINE))
.children(
snapshot
.history_entries
.iter()
.rev()
.enumerate()
.map(|(index, entry)| render_history_row(index, entry, cx)),
)
.into_any_element()
}
fn render_history_row(
index: usize,
entry: &HistoryEntry,
cx: &mut Context<ElyShell>,
) -> AnyElement {
let url = entry.url().clone();
let host = entry.url().host();
div()
.id(SharedString::from(format!("history-{index}")))
.py_3()
.border_b_1()
.border_color(rgb(colors::HAIRLINE))
.flex()
.items_center()
.justify_between()
.gap_4()
.child(
div()
.min_w_0()
.flex()
.flex_col()
.gap_1()
.child(
div()
.text_sm()
.font_semibold()
.truncate()
.text_color(rgb(colors::INK))
.child(entry.title().to_string()),
)
.child(
div()
.text_xs()
.truncate()
.text_color(rgb(colors::MUTED))
.child(entry.url().display_url()),
),
)
.child(
div()
.flex()
.items_center()
.gap_2()
.child(
Button::new(("open-history-entry", index))
.ghost()
.xsmall()
.label("Open")
.tooltip("Open History Entry")
.on_click(cx.listener(move |shell, _, window, cx| {
shell.open_url(url.clone(), window, cx);
})),
)
.when_some(host, |this, host| {
this.child(
Button::new(("clear-history-domain", index))
.danger()
.xsmall()
.label("Clear Domain")
.tooltip("Clear Domain History")
.on_click(cx.listener(move |shell, _, _, cx| {
shell.request_clear_history_domain_confirmation(host.clone(), cx);
})),
)
}),
)
.into_any_element()
}
+3
View File
@@ -16,6 +16,7 @@ use gpui::{App, AppContext, Context, Entity, FocusHandle, Focusable, Subscriptio
use gpui_component::input::{InputEvent, InputState, SelectAll};
use downloads::PendingDownloadFileAction;
use history::PendingHistoryDomainClear;
use plugins::{PendingPluginInstall, PendingPluginUninstall};
use crate::{
@@ -38,6 +39,7 @@ pub struct ElyShell {
download_clear_confirmation: bool,
download_security_confirmation: Option<PendingDownloadFileAction>,
history_clear_confirmation: Option<ProfileId>,
pending_history_domain_clear: Option<PendingHistoryDomainClear>,
site_permissions_clear_confirmation: Option<ProfileId>,
plugin_install_error: Option<String>,
pending_plugin_install: Option<PendingPluginInstall>,
@@ -101,6 +103,7 @@ impl ElyShell {
download_clear_confirmation: false,
download_security_confirmation: None,
history_clear_confirmation: None,
pending_history_domain_clear: None,
site_permissions_clear_confirmation: None,
plugin_install_error: None,
pending_plugin_install: None,