Skip to main content

Overview

d-sports-engage-native (package name: engage-native) 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.
  • Run: bunx expo start or bun run start — then press a for Android or i for iOS, or scan the QR code with Expo Go.

Tech stack

CategoryTechnology
FrameworkExpo 54, React Native 0.81, React 19
AuthClerk (Expo)
PaymentsRevenueCat (react-native-purchases)
Web3Thirdweb
StateZustand
StorageMMKV
UILucide React Native
NavigationExpo Router
PackageBun

Features

  • Wallet — Tokens, holdings, pack opening, crypto checkout (via PWA backend)
  • Shop — Collectibles, cart, coin bundles, checkout
  • Leaderboard — Rankings and filters
  • Locker room — Social feed and engagement
  • Profile — User profile and settings
  • Theme — Dark/light mode (default dark)

Getting started

  1. Clone the repository and run bun install.
  2. Configure environment (Clerk, RevenueCat, Thirdweb, API base URL) per repo README.
  3. Run bunx expo start.
  4. For development builds: bun run build:dev (EAS) or run with Expo dev client.
The app targets both native and web (responsive) and uses the same backend (d-sports-api) as the PWA for API and checkout flows.

API client layer

The native app communicates with d-sports-api through a typed HTTP client in lib/api/. All requests use Clerk bearer-token authentication and expect a normalized response envelope.

Normalized envelope contract

Every API response follows this strict envelope shape:
interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
  code?: string; // machine-readable error code
}
The client in lib/api/client.ts validates that every 2xx response includes a top-level success field. Responses missing it are rejected with an INVALID_RESPONSE_ENVELOPE code. This is referred to as strict envelope mode.

API modules

You access all endpoints through the useApi() hook exported from lib/api/index.ts:
import { useApi } from "@/lib/api";

function MyComponent() {
  const api = useApi();
  const result = await api.user.getProfile();
  if (result.success) {
    console.log(result.data);
  }
}
The hook exposes these domain modules, each backed by a dedicated file:
ModuleFilePurpose
userlib/api/user-api.tsProfile, follow/unfollow, onboarding
questslib/api/quests-api.tsQuest listing and completion
leaderboardlib/api/leaderboard-api.tsRankings and top users
walletlib/api/wallet-api.tsToken balances and transactions
lockerRoomlib/api/locker-room-api.tsSocial feed and posts
teamslib/api/teams-api.tsTeam data and membership
collectibleslib/api/collectibles-api.tsOwned packs and items
shoplib/api/shop-api.tsProduct catalog and listings
checkoutlib/api/checkout-api.tsCrypto and fiat checkout flows

User API endpoints

The user module (lib/api/user-api.ts) covers profile management and social features:
MethodEndpointDescription
GET/api/userGet current user profile
PATCH/api/userUpdate user profile
GET/api/user/onboardingCheck onboarding status
POST/api/user/onboardingComplete onboarding
GET/api/user/sync-clerkSync user with Clerk
POST/api/user/followFollow a user
DELETE/api/user/followUnfollow a user
GET/api/user/collectiblesGet user collectibles
GET/api/user/searchSearch users
GET/api/user/top-usersGet top 10 fans
GET/api/user/allList all community users
GET/api/user/check-usernameCheck username availability
The follow endpoint uses POST /api/user/follow with a { targetUserId } body. Unfollow uses DELETE /api/user/follow?targetUserId=<id>. The top-users endpoint returns a { topUsers: TopUser[] } shape inside the envelope data field.

Ecosystem overview

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