From 1ce4368dc68608452f154e3d5f538a2ad4e8f695 Mon Sep 17 00:00:00 2001
From: Armand <4831c0@proton.me>
Date: Sun, 24 Aug 2025 11:39:48 +0200
Subject: [PATCH] tt: add buttons to change between weeks
---
firka/assets/icons/dropdownLeft.svg | 3 +
firka/assets/icons/dropdownRight.svg | 3 +
firka/lib/helpers/extensions.dart | 16 +-
firka/lib/l10n | 2 +-
.../ui/phone/pages/home/home_timetable.dart | 204 ++++++++++++------
.../lib/ui/phone/widgets/bottom_tt_icon.dart | 2 +-
firka/lib/ui/phone/widgets/tt_day.dart | 19 +-
firka/lib/ui/widget/delayed_spinner.dart | 6 +-
firka/lib/ui/widget/firka_icon.dart | 7 +
firka/pubspec.yaml | 2 +
10 files changed, 189 insertions(+), 75 deletions(-)
create mode 100644 firka/assets/icons/dropdownLeft.svg
create mode 100644 firka/assets/icons/dropdownRight.svg
diff --git a/firka/assets/icons/dropdownLeft.svg b/firka/assets/icons/dropdownLeft.svg
new file mode 100644
index 0000000..09ee5c9
--- /dev/null
+++ b/firka/assets/icons/dropdownLeft.svg
@@ -0,0 +1,3 @@
+
diff --git a/firka/assets/icons/dropdownRight.svg b/firka/assets/icons/dropdownRight.svg
new file mode 100644
index 0000000..e81b856
--- /dev/null
+++ b/firka/assets/icons/dropdownRight.svg
@@ -0,0 +1,3 @@
+
diff --git a/firka/lib/helpers/extensions.dart b/firka/lib/helpers/extensions.dart
index e1490ee..435cd02 100644
--- a/firka/lib/helpers/extensions.dart
+++ b/firka/lib/helpers/extensions.dart
@@ -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));
}
diff --git a/firka/lib/l10n b/firka/lib/l10n
index 24104a8..f69c2d2 160000
--- a/firka/lib/l10n
+++ b/firka/lib/l10n
@@ -1 +1 @@
-Subproject commit 24104a853f3b62635412c364257b516198772490
+Subproject commit f69c2d2fe2b161a4e94706ee9a577ece677f3d84
diff --git a/firka/lib/ui/phone/pages/home/home_timetable.dart b/firka/lib/ui/phone/pages/home/home_timetable.dart
index 43d9c04..8370b86 100644
--- a/firka/lib/ui/phone/pages/home/home_timetable.dart
+++ b/firka/lib/ui/phone/pages/home/home_timetable.dart
@@ -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 {
List? lessons;
List? dates;
+ DateTime? now;
int active = 0;
bool disposed = false;
final CarouselSliderController _controller = CarouselSliderController();
@@ -37,50 +39,52 @@ class _HomeTimetableScreen extends State {
disposed = true;
}
+ Future 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 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 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 {
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 {
),
],
),
+ 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(),
+ ],
+ ),
+ );
}
}
}
diff --git a/firka/lib/ui/phone/widgets/bottom_tt_icon.dart b/firka/lib/ui/phone/widgets/bottom_tt_icon.dart
index 016e59f..d650913 100644
--- a/firka/lib/ui/phone/widgets/bottom_tt_icon.dart
+++ b/firka/lib/ui/phone/widgets/bottom_tt_icon.dart
@@ -17,7 +17,7 @@ class BottomTimeTableNavIconWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
- behavior: HitTestBehavior.opaque,
+ behavior: HitTestBehavior.translucent,
onTap: () {
onTap();
},
diff --git a/firka/lib/ui/phone/widgets/tt_day.dart b/firka/lib/ui/phone/widgets/tt_day.dart
index 6cca130..ca259c1 100644
--- a/firka/lib/ui/phone/widgets/tt_day.dart
+++ b/firka/lib/ui/phone/widgets/tt_day.dart
@@ -10,10 +10,8 @@ class TimeTableDayWidget extends StatelessWidget {
final AppLocalizations l10n;
final DateTime date;
final List 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,
+ ),
),
),
);
diff --git a/firka/lib/ui/widget/delayed_spinner.dart b/firka/lib/ui/widget/delayed_spinner.dart
index 53e4b65..aac5a57 100644
--- a/firka/lib/ui/widget/delayed_spinner.dart
+++ b/firka/lib/ui/widget/delayed_spinner.dart
@@ -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 {
@override
Widget build(BuildContext context) {
if (showSpinner) {
- return CircularProgressIndicator();
+ return CircularProgressIndicator(
+ color: appStyle.colors.accent,
+ );
} else {
return SizedBox();
}
diff --git a/firka/lib/ui/widget/firka_icon.dart b/firka/lib/ui/widget/firka_icon.dart
index 2034e74..2bab759 100644
--- a/firka/lib/ui/widget/firka_icon.dart
+++ b/firka/lib/ui/widget/firka_icon.dart
@@ -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:
diff --git a/firka/pubspec.yaml b/firka/pubspec.yaml
index adcdd68..b02223f 100644
--- a/firka/pubspec.yaml
+++ b/firka/pubspec.yaml
@@ -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