99 lines
5.4 KiB
Markdown
99 lines
5.4 KiB
Markdown
# Identity photo preview correction
|
|
|
|
Task [#69](https://git.sendico.io/tech/sxchnge/issues/69), branch `kyc-69`.
|
|
Delivery: [PR #71](https://git.sendico.io/tech/sxchnge/pulls/71).
|
|
This frontend change builds on the [identity verification journey](kyc.md)
|
|
and is independent of the birthday controls in task #67.
|
|
|
|
## Cause and correction
|
|
|
|
A synthetic valid PNG reproduced this Flutter 3.47.7 browser error:
|
|
|
|
```text
|
|
TypeError: Instance of '_UnmodifiableUint8ListView': type
|
|
'_UnmodifiableUint8ListView' is not a subtype of type 'NativeUint8List'
|
|
```
|
|
|
|
The stack reached `Uint8ListToJSUint8Array.toJS` from CanvasKit's browser
|
|
image decoder through `MemoryImage` and `ResizeImage`. File selection had
|
|
successfully decoded the original typed bytes. The `Photo` constructor then
|
|
copied them into an unmodifiable view, which was passed directly into the
|
|
preview's web decoder. Native decoding accepted that view, hiding the bug in
|
|
native-only tests. The generic image error builder presented the failure as a
|
|
damaged document even though the selected bytes were valid.
|
|
|
|
`PhotoPreview` now creates a native `Uint8List` copy for `MemoryImage` once per
|
|
selected photo. The shared domain model stays immutable, and validation,
|
|
providers, services and transport contracts remain unchanged. The preview still
|
|
requests a decode width of at most 1000 pixels and evicts its decoded cache entry
|
|
on replacement or disposal. The extra encoded-byte copy belongs to the preview
|
|
and is released with its provider/cache references; no secure-erasure guarantee
|
|
is made.
|
|
|
|
An actual preview failure now says that the preview could not be displayed and
|
|
suggests selecting the photo again. The English/Russian fallback keeps at least
|
|
the preview's usual height, can grow for long text, and has a separate live-region
|
|
semantic node. Existing replacement/removal controls stay available and selected
|
|
input is retained. This is distinct from validation's corrupt-file message.
|
|
|
|
## UX and measurement review
|
|
|
|
The outcome remains selecting readable ID and selfie photos, checking both on
|
|
review, and submitting for confirmed service acceptance. Existing KYC journey
|
|
attempts, retries, failure, submission and abandonment signals are reused. No
|
|
control-level events, image bytes, filenames, form values or raw exceptions are
|
|
added to telemetry. A selection success measures validation; it does not establish
|
|
that pixels were painted. Preview-only rendering failures are not separately
|
|
counted by existing telemetry. Server review/approval measurement and native
|
|
screen-reader QA retain the limitations documented in the original journey.
|
|
|
|
Regression tests use synthetic JPEG/PNG content only. They start and await real
|
|
image decoding outside the widget test's fake async zone, inspect the actual
|
|
rendered images, and surface stream errors so an error fallback cannot conceal a
|
|
failure. Checks cover portrait/landscape decoding, model immutability, bounded
|
|
decode width, stable provider reuse, replacement/removal cache eviction, both
|
|
review slots, returning to review, localized fallback/replacement, a 320px view
|
|
at 1.6 text scale, and error announcements isolated from surrounding instructions.
|
|
The `image` package is already a KYC runtime dependency; its direct app test
|
|
dependency is used only to create synthetic fixtures in memory.
|
|
|
|
## Validation and delivery
|
|
|
|
The original browser regression failed before the fix while its native equivalent
|
|
passed. The complete task diff was reviewed against frontend architecture, UX,
|
|
measurement, lifecycle and privacy requirements. Final validation passed:
|
|
|
|
- In `frontend/app`, `dart format --output=none --set-exit-if-changed
|
|
lib/kyc/preview.dart test/preview_test.dart`: two files, no changes.
|
|
- In `frontend/app`, `flutter analyze --no-pub`: no issues.
|
|
- In `frontend/app`, `flutter test --platform chrome test/preview_test.dart
|
|
--reporter expanded --no-pub --verbose`: all six regression tests passed.
|
|
- At the repository root, `make test-frontend`: all 228 tests passed across
|
|
the shared packages, theme example and app, including the six native preview
|
|
regressions and existing KYC journey/analytics tests.
|
|
- In `frontend/app`, `flutter build web --no-pub`: release build and Wasm
|
|
compatibility dry run succeeded.
|
|
|
|
Browser regression coverage uses Chrome; Safari and native assistive-technology
|
|
testing remain release checks. Generated localization and build code stays
|
|
ignored; the tracked app lockfile changes only `image` from transitive to direct
|
|
test dependency. The separate birthday worktree was preserved.
|
|
|
|
## Follow-up contribution review
|
|
|
|
The complete seven-file contribution was reviewed again before the authorized
|
|
merge and frontend rollout. No additional defects were found in the byte-copy
|
|
boundary, provider reuse, cache disposal, error semantics/localization, privacy,
|
|
architecture or regression coverage. No source changes were needed.
|
|
|
|
Current `main` (`ec4115d`, including the birthday, signup-delivery and account
|
|
setup changes) was merged into `kyc-69` before final verification. The two-file
|
|
format check and app analyzer passed; all six Chrome preview regressions and
|
|
all 240 tests in `make test-frontend` passed on the combined code. The original
|
|
228-test result above describes the earlier isolated branch.
|
|
|
|
The authorized rollout rebuilds only `frontend` from clean, pulled `main`, using
|
|
the repository's fresh stable-release resolver and digest-pinned local inputs.
|
|
Runtime image, HTTPS and served-bundle verification are recorded in task #69
|
|
after the rollout; generated local pins remain ignored.
|