Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .example.env
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ NEXT_PUBLIC_APP_NAME=GhostClass

# ⚠️ App version displayed in footer and health checks
# 🔨 Build-time (Infisical `/build-time` folder)
NEXT_PUBLIC_APP_VERSION=4.3.8
NEXT_PUBLIC_APP_VERSION=4.3.9

# ⚠️ Your production domain WITHOUT https://
# All URL-based variables are derived from this.
Expand Down Expand Up @@ -359,7 +359,7 @@ JWE_PRIVATE_KEY=

# ⚠️ Minimum supported app version required to bypass forced update
# 🚀 Runtime (Infisical `/runtime` folder → Server Env Var)
MIN_APP_VERSION=4.3.8
MIN_APP_VERSION=4.3.9

# ℹ️ Enforce Firebase App Check for all mobile clients in production
# Valid: "true", "false" (default: false in dev, true recommended in prod)
Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class AppConfig {

/// Current application version (derived from Infisical compilation injection).
static String get appVersion =>
const String.fromEnvironment('APP_VERSION', defaultValue: '4.3.8');
const String.fromEnvironment('APP_VERSION', defaultValue: '4.3.9');

/// Commit SHA injected by CI for release builds.
static String get appCommitSha =>
Expand Down
4 changes: 0 additions & 4 deletions mobile/lib/providers/dashboard_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ class DashboardNotifier extends AsyncNotifier<DashboardData> {
.from('class_courses')
.select()
.eq('class_id', classId)
.eq('academic_year', academic.year)
.eq('semester', academic.semester)
.then((coursesRes) {
if (coursesRes.isNotEmpty) {
sharedCourses = (coursesRes as List).map((raw) {
Expand All @@ -261,8 +259,6 @@ class DashboardNotifier extends AsyncNotifier<DashboardData> {
.from('course_instructors')
.select()
.eq('class_id', classId)
.eq('semester', academic.semester)
.eq('academic_year', academic.year)
.then((instructorsRes) {
if (instructorsRes.isNotEmpty) {
sharedInstructors = (instructorsRes as List)
Expand Down
7 changes: 6 additions & 1 deletion mobile/lib/screens/dashboard_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DashboardScreen extends ConsumerStatefulWidget {

class _DashboardScreenState extends ConsumerState<DashboardScreen> {
bool _isDialogOpen = false;
bool _hasSeenSyncing = false;

void _checkAndShowClassDialog() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
Expand Down Expand Up @@ -56,6 +57,10 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final user = ref.watch(authProvider).value;
final isSyncing = user?.isSyncing ?? false;

if (isSyncing) {
_hasSeenSyncing = true;
}

if (dashboardState.isLoading || isSyncing) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
Expand Down Expand Up @@ -85,7 +90,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
);
}

if (user != null && !isSyncing) {
if (user != null && !isSyncing && _hasSeenSyncing) {
_checkAndShowClassDialog();
}

Expand Down
8 changes: 0 additions & 8 deletions mobile/lib/services/api_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,13 @@ class ApiService {
Future<Response<dynamic>> addCourse({
required String courseCode,
required String courseName,
required String semester,
required String academicYear,
required String supabaseToken,
}) async {
return client.post(
'${AppConfig.ghostclassApiUrl}/courses/add',
data: {
'courseCode': courseCode,
'courseName': courseName,
'semester': semester,
'academicYear': academicYear,
},
options: Options(headers: {'Authorization': 'Bearer $supabaseToken'}),
);
Expand All @@ -229,17 +225,13 @@ class ApiService {
Future<Response<dynamic>> upsertInstructor({
required String courseCode,
required String instructorName,
required String semester,
required String academicYear,
required String supabaseToken,
}) async {
return client.post(
'${AppConfig.ghostclassApiUrl}/instructors/upsert',
data: {
'courseCode': courseCode,
'instructorName': instructorName,
'semester': semester,
'academicYear': academicYear,
},
options: Options(headers: {'Authorization': 'Bearer $supabaseToken'}),
);
Expand Down
2 changes: 0 additions & 2 deletions mobile/lib/widgets/attendance/add_course_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ class _AddCourseDialogState extends ConsumerState<AddCourseDialog> {
final res = await api.addCourse(
courseCode: _codeController.text.trim().toUpperCase(),
courseName: _nameController.text.trim().replaceAll(RegExp(r'\s+'), ' '),
semester: widget.semester,
academicYear: widget.academicYear,
supabaseToken: supabaseToken,
);

Expand Down
6 changes: 1 addition & 5 deletions mobile/lib/widgets/attendance/edit_instructor_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ghostclass/logic/attendance_utils.dart' as utils;
import 'package:ghostclass/logic/error_utils.dart';
import 'package:ghostclass/providers/academic_provider.dart';
import 'package:ghostclass/providers/auth_provider.dart';
import 'package:ghostclass/providers/dashboard_provider.dart';
import 'package:ghostclass/services/api_service.dart';
Expand Down Expand Up @@ -55,9 +54,8 @@ class _EditInstructorDialogState extends ConsumerState<EditInstructorDialog> {
setState(() => _isSaving = true);

try {
final academic = ref.read(academicProvider).value;
final auth = ref.read(authProvider).value;
if (academic == null || auth == null) throw Exception('Missing context');
if (auth == null) throw Exception('Missing context');

final apiService = ref.read(apiServiceProvider);
final client = ref.read(supabaseClientProvider);
Expand All @@ -67,8 +65,6 @@ class _EditInstructorDialogState extends ConsumerState<EditInstructorDialog> {
await apiService.upsertInstructor(
courseCode: widget.courseCode,
instructorName: name,
semester: academic.semester,
academicYear: academic.year,
supabaseToken: supabaseToken,
);

Expand Down
2 changes: 1 addition & 1 deletion mobile/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 4.3.8+1
version: 4.3.9+1
environment:
sdk: ^3.11.4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ void main() {
() => mockApi.upsertInstructor(
courseCode: any(named: 'courseCode'),
instructorName: any(named: 'instructorName'),
semester: any(named: 'semester'),
academicYear: any(named: 'academicYear'),
supabaseToken: any(named: 'supabaseToken'),
),
).thenAnswer(
Expand Down Expand Up @@ -159,8 +157,6 @@ void main() {
() => mockApi.upsertInstructor(
courseCode: 'CS101',
instructorName: 'Dr. New',
semester: 'odd',
academicYear: '2024-2025',
supabaseToken: 'test-supabase-token',
),
).called(1);
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ghostclass",
"version": "4.3.8",
"version": "4.3.9",
"private": true,
"engines": {
"node": ">=22.12.0",
Expand Down
23 changes: 2 additions & 21 deletions public/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ openapi: 3.1.0

info:
title: GhostClass API
version: 4.3.8
version: 4.3.9
description: |
**GhostClass API** provides endpoints for authentication, profile synchronization,
attendance integrations with EzyGo, telemetry, and build provenance.
Expand Down Expand Up @@ -749,7 +749,6 @@ paths:
description: Too many requests
content:
application/json:
schema:
$ref: "#/components/schemas/RateLimitError"

/api/courses/add:
Expand All @@ -773,8 +772,6 @@ paths:
required:
- courseCode
- courseName
- semester
- academicYear
properties:
courseCode:
type: string
Expand All @@ -784,14 +781,6 @@ paths:
type: string
description: The name of the course
example: "Introduction to Computer Science"
semester:
type: string
description: The semester name
example: "Fall 2026"
academicYear:
type: string
description: The academic year
example: "2026"
responses:
"201":
description: Course added successfully
Expand Down Expand Up @@ -833,7 +822,7 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"

/api/instructors/upsert:
post:
tags:
Expand All @@ -854,21 +843,13 @@ paths:
required:
- courseCode
- instructorName
- semester
- academicYear
properties:
courseCode:
type: string
example: "CS101"
instructorName:
type: string
example: "Dr. John Doe"
semester:
type: string
example: "Fall 2026"
academicYear:
type: string
example: "2026"
responses:
"200":
description: Instructor saved successfully
Expand Down
Loading
Loading