Normalize entry parsing and display order
This commit is contained in:
@@ -25,7 +25,7 @@ pub enum FieldNavigation {
|
||||
|
||||
impl EntryEditor {
|
||||
pub fn new(document: EntryDocument) -> Self {
|
||||
let focused = document.fields().first().map(EntryField::id);
|
||||
let focused = document.display_fields().first().map(|field| field.id());
|
||||
let multiline = document
|
||||
.fields()
|
||||
.iter()
|
||||
@@ -86,7 +86,7 @@ impl EntryEditor {
|
||||
}
|
||||
|
||||
pub fn focused_ratio(&self) -> f32 {
|
||||
let fields = self.document.fields();
|
||||
let fields = self.document.display_fields();
|
||||
self.focused
|
||||
.and_then(|id| fields.iter().position(|field| field.id() == id))
|
||||
.map_or(0.0, |index| {
|
||||
@@ -128,7 +128,7 @@ impl EntryEditor {
|
||||
}
|
||||
|
||||
pub fn navigate(&mut self, navigation: FieldNavigation) {
|
||||
let fields = self.document.fields();
|
||||
let fields = self.document.display_fields();
|
||||
if fields.is_empty() {
|
||||
self.focused = None;
|
||||
return;
|
||||
|
||||
@@ -4945,7 +4945,6 @@ fn viewer_label(field: &EntryField) -> String {
|
||||
EntryFieldKind::OtpUri => "One-time password".to_owned(),
|
||||
EntryFieldKind::Field => "Field".to_owned(),
|
||||
EntryFieldKind::Note => "Note".to_owned(),
|
||||
EntryFieldKind::Blank => "Blank line".to_owned(),
|
||||
},
|
||||
str::to_owned,
|
||||
)
|
||||
@@ -4982,7 +4981,7 @@ fn viewer_view<'a>(app: &'a App, editor: &'a EntryEditor) -> Element<'a, Message
|
||||
column![text("Up/Down/Home/End select fields · ⌘C copies the selected value").size(12),]
|
||||
.spacing(8);
|
||||
|
||||
for field in editor.fields() {
|
||||
for field in editor.document().display_fields() {
|
||||
let id = field.id();
|
||||
let label = viewer_label(field);
|
||||
let selected = editor.focused() == Some(id);
|
||||
@@ -5943,7 +5942,7 @@ mod tests {
|
||||
let temporary = editor.fields().last().expect("temporary field").id();
|
||||
editor.remove(temporary).expect("remove temporary line");
|
||||
let expected =
|
||||
b"password\nusername: alice\nfirst note line\ncustom-field: opaque\nsecond note line";
|
||||
b"password\nusername: alice\n first note line\ncustom-field: opaque\n second note line";
|
||||
assert_eq!(editor.document().serialize().expose(), expected);
|
||||
save_document(&storage, &editor).expect("initial save");
|
||||
|
||||
@@ -5966,7 +5965,7 @@ mod tests {
|
||||
assert!(stale.is_dirty());
|
||||
assert_eq!(
|
||||
stale.document().serialize().expose(),
|
||||
b"complete stale draft\nusername: alice\nfirst note line\ncustom-field: opaque\nsecond note line"
|
||||
b"complete stale draft\nusername: alice\n first note line\ncustom-field: opaque\n second note line"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6057,12 +6056,32 @@ mod tests {
|
||||
.text()
|
||||
);
|
||||
assert!(document_has_single_totp(editor.document()));
|
||||
assert_eq!(
|
||||
editor
|
||||
.document()
|
||||
.display_fields()
|
||||
.into_iter()
|
||||
.map(EntryField::id)
|
||||
.collect::<Vec<_>>(),
|
||||
[
|
||||
editor.fields()[1].id(),
|
||||
editor.fields()[0].id(),
|
||||
editor.fields()[7].id(),
|
||||
editor.fields()[8].id(),
|
||||
editor.fields()[4].id(),
|
||||
editor.fields()[5].id(),
|
||||
editor.fields()[6].id(),
|
||||
editor.fields()[9].id(),
|
||||
editor.fields()[2].id(),
|
||||
editor.fields()[3].id(),
|
||||
]
|
||||
);
|
||||
editor.navigate(FieldNavigation::First);
|
||||
assert_eq!(editor.focused(), Some(password));
|
||||
editor.navigate(FieldNavigation::Next);
|
||||
assert_eq!(editor.focused(), Some(editor.fields()[1].id()));
|
||||
editor.navigate(FieldNavigation::Next);
|
||||
assert_eq!(editor.focused(), Some(password));
|
||||
editor.navigate(FieldNavigation::Last);
|
||||
assert_eq!(editor.focused(), Some(editor.fields()[9].id()));
|
||||
assert_eq!(editor.focused(), Some(editor.fields()[3].id()));
|
||||
|
||||
assert!(!authentication_allows_content(&AuthenticationView::Locked));
|
||||
assert!(authentication_allows_content(
|
||||
|
||||
@@ -713,17 +713,17 @@ fn viewer_lines<'a>(
|
||||
otp_display: Option<&'a crate::app::OtpDisplay>,
|
||||
) -> Vec<Line<'a>> {
|
||||
let focused = viewer.focused_index();
|
||||
if viewer.document().fields().is_empty() {
|
||||
let fields = viewer.document().display_fields();
|
||||
if fields.is_empty() {
|
||||
return vec![Line::from("This entry is empty.")];
|
||||
}
|
||||
|
||||
let mut lines = Vec::new();
|
||||
for (index, field) in viewer.document().fields().iter().enumerate() {
|
||||
for (index, field) in fields.into_iter().enumerate() {
|
||||
let metadata = field.metadata();
|
||||
let label = metadata.name().map_or_else(
|
||||
|| match metadata.kind() {
|
||||
ironstorage::document::EntryFieldKind::Note => format!("note {}", index + 1),
|
||||
ironstorage::document::EntryFieldKind::Blank => format!("blank {}", index + 1),
|
||||
kind => format!("{kind:?}").to_ascii_lowercase(),
|
||||
},
|
||||
str::to_owned,
|
||||
@@ -1514,14 +1514,19 @@ mod tests {
|
||||
|
||||
render(40, 8, &app);
|
||||
assert_eq!(app.viewer().expect("viewer").scroll(), 0);
|
||||
app.dispatch(crate::action::Action::Next);
|
||||
app.dispatch(crate::action::Action::FocusNext);
|
||||
app.dispatch(crate::action::Action::FocusNext);
|
||||
let wrapped = render(40, 8, &app);
|
||||
assert!(wrapped.contains("url: xxxxxx"));
|
||||
let wrapped_scroll = app.viewer().expect("viewer").scroll();
|
||||
assert_eq!(wrapped_scroll, 0);
|
||||
app.dispatch(crate::action::Action::FocusNext);
|
||||
let login = render(40, 8, &app);
|
||||
assert!(login.contains("login: alice@example.test"));
|
||||
render(40, 8, &app);
|
||||
assert_eq!(
|
||||
app.viewer()
|
||||
.and_then(crate::viewer::EntryViewer::focused_field)
|
||||
.map(|field| field.metadata().kind()),
|
||||
Some(ironstorage::document::EntryFieldKind::Url)
|
||||
);
|
||||
assert!(app.viewer().expect("viewer").scroll() > wrapped_scroll);
|
||||
|
||||
render(140, 20, &app);
|
||||
@@ -1583,6 +1588,7 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
app.open_test_document("documents/multiline", document);
|
||||
app.dispatch(crate::action::Action::FocusNext);
|
||||
app.dispatch(crate::action::Action::FocusNext);
|
||||
|
||||
let viewer = app.viewer().expect("viewer");
|
||||
assert_eq!(
|
||||
@@ -1591,7 +1597,7 @@ mod tests {
|
||||
);
|
||||
let lines = viewer_lines(viewer, None);
|
||||
assert_eq!(lines.len(), 5);
|
||||
for line in &lines[1..4] {
|
||||
for line in &lines[2..5] {
|
||||
assert!(line.spans.iter().all(|span| {
|
||||
span.style.fg == Some(SELECTED_FOREGROUND)
|
||||
&& span.style.bg == Some(SELECTED_BACKGROUND)
|
||||
|
||||
@@ -39,7 +39,7 @@ impl EntryViewer {
|
||||
}
|
||||
|
||||
pub fn focused_field(&self) -> Option<&EntryField> {
|
||||
self.document.fields().get(self.focused)
|
||||
self.document.display_fields().get(self.focused).copied()
|
||||
}
|
||||
|
||||
pub fn focus_next(&mut self) {
|
||||
@@ -231,8 +231,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn copy_uses_only_the_focused_structured_value() {
|
||||
let mut viewer = EntryViewer::new(fixture_document("email/personal"));
|
||||
viewer.focus_next();
|
||||
let viewer = EntryViewer::new(fixture_document("email/personal"));
|
||||
let copied = viewer.copy_focused().expect("copy value");
|
||||
assert_eq!(copied.expose(), b"alice@example.test");
|
||||
assert!(!format!("{viewer:?}").contains("correct horse fixture"));
|
||||
|
||||
Reference in New Issue
Block a user