feat: Android
This commit is contained in:
@@ -55,3 +55,6 @@ yarn-error.log
|
||||
|
||||
# Expo
|
||||
.expo/*
|
||||
|
||||
ios/JecnaapiIOS.xcframework/
|
||||
ios/framework.zip
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'expo-module-gradle-plugin'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
group = 'cz.jzitnik.jecnaapireactnative'
|
||||
@@ -8,11 +9,29 @@ version = '0.1.0'
|
||||
|
||||
android {
|
||||
namespace "cz.jzitnik.jecnaapireactnative"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
versionCode 1
|
||||
versionName "0.1.0"
|
||||
minSdk = 26
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += [
|
||||
'-Xskip-metadata-version-check'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.tomhula:jecnaapi-jecna:10.3.5"
|
||||
implementation "io.github.tomhula:jecnaapi-canteen:10.3.5"
|
||||
implementation "com.google.code.gson:gson:2.14.0"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,222 @@
|
||||
package cz.jzitnik.jecnaapireactnative
|
||||
|
||||
import com.google.gson.Gson
|
||||
import expo.modules.kotlin.functions.Coroutine
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
import io.github.tomhula.jecnaapi.CanteenClient
|
||||
import io.github.tomhula.jecnaapi.JecnaClient
|
||||
import io.github.tomhula.jecnaapi.data.canteen.ExchangeItem
|
||||
import io.github.tomhula.jecnaapi.data.canteen.MenuItem
|
||||
import io.github.tomhula.jecnaapi.data.notification.NotificationReference
|
||||
import io.github.tomhula.jecnaapi.data.notification.NotificationReference.NotificationType
|
||||
import io.github.tomhula.jecnaapi.util.SchoolYear
|
||||
import io.github.tomhula.jecnaapi.util.SchoolYearHalf
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Month
|
||||
|
||||
class JecnaapiReactNativeModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("JecnaapiReactNative")
|
||||
}
|
||||
private var client: JecnaClient = JecnaClient(autoLogin = true)
|
||||
private var canteenClient: CanteenClient = CanteenClient(autoLogin = true)
|
||||
private val gson = Gson()
|
||||
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("JecnaapiReactNative")
|
||||
|
||||
// ==========================================
|
||||
// JecnaClient Functions
|
||||
// ==========================================
|
||||
|
||||
AsyncFunction("login") Coroutine { username: String, pass: String ->
|
||||
return@Coroutine client.login(username, pass)
|
||||
}
|
||||
|
||||
AsyncFunction("logout") Coroutine { ->
|
||||
client.logout()
|
||||
}
|
||||
|
||||
AsyncFunction("isLoggedIn") Coroutine { ->
|
||||
return@Coroutine client.isLoggedIn()
|
||||
}
|
||||
|
||||
AsyncFunction("getNewsPage") Coroutine { ->
|
||||
val news = client.getNewsPage()
|
||||
return@Coroutine gson.toJson(news)
|
||||
}
|
||||
|
||||
AsyncFunction("getGradesPage") Coroutine { ->
|
||||
val grades = client.getGradesPage()
|
||||
return@Coroutine gson.toJson(grades)
|
||||
}
|
||||
|
||||
AsyncFunction("getGradesPageByYear") Coroutine { firstCalendarYear: Int, half: String ->
|
||||
val schoolYear = SchoolYear(firstCalendarYear)
|
||||
val schoolYearHalf = SchoolYearHalf.valueOf(half.uppercase())
|
||||
val grades = client.getGradesPage(schoolYear, schoolYearHalf)
|
||||
return@Coroutine gson.toJson(grades)
|
||||
}
|
||||
|
||||
AsyncFunction("getTimetablePage") Coroutine { ->
|
||||
val timetable = client.getTimetablePage()
|
||||
return@Coroutine gson.toJson(timetable)
|
||||
}
|
||||
|
||||
AsyncFunction("getTimetablePageByYear") Coroutine { firstCalendarYear: Int, periodId: Int? ->
|
||||
val schoolYear = SchoolYear(firstCalendarYear)
|
||||
val timetable = client.getTimetablePage(schoolYear, periodId)
|
||||
return@Coroutine gson.toJson(timetable)
|
||||
}
|
||||
|
||||
AsyncFunction("getAttendancesPage") Coroutine { ->
|
||||
val attendance = client.getAttendancesPage()
|
||||
return@Coroutine gson.toJson(attendance)
|
||||
}
|
||||
|
||||
AsyncFunction("getAttendancesPageByMonth") Coroutine { firstCalendarYear: Int, monthName: String ->
|
||||
val schoolYear = SchoolYear(firstCalendarYear)
|
||||
val month = Month.valueOf(monthName.uppercase())
|
||||
val attendance = client.getAttendancesPage(schoolYear, month)
|
||||
return@Coroutine gson.toJson(attendance)
|
||||
}
|
||||
|
||||
AsyncFunction("getAbsencesPage") Coroutine { ->
|
||||
val absences = client.getAbsencesPage()
|
||||
return@Coroutine gson.toJson(absences)
|
||||
}
|
||||
|
||||
AsyncFunction("getAbsencesPageByYear") Coroutine { firstCalendarYear: Int ->
|
||||
val schoolYear = SchoolYear(firstCalendarYear)
|
||||
val absences = client.getAbsencesPage(schoolYear)
|
||||
return@Coroutine gson.toJson(absences)
|
||||
}
|
||||
|
||||
AsyncFunction("getTeachersPage") Coroutine { ->
|
||||
val teachers = client.getTeachersPage()
|
||||
return@Coroutine gson.toJson(teachers)
|
||||
}
|
||||
|
||||
AsyncFunction("getTeacher") Coroutine { teacherTag: String ->
|
||||
val teacher = client.getTeacher(teacherTag)
|
||||
return@Coroutine gson.toJson(teacher)
|
||||
}
|
||||
|
||||
AsyncFunction("getRoomsPage") Coroutine { ->
|
||||
val rooms = client.getRoomsPage()
|
||||
return@Coroutine gson.toJson(rooms)
|
||||
}
|
||||
|
||||
AsyncFunction("getRoom") Coroutine { roomCode: String ->
|
||||
val room = client.getRoom(roomCode)
|
||||
return@Coroutine gson.toJson(room)
|
||||
}
|
||||
|
||||
AsyncFunction("getLocker") Coroutine { ->
|
||||
val locker = client.getLocker()
|
||||
return@Coroutine gson.toJson(locker)
|
||||
}
|
||||
|
||||
AsyncFunction("getStudentProfile") Coroutine { ->
|
||||
val profile = client.getStudentProfile()
|
||||
return@Coroutine gson.toJson(profile)
|
||||
}
|
||||
|
||||
AsyncFunction("getStudentProfileByUsername") Coroutine { username: String ->
|
||||
val profile = client.getStudentProfile(username)
|
||||
return@Coroutine gson.toJson(profile)
|
||||
}
|
||||
|
||||
AsyncFunction("getNotifications") Coroutine { ->
|
||||
val notifications = client.getNotifications()
|
||||
return@Coroutine gson.toJson(notifications)
|
||||
}
|
||||
|
||||
AsyncFunction("getNotification") Coroutine { type: String, message: String, recordId: Int ->
|
||||
val notificationType = NotificationType.valueOf(type.uppercase())
|
||||
val notificationRef = NotificationReference(notificationType, message, recordId)
|
||||
val notification = client.getNotification(notificationRef)
|
||||
return@Coroutine gson.toJson(notification)
|
||||
}
|
||||
|
||||
AsyncFunction("getStudentCertificates") Coroutine { ->
|
||||
val certificates = client.getStudentCertificates()
|
||||
return@Coroutine gson.toJson(certificates)
|
||||
}
|
||||
|
||||
AsyncFunction("getDocumentsPage") Coroutine { path: String ->
|
||||
val documents = client.getDocumentsPage(path)
|
||||
return@Coroutine gson.toJson(documents)
|
||||
}
|
||||
|
||||
AsyncFunction("getDocumentsPageDefault") Coroutine { ->
|
||||
val documents = client.getDocumentsPage()
|
||||
return@Coroutine gson.toJson(documents)
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// CanteenClient Functions
|
||||
// ==========================================
|
||||
|
||||
AsyncFunction("canteenLogin") Coroutine { username: String, pass: String ->
|
||||
return@Coroutine canteenClient.login(username, pass)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenLogout") Coroutine { ->
|
||||
canteenClient.logout()
|
||||
}
|
||||
|
||||
AsyncFunction("canteenIsLoggedIn") Coroutine { ->
|
||||
return@Coroutine canteenClient.isLoggedIn()
|
||||
}
|
||||
|
||||
AsyncFunction("canteenGetMenuPage") Coroutine { ->
|
||||
val menuPage = canteenClient.getMenuPage()
|
||||
return@Coroutine gson.toJson(menuPage)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenGetMenuAsync") Coroutine { daysStrings: List<String> ->
|
||||
val localDates = daysStrings.map { LocalDate.parse(it) }
|
||||
val dayMenusList = mutableListOf<io.github.tomhula.jecnaapi.data.canteen.DayMenu>()
|
||||
canteenClient.getMenuAsync(localDates).collect { dayMenu ->
|
||||
dayMenusList.add(dayMenu)
|
||||
}
|
||||
return@Coroutine gson.toJson(dayMenusList)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenGetDayMenu") Coroutine { dayString: String ->
|
||||
val day = LocalDate.parse(dayString)
|
||||
val dayMenu = canteenClient.getDayMenu(day)
|
||||
return@Coroutine gson.toJson(dayMenu)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenGetExchange") Coroutine { ->
|
||||
val exchange = canteenClient.getExchange()
|
||||
return@Coroutine gson.toJson(exchange)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenGetCredit") Coroutine { ->
|
||||
return@Coroutine canteenClient.getCredit()
|
||||
}
|
||||
|
||||
AsyncFunction("canteenOrderMenuItem") Coroutine { menuItemJson: String ->
|
||||
val menuItem = gson.fromJson(menuItemJson, MenuItem::class.java)
|
||||
return@Coroutine canteenClient.order(menuItem)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenOrderExchangeItem") Coroutine { number: Int, orderPath: String, dayString: String, amount: Int ->
|
||||
val day = LocalDate.parse(dayString)
|
||||
val exchangeItem = ExchangeItem(
|
||||
number = number,
|
||||
description = null,
|
||||
amount = amount,
|
||||
orderPath = orderPath,
|
||||
day = day
|
||||
)
|
||||
return@Coroutine canteenClient.order(exchangeItem)
|
||||
}
|
||||
|
||||
AsyncFunction("canteenPutOnExchange") Coroutine { menuItemJson: String ->
|
||||
val menuItem = gson.fromJson(menuItemJson, MenuItem::class.java)
|
||||
return@Coroutine canteenClient.putOnExchange(menuItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+101
-20
@@ -1,28 +1,109 @@
|
||||
import { Button, SafeAreaView, ScrollView, Text, View } from 'react-native';
|
||||
import { useState } from 'react';
|
||||
import { StyleSheet, Text, View, TextInput, Button, ScrollView, ActivityIndicator } from 'react-native';
|
||||
import JecnaapiReactNative from 'jecnaapi-react-native';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<ScrollView style={styles.container}>
|
||||
<Text style={styles.header}>Module API Example</Text>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [result, setResult] = useState<string>('No data yet. Please login.');
|
||||
|
||||
const handleTestLoginAndFetch = async () => {
|
||||
if (!username || !password) {
|
||||
setResult('Error: Enter username and password');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setResult('Logging in...');
|
||||
|
||||
try {
|
||||
// 1. Call the raw native Kotlin bridge
|
||||
const loginSuccess = await JecnaapiReactNative.login(username, password);
|
||||
|
||||
if (loginSuccess) {
|
||||
setResult('Login Successful! Fetching profile...');
|
||||
|
||||
// 2. Fetch the raw JSON string
|
||||
const rawProfileString = await JecnaapiReactNative.getStudentProfile();
|
||||
|
||||
// 3. Manually parse it (since you kept the default export)
|
||||
const profile = JSON.parse(rawProfileString);
|
||||
|
||||
// Display it nicely on screen
|
||||
setResult(JSON.stringify(profile, null, 2));
|
||||
} else {
|
||||
setResult('Login failed. Check credentials.');
|
||||
}
|
||||
} catch (error: any) {
|
||||
setResult(`Bridge Error: ${error.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
function Group(props: { name: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<View style={styles.group}>
|
||||
<Text style={styles.groupHeader}>{props.name}</Text>
|
||||
{props.children}
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.header}>JecnaAPI Native Tester</Text>
|
||||
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
autoCapitalize="none"
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
|
||||
<Button title="Test Login & Fetch Profile" onPress={handleTestLoginAndFetch} disabled={loading} />
|
||||
|
||||
{loading && <ActivityIndicator style={{ marginTop: 20 }} size="large" color="#0000ff" />}
|
||||
|
||||
<ScrollView style={styles.resultBox}>
|
||||
<Text style={styles.resultText}>{result}</Text>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = {
|
||||
header: { fontSize: 30, margin: 20 },
|
||||
groupHeader: { fontSize: 20, marginBottom: 20 },
|
||||
group: { margin: 20, backgroundColor: '#fff', borderRadius: 10, padding: 20 },
|
||||
container: { flex: 1, backgroundColor: '#eee' },
|
||||
view: { flex: 1, height: 200 },
|
||||
};
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
paddingTop: 80,
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
header: {
|
||||
fontSize: 22,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
textAlign: 'center',
|
||||
},
|
||||
input: {
|
||||
height: 50,
|
||||
borderColor: '#ccc',
|
||||
borderWidth: 1,
|
||||
borderRadius: 8,
|
||||
marginBottom: 15,
|
||||
paddingHorizontal: 15,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
resultBox: {
|
||||
marginTop: 20,
|
||||
flex: 1,
|
||||
backgroundColor: '#1e1e1e',
|
||||
borderRadius: 8,
|
||||
padding: 15,
|
||||
},
|
||||
resultText: {
|
||||
color: '#00FF00',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
||||
|
||||
+11
-1
@@ -22,6 +22,16 @@
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
[
|
||||
"expo-build-properties",
|
||||
{
|
||||
"android": {
|
||||
"minSdkVersion": 26
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
Generated
+15
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"expo": "~56.0.12",
|
||||
"expo-build-properties": "~56.0.19",
|
||||
"react": "19.2.3",
|
||||
"react-native": "0.85.3"
|
||||
},
|
||||
@@ -2625,6 +2626,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/expo-build-properties": {
|
||||
"version": "56.0.19",
|
||||
"resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-56.0.19.tgz",
|
||||
"integrity": "sha512-InoviXcxWosNp4cC7L3SWoiY99Xr2HdgN+LYHb6mUm/BBVxy1mIMrZR+3PJ2gwDZzW6EJNDz8ioASWGHBTmzpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expo/schema-utils": "^56.0.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"semver": "^7.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-modules-autolinking": {
|
||||
"version": "56.0.16",
|
||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.16.tgz",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"main": "index.ts",
|
||||
"dependencies": {
|
||||
"expo": "~56.0.12",
|
||||
"expo-build-properties": "~56.0.19",
|
||||
"react": "19.2.3",
|
||||
"react-native": "0.85.3"
|
||||
},
|
||||
|
||||
Generated
+188
-1225
File diff suppressed because it is too large
Load Diff
+13
-4
@@ -11,7 +11,8 @@
|
||||
"test": "node internal/module_scripts/test.js",
|
||||
"prepare": "node internal/module_scripts/prepare.js",
|
||||
"open:ios": "node internal/module_scripts/open-ios.js",
|
||||
"open:android": "node internal/module_scripts/open-android.js"
|
||||
"open:android": "node internal/module_scripts/open-android.js",
|
||||
"postinstall": "node scripts/download-ios.mjs"
|
||||
},
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@@ -31,14 +32,14 @@
|
||||
"@babel/core": "^7.26.0",
|
||||
"@types/jest": "^29.2.1",
|
||||
"@types/react": "~19.1.1",
|
||||
"babel-preset-expo": "~55.0.8",
|
||||
"babel-preset-expo": "~56.0.8",
|
||||
"eslint": "~9.39.4",
|
||||
"eslint-config-universe": "^15.0.3",
|
||||
"expo": "^56.0.11",
|
||||
"jest": "^29.7.0",
|
||||
"jest-expo": "~55.0.9",
|
||||
"prettier": "^3.0.0",
|
||||
"react-native": "0.82.1",
|
||||
"react-native": "0.85.3",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"jest": {
|
||||
@@ -51,5 +52,13 @@
|
||||
"expo": "*",
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build",
|
||||
"android",
|
||||
"ios",
|
||||
"scripts",
|
||||
"src/web",
|
||||
"jecnaapi-react-native.podspec"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import https from 'https';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const url =
|
||||
'https://github.com/tomhula/JecnaAPI/releases/download/v10.3.5/JecnaapiIOS-v10.3.5-xcframework.zip';
|
||||
const iosDir = path.join(__dirname, '..', 'ios');
|
||||
const zipPath = path.join(iosDir, 'framework.zip');
|
||||
|
||||
if (fs.existsSync(path.join(iosDir, 'JecnaapiIOS.xcframework'))) {
|
||||
console.log('JecnaapiIOS XCFramework already exists.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log('Downloading JecnaapiIOS XCFramework...');
|
||||
|
||||
function download(url, dest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(url, (res) => {
|
||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||
return download(res.headers.location, dest).then(resolve).catch(reject);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
return reject(new Error(`Server responded with status code ${res.statusCode}`));
|
||||
}
|
||||
const file = fs.createWriteStream(dest);
|
||||
res.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close(resolve);
|
||||
});
|
||||
})
|
||||
.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
download(url, zipPath)
|
||||
.then(() => {
|
||||
console.log('Unzipping framework...');
|
||||
execSync(`unzip -o -q "${zipPath}" -d "${iosDir}"`);
|
||||
fs.unlinkSync(zipPath);
|
||||
console.log('iOS framework successfully linked.');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to download framework:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1 +1,143 @@
|
||||
// Define your exported module types here.
|
||||
export type JecnaapiReactNativeModuleEvents = {};
|
||||
|
||||
export type SchoolYearHalf = 'FIRST' | 'SECOND';
|
||||
|
||||
export type MonthName =
|
||||
| 'JANUARY'
|
||||
| 'FEBRUARY'
|
||||
| 'MARCH'
|
||||
| 'APRIL'
|
||||
| 'MAY'
|
||||
| 'JUNE'
|
||||
| 'JULY'
|
||||
| 'AUGUST'
|
||||
| 'SEPTEMBER'
|
||||
| 'OCTOBER'
|
||||
| 'NOVEMBER'
|
||||
| 'DECEMBER';
|
||||
|
||||
export type NotificationType = 'GRADE' | 'ABSENCE' | 'MESSAGE' | string;
|
||||
|
||||
// School portal types
|
||||
|
||||
export interface StudentProfile {
|
||||
userName?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
className?: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export interface News {
|
||||
title: string;
|
||||
content: string;
|
||||
date: string;
|
||||
author?: string;
|
||||
}
|
||||
|
||||
export interface Grade {
|
||||
subject: string;
|
||||
value: string;
|
||||
weight?: number;
|
||||
date: string;
|
||||
teacher?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export interface SubjectGrades {
|
||||
subject: string;
|
||||
average?: number;
|
||||
grades: Grade[];
|
||||
}
|
||||
|
||||
export interface TimetableEntry {
|
||||
subject: string;
|
||||
teacher?: string;
|
||||
room?: string;
|
||||
dayOfWeek?: number;
|
||||
hour?: number;
|
||||
parity?: string;
|
||||
}
|
||||
|
||||
export interface AttendanceRecord {
|
||||
subject: string;
|
||||
date: string;
|
||||
status: string;
|
||||
excused?: boolean;
|
||||
}
|
||||
|
||||
export interface AbsenceRecord {
|
||||
subject: string;
|
||||
date: string;
|
||||
hours: number;
|
||||
excused: boolean;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export interface Teacher {
|
||||
name: string;
|
||||
tag: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export interface Room {
|
||||
code: string;
|
||||
name?: string;
|
||||
capacity?: number;
|
||||
}
|
||||
|
||||
export interface Locker {
|
||||
number?: string;
|
||||
location?: string;
|
||||
validFrom?: string;
|
||||
validTo?: string;
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
type: string;
|
||||
message: string;
|
||||
recordId: number;
|
||||
date?: string;
|
||||
read?: boolean;
|
||||
}
|
||||
|
||||
export interface Certificate {
|
||||
title: string;
|
||||
date: string;
|
||||
issuer?: string;
|
||||
}
|
||||
|
||||
export interface Document {
|
||||
name: string;
|
||||
path: string;
|
||||
isDirectory: boolean;
|
||||
size?: number;
|
||||
lastModified?: string;
|
||||
}
|
||||
|
||||
// Canteen types
|
||||
|
||||
export interface MenuItem {
|
||||
id?: number;
|
||||
name: string;
|
||||
price?: number;
|
||||
allergens?: string[];
|
||||
mealType?: string;
|
||||
}
|
||||
|
||||
export interface DayMenu {
|
||||
date: string;
|
||||
items: MenuItem[];
|
||||
}
|
||||
|
||||
export interface ExchangeItem {
|
||||
number: number;
|
||||
orderPath: string;
|
||||
day: string;
|
||||
amount: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface CreditBalance {
|
||||
balance: number;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,72 @@
|
||||
import { NativeModule, requireNativeModule } from 'expo';
|
||||
|
||||
declare class JecnaapiReactNativeModule extends NativeModule<{}> {}
|
||||
import { JecnaapiReactNativeModuleEvents } from './JecnaapiReactNative.types';
|
||||
|
||||
declare class JecnaapiReactNativeModule extends NativeModule<JecnaapiReactNativeModuleEvents> {
|
||||
// ====================
|
||||
// JecnaClient (school portal)
|
||||
// ====================
|
||||
|
||||
login: (username: string, password: string) => Promise<boolean>;
|
||||
logout: () => Promise<void>;
|
||||
isLoggedIn: () => Promise<boolean>;
|
||||
|
||||
getNewsPage: () => Promise<string>;
|
||||
|
||||
getGradesPage: () => Promise<string>;
|
||||
getGradesPageByYear: (firstCalendarYear: number, half: string) => Promise<string>;
|
||||
|
||||
getTimetablePage: () => Promise<string>;
|
||||
getTimetablePageByYear: (firstCalendarYear: number, periodId: number | null) => Promise<string>;
|
||||
|
||||
getAttendancesPage: () => Promise<string>;
|
||||
getAttendancesPageByMonth: (firstCalendarYear: number, monthName: string) => Promise<string>;
|
||||
|
||||
getAbsencesPage: () => Promise<string>;
|
||||
getAbsencesPageByYear: (firstCalendarYear: number) => Promise<string>;
|
||||
|
||||
getTeachersPage: () => Promise<string>;
|
||||
getTeacher: (teacherTag: string) => Promise<string>;
|
||||
|
||||
getRoomsPage: () => Promise<string>;
|
||||
getRoom: (roomCode: string) => Promise<string>;
|
||||
|
||||
getLocker: () => Promise<string>;
|
||||
|
||||
getStudentProfile: () => Promise<string>;
|
||||
getStudentProfileByUsername: (username: string) => Promise<string>;
|
||||
|
||||
getNotifications: () => Promise<string>;
|
||||
getNotification: (type: string, message: string, recordId: number) => Promise<string>;
|
||||
|
||||
getStudentCertificates: () => Promise<string>;
|
||||
|
||||
getDocumentsPage: (path: string) => Promise<string>;
|
||||
getDocumentsPageDefault: () => Promise<string>;
|
||||
|
||||
// ====================
|
||||
// CanteenClient
|
||||
// ====================
|
||||
|
||||
canteenLogin: (username: string, password: string) => Promise<boolean>;
|
||||
canteenLogout: () => Promise<void>;
|
||||
canteenIsLoggedIn: () => Promise<boolean>;
|
||||
|
||||
canteenGetMenuPage: () => Promise<string>;
|
||||
canteenGetMenuAsync: (daysStrings: string[]) => Promise<string>;
|
||||
canteenGetDayMenu: (dayString: string) => Promise<string>;
|
||||
|
||||
canteenGetExchange: () => Promise<string>;
|
||||
canteenGetCredit: () => Promise<number>;
|
||||
|
||||
canteenOrderMenuItem: (menuItemJson: string) => Promise<boolean>;
|
||||
canteenOrderExchangeItem: (
|
||||
number: number,
|
||||
orderPath: string,
|
||||
dayString: string,
|
||||
amount: number
|
||||
) => Promise<boolean>;
|
||||
canteenPutOnExchange: (menuItemJson: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export default requireNativeModule<JecnaapiReactNativeModule>('JecnaapiReactNative');
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
import { registerWebModule, NativeModule } from 'expo';
|
||||
|
||||
class JecnaapiReactNativeModule extends NativeModule<{}> {}
|
||||
function unavailable(): never {
|
||||
throw new Error('JecnaapiReactNative is not available on web');
|
||||
}
|
||||
|
||||
class JecnaapiReactNativeModule extends NativeModule<{}> {
|
||||
login = (_username: string, _password: string) => unavailable();
|
||||
logout = () => unavailable();
|
||||
isLoggedIn = () => unavailable();
|
||||
|
||||
getNewsPage = () => unavailable();
|
||||
getGradesPage = () => unavailable();
|
||||
getGradesPageByYear = (_firstCalendarYear: number, _half: string) => unavailable();
|
||||
getTimetablePage = () => unavailable();
|
||||
getTimetablePageByYear = (_firstCalendarYear: number, _periodId: number | null) => unavailable();
|
||||
getAttendancesPage = () => unavailable();
|
||||
getAttendancesPageByMonth = (_firstCalendarYear: number, _monthName: string) => unavailable();
|
||||
getAbsencesPage = () => unavailable();
|
||||
getAbsencesPageByYear = (_firstCalendarYear: number) => unavailable();
|
||||
getTeachersPage = () => unavailable();
|
||||
getTeacher = (_teacherTag: string) => unavailable();
|
||||
getRoomsPage = () => unavailable();
|
||||
getRoom = (_roomCode: string) => unavailable();
|
||||
getLocker = () => unavailable();
|
||||
getStudentProfile = () => unavailable();
|
||||
getStudentProfileByUsername = (_username: string) => unavailable();
|
||||
getNotifications = () => unavailable();
|
||||
getNotification = (_type: string, _message: string, _recordId: number) => unavailable();
|
||||
getStudentCertificates = () => unavailable();
|
||||
getDocumentsPage = (_path: string) => unavailable();
|
||||
getDocumentsPageDefault = () => unavailable();
|
||||
|
||||
canteenLogin = (_username: string, _password: string) => unavailable();
|
||||
canteenLogout = () => unavailable();
|
||||
canteenIsLoggedIn = () => unavailable();
|
||||
canteenGetMenuPage = () => unavailable();
|
||||
canteenGetMenuAsync = (_daysStrings: string[]) => unavailable();
|
||||
canteenGetDayMenu = (_dayString: string) => unavailable();
|
||||
canteenGetExchange = () => unavailable();
|
||||
canteenGetCredit = () => unavailable();
|
||||
canteenOrderMenuItem = (_menuItemJson: string) => unavailable();
|
||||
canteenOrderExchangeItem = (
|
||||
_number: number,
|
||||
_orderPath: string,
|
||||
_dayString: string,
|
||||
_amount: number
|
||||
) => unavailable();
|
||||
canteenPutOnExchange = (_menuItemJson: string) => unavailable();
|
||||
}
|
||||
|
||||
export default registerWebModule(JecnaapiReactNativeModule, 'JecnaapiReactNativeModule');
|
||||
|
||||
Reference in New Issue
Block a user