Skip to main content

Overview

d-sports-engage-native (package name: engage-native, version 1.10.0) is the native mobile app for D-Sports. It mirrors the core PWA experience on iOS and Android: wallet, shop, leaderboard, locker room, and profile. It also runs as a responsive PWA on desktop web.

Tech stack

CategoryTechnology
FrameworkExpo 54, React Native 0.81, React 19
AuthClerk (Expo) with Apple Sign-In
PaymentsRevenueCat (react-native-purchases)
Web3Thirdweb (Arbitrum, Ethereum, Polygon)
StateZustand 5
StorageMMKV 4.1
UILucide React Native
NavigationExpo Router 6 (file-based, typed routes)
AnimationsReact Native Reanimated 4.1
MonitoringSentry
PackageBun 1.3.9

Features

  • Wallet — tokens, holdings, pack opening, crypto checkout (via PWA backend)
  • Shop — collectibles, cart, coin bundles, crypto and fiat checkout
  • Leaderboard — rankings and filters
  • Locker room — social feed, quests, daily games (Pick ‘Em, Spin Wheel, Guess the Player)
  • Profile — user profile, team membership, and settings
  • Theme — dark/light mode (default dark)
  • PWAdisplay: standalone, responsive desktop layout (maxWidth: 480px), web hover states

Getting started

1

Clone and install

git clone <repo-url>
cd d-sports-engage-native
bun install
2

Configure environment

Create a .env file at the project root with the following keys. Only EXPO_PUBLIC_* keys are accessible at runtime.
VariablePurpose
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEYClerk authentication
EXPO_PUBLIC_API_URLBackend API base URL (e.g. https://api.d-sports.org)
EXPO_PUBLIC_TW_CLIENT_IDThirdweb client ID
EXPO_PUBLIC_REVENUECAT_API_KEYRevenueCat API key
EXPO_PUBLIC_REVENUECAT_APPSTORE_IDRevenueCat App Store ID
EXPO_PUBLIC_REVENUECAT_ENTITLEMENTRevenueCat entitlement name
EXPO_PUBLIC_SUPABASE_URLSupabase project URL
EXPO_PUBLIC_SUPABASE_KEYSupabase publishable key
3

Start the dev server

bunx expo start
Press a for Android, i for iOS, or scan the QR code with Expo Go.

Project structure

app/
├── (auth)/              # Login, signup, SSO callback, password reset
├── (onboarding)/        # New user onboarding flow
├── (tabs)/              # Main tab navigation (wallet, shop, leaderboard, locker room, profile)
├── settings/            # Settings pages with nested modals and tabs
└── _layout.tsx          # Root layout with providers and auth protection

components/
├── wallet/              # 9 extracted wallet sub-components
├── shop/                # 7 extracted shop sub-components + crypto checkout modal
├── locker-room/         # Social feed, quests, daily games, team/fan exploration
├── leaderboard/         # Base leaderboard and modal
├── settings/            # Setting items, sections, modals, and tabs
├── layout/              # AppScreen wrapper (responsive web max-width)
├── ui/                  # Reusable primitives (Button, TextField, TutorialOverlay, etc.)
├── Icon/                # Icon wrapper using lucide-react-native
└── theme-provider.tsx   # Theme context (dark/light)

hooks/
├── use-wallet-screen.ts # All wallet state, effects, and handlers
├── use-shop-screen.ts   # All shop state, effects, and handlers
├── use-feed-section.ts  # Social feed logic
└── use-carousel-scroll.ts # Carousel auto-scroll logic

lib/
├── api/                 # API client with MMKV cache fallback
│   ├── client.ts        # Base HTTP client with auth token injection
│   └── (domain modules) # wallet, shop, user, quests, leaderboard, locker-room, teams, collectibles, checkout
├── revenuecat/          # RevenueCat in-app purchases provider
├── crypto/              # On-chain transaction signing (native + web)
└── utils.ts             # Shared utilities

context/
├── user-context.tsx         # Authentication, user profile, team membership
├── collectibles-context.tsx # Owned packs and items
├── navbar-visibility-context.tsx
└── create-action-context.tsx

services/
├── store.ts             # Zustand global store (theme, cart, points)
├── storage.ts           # MMKV persistence adapter
└── types.ts             # Core types (User, Room, Team, Product)

theme/
├── colors.ts            # Brand colors, dark/light palettes
├── spacing.ts           # Spacing scale
└── typography.ts        # Font configuration

Architecture patterns

  • File-based routing via Expo Router with route groups (tabs), (auth), (onboarding)
  • Modular screen architecture — screen files contain only JSX; all state, effects, and handlers live in dedicated hooks (use-wallet-screen.ts, use-shop-screen.ts)
  • Extracted sub-components — wallet and shop screens decomposed into components/wallet/ and components/shop/ with barrel exports
  • API client layer in lib/api/ with domain-specific modules and MMKV cache-first fetching (lib/api/cache.ts)
  • Zustand + MMKV for global state with synchronous persistence
  • React Context for auth, collectibles, navbar visibility
  • Crypto checkout via Thirdweb SDK calling the PWA backend for on-chain payments (Arbitrum, Ethereum, Polygon)
  • RevenueCat for fiat payments: Apple IAP (native), Google Play (native), Stripe (web)
  • Path alias @/* maps to the project root

EAS builds and deployment

The app uses Expo Application Services (EAS) for native builds, OTA updates, and store submissions.
Build profileCommandPurpose
developmentbun run build:devDev client with fast refresh
previewbun run build:previewInternal QA (APK/ad-hoc)
productionbun run build:prodStore-ready binaries

OTA updates

Push JS-only changes without a new store build:
bun run update --branch production --message "Fix: wallet balance display"
Build profileUpdate channelWho receives updates
developmentdevelopmentDev builds
previewpreviewInternal testers
productionproductionApp store users
OTA updates only work if the JS is compatible with the installed native binary. If you add or change native modules, bump the version in app.json and do a full build first.

Store submission

bun run submit           # Both platforms
bun run submit:ios       # iOS only
bun run submit:android   # Android only

API integration

The app calls the same backend (d-sports-api) as the PWA. API modules live in lib/api/ and are accessed via the useApi() hook:
ModuleEndpoints covered
wallet-api.tsToken balances, holdings, transactions
shop-api.tsProduct catalog, featured items
checkout-api.tsCrypto checkout initiation and verify
user-api.tsUser profile, preferences, avatar
quests-api.tsQuest listings, progress, completion
leaderboard-api.tsRankings, filters, user position
locker-room-api.tsPosts, reactions, social feed
teams-api.tsTeam data, membership, following
collectibles-api.tsOwned packs, items, opening
All requests pass through lib/api/client.ts, which injects the Clerk auth token and falls back to MMKV cache when offline.

Ecosystem overview

See how the native app fits with the PWA, site, and Mic’d Up.