Skip to content

Cluster 1 — code quality + dialog XamlRoot fix + CSV importer hardening [v2.0]#42

Merged
pexatar merged 1 commit into
mainfrom
feat/2.0/main
May 16, 2026
Merged

Cluster 1 — code quality + dialog XamlRoot fix + CSV importer hardening [v2.0]#42
pexatar merged 1 commit into
mainfrom
feat/2.0/main

Conversation

@pexatar
Copy link
Copy Markdown
Owner

@pexatar pexatar commented May 16, 2026

First cluster of the PassKey 2.0 refactor described in
piano-revisione-correzione-miglioramento.md (private notes file).

Summary

Code quality / modernization

  • Extract BaseDetailViewModel<TEntry> with a template-method pattern; the four Detail VMs (Password, CreditCard, Identity, SecureNote) inherit shared add/edit/save/delete plumbing — ~200 LOC of duplication removed.
  • Introduce IVaultEntry interface as the generic constraint.
  • Replace Task.Run(async () => await …) in LoginVM/SetupVM with Task.Run(Func<Task>) to keep KDF/decrypt off the UI thread without the nested async-over-async wrapper.
  • Implement IDisposable on MainVM and the four List VMs; named handlers replace closures so VaultLocked / NavigationStack.CurrentChanged can be unsubscribed, and Entries collections are cleared on vault lock to avoid retaining decrypted data after lock.
  • Extract BIN prefixes in CardTypeDetector to a dedicated BinPrefixes static class.

Dialog / UX fixes

  • Pre-existing bug fix: ContentDialog.ShowAsync threw "This element does not have a XamlRoot" on every delete confirmation triggered from a ViewModel. New IDialogQueueService.ConfirmAsync + lazy XamlRootAccessor delegate resolves Window.Content.XamlRoot just-in-time on the UI thread; the duplicated factory pattern is removed from 5 call sites and the crash is fixed.
  • Pre-existing UX bug fix: importing an empty / unrecognised file silently advanced to the merge dialog and then showed "Import completed" with zero changes. SettingsViewModel.ImportAsync now surfaces an explicit error when the parsed vault is empty.

CSV importer — NordPass support

  • Accept note (singular) as alias for the Notes column header.
  • Detect a type column and skip non-password rows so card / identity / note labels do not pollute the imported password list (richer cross-type import will land in a later cluster).
  • Two new unit tests cover the NordPass-shape header and the singular-note alias.

Test plan

  • dotnet test → 178 / 178 (was 176; +2 NordPass cases)
  • Local build via scripts/build-installer.ps1 produces both PassKey-Setup-x64.exe and PassKey-Portable-x64.zip
  • User-facing smoke test: delete confirmation works on all 4 entry types; NordPass CSV with 238 password rows imports cleanly; lock/unlock + add/edit cycles show no regressions

…t fix

Code quality / modernization (Cluster 1 of PassKey 2.0):
- Extract BaseDetailViewModel<TEntry> with template-method pattern; the four
  Detail VMs (Password, CreditCard, Identity, SecureNote) now inherit shared
  add/edit/save/delete plumbing (~200 LOC of duplication removed)
- Introduce IVaultEntry interface as the generic constraint for BaseDetailViewModel
- Replace Task.Run(async () => await …) anti-pattern in LoginVM/SetupVM with
  Task.Run(Func<Task>) so the KDF/decrypt work stays off the UI thread without
  the nested async-over-async wrapper
- Implement IDisposable on MainVM and the four List VMs; named handlers replace
  closures so VaultLocked / NavigationStack.CurrentChanged can be unsubscribed,
  and Entries collections are cleared on vault lock to avoid retaining decrypted
  data in memory after lock
- Extract Maestro/Visa/Amex/Discover/Diners BIN prefixes in CardTypeDetector to
  a dedicated BinPrefixes static class with explicit named constants

Dialog / UX fixes:
- Pre-existing bug: ContentDialog.ShowAsync threw "This element does not have a
  XamlRoot" on every delete confirmation triggered from a ViewModel (Detail and
  List). Introduce IDialogQueueService.ConfirmAsync + a lazy XamlRootAccessor
  delegate that resolves Window.Content.XamlRoot just-in-time on the UI thread,
  removing the duplicated factory pattern at five call sites and fixing the crash
- Pre-existing UX bug: importing an empty / unrecognised file silently advanced
  to the merge dialog and then showed "Import completed" with zero changes.
  SettingsViewModel.ImportAsync now surfaces an explicit "no recognisable entries"
  error when the parsed vault is empty

CSV importer (NordPass support):
- Add "note" (singular) as alias for the Notes column header
- Detect a "type" column and skip non-password rows so card / identity / note
  labels do not pollute the imported password list (proper cross-type import is
  tracked for a future cluster)
- Two new unit tests cover the NordPass-shape header and the singular-note alias

Test suite: 178 passed (was 176).

Verified locally: build-installer.ps1 produces both PassKey-Setup-x64.exe and
PassKey-Portable-x64.zip; user-facing smoke test (delete on all four entry types,
import of a real NordPass CSV with 238 password rows, lock/unlock cycle) confirms
no regressions and that the previously broken delete + import flows now work.
@pexatar pexatar merged commit ec4beba into main May 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant