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.4.1
NEXT_PUBLIC_APP_VERSION=4.4.2

# ⚠️ 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.4.1
MIN_APP_VERSION=4.4.2

# ℹ️ 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.4.1');
const String.fromEnvironment('APP_VERSION', defaultValue: '4.4.2');

/// Commit SHA injected by CI for release builds.
static String get appCommitSha =>
Expand Down
26 changes: 16 additions & 10 deletions mobile/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,14 @@ void main() async {
try {
await _initializeFirebase();

// Initialize Analytics after Firebase is ready
try {
await AnalyticsService.initialize();
} on Object catch (_) {
AppLogger.e('Analytics initialization failed');
}
// Initialize Analytics after Firebase is ready — do not block startup
AppLogger.safeUnawait(
AnalyticsService.initialize().catchError(
(Object e, StackTrace st) =>
AppLogger.e('Analytics initialization failed', e, st),
),
'Analytics init',
);

AppLogger.i('🛡️ [FIREBASE SHIELD] Initializing App Check...');
await SecurityInitializer.initialize();
Expand Down Expand Up @@ -175,10 +177,14 @@ void main() async {
// Eagerly pre-warm cryptographic services concurrently while other SDKs/Fonts initialize
AppLogger.safeUnawait(JweService.instance.preWarm(), 'JWE pre-warm');

await GoogleFonts.pendingFonts([
GoogleFonts.manrope(),
GoogleFonts.firaCode(),
]);
// Defer font pre-warm so UI can render faster
AppLogger.safeUnawait(
GoogleFonts.pendingFonts([
GoogleFonts.manrope(),
GoogleFonts.firaCode(),
]),
'Fonts pre-warm',
);

runApp(
ProviderScope(
Expand Down
32 changes: 22 additions & 10 deletions mobile/lib/screens/splash_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,33 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {

Future<void> _initializeApp() async {
// 1. Proactively pre-warm security layers while logo is showing
final jwePreWarm = JweService.instance.preWarm();
final apiPreWarm = ref.read(apiServiceProvider).preWarm();
// Keep the splash visible for 2s to improve perceived startup time
final splashHold = Future<void>.delayed(
const Duration(milliseconds: 3000),
const Duration(milliseconds: 2000),
() {
AppLogger.i('SplashScreen: 3s delay completed');
AppLogger.i('SplashScreen: 2s delay completed');
},
);

try {
await jwePreWarm;
await apiPreWarm;
} on Object catch (e) {
AppLogger.e('SplashScreen: JWKS/API pre-warm failed', e);
}
// Kick off non-critical pre-warms in the background so they do not block
AppLogger.safeUnawait(
JweService.instance.preWarm().catchError(
(Object e, StackTrace st) =>
AppLogger.e('SplashScreen: JWE pre-warm failed', e, st),
),
'SplashScreen: JWE pre-warm',
);

AppLogger.safeUnawait(
ref
.read(apiServiceProvider)
.preWarm()
.catchError(
(Object e, StackTrace st) =>
AppLogger.e('SplashScreen: API pre-warm failed', e, st),
),
'SplashScreen: API pre-warm',
);

// 2. Critical Security Check First
try {
Expand Down
8 changes: 7 additions & 1 deletion mobile/lib/services/dio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class DioService {
// Futures to deduplicate parallel token requests
Future<String?>? _tokenFetchInFlight;
Future<String?>? _limitedTokenFetchInFlight;
// Instrumentation: count how many times we requested a limited-use token
static int _limitedTokenRequestCount = 0;

void _handle401(RequestOptions options) {
if (suppress401) return;
Expand All @@ -133,6 +135,10 @@ class DioService {
if (useLimited) {
var isNew = false;
if (_limitedTokenFetchInFlight == null) {
_limitedTokenRequestCount++;
AppLogger.d(
'DioService: getLimitedUseToken requested (count: $_limitedTokenRequestCount)',
);
_limitedTokenFetchInFlight = _appCheck.getLimitedUseToken();
isNew = true;
}
Expand All @@ -141,7 +147,7 @@ class DioService {
);
if (isNew) {
AppLogger.safeUnawait(
Future.delayed(const Duration(seconds: 5), () {
Future.delayed(const Duration(seconds: 30), () {
_limitedTokenFetchInFlight = null;
}).catchError(
(Object e, StackTrace st) {
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.4.1+1
version: 4.4.2+1
environment:
sdk: ^3.11.4

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.4.1",
"version": "4.4.2",
"private": true,
"engines": {
"node": ">=22.12.0",
Expand Down
2 changes: 1 addition & 1 deletion 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.4.1
version: 4.4.2
description: |
**GhostClass API** provides endpoints for authentication, profile synchronization,
attendance integrations with EzyGo, telemetry, and build provenance.
Expand Down
Loading