mirror of
https://github.com/librekeys/picoforge.git
synced 2026-07-28 08:01:19 -07:00
Merge pull request #81 from librekeys/refactor/ui-data-structure
refactor: UI data layout
This commit is contained in:
+13
-5
@@ -58,20 +58,28 @@ pub fn logger_init() {
|
||||
)))
|
||||
.build();
|
||||
|
||||
let (app_level, root_level) = if cfg!(debug_assertions) {
|
||||
(LevelFilter::Trace, LevelFilter::Debug)
|
||||
let app_level = if cfg!(debug_assertions) {
|
||||
LevelFilter::Trace
|
||||
} else {
|
||||
(LevelFilter::Info, LevelFilter::Error)
|
||||
LevelFilter::Info
|
||||
};
|
||||
|
||||
let config = log4rs::Config::builder()
|
||||
.appender(Appender::builder().build("stdout", Box::new(stdout)))
|
||||
.appender(Appender::builder().build("logfile", Box::new(logfile)))
|
||||
.logger(Logger::builder().build("picoforge", app_level))
|
||||
.logger(
|
||||
Logger::builder()
|
||||
.appenders(["stdout", "logfile"])
|
||||
.additive(false)
|
||||
.build("picoforge", app_level),
|
||||
)
|
||||
.logger(Logger::builder().build("gpui", LevelFilter::Error))
|
||||
.logger(Logger::builder().build("gpui_component", LevelFilter::Error))
|
||||
.logger(Logger::builder().build("blade_graphics", LevelFilter::Error))
|
||||
.build(
|
||||
Root::builder()
|
||||
.appenders(vec!["logfile", "stdout"])
|
||||
.build(root_level),
|
||||
.build(LevelFilter::Error),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::device::types::DeviceMethod;
|
||||
use crate::ui::components::button::PFIconButton;
|
||||
use crate::ui::types::{ActiveView, GlobalDeviceState};
|
||||
use crate::ui::types::{ActiveView, DeviceConnectionState};
|
||||
use gpui::*;
|
||||
use gpui_component::{
|
||||
ActiveTheme, Icon, IconName, Side,
|
||||
@@ -18,7 +18,7 @@ pub struct AppSidebar<V: 'static> {
|
||||
active_view: ActiveView,
|
||||
width: Pixels,
|
||||
collapsed: bool,
|
||||
state: GlobalDeviceState,
|
||||
state: DeviceConnectionState,
|
||||
on_select: SelectHandler<V>,
|
||||
on_refresh: RefreshHandler<V>,
|
||||
}
|
||||
@@ -28,7 +28,7 @@ impl<V: 'static> AppSidebar<V> {
|
||||
active_view: ActiveView,
|
||||
width: Pixels,
|
||||
collapsed: bool,
|
||||
state: GlobalDeviceState,
|
||||
state: DeviceConnectionState,
|
||||
) -> Self {
|
||||
Self {
|
||||
active_view,
|
||||
@@ -191,7 +191,7 @@ impl<V: 'static> AppSidebar<V> {
|
||||
.w_full(),
|
||||
)
|
||||
.child(div().w(px(8.)).h(px(8.)).rounded_full().bg(
|
||||
if let Some(status) = &state.device_status {
|
||||
if let Some(status) = &state.status {
|
||||
if status.method == DeviceMethod::Fido {
|
||||
rgb(0xf59e0b)
|
||||
} else {
|
||||
@@ -220,7 +220,7 @@ impl<V: 'static> AppSidebar<V> {
|
||||
)
|
||||
.child({
|
||||
let (text, color_bg, color_text) =
|
||||
if let Some(status) = &state.device_status {
|
||||
if let Some(status) = &state.status {
|
||||
if status.method == DeviceMethod::Fido {
|
||||
("Online - Fido", rgb(0xf59e0b), rgb(0xffffff))
|
||||
} else {
|
||||
|
||||
+60
-69
@@ -1,11 +1,10 @@
|
||||
use crate::device::io;
|
||||
use crate::ui::components::sidebar::AppSidebar;
|
||||
use crate::ui::types::{ActiveView, GlobalDeviceState};
|
||||
use crate::ui::types::{ActiveView, DeviceConnectionState, LayoutState, ViewCache};
|
||||
use crate::ui::views::{
|
||||
about::AboutView, config::ConfigView, home::HomeView, passkeys::PasskeysEvent,
|
||||
passkeys::PasskeysView, security::SecurityView,
|
||||
};
|
||||
|
||||
use gpui::prelude::*;
|
||||
use gpui::*;
|
||||
use gpui_component::Root;
|
||||
@@ -16,31 +15,21 @@ use gpui_component::{
|
||||
gpui::actions!(picoforge, [ToggleSidebar]);
|
||||
|
||||
pub struct ApplicationRoot {
|
||||
active_view: ActiveView,
|
||||
is_sidebar_collapsed: bool,
|
||||
sidebar_toggle_hovered: bool,
|
||||
state: GlobalDeviceState,
|
||||
device_loading: bool,
|
||||
// Umm, why did my past self do this? This does not belong here.
|
||||
sidebar_width: Pixels,
|
||||
config_view: Option<Entity<ConfigView>>,
|
||||
passkeys_view: Option<Entity<PasskeysView>>,
|
||||
focus_handle: FocusHandle,
|
||||
pub device: DeviceConnectionState,
|
||||
pub layout: LayoutState,
|
||||
pub views: ViewCache,
|
||||
pub focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl ApplicationRoot {
|
||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||
let mut this = Self {
|
||||
active_view: ActiveView::Home,
|
||||
is_sidebar_collapsed: false,
|
||||
sidebar_toggle_hovered: false,
|
||||
state: GlobalDeviceState::new(),
|
||||
device_loading: false,
|
||||
sidebar_width: px(255.),
|
||||
config_view: None,
|
||||
passkeys_view: None,
|
||||
device: DeviceConnectionState::new(),
|
||||
layout: LayoutState::new(),
|
||||
views: ViewCache::new(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
};
|
||||
|
||||
this.refresh_device_status(None, cx);
|
||||
this
|
||||
}
|
||||
@@ -50,56 +39,60 @@ impl ApplicationRoot {
|
||||
}
|
||||
|
||||
fn refresh_device_status(&mut self, window: Option<&mut Window>, cx: &mut Context<Self>) {
|
||||
if self.device_loading {
|
||||
if self.device.loading {
|
||||
return;
|
||||
}
|
||||
|
||||
self.device_loading = true;
|
||||
self.state.error = None;
|
||||
self.device.loading = true;
|
||||
self.device.error = None;
|
||||
cx.notify();
|
||||
|
||||
match io::read_device_details() {
|
||||
Ok(status) => {
|
||||
self.state.device_status = Some(status.clone());
|
||||
self.state.error = None;
|
||||
let device_changed = self
|
||||
.device
|
||||
.status
|
||||
.as_ref()
|
||||
.map(|s| s.info.serial != status.info.serial)
|
||||
.unwrap_or(true);
|
||||
|
||||
self.device.status = Some(status);
|
||||
self.device.error = None;
|
||||
|
||||
if device_changed {
|
||||
self.views.passkeys = None;
|
||||
}
|
||||
|
||||
match io::get_fido_info() {
|
||||
Ok(fido) => {
|
||||
self.state.fido_info = Some(fido);
|
||||
self.device.fido_info = Some(fido);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("FIDO Info fetch failed: {}", e);
|
||||
self.state.fido_info = None;
|
||||
self.device.fido_info = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(config_view) = &self.config_view
|
||||
if let Some(config_view) = &self.views.config
|
||||
&& let Some(window) = window
|
||||
{
|
||||
let device = self.device.clone();
|
||||
config_view.update(cx, |view, cx| {
|
||||
view.update_device_status(Some(status.clone()), window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(passkeys_view) = &self.passkeys_view {
|
||||
let fido = self.state.fido_info.clone();
|
||||
passkeys_view.update(cx, |view, cx| {
|
||||
view.update_device_status(Some(status.clone()), fido, cx);
|
||||
view.sync_from_device(&device, window, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.state.device_status = None;
|
||||
self.state.error = Some(format!("{}", e));
|
||||
self.state.fido_info = None;
|
||||
self.device.status = None;
|
||||
self.device.error = Some(format!("{}", e));
|
||||
self.device.fido_info = None;
|
||||
}
|
||||
}
|
||||
self.device_loading = false;
|
||||
self.device.loading = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn toggle_sidebar(&mut self, cx: &mut Context<Self>) {
|
||||
self.is_sidebar_collapsed = !self.is_sidebar_collapsed;
|
||||
self.layout.is_sidebar_collapsed = !self.layout.is_sidebar_collapsed;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
@@ -108,7 +101,7 @@ impl Render for ApplicationRoot {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let window_width = window.bounds().size.width;
|
||||
let is_window_wide = window_width > px(800.0);
|
||||
let is_sidebar_collapsed = self.is_sidebar_collapsed || !is_window_wide;
|
||||
let is_sidebar_collapsed = self.layout.is_sidebar_collapsed || !is_window_wide;
|
||||
|
||||
let target_width = if is_sidebar_collapsed {
|
||||
px(48.)
|
||||
@@ -116,11 +109,12 @@ impl Render for ApplicationRoot {
|
||||
px(255.)
|
||||
};
|
||||
|
||||
if (self.sidebar_width - target_width).abs() > px(0.1) {
|
||||
self.sidebar_width = self.sidebar_width + (target_width - self.sidebar_width) * 0.2;
|
||||
if (self.layout.sidebar_width - target_width).abs() > px(0.1) {
|
||||
self.layout.sidebar_width =
|
||||
self.layout.sidebar_width + (target_width - self.layout.sidebar_width) * 0.2;
|
||||
window.request_animation_frame();
|
||||
} else {
|
||||
self.sidebar_width = target_width;
|
||||
self.layout.sidebar_width = target_width;
|
||||
}
|
||||
|
||||
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||
@@ -146,21 +140,15 @@ impl Render for ApplicationRoot {
|
||||
.overflow_y_scrollbar()
|
||||
.flex_grow()
|
||||
.bg(cx.theme().background)
|
||||
.child(match self.active_view {
|
||||
.child(match self.layout.active_view {
|
||||
ActiveView::Home => {
|
||||
HomeView::build(&self.state, cx.theme(), window.bounds().size.width)
|
||||
HomeView::build(&self.device, cx.theme(), window.bounds().size.width)
|
||||
.into_any_element()
|
||||
}
|
||||
ActiveView::Passkeys => {
|
||||
let view = self.passkeys_view.get_or_insert_with(|| {
|
||||
let view = cx.new(|cx| {
|
||||
PasskeysView::new(
|
||||
window,
|
||||
cx,
|
||||
self.state.device_status.clone(),
|
||||
self.state.fido_info.clone(),
|
||||
)
|
||||
});
|
||||
let view = self.views.passkeys.get_or_insert_with(|| {
|
||||
let root = cx.entity().downgrade();
|
||||
let view = cx.new(|cx| PasskeysView::new(window, cx, root));
|
||||
cx.subscribe_in(
|
||||
&view,
|
||||
window,
|
||||
@@ -176,23 +164,26 @@ impl Render for ApplicationRoot {
|
||||
view.clone().into_any_element()
|
||||
}
|
||||
ActiveView::Configuration => {
|
||||
let view = self.config_view.get_or_insert_with(|| {
|
||||
cx.new(|cx| ConfigView::new(window, cx, self.state.device_status.clone()))
|
||||
});
|
||||
view.clone().into_any_element()
|
||||
if self.views.config.is_none() {
|
||||
let root = cx.entity().downgrade();
|
||||
let device = self.device.clone();
|
||||
self.views.config =
|
||||
Some(cx.new(|cx| ConfigView::new(window, cx, root, device)));
|
||||
}
|
||||
self.views.config.clone().unwrap().into_any_element()
|
||||
}
|
||||
ActiveView::Security => SecurityView::build(cx).into_any_element(),
|
||||
ActiveView::About => AboutView::build(cx.theme()).into_any_element(),
|
||||
});
|
||||
|
||||
let sidebar = AppSidebar::new(
|
||||
self.active_view,
|
||||
self.sidebar_width,
|
||||
self.layout.active_view,
|
||||
self.layout.sidebar_width,
|
||||
is_sidebar_collapsed,
|
||||
self.state.clone(),
|
||||
self.device.clone(),
|
||||
)
|
||||
.on_select(|this: &mut Self, view, _, _| {
|
||||
this.active_view = view;
|
||||
this.layout.active_view = view;
|
||||
})
|
||||
.on_refresh(|this, window, cx| {
|
||||
this.refresh_device_status(Some(window), cx);
|
||||
@@ -203,8 +194,8 @@ impl Render for ApplicationRoot {
|
||||
let sidebar_bg = cx.theme().sidebar;
|
||||
let border_color = cx.theme().sidebar_border;
|
||||
let sidebar_fg = cx.theme().sidebar_foreground;
|
||||
let is_toggle_visible = !is_sidebar_collapsed || self.sidebar_toggle_hovered;
|
||||
let sidebar_width = self.sidebar_width;
|
||||
let is_toggle_visible = !is_sidebar_collapsed || self.layout.sidebar_toggle_hovered;
|
||||
let sidebar_width = self.layout.sidebar_width;
|
||||
let toggle_icon = if is_sidebar_collapsed {
|
||||
"icons/chevron-right.svg"
|
||||
} else {
|
||||
@@ -226,7 +217,7 @@ impl Render for ApplicationRoot {
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.on_hover(cx.listener(|this, hovered, _, cx| {
|
||||
this.sidebar_toggle_hovered = *hovered;
|
||||
this.layout.sidebar_toggle_hovered = *hovered;
|
||||
cx.notify();
|
||||
}))
|
||||
.child(
|
||||
@@ -249,7 +240,7 @@ impl Render for ApplicationRoot {
|
||||
.build(window, cx)
|
||||
})
|
||||
.on_click(cx.listener(|this, _, _, _| {
|
||||
this.is_sidebar_collapsed = !this.is_sidebar_collapsed;
|
||||
this.layout.is_sidebar_collapsed = !this.layout.is_sidebar_collapsed;
|
||||
}))
|
||||
.child(Icon::default().path(toggle_icon).text_color(sidebar_fg)),
|
||||
);
|
||||
|
||||
+44
-7
@@ -1,6 +1,8 @@
|
||||
use gpui::SharedString;
|
||||
|
||||
use crate::device::types::{FidoDeviceInfo, FullDeviceStatus};
|
||||
use crate::{
|
||||
device::types::{FidoDeviceInfo, FullDeviceStatus},
|
||||
ui::views::{config::ConfigView, passkeys::PasskeysView},
|
||||
};
|
||||
use gpui::{Entity, Pixels, SharedString, px};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum ActiveView {
|
||||
@@ -12,18 +14,53 @@ pub enum ActiveView {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct GlobalDeviceState {
|
||||
pub device_status: Option<FullDeviceStatus>,
|
||||
pub struct DeviceConnectionState {
|
||||
pub status: Option<FullDeviceStatus>,
|
||||
pub fido_info: Option<FidoDeviceInfo>,
|
||||
pub error: Option<String>,
|
||||
pub loading: bool,
|
||||
}
|
||||
|
||||
impl GlobalDeviceState {
|
||||
impl DeviceConnectionState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
device_status: None,
|
||||
status: None,
|
||||
fido_info: None,
|
||||
error: None,
|
||||
loading: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LayoutState {
|
||||
pub active_view: ActiveView,
|
||||
pub is_sidebar_collapsed: bool,
|
||||
pub sidebar_toggle_hovered: bool,
|
||||
pub sidebar_width: Pixels,
|
||||
}
|
||||
|
||||
impl LayoutState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_view: ActiveView::Home,
|
||||
is_sidebar_collapsed: false,
|
||||
sidebar_toggle_hovered: false,
|
||||
sidebar_width: px(255.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ViewCache {
|
||||
pub passkeys: Option<Entity<PasskeysView>>,
|
||||
pub config: Option<Entity<ConfigView>>,
|
||||
}
|
||||
|
||||
impl ViewCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
passkeys: None,
|
||||
config: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-28
@@ -1,12 +1,13 @@
|
||||
use crate::device::io;
|
||||
use crate::device::types::{AppConfigInput, FullDeviceStatus};
|
||||
use crate::device::types::AppConfigInput;
|
||||
use crate::ui::components::{
|
||||
card::Card,
|
||||
dialog,
|
||||
dialog::{PinPromptContent, StatusContent},
|
||||
page_view::PageView,
|
||||
};
|
||||
use crate::ui::types::{LedDriverType, UsbIdentityPreset};
|
||||
use crate::ui::rootview::ApplicationRoot;
|
||||
use crate::ui::types::{DeviceConnectionState, LedDriverType, UsbIdentityPreset};
|
||||
use gpui::*;
|
||||
use gpui_component::button::{ButtonCustomVariant, ButtonVariants};
|
||||
use gpui_component::{
|
||||
@@ -61,6 +62,7 @@ enum StatusDialogHandle {
|
||||
}
|
||||
|
||||
pub struct ConfigView {
|
||||
root: WeakEntity<ApplicationRoot>,
|
||||
vendor_select: Entity<SelectState<Vec<VendorSelectOption>>>,
|
||||
vid_input: Entity<InputState>,
|
||||
pid_input: Entity<InputState>,
|
||||
@@ -74,7 +76,6 @@ pub struct ConfigView {
|
||||
power_cycle: bool,
|
||||
enable_secp256k1: bool,
|
||||
loading: bool,
|
||||
device_status: Option<FullDeviceStatus>,
|
||||
is_custom_vendor: bool,
|
||||
_task: Option<Task<()>>,
|
||||
}
|
||||
@@ -83,9 +84,10 @@ impl ConfigView {
|
||||
pub fn new(
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
device_status: Option<FullDeviceStatus>,
|
||||
root: WeakEntity<ApplicationRoot>,
|
||||
device: DeviceConnectionState,
|
||||
) -> Self {
|
||||
let config = device_status.as_ref().map(|s| &s.config);
|
||||
let config = device.status.as_ref().map(|s| &s.config);
|
||||
|
||||
let vendors: Vec<VendorSelectOption> = UsbIdentityPreset::all()
|
||||
.iter()
|
||||
@@ -197,6 +199,7 @@ impl ConfigView {
|
||||
cx.new(|cx| InputState::new(window, cx).default_value(current_touch_timeout.clone()));
|
||||
|
||||
Self {
|
||||
root,
|
||||
vendor_select,
|
||||
vid_input,
|
||||
pid_input,
|
||||
@@ -210,7 +213,6 @@ impl ConfigView {
|
||||
power_cycle: config.map(|c| c.power_cycle_on_reset).unwrap_or(false),
|
||||
enable_secp256k1: config.map(|c| c.enable_secp256k1).unwrap_or(true),
|
||||
loading: false,
|
||||
device_status: device_status.clone(),
|
||||
is_custom_vendor,
|
||||
_task: None,
|
||||
}
|
||||
@@ -224,6 +226,14 @@ impl ConfigView {
|
||||
dialog_handle: StatusDialogHandle,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let expected_serial = self.root.upgrade().and_then(|r| {
|
||||
r.read(cx)
|
||||
.device
|
||||
.status
|
||||
.as_ref()
|
||||
.map(|s| s.info.serial.clone())
|
||||
});
|
||||
|
||||
self.loading = true;
|
||||
cx.notify();
|
||||
|
||||
@@ -254,19 +264,28 @@ impl ConfigView {
|
||||
log::info!("Success: {}", msg);
|
||||
|
||||
if let Some(Ok(new_status)) = new_status_result {
|
||||
log::info!(
|
||||
"Refreshed device status. LED Steady: {}",
|
||||
new_status.config.led_steady
|
||||
);
|
||||
let serial_matches = expected_serial.as_deref()
|
||||
== Some(new_status.info.serial.as_str());
|
||||
|
||||
let config = &new_status.config;
|
||||
if serial_matches {
|
||||
log::info!(
|
||||
"Refreshed device status. LED Steady: {}",
|
||||
new_status.config.led_steady
|
||||
);
|
||||
|
||||
this.led_dimmable = config.led_dimmable;
|
||||
this.led_steady = config.led_steady;
|
||||
this.power_cycle = config.power_cycle_on_reset;
|
||||
this.enable_secp256k1 = config.enable_secp256k1;
|
||||
let config = &new_status.config;
|
||||
this.led_dimmable = config.led_dimmable;
|
||||
this.led_steady = config.led_steady;
|
||||
this.power_cycle = config.power_cycle_on_reset;
|
||||
this.enable_secp256k1 = config.enable_secp256k1;
|
||||
|
||||
this.device_status = Some(new_status);
|
||||
let _ = this.root.update(cx, |root, cx| {
|
||||
root.device.status = Some(new_status);
|
||||
cx.notify();
|
||||
});
|
||||
} else {
|
||||
log::warn!("Device changed during config write, discarding stale status");
|
||||
}
|
||||
}
|
||||
|
||||
match &dialog_handle {
|
||||
@@ -349,11 +368,11 @@ impl ConfigView {
|
||||
}
|
||||
|
||||
fn apply_changes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let status = if let Some(s) = &self.device_status {
|
||||
s
|
||||
} else {
|
||||
let Some(root) = self.root.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let device = root.read(cx).device.clone();
|
||||
let Some(status) = &device.status else { return };
|
||||
|
||||
let current_config = &status.config;
|
||||
let mut changes = AppConfigInput {
|
||||
@@ -397,7 +416,8 @@ impl ConfigView {
|
||||
&& let Some(driver) = LedDriverType::all().get(idx.row)
|
||||
{
|
||||
let val = driver.value();
|
||||
if Some(val) != current_config.led_driver {
|
||||
let current_val = current_config.led_driver.unwrap_or(1);
|
||||
if val != current_val {
|
||||
changes.led_driver = Some(val);
|
||||
}
|
||||
}
|
||||
@@ -460,17 +480,13 @@ impl ConfigView {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_device_status(
|
||||
pub fn sync_from_device(
|
||||
&mut self,
|
||||
status: Option<FullDeviceStatus>,
|
||||
device: &DeviceConnectionState,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.device_status == status {
|
||||
return;
|
||||
}
|
||||
self.device_status = status.clone();
|
||||
let config = status.as_ref().map(|s| &s.config);
|
||||
let config = device.status.as_ref().map(|s| &s.config);
|
||||
|
||||
let vid = config
|
||||
.map(|c| c.vid.clone())
|
||||
@@ -511,6 +527,19 @@ impl ConfigView {
|
||||
self.led_brightness_slider
|
||||
.update(cx, |slider, cx| slider.set_value(brightness, window, cx));
|
||||
|
||||
let new_driver_val = config.and_then(|c| c.led_driver).unwrap_or(1);
|
||||
let new_driver_idx = LedDriverType::all()
|
||||
.iter()
|
||||
.position(|d| d.value() == new_driver_val)
|
||||
.unwrap_or(0);
|
||||
self.led_driver_select.update(cx, |select, cx| {
|
||||
select.set_selected_index(
|
||||
Some(gpui_component::IndexPath::default().row(new_driver_idx)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -727,7 +756,13 @@ impl ConfigView {
|
||||
impl Render for ConfigView {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
if self.device_status.is_none() {
|
||||
let has_device = self
|
||||
.root
|
||||
.upgrade()
|
||||
.map(|r| r.read(cx).device.status.is_some())
|
||||
.unwrap_or(false);
|
||||
|
||||
if !has_device {
|
||||
return PageView::build(
|
||||
"Configuration",
|
||||
"Customize device settings and behavior.",
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
use crate::device::types::DeviceMethod;
|
||||
use crate::ui::components::{card::Card, page_view::PageView, tag::Tag};
|
||||
use crate::ui::types::GlobalDeviceState;
|
||||
use crate::ui::types::DeviceConnectionState;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::*;
|
||||
use gpui_component::StyledExt;
|
||||
@@ -10,11 +10,11 @@ pub struct HomeView;
|
||||
|
||||
impl HomeView {
|
||||
pub fn build(
|
||||
state: &GlobalDeviceState,
|
||||
state: &DeviceConnectionState,
|
||||
theme: &Theme,
|
||||
window_width: Pixels,
|
||||
) -> impl IntoElement {
|
||||
let connected = state.device_status.is_some();
|
||||
let connected = state.status.is_some();
|
||||
let is_wide = window_width > px(1100.0);
|
||||
let columns = if is_wide { 2 } else { 1 };
|
||||
|
||||
@@ -82,8 +82,8 @@ impl HomeView {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_device_info(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.device_status.as_ref().unwrap();
|
||||
fn render_device_info(state: &DeviceConnectionState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.status.as_ref().unwrap();
|
||||
let info = &status.info;
|
||||
let config = &status.config;
|
||||
|
||||
@@ -159,7 +159,7 @@ impl HomeView {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_fido_info(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
fn render_fido_info(state: &DeviceConnectionState, theme: &Theme) -> impl IntoElement {
|
||||
Card::new()
|
||||
.title("FIDO2 Information")
|
||||
.icon(Icon::default().path("icons/shield.svg"))
|
||||
@@ -277,8 +277,8 @@ impl HomeView {
|
||||
})
|
||||
}
|
||||
|
||||
fn render_led_config(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.device_status.as_ref().unwrap();
|
||||
fn render_led_config(state: &DeviceConnectionState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.status.as_ref().unwrap();
|
||||
let config = &status.config;
|
||||
Card::new()
|
||||
.title("LED Configuration")
|
||||
@@ -365,8 +365,8 @@ impl HomeView {
|
||||
})
|
||||
}
|
||||
|
||||
fn render_security_status(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.device_status.as_ref().unwrap();
|
||||
fn render_security_status(state: &DeviceConnectionState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.status.as_ref().unwrap();
|
||||
Card::new()
|
||||
.title("Security Status")
|
||||
.icon(Icon::default().path("icons/shield-check.svg"))
|
||||
|
||||
+53
-42
@@ -1,5 +1,5 @@
|
||||
use crate::device::io;
|
||||
use crate::device::types::{FidoDeviceInfo, FullDeviceStatus, StoredCredential};
|
||||
use crate::device::types::StoredCredential;
|
||||
use crate::ui::components::{
|
||||
button::{PFButton, PFIconButton},
|
||||
card::Card,
|
||||
@@ -7,6 +7,8 @@ use crate::ui::components::{
|
||||
dialog::{ChangePinContent, ConfirmContent, PinPromptContent, SetPinContent, StatusContent},
|
||||
page_view::PageView,
|
||||
};
|
||||
use crate::ui::rootview::ApplicationRoot;
|
||||
use crate::ui::types::DeviceConnectionState;
|
||||
use gpui::*;
|
||||
use gpui_component::button::{Button, ButtonVariant, ButtonVariants};
|
||||
use gpui_component::{
|
||||
@@ -30,13 +32,11 @@ impl Render for SliderLabel {
|
||||
}
|
||||
|
||||
pub struct PasskeysView {
|
||||
device_status: Option<FullDeviceStatus>,
|
||||
fido_info: Option<FidoDeviceInfo>,
|
||||
root: WeakEntity<ApplicationRoot>,
|
||||
credentials: Vec<StoredCredential>,
|
||||
unlocked: bool,
|
||||
cached_pin: Option<String>,
|
||||
loading: bool,
|
||||
|
||||
_task: Option<Task<()>>,
|
||||
}
|
||||
|
||||
@@ -50,12 +50,10 @@ impl PasskeysView {
|
||||
pub fn new(
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Self>,
|
||||
device_status: Option<FullDeviceStatus>,
|
||||
fido_info: Option<FidoDeviceInfo>,
|
||||
root: WeakEntity<ApplicationRoot>,
|
||||
) -> Self {
|
||||
Self {
|
||||
device_status,
|
||||
fido_info,
|
||||
root,
|
||||
credentials: Vec::new(),
|
||||
unlocked: false,
|
||||
cached_pin: None,
|
||||
@@ -64,20 +62,6 @@ impl PasskeysView {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_device_status(
|
||||
&mut self,
|
||||
status: Option<FullDeviceStatus>,
|
||||
fido_info: Option<FidoDeviceInfo>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.device_status == status && self.fido_info == fido_info {
|
||||
return;
|
||||
}
|
||||
self.device_status = status;
|
||||
self.fido_info = fido_info;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn unlock_storage(
|
||||
&mut self,
|
||||
pin: String,
|
||||
@@ -283,7 +267,10 @@ impl PasskeysView {
|
||||
Ok(msg) => {
|
||||
log::info!("PIN configured: {}", msg);
|
||||
if let Ok(info) = io::get_fido_info() {
|
||||
this.fido_info = Some(info);
|
||||
let _ = this.root.update(cx, |root, cx| {
|
||||
root.device.fido_info = Some(info);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
let _ = dialog_handle.update(cx, |d, cx| {
|
||||
d.set_success("PIN configured successfully.".to_string(), cx);
|
||||
@@ -303,9 +290,15 @@ impl PasskeysView {
|
||||
|
||||
fn open_min_pin_length_dialog(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let current_min = self
|
||||
.fido_info
|
||||
.as_ref()
|
||||
.map(|f| f.min_pin_length)
|
||||
.root
|
||||
.upgrade()
|
||||
.and_then(|r| {
|
||||
r.read(cx)
|
||||
.device
|
||||
.fido_info
|
||||
.as_ref()
|
||||
.map(|f| f.min_pin_length)
|
||||
})
|
||||
.unwrap_or(4);
|
||||
|
||||
let slider = cx.new(|_| {
|
||||
@@ -468,7 +461,10 @@ impl PasskeysView {
|
||||
Ok(msg) => {
|
||||
log::info!("PIN changed: {}", msg);
|
||||
if let Ok(info) = io::get_fido_info() {
|
||||
this.fido_info = Some(info);
|
||||
let _ = this.root.update(cx, |root, cx| {
|
||||
root.device.fido_info = Some(info);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
let _ = dialog_handle.update(cx, |d, cx| {
|
||||
d.set_success("PIN changed successfully.".to_string(), cx);
|
||||
@@ -533,7 +529,10 @@ impl PasskeysView {
|
||||
Ok(_) => {
|
||||
log::info!("Minimum length and PIN updated successfully.");
|
||||
if let Ok(info) = io::get_fido_info() {
|
||||
this.fido_info = Some(info);
|
||||
let _ = this.root.update(cx, |root, cx| {
|
||||
root.device.fido_info = Some(info);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
let _ = status_handle.update(cx, |s, cx| {
|
||||
s.set_success("Minimum length and PIN updated.".to_string(), cx);
|
||||
@@ -556,7 +555,10 @@ impl PasskeysView {
|
||||
this.loading = false;
|
||||
log::info!("Minimum PIN length updated to {}.", min_len);
|
||||
if let Ok(info) = io::get_fido_info() {
|
||||
this.fido_info = Some(info);
|
||||
let _ = this.root.update(cx, |root, cx| {
|
||||
root.device.fido_info = Some(info);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
let _ = status_handle.update(cx, |s, cx| {
|
||||
s.set_success(format!("Minimum length updated to {}.", min_len), cx);
|
||||
@@ -613,8 +615,11 @@ impl PasskeysView {
|
||||
}
|
||||
|
||||
fn render_pin_status_row(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let pin_set = self
|
||||
.fido_info
|
||||
let fido_info = self
|
||||
.root
|
||||
.upgrade()
|
||||
.and_then(|r| r.read(cx).device.fido_info.clone());
|
||||
let pin_set = fido_info
|
||||
.as_ref()
|
||||
.and_then(|f| f.options.get("clientPin").copied())
|
||||
.unwrap_or(false);
|
||||
@@ -660,13 +665,12 @@ impl PasskeysView {
|
||||
}
|
||||
|
||||
fn render_min_pin_length_row(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let min_len = self
|
||||
.fido_info
|
||||
.as_ref()
|
||||
.map(|f| f.min_pin_length)
|
||||
.unwrap_or(4);
|
||||
let pin_set = self
|
||||
.fido_info
|
||||
let fido_info = self
|
||||
.root
|
||||
.upgrade()
|
||||
.and_then(|r| r.read(cx).device.fido_info.clone());
|
||||
let min_len = fido_info.as_ref().map(|f| f.min_pin_length).unwrap_or(4);
|
||||
let pin_set = fido_info
|
||||
.as_ref()
|
||||
.and_then(|f| f.options.get("clientPin").copied())
|
||||
.unwrap_or(false);
|
||||
@@ -1121,7 +1125,14 @@ impl PasskeysView {
|
||||
|
||||
impl Render for PasskeysView {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let device_connected = self.device_status.is_some();
|
||||
let device = self
|
||||
.root
|
||||
.upgrade()
|
||||
.map(|r| r.read(cx).device.clone())
|
||||
.unwrap_or_else(DeviceConnectionState::new);
|
||||
|
||||
let device_connected = device.status.is_some();
|
||||
|
||||
if !device_connected {
|
||||
let theme = cx.theme();
|
||||
return PageView::build(
|
||||
@@ -1133,12 +1144,12 @@ impl Render for PasskeysView {
|
||||
.into_any_element();
|
||||
}
|
||||
|
||||
let has_fido = self
|
||||
.device_status
|
||||
let has_fido = device
|
||||
.status
|
||||
.as_ref()
|
||||
.map(|s| s.method == crate::device::types::DeviceMethod::Fido)
|
||||
.unwrap_or(false)
|
||||
|| self.fido_info.is_some();
|
||||
|| device.fido_info.is_some();
|
||||
|
||||
if !has_fido {
|
||||
let theme = cx.theme();
|
||||
|
||||
Reference in New Issue
Block a user