Skip to content

feat(kiosk): point donors to the card reader with a per-tablet NFC hint - #6

Merged
osayed0001 merged 1 commit into
OpenMasjid-Solutions:devfrom
SyButter:feat/nfc-tap-guidance
Aug 9, 2026
Merged

feat(kiosk): point donors to the card reader with a per-tablet NFC hint#6
osayed0001 merged 1 commit into
OpenMasjid-Solutions:devfrom
SyButter:feat/nfc-tap-guidance

Conversation

@SyButter

@SyButter SyButter commented Aug 8, 2026

Copy link
Copy Markdown

During the card step the kiosk can now show a pulsing contactless (NFC) symbol with arrows marching toward it, on the side where the reader is mounted, turning green when the payment clears.

  • New per-device setting nfcSide (off/left/right), set in Admin → Devices → "Reader side". Default 'off' — existing kiosks unchanged. Wired end to end exactly like the existing per-device orientation: server device column + setter + zod route + per-device injection into getKioskConfig; web api.ts + devices.tsx control; Android KioskConfig + DataStore + JSON parse.
  • New NfcReaderHint composable (Compose Canvas): contactless waves + a marching-chevron arrow group, aligned to the logical left/right edge. It lives inside RotatedRoot, so left/right follow the mount and become top/bottom on a portrait tablet. Pulses on Card/Processing; SuccessDark green with arrows fading on Thanks (cleared).

Adds a store test covering nfcSide sanitisation + per-device delivery.

During the card step the kiosk can now show a pulsing contactless (NFC)
symbol with arrows marching toward it, on the side where the reader is
mounted, turning green when the payment clears.

- New per-device setting `nfcSide` (off/left/right), set in Admin →
  Devices → "Reader side". Default 'off' — existing kiosks unchanged.
  Wired end to end exactly like the existing per-device `orientation`:
  server device column + setter + zod route + per-device injection into
  getKioskConfig; web api.ts + devices.tsx control; Android KioskConfig +
  DataStore + JSON parse.
- New NfcReaderHint composable (Compose Canvas): contactless waves + a
  marching-chevron arrow group, aligned to the logical left/right edge.
  It lives inside RotatedRoot, so left/right follow the mount and become
  top/bottom on a portrait tablet. Pulses on Card/Processing; SuccessDark
  green with arrows fading on Thanks (cleared).

Adds a store test covering nfcSide sanitisation + per-device delivery.
@osayed0001
osayed0001 merged commit 690aeb6 into OpenMasjid-Solutions:dev Aug 9, 2026
1 check passed
hasan-ismail added a commit that referenced this pull request Aug 9, 2026
…#6 build

PR #6 (the per-tablet NFC reader hint) failed :app:compileReleaseKotlin with 8 errors, so
the APK job failed and the image job was SKIPPED — nothing published, and dev stayed on
dev.6. All 8 errors are one missing import.

`animateFloat` is an EXTENSION on InfiniteTransition (androidx.compose.animation.core,
file class InfiniteTransitionKt), so Kotlin needs it imported by name. The file imported
`animateFloatAsState` — a different, top-level function — which is why line 127 compiled
while lines 82 and 121 did not. Easy to miss because this is the first
rememberInfiniteTransition in the codebase, so there was no import block to copy.

The other 6 errors were cascade, which is why the log looked worse than the bug:
  82/121  Unresolved reference 'animateFloat'
  85/124  Cannot infer type          → the animationSpec lambda, from the failed call
  145     Overload resolution ambiguity → `phase` had an error type, so sin() couldn't
                                          choose between sin(Double) and sin(Float)
  147     Double where Float expected   → `wave`/`a` fell out of the Double branch above
All of them resolve once `phase` and `pulse` are State<Float> again.

Verified rather than assumed, since Android can't be compiled on this machine: pulled
animation-core-android-1.7.3 (resolved from composeBom 2024.09.03) from Google Maven and
read the signature out of the artifact —

  public static final State<Float> animateFloat(InfiniteTransition, float initialValue,
      float targetValue, InfiniteRepeatableSpec<Float> animationSpec, String label, ...)

— which matches both call sites exactly, and confirmed every other symbol the new file
touches resolves (SuccessDark, RotatedRoot, GivingStep.Card/Processing/Thanks, and the
BoxScope receiver at the GivingHome call site, which sits in the same Box that does
.align(Alignment.TopEnd) below it).

Also checked the half of PR #6 that CI never reached, because a skipped image job means
the server and web changes were merged with ZERO validation: server tsc clean, 120/120
tests pass (its nfcSide assertions included), web tsc + build clean, nfcSide traced end to
end (store → kiosk config → Models → DeviceStore → KioskApi → GivingHome).

This PR was clean where the last two were not — no main merge, compose line correct,
CHANGELOG prepended without destroying history.

Bumped to 0.11.0-dev.7 so the NFC hint is actually installable; dev.6 never carried it.
hasan-ismail added a commit that referenced this pull request Aug 10, 2026
… be installed

PR #7 built green, but green is not the same as shipped. It changed only NfcHint.kt and
left every version field alone, so the build republished the SAME tag, :0.11.0-dev.7,
with different content. Two consequences, both silent:

1. NOBODY CAN GET IT. OpenMasjidOS decides an app has an update by comparing the catalog's
   `version:` with the installed one. The catalog still said 0.11.0-dev.7 because the
   manifest still said 0.11.0-dev.7, so a box already on dev.7 sees no change, is never
   notified, and has nothing to install. This is exactly the failure mode CLAUDE.md rule 6b
   exists to prevent — a dev build that reuses a version is undetectable.

2. THE TAG MUTATED. The dev channel's whole integrity story is that :X.Y.Z-dev.N is an
   exact, immutable tag (that is what lets the compose pin a bare tag instead of a digest).
   Verified it moved:

     :0.11.0-dev.7 after PR #6's fix   sha256:82f00a87d0ead9a03b0cb261064f09bfe81d9ed…
     :0.11.0-dev.7 now                 sha256:d609a187272f20e84569a9ed95993ff1073f6f48…

   So "dev.7" now names two different builds depending on when you pulled. A box that
   installed the first one reports dev.7 and is right; the registry says dev.7 and is also
   right; they are different software. Worth knowing if anything odd shows up on a box that
   updated in that window.

The CI channel guard can't catch this: it checks the compose tag MATCHES manifest, and
both were consistently dev.7. Consistent-but-stale passes. Nothing in the pipeline compares
the version against what dev last published, which is why this needs a human or a habit
rather than a check. Cheapest fix is the habit in rule 6b: the version bump belongs in the
same push as the change it ships.

dev.8 is content-identical to the mutated dev.7 plus the version fields, so this is purely
about making PR #7 reachable. No changelog entry: the Unreleased section already describes
the NFC hint, and this is a size tweak to a feature that has never shipped.

Verified: server tsc clean, 120/120 tests, web tsc + build clean.
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.

2 participants