implemented foreign timetable support for students, teachers, rooms, and classes, including a searchable element picker with favorites support, introduced a capabilities system for feature gating, refactored the timetable UI into a reusable TimetableCalendarView component, and redesigned the chat input field with a unified emoji picker and integrated attachment actions.

This commit is contained in:
2026-05-31 21:29:16 +02:00
parent 6e12da08c0
commit b6d06dd3b4
41 changed files with 2325 additions and 290 deletions
+18 -1
View File
@@ -30,6 +30,7 @@ import 'share_intent/share_intent_listener.dart';
import 'state/app/modules/account/bloc/account_bloc.dart';
import 'state/app/modules/account/bloc/account_state.dart';
import 'state/app/modules/breaker/bloc/breaker_bloc.dart';
import 'state/app/modules/capabilities/bloc/capabilities_cubit.dart';
import 'state/app/modules/chat/bloc/chat_bloc.dart';
import 'state/app/modules/chat_list/bloc/chat_list_bloc.dart';
import 'state/app/modules/settings/bloc/settings_cubit.dart';
@@ -159,6 +160,7 @@ Future<void> main() async {
),
),
BlocProvider<BreakerBloc>(create: (_) => BreakerBloc()),
BlocProvider<CapabilitiesCubit>(create: (_) => CapabilitiesCubit()),
BlocProvider<ChatListBloc>(create: (_) => ChatListBloc()),
BlocProvider<ChatBloc>(
create: (ctx) => ChatBloc(chatListBloc: ctx.read<ChatListBloc>()),
@@ -193,7 +195,13 @@ class _MainState extends State<Main> {
accountBloc.setStatus(
value ? AccountStatus.loggedIn : AccountStatus.loggedOut,
);
if (value) _scheduleSessionValidation(accountBloc);
if (value) {
_scheduleSessionValidation(accountBloc);
// Cold start while already logged in: the account status doesn't
// change, so the loggedIn listener below never fires — refresh
// capabilities here.
unawaited(context.read<CapabilitiesCubit>().load());
}
});
}
@@ -262,6 +270,11 @@ class _MainState extends State<Main> {
listenWhen: (previous, current) =>
previous.status != current.status,
listener: (context, accountState) {
// Fresh login (loggedOut -> loggedIn): pull capability flags
// for the newly authenticated user.
if (accountState.status == AccountStatus.loggedIn) {
unawaited(context.read<CapabilitiesCubit>().load());
}
if (accountState.status != AccountStatus.loggedOut) return;
// A pending share would otherwise survive logout and be
// re-applied after re-login with file paths the OS may
@@ -283,6 +296,7 @@ class _MainState extends State<Main> {
final chatListBloc = context.read<ChatListBloc>();
final chatBloc = context.read<ChatBloc>();
final breakerBloc = context.read<BreakerBloc>();
final capabilitiesCubit = context.read<CapabilitiesCubit>();
// Defer the actual wipe until after this frame so the
// App tree (TimetableBloc/ChatListBloc watchers etc.)
// is already torn down. Resetting blocs while App is
@@ -295,6 +309,7 @@ class _MainState extends State<Main> {
chatListBloc: chatListBloc,
chatBloc: chatBloc,
breakerBloc: breakerBloc,
capabilitiesCubit: capabilitiesCubit,
),
);
});
@@ -339,6 +354,7 @@ Future<void> _wipeUserState({
required ChatListBloc chatListBloc,
required ChatBloc chatBloc,
required BreakerBloc breakerBloc,
required CapabilitiesCubit capabilitiesCubit,
}) async {
try {
// Reset user-data blocs whose tree is no longer mounted after the
@@ -351,6 +367,7 @@ Future<void> _wipeUserState({
chatListBloc.reset(),
chatBloc.reset(),
breakerBloc.reset(),
capabilitiesCubit.reset(),
]);
final prefs = await SharedPreferences.getInstance();
await prefs.clear();