Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add skeletons #1015

Merged
merged 14 commits into from
Jul 11, 2024
Merged
2 changes: 1 addition & 1 deletion l10n/intl_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@
"percentage": {}
}
},
"progress_bar_message_remaining_days": "{remainingDays} jours restant",
"progress_bar_message_remaining_days": "{remainingDays} jours restants",
"@progress_bar_message_remaining_days": {
"placeholders": {
"remainingDays": {}
Expand Down
1 change: 1 addition & 0 deletions lib/features/app/widgets/base_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class _BaseScaffoldState extends State<BaseScaffold> {
Widget bodyPortraitMode() {
return SafeArea(
top: false,
bottom: widget._safeArea,
child: Stack(
children: [
widget.body!,
Expand Down
221 changes: 135 additions & 86 deletions lib/features/dashboard/dashboard_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:notredame/features/app/signets-api/models/course.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:stacked/stacked.dart';

// Project imports:
Expand Down Expand Up @@ -80,7 +82,7 @@ class _DashboardViewState extends State<DashboardView>
child: ReorderableListView(
onReorder: (oldIndex, newIndex) =>
onReorder(model, oldIndex, newIndex),
padding: const EdgeInsets.fromLTRB(0, 4, 0, 8),
padding: const EdgeInsets.fromLTRB(0, 4, 0, 24),
children: _buildCards(model),
proxyDecorator: (child, _, __) {
return HapticsContainer(child: child);
Expand Down Expand Up @@ -213,7 +215,6 @@ class _DashboardViewState extends State<DashboardView>
Widget _buildProgressBarCard(
DashboardViewModel model, PreferencesFlag flag) =>
DismissibleCard(
isBusy: model.busy(model.progress),
key: UniqueKey(),
onDismissed: (DismissDirection direction) {
dismissCard(model, flag);
Expand All @@ -226,47 +227,52 @@ class _DashboardViewState extends State<DashboardView>
child: Text(AppIntl.of(context)!.progress_bar_title,
style: Theme.of(context).textTheme.titleLarge),
)),
if (model.progress >= 0.0)
Stack(children: [
Container(
padding: const EdgeInsets.fromLTRB(17, 10, 15, 20),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: GestureDetector(
onTap: () => setState(
() => setState(() {
model.changeProgressBarText();
setText(model);
}),
),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 30,
valueColor: const AlwaysStoppedAnimation<Color>(
AppTheme.gradeGoodMax),
backgroundColor: AppTheme.etsDarkGrey,
if (model.busy(model.progress) || model.progress >= 0.0)
Skeletonizer(
enabled: model.busy(model.progress),
ignoreContainers: true,
effect: const ShimmerEffect(),
child: Stack(children: [
Container(
padding: const EdgeInsets.fromLTRB(17, 10, 15, 20),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: GestureDetector(
onTap: () => setState(
() => setState(() {
model.changeProgressBarText();
setText(model);
}),
),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 30,
valueColor: const AlwaysStoppedAnimation<Color>(
AppTheme.gradeGoodMax),
backgroundColor: AppTheme.etsDarkGrey,
),
),
),
),
),
GestureDetector(
onTap: () => setState(() {
model.changeProgressBarText();
setText(model);
}),
child: Container(
padding: const EdgeInsets.only(top: 16),
child: Center(
child: progressBarText ??
Text(
AppIntl.of(context)!.progress_bar_message(
model.sessionDays[0], model.sessionDays[1]),
style: const TextStyle(color: Colors.white),
),
GestureDetector(
onTap: () => setState(() {
model.changeProgressBarText();
setText(model);
}),
child: Container(
padding: const EdgeInsets.only(top: 16),
child: Center(
child: progressBarText ??
Text(
AppIntl.of(context)!.progress_bar_message(
model.sessionDays[0], model.sessionDays[1]),
style: const TextStyle(color: Colors.white),
),
),
),
),
),
])
]),
)
else
Container(
padding: const EdgeInsets.all(16),
Expand Down Expand Up @@ -307,11 +313,34 @@ class _DashboardViewState extends State<DashboardView>
Widget _buildScheduleCard(DashboardViewModel model, PreferencesFlag flag) {
var title = AppIntl.of(context)!.title_schedule;
if (model.todayDateEvents.isEmpty && model.tomorrowDateEvents.isNotEmpty) {
title = title + AppIntl.of(context)!.card_schedule_tomorrow;
title += AppIntl.of(context)!.card_schedule_tomorrow;
}
final bool isLoading = model.busy(model.todayDateEvents) ||
model.busy(model.tomorrowDateEvents);

late List<CourseActivity>? courseActivities;
if (isLoading) {
courseActivities = [
CourseActivity(
courseGroup: "APP375-99",
LouisPhilippeHeon marked this conversation as resolved.
Show resolved Hide resolved
courseName: "Développement mobile (ÉTSMobile)",
activityName: '',
activityDescription: '5 à 7',
activityLocation: '100 Génies',
startDateTime: DateTime.now(),
endDateTime: DateTime.now())
];
XavierPaquet-Rapold marked this conversation as resolved.
Show resolved Hide resolved
} else if (model.todayDateEvents.isEmpty) {
if (model.tomorrowDateEvents.isEmpty) {
courseActivities = null;
} else {
courseActivities = model.tomorrowDateEvents;
}
} else {
courseActivities = model.todayDateEvents;
}

return DismissibleCard(
isBusy: model.busy(model.todayDateEvents) ||
model.busy(model.tomorrowDateEvents),
onDismissed: (DismissDirection direction) {
dismissCard(model, flag);
},
Expand All @@ -330,16 +359,14 @@ class _DashboardViewState extends State<DashboardView>
style: Theme.of(context).textTheme.titleLarge),
),
)),
if (model.todayDateEvents.isEmpty)
if (model.tomorrowDateEvents.isEmpty)
SizedBox(
height: 100,
child: Center(
child: Text(AppIntl.of(context)!.schedule_no_event)))
else
_buildEventList(model.tomorrowDateEvents)
if (courseActivities != null)
Skeletonizer(
enabled: isLoading, child: _buildEventList(courseActivities))
else
_buildEventList(model.todayDateEvents)
SizedBox(
height: 100,
child:
Center(child: Text(AppIntl.of(context)!.schedule_no_event)))
]),
),
);
Expand All @@ -359,53 +386,75 @@ class _DashboardViewState extends State<DashboardView>
itemCount: events.length);
}

Widget _buildGradesCards(DashboardViewModel model, PreferencesFlag flag) =>
DismissibleCard(
key: UniqueKey(),
onDismissed: (DismissDirection direction) {
dismissCard(model, flag);
},
isBusy: model.busy(model.courses),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.fromLTRB(17, 15, 0, 0),
child: GestureDetector(
onTap: () => _navigationService
.pushNamedAndRemoveUntil(RouterPaths.student),
child: Text(AppIntl.of(context)!.grades_title,
style: Theme.of(context).textTheme.titleLarge),
),
Widget _buildGradesCards(DashboardViewModel model, PreferencesFlag flag) {
final bool loaded = !model.busy(model.courses);
late List<Course> courses = model.courses;

if (courses.isEmpty && !loaded) {
final Course skeletonCourse = Course(
acronym: " ",
title: "",
group: "",
LouisPhilippeHeon marked this conversation as resolved.
Show resolved Hide resolved
session: "",
programCode: "",
numberOfCredits: 0);
courses = [
skeletonCourse,
skeletonCourse,
skeletonCourse,
skeletonCourse
];
}

return DismissibleCard(
key: UniqueKey(),
onDismissed: (DismissDirection direction) {
dismissCard(model, flag);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.fromLTRB(17, 15, 0, 0),
child: GestureDetector(
onTap: () => _navigationService
.pushNamedAndRemoveUntil(RouterPaths.student),
child: Text(AppIntl.of(context)!.grades_title,
style: Theme.of(context).textTheme.titleLarge),
),
),
if (model.courses.isEmpty)
SizedBox(
height: 100,
child: Center(
child: Text(AppIntl.of(context)!
.grades_msg_no_grades
.split("\n")
.first)),
)
else
Container(
),
if (model.courses.isEmpty && loaded)
SizedBox(
height: 100,
child: Center(
child: Text(AppIntl.of(context)!
.grades_msg_no_grades
.split("\n")
.first)),
)
else
Skeletonizer(
enabled: !loaded,
child: Container(
padding: const EdgeInsets.fromLTRB(17, 10, 15, 10),
child: Wrap(
children: model.courses
children: courses
.map((course) => GradeButton(course,
color:
Theme.of(context).brightness == Brightness.light
? AppTheme.lightThemeBackground
: AppTheme.darkThemeBackground))
.toList(),
),
)
]),
);
),
)
]),
);
}

Widget _buildMessageBroadcastCard(
DashboardViewModel model, PreferencesFlag flag) {
Expand Down
4 changes: 2 additions & 2 deletions lib/features/dashboard/dashboard_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,11 @@ class DashboardViewModel extends FutureViewModel<Map<PreferencesFlag, int>> {
}

Future<List<CourseActivity>> futureToRunSchedule() async {
setBusyForObject(_todayDateEvents, true);
setBusyForObject(_tomorrowDateEvents, true);
try {
var courseActivities =
await _courseRepository.getCoursesActivities(fromCacheOnly: true);
setBusyForObject(_todayDateEvents, true);
setBusyForObject(_tomorrowDateEvents, true);
_todayDateEvents.clear();
_tomorrowDateEvents.clear();
final todayDate = _settingsManager.dateTimeNow;
Expand Down
5 changes: 4 additions & 1 deletion lib/features/dashboard/widgets/course_activity_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:intl/intl.dart';

// Project imports:
import 'package:notredame/features/app/signets-api/models/course_activity.dart';
import 'package:skeletonizer/skeletonizer.dart';

class CourseActivityTile extends StatelessWidget {
/// Course to display
Expand Down Expand Up @@ -51,7 +52,9 @@ class CourseActivityTile extends StatelessWidget {
style: Theme.of(context).textTheme.bodySmall),
],
),
VerticalDivider(color: colorFor(activity.courseName), thickness: 2)
Skeleton.shade(
child: VerticalDivider(
color: colorFor(activity.courseName), thickness: 2))
],
),
);
Expand Down
Loading