Paths are relative to this repository root (Ax108/ssa-app).
Product code lives under src/ (not Expo Router’s app/ directory). Native shells android/ and ios/ are generated by prebuild.
ssa-app/
├── README.md # Project hub
├── docs/ # This documentation set
├── package.json # Scripts, deps, engines
├── .env.example # Template for ANDROID_UPLOAD_* (copy to gitignored `.env`)
├── app.json # Expo app config (name, ids, plugins)
├── app.config.js # Dynamic config — OTA URL + runtimeVersion on top of app.json
├── eas.json # Optional EAS Build / Submit profiles (not EAS Update)
├── index.ts # Metro entry → registerRootComponent
├── babel.config.js
├── metro.config.js
├── tsconfig.json # Path aliases (@modules, @store, …)
├── tsconfig.no-tests.json # Strict app-only typecheck (tsc:app)
├── eslint.config.js
├── jest.config.js / jest.setup.js
├── scripts/
│ └── publish-ota.mjs # bun run ota:export:* → stage ssa-static OTA
├── plugins/ # Expo config plugins (native tweaks)
│ └── android/
│ ├── withGradleJvmArgs.js
│ ├── withAndroidLinkingQueries.js
│ └── withAndroidReleaseSigning.js # `.env` → release signing on prebuild
├── assets/ # App icon, splash, adaptive icon, logos
├── src/ # All application TypeScript / UI
├── android/ # GENERATED — do not hand-edit for features
└── ios/ # GENERATED — macOS / Xcode
| Area | Rule |
|---|---|
| Config at root | Keep thin: Expo, Metro, ESLint, Jest, entry index.ts |
eas.json |
Optional cloud builds + store submit; JS OTA stays on ssa-static; local Play AAB uses .env + plugins |
plugins/ |
Expo config plugins only (Android linking queries, JVM args, release signing from .env) |
.env / credentials/ |
Local only (gitignored) — upload keystore + passwords for Play AAB signing |
assets/ |
Store branding binaries referenced from app.json |
android/ / ios/ |
Treat as output of bun run prebuild; regenerate when native deps/plugins change |
docs/ |
Markdown for humans; no runtime imports |
src/
├── App.tsx # Splash gate: fonts + content init + ~3000ms min after fonts
├── appStore/ # Zustand store (global — not under modules/)
│ ├── appStore.ts
│ ├── navbarSlice.ts
│ ├── contentSlice.ts
│ ├── storeUpdateSlice.ts # Store binary update snackbar session state
│ └── contentController.ts # Boot: AsyncStorage → sync/CDN (awaited at splash)
├── constants/ # palette, theme, CDN URLs
├── assets/
│ └── json/ # Seed config + en/bn/hi (offline fallback)
├── modules/ # One folder per product area
│ ├── app/ # Splash, fonts, AppRoot shell
│ ├── navigation/ # Stacks, tab bar, top navbar, navigationRef, helpers/
│ ├── home/ # GurujiRow, HomeHero, HomeScreen (+ Donate section)
│ ├── ashram/
│ ├── satsang/
│ ├── gallery/
│ ├── contact/
│ └── donation/ # Stack-only Donation screen (not a bottom tab)
├── shared/ # Cross-module UI, hooks, utils, types, APIs
│ ├── components/ # FollowUsSection, AppFooterStrip, StoreUpdateSnackbar, …
│ ├── hooks/ # useNavigateTab, useNavigateDonation, …
│ ├── ota/ # jsBundle OTA controller, storeVersion, storeUpdateController
│ │ # useJsBundleOtaSync
│ ├── helpers/
│ ├── serviceCalls/ # oxyApi (native fetch)
│ ├── types/
│ └── utils/ # assetUrl, openUrl, logger, formatConfig
└── tests/ # Essential Jest suite only
Each module under src/modules/<name>/ should stay self-contained:
modules/home/
├── HomeScreen.tsx # Screen entry used by navigation
├── components/ # Home-only UI pieces
└── data/ # Optional local meta (not CDN JSON)
| Put here | Do not put here |
|---|---|
| Screen UI for that domain | Global Zustand slices → use appStore/ |
| Module-only components | Shared buttons/images → shared/components/ |
Module path aliases (@home/*) |
Native Android/iOS edits |
Same pattern for ashram, satsang, gallery, contact, donation. Navigation chrome stays in modules/navigation/.
navbarSlice— active route title for bottom tab highlight (top bar is brand-only).contentSlice—loaded,version,locale,config,texts.storeUpdateSlice— session visibility for the store-update snackbar.contentController— async boot (cache → sync/CDN); awaited fromApp.tsxsplash. JS OTA starts fromAppRootviauseJsBundleOtaSync(not splash-gated).
Do not relocate the store into modules/.
| Kind of code | Location |
|---|---|
| Used by 2+ modules | src/shared/… |
| Single-module UI | src/modules/<module>/… |
| HTTP helpers for CDN | src/shared/serviceCalls/ |
| Types for config/texts | src/shared/types/ |
| Theme / CDN constants | src/constants/ |
| Alias | Maps to |
|---|---|
@store/* |
src/appStore/* |
@modules/* |
src/modules/* |
@shared/* |
src/shared/* |
@constants / @constants/* |
src/constants |
@home/*, @ashram/*, … |
Matching module folders |
@navigation/* |
src/modules/navigation/* |
@src/* |
src/* |
@/* |
Repo root |
Prefer aliases over deep relative ../../../ imports.
- Unit / screen tests live under
src/tests/(current layout). - Run via
bun test/bun verify. - Details: testing.md.
- Expo Router
app/routes — this project uses React Navigation. - Feature logic inside
android/orios/. - Parallel roots like
src/screens,src/componentsat the top ofsrc/— usemodules/andshared/.