From 53e9aca3762000072f25d8d79ae29f934ad54f10 Mon Sep 17 00:00:00 2001 From: 55nknown <55922348+55nknown@users.noreply.github.com> Date: Sun, 11 Sep 2022 21:15:56 +0200 Subject: [PATCH] common lesson tile --- filcnaplo/lib/ui/filter/sort.dart | 2 +- filcnaplo/lib/ui/filter/widgets/lessons.dart | 2 +- .../lib/ui/widgets/lesson/lesson_tile.dart | 308 ++++++++++++++++++ .../ui/widgets/lesson/lesson_tile.i18n.dart | 33 ++ .../lib/ui/widgets/message/message_tile.dart | 1 - filcnaplo_desktop_ui | 2 +- filcnaplo_mobile_ui | 2 +- 7 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart create mode 100644 filcnaplo/lib/ui/widgets/lesson/lesson_tile.i18n.dart diff --git a/filcnaplo/lib/ui/filter/sort.dart b/filcnaplo/lib/ui/filter/sort.dart index 4364257..7542ea6 100644 --- a/filcnaplo/lib/ui/filter/sort.dart +++ b/filcnaplo/lib/ui/filter/sort.dart @@ -6,9 +6,9 @@ import 'package:filcnaplo_kreta_api/models/message.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_viewable.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_tile.dart'; -import 'package:filcnaplo_mobile_ui/common/widgets/timetable/changed_lesson_tile.dart'; import 'package:flutter/material.dart'; import 'package:implicitly_animated_reorderable_list/implicitly_animated_reorderable_list.dart'; +import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_tile.dart'; import 'package:filcnaplo/utils/format.dart'; // difference.inDays is not reliable diff --git a/filcnaplo/lib/ui/filter/widgets/lessons.dart b/filcnaplo/lib/ui/filter/widgets/lessons.dart index 4eb030f..95c665c 100644 --- a/filcnaplo/lib/ui/filter/widgets/lessons.dart +++ b/filcnaplo/lib/ui/filter/widgets/lessons.dart @@ -1,6 +1,6 @@ import 'package:filcnaplo/ui/date_widget.dart'; import 'package:filcnaplo_kreta_api/models/lesson.dart'; -import 'package:filcnaplo_mobile_ui/common/widgets/timetable/changed_lesson_viewable.dart' as mobile; +import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_viewable.dart' as mobile; List getWidgets(List providerLessons) { List items = []; diff --git a/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart new file mode 100644 index 0000000..15049f1 --- /dev/null +++ b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart @@ -0,0 +1,308 @@ +import 'package:filcnaplo_kreta_api/providers/exam_provider.dart'; +import 'package:filcnaplo_kreta_api/providers/homework_provider.dart'; +import 'package:filcnaplo/theme/colors/colors.dart'; +import 'package:filcnaplo_kreta_api/models/exam.dart'; +import 'package:filcnaplo_kreta_api/models/homework.dart'; +import 'package:filcnaplo_kreta_api/models/lesson.dart'; +import 'package:filcnaplo/utils/format.dart'; +import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; +import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_view.dart'; +import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_view.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'lesson_tile.i18n.dart'; + +class LessonTile extends StatelessWidget { + const LessonTile(this.lesson, {Key? key, this.onTap, this.swapDesc = false}) : super(key: key); + + final Lesson lesson; + final bool swapDesc; + final void Function()? onTap; + + @override + Widget build(BuildContext context) { + List subtiles = []; + + Color accent = Theme.of(context).colorScheme.secondary; + bool fill = false; + bool fillLeading = false; + String lessonIndexTrailing = ""; + + // Only put a trailing . if its a digit + if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = "."; + + var now = DateTime.now(); + if (lesson.start.isBefore(now) && lesson.end.isAfter(now) && lesson.status?.name != "Elmaradt") { + fillLeading = true; + } + + if (lesson.substituteTeacher != "") { + fill = true; + accent = AppColors.of(context).yellow; + } + + if (lesson.status?.name == "Elmaradt") { + fill = true; + accent = AppColors.of(context).red; + } + + if (lesson.isEmpty) { + accent = AppColors.of(context).text.withOpacity(0.6); + } + + if (!lesson.studentPresence) { + subtiles.add(LessonSubtile( + type: LessonSubtileType.absence, + title: "absence".i18n, + )); + } + + if (lesson.homeworkId != "") { + Homework homework = Provider.of(context, listen: false) + .homework + .firstWhere((h) => h.id == lesson.homeworkId, orElse: () => Homework.fromJson({})); + + if (homework.id != "") { + subtiles.add(LessonSubtile( + type: LessonSubtileType.homework, + title: homework.content, + onPressed: () => HomeworkView.show(homework, context: context), + )); + } + } + + if (lesson.exam != "") { + Exam exam = Provider.of(context, listen: false).exams.firstWhere((t) => t.id == lesson.exam, orElse: () => Exam.fromJson({})); + if (exam.id != "") { + subtiles.add(LessonSubtile( + type: LessonSubtileType.exam, + title: exam.description != "" ? exam.description : exam.mode?.description ?? "exam".i18n, + onPressed: () => ExamView.show(exam, context: context), + )); + } + } + + String description = ''; + String room = ''; + + final cleanDesc = lesson.description.specialChars().toLowerCase().replaceAll(lesson.subject.name.specialChars().toLowerCase(), ''); + + if (!swapDesc) { + if (cleanDesc != "") { + description = lesson.description; + } + + // Changed lesson Description + if (lesson.isChanged) { + if (lesson.status?.name == "Elmaradt") { + description = 'cancelled'.i18n; + } else if (lesson.substituteTeacher != "") { + description = 'substitution'.i18n; + } + } + + room = lesson.room.replaceAll("_", " "); + } else { + description = lesson.room.replaceAll("_", " "); + } + + return Padding( + padding: const EdgeInsets.only(bottom: 2.0), + child: Material( + color: fill ? accent.withOpacity(.25) : Colors.transparent, + borderRadius: BorderRadius.circular(12.0), + child: Visibility( + visible: lesson.subject.id != '' || lesson.isEmpty, + replacement: Padding( + padding: const EdgeInsets.only(top: 6.0), + child: PanelTitle(title: Text(lesson.name)), + ), + child: Padding( + padding: EdgeInsets.only(bottom: subtiles.isEmpty ? 0.0 : 12.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + minVerticalPadding: 12.0, + dense: true, + onTap: onTap, + // onLongPress: kDebugMode ? () => log(jsonEncode(lesson.json)) : null, + visualDensity: VisualDensity.compact, + contentPadding: const EdgeInsets.symmetric(horizontal: 4.0), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), + title: Text( + !lesson.isEmpty ? lesson.subject.name.capital() : "empty".i18n, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 15.5, + color: AppColors.of(context).text.withOpacity(!lesson.isEmpty ? 1.0 : 0.5), + ), + ), + subtitle: description != "" + ? Text( + description, + style: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14.0, + ), + maxLines: 1, + softWrap: false, + overflow: TextOverflow.ellipsis, + ) + : null, + minLeadingWidth: 34.0, + leading: AspectRatio( + aspectRatio: 1, + child: Center( + child: Stack( + children: [ + Text( + lesson.lessonIndex + lessonIndexTrailing, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.w600, + color: accent, + ), + ), + + // Current lesson indicator + Transform.translate( + offset: const Offset(-12.0, -2.0), + child: Container( + decoration: BoxDecoration( + color: fillLeading ? Theme.of(context).colorScheme.secondary.withOpacity(.3) : const Color(0x00000000), + borderRadius: BorderRadius.circular(12.0), + boxShadow: [ + if (fillLeading) + BoxShadow( + color: Theme.of(context).colorScheme.secondary.withOpacity(.25), + blurRadius: 6.0, + ) + ], + ), + margin: const EdgeInsets.symmetric(vertical: 4.0), + width: 4.0, + height: double.infinity, + ), + ) + ], + ), + ), + ), + trailing: !lesson.isEmpty + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (!swapDesc) + SizedBox( + width: 52.0, + child: Padding( + padding: const EdgeInsets.only(right: 6.0), + child: Text( + room, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.of(context).text.withOpacity(.75), + ), + ), + ), + ), + Stack( + alignment: Alignment.center, + children: [ + // Fix alignment hack + const Opacity(opacity: 0, child: Text("EE:EE")), + Text( + "${DateFormat("H:mm").format(lesson.start)}\n${DateFormat("H:mm").format(lesson.end)}", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.of(context).text.withOpacity(.9), + ), + ), + ], + ), + ], + ) + : null, + ), + + // Homework & Exams + ...subtiles, + ], + ), + ), + ), + ), + ); + } +} + +enum LessonSubtileType { homework, exam, absence } + +class LessonSubtile extends StatelessWidget { + const LessonSubtile({Key? key, this.onPressed, required this.title, required this.type}) : super(key: key); + + final Function()? onPressed; + final String title; + final LessonSubtileType type; + + @override + Widget build(BuildContext context) { + IconData icon; + Color iconColor = AppColors.of(context).text; + + switch (type) { + case LessonSubtileType.absence: + icon = FeatherIcons.slash; + iconColor = AppColors.of(context).red; + break; + case LessonSubtileType.exam: + icon = FeatherIcons.file; + break; + case LessonSubtileType.homework: + icon = FeatherIcons.home; + break; + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: InkWell( + onTap: onPressed, + borderRadius: BorderRadius.circular(8.0), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + children: [ + Center( + child: SizedBox( + width: 30.0, + child: Icon(icon, color: iconColor.withOpacity(.75), size: 20.0), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 20.0), + child: Text( + title.escapeHtml(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.65)), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/filcnaplo/lib/ui/widgets/lesson/lesson_tile.i18n.dart b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.i18n.dart new file mode 100644 index 0000000..e2a0a9c --- /dev/null +++ b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.i18n.dart @@ -0,0 +1,33 @@ +import 'package:i18n_extension/i18n_extension.dart'; + +extension Localization on String { + static final _t = Translations.byLocale("hu_hu") + + { + "en_en": { + "empty": "Free period", + "cancelled": "Cancelled", + "substitution": "Substituted", + "absence": "You were absent on this lesson", + "exam": "Exam" + }, + "hu_hu": { + "empty": "Lyukasóra", + "cancelled": "Elmarad", + "substitution": "Helyettesítés", + "absence": "Hiányoztál ezen az órán", + "exam": "Dolgozat" + }, + "de_de": { + "empty": "Springstunde", + "cancelled": "Abgesagte", + "substitution": "Vertretene", + "absence": "Sie waren in dieser Lektion nicht anwesend", + "exam": "Prüfung" + } + }; + + String get i18n => localize(this, _t); + String fill(List params) => localizeFill(this, params); + String plural(int value) => localizePlural(value, this, _t); + String version(Object modifier) => localizeVersion(modifier, this, _t); +} diff --git a/filcnaplo/lib/ui/widgets/message/message_tile.dart b/filcnaplo/lib/ui/widgets/message/message_tile.dart index 4798344..18ed315 100644 --- a/filcnaplo/lib/ui/widgets/message/message_tile.dart +++ b/filcnaplo/lib/ui/widgets/message/message_tile.dart @@ -1,4 +1,3 @@ -import 'package:animations/animations.dart'; import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/utils/color.dart'; diff --git a/filcnaplo_desktop_ui b/filcnaplo_desktop_ui index 98ae9b4..1625cf7 160000 --- a/filcnaplo_desktop_ui +++ b/filcnaplo_desktop_ui @@ -1 +1 @@ -Subproject commit 98ae9b4139ca795ac9b39bcbb1d9ef2713d98a63 +Subproject commit 1625cf7a34d8bd98afcf5756ae9511de9f6f98cb diff --git a/filcnaplo_mobile_ui b/filcnaplo_mobile_ui index 5600330..46c8880 160000 --- a/filcnaplo_mobile_ui +++ b/filcnaplo_mobile_ui @@ -1 +1 @@ -Subproject commit 5600330a166e274c06cbbc5678ca2a40db919eb1 +Subproject commit 46c8880a3caedf665b90c5994d848ee745fcdf27