vineroute// field manual
Rider
Driver
Dispatch
Systems
The Rider/OTP entry
02 / 08

OTP entry

Six boxes, one dev shortcut

AuthOTPDev Tools

Overview

Six `TextInput` boxes laid out edge-to-edge with `keyboardType` set to number-pad. Each box auto-advances focus to the next on input, retreats on backspace when empty, and the cluster supports paste — if the user pastes a six-digit code on box 1, it distributes across all six. Below the inputs a DEV CODE pill shows the dev code. The RESEND CODE button starts at `RESEND IN 15S` and ticks down to active state. The VERIFY pill at the bottom is muted (`#C8C9BA` with `#7F806F` text) until six digits land, then flips to the sage primary.

How it works

1

The screen receives the email via expo-router search params and the API's expiresAt and devCode from the previous step's response.

2

Each digit input is a controlled `TextInput` in an array of six refs. `onChangeText` updates state and calls `ref.current?.focus()` on the next box if the input length is 1.

3

Paste handling: when a TextInput's `onChangeText` receives a string longer than 1 character, we slice it into individual digits, fill the cluster, and focus the last filled box.

4

Backspace handling: `onKeyPress` listens for `Backspace` — if the current box is empty, focus retreats to the previous box.

5

On VERIFY we call `POST /auth/otp/verify` with the email and code. Success returns a JWT plus user record; we persist via expo-secure-store and call `setSession()` from our auth context.

6

Role-based routing happens inside the auth context's `setSession` — `user.role === 'driver'` routes to `/(driver)`, anything else to `/(passenger)`.

Key decisions

Dev code as a visible pill, not a console.log

QA testers, Maestro flows, designers reviewing builds — none of them want to grep server logs for the OTP. We bake the code into the response in dev mode and render it on the screen. The dev-mode gate is a build-time env var, so production bundles literally do not contain the rendering branch.

Six separate inputs, not a single field

A single TextInput with character-level styling is fragile across iOS, Android, and software keyboards. Six discrete inputs give us deterministic focus, paste, and styling. The cost is more JSX; the win is a flow that works on every keyboard we've tested.

Auto-verify is off

We considered firing the verify call as soon as the sixth digit lands. We don't, because a paste with a transient typo would auto-fail. The user taps VERIFY when they're ready. This is a few hundred ms slower but eliminates a class of false-failure UX.

OTP entry

Quick Reference

Routeapps/mobile/app/(auth)/sign-in/code.tsx
StoreLocal state + AuthContext.setSession
APIsPOST /auth/otp/verify
Components
PrimaryButtonOtpInputexpo-secure-store
PreviousSign inNextToday