refactored data providers with centralized cache resolution, unified UI using custom dialogs and bottom sheets, and enhanced network error handling for Dio and TLS errors

This commit is contained in:
2026-05-08 20:01:45 +02:00
parent c62a14645a
commit 9e139b5704
37 changed files with 595 additions and 753 deletions
@@ -2,26 +2,22 @@ import '../../../../../api/marianumcloud/talk/create_room/create_room.dart';
import '../../../../../api/marianumcloud/talk/create_room/create_room_params.dart';
import '../../../../../api/marianumcloud/talk/room/get_room_cache.dart';
import '../../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../../api/request_cache.dart';
class ChatListDataProvider {
Future<GetRoomResponse> getRooms({
void Function(Object)? onError,
bool renew = false,
}) async {
GetRoomResponse? latest;
Object? capturedError;
final cache = GetRoomCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getRooms');
}
}) =>
resolveFromCache<GetRoomResponse>(
(onUpdate, onError) => GetRoomCache(
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getRooms',
);
Future<void> createDirectRoom(String invite) =>
CreateRoom(CreateRoomParams(roomType: 1, invite: invite)).run();
@@ -3,6 +3,7 @@ import 'package:nextcloud/nextcloud.dart';
import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_cache.dart';
import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_response.dart';
import '../../../../../api/marianumcloud/webdav/webdav_api.dart';
import '../../../../../api/request_cache.dart';
class FilesDataProvider {
/// Lists files at [path]. Cached payload is delivered via [onCacheData] as
@@ -14,22 +15,17 @@ class FilesDataProvider {
String path, {
void Function(ListFilesResponse)? onCacheData,
void Function(Object)? onError,
}) async {
ListFilesResponse? latest;
Object? capturedError;
final cache = ListFilesCache(
path: path,
onUpdate: (data) => latest = data,
onCacheData: onCacheData,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from listFiles');
}
}) =>
resolveFromCache<ListFilesResponse>(
(onUpdate, onError) => ListFilesCache(
path: path,
onUpdate: onUpdate,
onCacheData: onCacheData,
onError: onError,
),
onError: onError,
operationName: 'listFiles',
);
Future<void> createFolder(String fullPath) async {
final webdav = await WebdavApi.webdav;
@@ -10,6 +10,7 @@ import '../../../../../api/mhsl/custom_timetable_event/remove/remove_custom_time
import '../../../../../api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.dart';
import '../../../../../api/mhsl/custom_timetable_event/update/update_custom_timetable_event.dart';
import '../../../../../api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.dart';
import '../../../../../api/request_cache.dart';
import '../../../../../api/webuntis/queries/get_holidays/get_holidays_cache.dart';
import '../../../../../api/webuntis/queries/get_holidays/get_holidays_response.dart';
import '../../../../../api/webuntis/queries/get_rooms/get_rooms_cache.dart';
@@ -30,112 +31,84 @@ class TimetableDataProvider {
DateTime endDate, {
void Function(Object)? onError,
bool renew = false,
}) async {
GetTimetableResponse? latest;
Object? capturedError;
final cache = GetTimetableCache(
startdate: int.parse(_dateFormat.format(startDate)),
enddate: int.parse(_dateFormat.format(endDate)),
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getWeek');
}
}) =>
resolveFromCache<GetTimetableResponse>(
(onUpdate, onError) => GetTimetableCache(
startdate: int.parse(_dateFormat.format(startDate)),
enddate: int.parse(_dateFormat.format(endDate)),
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getWeek',
);
Future<GetRoomsResponse> getRooms({
void Function(Object)? onError,
bool renew = false,
}) async {
GetRoomsResponse? latest;
Object? capturedError;
final cache = GetRoomsCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getRooms');
}
}) =>
resolveFromCache<GetRoomsResponse>(
(onUpdate, onError) => GetRoomsCache(
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getRooms',
);
Future<GetSubjectsResponse> getSubjects({
void Function(Object)? onError,
bool renew = false,
}) async {
GetSubjectsResponse? latest;
Object? capturedError;
final cache = GetSubjectsCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getSubjects');
}
}) =>
resolveFromCache<GetSubjectsResponse>(
(onUpdate, onError) => GetSubjectsCache(
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getSubjects',
);
Future<GetHolidaysResponse> getSchoolHolidays({
void Function(Object)? onError,
bool renew = false,
}) async {
GetHolidaysResponse? latest;
Object? capturedError;
final cache = GetHolidaysCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getSchoolHolidays');
}
}) =>
resolveFromCache<GetHolidaysResponse>(
(onUpdate, onError) => GetHolidaysCache(
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getSchoolHolidays',
);
Future<GetTimegridUnitsResponse> getTimegrid({bool renew = false}) async {
GetTimegridUnitsResponse? latest;
Object? capturedError;
final cache = GetTimegridUnitsCache(
renew: renew,
onUpdate: (data) => latest = data,
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getTimegrid');
}
Future<GetTimegridUnitsResponse> getTimegrid({bool renew = false}) =>
resolveFromCache<GetTimegridUnitsResponse>(
(onUpdate, _) => GetTimegridUnitsCache(
renew: renew,
onUpdate: onUpdate,
),
operationName: 'getTimegrid',
);
Future<GetCustomTimetableEventResponse> getCustomEvents({
bool renew = false,
void Function(Object)? onError,
}) async {
GetCustomTimetableEventResponse? latest;
Object? capturedError;
final cache = GetCustomTimetableEventCache(
GetCustomTimetableEventParams(AccountData().getUserSecret()),
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getCustomEvents');
}
}) =>
resolveFromCache<GetCustomTimetableEventResponse>(
(onUpdate, onError) => GetCustomTimetableEventCache(
GetCustomTimetableEventParams(AccountData().getUserSecret()),
renew: renew,
onUpdate: onUpdate,
onError: onError,
),
onError: onError,
operationName: 'getCustomEvents',
);
Future<void> addCustomEvent(CustomTimetableEvent event) =>
AddCustomTimetableEvent(AddCustomTimetableEventParams(AccountData().getUserSecret(), event)).run();