From 613b66bc2234d7580a2f3175d2bbf803d73577c8 Mon Sep 17 00:00:00 2001 From: checkedear <271323618+checkedear@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:21:36 +0200 Subject: [PATCH] ref: lesson widget --- firka/lib/ui/phone/pages/home/home_main.dart | 3 +- firka/lib/ui/phone/widgets/lesson.dart | 101 +-- firka/lib/ui/phone/widgets/lesson_big.dart | 689 ++++++------------- 3 files changed, 215 insertions(+), 578 deletions(-) diff --git a/firka/lib/ui/phone/pages/home/home_main.dart b/firka/lib/ui/phone/pages/home/home_main.dart index fc261e3..6101c91 100644 --- a/firka/lib/ui/phone/pages/home/home_main.dart +++ b/firka/lib/ui/phone/pages/home/home_main.dart @@ -307,13 +307,12 @@ class _HomeMainScreen extends FirkaState { ...lessonTestMap.entries.map( (entry) => Padding( padding: EdgeInsets.symmetric(horizontal: 5), - child: LessonWidget( + child: LessonBigWidget( widget.data, lessons!.getLessonNo(entry.key), entry.key, entry.value, active: currentLesson == entry.key, - expanded: true, ), ), ), diff --git a/firka/lib/ui/phone/widgets/lesson.dart b/firka/lib/ui/phone/widgets/lesson.dart index e1bc970..6f3f7d9 100644 --- a/firka/lib/ui/phone/widgets/lesson.dart +++ b/firka/lib/ui/phone/widgets/lesson.dart @@ -21,7 +21,6 @@ class LessonWidget extends StatelessWidget { final AppInitialization data; final int? lessonNo; final Lesson lesson; - final bool expanded; final bool active; final Test? test; @@ -31,7 +30,6 @@ class LessonWidget extends StatelessWidget { this.lesson, this.test, { this.active = false, - this.expanded = false, super.key, }); @@ -78,8 +76,6 @@ class LessonWidget extends StatelessWidget { var roomName = lesson.roomName ?? 'N/A'; - final spacing = expanded ? 8.0 : 12.0; - elements.add( GestureDetector( onTap: () { @@ -96,16 +92,14 @@ class LessonWidget extends StatelessWidget { ); }, child: FirkaCard.single( - height: expanded ? 104 : 64, + height: 64, borderColor: active ? appStyle.colors.accent : null, margin: EdgeInsets.all(0), - padding: EdgeInsets.only(left: expanded ? 16 : 14, right: 16), + padding: EdgeInsets.only(left: 14, right: 16), color: isDismissed ? appStyle.colors.cardTranslucent : appStyle.colors.card, - attached: !expanded && showTests && test != null - ? Attach.bottom - : Attach.none, + attached: showTests && test != null ? Attach.bottom : Attach.none, shadow: !isDismissed, child: Column( spacing: 12, @@ -138,19 +132,19 @@ class LessonWidget extends StatelessWidget { ), ), FilledCircle( - diameter: expanded ? 32 : 36, + diameter: 36, color: bgColor, child: ClassIconWidget( uid: lesson.uid, className: lesson.name, category: subjectName, color: accent, - size: expanded ? 20 : 24, + size: 24, ), ), SizedOverflowBox( - size: Size(spacing, 0), - child: !expanded && !showTests && test != null + size: Size(12, 0), + child: !showTests && test != null ? Transform.translate( offset: Offset(4, -20), child: BubbleTest(), @@ -160,7 +154,7 @@ class LessonWidget extends StatelessWidget { Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: spacing, + spacing: 12, children: [ LimitedBox( maxWidth: 155, @@ -227,92 +221,23 @@ class LessonWidget extends StatelessWidget { color: appStyle.colors.textPrimary, ), ), - if (!expanded) - Text( - lesson.end.toLocal().format( - data.l10n, - FormatMode.hmm, - ), - style: appStyle.fonts.B_14R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - ], - ), - if (expanded && test == null) - Column( - spacing: 4, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - lesson.end - .difference(timeNow().max(lesson.start)) - .timeLeft(initData.l10n) ?? - "", - style: appStyle.fonts.B_14R.apply( - color: appStyle.colors.textSecondary, - ), - ), - Text( - lesson.end.format(initData.l10n, FormatMode.hmm), - style: appStyle.fonts.B_14R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ClipRRect( - borderRadius: BorderRadius.circular(16), - child: LinearProgressIndicator( - value: - timeNow().difference(lesson.start).inMilliseconds / - lesson.end.difference(lesson.start).inMilliseconds, - backgroundColor: appStyle.colors.a15p, - color: appStyle.colors.accent, - minHeight: 8, - ), - ), - ], - ), - if (expanded && test != null) - Container( - height: 28, - padding: EdgeInsets.symmetric(horizontal: 6), - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14), - ), - color: appStyle.colors.background, - ), - child: Row( - children: [ - FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.editPen4Solid, - size: 12, - color: appStyle.colors.accent, - ), - SizedBox(width: 8), Text( - test!.theme, - style: appStyle.fonts.B_16R.apply( + lesson.end.toLocal().format(data.l10n, FormatMode.hmm), + style: appStyle.fonts.B_14R.apply( color: appStyle.colors.textPrimary, ), ), ], ), - ), + ], + ), ], ), ), ), ); - if (!expanded && test != null && showTests) { + if (test != null && showTests) { var theme = test!.theme.firstUpper(); var method = test!.method.description.firstUpper(); diff --git a/firka/lib/ui/phone/widgets/lesson_big.dart b/firka/lib/ui/phone/widgets/lesson_big.dart index d3c5654..3570d81 100644 --- a/firka/lib/ui/phone/widgets/lesson_big.dart +++ b/firka/lib/ui/phone/widgets/lesson_big.dart @@ -1,554 +1,267 @@ -import 'package:kreta_api/kreta_api.dart'; import 'package:firka/core/extensions.dart'; +import 'package:firka/core/settings.dart'; import 'package:firka/ui/components/firka_card.dart'; -import 'package:firka/l10n/app_localizations.dart'; +import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/shared/firka_icon.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'; +import 'package:kreta_api/kreta_api.dart'; +import 'package:firka/core/debug_helper.dart'; +import 'package:firka/ui/components/common_bottom_sheets.dart'; import 'package:firka/ui/shared/class_icon.dart'; +import 'package:firka/ui/shared/firka_icon.dart'; +import 'bubble_test.dart'; class LessonBigWidget extends StatelessWidget { - final AppLocalizations l10n; - final DateTime now; - final int? lessonNo; - final Lesson? lesson; - final Lesson? prevLesson; - final Lesson? nextLesson; - final List lessons; - final List tests; + final AppInitialization data; + final int lessonNo; + final Lesson lesson; + final Test? test; + final bool active; const LessonBigWidget( - this.l10n, - this.now, + this.data, this.lessonNo, this.lesson, - this.prevLesson, - this.nextLesson, - this.lessons, - this.tests, { + this.test, { + this.active = false, super.key, }); @override Widget build(BuildContext context) { - var hasLesson = lesson != null; - var lessonsLeft = lessons.where((lesson) => lesson.end.isAfter(now)).length; - var hasPrevLesson = prevLesson != null; - var hasNextLesson = nextLesson != null; - // TODO: holnapi órák száma kiszámolás - var lessonsTomorrow = 0; + final isSubstituted = lesson.substituteTeacher != null; + final isDismissed = lesson.type.name == "UresOra"; - var testsTomorrow = tests - .where( - (test) => - test.date.isAfter(now) && - test.date.isBefore(DateTime(now.year, now.month, now.day + 2)), - ) - .length; + var accent = appStyle.colors.accent; + var secondary = appStyle.colors.secondary; + var bgColor = appStyle.colors.a15p; - if (lessonsLeft < 1) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FirkaCard( - left: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + if (isSubstituted) { + accent = appStyle.colors.warningAccent; + secondary = appStyle.colors.warningText; + bgColor = appStyle.colors.warning15p; + } + if (isDismissed) { + accent = appStyle.colors.errorAccent; + secondary = appStyle.colors.errorText; + bgColor = appStyle.colors.error15p; + } + + var subjectName = lesson.subject?.name.firstUpper() ?? 'N/A'; + + var roomName = lesson.roomName ?? 'N/A'; + + Widget extra = test == null + ? Column( + spacing: 4, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - SizedBox( - width: 40, - height: 40, - child: Stack( - children: [ - Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - child: Padding( - padding: EdgeInsets.all(6), - child: FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.moonSolid, - size: 32.0, - color: appStyle.colors.accent, - ), - ), - ), - ], - ), - ), - Text( - testsTomorrow == 0 - ? l10n.tt_no_classes_l2 - : l10n.get_ready, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], + Text( + (timeNow().isAfter(lesson.start) + ? lesson.end + .difference(timeNow()) + .timeLeft(initData.l10n) + : null) ?? + lesson.start.format(data.l10n, FormatMode.hmm), + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textSecondary, + ), + ), + Text( + lesson.end.format(initData.l10n, FormatMode.hmm), + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textSecondary, + ), ), ], ), + ClipRRect( + borderRadius: BorderRadius.circular(16), + child: LinearProgressIndicator( + value: + timeNow().difference(lesson.start).inMilliseconds / + lesson.end.difference(lesson.start).inMilliseconds, + backgroundColor: appStyle.colors.a15p, + color: appStyle.colors.accent, + minHeight: 8, + ), + ), ], - extra: Column( + ) + : Container( + height: 28, + padding: EdgeInsets.symmetric(horizontal: 6), + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + color: appStyle.colors.background, + ), + child: Row( children: [ - SizedBox(height: 4), - ClipRRect( - borderRadius: BorderRadius.circular(360), - child: Container( - width: double.infinity, - color: appStyle.colors.background, - padding: EdgeInsets.symmetric(vertical: 6, horizontal: 12), - child: Row( - children: [ - SizedBox( - width: 20, - height: 20, - child: Stack( - children: [ - Padding( - padding: EdgeInsets.all(2), - child: FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.editPen4Solid, - size: 32.0, - color: appStyle.colors.accent, - ), - ), - ], - ), - ), - SizedBox(width: 8), - Text( - (lessonsTomorrow == 0 && testsTomorrow == 0) - ? l10n.no_tests_tomorrow - : (testsTomorrow > 1) - ? l10n.tests_tomorrow(testsTomorrow.toString()) - : (testsTomorrow < 1 && lessonsTomorrow > 0) - ? l10n.lessons_tomorrow( - lessonsTomorrow.toString(), - ) - : l10n.tests_tomorrow(testsTomorrow.toString()), - textAlign: TextAlign.left, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), + FirkaIconWidget( + FirkaIconType.majesticons, + Majesticon.editPen4Solid, + size: 12, + color: appStyle.colors.accent, + ), + SizedBox(width: 8), + Text( + test!.theme, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textPrimary, ), + overflow: TextOverflow.ellipsis, ), ], ), - ), - ], - ); - } - if (!hasLesson && (!hasPrevLesson || !hasNextLesson)) { - if (!hasPrevLesson && !hasNextLesson) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FirkaCard( - left: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - child: Padding( - padding: EdgeInsets.all(4), - child: FirkaIconWidget( - FirkaIconType.majesticons, - 'cupFilled', - color: appStyle.colors.accent, - size: 24, - ), - ), - ), - Text( - l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - ], - ), - ], - right: [ - Column( - children: [ - Row( - children: [ - Text( - '-', - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( - children: [ - Text( - '-', - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - ], - ), - ], - extra: SizedBox.shrink(), - ), - ], + ); + + return GestureDetector( + onTap: () { + showLessonBottomSheet( + context, + data, + lesson, + lessonNo, + accent, + secondary, + bgColor, + test, ); - } - - // Before the first lesson: prev missing but next present. Show countdown - // to the next lesson using nextLesson data. - if (!hasPrevLesson && hasNextLesson) { - var timeLeft = nextLesson!.start.difference(now); - var timeLeftStr = l10n.timeLeft(timeLeft.inMinutes + 1); - - return Column( - crossAxisAlignment: CrossAxisAlignment.center, + }, + child: FirkaCard.single( + height: 104, + borderColor: active ? appStyle.colors.accent : null, + margin: EdgeInsets.all(0), + padding: EdgeInsets.only(left: 16, right: 16), + color: isDismissed + ? appStyle.colors.cardTranslucent + : appStyle.colors.card, + shadow: !isDismissed, + child: Column( + spacing: 12, + mainAxisAlignment: MainAxisAlignment.center, children: [ - FirkaCard( - left: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - child: Padding( - padding: EdgeInsets.all(4), - child: FirkaIconWidget( - FirkaIconType.majesticonsLocal, - 'cupFilled', - color: appStyle.colors.accent, - size: 24, - ), - ), - ), - Text( - l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( - children: [ - Text( - timeLeftStr, - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ], - ), - ], - right: [ - Column( - children: [ - Row( - children: [ - Text( - '-', - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( - children: [ - Text( - nextLesson!.start.toLocal().format( - l10n, - FormatMode.hmm, - ), - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - ], - ), - ], - extra: SizedBox.shrink(), - ), - ], - ); - } - - // After the last lesson: next missing but prev present. Show a simple - // "no more lessons" style card with the previous lesson end time. - if (hasPrevLesson && !hasNextLesson) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // TODO: implement home/today afternoon - ], - ); - } - } - - if (hasLesson) { - var timeLeft = lesson!.end.difference(now); - var duration = lesson!.end.difference(lesson!.start).inMilliseconds; - var progress = now.difference(lesson!.start).inMilliseconds; - - var minsLeft = timeLeft.inMinutes; - var secsLeft = timeLeft.inSeconds; - - var timeLeftStr = - "$minsLeft ${minsLeft == 1 ? l10n.starting_min : l10n.starting_min_plural}"; - if (minsLeft < 1) { - timeLeftStr = - "$secsLeft ${secsLeft == 1 ? l10n.starting_sec : l10n.starting_sec_plural}"; - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FirkaCard( - left: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + Row( + children: [ + SizedBox( + width: 18, + height: 18, + child: Stack( + alignment: Alignment.center, children: [ - SizedBox( + SvgPicture.asset( + "assets/icons/subtract.svg", + color: bgColor, width: 18, height: 18, - child: Stack( + ), + Text( + lessonNo.toString(), + style: appStyle.fonts.B_12R.apply(color: secondary), + textAlign: TextAlign.center, + ), + ], + ), + ), + FilledCircle( + diameter: 32, + color: bgColor, + child: ClassIconWidget( + uid: lesson.uid, + className: lesson.name, + category: subjectName, + color: accent, + size: 20, + ), + ), + SizedBox(width: 8), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8, + children: [ + LimitedBox( + maxWidth: 155, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - SvgPicture.asset( - "assets/icons/subtract.svg", - color: appStyle.colors.a15p, - width: 18, - height: 18, - ), - Padding( - padding: EdgeInsets.only(left: 5), - child: Text( - lessonNo.toString(), - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.secondary, - ), + Text( + subjectName, + style: appStyle.fonts.B_16SB.apply( + color: !isDismissed + ? appStyle.colors.textPrimary + : appStyle.colors.textSecondary, ), + overflow: TextOverflow.ellipsis, ), + if (isSubstituted) + Text( + lesson.substituteTeacher!, + style: appStyle.fonts.B_14R.apply( + color: appStyle.colors.textSecondary, + ), + overflow: TextOverflow.ellipsis, + ), ], ), ), - Transform.translate( - offset: Offset(-4, 0), + Flexible( + fit: FlexFit.loose, child: Card( shadowColor: Colors.transparent, color: appStyle.colors.a15p, + margin: EdgeInsets.all(0), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.circular(10), ), child: Padding( - padding: EdgeInsets.all(4), - child: ClassIconWidget( - color: appStyle.colors.accent, - size: 24, - uid: lesson!.uid, - className: lesson!.name, - category: lesson!.subject?.name ?? '', - ), - ), - ), - ), - Text( - lesson!.subject?.name ?? 'N/A', - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( - children: [ - Text( - timeLeftStr, - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ], - ), - ], - right: [ - Column( - children: [ - Row( - children: [ - Text( - lesson!.start.toLocal().format(l10n, FormatMode.hmm), - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - child: Padding( - padding: EdgeInsets.all(4), - child: Text( - lesson!.roomName ?? '?', - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.secondary, + padding: EdgeInsets.symmetric(horizontal: 6), + child: Text( + roomName, + style: appStyle.fonts.B_12R.apply( + color: appStyle.colors.textSecondary, + ), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, ), ), ), ), ], ), - Row( - children: [ - SizedBox(width: 18), - Text( - lesson!.end.toLocal().format(l10n, FormatMode.hmm), - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ], - ), - ], - extra: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: LinearProgressIndicator( - value: progress / duration, - backgroundColor: appStyle.colors.a15p, - color: appStyle.colors.accent, - minHeight: 8, - ), - ), - ), - ], - ); - } else { - var duration = nextLesson!.start - .difference(prevLesson!.end) - .inMilliseconds; - var progress = - duration - nextLesson!.start.difference(now).inMilliseconds; - var timeLeft = nextLesson!.start.difference(now); - - var timeLeftStr = l10n.timeLeft(timeLeft.inMinutes + 1); - - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FirkaCard( - left: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Card( - shadowColor: Colors.transparent, - color: appStyle.colors.a15p, - child: Padding( - padding: EdgeInsets.all(4), - child: FirkaIconWidget( - FirkaIconType.majesticonsLocal, - 'cupFilled', - color: appStyle.colors.accent, - size: 24, - ), - ), - ), - Text( - l10n.breakTxt, - style: appStyle.fonts.B_16SB.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( + ), + if (test != null) SizedBox(width: 8), + if (test != null) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - timeLeftStr, - style: appStyle.fonts.B_12R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - ], - ), - ], - right: [ - Column( - children: [ - Row( - children: [ - Text( - prevLesson!.end.toLocal().format(l10n, FormatMode.hmm), - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ], - ), - Row( - children: [ - Text( - nextLesson!.start.toLocal().format( - l10n, + lesson.start.toLocal().format( + data.l10n, FormatMode.hmm, ), - style: appStyle.fonts.B_16R.apply( + style: appStyle.fonts.B_14R.apply( color: appStyle.colors.textPrimary, ), ), ], ), - ], - ), - ], - extra: LinearProgressIndicator( - // TODO: Make this rounded - value: progress / duration, - backgroundColor: appStyle.colors.a15p, - color: appStyle.colors.accent, + ], ), - ), - ], - ); - } + extra, + ], + ), + ), + ); } }