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
+3
View File
@@ -10,6 +10,7 @@ import 'holidays_settings.dart';
import 'modules_settings.dart';
import 'notification_settings.dart';
import 'talk_settings.dart';
import 'timetable_favorites_settings.dart';
import 'timetable_settings.dart';
part 'settings.g.dart';
@@ -22,6 +23,7 @@ class Settings {
ModulesSettings modulesSettings;
TimetableSettings timetableSettings;
TimetableFavoritesSettings timetableFavoritesSettings;
TalkSettings talkSettings;
ChatBackgroundSettings chatBackgroundSettings;
FileSettings fileSettings;
@@ -36,6 +38,7 @@ class Settings {
required this.devToolsEnabled,
required this.modulesSettings,
required this.timetableSettings,
required this.timetableFavoritesSettings,
required this.talkSettings,
required this.chatBackgroundSettings,
required this.fileSettings,
+4
View File
@@ -15,6 +15,9 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
timetableSettings: TimetableSettings.fromJson(
json['timetableSettings'] as Map<String, dynamic>,
),
timetableFavoritesSettings: TimetableFavoritesSettings.fromJson(
json['timetableFavoritesSettings'] as Map<String, dynamic>,
),
talkSettings: TalkSettings.fromJson(
json['talkSettings'] as Map<String, dynamic>,
),
@@ -46,6 +49,7 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
'devToolsEnabled': instance.devToolsEnabled,
'modulesSettings': instance.modulesSettings.toJson(),
'timetableSettings': instance.timetableSettings.toJson(),
'timetableFavoritesSettings': instance.timetableFavoritesSettings.toJson(),
'talkSettings': instance.talkSettings.toJson(),
'chatBackgroundSettings': instance.chatBackgroundSettings.toJson(),
'fileSettings': instance.fileSettings.toJson(),
@@ -0,0 +1,49 @@
import 'package:json_annotation/json_annotation.dart';
import '../api/marianumconnect/queries/timetable_get_element_week/timetable_element_type.dart';
part 'timetable_favorites_settings.g.dart';
/// A timetable element the user starred for quick access from the picker.
@JsonSerializable(explicitToJson: true)
class FavoriteTimetableElement {
final TimetableElementType type;
final int id;
final String label;
FavoriteTimetableElement({
required this.type,
required this.id,
required this.label,
});
factory FavoriteTimetableElement.fromJson(Map<String, dynamic> json) =>
_$FavoriteTimetableElementFromJson(json);
Map<String, dynamic> toJson() => _$FavoriteTimetableElementToJson(this);
}
@JsonSerializable(explicitToJson: true)
class TimetableFavoritesSettings {
List<FavoriteTimetableElement> favorites;
TimetableFavoritesSettings({required this.favorites});
bool isFavorite(TimetableElementType type, int id) =>
favorites.any((f) => f.type == type && f.id == id);
/// Adds or removes the element in place. Returns the new favorite state
/// (`true` = now starred). Callers persist via `SettingsCubit.val(write: true)`.
bool toggle(TimetableElementType type, int id, String label) {
final existing = favorites.where((f) => f.type == type && f.id == id);
if (existing.isNotEmpty) {
favorites.removeWhere((f) => f.type == type && f.id == id);
return false;
}
favorites.add(FavoriteTimetableElement(type: type, id: id, label: label));
return true;
}
factory TimetableFavoritesSettings.fromJson(Map<String, dynamic> json) =>
_$TimetableFavoritesSettingsFromJson(json);
Map<String, dynamic> toJson() => _$TimetableFavoritesSettingsToJson(this);
}
@@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'timetable_favorites_settings.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
FavoriteTimetableElement _$FavoriteTimetableElementFromJson(
Map<String, dynamic> json,
) => FavoriteTimetableElement(
type: $enumDecode(_$TimetableElementTypeEnumMap, json['type']),
id: (json['id'] as num).toInt(),
label: json['label'] as String,
);
Map<String, dynamic> _$FavoriteTimetableElementToJson(
FavoriteTimetableElement instance,
) => <String, dynamic>{
'type': _$TimetableElementTypeEnumMap[instance.type]!,
'id': instance.id,
'label': instance.label,
};
const _$TimetableElementTypeEnumMap = {
TimetableElementType.student: 'student',
TimetableElementType.teacher: 'teacher',
TimetableElementType.room: 'room',
TimetableElementType.schoolClass: 'schoolClass',
};
TimetableFavoritesSettings _$TimetableFavoritesSettingsFromJson(
Map<String, dynamic> json,
) => TimetableFavoritesSettings(
favorites: (json['favorites'] as List<dynamic>)
.map((e) => FavoriteTimetableElement.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$TimetableFavoritesSettingsToJson(
TimetableFavoritesSettings instance,
) => <String, dynamic>{
'favorites': instance.favorites.map((e) => e.toJson()).toList(),
};