-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/same day exams display (#1059)
- Loading branch information
Showing
5 changed files
with
195 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,169 +1,100 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:uni/controller/local_storage/preferences_controller.dart'; | ||
import 'package:uni/generated/l10n.dart'; | ||
import 'package:uni/model/entities/app_locale.dart'; | ||
import 'package:uni/model/entities/exam.dart'; | ||
import 'package:uni/model/providers/lazy/exam_provider.dart'; | ||
import 'package:uni/utils/drawer_items.dart'; | ||
import 'package:uni/view/common_widgets/date_rectangle.dart'; | ||
import 'package:uni/view/common_widgets/generic_card.dart'; | ||
import 'package:uni/view/common_widgets/row_container.dart'; | ||
import 'package:uni/view/exams/widgets/exam_row.dart'; | ||
import 'package:uni/view/exams/widgets/exam_title.dart'; | ||
import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; | ||
import 'package:uni/view/lazy_consumer.dart'; | ||
import 'package:uni/view/locale_notifier.dart'; | ||
|
||
/// Manages the exam card section inside the personal area. | ||
class ExamCard extends GenericCard { | ||
ExamCard({super.key}); | ||
|
||
const ExamCard.fromEditingInformation( | ||
super.key, { | ||
required super.editingMode, | ||
super.onDelete, | ||
}) : super.fromEditingInformation(); | ||
|
||
@override | ||
String getTitle(BuildContext context) => | ||
S.of(context).nav_title(DrawerItem.navExams.title); | ||
|
||
@override | ||
Future<Object?> onClick(BuildContext context) => | ||
Navigator.pushNamed(context, '/${DrawerItem.navExams.title}'); | ||
|
||
@override | ||
void onRefresh(BuildContext context) { | ||
Provider.of<ExamProvider>(context, listen: false).forceRefresh(context); | ||
} | ||
|
||
/// Returns a widget with all the exams card content. | ||
/// | ||
/// If there are no exams, a message telling the user | ||
/// that no exams exist is displayed. | ||
@override | ||
Widget buildCardContent(BuildContext context) { | ||
return LazyConsumer<ExamProvider, List<Exam>>( | ||
builder: (context, exams) { | ||
return generateExams(shownExams(exams), context); | ||
}, | ||
hasContent: (exams) => shownExams(exams).isNotEmpty, | ||
onNullContent: Center( | ||
child: Text( | ||
S.of(context).no_selected_exams, | ||
style: Theme.of(context).textTheme.titleLarge, | ||
), | ||
), | ||
contentLoadingWidget: const ExamCardShimmer().build(context), | ||
); | ||
} | ||
|
||
List<Exam> shownExams(List<Exam> exams) { | ||
final filteredExams = PreferencesController.getFilteredExams(); | ||
final hiddenExams = PreferencesController.getHiddenExams(); | ||
|
||
return exams | ||
.where( | ||
(exam) => | ||
!hiddenExams.contains(exam.id) && | ||
(filteredExams[Exam.getExamTypeLong(exam.type)] ?? true), | ||
) | ||
.toList(); | ||
} | ||
|
||
/// Returns a widget with all the exams. | ||
Widget generateExams(List<Exam> exams, BuildContext context) { | ||
return Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: getExamRows(context, exams), | ||
); | ||
} | ||
|
||
/// Returns a list of widgets with the primary and secondary exams to | ||
/// be displayed in the exam card. | ||
List<Widget> getExamRows(BuildContext context, List<Exam> exams) { | ||
final rows = <Widget>[]; | ||
for (var i = 0; i < 1 && i < exams.length; i++) { | ||
rows.add(createRowFromExam(context, exams[i])); | ||
} | ||
if (exams.length > 1) { | ||
rows.add( | ||
Container( | ||
margin: | ||
const EdgeInsets.only(right: 80, left: 80, top: 15, bottom: 7), | ||
decoration: BoxDecoration( | ||
border: Border( | ||
bottom: BorderSide( | ||
width: 1.5, | ||
color: Theme.of(context).dividerColor, | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
for (var i = 1; i < 4 && i < exams.length; i++) { | ||
rows.add(createSecondaryRowFromExam(context, exams[i])); | ||
} | ||
return rows; | ||
} | ||
|
||
/// Creates a row with the closest exam (which appears separated from the | ||
/// others in the card). | ||
Widget createRowFromExam(BuildContext context, Exam exam) { | ||
final locale = Provider.of<LocaleNotifier>(context).getLocale(); | ||
return Column( | ||
children: [ | ||
if (locale == AppLocale.pt) ...[ | ||
DateRectangle( | ||
date: '''${exam.weekDay(locale)}, ''' | ||
'''${exam.begin.day} de ${exam.month(locale)}''', | ||
), | ||
] else ...[ | ||
DateRectangle( | ||
date: '''${exam.weekDay(locale)}, ''' | ||
'''${exam.begin.day} ${exam.month(locale)}''', | ||
), | ||
], | ||
RowContainer( | ||
child: ExamRow( | ||
exam: exam, | ||
teacher: '', | ||
mainPage: true, | ||
onChangeVisibility: () {}, | ||
), | ||
), | ||
], | ||
); | ||
} | ||
|
||
/// Creates a row for the exams which will be displayed under the closest | ||
/// date exam with a separator between them. | ||
Widget createSecondaryRowFromExam(BuildContext context, Exam exam) { | ||
final locale = Provider.of<LocaleNotifier>(context).getLocale(); | ||
return Container( | ||
margin: const EdgeInsets.only(top: 8), | ||
child: RowContainer( | ||
color: Theme.of(context).colorScheme.background, | ||
child: Container( | ||
padding: const EdgeInsets.all(11), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: <Widget>[ | ||
Text( | ||
'${exam.begin.day} de ${exam.month(locale)}', | ||
style: Theme.of(context).textTheme.bodyLarge, | ||
), | ||
ExamTitle( | ||
subject: exam.subject, | ||
type: exam.type, | ||
reverseOrder: true, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
import 'package:flutter/material.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:uni/generated/l10n.dart'; | ||
import 'package:uni/model/entities/exam.dart'; | ||
import 'package:uni/model/providers/lazy/exam_provider.dart'; | ||
import 'package:uni/utils/drawer_items.dart'; | ||
import 'package:uni/view/common_widgets/generic_card.dart'; | ||
import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; | ||
import 'package:uni/view/home/widgets/next_exams_card.dart'; | ||
import 'package:uni/view/home/widgets/remaining_exams_card.dart'; | ||
import 'package:uni/view/lazy_consumer.dart'; | ||
|
||
class ExamCard extends GenericCard { | ||
ExamCard({super.key}); | ||
|
||
const ExamCard.fromEditingInformation( | ||
super.key, { | ||
required super.editingMode, | ||
super.onDelete, | ||
}) : super.fromEditingInformation(); | ||
|
||
static const int maxExamsToDisplay = 4; | ||
|
||
@override | ||
String getTitle(BuildContext context) => | ||
S.of(context).nav_title(DrawerItem.navExams.title); | ||
|
||
@override | ||
Future<Object?> onClick(BuildContext context) => | ||
Navigator.pushNamed(context, '/${DrawerItem.navExams.title}'); | ||
|
||
@override | ||
void onRefresh(BuildContext context) { | ||
Provider.of<ExamProvider>(context, listen: false).forceRefresh(context); | ||
} | ||
|
||
@override | ||
Widget buildCardContent(BuildContext context) { | ||
return LazyConsumer<ExamProvider, List<Exam>>( | ||
builder: (context, allExams) { | ||
final nextExams = getPrimaryExams( | ||
allExams, | ||
); | ||
return Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
NextExamsWidget(exams: nextExams), | ||
if (nextExams.length < maxExamsToDisplay && | ||
allExams.length > nextExams.length) | ||
Column( | ||
children: [ | ||
Container( | ||
margin: const EdgeInsets.only( | ||
right: 80, | ||
left: 80, | ||
top: 7, | ||
bottom: 7, | ||
), | ||
decoration: BoxDecoration( | ||
border: Border( | ||
bottom: BorderSide( | ||
color: Theme.of(context).dividerColor, | ||
), | ||
), | ||
), | ||
), | ||
RemainingExamsWidget( | ||
exams: allExams | ||
.where((exam) => !nextExams.contains(exam)) | ||
.take(maxExamsToDisplay - nextExams.length) | ||
.toList(), | ||
), | ||
], | ||
), | ||
], | ||
); | ||
}, | ||
hasContent: (allExams) => allExams.isNotEmpty, | ||
onNullContent: Center( | ||
child: Text( | ||
S.of(context).no_selected_exams, | ||
style: Theme.of(context).textTheme.titleLarge, | ||
), | ||
), | ||
contentLoadingWidget: const ExamCardShimmer(), | ||
); | ||
} | ||
|
||
List<Exam> getPrimaryExams(List<Exam> allExams) { | ||
return allExams | ||
.where((exam) => isSameDay(allExams[0].begin, exam.begin)) | ||
.toList(); | ||
} | ||
|
||
bool isSameDay(DateTime? dateA, DateTime? dateB) { | ||
return dateA?.year == dateB?.year && | ||
dateA?.month == dateB?.month && | ||
dateA?.day == dateB?.day; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:uni/model/entities/app_locale.dart'; | ||
import 'package:uni/model/entities/exam.dart'; | ||
import 'package:uni/view/common_widgets/date_rectangle.dart'; | ||
import 'package:uni/view/common_widgets/row_container.dart'; | ||
import 'package:uni/view/exams/widgets/exam_row.dart'; | ||
import 'package:uni/view/locale_notifier.dart'; | ||
|
||
class NextExamsWidget extends StatelessWidget { | ||
const NextExamsWidget({required this.exams, super.key}); | ||
|
||
final List<Exam> exams; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Column( | ||
crossAxisAlignment: CrossAxisAlignment.stretch, | ||
children: [ | ||
DateRectangle( | ||
date: exams.isNotEmpty ? getFormattedDate(exams.first, context) : '', | ||
), | ||
Column( | ||
children: exams.map((exam) { | ||
return Padding( | ||
padding: const EdgeInsets.only(bottom: 8), | ||
child: RowContainer( | ||
child: ExamRow( | ||
exam: exam, | ||
teacher: '', | ||
mainPage: true, | ||
onChangeVisibility: () {}, | ||
), | ||
), | ||
); | ||
}).toList(), | ||
), | ||
], | ||
); | ||
} | ||
|
||
String getFormattedDate(Exam exam, BuildContext context) { | ||
final locale = Provider.of<LocaleNotifier>(context).getLocale(); | ||
return locale == AppLocale.pt | ||
? '${exam.weekDay(locale)}, ${exam.begin.day} de ${exam.month(locale)}' | ||
: '${exam.weekDay(locale)}, ${exam.begin.day} ${exam.month(locale)}'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:uni/model/entities/exam.dart'; | ||
import 'package:uni/view/common_widgets/row_container.dart'; | ||
import 'package:uni/view/exams/widgets/exam_title.dart'; | ||
import 'package:uni/view/locale_notifier.dart'; | ||
|
||
class RemainingExamsWidget extends StatelessWidget { | ||
const RemainingExamsWidget({required this.exams, super.key}); | ||
|
||
final List<Exam> exams; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Column( | ||
children: exams.map((exam) { | ||
final locale = Provider.of<LocaleNotifier>(context).getLocale(); | ||
return Container( | ||
margin: const EdgeInsets.only(top: 8), | ||
child: RowContainer( | ||
color: Theme.of(context).colorScheme.background, | ||
child: Container( | ||
padding: const EdgeInsets.all(11), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: <Widget>[ | ||
Text( | ||
'${exam.begin.day} ${exam.month(locale)}', | ||
style: Theme.of(context).textTheme.bodyLarge, | ||
), | ||
ExamTitle( | ||
subject: exam.subject, | ||
type: exam.type, | ||
reverseOrder: true, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
}).toList(), | ||
); | ||
} | ||
} |