Skip to content

Commit

Permalink
Feature/same day exams display (#1059)
Browse files Browse the repository at this point in the history
  • Loading branch information
thePeras authored Feb 7, 2024
2 parents 7246f7a + 8054160 commit 613e378
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 171 deletions.
3 changes: 2 additions & 1 deletion uni/lib/model/entities/exam.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ class Exam {

@override
bool operator ==(Object other) =>
identical(this, other) || other is Exam && id == other.id;
identical(this, other) ||
other is Exam && id == other.id && subject == other.subject;

@override
int get hashCode => id.hashCode;
Expand Down
2 changes: 1 addition & 1 deletion uni/lib/view/exams/widgets/exam_row.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class _ExamRowState extends State<ExamRow> {
}

Widget? getExamRooms(BuildContext context) {
if (widget.exam.rooms[0] == '') return null;
if (widget.exam.rooms.isEmpty || widget.exam.rooms[0] == '') return null;
return Wrap(
spacing: 13,
children: roomsList(context, widget.exam.rooms),
Expand Down
269 changes: 100 additions & 169 deletions uni/lib/view/home/widgets/exam_card.dart
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;
}
}
48 changes: 48 additions & 0 deletions uni/lib/view/home/widgets/next_exams_card.dart
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)}';
}
}
44 changes: 44 additions & 0 deletions uni/lib/view/home/widgets/remaining_exams_card.dart
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(),
);
}
}

0 comments on commit 613e378

Please sign in to comment.