forked from firka/firka
490 lines
18 KiB
Dart
490 lines
18 KiB
Dart
import 'package:firka/helpers/api/consts.dart';
|
|
import 'package:firka/helpers/api/consts.dart';
|
|
import 'package:firka/helpers/api/consts.dart';
|
|
import 'package:firka/helpers/api/model/timetable.dart';
|
|
import 'package:firka/helpers/debug_helper.dart';
|
|
import 'package:firka/helpers/extensions.dart';
|
|
import 'package:firka/ui/model/style.dart';
|
|
import 'package:firka/ui/widget/delayed_spinner.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
|
import 'package:transparent_pointer/transparent_pointer.dart';
|
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
|
|
|
import '../../../../main.dart';
|
|
import '../../../widget/firka_icon.dart';
|
|
import '../../screens/home/home_screen.dart';
|
|
|
|
class HomeTimetableMonthlyScreen extends StatefulWidget {
|
|
final AppInitialization data;
|
|
final void Function(ActiveHomePage, bool) cb;
|
|
|
|
const HomeTimetableMonthlyScreen(this.data, this.cb, {super.key});
|
|
|
|
@override
|
|
State<HomeTimetableMonthlyScreen> createState() =>
|
|
_HomeTimetableMonthlyScreen();
|
|
}
|
|
|
|
enum ActiveFilter { lessonNo, homework, omissions }
|
|
|
|
class _HomeTimetableMonthlyScreen extends State<HomeTimetableMonthlyScreen> {
|
|
List<Lesson>? lessons;
|
|
List<DateTime>? dates;
|
|
DateTime? now;
|
|
int active = 0;
|
|
bool disposed = false;
|
|
ActiveFilter activeFilter = ActiveFilter.lessonNo;
|
|
|
|
_HomeTimetableMonthlyScreen();
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
|
|
disposed = true;
|
|
}
|
|
|
|
Future<void> initForMonth(DateTime now) async {
|
|
final monthStart = DateTime.utc(now.year, now.month, 1);
|
|
final monthEnd =
|
|
DateTime.utc(now.year, now.month + 1).subtract(Duration(days: 1));
|
|
|
|
final start = monthStart.subtract(Duration(days: 7)).getMonday();
|
|
var end =
|
|
monthEnd.add(Duration(days: 7)).getMonday().add(Duration(days: 7));
|
|
|
|
var days = end.difference(start).inDays;
|
|
|
|
var lessonsResp =
|
|
await widget.data.client.getTimeTable(monthStart, monthEnd);
|
|
List<DateTime> dates = List.empty(growable: true);
|
|
|
|
for (var i = 0; i < days; i++) {
|
|
dates.add(start.add(Duration(days: i)));
|
|
}
|
|
|
|
if (lessonsResp.response != null) {
|
|
lessons = lessonsResp.response;
|
|
}
|
|
|
|
if (disposed) return;
|
|
setState(() {
|
|
this.dates = dates;
|
|
});
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
now = timeNow();
|
|
initForMonth(now!);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (lessons != null && dates != null) {
|
|
List<Widget> ttDays = [];
|
|
|
|
final meow = dates![20];
|
|
final currentMonthStart = DateTime.utc(meow.year, meow.month, 1);
|
|
final currentMonthEnd =
|
|
DateTime.utc(meow.year, meow.month + 1).subtract(Duration(days: 1));
|
|
|
|
for (var d in dates!) {
|
|
if (d.isBefore(currentMonthStart) || d.isAfter(currentMonthEnd)) {
|
|
ttDays.add(Column(
|
|
children: [
|
|
Container(
|
|
width: 40,
|
|
height: 40,
|
|
clipBehavior: Clip.antiAlias,
|
|
decoration: ShapeDecoration(
|
|
color: appStyle.colors.cardTranslucent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6)),
|
|
)),
|
|
SizedBox(height: 4),
|
|
Text(d.format(widget.data.l10n, FormatMode.dd),
|
|
style: appStyle.fonts.B_14R.apply(
|
|
color: (d.weekday == DateTime.saturday ||
|
|
d.weekday == DateTime.sunday)
|
|
? appStyle.colors.errorText
|
|
: appStyle.colors.textTertiary)),
|
|
SizedBox(height: 12),
|
|
],
|
|
));
|
|
} else {
|
|
Widget body = SizedBox();
|
|
|
|
var lessonsToday = lessons!.where((lesson) =>
|
|
lesson.start.isAfter(d.getMidnight()) &&
|
|
lesson.end.isBefore(
|
|
d.getMidnight().add(Duration(hours: 23, minutes: 59))));
|
|
|
|
var omissionType = lessonsToday.firstWhereOrNull((lesson) =>
|
|
lesson.studentPresence != null &&
|
|
lesson.studentPresence?.name != OmissionConsts.present);
|
|
|
|
switch (activeFilter) {
|
|
case ActiveFilter.lessonNo:
|
|
if (lessonsToday.isNotEmpty) {
|
|
body = Center(
|
|
child: Text(lessonsToday.length.toString(),
|
|
style: appStyle.fonts.H_16px.apply(
|
|
color: omissionType != null &&
|
|
(omissionType.studentPresence!.name ==
|
|
OmissionConsts.absence ||
|
|
omissionType.studentPresence!.name ==
|
|
OmissionConsts.na)
|
|
? appStyle.colors.errorText
|
|
: timeNow().day == d.day &&
|
|
timeNow().month == d.month
|
|
? appStyle.colors.accent
|
|
: appStyle.colors.secondary)),
|
|
);
|
|
}
|
|
break;
|
|
case ActiveFilter.homework:
|
|
if (lessonsToday.firstWhereOrNull(
|
|
(lesson) => lesson.homeworkUid != null) !=
|
|
null) {
|
|
body = Center(
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.editPen4Solid,
|
|
size: 20.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
);
|
|
}
|
|
break;
|
|
case ActiveFilter.omissions:
|
|
if (omissionType != null) {
|
|
switch (omissionType.studentPresence!.name) {
|
|
case OmissionConsts.na:
|
|
case OmissionConsts.absence:
|
|
body = Center(
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.multiplySolid,
|
|
size: 20.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
);
|
|
break;
|
|
default:
|
|
debugPrint(omissionType.studentPresence!.name);
|
|
body = Center(
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.timerLine,
|
|
size: 20.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ttDays.add(Column(
|
|
children: [
|
|
Container(
|
|
width: 40,
|
|
height: 40,
|
|
clipBehavior: Clip.antiAlias,
|
|
decoration: ShapeDecoration(
|
|
color: activeFilter == ActiveFilter.lessonNo &&
|
|
omissionType != null &&
|
|
(omissionType.studentPresence!.name ==
|
|
OmissionConsts.absence ||
|
|
omissionType.studentPresence!.name ==
|
|
OmissionConsts.na)
|
|
? appStyle.colors.error15p
|
|
: timeNow().day == d.day && timeNow().month == d.month
|
|
? appStyle.colors.buttonSecondaryFill
|
|
: appStyle.colors.card,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6)),
|
|
),
|
|
child: body,
|
|
),
|
|
SizedBox(height: 4),
|
|
Text(d.format(widget.data.l10n, FormatMode.d),
|
|
style: appStyle.fonts.B_14R.apply(
|
|
color: (d.weekday == DateTime.saturday ||
|
|
d.weekday == DateTime.sunday) &&
|
|
lessonsToday.isEmpty
|
|
? appStyle.colors.errorText
|
|
: appStyle.colors.textSecondary)),
|
|
SizedBox(height: 12),
|
|
],
|
|
));
|
|
}
|
|
}
|
|
|
|
return Stack(children: [
|
|
SizedBox(
|
|
width: MediaQuery.of(context).size.width,
|
|
height: 74 + 16,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
widget.data.l10n.timetable,
|
|
style: appStyle.fonts.H_H2
|
|
.apply(color: appStyle.colors.textPrimary),
|
|
),
|
|
Row(
|
|
children: [
|
|
GestureDetector(
|
|
child: Card(
|
|
color: appStyle.colors.buttonSecondaryFill,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.tableSolid,
|
|
size: 26.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
),
|
|
),
|
|
onTap: () {
|
|
widget.cb(
|
|
ActiveHomePage(HomePages.timetable), false);
|
|
},
|
|
),
|
|
Card(
|
|
color: appStyle.colors.buttonSecondaryFill,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(4),
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.plusLine,
|
|
size: 32.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
),
|
|
),
|
|
Card(
|
|
color: appStyle.colors.buttonSecondaryFill,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.majesticons,
|
|
Majesticon.settingsCogSolid,
|
|
size: 26.0,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 16),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
GestureDetector(
|
|
behavior: HitTestBehavior.translucent,
|
|
child: SizedBox(
|
|
width: 24,
|
|
height: 24,
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.icons,
|
|
"dropdownLeft",
|
|
size: 24,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
),
|
|
onTap: () async {
|
|
var newNow = now!.subtract(Duration(days: 30));
|
|
setState(() {
|
|
now = newNow;
|
|
lessons = null;
|
|
dates = null;
|
|
});
|
|
await initForMonth(newNow);
|
|
setState(() {
|
|
now = newNow;
|
|
});
|
|
},
|
|
),
|
|
Text(
|
|
now!
|
|
.format(widget.data.l10n, FormatMode.yyyymmmm)
|
|
.toLowerCase(),
|
|
style: appStyle.fonts.B_14R),
|
|
GestureDetector(
|
|
child: FirkaIconWidget(
|
|
FirkaIconType.icons,
|
|
"dropdownRight",
|
|
size: 24,
|
|
color: appStyle.colors.accent,
|
|
),
|
|
onTap: () async {
|
|
var newNow = now!.add(Duration(days: 30));
|
|
setState(() {
|
|
now = newNow;
|
|
lessons = null;
|
|
dates = null;
|
|
});
|
|
await initForMonth(newNow);
|
|
setState(() {
|
|
now = newNow;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
TransparentPointer(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 74 + 16 + 12),
|
|
child: SizedBox(
|
|
width: MediaQuery.of(context).size.width,
|
|
height: MediaQuery.of(context).size.height / 1.45,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
child: StaggeredGrid.count(
|
|
crossAxisCount: 7,
|
|
children: ttDays,
|
|
),
|
|
)),
|
|
)),
|
|
TransparentPointer(
|
|
child: Column(
|
|
children: [
|
|
SizedBox(
|
|
height: MediaQuery.of(context).size.height / 1.3,
|
|
),
|
|
SizedBox(
|
|
width: MediaQuery.of(context).size.width,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
SizedBox(),
|
|
Row(
|
|
children: [
|
|
_StatusToast(
|
|
FirkaIconWidget(FirkaIconType.majesticons,
|
|
Majesticon.clockSolid,
|
|
color: appStyle.colors.accent, size: 16),
|
|
lessons!
|
|
.where((lesson) =>
|
|
lesson.start.isAfter(currentMonthStart) &&
|
|
lesson.end.isBefore(currentMonthEnd))
|
|
.length,
|
|
activeFilter == ActiveFilter.lessonNo, () {
|
|
setState(() {
|
|
activeFilter = ActiveFilter.lessonNo;
|
|
});
|
|
}),
|
|
_StatusToast(
|
|
FirkaIconWidget(FirkaIconType.majesticons,
|
|
Majesticon.editPen4Solid,
|
|
color: appStyle.colors.accent, size: 16),
|
|
lessons!
|
|
.where((lesson) =>
|
|
lesson.start.isAfter(currentMonthStart) &&
|
|
lesson.end.isBefore(currentMonthEnd) &&
|
|
lesson.homeworkUid != null)
|
|
.length,
|
|
activeFilter == ActiveFilter.homework, () {
|
|
setState(() {
|
|
activeFilter = ActiveFilter.homework;
|
|
});
|
|
}),
|
|
_StatusToast(
|
|
FirkaIconWidget(
|
|
FirkaIconType.majesticons, Majesticon.timerLine,
|
|
color: appStyle.colors.accent, size: 16),
|
|
lessons!
|
|
.where((lesson) =>
|
|
lesson.start.isAfter(currentMonthStart) &&
|
|
lesson.end.isBefore(currentMonthEnd) &&
|
|
lesson.studentPresence != null &&
|
|
lesson.studentPresence?.name !=
|
|
OmissionConsts.present)
|
|
.length,
|
|
activeFilter == ActiveFilter.omissions, () {
|
|
setState(() {
|
|
activeFilter = ActiveFilter.omissions;
|
|
});
|
|
}),
|
|
],
|
|
),
|
|
SizedBox()
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
]);
|
|
} else {
|
|
return SizedBox(
|
|
height: MediaQuery.of(context).size.height / 1.35,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
SizedBox(),
|
|
DelayedSpinnerWidget(),
|
|
SizedBox(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _StatusToast extends StatelessWidget {
|
|
final FirkaIconWidget _icon;
|
|
final int _count;
|
|
final bool _active;
|
|
final void Function() _onTap;
|
|
|
|
const _StatusToast(this._icon, this._count, this._active, this._onTap);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: _onTap,
|
|
child: Container(
|
|
clipBehavior: Clip.antiAlias,
|
|
decoration: ShapeDecoration(
|
|
color: _active
|
|
? appStyle.colors.buttonSecondaryFill
|
|
: appStyle.colors.cardTranslucent,
|
|
shape:
|
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
child: Row(
|
|
children: [
|
|
_icon,
|
|
SizedBox(width: 6),
|
|
Text(_count.toString(),
|
|
style: appStyle.fonts.H_16px
|
|
.apply(color: appStyle.colors.textPrimary))
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|