feat(network): NetworkBanner + a11y motion guards + empty-state copy#52
Merged
Merged
Conversation
This was referenced May 15, 2026
epicexcelsior
added a commit
to epicexcelsior/anon0mesh
that referenced
this pull request
May 16, 2026
Two of the largest infinite Animated.loop / withRepeat(-1) sites that were deferred from PR anonmesh#52. Adds `useReducedMotion()` guards using the same pattern as PulseDot / Skeleton / NetworkStatusBadge / PigeonLoader. - MeshMap: the "me" node pulse halo + ghost skeleton breathe both honor reduce-motion. Pinned to mid-frame opacity so the indicators still read but don't loop. No structural changes. - RadarScan: rotation sweep pinned at 0deg + center dot held at resting scale under reduce-motion. Static ring guides still convey state. Both visual-only — happy path unchanged. Per ROADMAP Tier 4.1 (a11y motion audit).
epicexcelsior
added a commit
that referenced
this pull request
May 16, 2026
* chore(preview): wrap dead Yield/Swap/Send panel controls in PreviewedActions These three wallet panels are not exported from `components/wallet/index.ts` today (per PR #47) but the source still ships in the bundle and contains live Pressables that look real. Wrap the dead CTAs in <PreviewedActions> and add a <PreviewBadge label="…coming soon"> above each panel so that if anything ever does render them, the affordance reads as preview-only. - YieldPanel: deposit/withdraw vault buttons wrapped (the expand/collapse Pressable is purely local UI state, left alone). - SwapPanel: SWAP + "new order" CTAs wrapped (flip-asset Pressable left). - SendPanel: "send privately" + "new transfer" CTAs wrapped (asset picker and MAX shortcut left — local state only). Per AUDIT A6 / ROADMAP § 0.A.8. * feat(a11y): honor reduce-motion on MeshMap + RadarScan loops Two of the largest infinite Animated.loop / withRepeat(-1) sites that were deferred from PR #52. Adds `useReducedMotion()` guards using the same pattern as PulseDot / Skeleton / NetworkStatusBadge / PigeonLoader. - MeshMap: the "me" node pulse halo + ghost skeleton breathe both honor reduce-motion. Pinned to mid-frame opacity so the indicators still read but don't loop. No structural changes. - RadarScan: rotation sweep pinned at 0deg + center dot held at resting scale under reduce-motion. Static ring guides still convey state. Both visual-only — happy path unchanged. Per ROADMAP Tier 4.1 (a11y motion audit). * docs(release): pre-release smoke-test playbook (ROADMAP Tier 1.2) Adds `docs/SMOKE_TEST.md` — the 5-minute manual gate that runs before every tester APK release. Seven numbered steps with explicit pass/fail criteria, each linked to an AUDIT / ROADMAP ID for traceability. Steps: 1. Cold install + onboarding 2. Local wallet generation 3. DEVNET banner visible (Tier 0.5 / B1) 4. Send 0.001 SOL to self (PR #45 confirmation) 5. Nodes screen honesty (no fixture data) 6. Seed reveal biometric gate 7. Airplane-mode toggle Adapted from § 10 of LOCAL_NOTES/REVIEW-2026-05-13/raw-tracks/ OFFGRID_FALLBACK_AUDIT.md ("Off-grid testing playbook") for the team's actual release workflow. Includes a sign-off table. * chore(diagnostics): warn on 3 silent-failure sites (TxDetailModal + MeshRpcAdapter) Drops overlap w/ #53 (ReceivePanel + LxmfContext warns) — #53 owns those. Per AUDIT § 3 silent failure inventory.
b4155a0 to
8445ab8
Compare
Audit per Tier 4.1 -- the app had 7 components running infinite Animated.loop
or reanimated withRepeat(-1) cycles with no reduce-motion guard. iOS/Android
"Reduce Motion" users get vestibular discomfort from looping breathing /
pulse / shimmer effects. This pass wraps the five most visible:
- PulseDot -- header presence dots (every screen with PulseDot)
- Skeleton -- every list/card placeholder during loads
- NetworkStatusBadge -- wallet-tab mesh/isolated presence pulse
- PigeonLoader -- send-flow breathing scale on the pigeon sprite
- LoadingOverlay -- onboarding caret blink + 3-dot stagger
Pattern: `useReducedMotion()` from react-native-reanimated (re-uses the
same AccessibilityInfo subscription under the hood, works alongside the
RN Animated API the older components use). When reduced motion is on we
short-circuit before starting the loop and pin the relevant value to a
static state that still reads correctly with a screen reader.
Remaining loopers tracked for a follow-up: components/nodes/MeshMap.tsx
and components/settings/RadarScan.tsx (less above-the-fold, larger surface
area to retest, deferred to keep the scope tight).
Roadmap: Tier 4.1 ("a11y motion audit").
Tier 4.2 sweep -- bland "No X / X yet" labels replaced with copy that
points the user at the next step they can take.
- app/receive.tsx (no-wallet receive panel)
"Connect wallet to receive" → "Set up your wallet to share an address"
Subtitle now routes users to the home tab instead of restating the
problem.
- components/home/NearbyPeersCard.tsx (wallet-tab home card)
"No nearby peers yet" → "Tap to scan for nearby peers"
"Mesh offline" → "Mesh offline -- tap to start"
The card already navigates to the Nodes tab on press; the label now
advertises that affordance instead of stating an absence.
- components/messages/PeersDrawer.tsx (conversation list empty state)
"No conversations yet" → "Scan a contact QR to start your first conversation"
"No matches" → "No matches -- paste an LXMF hash or scan a QR"
- components/messages/GroupMembersSheet.tsx (empty channel roster)
Reframed from passive ("Members appear here once they send a message")
to active ("Share the channel invite -- members appear here after their
first message").
5 strings touched. Larger UX rewrite (replace inbox-only empty states
with full first-run guidance) tracked separately as messages-first
onboarding work.
Roadmap: Tier 4.2 ("empty-state copy audit").
Follow-on to the Tier 4.1 audit -- NoPeersScreen runs three offset
withRepeat sonar rings as the visual "scanning" affordance whenever a
user has zero peers. Under iOS/Android "Reduce Motion" we now short-
circuit before scheduling any of the three loops and pin all three ring
shared values to 0, so only the static center icon + "SCANNING FOR
PEERS" label render. Same information, no vestibular motion.
Roadmap: Tier 4.1 ("a11y motion audit").
8445ab8 to
957b2dd
Compare
epicexcelsior
added a commit
that referenced
this pull request
May 16, 2026
* chore(preview): wrap dead Yield/Swap/Send panel controls in PreviewedActions These three wallet panels are not exported from `components/wallet/index.ts` today (per PR #47) but the source still ships in the bundle and contains live Pressables that look real. Wrap the dead CTAs in <PreviewedActions> and add a <PreviewBadge label="…coming soon"> above each panel so that if anything ever does render them, the affordance reads as preview-only. - YieldPanel: deposit/withdraw vault buttons wrapped (the expand/collapse Pressable is purely local UI state, left alone). - SwapPanel: SWAP + "new order" CTAs wrapped (flip-asset Pressable left). - SendPanel: "send privately" + "new transfer" CTAs wrapped (asset picker and MAX shortcut left — local state only). Per AUDIT A6 / ROADMAP § 0.A.8. * feat(a11y): honor reduce-motion on MeshMap + RadarScan loops Two of the largest infinite Animated.loop / withRepeat(-1) sites that were deferred from PR #52. Adds `useReducedMotion()` guards using the same pattern as PulseDot / Skeleton / NetworkStatusBadge / PigeonLoader. - MeshMap: the "me" node pulse halo + ghost skeleton breathe both honor reduce-motion. Pinned to mid-frame opacity so the indicators still read but don't loop. No structural changes. - RadarScan: rotation sweep pinned at 0deg + center dot held at resting scale under reduce-motion. Static ring guides still convey state. Both visual-only — happy path unchanged. Per ROADMAP Tier 4.1 (a11y motion audit). * docs(release): pre-release smoke-test playbook (ROADMAP Tier 1.2) Adds `docs/SMOKE_TEST.md` — the 5-minute manual gate that runs before every tester APK release. Seven numbered steps with explicit pass/fail criteria, each linked to an AUDIT / ROADMAP ID for traceability. Steps: 1. Cold install + onboarding 2. Local wallet generation 3. DEVNET banner visible (Tier 0.5 / B1) 4. Send 0.001 SOL to self (PR #45 confirmation) 5. Nodes screen honesty (no fixture data) 6. Seed reveal biometric gate 7. Airplane-mode toggle Adapted from § 10 of LOCAL_NOTES/REVIEW-2026-05-13/raw-tracks/ OFFGRID_FALLBACK_AUDIT.md ("Off-grid testing playbook") for the team's actual release workflow. Includes a sign-off table. * chore(diagnostics): warn on 3 silent-failure sites (TxDetailModal + MeshRpcAdapter) Drops overlap w/ #53 (ReceivePanel + LxmfContext warns) — #53 owns those. Per AUDIT § 3 silent failure inventory.
epicexcelsior
added a commit
that referenced
this pull request
May 16, 2026
…52) * feat(a11y): honor reduce-motion in top 5 looping animations Audit per Tier 4.1 -- the app had 7 components running infinite Animated.loop or reanimated withRepeat(-1) cycles with no reduce-motion guard. iOS/Android "Reduce Motion" users get vestibular discomfort from looping breathing / pulse / shimmer effects. This pass wraps the five most visible: - PulseDot -- header presence dots (every screen with PulseDot) - Skeleton -- every list/card placeholder during loads - NetworkStatusBadge -- wallet-tab mesh/isolated presence pulse - PigeonLoader -- send-flow breathing scale on the pigeon sprite - LoadingOverlay -- onboarding caret blink + 3-dot stagger Pattern: `useReducedMotion()` from react-native-reanimated (re-uses the same AccessibilityInfo subscription under the hood, works alongside the RN Animated API the older components use). When reduced motion is on we short-circuit before starting the loop and pin the relevant value to a static state that still reads correctly with a screen reader. Remaining loopers tracked for a follow-up: components/nodes/MeshMap.tsx and components/settings/RadarScan.tsx (less above-the-fold, larger surface area to retest, deferred to keep the scope tight). Roadmap: Tier 4.1 ("a11y motion audit"). * copy(empty-states): teach the next action, not "No X" Tier 4.2 sweep -- bland "No X / X yet" labels replaced with copy that points the user at the next step they can take. - app/receive.tsx (no-wallet receive panel) "Connect wallet to receive" → "Set up your wallet to share an address" Subtitle now routes users to the home tab instead of restating the problem. - components/home/NearbyPeersCard.tsx (wallet-tab home card) "No nearby peers yet" → "Tap to scan for nearby peers" "Mesh offline" → "Mesh offline -- tap to start" The card already navigates to the Nodes tab on press; the label now advertises that affordance instead of stating an absence. - components/messages/PeersDrawer.tsx (conversation list empty state) "No conversations yet" → "Scan a contact QR to start your first conversation" "No matches" → "No matches -- paste an LXMF hash or scan a QR" - components/messages/GroupMembersSheet.tsx (empty channel roster) Reframed from passive ("Members appear here once they send a message") to active ("Share the channel invite -- members appear here after their first message"). 5 strings touched. Larger UX rewrite (replace inbox-only empty states with full first-run guidance) tracked separately as messages-first onboarding work. Roadmap: Tier 4.2 ("empty-state copy audit"). * feat(a11y): honor reduce-motion on MessagesScreen no-peers sonar Follow-on to the Tier 4.1 audit -- NoPeersScreen runs three offset withRepeat sonar rings as the visual "scanning" affordance whenever a user has zero peers. Under iOS/Android "Reduce Motion" we now short- circuit before scheduling any of the three loops and pin all three ring shared values to 0, so only the static center icon + "SCANNING FOR PEERS" label render. Same information, no vestibular motion. Roadmap: Tier 4.1 ("a11y motion audit"). * fix(lint): escape apostrophe in receive empty-state copy --------- Co-authored-by: epicexcelsior <intern@anonme.sh>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
Roadmap B1 / Tier 0.5: above-the-fold devnet disclosure. Today the only signal that the app is on devnet is the explorer-link query string on the success screen, by which point a real-funds mistake is already on chain. Phantom users have no other cue.
Roadmap Tier 4.1: a11y motion audit. The audit flagged 15+ infinite Animated.loop / withRepeat(-1) cycles with zero reduce-motion guards. This PR knocks out the 6 most visible.
Roadmap Tier 4.2: empty-state copy audit. Bland labels reframed to point at the next action.
Changes
NetworkBanner
components/ui/NetworkBanner.tsx+detectCluster()helper.process.env.EXPO_PUBLIC_SOLANA_RPC(matchessrc/infrastructure/network/connection.ts), falls back to devnet.DEVNET / TESTNET . testnet funds only; neutralCUSTOM RPC . hostname; hidden on mainnet.accessibilityRole="alert"+accessibilityLiveRegion="polite"; pinned at AppShell root with absolute positioning + zIndex 1000 +pointerEvents="box-none".app/_layout.tsxcovers wallet tab, receive (transparent-modal), and onboarding splash.Reduce-motion guards (
useReducedMotionfrom react-native-reanimated)components/ui/PulseDot.tsxcomponents/ui/Skeleton.tsxcomponents/ui/PigeonLoader.tsxcomponents/wallet/NetworkStatusBadge.tsxcomponents/onboarding/LoadingOverlay.tsx(dots + caret)screens/MessagesScreen.tsx(NoPeersScreen sonar)Outstanding loops deferred for a follow-up:
components/nodes/MeshMap.tsx,components/settings/RadarScan.tsx.Copy
app/receive.tsxno-wallet panelcomponents/home/NearbyPeersCard.tsx(offline + no-peers labels)components/messages/PeersDrawer.tsx(no matches + no conversations)components/messages/GroupMembersSheet.tsx(empty channel roster)What did not ship
Test plan