UI code cleanup
This commit is contained in:
parent
2b8dfa7b44
commit
515b3ff4fd
|
|
@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use const_gen::*;
|
||||
use embuild::cmd;
|
||||
use indoc::{formatdoc, writedoc};
|
||||
use indoc::writedoc;
|
||||
use json::JsonValue;
|
||||
use slint_build::{CompilerConfiguration, EmbedResourcesKind};
|
||||
use xz2::read::XzEncoder;
|
||||
|
|
@ -63,6 +63,7 @@ fn main() {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct NotBuilt {
|
||||
#[allow(unused)]
|
||||
lib_build_dir: String,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,120 +46,23 @@ use crate::{
|
|||
PartitionAcid, ReadTransactionExt,
|
||||
},
|
||||
ffi::alloc::__spre_free,
|
||||
ui::backend::SlintBackend,
|
||||
ui::{
|
||||
backend::SlintBackend,
|
||||
messages::{
|
||||
CallbackMessage, CallbackMessageLogin, CallbackMessageUserEdit, CallbackMessageUsers,
|
||||
},
|
||||
storage::SpectreUsersConfig,
|
||||
},
|
||||
util::DurationExt,
|
||||
};
|
||||
|
||||
pub mod backend;
|
||||
pub mod messages;
|
||||
pub mod storage;
|
||||
pub mod window_adapter;
|
||||
|
||||
slint::include_modules!();
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreUsersConfig {
|
||||
users: Vec<SpectreUserConfig>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreUserConfig {
|
||||
username: String,
|
||||
#[serde(with = "serde_bytes")]
|
||||
encrypted_key: Key,
|
||||
#[serde(with = "serde_bytes")]
|
||||
key_id: [u8; 32],
|
||||
}
|
||||
|
||||
trait ReprConvert: Copy {
|
||||
type Repr: Copy;
|
||||
|
||||
fn into_repr(self) -> Self::Repr;
|
||||
fn from_repr(repr: Self::Repr) -> Self;
|
||||
}
|
||||
|
||||
impl ReprConvert for SpectreAlgorithm {
|
||||
type Repr = u32;
|
||||
|
||||
fn from_repr(repr: Self::Repr) -> Self {
|
||||
unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
|
||||
}
|
||||
|
||||
fn into_repr(self) -> Self::Repr {
|
||||
self as Self::Repr
|
||||
}
|
||||
}
|
||||
|
||||
impl ReprConvert for SpectreResultType {
|
||||
type Repr = u32;
|
||||
|
||||
fn from_repr(repr: Self::Repr) -> Self {
|
||||
unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
|
||||
}
|
||||
|
||||
fn into_repr(self) -> Self::Repr {
|
||||
self as Self::Repr
|
||||
}
|
||||
}
|
||||
|
||||
mod with_repr {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
use super::ReprConvert;
|
||||
|
||||
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: ReprConvert<Repr = u32>,
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u32(value.into_repr())
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: ReprConvert<Repr = u32>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
<u32 as Deserialize>::deserialize(deserializer).map(T::from_repr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreSiteConfig {
|
||||
#[serde(with = "with_repr")]
|
||||
algorithm: SpectreAlgorithm,
|
||||
counter: SpectreCounter::Type,
|
||||
#[serde(rename = "type")]
|
||||
#[serde(with = "with_repr")]
|
||||
result_type: SpectreResultType,
|
||||
password: Option<String>,
|
||||
#[serde(with = "with_repr")]
|
||||
login_type: SpectreResultType,
|
||||
login_name: Option<String>,
|
||||
uses: u32,
|
||||
last_used: NaiveDateTime,
|
||||
}
|
||||
|
||||
impl Default for SpectreSiteConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
algorithm: SpectreAlgorithm::Current,
|
||||
counter: SpectreCounter::Default,
|
||||
result_type: SpectreResultType::SpectreResultDefaultResult,
|
||||
password: None,
|
||||
login_type: SpectreResultType::SpectreResultDefaultLogin,
|
||||
login_name: None,
|
||||
uses: 0,
|
||||
last_used: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreSite {
|
||||
username: String,
|
||||
site_name: String,
|
||||
config: SpectreSiteConfig,
|
||||
}
|
||||
|
||||
fn spectre_derive_user_key(username: &CStr, password: &CStr) -> SpectreUserKey {
|
||||
let user_key_start = Instant::now();
|
||||
|
||||
|
|
@ -282,6 +185,12 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
.unwrap();
|
||||
slint::platform::set_platform(Box::new(backend)).expect("backend already initialized");
|
||||
|
||||
let main = AppWindow::new().unwrap();
|
||||
let state = State::new(db, main).await;
|
||||
|
||||
state.borrow().run_event_loop().await;
|
||||
}
|
||||
|
||||
struct State {
|
||||
window: AppWindow,
|
||||
db: AcidDatabase,
|
||||
|
|
@ -296,6 +205,105 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
}
|
||||
|
||||
impl State {
|
||||
async fn new(db: AcidDatabase, main: AppWindow) -> Rc<RefCell<Self>> {
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
window: main.clone_strong(),
|
||||
users: {
|
||||
let read = db.read_transaction().await;
|
||||
let mut buffer = vec![0_u8; 128];
|
||||
match read
|
||||
.read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer)
|
||||
.await
|
||||
{
|
||||
Ok(bytes) => postcard::from_bytes::<SpectreUsersConfig>(&bytes).unwrap(),
|
||||
Err(ekv::ReadError::KeyNotFound) => Default::default(),
|
||||
Err(error) => panic!("Failed to read the users config: {error:?}"),
|
||||
}
|
||||
},
|
||||
db,
|
||||
view: AppState::Login,
|
||||
state_login: Default::default(),
|
||||
state_users: Default::default(),
|
||||
state_user_edit: Default::default(),
|
||||
state_user_sites: Default::default(),
|
||||
}));
|
||||
|
||||
main.on_enter_view({
|
||||
let state = state.clone();
|
||||
move |view| {
|
||||
state.borrow_mut().set_view(view, true);
|
||||
}
|
||||
});
|
||||
|
||||
main.on_escape({
|
||||
let state = state.clone();
|
||||
move || {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Escape);
|
||||
}
|
||||
});
|
||||
|
||||
main.on_login_pw_accepted({
|
||||
let state = state.clone();
|
||||
move |username, password| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Login(
|
||||
CallbackMessageLogin::PwAccepted { username, password },
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_users_edit_user({
|
||||
let state = state.clone();
|
||||
move |username, new| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Users(
|
||||
CallbackMessageUsers::EditUser { username, new },
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_user_edit_compute_identicon({
|
||||
let state = state.clone();
|
||||
move |encrypted_key, password| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::UserEdit(
|
||||
CallbackMessageUserEdit::ComputeIdenticon {
|
||||
encrypted_key,
|
||||
password,
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_user_edit_confirm({
|
||||
let state = state.clone();
|
||||
move |encrypted_key| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::UserEdit(
|
||||
CallbackMessageUserEdit::Confirm { encrypted_key },
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// let sites = Rc::new(VecModel::default());
|
||||
// sites.push("First".into());
|
||||
// sites.push("Second".into());
|
||||
// main.set_sites(ModelRc::new(ModelRc::new(sites.clone()).map(
|
||||
// |mut site: StandardListViewItem| {
|
||||
// site.text += "10";
|
||||
// site
|
||||
// },
|
||||
// )));
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
fn process_callback_message(&mut self, message: CallbackMessage) {
|
||||
match self.view {
|
||||
AppState::Login => StateLogin::process_callback_message(self, message),
|
||||
|
|
@ -318,6 +326,24 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve
|
||||
/// async by having only one iteration of the loop run, and `await`ing here.
|
||||
/// The following block is analogous to `main.run()`.
|
||||
async fn run_event_loop(&self) -> ! {
|
||||
self.window.show().unwrap();
|
||||
|
||||
loop {
|
||||
slint::run_event_loop().unwrap();
|
||||
SIGNAL_LCD_SUBMIT.signal(());
|
||||
#[cfg(feature = "limit-fps")]
|
||||
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
||||
SIGNAL_UI_RENDER.wait().await;
|
||||
}
|
||||
|
||||
#[expect(unreachable_code)]
|
||||
self.window.hide().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
trait AppViewTrait {
|
||||
|
|
@ -331,10 +357,10 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
fn process_callback_message(state: &mut State, message: CallbackMessage) {
|
||||
match message {
|
||||
CallbackMessage::Login(CallbackMessageLogin::PwAccepted { username, password }) => {
|
||||
let username_c = CString::new(&*username)
|
||||
.expect("Username cannot be converted to a C string.");
|
||||
let password_c = CString::new(&*password)
|
||||
.expect("Password cannot be converted to a C string.");
|
||||
let username_c =
|
||||
CString::new(&*username).expect("Username cannot be converted to a C string.");
|
||||
let password_c =
|
||||
CString::new(&*password).expect("Password cannot be converted to a C string.");
|
||||
let user_key = spectre_derive_user_key(&username_c, &password_c);
|
||||
|
||||
// let site_key_start = Instant::now();
|
||||
|
|
@ -417,8 +443,8 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
}) => {
|
||||
let username_c = CString::new(&*state.state_user_edit.username)
|
||||
.expect("Username cannot be converted to a C string.");
|
||||
let password_c = CString::new(&*password)
|
||||
.expect("Password cannot be converted to a C string.");
|
||||
let password_c =
|
||||
CString::new(&*password).expect("Password cannot be converted to a C string.");
|
||||
// let user_key = spectre_derive_user_key(&username_c, &password_c);
|
||||
let identicon: SharedString = unsafe {
|
||||
let identicon = spectre_api_sys::spectre_identicon(
|
||||
|
|
@ -462,154 +488,3 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
|||
}
|
||||
|
||||
impl AppViewTrait for StateUserSites {}
|
||||
|
||||
enum CallbackMessage {
|
||||
/// The escape key was pressed.
|
||||
Escape,
|
||||
Login(CallbackMessageLogin),
|
||||
Users(CallbackMessageUsers),
|
||||
UserEdit(CallbackMessageUserEdit),
|
||||
UserSites(CallbackMessageUserSites),
|
||||
}
|
||||
|
||||
enum CallbackMessageLogin {
|
||||
PwAccepted {
|
||||
username: SharedString,
|
||||
password: SharedString,
|
||||
},
|
||||
}
|
||||
|
||||
enum CallbackMessageUsers {
|
||||
EditUser { username: SharedString, new: bool },
|
||||
}
|
||||
|
||||
enum CallbackMessageUserEdit {
|
||||
ComputeIdenticon {
|
||||
encrypted_key: SharedString,
|
||||
password: SharedString,
|
||||
},
|
||||
Confirm {
|
||||
encrypted_key: SharedString,
|
||||
},
|
||||
}
|
||||
|
||||
enum CallbackMessageUserSites {}
|
||||
|
||||
let main = AppWindow::new().unwrap();
|
||||
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
window: main.clone_strong(),
|
||||
users: {
|
||||
let read = db.read_transaction().await;
|
||||
let mut buffer = vec![0_u8; 128];
|
||||
match read
|
||||
.read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer)
|
||||
.await
|
||||
{
|
||||
Ok(bytes) => postcard::from_bytes::<SpectreUsersConfig>(&bytes).unwrap(),
|
||||
Err(ekv::ReadError::KeyNotFound) => Default::default(),
|
||||
Err(error) => panic!("Failed to read the users config: {error:?}"),
|
||||
}
|
||||
},
|
||||
db,
|
||||
view: AppState::Login,
|
||||
state_login: Default::default(),
|
||||
state_users: Default::default(),
|
||||
state_user_edit: Default::default(),
|
||||
state_user_sites: Default::default(),
|
||||
}));
|
||||
|
||||
main.on_enter_view({
|
||||
let state = state.clone();
|
||||
move |view| {
|
||||
state.borrow_mut().set_view(view, true);
|
||||
}
|
||||
});
|
||||
|
||||
main.on_escape({
|
||||
let state = state.clone();
|
||||
move || {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Escape);
|
||||
}
|
||||
});
|
||||
|
||||
main.on_login_pw_accepted({
|
||||
let state = state.clone();
|
||||
move |username, password| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Login(
|
||||
CallbackMessageLogin::PwAccepted { username, password },
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_users_edit_user({
|
||||
let state = state.clone();
|
||||
move |username, new| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::Users(CallbackMessageUsers::EditUser {
|
||||
username,
|
||||
new,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_user_edit_compute_identicon({
|
||||
let state = state.clone();
|
||||
move |encrypted_key, password| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::UserEdit(
|
||||
CallbackMessageUserEdit::ComputeIdenticon {
|
||||
encrypted_key,
|
||||
password,
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
main.on_user_edit_confirm({
|
||||
let state = state.clone();
|
||||
move |encrypted_key| {
|
||||
state
|
||||
.borrow_mut()
|
||||
.process_callback_message(CallbackMessage::UserEdit(
|
||||
CallbackMessageUserEdit::Confirm { encrypted_key },
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// let sites = Rc::new(VecModel::default());
|
||||
// sites.push("First".into());
|
||||
// sites.push("Second".into());
|
||||
// main.set_sites(ModelRc::new(ModelRc::new(sites.clone()).map(
|
||||
// |mut site: StandardListViewItem| {
|
||||
// site.text += "10";
|
||||
// site
|
||||
// },
|
||||
// )));
|
||||
|
||||
run_event_loop(main).await;
|
||||
}
|
||||
|
||||
/// Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve
|
||||
/// async by having only one iteration of the loop run, and `await`ing here.
|
||||
/// The following block is analogous to `main.run()`.
|
||||
async fn run_event_loop(main: AppWindow) {
|
||||
main.show().unwrap();
|
||||
|
||||
loop {
|
||||
slint::run_event_loop().unwrap();
|
||||
SIGNAL_LCD_SUBMIT.signal(());
|
||||
#[cfg(feature = "limit-fps")]
|
||||
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
||||
SIGNAL_UI_RENDER.wait().await;
|
||||
}
|
||||
|
||||
#[expect(unreachable_code)]
|
||||
main.hide().unwrap();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue