Skip to content

Commit

Permalink
Improve course units loading behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
bdmendes committed Sep 28, 2023
1 parent 172d85e commit 01d728a
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 69 deletions.
92 changes: 70 additions & 22 deletions uni/lib/view/common_widgets/pages_layouts/general/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:uni/model/providers/startup/profile_provider.dart';
import 'package:uni/model/providers/startup/session_provider.dart';
import 'package:uni/utils/drawer_items.dart';
Expand All @@ -13,30 +14,64 @@ import 'package:uni/view/profile/profile.dart';
/// Page with a hamburger menu and the user profile picture
abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
final double borderMargin = 18;
bool _loadedOnce = false;
bool _loading = true;

Future<void> onRefresh(BuildContext context);

Future<void> onLoad(BuildContext context) async {}

@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => onLoad(context));
return getScaffold(context, getBody(context));
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (_loadedOnce) {
return;
}
_loadedOnce = true;
setState(() {
_loading = true;
});

try {
await onLoad(context);
} catch (e, stackTrace) {
await Sentry.captureException(e, stackTrace: stackTrace);
}

setState(() {
_loading = false;
});
});

return getScaffold(
context,
_loading
? const Flex(
direction: Axis.vertical,
children: [
Expanded(
child: Center(
child: CircularProgressIndicator(),
),
)
],
)
: getBody(context),
);
}

Widget getBody(BuildContext context) {
return Container();
}

Future<DecorationImage> buildProfileDecorationImage(
BuildContext context, {
Future<DecorationImage> buildProfileDecorationImage(BuildContext context, {
bool forceRetrieval = false,
}) async {
final sessionProvider =
Provider.of<SessionProvider>(context, listen: false);
Provider.of<SessionProvider>(context, listen: false);
await sessionProvider.ensureInitializedFromStorage();
final profilePictureFile =
await ProfileProvider.fetchOrGetCachedProfilePicture(
await ProfileProvider.fetchOrGetCachedProfilePicture(
sessionProvider.session,
forceRetrieval: forceRetrieval,
);
Expand All @@ -49,20 +84,23 @@ abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
DecorationImage getProfileDecorationImage(File? profilePicture) {
const fallbackPicture = AssetImage('assets/images/profile_placeholder.png');
final image =
profilePicture == null ? fallbackPicture : FileImage(profilePicture);
profilePicture == null ? fallbackPicture : FileImage(profilePicture);

final result =
DecorationImage(fit: BoxFit.cover, image: image as ImageProvider);
DecorationImage(fit: BoxFit.cover, image: image as ImageProvider);
return result;
}

Widget refreshState(BuildContext context, Widget child) {
return RefreshIndicator(
key: GlobalKey<RefreshIndicatorState>(),
onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture(
Provider.of<SessionProvider>(context, listen: false).session,
forceRetrieval: true,
).then((value) => onRefresh(context)),
onRefresh: () =>
ProfileProvider.fetchOrGetCachedProfilePicture(
Provider
.of<SessionProvider>(context, listen: false)
.session,
forceRetrieval: true,
).then((value) => onRefresh(context)),
child: child,
);
}
Expand All @@ -86,21 +124,30 @@ abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
bottom: PreferredSize(
preferredSize: Size.zero,
child: Container(
color: Theme.of(context).dividerColor,
color: Theme
.of(context)
.dividerColor,
margin: EdgeInsets.only(left: borderMargin, right: borderMargin),
height: 1.5,
),
),
elevation: 0,
iconTheme: Theme.of(context).iconTheme,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
iconTheme: Theme
.of(context)
.iconTheme,
backgroundColor: Theme
.of(context)
.scaffoldBackgroundColor,
titleSpacing: 0,
title: ButtonTheme(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const RoundedRectangleBorder(),
child: TextButton(
onPressed: () {
final currentRouteName = ModalRoute.of(context)!.settings.name;
final currentRouteName = ModalRoute
.of(context)!
.settings
.name;
if (currentRouteName != DrawerItem.navPersonalArea.title) {
Navigator.pushNamed(
context,
Expand All @@ -110,7 +157,9 @@ abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
},
child: SvgPicture.asset(
colorFilter: ColorFilter.mode(
Theme.of(context).primaryColor,
Theme
.of(context)
.primaryColor,
BlendMode.srcIn,
),
'assets/images/logo_dark.svg',
Expand All @@ -128,12 +177,11 @@ abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
Widget getTopRightButton(BuildContext context) {
return FutureBuilder(
future: buildProfileDecorationImage(context),
builder: (
BuildContext context,
AsyncSnapshot<DecorationImage> decorationImage,
) {
builder: (BuildContext context,
AsyncSnapshot<DecorationImage> decorationImage,) {
return TextButton(
onPressed: () => {
onPressed: () =>
{
Navigator.push(
context,
MaterialPageRoute<ProfilePageView>(
Expand Down
73 changes: 29 additions & 44 deletions uni/lib/view/course_unit_info/course_unit_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import 'package:uni/model/providers/lazy/course_units_info_provider.dart';
import 'package:uni/model/providers/startup/session_provider.dart';
import 'package:uni/view/common_widgets/page_title.dart';
import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart';
import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_classes.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_sheet.dart';
import 'package:uni/view/lazy_consumer.dart';

class CourseUnitDetailPageView extends StatefulWidget {
const CourseUnitDetailPageView(this.courseUnit, {super.key});

final CourseUnit courseUnit;

@override
Expand Down Expand Up @@ -91,50 +90,36 @@ class CourseUnitDetailPageViewState
}

Widget _courseUnitSheetView(BuildContext context) {
return LazyConsumer<CourseUnitsInfoProvider>(
builder: (context, courseUnitsInfoProvider) {
return RequestDependentWidgetBuilder(
onNullContent: Center(
child: Text(
S.of(context).no_info,
textAlign: TextAlign.center,
),
),
status: courseUnitsInfoProvider.status,
builder: () => CourseUnitSheetView(
courseUnitsInfoProvider.courseUnitsSheets[widget.courseUnit]!,
),
hasContentPredicate:
courseUnitsInfoProvider.courseUnitsSheets[widget.courseUnit] !=
null &&
courseUnitsInfoProvider.courseUnitsSheets[widget.courseUnit]!
.sections.isNotEmpty,
);
},
);
final sheet = context
.read<CourseUnitsInfoProvider>()
.courseUnitsSheets[widget.courseUnit];

if (sheet == null || sheet.sections.isEmpty) {
return Center(
child: Text(
S.of(context).no_info,
textAlign: TextAlign.center,
),
);
}

return CourseUnitSheetView(sheet);
}

Widget _courseUnitClassesView(BuildContext context) {
return LazyConsumer<CourseUnitsInfoProvider>(
builder: (context, courseUnitsInfoProvider) {
return RequestDependentWidgetBuilder(
onNullContent: Center(
child: Text(
S.of(context).no_class,
textAlign: TextAlign.center,
),
),
status: courseUnitsInfoProvider.status,
builder: () => CourseUnitClassesView(
courseUnitsInfoProvider.courseUnitsClasses[widget.courseUnit]!,
),
hasContentPredicate:
courseUnitsInfoProvider.courseUnitsClasses[widget.courseUnit] !=
null &&
courseUnitsInfoProvider
.courseUnitsClasses[widget.courseUnit]!.isNotEmpty,
);
},
);
final classes = context
.read<CourseUnitsInfoProvider>()
.courseUnitsClasses[widget.courseUnit];

if (classes == null || classes.isEmpty) {
return Center(
child: Text(
S.of(context).no_class,
textAlign: TextAlign.center,
),
);
}

return CourseUnitClassesView(classes);
}
}
4 changes: 1 addition & 3 deletions uni/lib/view/lazy_consumer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ class LazyConsumer<T extends StateProviderNotifier> extends StatelessWidget {

// Finally, complete provider initialization
if (context.mounted) {
// This will fail if the session initialization failed.
// That is the expected behavior.
await sessionFuture!.then((_) async {
await sessionFuture?.then((_) async {
await provider!.ensureInitializedFromRemote(context);
});
}
Expand Down

0 comments on commit 01d728a

Please sign in to comment.