A React Native / Expo iOS app for scanning and pricing Pokemon TCG cards and graded slabs in real time.
- Continuous card scanning — point camera at a card, OCR runs on-device via Apple Vision (no internet required for OCR)
- Live pricing — raw card prices by condition (NM/LP/MP/HP/DMG) via PokeTrace API
- Graded slab scanning — scan PSA, CGC, BGS, SGC barcode → instant grade + market value
- eBay sold comps — recent eBay sold listings per card
- Session management — save, name, and revisit scan sessions
- Collection tracker — aggregate value across all sessions
- CSV export — share session data as a spreadsheet
- Node 20+
- Xcode 15+ (iOS builds)
- Expo CLI / EAS CLI
- Apple Developer Program membership (for device/TestFlight builds)
git clone <repo-url>
cd card-valuer
npm installsrc/constants/config.ts is gitignored. Copy the example template and fill in your keys:
cp src/constants/config.example.ts src/constants/config.tsThen edit src/constants/config.ts — see Configuration below.
npx expo run:iosnpm install -g eas-cli
eas login
eas build:configure
eas build --platform ios --profile production
eas submit --platform ios --latestsrc/constants/config.ts is gitignored. Never commit real API keys.
Use src/constants/config.example.ts as the template (already committed).
// src/constants/config.ts
// PokeTrace API — https://poketrace.com/developers
// Sign up → subscribe to Pro plan ($19.99/mo) → Dashboard → copy API key
export const POKETRACE_API_BASE = 'https://api.poketrace.com/v1';
export const POKETRACE_API_KEY = 'YOUR_POKETRACE_API_KEY';
export const SCAN_INTERVAL_MS = 600;
export const DEDUP_WINDOW_MS = 8000;
export const MAX_SESSION_HISTORY = 20;
export const CONDITION_MULTIPLIERS: Record<string, number> = {
NM: 1.0,
LP: 0.8,
MP: 0.64,
HP: 0.4,
DMG: 0.25,
};
export const CONDITIONS = ['NM', 'LP', 'MP', 'HP', 'DMG'] as const;
// eBay Browse API (optional) — https://developer.ebay.com
// Leave EBAY_APP_ID empty and EBAY_DEMO_MODE = true to use mock eBay data
export const EBAY_APP_ID = '';
export const EBAY_API_BASE = 'https://api.ebay.com/buy/browse/v1';
export const EBAY_DEMO_MODE = true;| Key | Where to get it | Required |
|---|---|---|
POKETRACE_API_KEY |
poketrace.com/developers → subscribe → Dashboard | Yes (live prices) |
EBAY_APP_ID |
developer.ebay.com → My Apps → Production key | No (demo mode works without it) |
card-valuer/
├── App.tsx # Root — intro animation + tab navigator
├── app.json # Expo config (bundle ID, permissions, plugins)
├── babel.config.js # Metro / Reanimated / Worklets config
├── src/
│ ├── components/
│ │ ├── CardListItem.tsx # Scanned card row (image, name, condition, price)
│ │ ├── ConditionPicker.tsx # NM/LP/MP/HP/DMG selector chips
│ │ ├── GradedPricesPanel.tsx # PSA/CGC/BGS grade price table
│ │ ├── IntroAnimation.tsx # Retro diamond block intro
│ │ ├── PriceTrendBadge.tsx # 7-day price trend indicator
│ │ ├── ScannerOverlay.tsx # Card scan zone UI overlay
│ │ └── SlabScannerOverlay.tsx # Slab barcode scan zone overlay
│ ├── constants/
│ │ ├── config.example.ts # Committed key template
│ │ └── config.ts # GITIGNORED — your live keys go here
│ ├── context/
│ │ └── ScannerContext.tsx # Scanner state shared across tabs
│ ├── hooks/
│ │ └── useScanner.ts # Camera snapshot → OCR → API → dedup logic
│ ├── screens/
│ │ ├── CollectionScreen.tsx # Aggregate collection stats
│ │ ├── HistoryScreen.tsx # Past scan sessions
│ │ ├── ScannerScreen.tsx # Camera + cards/slabs mode toggle
│ │ └── SessionScreen.tsx # Current session card list + export
│ ├── services/
│ │ ├── ebayService.ts # eBay sold comps (demo or live)
│ │ ├── ocrService.ts # ML Kit on-device text recognition
│ │ ├── pokemonTcgService.ts # PokeTrace card search + lookup
│ │ ├── pricingService.ts # Condition pricing + graded price helpers
│ │ └── slabService.ts # Barcode parsing + graded slab lookup
│ ├── types/
│ │ └── index.ts # All TypeScript interfaces
│ └── utils/
│ ├── csvExport.ts # Session → CSV string
│ ├── dedupBuffer.ts # Prevents duplicate scan entries
│ └── storage.ts # AsyncStorage session persistence
Points camera at a raw Pokemon card. On-device OCR extracts the card name and set number, then looks up live pricing from PokeTrace. Cards appear in the session list with per-condition prices.
Switches to barcode mode. Points camera at a PSA/CGC/BGS/SGC slab barcode. Grader and cert number are parsed, and graded market prices are fetched from PokeTrace.
| Path | Reason |
|---|---|
src/constants/config.ts |
Contains live API keys |
/ios/ |
Generated by npx expo prebuild |
/android/ |
Generated by npx expo prebuild |
*.p8, *.p12, *.mobileprovision |
Apple signing certificates |
GoogleService-Info.plist |
Firebase config (if added later) |
*.env, .env.* |
Environment variable files |
To regenerate native folders after cloning:
npx expo prebuild --platform ios --clean| Library | Purpose |
|---|---|
| Expo SDK 54 | Build toolchain, managed workflow |
| React Native 0.81 | Core framework |
| react-native-vision-camera v4 | High-performance camera + barcode scanning |
| @react-native-ml-kit/text-recognition | On-device OCR (Apple Vision on iOS) |
| @react-navigation/bottom-tabs | Tab navigation |
| expo-image | Optimised card image rendering |
| react-native-reanimated | Intro animation |
| @react-native-async-storage/async-storage | Session persistence |
| expo-sharing + expo-file-system | CSV export + sharing |