diff --git a/android/fastlane/metadata/android/en-GB/changelogs/10041.txt b/android/fastlane/metadata/android/en-GB/changelogs/10041.txt new file mode 100644 index 000000000..776b933cb --- /dev/null +++ b/android/fastlane/metadata/android/en-GB/changelogs/10041.txt @@ -0,0 +1,2 @@ +Added: + -Users can now choose from setting if they want to see in the week indicator,from the timetable,the academic week number. \ No newline at end of file diff --git a/android/fastlane/metadata/android/en-US/changelogs/10041.txt b/android/fastlane/metadata/android/en-US/changelogs/10041.txt new file mode 100644 index 000000000..776b933cb --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/10041.txt @@ -0,0 +1,2 @@ +Added: + -Users can now choose from setting if they want to see in the week indicator,from the timetable,the academic week number. \ No newline at end of file diff --git a/android/fastlane/metadata/android/ro/changelogs/10041.txt b/android/fastlane/metadata/android/ro/changelogs/10041.txt new file mode 100644 index 000000000..2b271da3f --- /dev/null +++ b/android/fastlane/metadata/android/ro/changelogs/10041.txt @@ -0,0 +1,2 @@ +Adăugat: + -Utilizatorii pot alege din setări dacă vor să vadă în timetable numărul săptămânii din calendarul academic. \ No newline at end of file diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 180648c6f..25b01740e 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -288,6 +288,8 @@ class MessageLookup extends MessageLookupByLibrary { "settingsFeedbackForm" : MessageLookupByLibrary.simpleMessage("Contact us"), "settingsItemAdmin" : MessageLookupByLibrary.simpleMessage("Admin Panel"), "settingsItemDarkMode" : MessageLookupByLibrary.simpleMessage("Dark Mode"), + "settingsAcademicWeekNumber":MessageLookupByLibrary.simpleMessage("Academic week number"), + "settingsAcademicWeekNumberSubtitle":MessageLookupByLibrary.simpleMessage("Show number of week in the semester instead of the calendar year"), "settingsItemEditingPermissions" : MessageLookupByLibrary.simpleMessage("Your editing permissions"), "settingsItemLanguage" : MessageLookupByLibrary.simpleMessage("Language"), "settingsItemLanguageAuto" : MessageLookupByLibrary.simpleMessage("Auto"), diff --git a/lib/generated/intl/messages_ro.dart b/lib/generated/intl/messages_ro.dart index 5a9b4ccdf..f30e17471 100644 --- a/lib/generated/intl/messages_ro.dart +++ b/lib/generated/intl/messages_ro.dart @@ -288,6 +288,8 @@ class MessageLookup extends MessageLookupByLibrary { "settingsFeedbackForm" : MessageLookupByLibrary.simpleMessage("Contactează-ne"), "settingsItemAdmin" : MessageLookupByLibrary.simpleMessage("Panoul Administratorului"), "settingsItemDarkMode" : MessageLookupByLibrary.simpleMessage("Mod Întunecat"), + "settingsAcademicWeekNumber":MessageLookupByLibrary.simpleMessage("Săptămâni academice"), + "settingsAcademicWeekNumberSubtitle":MessageLookupByLibrary.simpleMessage("Afișează săptămână din semestru în loc de cea din an"), "settingsItemEditingPermissions" : MessageLookupByLibrary.simpleMessage("Permisiunile tale de editare"), "settingsItemLanguage" : MessageLookupByLibrary.simpleMessage("Limbă"), "settingsItemLanguageAuto" : MessageLookupByLibrary.simpleMessage("Auto"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 940307117..21ef8d5dd 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2195,6 +2195,25 @@ class S { args: [], ); } + /// `Academic week number` + String get settingsAcademicWeekNumber { + return Intl.message( + 'Academic week number', + name: 'settingsAcademicWeekNumber', + desc: '', + args: [], + ); + } + /// 'Show number of week in the semester instead of the calendar year' + String get settingsAcademicWeekNumberSubtitle { + return Intl.message( + 'Show number of week in the semester instead of the calendar year', + name: 'settingsAcademicWeekNumberSubtitle', + desc: '', + args: [], + ); + } + /// `Admin Panel` String get settingsItemAdmin { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 03c90f41f..dbf95e5ca 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -215,7 +215,9 @@ "settingsTitlePersonalization": "Personalization", "settingsItemDarkMode": "Dark Mode", + "settingsAcademicWeekNumber":"Academic week number", "settingsTitleLocalization": "Localization", + "settingsAcademicWeekNumberSubtitle":"Show number of week in the semester instead of the calendar year" "settingsTitleDataControl": "Data control", "settingsItemEditingPermissions": "Your editing permissions", "settingsPermissionsNone": "No special permissions", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 69224836d..e1451ada0 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -215,9 +215,11 @@ "settingsTitlePersonalization": "Personalizare", "settingsItemDarkMode": "Mod Întunecat", + "settingsAcademicWeekNumber":"Săptămâni academice", "settingsTitleLocalization": "Localizare", "settingsTitleDataControl": "Control date", "settingsItemEditingPermissions": "Permisiunile tale de editare", + "settingsAcademicWeekNumberSubtitle":"Afișează săptămână din semestru în loc de cea din an" "settingsPermissionsNone": "Fără permisiuni speciale", "settingsPermissionsAdd": "Permisiune pentru adăugare de date publice", "settingsPermissionsEdit": "Permisiune pentru editare de date publice", diff --git a/lib/pages/settings/view/settings_page.dart b/lib/pages/settings/view/settings_page.dart index 0d5a0cdd1..025ffd4f2 100644 --- a/lib/pages/settings/view/settings_page.dart +++ b/lib/pages/settings/view/settings_page.dart @@ -5,6 +5,7 @@ import 'package:acs_upb_mobile/generated/l10n.dart'; import 'package:acs_upb_mobile/navigation/routes.dart'; import 'package:acs_upb_mobile/pages/settings/service/request_provider.dart'; import 'package:acs_upb_mobile/pages/timetable/service/uni_event_provider.dart'; +import 'package:acs_upb_mobile/pages/timetable/view/lead_header.dart'; import 'package:acs_upb_mobile/resources/locale_provider.dart'; import 'package:acs_upb_mobile/resources/utils.dart'; import 'package:acs_upb_mobile/widgets/icon_text.dart'; @@ -149,6 +150,22 @@ class _SettingsPageState extends State { subtitle: Text(S.current.infoExportToGoogleCalendar), ), ), + Column( + children: [ + SwitchPreference( + S.current.settingsAcademicWeekNumber, + 'Academic week number', + onEnable: () { + LeadHeader.academicWeekNumber = true; + }, + onDisable: () { + LeadHeader.academicWeekNumber = false; + }, + defaultVal: false, + desc: S.current.settingsAcademicWeekNumberSubtitle, + ), + ], + ), PreferenceTitle(S.current.labelFeedback), ListTile( key: const ValueKey('feedback_and_issues'), diff --git a/lib/pages/timetable/model/academic_calendar.dart b/lib/pages/timetable/model/academic_calendar.dart index a39cc1f5d..565d33486 100644 --- a/lib/pages/timetable/model/academic_calendar.dart +++ b/lib/pages/timetable/model/academic_calendar.dart @@ -1,4 +1,5 @@ import 'package:acs_upb_mobile/pages/timetable/model/events/all_day_event.dart'; +import 'package:acs_upb_mobile/pages/timetable/view/lead_header.dart'; import 'package:acs_upb_mobile/resources/utils.dart'; import 'package:flutter/foundation.dart'; import 'package:time_machine/time_machine.dart'; @@ -77,4 +78,45 @@ class AcademicCalendar { return weeksByYear.values.expand((e) => e).toSet(); } + + int semesterForDate(LocalDate date) { + for (final semester in semesters) { + if (date._isBeforeOrDuring(semester)) { + return 1 + int.tryParse(semester.id[semester.id.length - 1]); + } + } + return -1; + } + + String getWeekNumber(LocalDate date) { + final week = ((date.dayOfYear - date.dayOfWeek.value + 10) / 7).floor(); + if (LeadHeader.academicWeekNumber == false) return week.toString(); + final int finalWeekOfFirstSem = ((semesters[0].endDate.dayOfYear - + semesters[0].endDate.dayOfWeek.value + + 10) / + 7) + .floor(); + if (!nonHolidayWeeks.contains(week)) { + return 'H'; + } else { + if (semesterForDate(date) == 1) { + return (nonHolidayWeeks.toList().indexOf(week) + 1).toString(); + } else { + return ((nonHolidayWeeks.toList().indexOf(week) + 1) - + (nonHolidayWeeks.toList().indexOf(finalWeekOfFirstSem) + 1)) + .toString(); + } + } + } +} + +extension LocalDateComparisons on LocalDate { + bool _isDuring(AllDayUniEvent semester) { + return DateInterval(semester.startDate, semester.endDate).contains(this); + } + + bool _isBeforeOrDuring(AllDayUniEvent semester) { + if (compareTo(semester.startDate) < 0) return true; + return _isDuring(semester); + } } diff --git a/lib/pages/timetable/view/events/add_event_view.dart b/lib/pages/timetable/view/events/add_event_view.dart index a7608f0ac..c01286955 100644 --- a/lib/pages/timetable/view/events/add_event_view.dart +++ b/lib/pages/timetable/view/events/add_event_view.dart @@ -109,22 +109,18 @@ class _AddEventViewState extends State { ? 2 : 1; } else { - bool foundSemester = false; - for (final calendar in calendars.entries) { - for (final semester in calendar.value.semesters) { - final LocalDate date = - widget.initialEvent.start.calendarDate ?? LocalDate.today(); - if (date.isBeforeOrDuring(semester)) { - // semester.id is represented as "semesterN", where "semester0" is the first semester - selectedSemester = - 1 + int.tryParse(semester.id[semester.id.length - 1]); - selectedCalendar = calendar.key; - foundSemester = true; - break; - } + const bool foundSemester = false; + final LocalDate date = + widget.initialEvent.start.calendarDate ?? LocalDate.today(); + calendars.forEach((key, value) { + final semester = value.semesterForDate(date); + if (semester != -1) { + selectedCalendar = key; + selectedSemester = semester; + return; } - if (foundSemester) break; - } + }); + if (!foundSemester) { selectedCalendar = calendars.entries.last.value.id; selectedSemester = 2; diff --git a/lib/pages/timetable/view/lead_header.dart b/lib/pages/timetable/view/lead_header.dart new file mode 100644 index 000000000..ab1a961e0 --- /dev/null +++ b/lib/pages/timetable/view/lead_header.dart @@ -0,0 +1,92 @@ +import 'package:acs_upb_mobile/authentication/model/user.dart'; +import 'package:acs_upb_mobile/authentication/service/auth_provider.dart'; +import 'package:acs_upb_mobile/pages/classes/model/class.dart'; +import 'package:acs_upb_mobile/pages/classes/service/class_provider.dart'; +import 'package:acs_upb_mobile/pages/people/model/person.dart'; +import 'package:acs_upb_mobile/pages/people/service/person_provider.dart'; +import 'package:acs_upb_mobile/pages/timetable/model/academic_calendar.dart'; +import 'package:acs_upb_mobile/pages/timetable/service/uni_event_provider.dart'; +import 'package:black_hole_flutter/black_hole_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:time_machine/time_machine.dart'; + +import 'package:timetable/src/theme.dart'; + +// ignore: implementation_imports +import 'package:provider/provider.dart'; + +class LeadHeader extends StatefulWidget { + const LeadHeader(this.date, {Key key}) : super(key: key); + final LocalDate date; + static var academicWeekNumber = false; + + @override + _LeadHeaderState createState() => _LeadHeaderState(); +} + +class _LeadHeaderState extends State { + List classHeaders = []; + List classTeachers = []; + User user; + AcademicCalendar calendar; + Map calendars = {}; + int academicWeek; + + @override + Widget build(BuildContext context) { + final theme = context.theme; + final timetableTheme = context.timetableTheme; + final defaultBackgroundColor = theme.contrastColor.withOpacity(0.12); + final textStyle = timetableTheme?.weekIndicatorTextStyle ?? + TextStyle( + color: defaultBackgroundColor + .alphaBlendOn(theme.scaffoldBackgroundColor) + .mediumEmphasisOnColor, + fontSize: 14); + final filteredCalendars = calendars?.values + ?.where((calendar) => calendar.semesterForDate(widget.date) != -1); + final calendar = filteredCalendars.isEmpty ? null : filteredCalendars.first; + return Container( + child: Center( + child: Container( + width: 27, + height: 20, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + color: defaultBackgroundColor, + border: Border.all(color: defaultBackgroundColor, width: 1)), + child: calendar == null + ? Container() + : Text( + calendar.getWeekNumber(widget.date), + style: textStyle, + textAlign: TextAlign.center, + ), + ), + ), + ); + } + + @override + void initState() { + if (!mounted) { + return; + } + super.initState(); + user = + Provider.of(context, listen: false).currentUserFromCache; + Provider.of(context, listen: false) + .fetchClassHeaders(uid: user.uid) + .then((headers) => setState(() => classHeaders = headers)); + Provider.of(context, listen: false) + .fetchPeople() + .then((teachers) => setState(() => classTeachers = teachers)); + Provider.of(context, listen: false) + .fetchCalendars() + .then((calendars) { + setState(() { + this.calendars = calendars; + }); + }); + } +} diff --git a/lib/pages/timetable/view/timetable_page.dart b/lib/pages/timetable/view/timetable_page.dart index eee844243..bb5bb09a1 100644 --- a/lib/pages/timetable/view/timetable_page.dart +++ b/lib/pages/timetable/view/timetable_page.dart @@ -22,6 +22,7 @@ import 'package:provider/provider.dart'; import 'package:recase/recase.dart'; import 'package:time_machine/time_machine.dart'; import 'package:timetable/timetable.dart'; +import 'lead_header.dart'; class TimetablePage extends StatefulWidget { const TimetablePage({Key key}) : super(key: key); @@ -98,6 +99,8 @@ class _TimetablePageState extends State { child: Stack( children: [ Timetable( + leadingHeaderBuilder: (_, date) => + LeadHeader(date ?? LocalDate.today()), controller: _controller, dateHeaderBuilder: (_, date) => DateHeader(date), eventBuilder: (event) => UniEventWidget(event), diff --git a/pubspec.yaml b/pubspec.yaml index 1698f9d7a..ae89bb7db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ description: A mobile application for students at ACS UPB. # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # # ACS UPB Mobile uses semantic versioning. You can read more in the CONTRIBUTING.md file. -version: 1.3.1+41 +version: 1.3.1+42 environment: sdk: ">=2.7.0 <3.0.0" diff --git a/test/integration_test.dart b/test/integration_test.dart index 91f6b33af..d7b89106f 100644 --- a/test/integration_test.dart +++ b/test/integration_test.dart @@ -47,6 +47,7 @@ import 'package:acs_upb_mobile/pages/timetable/model/events/uni_event.dart'; import 'package:acs_upb_mobile/pages/timetable/service/uni_event_provider.dart'; import 'package:acs_upb_mobile/pages/timetable/view/events/add_event_view.dart'; import 'package:acs_upb_mobile/pages/timetable/view/events/event_view.dart'; +import 'package:acs_upb_mobile/pages/timetable/view/lead_header.dart'; import 'package:acs_upb_mobile/pages/timetable/view/timetable_page.dart'; import 'package:acs_upb_mobile/resources/locale_provider.dart'; import 'package:acs_upb_mobile/resources/remote_config.dart'; @@ -64,7 +65,6 @@ import 'package:provider/provider.dart'; import 'package:rrule/rrule.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:time_machine/time_machine.dart' hide Offset; -import 'package:timetable/src/header/week_indicator.dart'; import 'firebase_mock.dart'; import 'test_utils.dart'; @@ -548,7 +548,7 @@ Future main() async { ), ]; final calendar = AcademicCalendar( - id: '2020', + id: '2021', semesters: [ AllDayUniEvent( start: weekStart, @@ -564,7 +564,7 @@ Future main() async { holidays: holidays, ); when(mockEventProvider.fetchCalendars()) - .thenAnswer((_) => Future.value({'2020': calendar})); + .thenAnswer((_) => Future.value({'2021': calendar})); when(mockEventProvider.getUpcomingEvents(LocalDate.today())) .thenAnswer((_) => Future.value([])); when(mockEventProvider.getUpcomingEvents(LocalDate.today(), @@ -915,15 +915,12 @@ Future main() async { // Scroll to previous week await tester.drag(find.text('Tue'), Offset(size.width - 30, 0)); - await tester.pumpAndSettle(); // Expect previous week - final previousWeek = WeekYearRules.iso - .getWeekOfWeekYear(LocalDate.today().subtractWeeks(1)); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == previousWeek.toString()), + + await tester.pumpAndSettle(); + + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); expect(find.text('Holiday'), findsNothing); @@ -945,12 +942,7 @@ Future main() async { await tester.pumpAndSettle(); // Expect current week - final currentWeek = - WeekYearRules.iso.getWeekOfWeekYear(LocalDate.today()); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == currentWeek.toString()), + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); expect(find.text('Holiday'), findsNothing); @@ -972,12 +964,8 @@ Future main() async { await tester.pumpAndSettle(); // Expect next week - final nextWeek = WeekYearRules.iso - .getWeekOfWeekYear(LocalDate.today().addWeeks(1)); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == nextWeek.toString()), + + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); expect(find.text('Holiday'), findsOneWidget); @@ -999,12 +987,8 @@ Future main() async { await tester.pumpAndSettle(); // Expect next week - final nextNextWeek = WeekYearRules.iso - .getWeekOfWeekYear(LocalDate.today().addWeeks(2)); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == nextNextWeek.toString()), + + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); expect(find.text('Holiday'), findsNothing); @@ -1026,12 +1010,8 @@ Future main() async { await tester.pumpAndSettle(); // Expect next week - final nextNextNextWeek = WeekYearRules.iso - .getWeekOfWeekYear(LocalDate.today().addWeeks(3)); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == nextNextNextWeek.toString()), + + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); expect(find.text('Holiday'), findsNothing); @@ -1053,10 +1033,7 @@ Future main() async { await tester.pumpAndSettle(); // Expect current week - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == currentWeek.toString()), + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); }); } @@ -1076,12 +1053,7 @@ Future main() async { await tester.pumpAndSettle(); // Expect current week - final currentWeek = - WeekYearRules.iso.getWeekOfWeekYear(LocalDate.today()); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == currentWeek.toString()), + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); // Scroll to next week @@ -1089,12 +1061,8 @@ Future main() async { await tester.pumpAndSettle(); // Expect next week - final nextWeek = WeekYearRules.iso - .getWeekOfWeekYear(LocalDate.today().addWeeks(1)); - expect( - find.byWidgetPredicate((widget) => - widget is WeekIndicator && - widget.week.toString() == nextWeek.toString()), + + expect(find.byWidgetPredicate((widget) => widget is LeadHeader), findsOneWidget); // Open holiday event