Files
galaxy/app/src/ai/blocklist/telemetry_banner.rs
T

211 lines
8.0 KiB
Rust

use crate::{
settings_view::SettingsSection,
terminal::view::TerminalAction,
ui_components::{buttons::icon_button, icons::Icon},
workspaces::{user_workspaces::UserWorkspaces, workspace::UgcCollectionEnablementSetting},
Appearance, FeatureFlag, WorkspaceAction,
};
use galaxyui::{
elements::{
ConstrainedBox, Container, CrossAxisAlignment, Flex, MainAxisAlignment, MainAxisSize,
MouseStateHandle, ParentElement, Shrinkable, Text,
},
platform::Cursor,
ui_components::{
button::ButtonVariant,
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, SingletonEntity, View, ViewContext,
};
const TITLE_EXISTING_USERS: &str = "We've updated our telemetry policy.";
const TITLE_NEW_USERS: &str = "Help improve Warp.";
const DESCRIPTION: &str = "We may collect certain console interactions to improve Warp's AI capabilities. You can opt out any time.";
const PRIVACY_URL: &str = "https://warp.dev/privacy";
#[derive(Default, Debug, Clone)]
pub struct TelemetryBanner {
pub is_onboarded: bool,
pub learn_more_mouse_state: MouseStateHandle,
pub privacy_settings_mouse_state: MouseStateHandle,
pub close_button_mouse_state: MouseStateHandle,
}
impl TelemetryBanner {
pub fn new(is_onboarded: bool, _ctx: &mut ViewContext<Self>) -> Self {
Self {
is_onboarded,
learn_more_mouse_state: Default::default(),
privacy_settings_mouse_state: Default::default(),
close_button_mouse_state: Default::default(),
}
}
}
impl View for TelemetryBanner {
fn ui_name() -> &'static str {
"TelemetryBanner"
}
fn render(&self, app: &AppContext) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let ui_builder = appearance.ui_builder();
let title = if self.is_onboarded {
TITLE_EXISTING_USERS
} else {
TITLE_NEW_USERS
};
let left = Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Start)
.with_child(
Container::new(
ConstrainedBox::new(
Icon::Info
.to_galaxyui_icon(theme.active_ui_text_color())
.finish(),
)
.with_height(20.)
.with_width(20.)
.finish(),
)
.with_margin_right(8.)
.finish(),
)
.with_child(
Shrinkable::new(
1.,
Flex::column()
.with_main_axis_alignment(MainAxisAlignment::Start)
.with_child(
Text::new(title, ui_builder.ui_font_family(), 14.)
.with_color(theme.active_ui_text_color().into_solid())
.finish(),
)
.with_child(
Text::new(DESCRIPTION, ui_builder.ui_font_family(), 12.)
.with_color(theme.nonactive_ui_text_color().into_solid())
.soft_wrap(true)
.finish(),
)
.finish(),
)
.finish(),
)
.finish();
let right = Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(
Container::new(
ui_builder
.button(ButtonVariant::Text, self.learn_more_mouse_state.clone())
.with_text_label("Learn more".into())
.with_style(UiComponentStyles {
height: Some(24.),
padding: Some(Coords {
left: 8.,
right: 8.,
..Default::default()
}),
..Default::default()
})
.build()
.on_click(|ctx, _, _| {
ctx.dispatch_typed_action(WorkspaceAction::OpenLink(
PRIVACY_URL.to_string(),
));
ctx.dispatch_typed_action(
TerminalAction::HideTelemetryBannerPermanently,
);
})
.finish(),
)
.with_margin_right(4.)
.finish(),
)
.with_child(
Container::new(
ui_builder
.button(
ButtonVariant::Outlined,
self.privacy_settings_mouse_state.clone(),
)
.with_text_label("Manage privacy settings".into())
.with_style(UiComponentStyles {
..Default::default()
})
.build()
.on_click(|ctx, _, _| {
ctx.dispatch_typed_action(WorkspaceAction::ShowSettingsPage(
SettingsSection::Privacy,
));
ctx.dispatch_typed_action(
TerminalAction::HideTelemetryBannerPermanently,
);
})
.finish(),
)
.with_margin_left(4.)
.with_margin_right(12.)
.finish(),
)
.with_child(
icon_button(
appearance,
Icon::X,
false,
self.close_button_mouse_state.clone(),
)
.build()
.on_click(|ctx, _, _| {
ctx.dispatch_typed_action(TerminalAction::HideTelemetryBannerPermanently);
})
.with_cursor(Cursor::PointingHand)
.finish(),
)
.finish();
Container::new(
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_main_axis_size(MainAxisSize::Max)
.with_child(Shrinkable::new(1., left).finish())
.with_child(right)
.finish(),
)
.with_background(theme.surface_1())
.with_uniform_padding(12.)
.finish()
}
}
impl Entity for TelemetryBanner {
type Event = ();
}
/// Returns `true` if we should collect UGC (user-generated content) telemetry for AI features.
///
/// This should apply to telemetry events that include user-generated content, like queries or
/// outputs, but need not be checked for regular metadata telemetry events.
///
/// For example, a metadata event that records if a user toggled Pair/Dispatch mode does not
/// require this check, but an event that logs the input buffer for natural language detection
/// _does_ need to check this.
pub fn should_collect_ai_ugc_telemetry(app: &AppContext, is_telemetry_enabled: bool) -> bool {
match UserWorkspaces::as_ref(app).get_ugc_collection_enablement_setting() {
UgcCollectionEnablementSetting::Disable => false,
UgcCollectionEnablementSetting::Enable => true,
UgcCollectionEnablementSetting::RespectUserSetting => {
(FeatureFlag::GlobalAIAnalyticsCollection.is_enabled()
// Do NOT remove this check. Unlike the send telemetry macro,
// UploadBlock endpoint does not automatically check user's telemetry setting.
&& is_telemetry_enabled)
|| FeatureFlag::AgentModeAnalytics.is_enabled()
}
}
}