A mobile app that compares Pokémon cards and determines which one is stronger based on real battle statistics.
BattleDex lets you select any two Pokémon cards and instantly see which one wins in a matchup. The app calculates a Power Score using HP, damage output, energy costs, type effectiveness, and weaknesses/resistances. Results are displayed side-by-side with a clear winner announcement, with polished loading skeletons and animated transitions.
- Framework: React Native 0.82.1
- Language: TypeScript
- Navigation: React Navigation (Native Stack)
- Architecture: MVVM with feature-specific hooks
- Database: @op-engineering/op-sqlite
- Theming: Custom theme provider with light/dark modes
- API: PCPowerScoreAPI with mapper layer
- Crypto:
node-forgefor HMAC-SHA256 signing - UI Kit: BD component system with Poppins fonts and skeleton loaders
- Animations: React Native Reanimated
- Graphics: React Native Skia
- Testing: Jest + @testing-library/react-native
Complete the React Native environment setup before proceeding.
-
Clone the repository
-
Install dependencies:
npm install
-
iOS only: Install CocoaPods dependencies
# First time setup bundle install # Install pods (run after every native dependency update) bundle exec pod install
npm run android # Production environment
npm run dev:android # Development environment
npm run mock:android # Mock data environmentnpm run ios # Production environment
npm run dev:ios # Development environment
npm run mock:ios # Mock data environmentThe Metro bundler starts automatically with these commands.
| Command | Description |
|---|---|
npm start |
Start Metro bundler |
npm run dev:start |
Start with .env.dev config |
npm run mock:start |
Start with .env.mock config |
| Command | Description |
|---|---|
npm run android |
Launch Android app |
npm run ios |
Launch iOS app |
npm run release:android |
Android debug build (release mode) |
npm run build:android |
Android production bundle |
npm run build:ios |
iOS production build |
| Command | Description |
|---|---|
npm run emu:list |
List available emulators |
npm run emu:start |
Start Pixel_2_API_29 emulator |
Release builds read signing credentials from android/keystore.properties, which in turn is loaded by android/app/build.gradle. Before generating a release APK/AAB:
-
Place your keystore file (for example
release.keystore) insideandroid/app/or provide an absolute path. -
Create
android/keystore.propertieswith the following keys matching your keystore:MYAPP_UPLOAD_STORE_FILE=release.keystore MYAPP_UPLOAD_KEY_ALIAS=your_key_alias MYAPP_UPLOAD_STORE_PASSWORD=your_store_password MYAPP_UPLOAD_KEY_PASSWORD=your_key_password
-
Keep
keystore.propertiesand the keystore file out of version control—they contain sensitive credentials.
| Command | Description |
|---|---|
npm run lint |
Run ESLint checks |
npm run lint:fix |
Auto-fix ESLint issues |
npm run format |
Format code with Prettier |
npm test |
Run Jest test suite |
src/
├── common/
│ ├── components/ # Reusable UI components
│ ├── db/ # CompareLocalDatabase (op-sqlite)
│ ├── utils/ # Time formatting, async helpers
│ └── styles/ # Theme provider, colors, spacing, typography
│
├── features/
│ ├── card/
│ │ ├── data/ # Repositories, mappers
│ │ ├── domain/ # Entities, mocks
│ │ └── presentation/ # Screens, components, viewModels, DI
│ │
│ ├── compare/
│ │ ├── data/ # API and DB repositories
│ │ ├── domain/ # Entities, mocks
│ │ └── presentation/ # Screens, components, viewModels, DI
│ │
│ └── home/
│ ├── data/ # API/DB repositories, mappers
│ ├── domain/ # Entities, mocks, use cases
│ └── presentation/ # Screens, components, viewModels, DI
│
└── App.tsx # Root component with providers
Each feature follows the Model-View-ViewModel pattern with clear separation of concerns:
- View: React components in
presentation/folders - ViewModel: Custom hooks (
useHomeScreenViewModel,useCardScreenViewModel, etc.) - Model: Entities and repositories in
domain/anddata/folders
Feature-specific hooks manage:
- Data fetching and state management
- Loading and error states
- UI-ready data transformation
Examples: useHomeScreenViewModel, useCardScreenViewModel, useCompareScreenViewModel
Each feature route imports repositories through DI helpers (homeScreenDI, cardScreenDI, compareScreenDI). These helpers select API or mock implementations based on DATA_SOURCE environment variables.
Business logic lives in dedicated use case classes (e.g., SearchCardNamesUseCase) to keep logic consistent and reusable across features.
API Repositories:
- Call
PCPowerScoreAPIendpoints (GET /v1/cards/:name,POST /v1/compare) - Transform DTOs using mapper classes (
SearchCardMapper,MatchResultMapper)
Database Repositories:
- Use
CompareLocalDatabase(op-sqlite) for local persistence - Convert database rows via mappers (
CardMapper,ComparePreviewMapper)
safeCall/safeCallWithFallback: Standardized async error handlingformatTimeAgo: Human-readable timestampsErrorMessage: Consistent error display component
- Light and dark themes defined in
src/common/styles/ ThemeProviderautomatically selects theme based on system preferences- Access theme via
useTheme()hook
Components use feature-specific style hooks for theme-aware styling:
useSearchBarStylesuseDuelCardStyles- Navigation headers adapt to theme automatically
- Colors: Primary, secondary, background, text variants
- Spacing: Consistent margins and padding scales
- Typography: Standardized font sizes and weights
.env- Production configuration.env.dev- Development configuration.env.mock- Mock data configuration
API_BASE_URL: Backend API endpointDATA_SOURCE: Selects real API vs. mock repositoriesAPI_KEY: Public identifier sent viaX-Api-KeyAPI_SECRET: Shared secret for HMAC signing
PCPowerScoreAPIwraps HTTP communication- Mapper classes transform API responses to domain entities
- Error handling via
safeCallwrappers provides user-friendly messages - Remote API calls attach
X-Api-Key,X-Date,X-Nonce, andX-Signatureheaders signed with HMAC-SHA256
Three main routes powered by React Navigation Native Stack:
- Home - Card search and comparison history
- Card - Detailed card information
- Compare - Side-by-side comparison results
- Large title behavior on Home screen
- Safe area handling for iOS notches
- Theme-aware header styling
- Smooth transitions between screens
Uses @op-engineering/op-sqlite for local persistence:
- Stores comparison history
- Serializes card entities as JSON
- Provides fast retrieval for recent matches
DBSaveCardRepositoryImpl: Serializes and saves card comparisonsComparePreviewMapper: Converts database rows to preview entities- Efficient querying for history lists
- Jest: Test runner
- @testing-library/react-native: Component testing utilities
Native modules are mocked in __mocks__/:
react-native-config@op-engineering/op-sqlite
Current tests cover:
useHomeScreenViewModellogic- Repository mock interactions via
safeCall
Extend testing by adding suites for:
- Compare and card viewModels
- Mapper classes
- API error scenarios


