forked from firka/firka
tt: add buttons to change between weeks
This commit is contained in:
3
firka/assets/icons/dropdownLeft.svg
Normal file
3
firka/assets/icons/dropdownLeft.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 7L9 12L14 17" stroke="#A7DC22" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 219 B |
3
firka/assets/icons/dropdownRight.svg
Normal file
3
firka/assets/icons/dropdownRight.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 7L15 12L10 17" stroke="#A7DC22" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 220 B |
@@ -33,7 +33,7 @@ extension DurationExtension on Duration {
|
||||
}
|
||||
}
|
||||
|
||||
enum FormatMode { yearly, grades, welcome, hmm, da, dd }
|
||||
enum FormatMode { yearly, grades, welcome, hmm, da, dd, yyyymmddwedd }
|
||||
|
||||
enum Cycle { morning, day, afternoon, night }
|
||||
|
||||
@@ -51,6 +51,9 @@ extension DateExtension on DateTime {
|
||||
var yesterday = today.subtract(Duration(days: 1));
|
||||
var yesterdayLim = today.subtract(Duration(days: 2));
|
||||
|
||||
var weekStart = today.subtract(Duration(days: today.weekday - 1));
|
||||
var weekEnd = weekStart.add(Duration(days: 6));
|
||||
|
||||
switch (mode) {
|
||||
case FormatMode.grades:
|
||||
if (isBefore(yesterdayLim)) {
|
||||
@@ -77,9 +80,20 @@ extension DateExtension on DateTime {
|
||||
return DateFormat('MMMMEEEEd').format(this).substring(0, 2);
|
||||
case FormatMode.dd:
|
||||
return DateFormat('dd').format(this);
|
||||
case FormatMode.yyyymmddwedd:
|
||||
return "${DateFormat('yyyy MMM. dd').format(weekStart).toLowerCase()}-${DateFormat('dd').format(weekEnd)}";
|
||||
}
|
||||
}
|
||||
|
||||
int weekNumber() {
|
||||
int dayOfYear = int.parse(DateFormat("D").format(this));
|
||||
return ((dayOfYear - weekday + 10) / 7).floor();
|
||||
}
|
||||
|
||||
bool isAWeek() {
|
||||
return weekNumber() % 2 == 0;
|
||||
}
|
||||
|
||||
DateTime getMonday() {
|
||||
return subtract(Duration(days: weekday - 1));
|
||||
}
|
||||
|
||||
Submodule firka/lib/l10n updated: 24104a853f...f69c2d2fe2
@@ -6,6 +6,7 @@ import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/delayed_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:transparent_pointer/transparent_pointer.dart';
|
||||
|
||||
import '../../../../main.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
@@ -24,6 +25,7 @@ class HomeTimetableScreen extends StatefulWidget {
|
||||
class _HomeTimetableScreen extends State<HomeTimetableScreen> {
|
||||
List<Lesson>? lessons;
|
||||
List<DateTime>? dates;
|
||||
DateTime? now;
|
||||
int active = 0;
|
||||
bool disposed = false;
|
||||
final CarouselSliderController _controller = CarouselSliderController();
|
||||
@@ -37,50 +39,52 @@ class _HomeTimetableScreen extends State<HomeTimetableScreen> {
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
Future<void> initForWeek(DateTime now) async {
|
||||
var monday = now.getMonday().getMidnight();
|
||||
var sunday = monday.add(Duration(days: 6));
|
||||
|
||||
var lessonsResp = await widget.data.client.getTimeTable(monday, sunday);
|
||||
List<DateTime> dates = List.empty(growable: true);
|
||||
|
||||
if (lessonsResp.response != null) {
|
||||
lessons = lessonsResp.response;
|
||||
|
||||
for (var i = 0; i < 7; i++) {
|
||||
var t = monday.add(Duration(days: i));
|
||||
|
||||
var hasLessons = i < 5 ||
|
||||
lessons!.firstWhereOrNull((lesson) {
|
||||
return lesson.start.getMidnight().millisecondsSinceEpoch ==
|
||||
t.getMidnight().millisecondsSinceEpoch;
|
||||
}) !=
|
||||
null;
|
||||
|
||||
if (hasLessons) {
|
||||
dates.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disposed) return;
|
||||
setState(() {
|
||||
this.dates = dates;
|
||||
if (now.isAfter(dates.last)) {
|
||||
active = dates.length - 1;
|
||||
} else {
|
||||
active = dates.indexWhere((d) =>
|
||||
d.isAfter(now.getMidnight()) &&
|
||||
d.isBefore(
|
||||
now.getMidnight().add(Duration(hours: 23, minutes: 59))));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
var monday = timeNow().getMonday().getMidnight();
|
||||
var sunday = monday.add(Duration(days: 6));
|
||||
|
||||
(() async {
|
||||
var lessonsResp = await widget.data.client.getTimeTable(monday, sunday);
|
||||
List<DateTime> dates = List.empty(growable: true);
|
||||
|
||||
if (lessonsResp.response != null) {
|
||||
lessons = lessonsResp.response;
|
||||
|
||||
for (var i = 0; i < 7; i++) {
|
||||
var t = monday.add(Duration(days: i));
|
||||
|
||||
var hasLessons = i < 5 ||
|
||||
lessons!.firstWhereOrNull((lesson) {
|
||||
return lesson.start.getMidnight().millisecondsSinceEpoch ==
|
||||
t.getMidnight().millisecondsSinceEpoch;
|
||||
}) !=
|
||||
null;
|
||||
|
||||
if (hasLessons) {
|
||||
dates.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disposed) return;
|
||||
setState(() {
|
||||
this.dates = dates;
|
||||
if (timeNow().isAfter(dates.last)) {
|
||||
active = dates.length - 1;
|
||||
} else {
|
||||
active = dates.indexWhere((d) =>
|
||||
d.isAfter(timeNow().getMidnight()) &&
|
||||
d.isBefore(timeNow()
|
||||
.getMidnight()
|
||||
.add(Duration(hours: 23, minutes: 59))));
|
||||
}
|
||||
});
|
||||
})();
|
||||
now = timeNow();
|
||||
initForWeek(now!);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -105,14 +109,13 @@ class _HomeTimetableScreen extends State<HomeTimetableScreen> {
|
||||
lesson.end.isBefore(date.add(Duration(hours: 24))))
|
||||
.toList();
|
||||
|
||||
ttDays.add(TimeTableDayWidget(
|
||||
widget.data.l10n, date, lessonsOnDate, active == i));
|
||||
ttDays.add(TimeTableDayWidget(widget.data.l10n, date, lessonsOnDate));
|
||||
}
|
||||
|
||||
return Stack(children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 50,
|
||||
height: 74 + 16,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
@@ -167,36 +170,113 @@ class _HomeTimetableScreen extends State<HomeTimetableScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
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: 7));
|
||||
setState(() {
|
||||
now = newNow;
|
||||
lessons = null;
|
||||
dates = null;
|
||||
});
|
||||
await initForWeek(newNow);
|
||||
setState(() {
|
||||
now = newNow;
|
||||
});
|
||||
},
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
dates!.first.format(
|
||||
widget.data.l10n, FormatMode.yyyymmddwedd),
|
||||
style: appStyle.fonts.B_14R),
|
||||
SizedBox(width: 4),
|
||||
Text("•",
|
||||
style: appStyle.fonts.B_16R
|
||||
.apply(color: appStyle.colors.accent)),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
dates!.first.isAWeek()
|
||||
? widget.data.l10n.a_week
|
||||
: widget.data.l10n.b_week,
|
||||
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: 7));
|
||||
await initForWeek(newNow);
|
||||
setState(() {
|
||||
now = newNow;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height / 1.4,
|
||||
child: CarouselSlider(
|
||||
items: ttDays,
|
||||
carouselController: _controller,
|
||||
options: CarouselOptions(
|
||||
height: MediaQuery.of(context).size.height / 1.36,
|
||||
enableInfiniteScroll: false,
|
||||
initialPage: active,
|
||||
onPageChanged: (i, _) {
|
||||
setState(() {
|
||||
active = i;
|
||||
});
|
||||
}),
|
||||
)),
|
||||
Row(
|
||||
TransparentPointer(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height / 1.4,
|
||||
child: CarouselSlider(
|
||||
items: ttDays,
|
||||
carouselController: _controller,
|
||||
options: CarouselOptions(
|
||||
height: MediaQuery.of(context).size.height / 1.36,
|
||||
viewportFraction: 1,
|
||||
enableInfiniteScroll: false,
|
||||
initialPage: active,
|
||||
onPageChanged: (i, _) {
|
||||
setState(() {
|
||||
active = i;
|
||||
});
|
||||
}),
|
||||
))),
|
||||
TransparentPointer(
|
||||
child: Row(
|
||||
children: ttWidgets,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)
|
||||
]);
|
||||
} else {
|
||||
return DelayedSpinnerWidget();
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height / 1.35,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(),
|
||||
DelayedSpinnerWidget(),
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class BottomTimeTableNavIconWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
onTap();
|
||||
},
|
||||
|
||||
@@ -10,10 +10,8 @@ class TimeTableDayWidget extends StatelessWidget {
|
||||
final AppLocalizations l10n;
|
||||
final DateTime date;
|
||||
final List<Lesson> lessons;
|
||||
final bool active;
|
||||
|
||||
const TimeTableDayWidget(this.l10n, this.date, this.lessons, this.active,
|
||||
{super.key});
|
||||
const TimeTableDayWidget(this.l10n, this.date, this.lessons, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -41,15 +39,18 @@ class TimeTableDayWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width / (active ? 1 : 1.6),
|
||||
width: MediaQuery.of(context).size.width / 1.1,
|
||||
child: lessons.isEmpty
|
||||
? noLessonsWidget
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 50 + 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: ttBody,
|
||||
padding:
|
||||
const EdgeInsets.only(top: 70 + 16 + 20, left: 4, right: 4),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: ttBody,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../model/style.dart';
|
||||
|
||||
class DelayedSpinnerWidget extends StatefulWidget {
|
||||
const DelayedSpinnerWidget({super.key});
|
||||
|
||||
@@ -27,7 +29,9 @@ class _DelayedSpinner extends State<DelayedSpinnerWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (showSpinner) {
|
||||
return CircularProgressIndicator();
|
||||
return CircularProgressIndicator(
|
||||
color: appStyle.colors.accent,
|
||||
);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
enum FirkaIconType {
|
||||
icons,
|
||||
majesticons,
|
||||
majesticonsLocal,
|
||||
}
|
||||
@@ -21,6 +22,12 @@ class FirkaIconWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (iconType) {
|
||||
case FirkaIconType.icons:
|
||||
return SvgPicture.asset(
|
||||
'assets/icons/${iconData as String}.svg',
|
||||
color: color,
|
||||
height: size,
|
||||
);
|
||||
case FirkaIconType.majesticons:
|
||||
return Majesticon(iconData as Uint8List, color: color, size: size);
|
||||
case FirkaIconType.majesticonsLocal:
|
||||
|
||||
@@ -61,6 +61,7 @@ dependencies:
|
||||
home_widget: ^0.8.0
|
||||
brotli: ^0.6.0
|
||||
crypto: ^3.0.6
|
||||
transparent_pointer: ^1.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -89,6 +90,7 @@ flutter:
|
||||
- assets/images/carousel/
|
||||
- assets/images/icons/
|
||||
- assets/images/background.png
|
||||
- assets/icons/
|
||||
- assets/majesticons/
|
||||
- assets/firka.i
|
||||
|
||||
|
||||
Reference in New Issue
Block a user