From 251e8de44669a584fbdb39365334df2f39cb71fe Mon Sep 17 00:00:00 2001 From: checkedear <271323618+checkedear@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:36:09 +0200 Subject: [PATCH] ref: lesson card --- .../ui/components/common_bottom_sheets.dart | 8 - firka/lib/ui/phone/widgets/lesson.dart | 491 +++++------------- firka/lib/ui/phone/widgets/lesson_small.dart | 4 +- firka/lib/ui/phone/widgets/tt_day.dart | 86 ++- 4 files changed, 199 insertions(+), 390 deletions(-) diff --git a/firka/lib/ui/components/common_bottom_sheets.dart b/firka/lib/ui/components/common_bottom_sheets.dart index 16d4761..a9f2320 100644 --- a/firka/lib/ui/components/common_bottom_sheets.dart +++ b/firka/lib/ui/components/common_bottom_sheets.dart @@ -654,8 +654,6 @@ Future showGradeBottomSheet( SizedBox(height: 20), LessonWidget( data, - [], - [], null, Lesson( uid: "-2", @@ -683,8 +681,6 @@ Future showGradeBottomSheet( lastModifiedAt: timeNow(), ), null, - null, - placeholderMode: true, ), FirkaCard( left: [ @@ -827,8 +823,6 @@ Future showHomeworkBottomSheet( SizedBox(height: 8), LessonWidget( data, - [], - [], null, Lesson( uid: "-1", @@ -848,8 +842,6 @@ Future showHomeworkBottomSheet( lastModifiedAt: timeNow(), ), null, - null, - placeholderMode: true, ), SizedBox( width: double.infinity, diff --git a/firka/lib/ui/phone/widgets/lesson.dart b/firka/lib/ui/phone/widgets/lesson.dart index a7a04fc..e7102c1 100644 --- a/firka/lib/ui/phone/widgets/lesson.dart +++ b/firka/lib/ui/phone/widgets/lesson.dart @@ -3,6 +3,7 @@ import 'package:firka/core/settings.dart'; import 'package:firka/ui/components/firka_card.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; +import 'package:firka_common/ui/components/filled_circle.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; @@ -16,24 +17,16 @@ import 'bubble_test.dart'; class LessonWidget extends StatelessWidget { final AppInitialization data; - final List week; - final List day; final int? lessonNo; final Lesson lesson; final Test? test; - final Lesson? nextLesson; - final bool? placeholderMode; const LessonWidget( this.data, - this.week, - this.day, this.lessonNo, this.lesson, - this.test, - this.nextLesson, { + this.test, { super.key, - this.placeholderMode, }); @override @@ -42,26 +35,22 @@ class LessonWidget extends StatelessWidget { .group("settings") .subGroup("timetable_toast") .boolean("tests_and_homework"); - final showSubstitutions = data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("substitution"); - final showLessonNos = data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("lesson_no"); + final isSubstituted = lesson.substituteTeacher != null; + final showSubstitutions = + isSubstituted && + data.settings + .group("settings") + .subGroup("timetable_toast") + .boolean("substitution"); + final showLessonNos = + lessonNo != null && + data.settings + .group("settings") + .subGroup("timetable_toast") + .boolean("lesson_no"); final isDismissed = lesson.type.name == "UresOra"; - var showBreak = false; - if (week.isNotEmpty) { - showBreak = - timeNow().isAfter(lesson.start) && timeNow().isBefore(lesson.end) || - timeNow().isAfter(week.last.end) || - lesson.start.getMidnight() != timeNow().getMidnight() || - timeNow().isAfter(day.last.end) || - timeNow().isBefore(day.first.start); - } var accent = appStyle.colors.accent; var secondary = appStyle.colors.secondary; var bgColor = appStyle.colors.a15p; @@ -79,16 +68,9 @@ class LessonWidget extends StatelessWidget { List elements = []; - var subjectName = lesson.subject?.name ?? 'N/A'; - if (subjectName.length >= 19) { - subjectName = "${subjectName.substring(0, 19 - 3)}..."; - } - subjectName = subjectName.firstUpper(); + var subjectName = lesson.subject?.name.firstUpper() ?? 'N/A'; var roomName = lesson.roomName ?? 'N/A'; - if (roomName.length >= 11) { - roomName = "${roomName.substring(0, 11 - 3)}..."; - } elements.add( GestureDetector( @@ -105,144 +87,143 @@ class LessonWidget extends StatelessWidget { test, ); }, - child: FirkaCard( + child: FirkaCard.single( + height: 64, + margin: EdgeInsets.all(0), + padding: EdgeInsets.only(left: 14, right: 16), color: isDismissed ? appStyle.colors.cardTranslucent : appStyle.colors.card, + attached: showTests && test != null ? Attach.bottom : Attach.none, shadow: !isDismissed, - left: [ - showLessonNos == false || lessonNo == null - ? SizedBox() - : SizedBox( - width: 18, - height: 18, - child: Stack( - alignment: Alignment.center, - children: [ - SvgPicture.asset( - "assets/icons/subtract.svg", - color: bgColor, - width: 18, - height: 18, - ), - Text( - lessonNo.toString(), - style: appStyle.fonts.B_12R.apply(color: secondary), - textAlign: TextAlign.center, - ), - ], - ), - ), - Transform.translate( - offset: Offset(-4, 0), - child: Card( - shadowColor: Colors.transparent, - color: bgColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Stack( - children: [ - Padding( - padding: EdgeInsetsGeometry.all(4), - child: ClassIconWidget( - color: accent, - size: 20, - uid: lesson.uid, - className: lesson.name, - category: lesson.subject?.name != null - ? lesson.subject!.name.firstUpper() - : '', + child: Row( + children: [ + !showLessonNos + ? SizedBox() + : SizedBox( + width: 18, + height: 18, + child: Stack( + alignment: Alignment.center, + children: [ + SvgPicture.asset( + "assets/icons/subtract.svg", + color: bgColor, + width: 18, + height: 18, + ), + Text( + lessonNo.toString(), + style: appStyle.fonts.B_12R.apply(color: secondary), + textAlign: TextAlign.center, + ), + ], + ), + ), + FilledCircle( + diameter: 36, + color: bgColor, + child: ClassIconWidget( + uid: lesson.uid, + className: lesson.name, + category: subjectName, + color: accent, + size: 24, + ), + ), + SizedOverflowBox( + size: Size(12, 0), + child: !showTests && test != null + ? Transform.translate( + offset: Offset(4, -20), + child: BubbleTest(), + ) + : SizedBox(), + ), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 12, + children: [ + LimitedBox( + maxWidth: 150, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + subjectName, + style: appStyle.fonts.B_16SB.apply( + color: !isDismissed + ? appStyle.colors.textPrimary + : appStyle.colors.textSecondary, + ), + overflow: TextOverflow.ellipsis, + ), + showSubstitutions + ? Text( + lesson.substituteTeacher!, + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textSecondary, + ), + ) + : SizedBox(), + ], + ), + ), + Flexible( + fit: FlexFit.loose, + child: Card( + shadowColor: Colors.transparent, + color: appStyle.colors.a15p, + margin: EdgeInsets.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 6), + child: Text( + roomName, + style: appStyle.fonts.B_12R.apply( + color: appStyle.colors.textSecondary, + ), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ), ), ), - !showTests && test != null - ? Transform.translate( - offset: Offset(26, -18), - child: BubbleTest(), - ) - : SizedBox(), ], ), ), - ), - SizedBox(width: !showTests && test != null ? 16 : 8), - Text( - subjectName, - style: appStyle.fonts.B_15SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - right: [ - placeholderMode == true - ? SizedBox() - : Text( - isDismissed - ? data.l10n.class_dismissed - : lesson.start.toLocal().format( - data.l10n, - FormatMode.hmm, - ), + SizedBox(width: 8), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + lesson.start.toLocal().format(data.l10n, FormatMode.hmm), style: appStyle.fonts.B_14R.apply( color: appStyle.colors.textPrimary, ), ), - placeholderMode == true - ? SizedBox() - : isDismissed - ? SizedBox() - : Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - child: Padding( - padding: EdgeInsets.all(5), - child: Text( - roomName, - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.secondary, - ), - ), + Text( + lesson.end.toLocal().format(data.l10n, FormatMode.hmm), + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textPrimary, ), ), - ], + ], + ), + ], + ), ), ), ); - if (isSubstituted && showSubstitutions) { - elements.add( - FirkaCard( - left: [ - Text( - data.l10n.class_substitution, - style: appStyle.fonts.H_16px.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - right: [ - Text( - lesson.substituteTeacher!, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ); - } - if (test != null && showTests) { - var theme = test!.theme; - if (theme.length >= 20) { - theme = "${theme.substring(0, 20)}..."; - } - var method = test!.method.description ?? 'N/A'; - if (method.length >= 15) { - method = "${method.substring(0, 15)}..."; - } - theme = theme.firstUpper(); - method = method.firstUpper(); + var theme = test!.theme.firstUpper(); + var method = test!.method.description.firstUpper(); elements.add( GestureDetector( @@ -259,25 +240,28 @@ class LessonWidget extends StatelessWidget { ); }, child: FirkaCard( + margin: EdgeInsets.only(top: 4), + attached: Attach.top, left: [ FirkaIconWidget( FirkaIconType.majesticons, Majesticon.editPen4Solid, color: appStyle.colors.accent, + size: 20, ), - SizedBox(width: 6), + SizedBox(width: 8), Text( theme, style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textSecondary, + color: appStyle.colors.textPrimary, ), ), ], right: [ Text( method, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textTertiary, + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textSecondary, ), ), ], @@ -286,201 +270,6 @@ class LessonWidget extends StatelessWidget { ); } - if (nextLesson != null) { - var breakMins = nextLesson!.start.difference(lesson.end).inMinutes; - var seqSchedule = week.getAllSeqs(lesson); - - if (breakMins > 45) { - final breakEnd = lesson.end.add(Duration(minutes: breakMins)); - final emptyClass = seqSchedule.firstWhereOrNull( - (lesson2) => - lesson2.start.isAfter(lesson.end) && - lesson2.end.isBefore(breakEnd), - ); - - if (emptyClass != null) { - final preBreak = emptyClass.start.difference(lesson.end).inMinutes; - final postBreak = breakEnd.difference(emptyClass.end).inMinutes; - - if (data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("breaks") && - showBreak) { - elements.add( - FirkaCard( - color: appStyle.colors.cardTranslucent, - shadow: false, - left: [ - Text( - data.l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - right: [ - Text( - "$preBreak ${preBreak == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}", - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textTertiary, - ), - ), - ], - ), - ); - } - - elements.add( - FirkaCard( - left: [ - SizedBox( - width: 18, - height: 18, - child: Stack( - children: [ - SvgPicture.asset( - "assets/icons/subtract.svg", - color: bgColor, - width: 18, - height: 18, - ), - Padding( - padding: EdgeInsets.only(left: 5), - child: Text( - emptyClass.lessonNumber.toString(), - style: appStyle.fonts.B_12R.apply(color: secondary), - ), - ), - ], - ), - ), - Transform.translate( - offset: Offset(-4, 0), - child: Card( - shadowColor: Colors.transparent, - color: bgColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Padding( - padding: EdgeInsetsGeometry.all(4), - child: FirkaIconWidget( - FirkaIconType.majesticonsLocal, - 'cupFilled', - color: appStyle.colors.accent, - size: 24, - ), - ), - ), - ), - SizedBox(width: 8), - Text( - data.l10n.empty_class, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - right: [ - Text( - isDismissed - ? data.l10n.class_dismissed - : "${emptyClass.start.toLocal().format(data.l10n, FormatMode.hmm)} - ${emptyClass.end.toLocal().format(data.l10n, FormatMode.hmm)}", - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - ); - - if (data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("breaks") && - showBreak) { - elements.add( - FirkaCard( - color: appStyle.colors.cardTranslucent, - shadow: false, - left: [ - Text( - data.l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - right: [ - Text( - "$postBreak ${postBreak == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}", - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textTertiary, - ), - ), - ], - ), - ); - } - } else if (data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("breaks") && - showBreak) { - elements.add( - FirkaCard( - color: appStyle.colors.cardTranslucent, - shadow: false, - left: [ - Text( - data.l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - right: [ - Text( - "$breakMins ${breakMins == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}", - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textTertiary, - ), - ), - ], - ), - ); - } - } else if (data.settings - .group("settings") - .subGroup("timetable_toast") - .boolean("breaks") && - showBreak) { - elements.add( - FirkaCard( - color: appStyle.colors.cardTranslucent, - shadow: false, - left: [ - Text( - data.l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - right: [ - Text( - "$breakMins ${breakMins == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}", - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textTertiary, - ), - ), - ], - ), - ); - } - } - return Column( crossAxisAlignment: CrossAxisAlignment.center, children: elements, diff --git a/firka/lib/ui/phone/widgets/lesson_small.dart b/firka/lib/ui/phone/widgets/lesson_small.dart index c17e69e..13a3e98 100644 --- a/firka/lib/ui/phone/widgets/lesson_small.dart +++ b/firka/lib/ui/phone/widgets/lesson_small.dart @@ -43,9 +43,7 @@ class LessonSmallWidget extends StatelessWidget { size: 20, uid: lesson.uid, className: lesson.name, - category: lesson.subject?.name != null - ? lesson.subject!.name.firstUpper() - : '', + category: lesson.subject?.name.firstUpper() ?? '', ), SizedBox(width: 8), Text( diff --git a/firka/lib/ui/phone/widgets/tt_day.dart b/firka/lib/ui/phone/widgets/tt_day.dart index c6632cf..11319b9 100644 --- a/firka/lib/ui/phone/widgets/tt_day.dart +++ b/firka/lib/ui/phone/widgets/tt_day.dart @@ -1,3 +1,4 @@ +import 'package:firka/core/settings.dart'; import 'package:kreta_api/kreta_api.dart'; import 'package:firka/core/extensions.dart'; import 'package:firka/ui/components/firka_card.dart'; @@ -5,6 +6,7 @@ import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:majesticons_flutter/majesticons_flutter.dart'; import 'lesson.dart'; @@ -30,11 +32,10 @@ class TimeTableDayWidget extends StatelessWidget { @override Widget build(BuildContext context) { - Widget noLessonsWidget = SizedBox(); - List ttBody = List.empty(growable: true); + Widget ttBody; if (lessons.isEmpty) { - noLessonsWidget = Column( + ttBody = Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -70,9 +71,10 @@ class TimeTableDayWidget extends StatelessWidget { ], ); } else { + List ttLessons = List.empty(growable: true); for (var i = 0; i < events.length; i++) { var event = events[i]; - ttBody.add( + ttLessons.add( FirkaCard( left: [ Text( @@ -85,43 +87,71 @@ class TimeTableDayWidget extends StatelessWidget { ), ); } + + var showBreak = data.settings + .group("settings") + .subGroup("timetable_toast") + .boolean("breaks"); + for (var i = 0; i < lessons.length; i++) { var lesson = lessons[i]; - Lesson? nextLesson = lessons.length > i + 1 ? lessons[i + 1] : null; - ttBody.add( + var nextLesson = lessons.length > i + 1 ? lessons[i + 1] : null; + ttLessons.add( LessonWidget( data, - week, - day, lessons.getLessonNo(lesson), lesson, tests.firstWhereOrNull( (test) => test.lessonNumber == lesson.lessonNumber, ), - nextLesson, + ), + ); + + if (!showBreak || nextLesson == null) { + continue; + } + + var breakMins = nextLesson.start.difference(lesson.end).inMinutes; + ttLessons.add( + FirkaCard( + color: appStyle.colors.cardTranslucent, + margin: EdgeInsets.all(0), + padding: EdgeInsets.symmetric(vertical: 11, horizontal: 16), + shadow: false, + left: [ + Text( + initData.l10n.breakTxt, + style: appStyle.fonts.B_14SB.copyWith( + color: appStyle.colors.textSecondary, + ), + ), + ], + right: [ + Text( + "$breakMins ${breakMins > 1 ? initData.l10n.starting_min_plural : initData.l10n.starting_min}", + style: appStyle.fonts.B_14R.copyWith( + color: appStyle.colors.textSecondary, + ), + ), + ], ), ); } + + ttBody = Padding( + padding: const EdgeInsets.only(top: 70 + 16 + 20, left: 20, right: 20), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 16, + children: [...ttLessons, SizedBox(height: 55)], + ), + ), + ); } - return SizedBox( - width: MediaQuery.of(context).size.width / 1.1, - child: ttBody.isEmpty - ? noLessonsWidget - : Padding( - padding: const EdgeInsets.only( - top: 70 + 16 + 20, - left: 4, - right: 4, - ), - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [...ttBody, SizedBox(height: 24)], - ), - ), - ), - ); + return ttBody; } }