Single source of truth: `com.tt.honeyDue.testing.GalleryScreens` lists every user-reachable screen with its category (DataCarrying / DataFree) and per-platform reachability. Both platforms' test harnesses are CI-gated against it — `GalleryManifestParityTest` on each side fails if the surface list drifts from the manifest. Variant matrix by category: DataCarrying captures 4 PNGs (empty/populated × light/dark), DataFree captures 2 (light/dark only). Empty variants for DataCarrying use `FixtureDataManager.empty(seedLookups = false)` so form screens that only read DM lookups can diff against populated. Detail-screen rendering fixed on both platforms. Root cause: VM `stateIn(Eagerly, initialValue = …)` closures evaluated `_selectedX.value` before screen-side `LaunchedEffect` / `.onAppear` could set the id, leaving populated captures byte-identical to empty. Kotlin: `ContractorViewModel` + `DocumentViewModel` accept `initialSelectedX: Int? = null` so the id is set in the primary constructor before `stateIn` computes its seed. Swift: `ContractorViewModel`, `DocumentViewModelWrapper`, `ResidenceViewModel`, `OnboardingTasksViewModel` gained pre-seed init params. `ContractorDetailView`, `DocumentDetailView`, `ResidenceDetailView`, `OnboardingFirstTaskContent` gained test/preview init overloads that accept the pre-seeded VM. Corresponding view bodies prefer cached success state over loading/error — avoids a spinner flashing over already-visible content during background refreshes (production benefit too). Real production bug fixed along the way: `DataManager.clear()` was missing `_contractorDetail`, `_documentDetail`, `_contractorsByResidence`, `_taskCompletions`, `_notificationPreferences`. On logout these maps leaked across user sessions; in the gallery they leaked the previous surface's populated state into the next surface's empty capture. `ImagePicker.android.kt` guards `rememberCameraPicker` with `LocalInspectionMode` — `FileProvider.getUriForFile` can't resolve the Robolectric test-cache path, so `add_document` / `edit_document` previously failed the entire capture. Honest reclassifications: `complete_task`, `manage_users`, and `task_suggestions` moved to DataFree. Their first-paint visible state is driven by static props or APILayer calls, not by anything on `IDataManager` — populated would be byte-identical to empty without a significant production rewire. The manifest comments call this out. Manifest counts after all moves: 43 screens = 12 DataCarrying + 31 DataFree, 37 on both platforms + 3 Android-only (home, documents, biometric_lock) + 3 iOS-only (documents_warranties, add_task, profile_edit). Test results after full record: Android: 11/11 DataCarrying diff populated vs empty iOS: 12/12 DataCarrying diff populated vs empty Also in this change: - `scripts/build_parity_gallery.py` parses the Kotlin manifest directly, renders rows in product-flow order, shows explicit `[missing — <platform>]` placeholders for expected-but-absent captures and muted `not on <platform>` placeholders for platform-specific screens. Docs regenerated. - `scripts/cleanup_orphan_goldens.sh` safely removes PNGs from prior test configurations (theme-named, compare artifacts, legacy empty/populated pairs for what is now DataFree). Dry-run by default. - `docs/parity-gallery.md` rewritten: canonical-manifest workflow, adding-a-screen guide, variant matrix explained. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
554 lines
30 KiB
Markdown
554 lines
30 KiB
Markdown
# honeyDue parity gallery
|
|
|
|
*43 screens · 102 Android · 174 iOS*
|
|
|
|
Auto-generated by `scripts/build_parity_gallery.py` — do not hand-edit.
|
|
|
|
See [parity-gallery.md](parity-gallery.md) for the workflow guide.
|
|
|
|
## Screens
|
|
|
|
- [login](#login) *(DataFree)*
|
|
- [register](#register) *(DataFree)*
|
|
- [forgot_password](#forgot-password) *(DataFree)*
|
|
- [verify_reset_code](#verify-reset-code) *(DataFree)*
|
|
- [reset_password](#reset-password) *(DataFree)*
|
|
- [verify_email](#verify-email) *(DataFree)*
|
|
- [onboarding_welcome](#onboarding-welcome) *(DataFree)*
|
|
- [onboarding_value_props](#onboarding-value-props) *(DataFree)*
|
|
- [onboarding_create_account](#onboarding-create-account) *(DataFree)*
|
|
- [onboarding_verify_email](#onboarding-verify-email) *(DataFree)*
|
|
- [onboarding_location](#onboarding-location) *(DataFree)*
|
|
- [onboarding_name_residence](#onboarding-name-residence) *(DataFree)*
|
|
- [onboarding_home_profile](#onboarding-home-profile) *(DataFree)*
|
|
- [onboarding_join_residence](#onboarding-join-residence) *(DataFree)*
|
|
- [onboarding_first_task](#onboarding-first-task) *(DataCarrying)*
|
|
- [onboarding_subscription](#onboarding-subscription) *(DataFree)*
|
|
- [home](#home) *(DataCarrying)* — *Android-only*
|
|
- [residences](#residences) *(DataCarrying)*
|
|
- [residence_detail](#residence-detail) *(DataCarrying)*
|
|
- [add_residence](#add-residence) *(DataFree)*
|
|
- [edit_residence](#edit-residence) *(DataFree)*
|
|
- [join_residence](#join-residence) *(DataFree)*
|
|
- [manage_users](#manage-users) *(DataFree)*
|
|
- [all_tasks](#all-tasks) *(DataCarrying)*
|
|
- [add_task](#add-task) *(DataFree)* — *iOS-only*
|
|
- [add_task_with_residence](#add-task-with-residence) *(DataFree)*
|
|
- [edit_task](#edit-task) *(DataFree)*
|
|
- [complete_task](#complete-task) *(DataFree)*
|
|
- [task_suggestions](#task-suggestions) *(DataFree)*
|
|
- [task_templates_browser](#task-templates-browser) *(DataCarrying)*
|
|
- [contractors](#contractors) *(DataCarrying)*
|
|
- [contractor_detail](#contractor-detail) *(DataCarrying)*
|
|
- [documents](#documents) *(DataCarrying)* — *Android-only*
|
|
- [documents_warranties](#documents-warranties) *(DataCarrying)* — *iOS-only*
|
|
- [document_detail](#document-detail) *(DataCarrying)*
|
|
- [add_document](#add-document) *(DataFree)*
|
|
- [edit_document](#edit-document) *(DataFree)*
|
|
- [profile](#profile) *(DataCarrying)*
|
|
- [profile_edit](#profile-edit) *(DataFree)* — *iOS-only*
|
|
- [notification_preferences](#notification-preferences) *(DataFree)*
|
|
- [theme_selection](#theme-selection) *(DataFree)*
|
|
- [biometric_lock](#biometric-lock) *(DataFree)* — *Android-only*
|
|
- [feature_comparison](#feature-comparison) *(DataFree)*
|
|
|
|
---
|
|
|
|
## login *(DataFree)*<a id='login'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## register *(DataFree)*<a id='register'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## forgot_password *(DataFree)*<a id='forgot-password'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## verify_reset_code *(DataFree)*<a id='verify-reset-code'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## reset_password *(DataFree)*<a id='reset-password'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## verify_email *(DataFree)*<a id='verify-email'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_welcome *(DataFree)*<a id='onboarding-welcome'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_value_props *(DataFree)*<a id='onboarding-value-props'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_create_account *(DataFree)*<a id='onboarding-create-account'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_verify_email *(DataFree)*<a id='onboarding-verify-email'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_location *(DataFree)*<a id='onboarding-location'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_name_residence *(DataFree)*<a id='onboarding-name-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_home_profile *(DataFree)*<a id='onboarding-home-profile'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_join_residence *(DataFree)*<a id='onboarding-join-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_first_task *(DataCarrying)*<a id='onboarding-first-task'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## onboarding_subscription *(DataFree)*<a id='onboarding-subscription'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## home *(DataCarrying)*<a id='home'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  | _(not on ios)_ |
|
|
| **empty / dark** |  | _(not on ios)_ |
|
|
| **populated / light** |  | _(not on ios)_ |
|
|
| **populated / dark** |  | _(not on ios)_ |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## residences *(DataCarrying)*<a id='residences'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## residence_detail *(DataCarrying)*<a id='residence-detail'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## add_residence *(DataFree)*<a id='add-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## edit_residence *(DataFree)*<a id='edit-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## join_residence *(DataFree)*<a id='join-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## manage_users *(DataFree)*<a id='manage-users'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  | _\[missing — ios\]_ |
|
|
| **dark** |  | _\[missing — ios\]_ |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## all_tasks *(DataCarrying)*<a id='all-tasks'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## add_task *(DataFree)*<a id='add-task'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** | _(not on android)_ |  |
|
|
| **dark** | _(not on android)_ |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## add_task_with_residence *(DataFree)*<a id='add-task-with-residence'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## edit_task *(DataFree)*<a id='edit-task'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## complete_task *(DataFree)*<a id='complete-task'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## task_suggestions *(DataFree)*<a id='task-suggestions'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  | _\[missing — ios\]_ |
|
|
| **dark** |  | _\[missing — ios\]_ |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## task_templates_browser *(DataCarrying)*<a id='task-templates-browser'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## contractors *(DataCarrying)*<a id='contractors'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## contractor_detail *(DataCarrying)*<a id='contractor-detail'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## documents *(DataCarrying)*<a id='documents'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  | _(not on ios)_ |
|
|
| **empty / dark** |  | _(not on ios)_ |
|
|
| **populated / light** |  | _(not on ios)_ |
|
|
| **populated / dark** |  | _(not on ios)_ |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## documents_warranties *(DataCarrying)*<a id='documents-warranties'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** | _(not on android)_ |  |
|
|
| **empty / dark** | _(not on android)_ |  |
|
|
| **populated / light** | _(not on android)_ |  |
|
|
| **populated / dark** | _(not on android)_ |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## document_detail *(DataCarrying)*<a id='document-detail'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## add_document *(DataFree)*<a id='add-document'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## edit_document *(DataFree)*<a id='edit-document'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## profile *(DataCarrying)*<a id='profile'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **empty / light** |  |  |
|
|
| **empty / dark** |  |  |
|
|
| **populated / light** |  |  |
|
|
| **populated / dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## profile_edit *(DataFree)*<a id='profile-edit'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** | _(not on android)_ |  |
|
|
| **dark** | _(not on android)_ |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## notification_preferences *(DataFree)*<a id='notification-preferences'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## theme_selection *(DataFree)*<a id='theme-selection'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## biometric_lock *(DataFree)*<a id='biometric-lock'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  | _(not on ios)_ |
|
|
| **dark** |  | _(not on ios)_ |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|
|
## feature_comparison *(DataFree)*<a id='feature-comparison'></a>
|
|
|
|
| State / Mode | Android | iOS |
|
|
|---|---|---|
|
|
| **light** |  |  |
|
|
| **dark** |  |  |
|
|
|
|
[top](#honeydue-parity-gallery)
|
|
|
|
---
|
|
|