Implement native iPhone entry editor

This commit is contained in:
2026-08-11 20:27:14 +02:00
parent 900e62e523
commit ae64ce47a3
8 changed files with 2516 additions and 76 deletions

View File

@@ -15,7 +15,11 @@ use ironstorage::{
MobileEntryCopy as StorageEntryCopy,
},
mobile_entry::{
MobileEntryPage as StorageEntryPage, MobileEntrySectionKind as StorageEntrySectionKind,
MobileEntryEditorFieldKind as StorageEntryEditorFieldKind,
MobileEntryEditorInput as StorageEntryEditorInput,
MobileEntryEditorPage as StorageEntryEditorPage,
MobileEntryEditorSession as StorageEntryEditorSession, MobileEntryPage as StorageEntryPage,
MobileEntrySectionKind as StorageEntrySectionKind,
},
mobile_home::{
self, MobileHomeChangeKind as StorageHomeChangeKind,
@@ -511,6 +515,7 @@ pub enum MobileAuthenticationErrorKind {
Entry,
SecureStorage,
Expired,
Conflict,
}
impl From<StorageAuthenticationErrorKind> for MobileAuthenticationErrorKind {
@@ -525,6 +530,7 @@ impl From<StorageAuthenticationErrorKind> for MobileAuthenticationErrorKind {
StorageAuthenticationErrorKind::Entry => Self::Entry,
StorageAuthenticationErrorKind::SecureStorage => Self::SecureStorage,
StorageAuthenticationErrorKind::Expired => Self::Expired,
StorageAuthenticationErrorKind::Conflict => Self::Conflict,
}
}
}
@@ -624,6 +630,124 @@ pub struct MobileEntryCopy {
pub timeout_seconds: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
pub enum MobileEntryEditorFieldKind {
Password,
Field,
Note,
OtpUri,
}
impl From<StorageEntryEditorFieldKind> for MobileEntryEditorFieldKind {
fn from(kind: StorageEntryEditorFieldKind) -> Self {
match kind {
StorageEntryEditorFieldKind::Password => Self::Password,
StorageEntryEditorFieldKind::Field => Self::Field,
StorageEntryEditorFieldKind::Note => Self::Note,
StorageEntryEditorFieldKind::OtpUri => Self::OtpUri,
}
}
}
impl From<MobileEntryEditorFieldKind> for StorageEntryEditorFieldKind {
fn from(kind: MobileEntryEditorFieldKind) -> Self {
match kind {
MobileEntryEditorFieldKind::Password => Self::Password,
MobileEntryEditorFieldKind::Field => Self::Field,
MobileEntryEditorFieldKind::Note => Self::Note,
MobileEntryEditorFieldKind::OtpUri => Self::OtpUri,
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileEntryEditorField {
pub id: u64,
pub kind: MobileEntryEditorFieldKind,
pub name: Option<String>,
pub label: String,
pub system_image: String,
pub value: Option<String>,
pub masked_value: String,
pub diagnostic: Option<String>,
pub sensitive: bool,
pub multiline: bool,
pub name_editable: bool,
pub removable: bool,
pub reorderable: bool,
}
#[derive(Clone, uniffi::Record)]
pub struct MobileEntryEditorPage {
pub path: String,
pub title: String,
pub fields: Vec<MobileEntryEditorField>,
pub dirty: bool,
pub creating: bool,
pub default_password_length: u32,
pub maximum_password_length: u32,
}
impl From<&StorageEntryEditorPage> for MobileEntryEditorPage {
fn from(page: &StorageEntryEditorPage) -> Self {
Self {
path: page.path().to_owned(),
title: page.title().to_owned(),
fields: page
.fields()
.iter()
.map(|field| MobileEntryEditorField {
id: field.id(),
kind: field.kind().into(),
name: field.name().map(str::to_owned),
label: field.label().to_owned(),
system_image: field.system_image().to_owned(),
value: field.value().map(str::to_owned),
masked_value: field.masked_value().to_owned(),
diagnostic: field.diagnostic().map(str::to_owned),
sensitive: field.sensitive(),
multiline: field.multiline(),
name_editable: field.name_editable(),
removable: field.removable(),
reorderable: field.reorderable(),
})
.collect(),
dirty: page.dirty(),
creating: page.creating(),
default_password_length: page.default_password_length(),
maximum_password_length: page.maximum_password_length(),
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileEntryEditorSession {
pub id: u64,
pub page: MobileEntryEditorPage,
}
impl From<StorageEntryEditorSession> for MobileEntryEditorSession {
fn from(session: StorageEntryEditorSession) -> Self {
Self {
id: session.id(),
page: session.page().into(),
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileEntryEditorInput {
pub id: u64,
pub name: Option<String>,
pub value: Option<String>,
}
impl From<MobileEntryEditorInput> for StorageEntryEditorInput {
fn from(input: MobileEntryEditorInput) -> Self {
Self::new(input.id, input.name, input.value)
}
}
impl From<StorageEntryCopy> for MobileEntryCopy {
fn from(copy: StorageEntryCopy) -> Self {
Self {
@@ -760,6 +884,119 @@ impl MobileAuthentication {
.map_err(Into::into)
}
pub fn begin_entry_editor(
&self,
path: String,
) -> Result<MobileEntryEditorSession, MobileAuthenticationFfiError> {
self.authentication
.begin_entry_editor(&path)
.map(Into::into)
.map_err(Into::into)
}
pub fn begin_create_entry(
&self,
directory: String,
name: String,
passphrase: Option<String>,
) -> Result<MobileEntryEditorSession, MobileAuthenticationFfiError> {
self.authentication
.begin_create_entry(
&directory,
&name,
passphrase
.map(|value| ironstorage::repository::SecretBytes::new(value.into_bytes())),
)
.map(Into::into)
.map_err(Into::into)
}
pub fn entry_editor(
&self,
editor: u64,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.entry_editor(editor)
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn update_entry_editor(
&self,
editor: u64,
fields: Vec<MobileEntryEditorInput>,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.update_entry_editor(editor, fields.into_iter().map(Into::into).collect())
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn add_entry_editor_field(
&self,
editor: u64,
kind: MobileEntryEditorFieldKind,
name: Option<String>,
value: String,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.add_entry_editor_field(editor, kind.into(), name, value)
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn remove_entry_editor_field(
&self,
editor: u64,
field: u64,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.remove_entry_editor_field(editor, field)
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn reorder_entry_editor_field(
&self,
editor: u64,
field: u64,
index: u32,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.reorder_entry_editor_field(editor, field, index as usize)
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn generate_entry_editor_password(
&self,
editor: u64,
length: Option<u32>,
no_symbols: bool,
) -> Result<MobileEntryEditorPage, MobileAuthenticationFfiError> {
self.authentication
.generate_entry_editor_password(editor, length, no_symbols)
.map(|page| (&page).into())
.map_err(Into::into)
}
pub fn save_entry_editor(
&self,
editor: u64,
fields: Vec<MobileEntryEditorInput>,
) -> Result<MobileEntryPage, MobileAuthenticationFfiError> {
self.authentication
.save_entry_editor(editor, fields.into_iter().map(Into::into).collect())
.map(Into::into)
.map_err(Into::into)
}
pub fn discard_entry_editor(&self, editor: u64) -> Result<(), MobileAuthenticationFfiError> {
self.authentication
.discard_entry_editor(editor)
.map_err(Into::into)
}
pub fn manual_lock(&self) -> Result<(), MobileAuthenticationFfiError> {
self.authentication.manual_lock().map_err(Into::into)
}