Add profile sync policy controls

This commit is contained in:
2026-05-08 03:44:36 -04:00
parent 38a847c27e
commit ada0a82da8
6 changed files with 139 additions and 10 deletions
@@ -1,12 +1,14 @@
use ely_browser_core::BrowserSnapshot; use ely_browser_core::BrowserSnapshot;
use ely_design_system::colors; use ely_design_system::colors;
use ely_domain::{DownloadDestination, DownloadPolicy, Profile, ProfileId, ProfileKind}; use ely_domain::{
DownloadDestination, DownloadPolicy, Profile, ProfileId, ProfileKind, ProfileSyncPolicy,
};
use gpui::{ use gpui::{
AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div, AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div,
px, rgb, px, rgb,
}; };
use gpui_component::{ use gpui_component::{
IconName, Sizable, StyledExt, IconName, Selectable, Sizable, StyledExt,
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
scroll::ScrollableElement, scroll::ScrollableElement,
}; };
@@ -81,6 +83,8 @@ fn render_profile_row(
cx: &mut Context<ElyShell>, cx: &mut Context<ElyShell>,
) -> AnyElement { ) -> AnyElement {
let profile_id = profile.id().clone(); let profile_id = profile.id().clone();
let sync_profile_id = profile.id().clone();
let sync_policy = profile.sync_policy();
div() div()
.id(SharedString::from(format!("profile-{}", profile.id().as_str()))) .id(SharedString::from(format!("profile-{}", profile.id().as_str())))
@@ -121,7 +125,14 @@ fn render_profile_row(
), ),
), ),
) )
.child(render_profile_action(index, profile_id, active, cx)) .child(
div()
.flex()
.items_center()
.gap_3()
.child(render_profile_sync_action(index, sync_profile_id, sync_policy, cx))
.child(render_profile_action(index, profile_id, active, cx)),
)
.into_any_element() .into_any_element()
} }
@@ -163,11 +174,33 @@ fn render_profile_action(
.into_any_element() .into_any_element()
} }
fn render_profile_sync_action(
index: usize,
profile_id: ProfileId,
sync_policy: ProfileSyncPolicy,
cx: &mut Context<ElyShell>,
) -> AnyElement {
let next_policy = sync_policy.toggled();
let paused = sync_policy == ProfileSyncPolicy::Paused;
Button::new(("profile-sync-policy", index))
.ghost()
.xsmall()
.selected(paused)
.label(sync_policy.action_label())
.tooltip(sync_policy.label())
.on_click(cx.listener(move |shell, _, _, cx| {
shell.set_profile_sync_policy(&profile_id, next_policy, cx);
}))
.into_any_element()
}
fn profile_detail_label(profile: &Profile) -> String { fn profile_detail_label(profile: &Profile) -> String {
format!( format!(
"{} - {}", "{} - {} - {}",
profile_kind_label(profile.kind()), profile_kind_label(profile.kind()),
download_policy_label(profile.download_policy()) download_policy_label(profile.download_policy()),
profile.sync_policy().label()
) )
} }
+15 -2
View File
@@ -8,8 +8,8 @@ mod splits;
use ely_browser_core::{BrowserCore, InitialBrowserConfig}; use ely_browser_core::{BrowserCore, InitialBrowserConfig};
use ely_domain::{ use ely_domain::{
ArchivePolicy, CommandIntent, DownloadPolicy, FavoriteLimit, HistoryRecordingPolicy, ArchivePolicy, CommandIntent, DownloadPolicy, FavoriteLimit, HistoryRecordingPolicy,
NewTabDestination, ProfileId, SearchEngine, SpaceId, SyncObjectKind, SyncObjectPolicy, TabId, NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine, SpaceId, SyncObjectKind,
UrlText, SyncObjectPolicy, TabId, UrlText,
}; };
use gpui::{App, AppContext, Context, Entity, FocusHandle, Focusable, Subscription, Window}; use gpui::{App, AppContext, Context, Entity, FocusHandle, Focusable, Subscription, Window};
use gpui_component::input::{InputEvent, InputState, SelectAll}; use gpui_component::input::{InputEvent, InputState, SelectAll};
@@ -311,6 +311,19 @@ impl ElyShell {
} }
} }
fn set_profile_sync_policy(
&mut self,
profile_id: &ProfileId,
sync_policy: ProfileSyncPolicy,
cx: &mut Context<Self>,
) {
if let ShellState::Ready(core) = &mut self.state
&& core.set_profile_sync_policy(profile_id, sync_policy).is_ok()
{
cx.notify();
}
}
fn set_favorite_limit(&mut self, favorite_limit: FavoriteLimit, cx: &mut Context<Self>) { fn set_favorite_limit(&mut self, favorite_limit: FavoriteLimit, cx: &mut Context<Self>) {
if let ShellState::Ready(core) = &mut self.state { if let ShellState::Ready(core) = &mut self.state {
core.set_favorite_limit(favorite_limit); core.set_favorite_limit(favorite_limit);
+16 -1
View File
@@ -1,4 +1,4 @@
use ely_domain::{DownloadPolicy, Profile, ProfileId, ProfileKind, TabId}; use ely_domain::{DownloadPolicy, Profile, ProfileId, ProfileKind, ProfileSyncPolicy, TabId};
use crate::CoreError; use crate::CoreError;
@@ -89,4 +89,19 @@ impl BrowserCore {
let profile_id = self.active_profile_id.clone(); let profile_id = self.active_profile_id.clone();
self.set_profile_download_policy(&profile_id, download_policy) self.set_profile_download_policy(&profile_id, download_policy)
} }
pub fn set_profile_sync_policy(
&mut self,
profile_id: &ProfileId,
sync_policy: ProfileSyncPolicy,
) -> Result<(), CoreError> {
let profile = self
.profiles
.iter_mut()
.find(|profile| profile.id() == profile_id)
.ok_or_else(|| CoreError::ProfileNotFound { id: profile_id.clone() })?;
profile.set_sync_policy(sync_policy);
Ok(())
}
} }
+25 -1
View File
@@ -1,7 +1,7 @@
use std::error::Error; use std::error::Error;
use ely_browser_core::{BrowserCore, InitialBrowserConfig}; use ely_browser_core::{BrowserCore, InitialBrowserConfig};
use ely_domain::{CommandIntent, ProfileKind, UrlText}; use ely_domain::{CommandIntent, ProfileKind, ProfileSyncPolicy, UrlText};
#[test] #[test]
fn new_private_profile_command_creates_private_profile() -> Result<(), Box<dyn Error>> { fn new_private_profile_command_creates_private_profile() -> Result<(), Box<dyn Error>> {
@@ -45,3 +45,27 @@ fn private_profiles_do_not_record_history() -> Result<(), Box<dyn Error>> {
assert!(private_snapshot.history_entries.is_empty()); assert!(private_snapshot.history_entries.is_empty());
Ok(()) Ok(())
} }
#[test]
fn profile_sync_policy_can_pause_one_profile() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
let default_profile_id = core.snapshot()?.active_profile_id;
let research_profile_id = core.create_profile("Research", 0x9fc9a2, ProfileKind::Standard)?;
core.set_profile_sync_policy(&research_profile_id, ProfileSyncPolicy::Paused)?;
let snapshot = core.snapshot()?;
let Some(default_profile) =
snapshot.profiles.iter().find(|profile| profile.id() == &default_profile_id)
else {
return Err("missing default profile".into());
};
let Some(research_profile) =
snapshot.profiles.iter().find(|profile| profile.id() == &research_profile_id)
else {
return Err("missing research profile".into());
};
assert_eq!(default_profile.sync_policy(), ProfileSyncPolicy::Enabled);
assert_eq!(research_profile.sync_policy(), ProfileSyncPolicy::Paused);
Ok(())
}
+1 -1
View File
@@ -38,7 +38,7 @@ pub use plugin::{
PluginSignature, PluginSignatureAlgorithm, PluginSignature, PluginSignatureAlgorithm,
}; };
pub use privacy::HistoryRecordingPolicy; pub use privacy::HistoryRecordingPolicy;
pub use profile::{Profile, ProfileKind}; pub use profile::{Profile, ProfileKind, ProfileSyncPolicy};
pub use reading_list::{ReadingListEntry, ReadingProgress}; pub use reading_list::{ReadingListEntry, ReadingProgress};
pub use search::SearchEngine; pub use search::SearchEngine;
pub use site_permission::{ pub use site_permission::{
+44
View File
@@ -6,6 +6,39 @@ pub enum ProfileKind {
Private, Private,
} }
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ProfileSyncPolicy {
#[default]
Enabled,
Paused,
}
impl ProfileSyncPolicy {
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Enabled => "Sync on",
Self::Paused => "Sync paused",
}
}
#[must_use]
pub fn action_label(self) -> &'static str {
match self {
Self::Enabled => "Pause Sync",
Self::Paused => "Resume Sync",
}
}
#[must_use]
pub fn toggled(self) -> Self {
match self {
Self::Enabled => Self::Paused,
Self::Paused => Self::Enabled,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Profile { pub struct Profile {
id: ProfileId, id: ProfileId,
@@ -13,6 +46,7 @@ pub struct Profile {
color_hex: u32, color_hex: u32,
kind: ProfileKind, kind: ProfileKind,
download_policy: DownloadPolicy, download_policy: DownloadPolicy,
sync_policy: ProfileSyncPolicy,
} }
impl Profile { impl Profile {
@@ -24,6 +58,7 @@ impl Profile {
color_hex, color_hex,
kind, kind,
download_policy: DownloadPolicy::ask_every_time(), download_policy: DownloadPolicy::ask_every_time(),
sync_policy: ProfileSyncPolicy::default(),
} }
} }
@@ -52,7 +87,16 @@ impl Profile {
&self.download_policy &self.download_policy
} }
#[must_use]
pub fn sync_policy(&self) -> ProfileSyncPolicy {
self.sync_policy
}
pub fn set_download_policy(&mut self, download_policy: DownloadPolicy) { pub fn set_download_policy(&mut self, download_policy: DownloadPolicy) {
self.download_policy = download_policy; self.download_policy = download_policy;
} }
pub fn set_sync_policy(&mut self, sync_policy: ProfileSyncPolicy) {
self.sync_policy = sync_policy;
}
} }