diff --git a/README.md b/README.md index 10b813b8..b8361eb4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ![xcodebuild](https://github.com/TUM-Dev/campus_flutter/actions/workflows/deploy_beta.yml/badge.svg?branch=development) ![xcodebuild](https://github.com/TUM-Dev/campus_flutter/actions/workflows/deploy_web.yml/badge.svg?branch=development) -[![Discord Channel](https://img.shields.io/badge/Chat-on%20Discord-brightgreen)](https://discord.gg/k558T6ktuh) @@ -49,7 +48,7 @@ If you want to participate in the beta of this app, enter your details [here](ht - [Privacy policy](https://app.tum.de/landing/privacy/) ## Support -You can reach us on [Discord](https://discord.gg/k558T6ktuh), [GitHub](https://github.com/TUM-Dev/campus_flutter) or via E-Mail [app@tum.de](mailto:app@tum.de) +You can reach us on [GitHub](https://github.com/TUM-Dev/campus_flutter) or via E-Mail [app@tum.de](mailto:app@tum.de) ## Development diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 40803d96..f6f8945b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -185,7 +185,7 @@ SPEC CHECKSUMS: flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461 - google_maps_flutter_ios: 19fa4ef0fb77132d131df7cd974cb74c76f0fcd4 + google_maps_flutter_ios: d9b9a308a1f14275c2967aefa639018a02bd44e8 GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe GoogleMaps: b47b67bd63d708477d6ff457da2d695c0d8ceb5f GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 @@ -197,7 +197,7 @@ SPEC CHECKSUMS: PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a + sqflite: 50a33e1d72bd59ee092a519a35d107502757ebed url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 diff --git a/lib/base/localization/app_de.arb b/lib/base/localization/app_de.arb index f3a1c138..024aaea4 100644 --- a/lib/base/localization/app_de.arb +++ b/lib/base/localization/app_de.arb @@ -16,6 +16,7 @@ "hideFailedGrades":"Durchgefallene Noten ausblenden", "defaultMapsApplication":"Standard-Kartenanwendung", "map":"Karte", + "maps":"Karten", "contactMore":"Kontakt & Mehr", "tokenPermissions":"Berechtigungen für Token", "permissionChangePossibleInTUMonline":"Du kannst deine Berechtigungen in TUMOnline ändern", @@ -43,7 +44,6 @@ "nearestStudyRooms":"Nächste Lernräume", "noNearFreeStudyRoomsFound":"Keine Lernräume in deiner Nähe gefunden", "mostSearchedRooms":"Meist gesuchte Räume", - "noMoviesFound":"Keine Filme gefunden", "tuition":"Tuition", "tuitionFees":"Studiengebühren", "tuitionPaid":"Bezahlt", @@ -83,7 +83,6 @@ "thisMeeting":"Dieser Termin", "lectureDetails":"Vorlesungsdetails", "noLecturesSelected":"keine vorlesungen ausgewählt", - "noLecturesFound":"keine vorlesungen gefunden", "showDirections":"Wegbeschreibung anzeigen", "notAvailableAbbrev":"n. v.", "becomeABetaTester":"Werde Beta-Tester", @@ -158,11 +157,9 @@ } } }, - "noNewsFound":"Keine Nachrichten gefunden", "news":"Nachrichten", "movies":"Filme", "personalData":"Persönliche Daten", - "noGradesFound":"Keine Noten gefunden", "calendarViewToday":"Heute", "calendarViewDay":"Tag", "calendarViewWeek":"Woche", @@ -200,14 +197,12 @@ } } }, - "noRoomsFound":"Keine Räume gefunden", - "noMealPlanFound":"Kein Essensplan gefunden", "mealPlan":"Essensplan", - "noEntriesFoundSearch":"Keine {searchCategory} gefunden", - "@noEntriesFoundSearch":{ - "description":"Title of Search Category", + "noEntriesFound":"Keine {category} gefunden!", + "@noEntriesFound":{ + "description":"Category", "placeholders":{ - "searchCategory":{ + "category":{ "type":"String", "example":"Grades" } @@ -219,7 +214,6 @@ "search":"Suche", "today":"Heute", "tomorrow":"Morgen", - "noMapsFound":"Keine Karten gefunden", "roomSearch":"Raum Suche", "personSearch":"Personen Suche", "badResponse":"Ungültige Antwort", diff --git a/lib/base/localization/app_en.arb b/lib/base/localization/app_en.arb index b9ad1acf..5d8b5575 100644 --- a/lib/base/localization/app_en.arb +++ b/lib/base/localization/app_en.arb @@ -16,6 +16,7 @@ "hideFailedGrades":"Hide Failed Grades", "defaultMapsApplication":"Default Maps Application", "map":"Map", + "maps":"Maps", "contactMore":"Contact & More", "tokenPermissions":"Token Permissions", "permissionChangePossibleInTUMonline":"You can change your permissions on TUMOnline", @@ -41,9 +42,8 @@ "done":"Done", "studyRooms":"Study Rooms", "nearestStudyRooms":"Nearest Study Rooms", - "noNearFreeStudyRoomsFound":"no study rooms near you found", + "noNearFreeStudyRoomsFound":"No Study Rooms Near You Found!", "mostSearchedRooms":"Most Searched Rooms", - "noMoviesFound":"no movies found", "tuition":"Tuition", "tuitionFees":"Tuition fees", "tuitionPaid":"Tuition Paid", @@ -83,7 +83,6 @@ "thisMeeting":"This Meeting", "lectureDetails":"Lecture Details", "noLecturesSelected":"no lecture selected", - "noLecturesFound":"no lectures found", "showDirections":"Show Directions", "notAvailableAbbrev":"n/a", "becomeABetaTester":"Become a Beta Tester", @@ -158,11 +157,9 @@ } } }, - "noNewsFound":"no news found", "news":"News", "movies":"Movies", "personalData":"Personal Data", - "noGradesFound":"no grades found", "calendarViewToday":"Today", "calendarViewDay":"Day", "calendarViewWeek":"Week", @@ -200,14 +197,12 @@ } } }, - "noRoomsFound":"No Rooms Found", - "noMealPlanFound":"No Meal Plan Found", "mealPlan":"Meal Plan", - "noEntriesFoundSearch":"No {searchCategory} Found", - "@noEntriesFoundSearch":{ - "description":"Title of Search Category", + "noEntriesFound":"No {category} Found!", + "@noEntriesFound":{ + "description":"Category", "placeholders":{ - "searchCategory":{ + "category":{ "type":"String", "example":"Grades" } @@ -219,7 +214,6 @@ "search":"Search", "today":"Today", "tomorrow":"Tomorrow", - "noMapsFound":"No Maps Found", "roomSearch":"Room Search", "personSearch":"Person Search", "badResponse":"Bad Response", diff --git a/lib/base/networking/base/rest_client.dart b/lib/base/networking/base/rest_client.dart index 58e1233d..e3bd1fed 100644 --- a/lib/base/networking/base/rest_client.dart +++ b/lib/base/networking/base/rest_client.dart @@ -36,15 +36,16 @@ class RESTClient { if (body.headers["content-type"]?.first.contains("xml") ?? false) { final transformer = Xml2Json(); transformer.parse(decoded); - return transformer.toParkerWithAttrsCustom( - array: [ - "row", - "event", - "studium", - "raeume", - "gruppen", - "telefon_nebenstellen", - ], + return transformer.toParkerWithAttrs( + entries: { + "row": "rowset", + "event": "events", + "studium": "studien", + "raum": "raeume", + "gruppe": "gruppen", + "nebenstelle": "telefon_nebenstellen", + "card": "cards", + }, ); } else { return decoded; @@ -85,15 +86,16 @@ class RESTClient { if (body.headers["content-type"]?.first.contains("xml") ?? false) { final transformer = Xml2Json(); transformer.parse(decoded); - return transformer.toParkerWithAttrsCustom( - array: [ - "row", - "event", - "studium", - "raeume", - "gruppen", - "telefon_nebenstellen", - ], + return transformer.toParkerWithAttrs( + entries: { + "row": "rowset", + "event": "events", + "studium": "studien", + "raum": "raeume", + "gruppe": "gruppen", + "nebenstelle": "telefon_nebenstellen", + "card": "cards", + }, ); } else { return decoded; diff --git a/lib/calendarComponent/model/calendar_event.dart b/lib/calendarComponent/model/calendar_event.dart index 792b969e..98873380 100644 --- a/lib/calendarComponent/model/calendar_event.dart +++ b/lib/calendarComponent/model/calendar_event.dart @@ -111,23 +111,12 @@ class CalendarEvent extends Searchable { Map toJson() => _$CalendarEventToJson(this); } -@JsonSerializable() -class CalendarEventsData { - final CalendarEvents? events; - - CalendarEventsData({required this.events}); - - factory CalendarEventsData.fromJson(Map json) => - _$CalendarEventsDataFromJson(json); - - Map toJson() => _$CalendarEventsDataToJson(this); -} - @JsonSerializable() class CalendarEvents { - final List event; + @JsonKey(name: "event", defaultValue: []) + final List events; - CalendarEvents({required this.event}); + CalendarEvents({required this.events}); factory CalendarEvents.fromJson(Map json) => _$CalendarEventsFromJson(json); diff --git a/lib/calendarComponent/model/calendar_event.g.dart b/lib/calendarComponent/model/calendar_event.g.dart index 2b3c9f3c..67fa7e57 100644 --- a/lib/calendarComponent/model/calendar_event.g.dart +++ b/lib/calendarComponent/model/calendar_event.g.dart @@ -30,26 +30,15 @@ Map _$CalendarEventToJson(CalendarEvent instance) => 'location': instance.location, }; -CalendarEventsData _$CalendarEventsDataFromJson(Map json) => - CalendarEventsData( - events: json['events'] == null - ? null - : CalendarEvents.fromJson(json['events'] as Map), - ); - -Map _$CalendarEventsDataToJson(CalendarEventsData instance) => - { - 'events': instance.events, - }; - CalendarEvents _$CalendarEventsFromJson(Map json) => CalendarEvents( - event: (json['event'] as List) - .map((e) => CalendarEvent.fromJson(e as Map)) - .toList(), + events: (json['event'] as List?) + ?.map((e) => CalendarEvent.fromJson(e as Map)) + .toList() ?? + [], ); Map _$CalendarEventsToJson(CalendarEvents instance) => { - 'event': instance.event, + 'event': instance.events, }; diff --git a/lib/calendarComponent/services/calendar_service.dart b/lib/calendarComponent/services/calendar_service.dart index 1dd445c7..fc69806c 100644 --- a/lib/calendarComponent/services/calendar_service.dart +++ b/lib/calendarComponent/services/calendar_service.dart @@ -11,14 +11,14 @@ class CalendarService { bool forcedRefresh, ) async { RESTClient restClient = getIt(); - final response = await restClient.getWithException( + final response = await restClient + .getWithException( TumOnlineApi(TumOnlineServiceCalendar()), - CalendarEventsData.fromJson, + CalendarEvents.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.events?.event ?? []); + return (response.saved, response.data.events); } static Future createCalendarEvent( diff --git a/lib/calendarComponent/viewModels/calendar_viewmodel.dart b/lib/calendarComponent/viewModels/calendar_viewmodel.dart index c2820e50..a3f87b9d 100644 --- a/lib/calendarComponent/viewModels/calendar_viewmodel.dart +++ b/lib/calendarComponent/viewModels/calendar_viewmodel.dart @@ -29,7 +29,7 @@ class CalendarViewModel { final filteredEvents = events.value ?.where( - (element) => element.startDate.isBefore( + (element) => element.startDate.isAfter( DateTime.now(), ), ) @@ -40,7 +40,7 @@ class CalendarViewModel { final currentDay = DateTime(currentDate.year, currentDate.month, currentDate.day); - for (CalendarEvent event in events.value ?? []) { + for (CalendarEvent event in filteredEvents) { final dateToCheck = DateTime( event.startDate.year, event.startDate.month, diff --git a/lib/calendarComponent/views/calendar_day_view.dart b/lib/calendarComponent/views/calendar_day_view.dart index 76619bd6..e93b369b 100644 --- a/lib/calendarComponent/views/calendar_day_view.dart +++ b/lib/calendarComponent/views/calendar_day_view.dart @@ -41,7 +41,6 @@ class CalendarDayView extends ConsumerWidget { }, headerDateFormat: "EEEE, dd.MM.yyyy", showNavigationArrow: true, - maxDate: getIt().maxDate(ref), timeSlotViewSettings: const TimeSlotViewSettings( startHour: 7, endHour: 22, diff --git a/lib/calendarComponent/views/calendar_month_view.dart b/lib/calendarComponent/views/calendar_month_view.dart index 14d818ca..ae17aa8d 100644 --- a/lib/calendarComponent/views/calendar_month_view.dart +++ b/lib/calendarComponent/views/calendar_month_view.dart @@ -36,7 +36,6 @@ class CalendarMonthView extends ConsumerWidget { firstDayOfWeek: 1, showDatePickerButton: true, showNavigationArrow: true, - maxDate: getIt().maxDate(ref), onTap: (details) { if (details.targetElement == CalendarElement.appointment) { getIt().showDetails( diff --git a/lib/calendarComponent/views/calendar_week_view.dart b/lib/calendarComponent/views/calendar_week_view.dart index 0ec7c883..bf59a54b 100644 --- a/lib/calendarComponent/views/calendar_week_view.dart +++ b/lib/calendarComponent/views/calendar_week_view.dart @@ -47,7 +47,6 @@ class CalendarWeekView extends ConsumerWidget { headerDateFormat: "", showWeekNumber: true, showNavigationArrow: true, - maxDate: getIt().maxDate(ref), timeSlotViewSettings: const TimeSlotViewSettings( startHour: 7, endHour: 22, diff --git a/lib/gradeComponent/model/average_grade.dart b/lib/gradeComponent/model/average_grade.dart index 36b8e9ec..cc719df9 100644 --- a/lib/gradeComponent/model/average_grade.dart +++ b/lib/gradeComponent/model/average_grade.dart @@ -27,22 +27,9 @@ class AverageGrade { Map toJson() => _$AverageGradeToJson(this); } -@JsonSerializable() -class AverageGradeResponse { - @JsonKey(name: "studien") - final AverageGrades? averageGradeData; - - AverageGradeResponse({required this.averageGradeData}); - - factory AverageGradeResponse.fromJson(Map json) => - _$AverageGradeResponseFromJson(json); - - Map toJson() => _$AverageGradeResponseToJson(this); -} - @JsonSerializable() class AverageGrades { - @JsonKey(name: "studium") + @JsonKey(name: "studium", defaultValue: []) final List averageGrades; AverageGrades({required this.averageGrades}); diff --git a/lib/gradeComponent/model/average_grade.g.dart b/lib/gradeComponent/model/average_grade.g.dart index 55cfaae5..f3981d6f 100644 --- a/lib/gradeComponent/model/average_grade.g.dart +++ b/lib/gradeComponent/model/average_grade.g.dart @@ -20,25 +20,12 @@ Map _$AverageGradeToJson(AverageGrade instance) => 'avg_grade_weighted_by_credits': instance.averageGrade, }; -AverageGradeResponse _$AverageGradeResponseFromJson( - Map json) => - AverageGradeResponse( - averageGradeData: json['studien'] == null - ? null - : AverageGrades.fromJson(json['studien'] as Map), - ); - -Map _$AverageGradeResponseToJson( - AverageGradeResponse instance) => - { - 'studien': instance.averageGradeData, - }; - AverageGrades _$AverageGradesFromJson(Map json) => AverageGrades( - averageGrades: (json['studium'] as List) - .map((e) => AverageGrade.fromJson(e as Map)) - .toList(), + averageGrades: (json['studium'] as List?) + ?.map((e) => AverageGrade.fromJson(e as Map)) + .toList() ?? + [], ); Map _$AverageGradesToJson(AverageGrades instance) => diff --git a/lib/gradeComponent/model/grade.dart b/lib/gradeComponent/model/grade.dart index b17e676c..3d919e8f 100644 --- a/lib/gradeComponent/model/grade.dart +++ b/lib/gradeComponent/model/grade.dart @@ -81,22 +81,9 @@ class Grade extends Searchable { ]; } -@JsonSerializable() -class GradeData { - @JsonKey(name: "rowset") - Grades? gradesAttribute; - - GradeData({required this.gradesAttribute}); - - factory GradeData.fromJson(Map json) => - _$GradeDataFromJson(json); - - Map toJson() => _$GradeDataToJson(this); -} - @JsonSerializable() class Grades { - @JsonKey(name: "row") + @JsonKey(name: "row", defaultValue: []) final List personalGrades; Grades({required this.personalGrades}); diff --git a/lib/gradeComponent/model/grade.g.dart b/lib/gradeComponent/model/grade.g.dart index 4b9dca61..13c0d5a8 100644 --- a/lib/gradeComponent/model/grade.g.dart +++ b/lib/gradeComponent/model/grade.g.dart @@ -34,20 +34,11 @@ Map _$GradeToJson(Grade instance) => { 'st_studium_nr': instance.studyNumber, }; -GradeData _$GradeDataFromJson(Map json) => GradeData( - gradesAttribute: json['rowset'] == null - ? null - : Grades.fromJson(json['rowset'] as Map), - ); - -Map _$GradeDataToJson(GradeData instance) => { - 'rowset': instance.gradesAttribute, - }; - Grades _$GradesFromJson(Map json) => Grades( - personalGrades: (json['row'] as List) - .map((e) => Grade.fromJson(e as Map)) - .toList(), + personalGrades: (json['row'] as List?) + ?.map((e) => Grade.fromJson(e as Map)) + .toList() ?? + [], ); Map _$GradesToJson(Grades instance) => { diff --git a/lib/gradeComponent/services/grade_service.dart b/lib/gradeComponent/services/grade_service.dart index 9ea1e528..69227d12 100644 --- a/lib/gradeComponent/services/grade_service.dart +++ b/lib/gradeComponent/services/grade_service.dart @@ -12,31 +12,25 @@ class GradeService { ) async { RESTClient restClient = getIt(); final response = await restClient - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServicePersonalGrades()), - GradeData.fromJson, + Grades.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return ( - saved: response.saved, - data: response.data.gradesAttribute?.personalGrades ?? [] - ); + return (saved: response.saved, data: response.data.personalGrades); } static Future<({DateTime? saved, List data})> fetchAverageGrades(bool forcedRefresh) async { RESTClient restClient = getIt(); - final response = await restClient.getWithException( + final response = await restClient + .getWithException( TumOnlineApi(TumOnlineServiceAverageGrades()), - AverageGradeResponse.fromJson, + AverageGrades.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return ( - saved: response.saved, - data: response.data.averageGradeData?.averageGrades ?? [] - ); + return (saved: response.saved, data: response.data.averageGrades); } } diff --git a/lib/gradeComponent/views/grades_view.dart b/lib/gradeComponent/views/grades_view.dart index 01f3e23b..face6a23 100644 --- a/lib/gradeComponent/views/grades_view.dart +++ b/lib/gradeComponent/views/grades_view.dart @@ -39,7 +39,13 @@ class _GradesViewState extends ConsumerState stream: ref.watch(gradeVM).studyProgramGrades, dataBuilder: (context, data) { if (data.isEmpty) { - return Center(child: Text(context.localizations.noGradesFound)); + return Center( + child: Text( + context.localizations.noEntriesFound( + context.localizations.grades, + ), + ), + ); } else { final lastFetched = ref.read(gradeViewModel).lastFetched.value; return OrientationBuilder( diff --git a/lib/homeComponent/contactComponent/views/contact_card_loading_view.dart b/lib/homeComponent/contactComponent/views/contact_card_loading_view.dart index 15d756b4..42b9b58a 100644 --- a/lib/homeComponent/contactComponent/views/contact_card_loading_view.dart +++ b/lib/homeComponent/contactComponent/views/contact_card_loading_view.dart @@ -7,33 +7,37 @@ class ContactCardLoadingView extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( - children: [ - const CircleAvatar( - backgroundImage: - AssetImage('assets/images/placeholders/portrait_placeholder.png'), - radius: 50, - ), - const Padding(padding: EdgeInsets.only(left: 15)), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ShimmerView( - child: PlaceholderText( - text: "Max Mustermann", - style: Theme.of(context).textTheme.headlineSmall, - ), - ), - const ShimmerView(child: PlaceholderText(text: "go43hum")), - const ShimmerView( - child: PlaceholderText(text: "max.mustermann@tum.de"), - ), - const ShimmerView( - child: PlaceholderText(text: "Musterstudiengang (B.Sc.)"), + return Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + children: [ + const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/placeholders/portrait_placeholder.png', ), - ], - ), - ], + radius: 50, + ), + const Padding(padding: EdgeInsets.only(left: 15)), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ShimmerView( + child: PlaceholderText( + text: "Max Mustermann", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + const ShimmerView(child: PlaceholderText(text: "go43hum")), + const ShimmerView( + child: PlaceholderText(text: "max.mustermann@tum.de"), + ), + const ShimmerView( + child: PlaceholderText(text: "Musterstudiengang (B.Sc.)"), + ), + ], + ), + ], + ), ); } } diff --git a/lib/homeComponent/contactComponent/views/contact_card_view.dart b/lib/homeComponent/contactComponent/views/contact_card_view.dart index fa33f6d8..715d1c47 100644 --- a/lib/homeComponent/contactComponent/views/contact_card_view.dart +++ b/lib/homeComponent/contactComponent/views/contact_card_view.dart @@ -36,10 +36,11 @@ class _ContactCardViewState extends ConsumerState { (personDetails, studentCard) => (personDetails, studentCard), ), builder: (context, snapshot) { - if (snapshot.hasData && - snapshot.data?.$1 != null && - snapshot.data?.$2 != null) { - return contactInfo(snapshot.data!.$1!, snapshot.data!.$2!); + if (snapshot.hasData && snapshot.data?.$1 != null) { + return contactInfo( + snapshot.data!.$1!, + snapshot.data!.$2?.firstOrNull, + ); } else { return DelayedLoadingIndicator( name: context.localizations.personalData, @@ -51,8 +52,7 @@ class _ContactCardViewState extends ConsumerState { ); } - Widget contactInfo(PersonDetails data, StudentCard studentCard) { - final studies = studentCard.studies?.study; + Widget contactInfo(PersonDetails data, StudentCard? studentCard) { return Padding( padding: const EdgeInsets.all(10.0), child: Row( @@ -80,9 +80,11 @@ class _ContactCardViewState extends ConsumerState { ref.watch(profileViewModel).profile.value?.tumID ?? "go42tum", ), Text(data.email), - for (var studyProgram in studies?.sublist( + for (var studyProgram in studentCard?.studies.sublist( 0, - studies.length >= 2 ? 2 : studies.length, + studentCard.studies.length >= 2 + ? 2 + : studentCard.studies.length, ) ?? []) ...[ Text( diff --git a/lib/lectureComponent/model/lecture.dart b/lib/lectureComponent/model/lecture.dart index bd3f2644..5e7a58b8 100644 --- a/lib/lectureComponent/model/lecture.dart +++ b/lib/lectureComponent/model/lecture.dart @@ -96,22 +96,9 @@ class Lecture extends Searchable { Map toJson() => _$LectureToJson(this); } -@JsonSerializable() -class LectureData { - @JsonKey(name: "rowset") - Lectures? lecturesAttribute; - - LectureData({required this.lecturesAttribute}); - - factory LectureData.fromJson(Map json) => - _$LectureDataFromJson(json); - - Map toJson() => _$LectureDataToJson(this); -} - @JsonSerializable() class Lectures { - @JsonKey(name: "row") + @JsonKey(name: "row", defaultValue: []) final List lectures; Lectures({required this.lectures}); diff --git a/lib/lectureComponent/model/lecture.g.dart b/lib/lectureComponent/model/lecture.g.dart index 82c723fe..5778ff4f 100644 --- a/lib/lectureComponent/model/lecture.g.dart +++ b/lib/lectureComponent/model/lecture.g.dart @@ -42,21 +42,11 @@ Map _$LectureToJson(Lecture instance) => { 'vortragende_mitwirkende': instance.speaker, }; -LectureData _$LectureDataFromJson(Map json) => LectureData( - lecturesAttribute: json['rowset'] == null - ? null - : Lectures.fromJson(json['rowset'] as Map), - ); - -Map _$LectureDataToJson(LectureData instance) => - { - 'rowset': instance.lecturesAttribute, - }; - Lectures _$LecturesFromJson(Map json) => Lectures( - lectures: (json['row'] as List) - .map((e) => Lecture.fromJson(e as Map)) - .toList(), + lectures: (json['row'] as List?) + ?.map((e) => Lecture.fromJson(e as Map)) + .toList() ?? + [], ); Map _$LecturesToJson(Lectures instance) => { diff --git a/lib/lectureComponent/model/lecture_details.dart b/lib/lectureComponent/model/lecture_details.dart index f51bdf7d..7c561c52 100644 --- a/lib/lectureComponent/model/lecture_details.dart +++ b/lib/lectureComponent/model/lecture_details.dart @@ -116,19 +116,6 @@ class LectureDetails { Map toJson() => _$LectureDetailsToJson(this); } -@JsonSerializable() -class LectureDetailsData { - @JsonKey(name: "rowset") - LectureDetailsElement lectureDetailsAttribute; - - LectureDetailsData({required this.lectureDetailsAttribute}); - - factory LectureDetailsData.fromJson(Map json) => - _$LectureDetailsDataFromJson(json); - - Map toJson() => _$LectureDetailsDataToJson(this); -} - @JsonSerializable() class LectureDetailsElement { @JsonKey(name: "row", fromJson: _lectureDetailsFromJson) diff --git a/lib/lectureComponent/model/lecture_details.g.dart b/lib/lectureComponent/model/lecture_details.g.dart index e19298f9..757ee35c 100644 --- a/lib/lectureComponent/model/lecture_details.g.dart +++ b/lib/lectureComponent/model/lecture_details.g.dart @@ -69,17 +69,6 @@ Map _$LectureDetailsToJson(LectureDetails instance) => 'pruef_termine_url': instance.examDateURL, }; -LectureDetailsData _$LectureDetailsDataFromJson(Map json) => - LectureDetailsData( - lectureDetailsAttribute: LectureDetailsElement.fromJson( - json['rowset'] as Map), - ); - -Map _$LectureDetailsDataToJson(LectureDetailsData instance) => - { - 'rowset': instance.lectureDetailsAttribute, - }; - LectureDetailsElement _$LectureDetailsElementFromJson( Map json) => LectureDetailsElement( diff --git a/lib/lectureComponent/services/lecture_details_service.dart b/lib/lectureComponent/services/lecture_details_service.dart index 3f9381cd..dff40047 100644 --- a/lib/lectureComponent/services/lecture_details_service.dart +++ b/lib/lectureComponent/services/lecture_details_service.dart @@ -11,17 +11,14 @@ class LectureDetailsService { bool forcedRefresh, ) async { RESTClient restClient = getIt(); - final response = await restClient.getWithException( TumOnlineApi(TumOnlineServiceLectureDetails(lvNr: lvNumber)), - LectureDetailsData.fromJson, + LectureDetailsElement.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return ( - response.saved, - response.data.lectureDetailsAttribute.lectureDetails - ); + return (response.saved, response.data.lectureDetails); } } diff --git a/lib/lectureComponent/services/lecture_search_service.dart b/lib/lectureComponent/services/lecture_search_service.dart index 2db9e3b4..0dd678fa 100644 --- a/lib/lectureComponent/services/lecture_search_service.dart +++ b/lib/lectureComponent/services/lecture_search_service.dart @@ -11,13 +11,13 @@ class LectureSearchService { String query, ) async { final response = await getIt() - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServiceLectureSearch(search: query)), - LectureData.fromJson, + Lectures.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.lecturesAttribute?.lectures ?? []); + return (response.saved, response.data.lectures); } } diff --git a/lib/lectureComponent/services/lecture_service.dart b/lib/lectureComponent/services/lecture_service.dart index 4eb1d98d..2041d77e 100644 --- a/lib/lectureComponent/services/lecture_service.dart +++ b/lib/lectureComponent/services/lecture_service.dart @@ -11,12 +11,12 @@ class LectureService { ) async { RESTClient restClient = getIt(); final response = await restClient - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServicePersonalLectures()), - LectureData.fromJson, + Lectures.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.lecturesAttribute?.lectures ?? []); + return (response.saved, response.data.lectures); } } diff --git a/lib/lectureComponent/views/lectures_view.dart b/lib/lectureComponent/views/lectures_view.dart index 8707ab74..f243c3f6 100644 --- a/lib/lectureComponent/views/lectures_view.dart +++ b/lib/lectureComponent/views/lectures_view.dart @@ -39,7 +39,13 @@ class _LecturesViewState extends ConsumerState stream: ref.watch(lectureViewModel).lectures, dataBuilder: (context, data) { if (data.isEmpty) { - return Center(child: Text(context.localizations.noLecturesFound)); + return Center( + child: Text( + context.localizations.noEntriesFound( + context.localizations.lecture, + ), + ), + ); } else { Future(() { ref.read(lectureSplitViewModel).selectedWidget.add( diff --git a/lib/movieComponent/views/homeWidget/movies_widget_view.dart b/lib/movieComponent/views/homeWidget/movies_widget_view.dart index 30c8e715..fc8d5cab 100644 --- a/lib/movieComponent/views/homeWidget/movies_widget_view.dart +++ b/lib/movieComponent/views/homeWidget/movies_widget_view.dart @@ -38,7 +38,11 @@ class _MoviesHomeWidgetState extends ConsumerState { child: SizedBox( height: MediaQuery.of(context).size.height * 0.34, child: Center( - child: Text(context.localizations.noMoviesFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.movies, + ), + ), ), ), ); diff --git a/lib/navigaTumComponent/views/navigatum_room_maps_view.dart b/lib/navigaTumComponent/views/navigatum_room_maps_view.dart index 63535aa1..05970ba1 100644 --- a/lib/navigaTumComponent/views/navigatum_room_maps_view.dart +++ b/lib/navigaTumComponent/views/navigatum_room_maps_view.dart @@ -49,7 +49,11 @@ class NavigaTumRoomMapsView extends StatelessWidget { aspectRatio: 2, ) : Center( - child: Text(context.localizations.noMapsFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.maps, + ), + ), ), ), ); diff --git a/lib/navigaTumComponent/views/navigatum_room_view.dart b/lib/navigaTumComponent/views/navigatum_room_view.dart index 564275cf..5a8ae21c 100644 --- a/lib/navigaTumComponent/views/navigatum_room_view.dart +++ b/lib/navigaTumComponent/views/navigatum_room_view.dart @@ -44,7 +44,9 @@ class _NavigaTumRoomState extends ConsumerState { @override void initState() { viewModel = navigaTumDetailsViewModel(widget.id); - ref.read(viewModel).fetchDetails(false, context); + WidgetsBinding.instance.addPostFrameCallback( + (timeStamp) => ref.read(viewModel).fetchDetails(false, context), + ); super.initState(); } diff --git a/lib/newsComponent/views/homeWidget/news_widget_view.dart b/lib/newsComponent/views/homeWidget/news_widget_view.dart index 348e47f1..253c506e 100644 --- a/lib/newsComponent/views/homeWidget/news_widget_view.dart +++ b/lib/newsComponent/views/homeWidget/news_widget_view.dart @@ -52,7 +52,11 @@ class _NewsWidgetViewState extends ConsumerState { height: 300, child: Card( child: Center( - child: Text(context.localizations.noNewsFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.news, + ), + ), ), ), ); diff --git a/lib/personDetailedComponent/model/person_details.dart b/lib/personDetailedComponent/model/person_details.dart index b022ea56..3672ea47 100644 --- a/lib/personDetailedComponent/model/person_details.dart +++ b/lib/personDetailedComponent/model/person_details.dart @@ -43,29 +43,12 @@ class PersonDetails { final ContactInfo? privateContact; @JsonKey(name: "image_data") final String? imageData; - @JsonKey(name: "gruppen", readValue: readValue) - final List? organisations; - @JsonKey(name: "raeume", readValue: readValue) - final List? rooms; - @JsonKey(name: "telefon_nebenstellen", readValue: readValue) - final List? phoneExtensions; - - static Object? readValue(Map json, String key) { - final data = json[key] as List?; - if (data != null) { - if ((data.first as Map).values.first is List) { - return (data.first as Map).values.first - as List; - } else { - return data.map((e) { - final singleData = e as Map; - return singleData[singleData.keys.first]; - }).toList(); - } - } else { - return null; - } - } + @JsonKey(name: "gruppe", defaultValue: []) + final List organisations; + @JsonKey(name: "raum", defaultValue: []) + final List rooms; + @JsonKey(name: "nebenstelle", defaultValue: []) + final List phoneExtensions; PersonDetails({ required this.nr, @@ -79,9 +62,9 @@ class PersonDetails { this.officialContact, this.privateContact, this.imageData, - this.organisations, - this.rooms, - this.phoneExtensions, + required this.organisations, + required this.rooms, + required this.phoneExtensions, }); String get fullName { diff --git a/lib/personDetailedComponent/model/person_details.g.dart b/lib/personDetailedComponent/model/person_details.g.dart index fe599390..190f2cbc 100644 --- a/lib/personDetailedComponent/model/person_details.g.dart +++ b/lib/personDetailedComponent/model/person_details.g.dart @@ -23,17 +23,18 @@ PersonDetails _$PersonDetailsFromJson(Map json) => ? null : ContactInfo.fromJson(json['privat'] as Map), imageData: json['image_data'] as String?, - organisations: - (PersonDetails.readValue(json, 'gruppen') as List?) + organisations: (json['gruppe'] as List?) ?.map((e) => Organisation.fromJson(e as Map)) - .toList(), - rooms: (PersonDetails.readValue(json, 'raeume') as List?) - ?.map((e) => Room.fromJson(e as Map)) - .toList(), - phoneExtensions: (PersonDetails.readValue(json, 'telefon_nebenstellen') - as List?) - ?.map((e) => PhoneExtension.fromJson(e as Map)) - .toList(), + .toList() ?? + [], + rooms: (json['raum'] as List?) + ?.map((e) => Room.fromJson(e as Map)) + .toList() ?? + [], + phoneExtensions: (json['nebenstelle'] as List?) + ?.map((e) => PhoneExtension.fromJson(e as Map)) + .toList() ?? + [], ); Map _$PersonDetailsToJson(PersonDetails instance) => @@ -49,9 +50,9 @@ Map _$PersonDetailsToJson(PersonDetails instance) => 'dienstlich': instance.officialContact, 'privat': instance.privateContact, 'image_data': instance.imageData, - 'gruppen': instance.organisations, - 'raeume': instance.rooms, - 'telefon_nebenstellen': instance.phoneExtensions, + 'gruppe': instance.organisations, + 'raum': instance.rooms, + 'nebenstelle': instance.phoneExtensions, }; const _$GenderEnumMap = { diff --git a/lib/personDetailedComponent/views/person_details_view.dart b/lib/personDetailedComponent/views/person_details_view.dart index c7b93f2a..438824d1 100644 --- a/lib/personDetailedComponent/views/person_details_view.dart +++ b/lib/personDetailedComponent/views/person_details_view.dart @@ -79,16 +79,17 @@ class _PersonDetailsViewState extends ConsumerState { _image(personDetails.imageData), _name(personDetails.fullNameWithTitle), _contact(personDetails), - if (personDetails.rooms != null) _room(personDetails), + if (personDetails.rooms.isNotEmpty) _room(personDetails), ], ); } Widget _name(String name) { return Padding( - padding: EdgeInsets.symmetric(vertical: context.padding), + padding: EdgeInsets.all(context.padding), child: Text( name, + textAlign: TextAlign.center, style: context.theme.textTheme.titleLarge, ), ); @@ -124,21 +125,21 @@ class _PersonDetailsViewState extends ConsumerState { onTap: () => UrlLauncher.urlString("mailto:${personDetails.email}", ref), ), - if (personDetails.phoneExtensions?.first.phoneNumber != null) + if (personDetails.phoneExtensions.firstOrNull?.phoneNumber != null) ListTile( leading: Icon( Icons.phone, color: context.theme.primaryColor, ), title: Text( - personDetails.phoneExtensions!.first.phoneNumber ?? + personDetails.phoneExtensions.first.phoneNumber ?? context.localizations.unknown, style: const TextStyle( decoration: TextDecoration.underline, ), ), onTap: () => UrlLauncher.urlString( - "tel:${personDetails.phoneExtensions!.first.phoneNumber}", + "tel:${personDetails.phoneExtensions.firstOrNull?.phoneNumber}", ref, ), ), @@ -175,7 +176,7 @@ class _PersonDetailsViewState extends ConsumerState { color: context.theme.primaryColor, ), title: Text( - personDetails.rooms!.first.shortLocationDescription ?? + personDetails.rooms.first.shortLocationDescription ?? context.localizations.unknown, ), trailing: const Icon( @@ -186,7 +187,7 @@ class _PersonDetailsViewState extends ConsumerState { context, MaterialPageRoute( builder: (context) => PersonRoomSearchScaffold( - searchString: personDetails.rooms!.first.id, + searchString: personDetails.rooms.first.id, ), ), ), @@ -197,7 +198,7 @@ class _PersonDetailsViewState extends ConsumerState { color: context.theme.primaryColor, ), title: Text( - personDetails.rooms!.first.floorName ?? + personDetails.rooms.first.floorName ?? context.localizations.unknown, ), ), @@ -207,7 +208,7 @@ class _PersonDetailsViewState extends ConsumerState { color: context.theme.primaryColor, ), title: Text( - personDetails.rooms!.first.buildingName ?? + personDetails.rooms.first.buildingName ?? context.localizations.unknown, ), ), diff --git a/lib/personSearchComponent/model/person.dart b/lib/personSearchComponent/model/person.dart index 44d123e0..6801dc41 100644 --- a/lib/personSearchComponent/model/person.dart +++ b/lib/personSearchComponent/model/person.dart @@ -52,22 +52,9 @@ class Person extends Searchable { Map toJson() => _$PersonToJson(this); } -@JsonSerializable() -class PersonData { - @JsonKey(name: "rowset") - Persons? personAttribute; - - PersonData({required this.personAttribute}); - - factory PersonData.fromJson(Map json) => - _$PersonDataFromJson(json); - - Map toJson() => _$PersonDataToJson(this); -} - @JsonSerializable() class Persons { - @JsonKey(name: "row") + @JsonKey(name: "row", defaultValue: []) final List persons; Persons({required this.persons}); diff --git a/lib/personSearchComponent/model/person.g.dart b/lib/personSearchComponent/model/person.g.dart index 50ad832c..316ceea7 100644 --- a/lib/personSearchComponent/model/person.g.dart +++ b/lib/personSearchComponent/model/person.g.dart @@ -22,21 +22,11 @@ Map _$PersonToJson(Person instance) => { 'obfuscated_id': instance.obfuscatedID, }; -PersonData _$PersonDataFromJson(Map json) => PersonData( - personAttribute: json['rowset'] == null - ? null - : Persons.fromJson(json['rowset'] as Map), - ); - -Map _$PersonDataToJson(PersonData instance) => - { - 'rowset': instance.personAttribute, - }; - Persons _$PersonsFromJson(Map json) => Persons( - persons: (json['row'] as List) - .map((e) => Person.fromJson(e as Map)) - .toList(), + persons: (json['row'] as List?) + ?.map((e) => Person.fromJson(e as Map)) + .toList() ?? + [], ); Map _$PersonsToJson(Persons instance) => { diff --git a/lib/personSearchComponent/services/person_search_service.dart b/lib/personSearchComponent/services/person_search_service.dart index f79f5400..95c20439 100644 --- a/lib/personSearchComponent/services/person_search_service.dart +++ b/lib/personSearchComponent/services/person_search_service.dart @@ -12,13 +12,13 @@ class PersonSearchService { ) async { RESTClient restClient = getIt(); final response = await restClient - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServicePersonSearch(search: query)), - PersonData.fromJson, + Persons.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.personAttribute?.persons ?? []); + return (response.saved, response.data.persons); } } diff --git a/lib/placesComponent/views/cafeterias/cafeteria_view.dart b/lib/placesComponent/views/cafeterias/cafeteria_view.dart index 11dbc110..bf816708 100644 --- a/lib/placesComponent/views/cafeterias/cafeteria_view.dart +++ b/lib/placesComponent/views/cafeterias/cafeteria_view.dart @@ -210,7 +210,11 @@ class _CafeteriaViewState extends ConsumerState { if (snapshot.hasData) { if (snapshot.data!.isEmpty) { return Center( - child: Text(context.localizations.noMealPlanFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.mealPlan, + ), + ), ); } else { final menu = snapshot.data!; @@ -256,7 +260,11 @@ class _CafeteriaViewState extends ConsumerState { ), if (todayMeals.isEmpty) Center( - child: Text(context.localizations.noMealPlanFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.mealPlan, + ), + ), ), ], ); diff --git a/lib/placesComponent/views/campuses/campus_most_searched_view.dart b/lib/placesComponent/views/campuses/campus_most_searched_view.dart index 6136c55b..a5abef1a 100644 --- a/lib/placesComponent/views/campuses/campus_most_searched_view.dart +++ b/lib/placesComponent/views/campuses/campus_most_searched_view.dart @@ -30,7 +30,11 @@ class CampusMostSearchedView extends ConsumerWidget { return Padding( padding: EdgeInsets.all(context.padding), child: Center( - child: Text(context.localizations.noRoomsFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.rooms, + ), + ), ), ); } else { diff --git a/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart b/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart index 4b592956..1f79f808 100644 --- a/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart +++ b/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart @@ -109,7 +109,11 @@ class _CafeteriaWidgetViewState extends ConsumerState { child: SizedBox( height: 150, child: Center( - child: Text(context.localizations.noMealPlanFound), + child: Text( + context.localizations.noEntriesFound( + context.localizations.mealPlan, + ), + ), ), ), ); diff --git a/lib/profileComponent/model/profile.dart b/lib/profileComponent/model/profile.dart index 9ab79d6f..7d12b00d 100644 --- a/lib/profileComponent/model/profile.dart +++ b/lib/profileComponent/model/profile.dart @@ -62,19 +62,6 @@ class Profile { Map toJson() => _$ProfileToJson(this); } -@JsonSerializable() -class ProfileData { - @JsonKey(name: "rowset") - Profiles profilesAttribute; - - ProfileData({required this.profilesAttribute}); - - factory ProfileData.fromJson(Map json) => - _$ProfileDataFromJson(json); - - Map toJson() => _$ProfileDataToJson(this); -} - @JsonSerializable() class Profiles { @JsonKey(name: "row", fromJson: _profileFromJson) diff --git a/lib/profileComponent/model/profile.g.dart b/lib/profileComponent/model/profile.g.dart index 1942e498..66e3b5c3 100644 --- a/lib/profileComponent/model/profile.g.dart +++ b/lib/profileComponent/model/profile.g.dart @@ -26,16 +26,6 @@ Map _$ProfileToJson(Profile instance) => { 'kennung': instance.tumID, }; -ProfileData _$ProfileDataFromJson(Map json) => ProfileData( - profilesAttribute: - Profiles.fromJson(json['rowset'] as Map), - ); - -Map _$ProfileDataToJson(ProfileData instance) => - { - 'rowset': instance.profilesAttribute, - }; - Profiles _$ProfilesFromJson(Map json) => Profiles( profile: Profiles._profileFromJson(json['row']), ); diff --git a/lib/profileComponent/model/tuition.dart b/lib/profileComponent/model/tuition.dart index 57ceb85d..05e72d24 100644 --- a/lib/profileComponent/model/tuition.dart +++ b/lib/profileComponent/model/tuition.dart @@ -27,19 +27,6 @@ class Tuition { Map toJson() => _$TuitionToJson(this); } -@JsonSerializable() -class TuitionData { - @JsonKey(name: "rowset") - Tuitions profilesAttribute; - - TuitionData({required this.profilesAttribute}); - - factory TuitionData.fromJson(Map json) => - _$TuitionDataFromJson(json); - - Map toJson() => _$TuitionDataToJson(this); -} - @JsonSerializable() class Tuitions { @JsonKey(name: "row", fromJson: _tuitionFromJson) diff --git a/lib/profileComponent/model/tuition.g.dart b/lib/profileComponent/model/tuition.g.dart index 5c85629a..813a6b73 100644 --- a/lib/profileComponent/model/tuition.g.dart +++ b/lib/profileComponent/model/tuition.g.dart @@ -20,16 +20,6 @@ Map _$TuitionToJson(Tuition instance) => { 'semester_id': instance.semesterID, }; -TuitionData _$TuitionDataFromJson(Map json) => TuitionData( - profilesAttribute: - Tuitions.fromJson(json['rowset'] as Map), - ); - -Map _$TuitionDataToJson(TuitionData instance) => - { - 'rowset': instance.profilesAttribute, - }; - Tuitions _$TuitionsFromJson(Map json) => Tuitions( tuition: Tuitions._tuitionFromJson(json['row']), ); diff --git a/lib/profileComponent/services/profile_service.dart b/lib/profileComponent/services/profile_service.dart index e46da2fe..1558d4a0 100644 --- a/lib/profileComponent/services/profile_service.dart +++ b/lib/profileComponent/services/profile_service.dart @@ -10,14 +10,14 @@ class ProfileService { static Future<(DateTime?, Profile)> fetchProfile(bool forcedRefresh) async { RESTClient restClient = getIt(); final response = await restClient - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServiceIdentify()), - ProfileData.fromJson, + Profiles.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.profilesAttribute.profile); + return (response.saved, response.data.profile); } static Future<(DateTime?, Tuition?)> fetchTuition( @@ -27,13 +27,13 @@ class ProfileService { ) async { RESTClient restClient = getIt(); final response = await restClient - .getWithException( + .getWithException( TumOnlineApi(TumOnlineServiceTuitionStatus()), - TuitionData.fromJson, + Tuitions.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.profilesAttribute.tuition); + return (response.saved, response.data.tuition); } } diff --git a/lib/searchComponent/viewModels/searchableViewModels/news_search_viewmodel.dart b/lib/searchComponent/viewModels/searchableViewModels/news_search_viewmodel.dart index 8b032920..2388a0a0 100644 --- a/lib/searchComponent/viewModels/searchableViewModels/news_search_viewmodel.dart +++ b/lib/searchComponent/viewModels/searchableViewModels/news_search_viewmodel.dart @@ -21,6 +21,15 @@ class NewsSearchViewModel implements SearchViewModel { if (newsData.isEmpty) { return NewsService.fetchNews(forcedRefresh).then( (value) { + Set seenTitles = {}; + value.$2.retainWhere((element) { + if (seenTitles.contains(element.title)) { + return false; + } else { + seenTitles.add(element.title); + return true; + } + }); newsData = value.$2.map((e) => NewsSearch(e)).toList(); _search(query); }, diff --git a/lib/searchComponent/views/appWideSearch/resultViews/news_search_result_view.dart b/lib/searchComponent/views/appWideSearch/resultViews/news_search_result_view.dart index 7229c2f7..15b6a495 100644 --- a/lib/searchComponent/views/appWideSearch/resultViews/news_search_result_view.dart +++ b/lib/searchComponent/views/appWideSearch/resultViews/news_search_result_view.dart @@ -23,18 +23,21 @@ class NewsSearchResultView extends ConsumerWidget { return ListTile( leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(10)), - child: CachedNetworkImage( - height: 60, - imageUrl: newsSearch.news.imageUrl, - placeholder: (context, string) => Image.asset( - "assets/images/placeholders/news_placeholder.png", - fit: BoxFit.fill, - ), - errorWidget: (context, url, error) => Image.asset( - "assets/images/placeholders/news_placeholder.png", - fit: BoxFit.fill, + child: AspectRatio( + aspectRatio: 2, + child: CachedNetworkImage( + height: 60, + imageUrl: newsSearch.news.imageUrl, + placeholder: (context, string) => Image.asset( + "assets/images/placeholders/news_placeholder.png", + fit: BoxFit.cover, + ), + errorWidget: (context, url, error) => Image.asset( + "assets/images/placeholders/news_placeholder.png", + fit: BoxFit.cover, + ), + fit: BoxFit.cover, ), - fit: BoxFit.fitHeight, ), ), title: Text(newsSearch.news.title), diff --git a/lib/searchComponent/views/appWideSearch/search_result_card_view.dart b/lib/searchComponent/views/appWideSearch/search_result_card_view.dart index 89cc7929..09bab4aa 100644 --- a/lib/searchComponent/views/appWideSearch/search_result_card_view.dart +++ b/lib/searchComponent/views/appWideSearch/search_result_card_view.dart @@ -40,7 +40,7 @@ class SearchResultCardView, S extends Searchable> padding: EdgeInsets.all(context.padding), child: Center( child: Text( - context.localizations.noEntriesFoundSearch( + context.localizations.noEntriesFound( SearchCategoryExtension.localizedEnumTitle( searchCategory, context, diff --git a/lib/studentCardComponent/model/student_card.dart b/lib/studentCardComponent/model/student_card.dart index 213bac37..a20d4cd0 100644 --- a/lib/studentCardComponent/model/student_card.dart +++ b/lib/studentCardComponent/model/student_card.dart @@ -29,8 +29,8 @@ class StudentCard { @JsonKey(name: "gueltig_bis") final DateTime validUntil; - @JsonKey(name: "studien") - final Studies? studies; + @JsonKey(name: "studium", defaultValue: []) + final List studies; @JsonKey(name: "foto") final String image; @@ -45,7 +45,7 @@ class StudentCard { required this.semester, required this.validFrom, required this.validUntil, - this.studies, + required this.studies, required this.image, }); @@ -55,28 +55,6 @@ class StudentCard { Map toJson() => _$StudentCardToJson(this); } -@JsonSerializable() -class Studies { - @JsonKey(name: "studium", fromJson: alwaysAsList) - final List study; - - Studies({required this.study}); - - factory Studies.fromJson(Map json) => - _$StudiesFromJson(json); - - Map toJson() => _$StudiesToJson(this); -} - -// TODO: try to make this generic? -List alwaysAsList(dynamic data) { - if (data is Map) { - return [Subject.fromJson(data)]; - } else { - return (data as List).map((e) => Subject.fromJson(e)).toList(); - } -} - @JsonSerializable() class Subject { final String name; @@ -97,26 +75,13 @@ class Subject { @JsonSerializable() class StudentCards { - @JsonKey(name: "card") - final StudentCard studentCard; + @JsonKey(name: "card", defaultValue: []) + final List studentCards; - StudentCards({required this.studentCard}); + StudentCards({required this.studentCards}); factory StudentCards.fromJson(Map json) => _$StudentCardsFromJson(json); Map toJson() => _$StudentCardsToJson(this); } - -@JsonSerializable() -class StudentCardData { - @JsonKey(name: "cards") - final StudentCards studentCards; - - StudentCardData({required this.studentCards}); - - factory StudentCardData.fromJson(Map json) => - _$StudentCardDataFromJson(json); - - Map toJson() => _$StudentCardDataToJson(this); -} diff --git a/lib/studentCardComponent/model/student_card.g.dart b/lib/studentCardComponent/model/student_card.g.dart index bb46cd81..31df3086 100644 --- a/lib/studentCardComponent/model/student_card.g.dart +++ b/lib/studentCardComponent/model/student_card.g.dart @@ -16,9 +16,10 @@ StudentCard _$StudentCardFromJson(Map json) => StudentCard( semester: json['semester'] as String, validFrom: DateTime.parse(json['gueltig_ab'] as String), validUntil: DateTime.parse(json['gueltig_bis'] as String), - studies: json['studien'] == null - ? null - : Studies.fromJson(json['studien'] as Map), + studies: (json['studium'] as List?) + ?.map((e) => Subject.fromJson(e as Map)) + .toList() ?? + [], image: json['foto'] as String, ); @@ -33,18 +34,10 @@ Map _$StudentCardToJson(StudentCard instance) => 'semester': instance.semester, 'gueltig_ab': instance.validFrom.toIso8601String(), 'gueltig_bis': instance.validUntil.toIso8601String(), - 'studien': instance.studies, + 'studium': instance.studies, 'foto': instance.image, }; -Studies _$StudiesFromJson(Map json) => Studies( - study: alwaysAsList(json['studium']), - ); - -Map _$StudiesToJson(Studies instance) => { - 'studium': instance.study, - }; - Subject _$SubjectFromJson(Map json) => Subject( name: json['name'] as String, degree: json['abschluss'] as String, @@ -58,21 +51,13 @@ Map _$SubjectToJson(Subject instance) => { }; StudentCards _$StudentCardsFromJson(Map json) => StudentCards( - studentCard: StudentCard.fromJson(json['card'] as Map), + studentCards: (json['card'] as List?) + ?.map((e) => StudentCard.fromJson(e as Map)) + .toList() ?? + [], ); Map _$StudentCardsToJson(StudentCards instance) => { - 'card': instance.studentCard, - }; - -StudentCardData _$StudentCardDataFromJson(Map json) => - StudentCardData( - studentCards: - StudentCards.fromJson(json['cards'] as Map), - ); - -Map _$StudentCardDataToJson(StudentCardData instance) => - { - 'cards': instance.studentCards, + 'card': instance.studentCards, }; diff --git a/lib/studentCardComponent/services/student_card_service.dart b/lib/studentCardComponent/services/student_card_service.dart index 7f35f1dc..d789a69e 100644 --- a/lib/studentCardComponent/services/student_card_service.dart +++ b/lib/studentCardComponent/services/student_card_service.dart @@ -8,20 +8,20 @@ import 'package:campus_flutter/main.dart'; import 'package:campus_flutter/studentCardComponent/model/student_card.dart'; class StudentCardService { - static Future<(DateTime?, StudentCard)> fetchStudentCard( + static Future<(DateTime?, List)> fetchStudentCard( bool forcedRefresh, ) async { try { RESTClient restClient = getIt(); - final response = await restClient.getWithException( + final response = await restClient + .getWithException( TumOnlineApi(TumOnlineServiceTumCard()), - StudentCardData.fromJson, + StudentCards.fromJson, TumOnlineApiException.fromJson, forcedRefresh, ); - return (response.saved, response.data.studentCards.studentCard); + return (response.saved, response.data.studentCards); } catch (e) { log(e.toString()); rethrow; diff --git a/lib/studentCardComponent/viewModel/student_card_viewmodel.dart b/lib/studentCardComponent/viewModel/student_card_viewmodel.dart index 10a100ef..225e93e6 100644 --- a/lib/studentCardComponent/viewModel/student_card_viewmodel.dart +++ b/lib/studentCardComponent/viewModel/student_card_viewmodel.dart @@ -6,7 +6,8 @@ import 'package:rxdart/rxdart.dart'; final studentCardViewModel = Provider((ref) => StudentCardViewModel()); class StudentCardViewModel { - BehaviorSubject studentCard = BehaviorSubject.seeded(null); + BehaviorSubject?> studentCard = + BehaviorSubject.seeded(null); BehaviorSubject lastFetched = BehaviorSubject.seeded(null); Future fetch(bool forcedRefresh) async { diff --git a/lib/studentCardComponent/views/information_view.dart b/lib/studentCardComponent/views/information_view.dart index ce3f41a9..633c0d1d 100644 --- a/lib/studentCardComponent/views/information_view.dart +++ b/lib/studentCardComponent/views/information_view.dart @@ -49,8 +49,7 @@ class InformationView extends StatelessWidget { ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 5.0)), - if (studentCard.studies != null) - ..._currentSubjects(context), + ..._currentSubjects(context), ], ), ), @@ -139,11 +138,11 @@ class InformationView extends StatelessWidget { } List _currentSubjects(BuildContext context) { - final studies = studentCard.studies?.study; return [ - for (var studyProgram - in studies?.sublist(0, studies.length >= 2 ? 2 : studies.length) ?? - []) + for (var studyProgram in studentCard.studies.sublist( + 0, + studentCard.studies.length >= 2 ? 2 : studentCard.studies.length, + )) Text( "${studyProgram.name} (${StringParser.degreeShort(studyProgram.degree, context)})", ), diff --git a/lib/studentCardComponent/views/student_card_view.dart b/lib/studentCardComponent/views/student_card_view.dart index 06c8b578..7376dd99 100644 --- a/lib/studentCardComponent/views/student_card_view.dart +++ b/lib/studentCardComponent/views/student_card_view.dart @@ -19,16 +19,27 @@ class StudentCardView extends ConsumerWidget { stream: ref.watch(studentCardViewModel).studentCard, builder: (context, snapshot) { if (snapshot.hasData) { - var data = snapshot.data!; - final lastFetched = ref.read(studentCardViewModel).lastFetched.value; - return Column( - children: [ - if (lastFetched != null) LastUpdatedText(lastFetched), - _warningCard(context), - InformationView(studentCard: data), - BarCodeView(libraryID: data.libraryID), - ], - ); + if (snapshot.data!.isNotEmpty) { + var data = snapshot.data!.first; + final lastFetched = + ref.read(studentCardViewModel).lastFetched.value; + return Column( + children: [ + if (lastFetched != null) LastUpdatedText(lastFetched), + _warningCard(context), + InformationView(studentCard: data), + BarCodeView(libraryID: data.libraryID), + ], + ); + } else { + return Center( + child: Text( + context.localizations.noEntriesFound( + "StudentCard", + ), + ), + ); + } } else if (snapshot.hasError) { return ErrorHandlingRouter( error: snapshot.error!, diff --git a/pubspec.lock b/pubspec.lock index 02947a42..ea306067 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: built_value - sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 + sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 url: "https://pub.dev" source: hosted - version: "8.8.1" + version: "8.9.0" cached_network_image: dependency: "direct main" description: @@ -513,18 +513,18 @@ packages: dependency: transitive description: name: geolocator_android - sha256: "741579fa6c9e412984d2bdb2fbaa54e3c3f7587c60aeacfe6e058358a11f40f8" + sha256: "30ff8fa384ab6d35965aecc15dfc980e5ebc5f823352c1adfc87dc3d000e8e24" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.5.0" geolocator_apple: dependency: transitive description: name: geolocator_apple - sha256: "16cdb6b8d3685d3e07a7e54e88e97106f4fae9e496191f33e8bb389cce14f198" + sha256: "79babf44b692ec5e789d322dc736ef71586056e8e6828f747c9e005456b248bf" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" geolocator_platform_interface: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: d0b88dc35a7f97fd91fec0cf8f165abd97a57977968d8fc02ba0bc92e14ba07e + sha256: e6017ce7fdeaf218dc51a100344d8cb70134b80e28b760f8bb23c242437bafd7 url: "https://pub.dev" source: hosted - version: "7.6.6" + version: "7.6.7" glob: dependency: transitive description: @@ -594,10 +594,10 @@ packages: description: path: "packages/google_maps_flutter/google_maps_flutter_ios" ref: main - resolved-ref: ca9d019de61f4e20acd3004a1a49dc9db9bb4f58 + resolved-ref: "89d134da61ae7602820ed8386dd8abc19644d253" url: "https://github.com/jakobkoerber/packages.git" source: git - version: "2.4.0" + version: "2.4.1" google_maps_flutter_platform_interface: dependency: transitive description: @@ -659,10 +659,10 @@ packages: dependency: transitive description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" http2: dependency: transitive description: @@ -811,10 +811,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" node_preamble: dependency: transitive description: @@ -923,50 +923,50 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" + sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6" url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "11.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" + sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb" url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "12.0.3" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" + sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830 url: "https://pub.dev" source: hosted - version: "9.2.0" + version: "9.3.0" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" url: "https://pub.dev" source: hosted - version: "0.1.0+2" + version: "0.1.1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 + sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" petitparser: dependency: transitive description: @@ -995,10 +995,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: @@ -1216,18 +1216,18 @@ packages: dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: c2c32eb0c74021d987336522acc3b6bf0082fbd0c540c36a9cf4ddb8ba891ddc url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" url: "https://pub.dev" source: hosted - version: "2.5.0+2" + version: "2.5.3" stack_trace: dependency: transitive description: @@ -1272,34 +1272,34 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_calendar - sha256: "12c9750dc5b728cb2eee251533a7bcc0b66171f9c7d2c5a3ed665494534148f0" + sha256: ac959ddb9d2d3d153bcacb061167fe5fc559d213598f4481058f641938b73151 url: "https://pub.dev" source: hosted - version: "24.1.45" + version: "24.1.47" syncfusion_flutter_charts: dependency: "direct main" description: name: syncfusion_flutter_charts - sha256: "02c11d461f4177c60a4cdd501594bb6678f46bee8754f678c7e41c2109f935e7" + sha256: "224dfecbe5beb6e6e2a7eb82abe6510e386861c32e9b669ff256d156be788dd8" url: "https://pub.dev" source: hosted - version: "24.1.45+2" + version: "24.1.47+2" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: "4afb43254d436e1d41a2ee2761908650ce3ad3453f5bebd283dfe3185d5026a2" + sha256: ed8527189f874b9d07f78f2f4306739ce003e8baccfdd7453c0098beef16428f url: "https://pub.dev" source: hosted - version: "24.1.45" + version: "24.1.47" syncfusion_flutter_datepicker: dependency: "direct main" description: name: syncfusion_flutter_datepicker - sha256: "22e9845e3389a7554f5ccf683032ba4ce4cf9a19f656f5e811a075df31f78c9e" + sha256: "374ab719b7591af6bf81dabe2db60fa85968bbf4d29522fe61979344454dcc7a" url: "https://pub.dev" source: hosted - version: "24.1.45" + version: "24.1.47" synchronized: dependency: transitive description: @@ -1384,10 +1384,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: d25bb0ca00432a5e1ee40e69c36c85863addf7cc45e433769d61bed3fe81fd96 + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c url: "https://pub.dev" source: hosted - version: "6.2.3" + version: "6.2.4" url_launcher_android: dependency: transitive description: @@ -1601,7 +1601,7 @@ packages: description: path: "." ref: master - resolved-ref: "1c1b397652b5806bf2e04333139bb9d3282fe630" + resolved-ref: "69b906d726277fb1dbbbdde0bf5adc31b2190a6e" url: "https://github.com/jakobkoerber/xml2json.git" source: git version: "6.2.2"