use std::borrow::Cow; use pathfinder_color::ColorU; use warpui::{ elements::{ Align, Border, ConstrainedBox, Container, CornerRadius, Flex, ParentElement, Shrinkable, }, ui_components::{ components::{UiComponent, UiComponentStyles}, text::Span, }, AppContext, Element, SingletonEntity as _, }; use crate::{ appearance::Appearance, modal::MODAL_CORNER_RADIUS, root_view::unthemed_window_border, themes::theme::{Blend, Fill}, }; /// A full-window login error. /// /// This is used for uncommon login error states, such as: /// * A user needing to link SSO after logging in with an incorrect Firebase provider. /// * An error importing the user from a host web application. pub struct LoginErrorModal { modal_styles: UiComponentStyles, header_styles: UiComponentStyles, header: Option>, detail_styles: UiComponentStyles, detail: Option>, action: Option>, window_corner_radius: CornerRadius, } impl LoginErrorModal { pub fn new(app: &AppContext) -> Self { let appearance = Appearance::as_ref(app); let modal_styles = UiComponentStyles { width: Some(480.), height: Some(280.), border_color: Some(Fill::black().blend(&Fill::white().with_opacity(15)).into()), border_width: Some(1.), ..Default::default() }; let header_styles = UiComponentStyles { font_family_id: Some(appearance.ui_font_family()), font_size: Some(appearance.header_font_size()), ..Default::default() }; let detail_styles = UiComponentStyles { font_family_id: Some(appearance.ui_font_family()), font_size: Some(appearance.ui_font_size()), font_color: Some(appearance.theme().nonactive_ui_text_color().into()), ..Default::default() }; LoginErrorModal { modal_styles, header_styles, detail_styles, window_corner_radius: app.windows().window_corner_radius(), header: None, detail: None, action: None, } } pub fn with_header(mut self, header: impl Into>) -> Self { self.header = Some(header.into()); self } pub fn with_detail(mut self, detail: impl Into>) -> Self { self.detail = Some(detail.into()); self } pub fn with_action(mut self, action: Box) -> Self { self.action = Some(action); self } } impl UiComponent for LoginErrorModal { type ElementType = Container; fn build(self) -> Self::ElementType { let mut contents = Flex::column(); if let Some(header) = self.header { contents.add_child( Shrinkable::new( 1., Align::new(Span::new(header, self.header_styles).build().finish()).finish(), ) .finish(), ); } if let Some(detail) = self.detail { contents.add_child( Shrinkable::new( 1., Align::new(Span::new(detail, self.detail_styles).build().finish()).finish(), ) .finish(), ); } if let Some(action) = self.action { contents.add_child(Shrinkable::new(1., Align::new(action).finish()).finish()); } let modal = Container::new( ConstrainedBox::new(contents.finish()) .with_width(self.modal_styles.width.unwrap_or_default()) .with_height(self.modal_styles.height.unwrap_or_default()) .finish(), ) .with_border( Border::all(self.modal_styles.border_width.unwrap_or_default()) .with_border_fill(self.modal_styles.border_color.unwrap_or_default()), ) .with_corner_radius(CornerRadius::with_all(MODAL_CORNER_RADIUS)) .finish(); Container::new(Align::new(modal).finish()) .with_background_color(ColorU::black()) .with_corner_radius(self.window_corner_radius) .with_border(unthemed_window_border()) } fn with_style(mut self, style: UiComponentStyles) -> Self { self.modal_styles = self.modal_styles.merge(style); self.header_styles = self.header_styles.merge(style); self.detail_styles = self.detail_styles.merge(style); self } }