From 7a3744a70acbbf71b14ca3400fe9bad5b74bf82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 22 Feb 2023 00:48:50 +0100 Subject: [PATCH] Change timetable view from Dayview to Weekview Added current day and time hint --- .../getTimetable/getTimetableCache.dart | 11 +- lib/data/timetable/timetableProps.dart | 54 ++++-- lib/screen/pages/timetable/dayListView.dart | 139 ------------- lib/screen/pages/timetable/timetable.dart | 8 +- lib/screen/pages/timetable/weekView.dart | 183 ++++++++++++++++++ 5 files changed, 230 insertions(+), 165 deletions(-) delete mode 100644 lib/screen/pages/timetable/dayListView.dart create mode 100644 lib/screen/pages/timetable/weekView.dart diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart b/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart index 8e2d903..d2cb305 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart @@ -8,10 +8,11 @@ import 'getTimetableParams.dart'; import 'getTimetableResponse.dart'; class GetTimetableCache extends RequestCache { - int day; + int startdate; + int enddate; - GetTimetableCache({required onUpdate, required this.day}) : super(RequestCache.cacheMinute, onUpdate) { - start("MarianumMobile", "wu-timetable-$day"); + GetTimetableCache({required onUpdate, required this.startdate, required this.enddate}) : super(RequestCache.cacheMinute, onUpdate) { + start("MarianumMobile", "wu-timetable-$startdate-$enddate"); } @override @@ -29,8 +30,8 @@ class GetTimetableCache extends RequestCache { type: 5, keyType: GetTimetableParamsOptionsElementKeyType.id, ), - startDate: day, - endDate: day, + startDate: startdate, + endDate: enddate, teacherFields: GetTimetableParamsOptionsFields.all, subjectFields: GetTimetableParamsOptionsFields.all, roomFields: GetTimetableParamsOptionsFields.all, diff --git a/lib/data/timetable/timetableProps.dart b/lib/data/timetable/timetableProps.dart index 8868003..1bc114b 100644 --- a/lib/data/timetable/timetableProps.dart +++ b/lib/data/timetable/timetableProps.dart @@ -1,4 +1,6 @@ +import 'dart:developer'; + import 'package:intl/intl.dart'; import 'package:marianum_mobile/api/apiResponse.dart'; import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysCache.dart'; @@ -12,8 +14,21 @@ import 'package:marianum_mobile/data/dataHolder.dart'; import '../../api/webuntis/queries/getTimetable/getTimetableCache.dart'; import '../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +extension DateTimeExtension on DateTime { + DateTime next(int day) { + return add( + Duration( + days: (day - weekday) % DateTime.daysPerWeek, + ), + ); + } +} + class TimetableProps extends DataHolder { - late DateTime queryDate; + var _queryWeek = DateTime.now(); + + late DateTime startDate = getDate(_queryWeek.subtract(Duration(days: _queryWeek.weekday - 1))); + late DateTime endDate = getDate(_queryWeek.add(Duration(days: DateTime.daysPerWeek - _queryWeek.weekday))); GetTimetableResponse? _getTimetableResponse; GetTimetableResponse get getTimetableResponse => _getTimetableResponse!; @@ -29,7 +44,8 @@ class TimetableProps extends DataHolder { TimetableProps() { - nearest(); + //nearest(); + } @override @@ -40,7 +56,8 @@ class TimetableProps extends DataHolder { @override void run() { GetTimetableCache( - day: int.parse(DateFormat("yyyyMMdd").format(queryDate)), + startdate: int.parse(DateFormat("yyyyMMdd").format(startDate)), + enddate: int.parse(DateFormat("yyyyMMdd").format(endDate)), onUpdate: (GetTimetableResponse data) => { _getTimetableResponse = data, notifyListeners(), @@ -70,25 +87,28 @@ class TimetableProps extends DataHolder { } void nearest() { - queryDate = DateTime.now(); - while(isWeekend(queryDate)) { - next(); - } - run(); + _queryWeek = _queryWeek = DateTime.now(); + updateWeek(); } - void next({previous = false}) { - do { - if(previous) { - queryDate = queryDate.subtract(const Duration(days: 1)); - } else { - queryDate = queryDate.add(const Duration(days: 1)); - } - } while(isWeekend(queryDate)); - run(); + void switchWeek({previous = false}) { + if(previous) { + _queryWeek = _queryWeek.subtract(const Duration(days: 7)); + } else { + _queryWeek = _queryWeek.add(const Duration(days: 7)); + } + updateWeek(); } + DateTime getDate(DateTime d) => DateTime(d.year, d.month, d.day); + bool isWeekend(DateTime queryDate) { return queryDate.weekday == DateTime.saturday || queryDate.weekday == DateTime.sunday; } + + void updateWeek() { + startDate = getDate(_queryWeek.subtract(Duration(days: _queryWeek.weekday - 1))); + endDate = getDate(_queryWeek.add(Duration(days: DateTime.daysPerWeek - _queryWeek.weekday))); + run(); + } } \ No newline at end of file diff --git a/lib/screen/pages/timetable/dayListView.dart b/lib/screen/pages/timetable/dayListView.dart deleted file mode 100644 index bafd9c4..0000000 --- a/lib/screen/pages/timetable/dayListView.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; -import 'package:timetable_view/timetable_view.dart'; - -import '../../../api/webuntis/queries/getHolidays/getHolidays.dart'; -import '../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import '../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; -import '../../../data/timetable/timetableProps.dart'; - -class DayListView extends StatefulWidget { - final TimetableProps value; - const DayListView(this.value, {Key? key}) : super(key: key); - - @override - State createState() => _DayListViewState(); -} - -class _DayListViewState extends State { - @override - Widget build(BuildContext context) { - return TimetableView( - laneEventsList: _buildLaneEvents(widget.value), - onEventTap: (TableEvent event) {}, - timetableStyle: CustomTableStyle(context), - onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, - ); - } - - List _buildLaneEvents(TimetableProps data) { - List laneEvents = List.empty(growable: true); - Jiffy.locale("de"); // todo move outwards - - GetTimetableResponse timetable = data.getTimetableResponse; - GetRoomsResponse rooms = data.getRoomsResponse; - GetSubjectsResponse subjects = data.getSubjectsResponse; - GetHolidaysResponse holidays = data.getHolidaysResponse; - - GetHolidaysResponseObject? holidayInfo = GetHolidays.find(holidays, time: data.queryDate); - if(holidayInfo != null) { - laneEvents.add( - LaneEvents( - lane: Lane( - laneIndex: data.queryDate.millisecondsSinceEpoch, - name: "${Jiffy(data.queryDate.toString()).format("dd.MM.yy")}\n${Jiffy(data.queryDate.toString()).format("EEEE")}", - textStyle: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14 - ) - ), - events: List.of([ - TableEvent( - title: holidayInfo.name, - eventId: holidayInfo.id, - laneIndex: data.queryDate.millisecondsSinceEpoch, - startTime: parseTime(0800), - endTime: parseTime(1500), - padding: const EdgeInsets.all(5), - backgroundColor: Theme.of(context).disabledColor, - location: "\n${holidayInfo.longName}", - ) - ]), - ) - ); - } - - List dayList = timetable.result.map((e) => e.date).toSet().toList(); - dayList.sort((a, b) => a-b); - dayList.forEach((day) { - //Every Day - laneEvents.add( - LaneEvents( - lane: Lane( - laneIndex: day, - name: "${Jiffy(day.toString()).format("dd.MM.yy")}\n${Jiffy(day.toString()).format("EEEE")}", - textStyle: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14 - ) - ), - events: List.generate( - timetable.result.where((element) => element.date == day).length, - (index) { - GetTimetableResponseObject tableEvent = timetable.result.where((element) => element.date == day).elementAt(index); - - GetSubjectsResponseObject subject = subjects.result.firstWhere((subject) => subject.id == tableEvent.su[0]['id']); - - return TableEvent( - title: "${subject.alternateName} (${subject.longName})", - eventId: tableEvent.id, - laneIndex: day, - startTime: parseTime(tableEvent.startTime), - endTime: parseTime(tableEvent.endTime), - padding: const EdgeInsets.all(5), - backgroundColor: Theme.of(context).primaryColor, - location: "\n${rooms.result.firstWhere((room) => room.id == tableEvent.ro[0]['id']).name} - ${tableEvent.te[0]['longname']} (${tableEvent.te[0]['name']})", - ); - } - ) - ) - ); - }); - - return laneEvents; - } - - TableEventTime parseTime(int input) { - String time = input.toString().length < 4 ? "0$input" : input.toString(); - return TableEventTime(hour: int.parse(time.substring(0, 2)), minute: int.parse(time.substring(2, 4))); - } -} - -class CustomTableStyle extends TimetableStyle { - dynamic context; - CustomTableStyle(this.context); - - @override - int get startHour => 07; - @override - int get endHour => 17; - @override - Color get cornerColor => Theme.of(context).primaryColor; - @override - Color get timeItemTextColor => Theme.of(context).primaryColor; - @override - double get timeItemHeight => 70; - @override - double get timeItemWidth => 50; - @override - double get laneWidth => MediaQuery.of(context).size.width - timeItemWidth; - -} \ No newline at end of file diff --git a/lib/screen/pages/timetable/timetable.dart b/lib/screen/pages/timetable/timetable.dart index ade1258..ca0f874 100644 --- a/lib/screen/pages/timetable/timetable.dart +++ b/lib/screen/pages/timetable/timetable.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:marianum_mobile/data/timetable/timetableProps.dart'; -import 'package:marianum_mobile/screen/pages/timetable/dayListView.dart'; +import 'package:marianum_mobile/screen/pages/timetable/weekView.dart'; import 'package:provider/provider.dart'; @@ -34,7 +34,7 @@ class _TimetableState extends State { return Column( children: [ Flexible( - child: DayListView(value), + child: WeekView(value), ), Container( @@ -49,7 +49,7 @@ class _TimetableState extends State { children: [ IconButton( - onPressed: () => timetable.next(previous: true), + onPressed: () => timetable.switchWeek(previous: true), icon: const Icon(Icons.navigate_before_sharp), color: Theme.of(context).primaryColor, iconSize: 30, @@ -65,7 +65,7 @@ class _TimetableState extends State { ], ), IconButton( - onPressed: () => timetable.next(), + onPressed: () => timetable.switchWeek(), icon: const Icon(Icons.navigate_next_sharp), color: Theme.of(context).primaryColor, iconSize: 30, diff --git a/lib/screen/pages/timetable/weekView.dart b/lib/screen/pages/timetable/weekView.dart new file mode 100644 index 0000000..7e8ed33 --- /dev/null +++ b/lib/screen/pages/timetable/weekView.dart @@ -0,0 +1,183 @@ +import 'dart:developer' as dev; +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; +import 'package:timetable_view/timetable_view.dart'; + +import '../../../api/webuntis/queries/getHolidays/getHolidays.dart'; +import '../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; +import '../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; +import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../data/timetable/timetableProps.dart'; + +extension DateHelpers on DateTime { + bool isToday() { + final now = DateTime.now(); + return now.day == this.day && + now.month == this.month && + now.year == this.year; + } + + bool isYesterday() { + final yesterday = DateTime.now().subtract(Duration(days: 1)); + return yesterday.day == this.day && + yesterday.month == this.month && + yesterday.year == this.year; + } +} + + +class WeekView extends StatefulWidget { + final TimetableProps value; + const WeekView(this.value, {Key? key}) : super(key: key); + + @override + State createState() => _WeekViewState(); +} + +class _WeekViewState extends State { + @override + Widget build(BuildContext context) { + return TimetableView( + laneEventsList: _buildLaneEvents(widget.value), + onEventTap: (TableEvent event) {}, + timetableStyle: CustomTableStyle(context), + onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, + ); + } + + List _buildLaneEvents(TimetableProps data) { + List laneEvents = List.empty(growable: true); + Jiffy.locale("de"); // todo move outwards + + GetTimetableResponse timetable = data.getTimetableResponse; + GetRoomsResponse rooms = data.getRoomsResponse; + GetSubjectsResponse subjects = data.getSubjectsResponse; + GetHolidaysResponse holidays = data.getHolidaysResponse; + + List dayList = timetable.result.map((e) => e.date).toSet().toList(); + dayList.sort((a, b) => a-b); + + for(int i = 0; i <= data.endDate.difference(data.startDate).inDays; i++) { + DateTime currentDay = data.startDate.copyWith().add(Duration(days: i)); + + // Check Holiday Information + GetHolidaysResponseObject? holidayInfo = GetHolidays.find(holidays, time: currentDay); + if(holidayInfo != null) { + laneEvents.add( + LaneEvents( + lane: getLane(currentDay), + events: List.of([ + TableEvent( + title: holidayInfo.name, + eventId: holidayInfo.id, + laneIndex: data.startDate.millisecondsSinceEpoch, + startTime: parseTime(0800), + endTime: parseTime(1500), + padding: const EdgeInsets.all(5), + backgroundColor: Theme.of(context).disabledColor, + location: "\n${holidayInfo.longName}", + ) + ]), + ) + ); + } + } + + for (var day in dayList) { + DateTime currentDay = DateTime.parse("$day"); + Lane currentLane = getLane(currentDay); + //Every Day + + List events = List.generate( + timetable.result.where((element) => element.date == day).length, + (index) { + GetTimetableResponseObject tableEvent = timetable.result.where((element) => element.date == day).elementAt(index); + + GetSubjectsResponseObject subject = subjects.result.firstWhere((subject) => subject.id == tableEvent.su[0]['id']); + + return TableEvent( + title: "${subject.alternateName} (${subject.longName})", + eventId: tableEvent.id, + laneIndex: day, + startTime: parseTime(tableEvent.startTime), + endTime: parseTime(tableEvent.endTime), + padding: const EdgeInsets.all(5), + backgroundColor: Theme.of(context).primaryColor, + location: "\n${rooms.result.firstWhere((room) => room.id == tableEvent.ro[0]['id']).name} - ${tableEvent.te[0]['longname']} (${tableEvent.te[0]['name']})", + ); + } + ); + + //Timepointer + if(currentDay.isToday()) { + events.add(TableEvent( + title: "", + eventId: 1, + laneIndex: day, + startTime: formatTime(DateTime.now().add(Duration(hours: 8))), + endTime: formatTime(DateTime.now().add(const Duration(minutes: 3, hours: 8))), + backgroundColor: Theme.of(context).disabledColor, + )); + } + + laneEvents.add( + LaneEvents( + lane: currentLane, + events: events + ) + ); + } + + return laneEvents; + } + + Lane getLane(DateTime currentDay) { + return Lane( + backgroundColor: currentDay.isToday() ? Theme.of(context).dividerColor : Colors.white, + laneIndex: currentDay.millisecondsSinceEpoch, + name: "${Jiffy(currentDay.toString()).format("dd MMM")}\n${Jiffy(currentDay.toString()).format("EE")}", + textStyle: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + fontSize: 14 + ) + ); + } + + TableEventTime parseTime(int input) { + String time = input.toString().length < 4 ? "0$input" : input.toString(); + return TableEventTime(hour: int.parse(time.substring(0, 2)), minute: int.parse(time.substring(2, 4))); + } + + TableEventTime formatTime(DateTime input) { + return TableEventTime(hour: input.hour, minute: input.minute); + } +} + +class CustomTableStyle extends TimetableStyle { + dynamic context; + CustomTableStyle(this.context); + + @override + int get startHour => 07; + @override + int get endHour => 17; + @override + Color get cornerColor => Theme.of(context).primaryColor; + @override + Color get timeItemTextColor => Theme.of(context).primaryColor; + @override + double get timeItemHeight => MediaQuery.of(context).size.width > 1000 ? 60 : 100 + (-MediaQuery.of(context).size.width + 1000) * 0.01; + @override + double get timeItemWidth => 50; + @override + double get laneHeight => 40; + @override + double get laneWidth => (MediaQuery.of(context).size.width - timeItemWidth) / 5; + +} \ No newline at end of file