Skip to content

rahulshendre/BookBox

Repository files navigation

BookBox

A React Native app built with Expo for animated story reading and language learning. Fetches stories from a REST API, displays them with filtering options, and plays YouTube videos with custom start/end times or PDFs.

Architecture Flow

┌─────────────┐
│   Splash    │ (app/index.tsx)
│   Screen    │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│    Home     │ (app/home.tsx)
│   Screen    │
└──────┬──────┘
       │
       ├──► Fetch Stories API
       │    POST /Services/GetVideoList
       │
       ├──► Filter by Language/Level
       │    POST /Services/GetFilter
       │
       ├──► Search Stories
       │    POST /Services/SearchVideo
       │
       └──► User Selects Story
            │
            ├──► YouTube Video ──► Video Player (app/video.tsx)
            │                      - Plays with start/end times
            │                      - Auto-plays next in playlist
            │
            └──► PDF Story ──────► PDF Viewer (app/pdf.tsx)
                                   - WebView-based renderer

Tech Stack

  • Framework: React Native 0.81.5 with Expo 54
  • Navigation: Expo Router (file-based routing)
  • Language: TypeScript 5.9
  • State: In-memory playlist store
  • Video: react-native-youtube-iframe for YouTube playback
  • PDF: react-native-webview for PDF rendering

Project Structure

BookBox/
├── app/                    # Expo Router screens (file-based routing)
│   ├── _layout.tsx        # Root layout with error boundary & navigation
│   ├── index.tsx          # Splash screen
│   ├── home.tsx           # Main screen: story list, filters, search
│   ├── video.tsx          # YouTube video player with playlist support
│   ├── pdf.tsx            # PDF viewer using WebView
│   └── faq.tsx            # FAQ/Help screen
├── components/            # Reusable components
│   ├── error-boundary.tsx # Global error handler
│   └── network-status.tsx # Network connectivity indicator
├── store/                 # State management
│   └── playlist.ts        # In-memory playlist store
├── types/                 # TypeScript definitions
│   └── bookbox.ts         # API response types & StoryItem interface
├── assets/                # Static assets (images, logos)
├── app.config.js          # Expo configuration
└── package.json           # Dependencies & scripts

Key Code Components

1. API Integration (app/home.tsx)

REST API at https://templateapp.planetread.org/Services:

const BASE_URL = 'https://templateapp.planetread.org/Services';
const COMPANY_ID = '37';

// Fetch stories with filters
const fetchStories = async (language: string, level: string, rank: string) => {
  const response = await fetchJson<VideoListResponse>(
    '/GetVideoList',
    { company_id: COMPANY_ID, Language: language, lavel: level, rank },
    abortController.signal
  );
  return response.Result?.map(mapStory) || [];
};

// Search stories
const searchStories = async (query: string) => {
  const response = await fetchJson<SearchResponse>(
    '/SearchVideo',
    { company_id: COMPANY_ID, search: query },
    abortController.signal
  );
  return response.Result?.map(mapStory) || [];
};

Data Transformation: Maps raw API responses to typed StoryItem:

const mapStory = (item: Record<string, string>): StoryItem => ({
  id: item.id ?? '',
  youtubeId: item.Youtubeurl ?? '',
  videoStart: parseTimeValue(item.Videostart),  // "1:30" → 90 seconds
  videoEnd: parseTimeValue(item.Videoend),
  // ... other fields
});

2. Navigation (app/_layout.tsx)

Expo Router Stack navigator with error boundary:

<ErrorBoundary>
  <Stack screenOptions={{ headerShown: false }}>
    <Stack.Screen name="index" />
    <Stack.Screen name="home" />
    <Stack.Screen 
      name="video" 
      options={{ presentation: 'fullScreenModal' }}
    />
    <Stack.Screen name="pdf" />
    <Stack.Screen name="faq" />
  </Stack>
</ErrorBoundary>

3. Video Player (app/video.tsx)

Plays YouTube videos with custom start/end times and auto-advances:

const playlist = getPlaylist();
const currentStory = playlist[parseInt(params.index || '0', 10)];

<YoutubePlayer
  videoId={currentStory.youtubeId}
  initialPlayerParams={{
    start: currentStory.videoStart,
    end: currentStory.videoEnd,
  }}
  onStateChange={(state) => {
    if (state === 'ended' && hasNext) {
      router.replace(`/video?index=${nextIndex}`);
    }
  }}
/>

// Lock to landscape during playback
useEffect(() => {
  ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
  return () => ScreenOrientation.unlockAsync();
}, []);

4. State Management (store/playlist.ts)

In-memory playlist store:

let playlist: StoryItem[] = [];

export const setPlaylist = (stories: StoryItem[]) => {
  playlist = Array.isArray(stories) ? stories : [];
};

export const getPlaylist = (): StoryItem[] => {
  return Array.isArray(playlist) ? playlist : [];
};

Usage:

// In home.tsx: setPlaylist(filteredStories);
// In video.tsx: const playlist = getPlaylist();

5. Type Definitions (types/bookbox.ts)

TypeScript interfaces for API responses:

export interface StoryItem {
  id: string;
  youtubeId: string;
  videoStart: number;      // Start time in seconds
  videoEnd: number;         // End time in seconds
  language: string;
  grade: string;
  fileUrl: string;         // PDF URL if available
}

export interface VideoListResponse {
  Success: string;
  Result?: Array<Record<string, string>>;
  Message?: string;
}

Setup & Development

Prerequisites

  • Node.js 18+, npm/yarn, Expo CLI, iOS Simulator (Mac) or Android Emulator

Installation

npm install
npm start  # or npx expo start

Running on Devices

npm run ios      # iOS Simulator (or press 'i' in Expo CLI)
npm run android # Android Emulator (or press 'a')
npm run web     # Web Browser (or press 'w')

Development Scripts

npm start          # Start Expo dev server
npm run android    # Start on Android
npm run ios        # Start on iOS
npm run web        # Start on web
npm run lint       # Run ESLint

Build Configuration

App Config (app.config.js):

  • Bundle IDs: iOS com.bookbox.BBXFRE004, Android com.bookbox.anibooks
  • Version: 5.4 (iOS build: 3, Android versionCode: 3)
  • Features: New Architecture enabled, React Compiler, Typed routes, Light theme only

Key Dependencies:

{
  "expo": "54.0.23",
  "react": "19.1.0",
  "react-native": "0.81.5",
  "expo-router": "~6.0.14",
  "react-native-youtube-iframe": "^2.4.1",
  "react-native-webview": "13.15.0",
  "typescript": "5.9.2"
}

Code Patterns

Time Parsing

Converts time strings to seconds:

parseTimeValue("1:30")    // → 90
parseTimeValue("2:15:30") // → 8130
parseTimeValue("45")      // → 45

Error Handling

API calls use AbortController for timeout (30s):

const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);
const response = await fetch(url, {
  method: 'POST',
  signal: controller.signal,
});

Network Status

Connectivity indicator using @react-native-community/netinfo:

<NetworkStatus />  // In app/_layout.tsx

Troubleshooting

  • Video not playing: Check YouTube video ID and network connectivity
  • PDF not loading: Verify fileUrl is a valid PDF URL
  • API errors: Check BASE_URL and COMPANY_ID in app/home.tsx
  • Build errors: Ensure Expo SDK 54 is installed and dependencies are up to date

About

Under the BIRD initiative, the app is a wrapper around youtube, showcasing the videos of the BookBoxInc. channel in the application

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors