Skip to main content

Overview

d-sports-engage-native (package name engage-native, version 1.10.5) is the native mobile and web app for D-Sports. It mirrors the core PWA experience on iOS, Android, and web with wallet, shop, leaderboard, locker room, and profile screens.

Ecosystem overview

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

Tech stack

CategoryTechnology
FrameworkExpo 54, React Native 0.81, React 19
LanguageTypeScript 5.9
RoutingExpo Router 6 (file-based, typed routes)
AuthClerk (@clerk/clerk-expo)
PaymentsRevenueCat (react-native-purchases) — Apple IAP, Google Play, Stripe (web)
Web3Thirdweb SDK — on-chain crypto checkout
StateZustand 5 with MMKV persistence
StorageMMKV (react-native-mmkv)
IconsLucide React Native
AnimationsReact Native Reanimated 4
MonitoringSentry (@sentry/react-native)
Package managerBun 1.3

Getting started

1

Clone and install

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

Configure environment variables

Copy the .env file and fill in values for your environment. Only EXPO_PUBLIC_* keys are accessible at runtime.
VariablePurpose
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEYClerk authentication publishable key
EXPO_PUBLIC_API_URLD-Sports API backend URL (e.g. https://api.d-sports.org)
EXPO_PUBLIC_TW_CLIENT_IDThirdweb client ID for web3
EXPO_PUBLIC_REVENUECAT_API_KEYRevenueCat API key
EXPO_PUBLIC_REVENUECAT_APPSTORE_IDRevenueCat App Store app ID
EXPO_PUBLIC_REVENUECAT_ENTITLEMENTRevenueCat entitlement identifier (e.g. premium)
EXPO_PUBLIC_SUPABASE_URLSupabase project URL
EXPO_PUBLIC_SUPABASE_KEYSupabase publishable (anon) key
3

Start the development server

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

Type-check

bun tsc --noEmit

Project structure

app/
├── (auth)/              # Login, sign-up, SSO callback, password reset
├── (onboarding)/        # New user onboarding flow
├── (tabs)/              # Main tab navigation
│   ├── wallet.tsx       # Wallet screen (JSX only — logic in hook)
│   ├── shop.tsx         # Shop screen (JSX only — logic in hook)
│   ├── leaderboard.tsx  # Leaderboard screen
│   ├── locker-room.tsx  # Social feed
│   └── profile.tsx      # User profile
├── settings/            # Settings pages with nested modals/tabs
└── _layout.tsx          # Root layout with providers + auth protection

components/
├── wallet/              # 9 wallet sub-components (TokenRow, ActionModal, etc.)
├── shop/                # 7 shop sub-components (CartModal, CryptoCheckoutModal, etc.)
├── locker-room/         # Locker room feed, games, quests
├── leaderboard/         # Leaderboard table and modal
├── settings/            # Settings items, sections, modals
├── layout/              # AppScreen responsive wrapper
├── ui/                  # Reusable primitives (Button, TextField, WebHoverWrapper)
├── 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
└── use-draggable-scroll.ts

lib/
├── api/                 # API client modules (see below)
├── revenuecat/          # RevenueCat in-app purchases provider
├── crypto/              # On-chain transaction helpers
├── logger.ts            # Structured logging
└── utils.ts             # cn() class merging utility

context/
├── user-context.tsx     # Auth, 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 definitions

Architecture

Modular screen pattern

Screen files in app/(tabs)/ contain only imports and JSX. All state, effects, and handlers live in dedicated hooks:
  • hooks/use-wallet-screen.ts — wallet/token fetch, PIN verification, transaction handlers
  • hooks/use-shop-screen.ts — cart state, product queries, carousel auto-scroll, checkout
Sub-components are extracted into components/wallet/ and components/shop/ with barrel exports via index.ts. Styles live in co-located styles/ directories, never inline.

API client layer

The API client in lib/api/client.ts provides authenticated requests using Clerk tokens with normalized { success, data?, error?, code? } response envelopes. You access all API modules via the useApi() hook:
import { useApi } from "@/lib/api";

function MyComponent() {
  const api = useApi();

  const loadData = async () => {
    const result = await api.user.getProfile();
    if (result.success) {
      console.log(result.data);
    }
  };
}
Available API modules: user, quests, leaderboard, wallet, lockerRoom, teams, collectibles, shop, checkout. The client includes an MMKV-backed cache layer (lib/api/cache.ts) for offline-first data fetching.

State management

  • Zustand (services/store.ts) — global store for theme, cart, and points with synchronous MMKV persistence
  • React ContextUserContext for auth and profile, CollectiblesContext for owned items, NavBarVisibilityContext for scroll-based navbar, CreateActionContext for FAB state
  • MMKV (services/storage.ts) — synchronous key-value storage via getItem<T>(), setItem(), removeItem()

Checkout and payments

The app supports two payment paths:
  • Fiat — RevenueCat handles Apple IAP (native), Google Play (native), and Stripe (web) via lib/revenuecat/provider.tsx
  • Crypto — Thirdweb SDK signs on-chain transactions calling the PWA backend (POST /api/checkout/crypto and POST /api/checkout/crypto/verify). Supported chains: Arbitrum (default), Ethereum, Polygon.

Routing

Expo Router provides file-based routing with route groups:
  • (tabs) — main tab navigation with animated pill tab bar
  • (auth) — login, sign-up, SSO callback, password reset
  • (onboarding) — new user flow
Auth protection in _layout.tsx automatically redirects based on authentication and onboarding state.

Web optimization

The app is PWA-ready with display: "standalone" and includes:
  • Responsive desktop layout (maxWidth: 480px) via AppScreen wrapper
  • Web hover states on interactive components
  • Keyboard navigation support
  • accessibilityRole and accessibilityLabel on key elements

EAS build profiles

ProfileDistributionChannelNotes
developmentInternaldevelopmentDev client, iOS simulator enabled
development-deviceInternaldevelopmentDev client, physical devices
previewInternalpreviewAPK for Android
stagingDefaultstagingAuto-increment version
productionDefaultproductionAuto-increment version
Run builds with:
bun run build:dev          # All platforms, development
bun run build:preview:ios  # iOS only, preview
bun run build:prod         # All platforms, production

Features

  • Wallet — token balances, holdings, pack opening, crypto checkout
  • Shop — collectibles, cart, coin bundles, featured packs, crypto and fiat checkout
  • Leaderboard — rankings with filters and modals
  • Locker room — social feed, daily pick’em, spin wheel, guess the player, quests
  • Profile — user profile, settings, team membership
  • Theme — dark/light mode (default dark), gold accent (#F5C842)

Path aliases

Use @/* to import from the project root:
import { useUser } from "@/context/user-context";
import { getTokenColors } from "@/lib/token-utils";
import type { Token } from "@/types/wallet.types";