Significant progress. Performing cleanup now
This commit is contained in:
@@ -43,17 +43,15 @@ use crate::ui_components::buttons::icon_button;
|
||||
use crate::view_components::{Dropdown, DropdownItem};
|
||||
use crate::{
|
||||
appearance::Appearance,
|
||||
auth::auth_manager::AuthManager,
|
||||
channel::ChannelState,
|
||||
report_if_error, send_telemetry_from_ctx,
|
||||
server::telemetry::TelemetryEvent,
|
||||
settings::{AISettings, PrivacySettings},
|
||||
terminal::safe_mode_settings::{SafeModeEnabled, SafeModeSettings},
|
||||
ui_components::icons::Icon,
|
||||
util::links::PRIVACY_POLICY_URL,
|
||||
workspaces::{
|
||||
user_workspaces::UserWorkspaces,
|
||||
workspace::{AdminEnablementSetting, CustomerType, UgcCollectionEnablementSetting},
|
||||
workspace::{CustomerType, UgcCollectionEnablementSetting},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -97,25 +95,7 @@ const TELEMETRY_FREE_TIER_NOTE: &str =
|
||||
const TELEMETRY_DOCS_URL: &str =
|
||||
"https://docs.warp.dev/support-and-community/privacy-and-security/privacy#what-telemetry-data-does-warp-collect-and-why";
|
||||
|
||||
const DATA_MANAGEMENT_TITLE: &str = "Manage your data";
|
||||
const DATA_MANAGEMENT_DESCRIPTION: &str =
|
||||
"At any time, you may choose to delete your Warp account permanently. \
|
||||
You will no longer be able to use Warp.";
|
||||
const DATA_MANAGEMENT_LINK_TEXT: &str = "Visit the data management page";
|
||||
|
||||
const PRIVACY_POLICY_TITLE: &str = "Privacy policy";
|
||||
const PRIVACY_POLICY_LINK_TEXT: &str = "Read Warp's privacy policy";
|
||||
|
||||
pub fn data_management_url(custom_token: Option<&str>) -> String {
|
||||
match custom_token {
|
||||
Some(token) => format!(
|
||||
"{}/data_management?customToken={}",
|
||||
ChannelState::server_root_url(),
|
||||
token
|
||||
),
|
||||
None => format!("{}/data_management", ChannelState::server_root_url(),),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrivacyPageView {
|
||||
page: PageType<Self>,
|
||||
@@ -241,13 +221,10 @@ impl PrivacyPageView {
|
||||
Box::new(SecretRedactionWidget::default()),
|
||||
Box::new(AppAnalyticsWidget::default()),
|
||||
Box::new(CrashReportsWidget::default()),
|
||||
Box::new(CloudConversationStorageWidget::default()),
|
||||
];
|
||||
if ContextFlag::NetworkLogConsole.is_enabled() {
|
||||
widgets.push(Box::new(NetworkLogWidget::default()));
|
||||
}
|
||||
widgets.push(Box::new(DataManagementWidget::default()));
|
||||
widgets.push(Box::new(PrivacyPolicyWidget::default()));
|
||||
PageType::new_uncategorized(widgets, Some("Privacy"))
|
||||
}
|
||||
|
||||
@@ -327,17 +304,6 @@ impl PrivacyPageView {
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
fn toggle_cloud_conversation_storage(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
let privacy_settings_handle = PrivacySettings::handle(ctx);
|
||||
let old_value = privacy_settings_handle
|
||||
.as_ref(ctx)
|
||||
.is_cloud_conversation_storage_enabled;
|
||||
ctx.update_model(&privacy_settings_handle, |privacy_settings, ctx| {
|
||||
privacy_settings.set_is_cloud_conversation_storage_enabled(!old_value, ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
fn queue_regex_removal(&mut self, idx: usize, ctx: &mut ViewContext<Self>) {
|
||||
// Check if this removal is already pending
|
||||
if self.pending_regex_removals.contains(&idx) {
|
||||
@@ -503,10 +469,8 @@ pub enum PrivacyPageAction {
|
||||
SetSecretDisplayMode(SecretDisplayMode),
|
||||
ToggleTelemetry,
|
||||
ToggleCrashReporting,
|
||||
ToggleCloudConversationStorage,
|
||||
LaunchNetworkLogging,
|
||||
RemoveCustomRegex(usize),
|
||||
OpenDataManagementWebpage,
|
||||
AddAllRecommendedRegexes,
|
||||
ShowAddRegexModal,
|
||||
AddRecommendedRegex(usize),
|
||||
@@ -584,19 +548,10 @@ impl TypedActionView for PrivacyPageView {
|
||||
}
|
||||
PrivacyPageAction::ToggleTelemetry => self.toggle_telemetry(ctx),
|
||||
PrivacyPageAction::ToggleCrashReporting => self.toggle_crash_reporting(ctx),
|
||||
PrivacyPageAction::ToggleCloudConversationStorage => {
|
||||
self.toggle_cloud_conversation_storage(ctx)
|
||||
}
|
||||
PrivacyPageAction::LaunchNetworkLogging => self.launch_network_logging(ctx),
|
||||
PrivacyPageAction::RemoveCustomRegex(idx) => {
|
||||
self.queue_regex_removal(*idx, ctx);
|
||||
}
|
||||
PrivacyPageAction::OpenDataManagementWebpage => {
|
||||
AuthManager::handle(ctx).update(ctx, |auth_manager, ctx| {
|
||||
auth_manager
|
||||
.open_url_maybe_with_anonymous_token(ctx, Box::new(data_management_url));
|
||||
});
|
||||
}
|
||||
PrivacyPageAction::AddAllRecommendedRegexes => {
|
||||
// First process any pending removals
|
||||
if !self.pending_regex_removals.is_empty() {
|
||||
@@ -1673,120 +1628,6 @@ impl SettingsWidget for CrashReportsWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CloudConversationStorageWidget {
|
||||
switch_state: SwitchStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for CloudConversationStorageWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"sync cloud conversation store storage ai agent"
|
||||
}
|
||||
|
||||
fn should_render(&self, app: &AppContext) -> bool {
|
||||
if !FeatureFlag::CloudConversations.is_enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide the toggle entirely when AI is disabled: the setting has no
|
||||
// effect without AI (no agent conversations are produced), so showing
|
||||
// it is confusing.
|
||||
if !AISettings::as_ref(app).is_any_ai_enabled(app) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let privacy_settings = PrivacySettings::as_ref(app);
|
||||
!privacy_settings.is_telemetry_force_enabled()
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ui_builder = appearance.ui_builder();
|
||||
let privacy_settings = PrivacySettings::as_ref(app);
|
||||
let org_setting =
|
||||
UserWorkspaces::as_ref(app).get_cloud_conversation_storage_enablement_setting();
|
||||
|
||||
let (toggle_state, is_checked) = match org_setting {
|
||||
AdminEnablementSetting::Enable => (ToggleState::Disabled, true),
|
||||
AdminEnablementSetting::Disable => (ToggleState::Disabled, false),
|
||||
AdminEnablementSetting::RespectUserSetting => (
|
||||
ToggleState::Enabled,
|
||||
privacy_settings.is_cloud_conversation_storage_enabled,
|
||||
),
|
||||
};
|
||||
|
||||
let switch = ui_builder
|
||||
.switch(self.switch_state.clone())
|
||||
.check(is_checked);
|
||||
let switch = if matches!(toggle_state, ToggleState::Enabled) {
|
||||
switch
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(PrivacyPageAction::ToggleCloudConversationStorage)
|
||||
})
|
||||
.finish()
|
||||
} else {
|
||||
switch
|
||||
.with_tooltip(TooltipConfig {
|
||||
text: "This setting is managed by your organization.".to_string(),
|
||||
styles: ui_builder.default_tool_tip_styles(),
|
||||
})
|
||||
.disable()
|
||||
.build()
|
||||
.finish()
|
||||
};
|
||||
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
"Store AI conversations in the cloud".into(),
|
||||
None,
|
||||
LocalOnlyIconState::Hidden,
|
||||
toggle_state,
|
||||
appearance,
|
||||
switch,
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
ui_builder
|
||||
.paragraph(
|
||||
if is_checked {
|
||||
"Agent conversations can be shared with others and are retained \
|
||||
when you log in on different devices. This data is only stored \
|
||||
for product functionality, and Warp will not use it for analytics."
|
||||
} else {
|
||||
"Agent conversations are only stored locally on your machine, are \
|
||||
lost upon logout, and cannot be shared. Note: conversation data \
|
||||
for ambient agents are still stored in the cloud."
|
||||
}
|
||||
.to_owned(),
|
||||
)
|
||||
.with_style(UiComponentStyles {
|
||||
font_color: Some(
|
||||
appearance
|
||||
.theme()
|
||||
.sub_text_color(appearance.theme().surface_2())
|
||||
.into_solid(),
|
||||
),
|
||||
margin: Some(
|
||||
Coords::default()
|
||||
.top(styles::DESCRIPTION_NEGATIVE_MARGIN_OFFSET)
|
||||
.bottom(styles::DESCRIPTION_MARGIN_BOTTOM),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct NetworkLogWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
@@ -1865,133 +1706,6 @@ impl SettingsWidget for NetworkLogWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DataManagementWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for DataManagementWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"data management delete account"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
_app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ui_builder = appearance.ui_builder();
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
DATA_MANAGEMENT_TITLE.into(),
|
||||
None,
|
||||
// Not rendering a setting, so no need to show local only icon state.
|
||||
LocalOnlyIconState::Hidden,
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
Empty::new().finish(),
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
ui_builder
|
||||
.paragraph(DATA_MANAGEMENT_DESCRIPTION)
|
||||
.with_style(UiComponentStyles {
|
||||
font_color: Some(
|
||||
appearance
|
||||
.theme()
|
||||
.sub_text_color(appearance.theme().surface_2())
|
||||
.into_solid(),
|
||||
),
|
||||
margin: Some(
|
||||
Coords::default()
|
||||
.top(styles::DESCRIPTION_NEGATIVE_MARGIN_OFFSET)
|
||||
.bottom(styles::DESCRIPTION_LINE_MARGIN_BOTTOM),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.finish(),
|
||||
)
|
||||
.with_child(
|
||||
Align::new(
|
||||
appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
DATA_MANAGEMENT_LINK_TEXT.into(),
|
||||
None,
|
||||
Some(Box::new(|ctx| {
|
||||
ctx.dispatch_typed_action(
|
||||
PrivacyPageAction::OpenDataManagementWebpage,
|
||||
);
|
||||
})),
|
||||
self.link_mouse_state.clone(),
|
||||
)
|
||||
.soft_wrap(false)
|
||||
.build()
|
||||
.with_margin_bottom(styles::DESCRIPTION_MARGIN_BOTTOM)
|
||||
.finish(),
|
||||
)
|
||||
.left()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrivacyPolicyWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for PrivacyPolicyWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"privacy policy terms"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
_app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
PRIVACY_POLICY_TITLE.into(),
|
||||
None,
|
||||
// Not rendering a setting, so no need to show local only icon state.
|
||||
LocalOnlyIconState::Hidden,
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
Empty::new().finish(),
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
Align::new(
|
||||
appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
PRIVACY_POLICY_LINK_TEXT.into(),
|
||||
Some(PRIVACY_POLICY_URL.into()),
|
||||
None,
|
||||
self.link_mouse_state.clone(),
|
||||
)
|
||||
.soft_wrap(false)
|
||||
.build()
|
||||
.with_margin_bottom(styles::DESCRIPTION_MARGIN_BOTTOM)
|
||||
.finish(),
|
||||
)
|
||||
.left()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_actions_from_parent_view<T: Action + Clone>(
|
||||
app: &mut AppContext,
|
||||
context: &ContextPredicate,
|
||||
|
||||
Reference in New Issue
Block a user