dart format
This commit is contained in:
@@ -4,8 +4,11 @@ import 'account_event.dart';
|
||||
import 'account_state.dart';
|
||||
|
||||
class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
AccountBloc({AccountStatus initialStatus = AccountStatus.undefined}) : super(AccountState(status: initialStatus)) {
|
||||
on<AccountStatusChanged>((event, emit) => emit(state.copyWith(status: event.status)));
|
||||
AccountBloc({AccountStatus initialStatus = AccountStatus.undefined})
|
||||
: super(AccountState(status: initialStatus)) {
|
||||
on<AccountStatusChanged>(
|
||||
(event, emit) => emit(state.copyWith(status: event.status)),
|
||||
);
|
||||
}
|
||||
|
||||
void setStatus(AccountStatus status) => add(AccountStatusChanged(status));
|
||||
|
||||
@@ -4,5 +4,6 @@ class AccountState {
|
||||
final AccountStatus status;
|
||||
const AccountState({this.status = AccountStatus.undefined});
|
||||
|
||||
AccountState copyWith({AccountStatus? status}) => AccountState(status: status ?? this.status);
|
||||
AccountState copyWith({AccountStatus? status}) =>
|
||||
AccountState(status: status ?? this.status);
|
||||
}
|
||||
|
||||
@@ -27,9 +27,18 @@ class AppModule {
|
||||
BreakerArea breakerArea;
|
||||
Widget Function() create;
|
||||
|
||||
AppModule(this.module, {required this.name, required this.icon, this.breakerArea = BreakerArea.global, required this.create});
|
||||
AppModule(
|
||||
this.module, {
|
||||
required this.name,
|
||||
required this.icon,
|
||||
this.breakerArea = BreakerArea.global,
|
||||
required this.create,
|
||||
});
|
||||
|
||||
static Map<Modules, AppModule> modules(BuildContext context, {bool showFiltered = false}) {
|
||||
static Map<Modules, AppModule> modules(
|
||||
BuildContext context, {
|
||||
bool showFiltered = false,
|
||||
}) {
|
||||
final settings = context.read<SettingsCubit>();
|
||||
var available = {
|
||||
Modules.timetable: AppModule(
|
||||
@@ -45,8 +54,12 @@ class AppModule {
|
||||
icon: () => BlocBuilder<ChatListBloc, LoadableState<ChatListState>>(
|
||||
builder: (context, state) {
|
||||
final rooms = state.data?.rooms;
|
||||
if (rooms == null || rooms.data.isEmpty) return const Icon(Icons.chat);
|
||||
final messages = rooms.data.map((e) => e.unreadMessages).reduce((a, b) => a + b);
|
||||
if (rooms == null || rooms.data.isEmpty) {
|
||||
return const Icon(Icons.chat);
|
||||
}
|
||||
final messages = rooms.data
|
||||
.map((e) => e.unreadMessages)
|
||||
.reduce((a, b) => a + b);
|
||||
return badges.Badge(
|
||||
showBadge: messages > 0,
|
||||
position: badges.BadgePosition.topEnd(top: -3, end: -3),
|
||||
@@ -56,7 +69,14 @@ class AppModule {
|
||||
badgeColor: Theme.of(context).primaryColor,
|
||||
elevation: 1,
|
||||
),
|
||||
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
badgeContent: Text(
|
||||
'$messages',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.chat),
|
||||
);
|
||||
},
|
||||
@@ -108,9 +128,19 @@ class AppModule {
|
||||
),
|
||||
};
|
||||
|
||||
if (!showFiltered) available.removeWhere((key, value) => settings.val().modulesSettings.hiddenModules.contains(key));
|
||||
if (!showFiltered) {
|
||||
available.removeWhere(
|
||||
(key, value) =>
|
||||
settings.val().modulesSettings.hiddenModules.contains(key),
|
||||
);
|
||||
}
|
||||
|
||||
return { for (var element in settings.val().modulesSettings.moduleOrder.where((element) => available.containsKey(element))) element : available[element]! };
|
||||
return {
|
||||
for (var element in settings.val().modulesSettings.moduleOrder.where(
|
||||
(element) => available.containsKey(element),
|
||||
))
|
||||
element: available[element]!,
|
||||
};
|
||||
}
|
||||
|
||||
static const int minBottomBarSlots = 3;
|
||||
@@ -150,26 +180,45 @@ class AppModule {
|
||||
return all.skip(slots).toList();
|
||||
}
|
||||
|
||||
Widget toListTile(BuildContext context, {Key? key, bool isReorder = false, Function()? onVisibleChange, bool isVisible = true}) => ListTile(
|
||||
Widget toListTile(
|
||||
BuildContext context, {
|
||||
Key? key,
|
||||
bool isReorder = false,
|
||||
Function()? onVisibleChange,
|
||||
bool isVisible = true,
|
||||
}) => ListTile(
|
||||
key: key,
|
||||
leading: CenteredLeading(icon()),
|
||||
title: Text(name),
|
||||
onTap: isReorder ? null : () => AppRoutes.openModule(context, this),
|
||||
trailing: isReorder
|
||||
? Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(onPressed: onVisibleChange, icon: Icon(isVisible ? Icons.visibility_outlined : Icons.visibility_off_outlined)),
|
||||
Icon(Icons.drag_handle_outlined)
|
||||
])
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: onVisibleChange,
|
||||
icon: Icon(
|
||||
isVisible
|
||||
? Icons.visibility_outlined
|
||||
: Icons.visibility_off_outlined,
|
||||
),
|
||||
),
|
||||
Icon(Icons.drag_handle_outlined),
|
||||
],
|
||||
)
|
||||
: const Icon(Icons.arrow_right),
|
||||
);
|
||||
|
||||
PersistentTabConfig toBottomTab(BuildContext context, {Widget Function(IconData icon)? iconBuilder}) => PersistentTabConfig(
|
||||
PersistentTabConfig toBottomTab(
|
||||
BuildContext context, {
|
||||
Widget Function(IconData icon)? iconBuilder,
|
||||
}) => PersistentTabConfig(
|
||||
screen: Breaker(breaker: breakerArea, child: create()),
|
||||
item: ItemConfig(
|
||||
activeForegroundColor: Theme.of(context).primaryColor,
|
||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||
icon: icon(),
|
||||
title: name
|
||||
activeForegroundColor: Theme.of(context).primaryColor,
|
||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||
icon: icon(),
|
||||
title: name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import '../repository/breaker_repository.dart';
|
||||
import 'breaker_event.dart';
|
||||
import 'breaker_state.dart';
|
||||
|
||||
class BreakerBloc extends LoadableHydratedBloc<BreakerEvent, BreakerState, BreakerRepository> {
|
||||
class BreakerBloc
|
||||
extends
|
||||
LoadableHydratedBloc<BreakerEvent, BreakerState, BreakerRepository> {
|
||||
PackageInfo? _packageInfo;
|
||||
|
||||
@override
|
||||
@@ -18,7 +20,8 @@ class BreakerBloc extends LoadableHydratedBloc<BreakerEvent, BreakerState, Break
|
||||
BreakerState fromNothing() => const BreakerState();
|
||||
|
||||
@override
|
||||
BreakerState fromStorage(Map<String, dynamic> json) => BreakerState.fromJson(json);
|
||||
BreakerState fromStorage(Map<String, dynamic> json) =>
|
||||
BreakerState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(BreakerState state) => state.toJson();
|
||||
|
||||
@@ -7,9 +7,8 @@ part 'breaker_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class BreakerState with _$BreakerState {
|
||||
const factory BreakerState({
|
||||
GetBreakersResponse? response,
|
||||
}) = _BreakerState;
|
||||
const factory BreakerState({GetBreakersResponse? response}) = _BreakerState;
|
||||
|
||||
factory BreakerState.fromJson(Map<String, Object?> json) => _$BreakerStateFromJson(json);
|
||||
factory BreakerState.fromJson(Map<String, Object?> json) =>
|
||||
_$BreakerStateFromJson(json);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import '../../../../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'
|
||||
class BreakerDataProvider {
|
||||
Future<GetBreakersResponse> getBreakers() {
|
||||
final completer = Completer<GetBreakersResponse>();
|
||||
GetBreakersCache(onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
});
|
||||
GetBreakersCache(
|
||||
onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import '../data_provider/breaker_data_provider.dart';
|
||||
class BreakerRepository extends Repository<BreakerState> {
|
||||
final BreakerDataProvider _provider;
|
||||
|
||||
BreakerRepository([BreakerDataProvider? provider]) : _provider = provider ?? BreakerDataProvider();
|
||||
BreakerRepository([BreakerDataProvider? provider])
|
||||
: _provider = provider ?? BreakerDataProvider();
|
||||
|
||||
BreakerDataProvider get data => _provider;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import '../repository/chat_repository.dart';
|
||||
import 'chat_event.dart';
|
||||
import 'chat_state.dart';
|
||||
|
||||
class ChatBloc extends LoadableHydratedBloc<ChatEvent, ChatState, ChatRepository> {
|
||||
class ChatBloc
|
||||
extends LoadableHydratedBloc<ChatEvent, ChatState, ChatRepository> {
|
||||
DateTime _lastTokenSet = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
|
||||
@override
|
||||
@@ -86,11 +87,15 @@ class ChatBloc extends LoadableHydratedBloc<ChatEvent, ChatState, ChatRepository
|
||||
if (!stillCurrent()) return;
|
||||
|
||||
if (capturedError != null) {
|
||||
add(Error(LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
)));
|
||||
add(
|
||||
Error(
|
||||
LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ abstract class ChatState with _$ChatState {
|
||||
int? referenceMessageId,
|
||||
}) = _ChatState;
|
||||
|
||||
factory ChatState.fromJson(Map<String, Object?> json) => _$ChatStateFromJson(json);
|
||||
factory ChatState.fromJson(Map<String, Object?> json) =>
|
||||
_$ChatStateFromJson(json);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import '../data_provider/chat_data_provider.dart';
|
||||
class ChatRepository extends Repository<ChatState> {
|
||||
final ChatDataProvider _provider;
|
||||
|
||||
ChatRepository([ChatDataProvider? provider]) : _provider = provider ?? ChatDataProvider();
|
||||
ChatRepository([ChatDataProvider? provider])
|
||||
: _provider = provider ?? ChatDataProvider();
|
||||
|
||||
ChatDataProvider get data => _provider;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import '../repository/chat_list_repository.dart';
|
||||
import 'chat_list_event.dart';
|
||||
import 'chat_list_state.dart';
|
||||
|
||||
class ChatListBloc extends LoadableHydratedBloc<ChatListEvent, ChatListState, ChatListRepository> {
|
||||
class ChatListBloc
|
||||
extends
|
||||
LoadableHydratedBloc<ChatListEvent, ChatListState, ChatListRepository> {
|
||||
bool _forceRenew = false;
|
||||
|
||||
@override
|
||||
@@ -27,7 +29,8 @@ class ChatListBloc extends LoadableHydratedBloc<ChatListEvent, ChatListState, Ch
|
||||
ChatListState fromNothing() => const ChatListState();
|
||||
|
||||
@override
|
||||
ChatListState fromStorage(Map<String, dynamic> json) => ChatListState.fromJson(json);
|
||||
ChatListState fromStorage(Map<String, dynamic> json) =>
|
||||
ChatListState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(ChatListState state) => state.toJson();
|
||||
@@ -62,11 +65,15 @@ class ChatListBloc extends LoadableHydratedBloc<ChatListEvent, ChatListState, Ch
|
||||
capturedError = e;
|
||||
}
|
||||
if (capturedError != null) {
|
||||
add(Error(LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
)));
|
||||
add(
|
||||
Error(
|
||||
LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +84,10 @@ class ChatListBloc extends LoadableHydratedBloc<ChatListEvent, ChatListState, Ch
|
||||
|
||||
void _updateAppBadge(GetRoomResponse rooms) {
|
||||
try {
|
||||
final unread = rooms.data.fold<int>(0, (a, room) => a + room.unreadMessages);
|
||||
final unread = rooms.data.fold<int>(
|
||||
0,
|
||||
(a, room) => a + room.unreadMessages,
|
||||
);
|
||||
FlutterAppBadge.count(unread);
|
||||
} on Object catch (e) {
|
||||
log('Failed to update app badge: $e');
|
||||
|
||||
@@ -7,9 +7,8 @@ part 'chat_list_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class ChatListState with _$ChatListState {
|
||||
const factory ChatListState({
|
||||
GetRoomResponse? rooms,
|
||||
}) = _ChatListState;
|
||||
const factory ChatListState({GetRoomResponse? rooms}) = _ChatListState;
|
||||
|
||||
factory ChatListState.fromJson(Map<String, Object?> json) => _$ChatListStateFromJson(json);
|
||||
factory ChatListState.fromJson(Map<String, Object?> json) =>
|
||||
_$ChatListStateFromJson(json);
|
||||
}
|
||||
|
||||
@@ -8,16 +8,12 @@ class ChatListDataProvider {
|
||||
Future<GetRoomResponse> getRooms({
|
||||
void Function(Object)? onError,
|
||||
bool renew = false,
|
||||
}) =>
|
||||
resolveFromCache<GetRoomResponse>(
|
||||
(onUpdate, onError) => GetRoomCache(
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: '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();
|
||||
|
||||
@@ -5,7 +5,8 @@ import '../data_provider/chat_list_data_provider.dart';
|
||||
class ChatListRepository extends Repository<ChatListState> {
|
||||
final ChatListDataProvider _provider;
|
||||
|
||||
ChatListRepository([ChatListDataProvider? provider]) : _provider = provider ?? ChatListDataProvider();
|
||||
ChatListRepository([ChatListDataProvider? provider])
|
||||
: _provider = provider ?? ChatListDataProvider();
|
||||
|
||||
ChatListDataProvider get data => _provider;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import '../repository/files_repository.dart';
|
||||
import 'files_event.dart';
|
||||
import 'files_state.dart';
|
||||
|
||||
class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesRepository> {
|
||||
class FilesBloc
|
||||
extends LoadableHydratedBloc<FilesEvent, FilesState, FilesRepository> {
|
||||
final List<String> initialPath;
|
||||
|
||||
FilesBloc({this.initialPath = const []});
|
||||
@@ -19,7 +20,8 @@ class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesReposi
|
||||
FilesState fromNothing() => FilesState(currentPath: initialPath);
|
||||
|
||||
@override
|
||||
FilesState fromStorage(Map<String, dynamic> json) => FilesState.fromJson(json);
|
||||
FilesState fromStorage(Map<String, dynamic> json) =>
|
||||
FilesState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(FilesState state) => null;
|
||||
@@ -60,7 +62,9 @@ class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesReposi
|
||||
// Cached payload arrives before the network call settles. Surface it
|
||||
// immediately via Emit so the listing is visible while isLoading
|
||||
// stays true and the top loading bar keeps spinning.
|
||||
cached.files.removeWhere((file) => file.name.isEmpty || file.name == path.lastOrNull);
|
||||
cached.files.removeWhere(
|
||||
(file) => file.name.isEmpty || file.name == path.lastOrNull,
|
||||
);
|
||||
add(Emit((s) => s.copyWith(listing: cached)));
|
||||
},
|
||||
onError: (e) => capturedError = e,
|
||||
@@ -70,15 +74,21 @@ class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesReposi
|
||||
}
|
||||
|
||||
if (listing != null) {
|
||||
listing.files.removeWhere((file) => file.name.isEmpty || file.name == path.lastOrNull);
|
||||
listing.files.removeWhere(
|
||||
(file) => file.name.isEmpty || file.name == path.lastOrNull,
|
||||
);
|
||||
add(DataGathered((s) => s.copyWith(listing: listing)));
|
||||
}
|
||||
if (capturedError != null) {
|
||||
add(Error(LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
)));
|
||||
add(
|
||||
Error(
|
||||
LoadingError(
|
||||
message: errorToUserMessage(capturedError),
|
||||
technicalDetails: errorToTechnicalDetails(capturedError),
|
||||
allowRetry: errorAllowsRetry(capturedError),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ abstract class FilesState with _$FilesState {
|
||||
ListFilesResponse? listing,
|
||||
}) = _FilesState;
|
||||
|
||||
factory FilesState.fromJson(Map<String, Object?> json) => _$FilesStateFromJson(json);
|
||||
factory FilesState.fromJson(Map<String, Object?> json) =>
|
||||
_$FilesStateFromJson(json);
|
||||
}
|
||||
|
||||
@@ -15,17 +15,16 @@ class FilesDataProvider {
|
||||
String path, {
|
||||
void Function(ListFilesResponse)? onCacheData,
|
||||
void Function(Object)? onError,
|
||||
}) =>
|
||||
resolveFromCache<ListFilesResponse>(
|
||||
(onUpdate, onError) => ListFilesCache(
|
||||
path: path,
|
||||
onUpdate: onUpdate,
|
||||
onCacheData: onCacheData,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: '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;
|
||||
|
||||
@@ -5,7 +5,8 @@ import '../data_provider/files_data_provider.dart';
|
||||
class FilesRepository extends Repository<FilesState> {
|
||||
final FilesDataProvider _provider;
|
||||
|
||||
FilesRepository([FilesDataProvider? provider]) : _provider = provider ?? FilesDataProvider();
|
||||
FilesRepository([FilesDataProvider? provider])
|
||||
: _provider = provider ?? FilesDataProvider();
|
||||
|
||||
FilesDataProvider get data => _provider;
|
||||
}
|
||||
|
||||
@@ -3,17 +3,23 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'grade_averages_event.dart';
|
||||
import 'grade_averages_state.dart';
|
||||
|
||||
class GradeAveragesBloc extends HydratedBloc<GradeAveragesEvent, GradeAveragesState> {
|
||||
GradeAveragesBloc() : super(const GradeAveragesState(gradingSystem: GradeAveragesGradingSystem.middleSchool, grades: [])) {
|
||||
|
||||
class GradeAveragesBloc
|
||||
extends HydratedBloc<GradeAveragesEvent, GradeAveragesState> {
|
||||
GradeAveragesBloc()
|
||||
: super(
|
||||
const GradeAveragesState(
|
||||
gradingSystem: GradeAveragesGradingSystem.middleSchool,
|
||||
grades: [],
|
||||
),
|
||||
) {
|
||||
on<GradingSystemChanged>((event, emit) {
|
||||
add(ResetAll());
|
||||
emit(
|
||||
state.copyWith(
|
||||
gradingSystem: event.isMiddleSchool
|
||||
? GradeAveragesGradingSystem.middleSchool
|
||||
: GradeAveragesGradingSystem.highSchool
|
||||
)
|
||||
? GradeAveragesGradingSystem.middleSchool
|
||||
: GradeAveragesGradingSystem.highSchool,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -22,7 +28,12 @@ class GradeAveragesBloc extends HydratedBloc<GradeAveragesEvent, GradeAveragesSt
|
||||
});
|
||||
|
||||
on<ResetGrade>((event, emit) {
|
||||
emit(state.copyWith(grades: [...state.grades]..removeWhere((grade) => grade == event.grade)));
|
||||
emit(
|
||||
state.copyWith(
|
||||
grades: [...state.grades]
|
||||
..removeWhere((grade) => grade == event.grade),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
on<IncrementGrade>((event, emit) {
|
||||
@@ -30,20 +41,26 @@ class GradeAveragesBloc extends HydratedBloc<GradeAveragesEvent, GradeAveragesSt
|
||||
});
|
||||
|
||||
on<DecrementGrade>((event, emit) {
|
||||
emit(state.copyWith(grades: List.from(state.grades)..remove(event.grade)));
|
||||
emit(
|
||||
state.copyWith(grades: List.from(state.grades)..remove(event.grade)),
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
double average() => state.grades.isEmpty ? 0 : state.grades.reduce((a, b) => a + b) / state.grades.length;
|
||||
bool isMiddleSchool() => state.gradingSystem == GradeAveragesGradingSystem.middleSchool;
|
||||
double average() => state.grades.isEmpty
|
||||
? 0
|
||||
: state.grades.reduce((a, b) => a + b) / state.grades.length;
|
||||
bool isMiddleSchool() =>
|
||||
state.gradingSystem == GradeAveragesGradingSystem.middleSchool;
|
||||
bool canDecrementOrDelete(int grade) => state.grades.contains(grade);
|
||||
int countOfGrade(int grade) => state.grades.where((g) => g == grade).length;
|
||||
int gradesInGradingSystem() => state.gradingSystem == GradeAveragesGradingSystem.middleSchool ? 6 : 16;
|
||||
int gradesInGradingSystem() =>
|
||||
state.gradingSystem == GradeAveragesGradingSystem.middleSchool ? 6 : 16;
|
||||
int getGradeFromIndex(int index) => isMiddleSchool() ? index + 1 : 15 - index;
|
||||
|
||||
@override
|
||||
GradeAveragesState? fromJson(Map<String, dynamic> json) => GradeAveragesState.fromJson(json);
|
||||
GradeAveragesState? fromJson(Map<String, dynamic> json) =>
|
||||
GradeAveragesState.fromJson(json);
|
||||
@override
|
||||
Map<String, dynamic>? toJson(GradeAveragesState state) => state.toJson();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
|
||||
sealed class GradeAveragesEvent {}
|
||||
|
||||
final class GradingSystemChanged extends GradeAveragesEvent {
|
||||
final bool isMiddleSchool;
|
||||
GradingSystemChanged(this.isMiddleSchool);
|
||||
}
|
||||
|
||||
final class ResetAll extends GradeAveragesEvent {}
|
||||
|
||||
final class ResetGrade extends GradeAveragesEvent {
|
||||
final int grade;
|
||||
ResetGrade(this.grade);
|
||||
}
|
||||
|
||||
final class IncrementGrade extends GradeAveragesEvent {
|
||||
final int grade;
|
||||
IncrementGrade(this.grade);
|
||||
}
|
||||
|
||||
final class DecrementGrade extends GradeAveragesEvent {
|
||||
final int grade;
|
||||
DecrementGrade(this.grade);
|
||||
|
||||
@@ -10,10 +10,8 @@ abstract class GradeAveragesState with _$GradeAveragesState {
|
||||
required List<int> grades,
|
||||
}) = _GradeAveragesState;
|
||||
|
||||
factory GradeAveragesState.fromJson(Map<String, dynamic> json) => _$GradeAveragesStateFromJson(json);
|
||||
factory GradeAveragesState.fromJson(Map<String, dynamic> json) =>
|
||||
_$GradeAveragesStateFromJson(json);
|
||||
}
|
||||
|
||||
enum GradeAveragesGradingSystem {
|
||||
highSchool,
|
||||
middleSchool,
|
||||
}
|
||||
enum GradeAveragesGradingSystem { highSchool, middleSchool }
|
||||
|
||||
@@ -4,32 +4,51 @@ import '../repository/holidays_repository.dart';
|
||||
import 'holidays_event.dart';
|
||||
import 'holidays_state.dart';
|
||||
|
||||
class HolidaysBloc extends LoadableHydratedBloc<HolidaysEvent, HolidaysState, HolidaysRepository> {
|
||||
class HolidaysBloc
|
||||
extends
|
||||
LoadableHydratedBloc<HolidaysEvent, HolidaysState, HolidaysRepository> {
|
||||
HolidaysBloc() {
|
||||
on<SetPastHolidaysVisible>((event, emit) {
|
||||
add(Emit((state) => state.copyWith(showPastHolidays: event.shouldBeVisible)));
|
||||
add(
|
||||
Emit(
|
||||
(state) => state.copyWith(showPastHolidays: event.shouldBeVisible),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
on<DisclaimerDismissed>((event, emit) => add(
|
||||
Emit((state) => state.copyWith(showDisclaimer: false))
|
||||
));
|
||||
on<DisclaimerDismissed>(
|
||||
(event, emit) =>
|
||||
add(Emit((state) => state.copyWith(showDisclaimer: false))),
|
||||
);
|
||||
}
|
||||
|
||||
bool showPastHolidays() => innerState?.showPastHolidays ?? false;
|
||||
bool showDisclaimerOnEntry() => innerState?.showDisclaimer ?? false;
|
||||
List<Holiday>? getHolidays() => innerState?.holidays
|
||||
.where((element) => showPastHolidays() || DateTime.parse(element.end).isAfter(DateTime.now()))
|
||||
.toList() ?? [];
|
||||
List<Holiday>? getHolidays() =>
|
||||
innerState?.holidays
|
||||
.where(
|
||||
(element) =>
|
||||
showPastHolidays() ||
|
||||
DateTime.parse(element.end).isAfter(DateTime.now()),
|
||||
)
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
@override
|
||||
HolidaysState fromNothing() => const HolidaysState(showPastHolidays: false, holidays: [], showDisclaimer: true);
|
||||
HolidaysState fromNothing() => const HolidaysState(
|
||||
showPastHolidays: false,
|
||||
holidays: [],
|
||||
showDisclaimer: true,
|
||||
);
|
||||
@override
|
||||
HolidaysState fromStorage(Map<String, dynamic> json) => HolidaysState.fromJson(json);
|
||||
HolidaysState fromStorage(Map<String, dynamic> json) =>
|
||||
HolidaysState.fromJson(json);
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
var holidays = await repo.getHolidays();
|
||||
add(DataGathered((state) => state.copyWith(holidays: holidays)));
|
||||
}
|
||||
|
||||
@override
|
||||
HolidaysRepository repository() => HolidaysRepository();
|
||||
@override
|
||||
|
||||
@@ -2,8 +2,10 @@ import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_
|
||||
import 'holidays_state.dart';
|
||||
|
||||
sealed class HolidaysEvent extends LoadableHydratedBlocEvent<HolidaysState> {}
|
||||
|
||||
class SetPastHolidaysVisible extends HolidaysEvent {
|
||||
final bool shouldBeVisible;
|
||||
SetPastHolidaysVisible(this.shouldBeVisible);
|
||||
}
|
||||
|
||||
class DisclaimerDismissed extends HolidaysEvent {}
|
||||
|
||||
@@ -12,7 +12,8 @@ abstract class HolidaysState with _$HolidaysState {
|
||||
required List<Holiday> holidays,
|
||||
}) = _HolidaysState;
|
||||
|
||||
factory HolidaysState.fromJson(Map<String, Object?> json) => _$HolidaysStateFromJson(json);
|
||||
factory HolidaysState.fromJson(Map<String, Object?> json) =>
|
||||
_$HolidaysStateFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -26,5 +27,6 @@ abstract class Holiday with _$Holiday {
|
||||
required String slug,
|
||||
}) = _Holiday;
|
||||
|
||||
factory Holiday.fromJson(Map<String, Object?> json) => _$HolidayFromJson(json);
|
||||
factory Holiday.fromJson(Map<String, Object?> json) =>
|
||||
_$HolidayFromJson(json);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import '../bloc/holidays_state.dart';
|
||||
|
||||
class HolidaysGetHolidays extends HolidayDataLoader<List<Holiday>> {
|
||||
@override
|
||||
List<Holiday> assemble(DataLoaderResult data) => data.asListOfMaps().map(Holiday.fromJson).toList();
|
||||
List<Holiday> assemble(DataLoaderResult data) =>
|
||||
data.asListOfMaps().map(Holiday.fromJson).toList();
|
||||
|
||||
@override
|
||||
Future<Response<String>> fetch() => dio.get('/holidays/HE');
|
||||
|
||||
@@ -4,28 +4,41 @@ import '../repository/marianum_dates_repository.dart';
|
||||
import 'marianum_dates_event.dart';
|
||||
import 'marianum_dates_state.dart';
|
||||
|
||||
class MarianumDatesBloc extends LoadableHydratedBloc<MarianumDatesEvent, MarianumDatesState, MarianumDatesRepository> {
|
||||
class MarianumDatesBloc
|
||||
extends
|
||||
LoadableHydratedBloc<
|
||||
MarianumDatesEvent,
|
||||
MarianumDatesState,
|
||||
MarianumDatesRepository
|
||||
> {
|
||||
MarianumDatesBloc() {
|
||||
on<SetPastEventsVisible>((event, emit) {
|
||||
add(Emit((state) => state.copyWith(showPastEvents: event.shouldBeVisible)));
|
||||
add(
|
||||
Emit((state) => state.copyWith(showPastEvents: event.shouldBeVisible)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
bool showPastEvents() => innerState?.showPastEvents ?? false;
|
||||
|
||||
List<MarianumDate>? getEvents() => innerState?.events
|
||||
.where((e) => showPastEvents() || e.end.isAfter(DateTime.now()))
|
||||
.toList() ?? [];
|
||||
List<MarianumDate>? getEvents() =>
|
||||
innerState?.events
|
||||
.where((e) => showPastEvents() || e.end.isAfter(DateTime.now()))
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
@override
|
||||
MarianumDatesState fromNothing() => const MarianumDatesState(showPastEvents: false, events: []);
|
||||
MarianumDatesState fromNothing() =>
|
||||
const MarianumDatesState(showPastEvents: false, events: []);
|
||||
@override
|
||||
MarianumDatesState fromStorage(Map<String, dynamic> json) => MarianumDatesState.fromJson(json);
|
||||
MarianumDatesState fromStorage(Map<String, dynamic> json) =>
|
||||
MarianumDatesState.fromJson(json);
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
final events = await repo.getEvents();
|
||||
add(DataGathered((state) => state.copyWith(events: events)));
|
||||
}
|
||||
|
||||
@override
|
||||
MarianumDatesRepository repository() => MarianumDatesRepository();
|
||||
@override
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'marianum_dates_state.dart';
|
||||
|
||||
sealed class MarianumDatesEvent extends LoadableHydratedBlocEvent<MarianumDatesState> {}
|
||||
sealed class MarianumDatesEvent
|
||||
extends LoadableHydratedBlocEvent<MarianumDatesState> {}
|
||||
|
||||
class SetPastEventsVisible extends MarianumDatesEvent {
|
||||
final bool shouldBeVisible;
|
||||
|
||||
@@ -11,7 +11,8 @@ abstract class MarianumDatesState with _$MarianumDatesState {
|
||||
required List<MarianumDate> events,
|
||||
}) = _MarianumDatesState;
|
||||
|
||||
factory MarianumDatesState.fromJson(Map<String, Object?> json) => _$MarianumDatesStateFromJson(json);
|
||||
factory MarianumDatesState.fromJson(Map<String, Object?> json) =>
|
||||
_$MarianumDatesStateFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -25,5 +26,6 @@ abstract class MarianumDate with _$MarianumDate {
|
||||
required bool isAllDay,
|
||||
}) = _MarianumDate;
|
||||
|
||||
factory MarianumDate.fromJson(Map<String, Object?> json) => _$MarianumDateFromJson(json);
|
||||
factory MarianumDate.fromJson(Map<String, Object?> json) =>
|
||||
_$MarianumDateFromJson(json);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ import 'package:enough_icalendar/enough_icalendar.dart';
|
||||
import '../bloc/marianum_dates_state.dart';
|
||||
|
||||
class MarianumDatesGetEvents {
|
||||
static const String url = 'https://public-cal.marianumlan.de/cal_public/ad4c5da8-7466-9c72-89cb-8b8d9a5cf26c';
|
||||
static const String url =
|
||||
'https://public-cal.marianumlan.de/cal_public/ad4c5da8-7466-9c72-89cb-8b8d9a5cf26c';
|
||||
|
||||
final Dio _dio = Dio(BaseOptions(
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
));
|
||||
final Dio _dio = Dio(
|
||||
BaseOptions(
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
),
|
||||
);
|
||||
|
||||
Future<List<MarianumDate>> run() async {
|
||||
final response = await _dio.get<String>(url);
|
||||
@@ -20,7 +23,11 @@ class MarianumDatesGetEvents {
|
||||
final calendar = root is VCalendar ? root : null;
|
||||
final source = calendar?.children ?? root.children;
|
||||
|
||||
final events = source.whereType<VEvent>().map(_toMarianumDate).whereType<MarianumDate>().toList();
|
||||
final events = source
|
||||
.whereType<VEvent>()
|
||||
.map(_toMarianumDate)
|
||||
.whereType<MarianumDate>()
|
||||
.toList();
|
||||
events.sort((a, b) => a.start.compareTo(b.start));
|
||||
return events;
|
||||
}
|
||||
@@ -41,8 +48,11 @@ class MarianumDatesGetEvents {
|
||||
}
|
||||
|
||||
static bool _isAllDay(DateTime start, DateTime end) {
|
||||
final startMidnight = start.hour == 0 && start.minute == 0 && start.second == 0;
|
||||
final startMidnight =
|
||||
start.hour == 0 && start.minute == 0 && start.second == 0;
|
||||
final endMidnight = end.hour == 0 && end.minute == 0 && end.second == 0;
|
||||
return startMidnight && endMidnight && end.difference(start).inHours % 24 == 0;
|
||||
return startMidnight &&
|
||||
endMidnight &&
|
||||
end.difference(start).inHours % 24 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,13 @@ import '../repository/marianum_message_repository.dart';
|
||||
import 'marianum_message_event.dart';
|
||||
import 'marianum_message_state.dart';
|
||||
|
||||
class MarianumMessageBloc extends LoadableHydratedBloc<MarianumMessageEvent, MarianumMessageState, MarianumMessageRepository> {
|
||||
class MarianumMessageBloc
|
||||
extends
|
||||
LoadableHydratedBloc<
|
||||
MarianumMessageEvent,
|
||||
MarianumMessageState,
|
||||
MarianumMessageRepository
|
||||
> {
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
var messages = await repo.getMessages();
|
||||
@@ -15,10 +21,13 @@ class MarianumMessageBloc extends LoadableHydratedBloc<MarianumMessageEvent, Mar
|
||||
MarianumMessageRepository repository() => MarianumMessageRepository();
|
||||
|
||||
@override
|
||||
MarianumMessageState fromNothing() => const MarianumMessageState(messageList: MarianumMessageList(base: '', messages: []));
|
||||
MarianumMessageState fromNothing() => const MarianumMessageState(
|
||||
messageList: MarianumMessageList(base: '', messages: []),
|
||||
);
|
||||
|
||||
@override
|
||||
MarianumMessageState fromStorage(Map<String, dynamic> json) => MarianumMessageState.fromJson(json);
|
||||
MarianumMessageState fromStorage(Map<String, dynamic> json) =>
|
||||
MarianumMessageState.fromJson(json);
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(MarianumMessageState state) => state.toJson();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'marianum_message_state.dart';
|
||||
|
||||
sealed class MarianumMessageEvent extends LoadableHydratedBlocEvent<MarianumMessageState> {}
|
||||
sealed class MarianumMessageEvent
|
||||
extends LoadableHydratedBlocEvent<MarianumMessageState> {}
|
||||
|
||||
class MessageEvent extends MarianumMessageEvent {}
|
||||
|
||||
@@ -3,14 +3,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'marianum_message_state.freezed.dart';
|
||||
part 'marianum_message_state.g.dart';
|
||||
|
||||
|
||||
@freezed
|
||||
abstract class MarianumMessageState with _$MarianumMessageState {
|
||||
const factory MarianumMessageState({
|
||||
required MarianumMessageList messageList,
|
||||
}) = _MarianumMessageState;
|
||||
|
||||
factory MarianumMessageState.fromJson(Map<String, dynamic> json) => _$MarianumMessageStateFromJson(json);
|
||||
factory MarianumMessageState.fromJson(Map<String, dynamic> json) =>
|
||||
_$MarianumMessageStateFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -20,7 +20,8 @@ abstract class MarianumMessageList with _$MarianumMessageList {
|
||||
required List<MarianumMessage> messages,
|
||||
}) = _MarianumMessageList;
|
||||
|
||||
factory MarianumMessageList.fromJson(Map<String, dynamic> json) => _$MarianumMessageListFromJson(json);
|
||||
factory MarianumMessageList.fromJson(Map<String, dynamic> json) =>
|
||||
_$MarianumMessageListFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -31,11 +32,8 @@ abstract class MarianumMessage with _$MarianumMessage {
|
||||
required String url,
|
||||
}) = _MarianumMessage;
|
||||
|
||||
factory MarianumMessage.fromJson(Map<String, dynamic> json) => _$MarianumMessageFromJson(json);
|
||||
factory MarianumMessage.fromJson(Map<String, dynamic> json) =>
|
||||
_$MarianumMessageFromJson(json);
|
||||
}
|
||||
|
||||
|
||||
enum GradeAveragesGradingSystem {
|
||||
highSchool,
|
||||
middleSchool,
|
||||
}
|
||||
enum GradeAveragesGradingSystem { highSchool, middleSchool }
|
||||
|
||||
+2
-1
@@ -8,5 +8,6 @@ class MarianumMessageGetMessages extends MhslDataLoader<MarianumMessageList> {
|
||||
@override
|
||||
Future<Response<String>> fetch() async => dio.get('/message/messages.json');
|
||||
@override
|
||||
MarianumMessageList assemble(DataLoaderResult data) => MarianumMessageList.fromJson(data.asMap());
|
||||
MarianumMessageList assemble(DataLoaderResult data) =>
|
||||
MarianumMessageList.fromJson(data.asMap());
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ import '../bloc/marianum_message_state.dart';
|
||||
import '../data_provider/marianum_message_get_messages.dart';
|
||||
|
||||
class MarianumMessageRepository extends Repository<MarianumMessageState> {
|
||||
Future<MarianumMessageList> getMessages() => MarianumMessageGetMessages().run();
|
||||
Future<MarianumMessageList> getMessages() =>
|
||||
MarianumMessageGetMessages().run();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ class SettingsCubit extends HydratedCubit<Settings> {
|
||||
_emitFreshInstance();
|
||||
});
|
||||
}
|
||||
Debouncer.debounce(_debounceTag, const Duration(milliseconds: 500), _emitFreshInstance);
|
||||
Debouncer.debounce(
|
||||
_debounceTag,
|
||||
const Duration(milliseconds: 500),
|
||||
_emitFreshInstance,
|
||||
);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@@ -50,7 +54,11 @@ class SettingsCubit extends HydratedCubit<Settings> {
|
||||
return _appendNewModules(Settings.fromJson(json));
|
||||
} catch (_) {
|
||||
try {
|
||||
return _appendNewModules(Settings.fromJson(_mergeSettings(json, DefaultSettings.get().toJson())));
|
||||
return _appendNewModules(
|
||||
Settings.fromJson(
|
||||
_mergeSettings(json, DefaultSettings.get().toJson()),
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return DefaultSettings.get();
|
||||
}
|
||||
@@ -63,7 +71,9 @@ class SettingsCubit extends HydratedCubit<Settings> {
|
||||
Settings _appendNewModules(Settings s) {
|
||||
final order = s.modulesSettings.moduleOrder;
|
||||
final hidden = s.modulesSettings.hiddenModules;
|
||||
final missing = Modules.values.where((m) => !order.contains(m) && !hidden.contains(m));
|
||||
final missing = Modules.values.where(
|
||||
(m) => !order.contains(m) && !hidden.contains(m),
|
||||
);
|
||||
if (missing.isEmpty) return s;
|
||||
s.modulesSettings.moduleOrder = [...order, ...missing];
|
||||
return s;
|
||||
@@ -72,12 +82,19 @@ class SettingsCubit extends HydratedCubit<Settings> {
|
||||
@override
|
||||
Map<String, dynamic>? toJson(Settings state) => state.toJson();
|
||||
|
||||
Map<String, dynamic> _mergeSettings(Map<String, dynamic> oldMap, Map<String, dynamic> newMap) {
|
||||
Map<String, dynamic> _mergeSettings(
|
||||
Map<String, dynamic> oldMap,
|
||||
Map<String, dynamic> newMap,
|
||||
) {
|
||||
final merged = Map<String, dynamic>.from(newMap);
|
||||
oldMap.forEach((key, value) {
|
||||
if (merged.containsKey(key)) {
|
||||
if (value is Map<String, dynamic> && merged[key] is Map<String, dynamic>) {
|
||||
merged[key] = _mergeSettings(value, merged[key] as Map<String, dynamic>);
|
||||
if (value is Map<String, dynamic> &&
|
||||
merged[key] is Map<String, dynamic>) {
|
||||
merged[key] = _mergeSettings(
|
||||
value,
|
||||
merged[key] as Map<String, dynamic>,
|
||||
);
|
||||
} else {
|
||||
merged[key] = value;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,13 @@ import '../repository/timetable_repository.dart';
|
||||
import 'timetable_event.dart';
|
||||
import 'timetable_state.dart';
|
||||
|
||||
class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState, TimetableRepository> {
|
||||
class TimetableBloc
|
||||
extends
|
||||
LoadableHydratedBloc<
|
||||
TimetableEvent,
|
||||
TimetableState,
|
||||
TimetableRepository
|
||||
> {
|
||||
static const Duration _weekSpan = Duration(days: 7);
|
||||
static final DateFormat _weekKeyFormat = DateFormat('yyyyMMdd');
|
||||
|
||||
@@ -37,7 +43,8 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
}
|
||||
|
||||
@override
|
||||
TimetableState fromStorage(Map<String, dynamic> json) => TimetableState.fromJson(json);
|
||||
TimetableState fromStorage(Map<String, dynamic> json) =>
|
||||
TimetableState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(TimetableState state) => state.toJson();
|
||||
@@ -54,7 +61,12 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
}
|
||||
|
||||
await Future.wait([
|
||||
_loadCurrentWeek(initial.startDate, initial.endDate, onError: recordError, renew: renew),
|
||||
_loadCurrentWeek(
|
||||
initial.startDate,
|
||||
initial.endDate,
|
||||
onError: recordError,
|
||||
renew: renew,
|
||||
),
|
||||
_loadStaticReferenceData(onError: recordError, renew: renew),
|
||||
_loadCustomEvents(onError: recordError, renew: renew),
|
||||
]);
|
||||
@@ -104,7 +116,12 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
final requestStart = DateTime.now();
|
||||
_lastWeekRequestStart = requestStart;
|
||||
try {
|
||||
final week = await repo.data.getWeek(startDate, endDate, onError: onError, renew: renew);
|
||||
final week = await repo.data.getWeek(
|
||||
startDate,
|
||||
endDate,
|
||||
onError: onError,
|
||||
renew: renew,
|
||||
);
|
||||
if (_lastWeekRequestStart.isAfter(requestStart)) return;
|
||||
_writeWeekToCache(startDate, week);
|
||||
} catch (e) {
|
||||
@@ -123,19 +140,27 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
repo.data.getSchoolHolidays(onError: onError, renew: renew),
|
||||
).wait;
|
||||
|
||||
add(Emit((s) => s.copyWith(
|
||||
add(
|
||||
Emit(
|
||||
(s) => s.copyWith(
|
||||
rooms: rooms,
|
||||
subjects: subjects,
|
||||
schoolHolidays: schoolHolidays,
|
||||
dataVersion: s.dataVersion + 1,
|
||||
)));
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
onError?.call(e);
|
||||
}
|
||||
|
||||
try {
|
||||
final timegrid = await repo.data.getTimegrid(renew: renew);
|
||||
add(Emit((s) => s.copyWith(timegrid: timegrid, dataVersion: s.dataVersion + 1)));
|
||||
add(
|
||||
Emit(
|
||||
(s) => s.copyWith(timegrid: timegrid, dataVersion: s.dataVersion + 1),
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
// Timegrid load failure falls back to a hardcoded schedule in the UI layer.
|
||||
}
|
||||
@@ -146,8 +171,16 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
bool renew = false,
|
||||
}) async {
|
||||
try {
|
||||
final events = await repo.data.getCustomEvents(renew: renew, onError: onError);
|
||||
add(Emit((s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1)));
|
||||
final events = await repo.data.getCustomEvents(
|
||||
renew: renew,
|
||||
onError: onError,
|
||||
);
|
||||
add(
|
||||
Emit(
|
||||
(s) =>
|
||||
s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
onError?.call(e);
|
||||
}
|
||||
@@ -155,7 +188,11 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
|
||||
Future<void> _refreshCustomEvents() async {
|
||||
final events = await repo.data.getCustomEvents(renew: true);
|
||||
add(DataGathered((s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1)));
|
||||
add(
|
||||
DataGathered(
|
||||
(s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _prefetchAdjacentWeeks(DateTime start, DateTime end) {
|
||||
@@ -164,16 +201,21 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
}
|
||||
|
||||
void _prefetchWeek(DateTime start, DateTime end) {
|
||||
repo.data.getWeek(start, end).then((week) => _writeWeekToCache(start, week)).catchError((_) {});
|
||||
repo.data
|
||||
.getWeek(start, end)
|
||||
.then((week) => _writeWeekToCache(start, week))
|
||||
.catchError((_) {});
|
||||
}
|
||||
|
||||
void _writeWeekToCache(DateTime weekStart, GetTimetableResponse week) {
|
||||
final key = _weekKeyFormat.format(weekStart);
|
||||
add(Emit((s) {
|
||||
final updated = Map<String, GetTimetableResponse>.of(s.weekCache);
|
||||
updated[key] = week;
|
||||
return s.copyWith(weekCache: updated, dataVersion: s.dataVersion + 1);
|
||||
}));
|
||||
add(
|
||||
Emit((s) {
|
||||
final updated = Map<String, GetTimetableResponse>.of(s.weekCache);
|
||||
updated[key] = week;
|
||||
return s.copyWith(weekCache: updated, dataVersion: s.dataVersion + 1);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
static DateTime _startOfWeek(DateTime reference) {
|
||||
@@ -182,7 +224,9 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
|
||||
}
|
||||
|
||||
static DateTime _endOfWeek(DateTime reference) {
|
||||
final friday = reference.add(Duration(days: DateTime.daysPerWeek - reference.weekday - 2));
|
||||
final friday = reference.add(
|
||||
Duration(days: DateTime.daysPerWeek - reference.weekday - 2),
|
||||
);
|
||||
return DateTime(friday.year, friday.month, friday.day);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ abstract class TimetableState with _$TimetableState {
|
||||
const TimetableState._();
|
||||
|
||||
const factory TimetableState({
|
||||
@Default(<String, GetTimetableResponse>{}) Map<String, GetTimetableResponse> weekCache,
|
||||
@Default(<String, GetTimetableResponse>{})
|
||||
Map<String, GetTimetableResponse> weekCache,
|
||||
GetRoomsResponse? rooms,
|
||||
GetSubjectsResponse? subjects,
|
||||
GetHolidaysResponse? schoolHolidays,
|
||||
@@ -26,10 +27,15 @@ abstract class TimetableState with _$TimetableState {
|
||||
@Default(0) int dataVersion,
|
||||
}) = _TimetableState;
|
||||
|
||||
factory TimetableState.fromJson(Map<String, Object?> json) => _$TimetableStateFromJson(json);
|
||||
factory TimetableState.fromJson(Map<String, Object?> json) =>
|
||||
_$TimetableStateFromJson(json);
|
||||
|
||||
Iterable<GetTimetableResponseObject> getAllKnownLessons() =>
|
||||
weekCache.values.expand((response) => response.result);
|
||||
|
||||
bool get hasReferenceData => rooms != null && subjects != null && schoolHolidays != null && customEvents != null;
|
||||
bool get hasReferenceData =>
|
||||
rooms != null &&
|
||||
subjects != null &&
|
||||
schoolHolidays != null &&
|
||||
customEvents != null;
|
||||
}
|
||||
|
||||
@@ -31,90 +31,78 @@ class TimetableDataProvider {
|
||||
DateTime endDate, {
|
||||
void Function(Object)? onError,
|
||||
bool renew = false,
|
||||
}) =>
|
||||
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',
|
||||
);
|
||||
}) => 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,
|
||||
}) =>
|
||||
resolveFromCache<GetRoomsResponse>(
|
||||
(onUpdate, onError) => GetRoomsCache(
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: '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,
|
||||
}) =>
|
||||
resolveFromCache<GetSubjectsResponse>(
|
||||
(onUpdate, onError) => GetSubjectsCache(
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: '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,
|
||||
}) =>
|
||||
resolveFromCache<GetHolidaysResponse>(
|
||||
(onUpdate, onError) => GetHolidaysCache(
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: 'getSchoolHolidays',
|
||||
);
|
||||
}) => resolveFromCache<GetHolidaysResponse>(
|
||||
(onUpdate, onError) =>
|
||||
GetHolidaysCache(renew: renew, onUpdate: onUpdate, onError: onError),
|
||||
onError: onError,
|
||||
operationName: 'getSchoolHolidays',
|
||||
);
|
||||
|
||||
Future<GetTimegridUnitsResponse> getTimegrid({bool renew = false}) =>
|
||||
resolveFromCache<GetTimegridUnitsResponse>(
|
||||
(onUpdate, _) => GetTimegridUnitsCache(
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
),
|
||||
(onUpdate, _) =>
|
||||
GetTimegridUnitsCache(renew: renew, onUpdate: onUpdate),
|
||||
operationName: 'getTimegrid',
|
||||
);
|
||||
|
||||
Future<GetCustomTimetableEventResponse> getCustomEvents({
|
||||
bool renew = false,
|
||||
void Function(Object)? onError,
|
||||
}) =>
|
||||
resolveFromCache<GetCustomTimetableEventResponse>(
|
||||
(onUpdate, onError) => GetCustomTimetableEventCache(
|
||||
GetCustomTimetableEventParams(AccountData().getUserSecret()),
|
||||
renew: renew,
|
||||
onUpdate: onUpdate,
|
||||
onError: onError,
|
||||
),
|
||||
onError: onError,
|
||||
operationName: '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();
|
||||
AddCustomTimetableEvent(
|
||||
AddCustomTimetableEventParams(AccountData().getUserSecret(), event),
|
||||
).run();
|
||||
|
||||
Future<void> updateCustomEvent(String id, CustomTimetableEvent event) =>
|
||||
UpdateCustomTimetableEvent(UpdateCustomTimetableEventParams(id, event)).run();
|
||||
UpdateCustomTimetableEvent(
|
||||
UpdateCustomTimetableEventParams(id, event),
|
||||
).run();
|
||||
|
||||
Future<void> removeCustomEvent(String id) =>
|
||||
RemoveCustomTimetableEvent(RemoveCustomTimetableEventParams(id)).run();
|
||||
|
||||
@@ -5,7 +5,8 @@ import '../data_provider/timetable_data_provider.dart';
|
||||
class TimetableRepository extends Repository<TimetableState> {
|
||||
final TimetableDataProvider _provider;
|
||||
|
||||
TimetableRepository([TimetableDataProvider? provider]) : _provider = provider ?? TimetableDataProvider();
|
||||
TimetableRepository([TimetableDataProvider? provider])
|
||||
: _provider = provider ?? TimetableDataProvider();
|
||||
|
||||
TimetableDataProvider get data => _provider;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user