forked from firka/firka
Add logging, await token callback and widget refresh
Fixes async token handling and improves observability: await the callback in KretaClient to ensure proper async flow, add informative logs for token expiry/refresh and warn on empty 200/201 API responses. Enhance token refresh flow in token_grant with detailed info/warning/severe logs and exception logging for easier debugging. Update widget DB helper to force fresh fetches for timetables/grades when updating widget cache, avoid writing empty caches and add debug prints for counts and cache status. Wire widget refresh into LiveActivityService background fetch (with import and try/catch logging) so iOS widgets get refreshed during background processing.
This commit is contained in:
@@ -66,7 +66,7 @@ class KretaClient {
|
||||
}
|
||||
_tokenMutex = true;
|
||||
try {
|
||||
return callback();
|
||||
return await callback();
|
||||
} finally {
|
||||
_tokenMutex = false;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class KretaClient {
|
||||
|
||||
if (now.millisecondsSinceEpoch >=
|
||||
model.expiryDate!.millisecondsSinceEpoch) {
|
||||
logger.finest("Token expired, refreshing: $model");
|
||||
logger.info("Token expired at ${model.expiryDate}, refreshing for user: ${model.studentId}");
|
||||
var extended = await extendToken(model);
|
||||
var tokenModel = TokenModel.fromResp(extended);
|
||||
|
||||
@@ -86,7 +86,7 @@ class KretaClient {
|
||||
await isar.tokenModels.put(tokenModel);
|
||||
});
|
||||
|
||||
logger.finest("Token refreshed and saved: $model");
|
||||
logger.info("Token refreshed successfully. New expiry: ${tokenModel.expiryDate}");
|
||||
|
||||
model = tokenModel;
|
||||
}
|
||||
@@ -116,6 +116,15 @@ class KretaClient {
|
||||
if (!url.endsWith("TanuloAdatlap")) {
|
||||
logger.finest("Response: ${resp.statusCode} ${resp.data}");
|
||||
}
|
||||
|
||||
if (resp.statusCode == 200 || resp.statusCode == 201) {
|
||||
final responseData = resp.data;
|
||||
if (responseData == null ||
|
||||
(responseData is List && responseData.isEmpty) ||
|
||||
(responseData is Map && responseData.isEmpty)) {
|
||||
logger.warning("API returned ${resp.statusCode} with empty data for: $url - possible stale session");
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
if (ex is Error) {
|
||||
logger.shout(
|
||||
|
||||
@@ -41,6 +41,8 @@ Future<TokenGrantResponse> getAccessToken(String code) async {
|
||||
}
|
||||
|
||||
Future<TokenGrantResponse> extendToken(TokenModel model) async {
|
||||
logger.info("Extending token for user: ${model.studentId}, institute: ${model.iss}");
|
||||
|
||||
final headers = <String, String>{
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"accept": "*/*",
|
||||
@@ -60,16 +62,21 @@ Future<TokenGrantResponse> extendToken(TokenModel model) async {
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
logger.info("Token extended successfully for user: ${model.studentId}");
|
||||
return TokenGrantResponse.fromJson(response.data);
|
||||
case 400:
|
||||
logger.warning("Token refresh failed (400) - refresh token expired for user: ${model.studentId}");
|
||||
throw TokenExpiredException();
|
||||
case 401:
|
||||
logger.warning("Token refresh failed (401) - invalid grant for user: ${model.studentId}");
|
||||
throw InvalidGrantException();
|
||||
default:
|
||||
logger.severe("Token refresh failed with unexpected status: ${response.statusCode} for user: ${model.studentId}");
|
||||
throw Exception(
|
||||
"Failed to get access token, response code: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
logger.severe("Token refresh exception for user: ${model.studentId}: $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,13 +64,16 @@ class WidgetCacheHelper {
|
||||
|
||||
final start = now.subtract(Duration(days: 7));
|
||||
final end = now.add(Duration(days: 14));
|
||||
final lessons = await client.getTimeTable(start, end);
|
||||
final lessons = await client.getTimeTable(start, end, forceCache: false);
|
||||
|
||||
final widgetFile = File(p.join(dataDir.path, "widget_state.json"));
|
||||
|
||||
if (lessons.response != null) {
|
||||
debugPrint('Android widget cache: ${lessons.response!.length} lessons (cached: ${lessons.cached})');
|
||||
widgetFile.writeAsString(
|
||||
jsonEncode(WidgetCacheHelper.toJson(style, lessons.response!)));
|
||||
} else {
|
||||
debugPrint('Android widget cache: No lessons to cache');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +140,6 @@ class WidgetCacheHelper {
|
||||
theme = isLightMode.value ? 'light' : 'dark';
|
||||
}
|
||||
|
||||
// Get today's and tomorrow's lessons
|
||||
final now = timeNow();
|
||||
final todayMidnight = DateTime(now.year, now.month, now.day);
|
||||
final tomorrowMidnight = todayMidnight.add(Duration(days: 1));
|
||||
@@ -145,19 +147,24 @@ class WidgetCacheHelper {
|
||||
final todayResponse = await client.getTimeTable(
|
||||
todayMidnight,
|
||||
todayMidnight.add(Duration(hours: 23, minutes: 59)),
|
||||
forceCache: false,
|
||||
);
|
||||
final tomorrowResponse = await client.getTimeTable(
|
||||
tomorrowMidnight,
|
||||
tomorrowMidnight.add(Duration(hours: 23, minutes: 59)),
|
||||
forceCache: false,
|
||||
);
|
||||
|
||||
final todayLessons = todayResponse.response ?? [];
|
||||
final tomorrowLessons = tomorrowResponse.response ?? [];
|
||||
|
||||
// Get grades
|
||||
final gradesResponse = await client.getGrades();
|
||||
debugPrint('iOS widget refresh: ${todayLessons.length} today lessons, ${tomorrowLessons.length} tomorrow lessons');
|
||||
|
||||
final gradesResponse = await client.getGrades(forceCache: false);
|
||||
final grades = gradesResponse.response ?? [];
|
||||
|
||||
debugPrint('iOS widget refresh: ${grades.length} grades fetched (cached: ${gradesResponse.cached})');
|
||||
|
||||
// Calculate subject averages
|
||||
final Map<String, double> subjectAverages = {};
|
||||
final Set<String> subjectUids = {};
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:firka/helpers/api/client/live_activity_backend_client.dart';
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/db/widget.dart';
|
||||
import 'package:firka/helpers/live_activity_manager.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/ui/phone/screens/live_activity/live_activity_consent_screen.dart';
|
||||
@@ -443,6 +444,14 @@ class LiveActivityService {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
_logger.info('Background fetch: refreshing iOS widgets...');
|
||||
await WidgetCacheHelper.refreshIOSWidgets(client, initData.settings);
|
||||
_logger.info('Background fetch: iOS widgets refreshed successfully');
|
||||
} catch (e) {
|
||||
_logger.warning('Background fetch: failed to refresh iOS widgets: $e');
|
||||
}
|
||||
|
||||
bool foundFirstSchoolDay = false;
|
||||
for (int dayOffset = 1; dayOffset <= 5; dayOffset++) {
|
||||
final candidateDay = endOfWeek.add(Duration(days: dayOffset));
|
||||
|
||||
Reference in New Issue
Block a user