Mobile · Expo / React Native

Mobile App

Folder intent

Project Structure

Path Role Why it exists
app/ React Native application All user-facing state, gestures, dialogs, preferences, and local UI features.
app/hooks/_useWorklet.ts Runtime bridge Starts the Bare bundle once and translates backend RPC messages into React state.
backend/ Embedded backend source Owns canonical list mutations, Autobase setup, Corestore, P2P join, and cleanup.
rpc-commands.mjs Shared command ABI Keeps frontend and backend in sync without string command names.
assets/data/ Grocery taxonomy source CSV inputs for category and translation generation.
scripts/ Build and generation helpers Bundles the Bare backend and generates multilingual grocery lookup files.
website/ Public marketing/static site Listam landing page, privacy page, and join link handling.
/wiki/ This study site (multi-app wiki) Human-readable architecture docs for all Listam apps; a sibling folder of listam-mobile.
Behavior

Runtime Flows

1

App boot

useWorklet starts a singleton Bare worklet, passing the Expo document directory as the backend base path. It also creates the frontend RPC client.

app/hooks/_useWorklet.ts:81
2

Backend boot

The backend acquires a lock file, loads any saved base/encryption keys, starts an RPC server, and initializes Autobase.

backend/backend.mjs:50
3

List mutation

Taps update React state optimistically and send RPC_UPDATE, RPC_ADD, or RPC_DELETE. The backend serializes writes through a promise chain before appending to Autobase.

backend/lib/item.mjs:8
4

Replication

Hyperswarm joins the Autobase discovery key as a topic. Peer connections are passed to autobase.replicate(conn).

backend/lib/network.mjs:404

Invite join sequence

  1. Frontend parses an invite code or listam.ch/join?invite=... link.
  2. Frontend sends RPC_JOIN_KEY to the worklet.
  3. Guest opens a temporary Hyperswarm/BlindPairing candidate using the invite.
  4. Host validates the invite, appends an add-writer operation, and confirms base credentials.
  5. Guest reinitializes Autobase with the host base key and encryption key.
  6. Guest replicates over the temporary connection, then waits until it becomes writable.
backend/lib/network.mjs:462
React Native layer

Frontend Functionality

App composition

app/index.tsx is the composition root. It wires useWorklet, useSubscription, join handling, preferences, list/grid rendering, loyalty cards, and paywall gating.

app/index.tsx:35

View modes

The standard list uses an animated inertial list with center-item scaling. The grid view groups grocery items into categories and renders image cards with haptic feedback.

intertial_scroll.tsx VisualGridList.tsx

Categories and icons

Category lookup uses generated multilingual maps, keyword fallback, substring matching, and Levenshtein typo tolerance. Icon lookup normalizes item text, translates to English, tries exact and partial matches, then falls back to letter icons.

categoryLookup.ts:129 itemIconMap.ts:1680
Bare worklet layer

Backend Internals

Mutable module state

backend/lib/state.mjs exports shared module-level variables and setters for Autobase, Corestore, swarm, discovery, RPC, keys, pairing, current list, and join flags. This is simple for an embedded singleton backend, but it depends on only one backend instance touching storage.

backend/lib/state.mjs

Singleton guard

The backend opens lista.lock with exclusive write mode. If another backend is already running, the new instance throws before it can touch storage.

backend/backend.mjs:58

Autobase initialization

initAutobase tears down prior resources, opens Corestore, clears stale boot/encryption metadata when joining a different base, enables encrypted values, rebuilds the list view, starts Hyperswarm, sets up blind pairing, and sends an invite to the frontend.

backend/lib/network.mjs:291

Apply function

The Autobase apply function is the reducer. It validates item payloads, writes a materialized JSON view, updates currentList, and emits backend-to- frontend RPC updates.

backend/backend.mjs:239
Persistence

Storage and Local State

Data Storage Owner Notes
Shopping list log and materialized view {document}/lista-local Bare backend Corestore + Autobase, encrypted when Autobase has an encryption key.
Autobase base key lista-autobase-key.txt Bare backend Plain hex file in app documents, loaded on restart.
Autobase encryption key lista-encryption-key.txt Bare backend Plain hex file in app documents, loaded only when a base key exists.
Invite lista-invite.json Bare backend Serialized by saveInvite; current code creates a fresh runtime invite.
UI preferences AsyncStorage keys like @lista_grid_view React Native app View mode, category switches, icon/list sizing, and icon variant.
Loyalty cards @lista_loyalty_cards React Native app JSON array containing card name, barcode/QR data, and type.
Trial start @lista_trial_start React Native app Local timestamp used for the 30-day trial calculation.
Dependency roles

Libraries

React Native / ExpoMobile shell, app lifecycle, camera, linking, haptics, file paths.
react-native-bare-kitEmbeds and starts the packed Bare backend inside the app process.
bare-rpcTyped-by-number IPC contract between frontend and worklet.
CorestoreLocal Hypercore storage used by Autobase.
AutobaseMulti-writer append log and deterministic materialized view.
HyperswarmPeer discovery and replication connections.
BlindPairingInvite flow that exchanges base and encryption credentials.
z32Shareable encoding for blind-pairing invites.
react-native-iapSubscription paywall, trial expiry, purchase and restore.
package.json:14
Maintainability

Conventions and Quality Notes

Conventions observed

  • Single quotes and no semicolon-heavy style in application files.
  • React state is colocated in the composition root for feature wiring.
  • Backend mutable singletons are centralized in backend/lib/state.mjs.
  • Generated lookup files are marked as auto-generated and sourced from CSV.
  • RPC command constants are numeric and shared from one file.

Quality gaps to track

  • No test files or test script were found.
  • package-lock.json is ignored, so dependency resolution is not reproducible by default.
  • Committed logs and generated bundles make review noisy and may leak runtime details.
  • app/hooks/_useWorklet.ts imports the iOS backend bundle while the Android import is commented.
  • react-native-svg and qrcode-terminal are imported but not listed as direct dependencies.
Reading guide

Study Path

01

Start with the promise

Read the README to understand why the architecture is local-first and P2P.

README.md
03

Trace a mutation

Start at a tap handler in app/index.tsx, cross RPC, append an op, then watch apply send a backend update.

app/index.tsx:248 item.mjs:17
06

Run the hardening checklist

Use the Global security notes and the Plans review findings as a review checklist before release or wider collaboration.

Security notes Review findings
Clickable map

File Index

Verification

Testing

How an implementation agent tests the mobile app in isolation and proves it interacts with other instances. Pair this with the implementation plans and the cross-instance matrix.

Unit

Reducers/selectors; category lookup and grocery intelligence; item reduction/replay order; id-migration of legacy text-only items (M1); redaction helpers (M5).

Integration

Worklet RPC round-trips (add/update/delete → backend → SYNC_LIST / *_FROM_BACKEND); join success/timeout/rollback; secure-storage migration of keys and loyalty cards; deep-link join requires confirmation (H2).

Manual / E2E

Parity checklist on iOS and Android; offline edit then reconnect-sync; restart persistence.

How to run

npm test (Jest/Vitest, added in milestone-zero); Expo dev client for manual runs; two simulators for device-to-device.

Interaction: Use the shared harness (separate storage roots + a private --bootstrap) to test mobile ↔ mobile, mobile ↔ desktop, and mobile ↔ headless. Mobile rows of the cross-instance matrix: generated, edited, completed, and deleted content converge; stays-synced-after-reopen; link-join confirmation (H2); and id-based duplicate handling (M1).