Add site permissions settings page
This commit is contained in:
@@ -15,6 +15,7 @@ mod search;
|
||||
mod settings;
|
||||
mod shortcuts;
|
||||
mod sidebar_tabs;
|
||||
mod site_permissions_settings;
|
||||
mod site_settings;
|
||||
mod spaces;
|
||||
mod sync;
|
||||
@@ -60,6 +61,9 @@ impl ElyShell {
|
||||
"ely://settings/privacy-security" => self.render_privacy_security_page(snapshot, cx),
|
||||
"ely://settings/downloads" => self.render_download_settings_page(snapshot, cx),
|
||||
"ely://settings/spaces" => self.render_spaces_page(snapshot, cx),
|
||||
"ely://settings/site-permissions" => {
|
||||
self.render_site_permissions_settings_page(snapshot, cx)
|
||||
}
|
||||
"ely://settings/shortcuts" => self.render_shortcuts_page(snapshot),
|
||||
"ely://settings/plugins" => self.render_plugins_page(snapshot, cx),
|
||||
"ely://settings/profiles" => self.render_profiles_page(snapshot, cx),
|
||||
|
||||
@@ -54,6 +54,12 @@ const SETTINGS_ROUTES: &[SettingsRoute] = &[
|
||||
detail: "Profile download location and save behavior.",
|
||||
route: "ely://settings/downloads",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Globe,
|
||||
title: "Site Permissions",
|
||||
detail: "Profile-scoped site permissions and local audit state.",
|
||||
route: "ely://settings/site-permissions",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::CircleUser,
|
||||
title: "Profiles",
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::colors;
|
||||
use ely_domain::{SiteOrigin, SitePermissionDecision, SitePermissionEntry};
|
||||
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
||||
use gpui_component::{
|
||||
IconName, Sizable, StyledExt,
|
||||
button::{Button, ButtonVariants},
|
||||
scroll::ScrollableElement,
|
||||
};
|
||||
|
||||
use super::{ElyShell, render_canvas_surface};
|
||||
|
||||
impl ElyShell {
|
||||
pub(super) fn render_site_permissions_settings_page(
|
||||
&mut self,
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
render_canvas_surface(
|
||||
div()
|
||||
.size_full()
|
||||
.p_8()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_5()
|
||||
.child(render_site_permissions_header(snapshot))
|
||||
.child(render_site_permissions_summary(snapshot))
|
||||
.child(render_site_permissions_list(snapshot, cx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_site_permissions_header(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.items_end()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(26.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child("Site Permissions"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(format!("Profile: {}", snapshot.active_profile_name)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(IconName::Globe)
|
||||
.child("Profile scoped"),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_site_permissions_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.border_t_1()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.py_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.children([
|
||||
site_permission_metric("Configured", snapshot.site_permissions.len()),
|
||||
site_permission_metric("Allowed", allowed_count(snapshot)),
|
||||
site_permission_metric("Denied", denied_count(snapshot)),
|
||||
site_permission_metric("Audit Events", snapshot.site_permission_audit_events.len()),
|
||||
])
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn site_permission_metric(label: &'static str, value: usize) -> AnyElement {
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(div().text_xs().text_color(rgb(colors::MUTED)).child(label))
|
||||
.child(
|
||||
div().text_sm().font_semibold().text_color(rgb(colors::INK)).child(value.to_string()),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_site_permissions_list(
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
if snapshot.site_permissions.is_empty() {
|
||||
return div()
|
||||
.flex_1()
|
||||
.border_t_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.pt_5()
|
||||
.text_sm()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child("No site permissions are configured for this 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
|
||||
.site_permissions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, entry)| render_site_permission_entry(index, entry, cx)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_site_permission_entry(
|
||||
index: usize,
|
||||
entry: &SitePermissionEntry,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let origin = entry.origin().clone();
|
||||
let route = site_settings_route(&origin);
|
||||
let decision = entry.decision();
|
||||
|
||||
div()
|
||||
.py_3()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(
|
||||
div()
|
||||
.text_color(rgb(permission_decision_color(decision)))
|
||||
.child(permission_decision_icon(decision)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(entry.origin().as_str().to_string()),
|
||||
)
|
||||
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(
|
||||
format!("{} - {}", entry.feature().label(), entry.decision().label()),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new(("open-site-permission-settings", index))
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.label("Open")
|
||||
.tooltip("Open Site Settings")
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.open_internal_tab(&route, window, cx);
|
||||
})),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn allowed_count(snapshot: &BrowserSnapshot) -> usize {
|
||||
snapshot
|
||||
.site_permissions
|
||||
.iter()
|
||||
.filter(|entry| {
|
||||
matches!(
|
||||
entry.decision(),
|
||||
SitePermissionDecision::AllowOnce | SitePermissionDecision::AllowAlways
|
||||
)
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
fn denied_count(snapshot: &BrowserSnapshot) -> usize {
|
||||
snapshot
|
||||
.site_permissions
|
||||
.iter()
|
||||
.filter(|entry| entry.decision() == SitePermissionDecision::DenyAlways)
|
||||
.count()
|
||||
}
|
||||
|
||||
fn site_settings_route(origin: &SiteOrigin) -> String {
|
||||
format!("ely://site/{}", origin.as_str())
|
||||
}
|
||||
|
||||
fn permission_decision_color(decision: SitePermissionDecision) -> u32 {
|
||||
match decision {
|
||||
SitePermissionDecision::AllowOnce | SitePermissionDecision::AllowAlways => colors::SUCCESS,
|
||||
SitePermissionDecision::DenyAlways => colors::ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn permission_decision_icon(decision: SitePermissionDecision) -> IconName {
|
||||
match decision {
|
||||
SitePermissionDecision::AllowOnce | SitePermissionDecision::AllowAlways => {
|
||||
IconName::CircleCheck
|
||||
}
|
||||
SitePermissionDecision::DenyAlways => IconName::CircleX,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user