npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-native-t-schedule

v1.0.0

Published

Reusable React Native package for fetching TDMU university schedules with Google authentication

Readme

react-native-t-schedule

Reusable React Native package for fetching TDMU (Thủ Dầu Một University) schedules with Google authentication using Expo AuthSession.

Features

Easy Integration - Simple to integrate into any React Native project
Expo AuthSession - Modern OAuth authentication without Google Cloud Console requirements
Dynamic Client ID - Automatically fetches Google OAuth config from TDMU
Automatic Caching - Built-in caching to reduce API calls
TypeScript Support - Full TypeScript definitions included
Flexible Usage - Use as a component, hook, or direct client
Customizable UI - Fully customizable schedule rendering

🇻🇳 Vietnamese Guide: Hướng dẫn Best Fix (Tiếng Việt)

Installation

npm install react-native-t-schedule
# or
yarn add react-native-t-schedule

Peer Dependencies

This package requires the following peer dependencies:

npm install @react-native-async-storage/async-storage axios expo-auth-session expo-web-browser expo-crypto
# or
yarn add @react-native-async-storage/async-storage axios expo-auth-session expo-web-browser expo-crypto

Setup

1. Configure Expo for OAuth

If you're using Expo, update your app.json:

{
  "expo": {
    "name": "your-app",
    "scheme": "your-app-scheme",
    "plugins": ["expo-router"]
  }
}

2. No Google Cloud Console Required!

This package uses Expo AuthSession with TDMU's public Google OAuth configuration, so you don't need to:

  • ❌ Set up Google Cloud Console project
  • ❌ Configure OAuth consent screen
  • ❌ Add SHA-1 fingerprints
  • ❌ Create Android/iOS Client IDs
  • ❌ Manage OAuth credentials

The authentication flow automatically uses the university's Google OAuth settings.

📖 Read the Complete Expo AuthSession Setup Guide

3. Platform-Specific Setup

iOS & Android

No additional setup required! Expo AuthSession handles all native configuration automatically.

Usage

Method 1: Component (Easiest)

The simplest way to integrate TDMU schedule fetching:

import React from 'react';
import { SafeAreaView } from 'react-native';
import { TDMUScheduleView } from 'react-native-t-schedule';

export default function App() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <TDMUScheduleView />
    </SafeAreaView>
  );
}

With Configuration

import { TDMUScheduleView } from 'react-native-t-schedule';
import type { ScheduleItem, Semester } from 'react-native-t-schedule';

function App() {
  const handleScheduleFetched = (schedule: ScheduleItem[], semester: Semester | null) => {
    console.log('Schedule fetched!', schedule.length, 'items');
  };

  return (
    <TDMUScheduleView
      config={{
        tdmuUsername: 'user@gw',
        cacheEnabled: true,
        cacheDuration: 3600000, // 1 hour
      }}
      onScheduleFetched={handleScheduleFetched}
    />
  );
}

With Custom Rendering

import { View, Text } from 'react-native';
import { TDMUScheduleView } from 'react-native-t-schedule';
import type { ScheduleItem } from 'react-native-t-schedule';

function App() {
  const renderScheduleItem = ({ item }: { item: ScheduleItem }) => (
    <View style={styles.customCard}>
      <Text style={styles.courseCode}>{item.maMon || item.courseCode}</Text>
      <Text style={styles.courseName}>{item.tenMon || item.courseName}</Text>
      <Text>{item.thu || item.dayOfWeek} - Tiết {item.tiet || item.period}</Text>
      <Text>📍 {item.phong || item.room}</Text>
    </View>
  );

  return (
    <TDMUScheduleView renderScheduleItem={renderScheduleItem} />
  );
}

Method 2: Hook (More Control)

For more control over the authentication and fetching process:

import React from 'react';
import { View, Text, Button, FlatList } from 'react-native';
import { useTDMUSchedule } from 'react-native-t-schedule';

function MyScheduleScreen() {
  const {
    isAuthenticated,
    isLoading,
    schedule,
    semester,
    error,
    authenticate,
    fetchSchedule,
    logout,
  } = useTDMUSchedule({
    tdmuUsername: 'user@gw',
    cacheEnabled: true,
  });

  if (!isAuthenticated) {
    return (
      <View>
        <Button title="Login with Google" onPress={authenticate} />
      </View>
    );
  }

  if (error) {
    return <Text>Error: {error}</Text>;
  }

  if (isLoading) {
    return <Text>Loading...</Text>;
  }

  return (
    <View>
      <Text>Semester: {semester?.tenHocKy || semester?.semesterName}</Text>
      <Button title="Refresh" onPress={fetchSchedule} />
      <Button title="Logout" onPress={logout} />

      <FlatList
        data={schedule}
        renderItem={({ item }) => (
          <View>
            <Text>{item.tenMon}</Text>
            <Text>{item.thu} - Tiết {item.tiet}</Text>
          </View>
        )}
      />
    </View>
  );
}

Method 3: Direct Client (Most Control)

For complete control over the authentication flow:

import { TDMUScheduleClient } from 'react-native-t-schedule';
import * as AuthSession from 'expo-auth-session';
import axios from 'axios';

async function fetchMySchedule() {
  const client = new TDMUScheduleClient({
    tdmuUsername: 'user@gw',
    cacheEnabled: true,
    cacheDuration: 3600000,
  });

  try {
    // 1️⃣ Get Google client ID from TDMU
    const { data } = await axios.get('https://dkmh.tdmu.edu.vn/authconfig');
    const clientId = data.gg;

    // 2️⃣ Start Google OAuth flow
    const redirectUri = AuthSession.makeRedirectUri({ useProxy: true });
    const authUrl =
      `https://accounts.google.com/o/oauth2/v2/auth?` +
      `response_type=id_token&` +
      `scope=openid%20email%20profile&` +
      `client_id=${clientId}&` +
      `redirect_uri=${encodeURIComponent(redirectUri)}&` +
      `nonce=${Math.random().toString(36).substring(2)}`;

    const result = await AuthSession.startAsync({ authUrl });

    if (result.type !== 'success') {
      throw new Error('Login cancelled');
    }

    const idToken = result.params.id_token;

    // 3️⃣ Authenticate with TDMU using Google ID token
    await client.authenticateWithGoogle(idToken);

    // Fetch current schedule
    const scheduleResult = await client.fetchCurrentSchedule();

    console.log('Schedule:', scheduleResult.schedule);
    console.log('Semester:', scheduleResult.semester);

    return scheduleResult;
  } catch (error) {
    console.error('Error:', error);
  }
}

API Reference

TDMUScheduleClient

Constructor

new TDMUScheduleClient(config?: TDMUConfig)

Config Options:

  • baseURL (string): TDMU API base URL. Default: 'https://dkmh.tdmu.edu.vn/api'
  • tdmuUsername (string): TDMU username. Default: 'user@gw'
  • cacheEnabled (boolean): Enable caching. Default: true
  • cacheDuration (number): Cache duration in ms. Default: 3600000 (1 hour)

Methods

authenticateWithGoogle(googleIdToken: string): Promise<TDMUAuthResponse>

Authenticate with TDMU using Google ID token.

getAuthConfig(): Promise<{ gg: string; logoff: boolean; timeout: number }>

Get TDMU authentication configuration including Google client ID.

validateAccess(): Promise<any>

Validate user's access to TDMU functions.

getSemesters(): Promise<Semester[]>

Get list of available semesters.

getSchedule(semesterId: string | number, userId?: string): Promise<ScheduleItem[]>

Get schedule for a specific semester.

fetchCurrentSchedule(): Promise<ScheduleResult>

Fetch schedule for the current semester (complete flow).

clearCache(): Promise<void>

Clear all cached data.

logout(): Promise<void>

Logout and clear all data.


useTDMUSchedule Hook

const {
  isAuthenticated,
  isLoading,
  schedule,
  semester,
  error,
  authenticate,
  fetchSchedule,
  fetchScheduleForSemester,
  fetchSemesters,
  logout,
  clearCache
} = useTDMUSchedule(config?: TDMUConfig);

Returns:

State:

  • isAuthenticated (boolean): Authentication status
  • isLoading (boolean): Loading state
  • schedule (ScheduleItem[] | null): Schedule data
  • semester (Semester | null): Current semester info
  • error (string | null): Error message

Actions:

  • authenticate(): Authenticate with Google
  • fetchSchedule(): Fetch current schedule
  • fetchScheduleForSemester(semesterId): Fetch specific semester
  • fetchSemesters(): Get available semesters
  • logout(): Logout user
  • clearCache(): Clear cached data

TDMUScheduleView Component

<TDMUScheduleView
  config?: TDMUConfig
  onScheduleFetched?: (schedule: ScheduleItem[], semester: Semester | null) => void
  renderScheduleItem?: (info: { item: ScheduleItem }) => React.ReactElement
  style?: ViewStyle
/>

Props:

  • config: Configuration options (same as TDMUScheduleClient)
  • onScheduleFetched: Callback when schedule is fetched
  • renderScheduleItem: Custom render function for schedule items
  • style: Custom container style

Types

ScheduleItem

interface ScheduleItem {
  id?: string;
  maMon?: string; // Course code
  courseCode?: string;
  tenMon?: string; // Course name
  courseName?: string;
  thu?: string; // Day of week
  dayOfWeek?: string;
  tiet?: string; // Period
  period?: string;
  phong?: string; // Room
  room?: string;
  giangVien?: string; // Instructor
  instructor?: string;
}

Semester

interface Semester {
  id?: string;
  hocKyId?: string;
  tenHocKy?: string; // Semester name
  semesterName?: string;
}

ScheduleResult

interface ScheduleResult {
  semester: Semester;
  schedule: ScheduleItem[];
  fetchedAt: string; // ISO timestamp
}

Examples

Check the /example directory for complete working examples:

  • BasicExample.tsx - Simple component usage
  • HookExample.tsx - Using the hook
  • CustomRenderExample.tsx - Custom rendering
  • DirectClientExample.tsx - Direct client usage

Troubleshooting

"Failed to authenticate with TDMU system"

Solution: Ensure Google Access Token is valid and not expired. Check your Google Sign-In configuration.

"No semesters available"

Solution: Verify the user has access to the TDMU system and is properly authenticated.

Cache not working

Solution: Check AsyncStorage permissions and storage availability on the device.

Google Sign-In fails

Solution: Verify webClientId in GoogleSignin.configure() and ensure Firebase setup is correct.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT

Author

TrungDev - GitHub

Acknowledgments

This package is designed to work with the TDMU (Thủ Dầu Một University) academic system.