Skip to content

Commit

Permalink
fix: Improve Quran Download and Navigation Experience (#1452)
Browse files Browse the repository at this point in the history
* fix: Ensure correct Moshaf type (Hafs) is displayed after download

* fix: display Hafs Quran correctly and remove success dialog

- Set Hafs as default Moshaf type if none is selected.
- Auto-dismiss success dialog on download completion.
- Improved state invalidation for Quran reading updates.
- Added FocusNode for better dialog interaction.
- Optimized resource management with keepAlive and link.close().

* fix: improve Quran Download and Navigation Experience

- Redirect user to Quran reading screen automatically after successful download and extraction of Quran (Hafs).
- Remove the unnecessary "OK" button to confirm Quran download completion, streamlining the user experience.
- Enhance state management for download-related UI in `quran_reading_screen.dart` to handle various download states (needed, downloading, extracting).
- Update `download_quran_popup.dart` to ensure proper navigation based on the user's first-time download experience.
- Improve error handling and loading indicators for a smoother and more intuitive flow.

* fix formating
  • Loading branch information
YassinNouh21 authored Dec 1, 2024
1 parent a651f91 commit 69e6049
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 154 deletions.
74 changes: 43 additions & 31 deletions lib/src/pages/quran/reading/quran_reading_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ class _QuranReadingScreenState extends ConsumerState<QuranReadingScreen> {

WidgetsBinding.instance.addPostFrameCallback((_) async {
ref.read(downloadQuranNotifierProvider);
ref.read(quranReadingNotifierProvider);
});
}

Expand Down Expand Up @@ -321,38 +320,51 @@ class _QuranReadingScreenState extends ConsumerState<QuranReadingScreen> {
});

final autoReadingState = ref.watch(autoScrollNotifierProvider);

return WillPopScope(
onWillPop: () async {
userPrefs.orientationLandscape = true;
return true;
},
child: quranReadingState.when(
data: (state) {
setState(() {
_isRotated = state.isRotated;
});
return RotatedBox(
quarterTurns: state.isRotated ? -1 : 0,
child: SizedBox(
width: MediaQuery.of(context).size.height,
height: MediaQuery.of(context).size.width,
child: Scaffold(
backgroundColor: Colors.white,
floatingActionButtonLocation: _getFloatingActionButtonLocation(context),
floatingActionButton: QuranFloatingActionControls(
switchScreenViewFocusNode: _switchScreenViewFocusNode,
switchQuranModeNode: _switchQuranModeNode,
switchToPlayQuranFocusNode: _switchToPlayQuranFocusNode,
),
body: _buildBody(quranReadingState, state.isRotated, userPrefs, autoReadingState),
),
final downloadState = ref.watch(downloadQuranNotifierProvider);
return downloadState.when(
data: (data) {
if (data is NeededDownloadedQuran || data is Downloading || data is Extracting) {
return Scaffold(
body: Container(
color: Colors.white,
),
);
},
loading: () => SizedBox(),
error: (error, stack) => const Icon(Icons.error),
),
}
return WillPopScope(
onWillPop: () async {
userPrefs.orientationLandscape = true;
return true;
},
child: quranReadingState.when(
data: (state) {
setState(() {
_isRotated = state.isRotated;
});
return RotatedBox(
quarterTurns: state.isRotated ? -1 : 0,
child: SizedBox(
width: MediaQuery.of(context).size.height,
height: MediaQuery.of(context).size.width,
child: Scaffold(
backgroundColor: Colors.white,
floatingActionButtonLocation: _getFloatingActionButtonLocation(context),
floatingActionButton: QuranFloatingActionControls(
switchScreenViewFocusNode: _switchScreenViewFocusNode,
switchQuranModeNode: _switchQuranModeNode,
switchToPlayQuranFocusNode: _switchToPlayQuranFocusNode,
),
body: _buildBody(quranReadingState, state.isRotated, userPrefs, autoReadingState),
),
),
);
},
loading: () => Scaffold(body: SizedBox()),
error: (error, stack) => Scaffold(body: const Icon(Icons.error)),
),
);
},
loading: () => Scaffold(body: _buildLoadingIndicator()),
error: (error, stack) => Scaffold(body: _buildErrorIndicator(error)),
);
}

Expand Down
219 changes: 131 additions & 88 deletions lib/src/pages/quran/widget/download_quran_popup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:mawaqit/src/domain/model/quran/moshaf_type_model.dart';
import 'package:mawaqit/src/state_management/quran/download_quran/download_quran_notifier.dart';
import 'package:mawaqit/src/state_management/quran/download_quran/download_quran_state.dart';
import 'package:mawaqit/src/state_management/quran/reading/moshaf_type_notifier.dart';
import 'package:mawaqit/src/state_management/quran/reading/quran_reading_state.dart';
import 'package:mawaqit/src/state_management/quran/reading/quran_reading_notifer.dart';

class DownloadQuranDialog extends ConsumerStatefulWidget {
const DownloadQuranDialog({super.key});
Expand All @@ -19,38 +19,66 @@ class DownloadQuranDialog extends ConsumerStatefulWidget {

class _DownloadQuranDialogState extends ConsumerState<DownloadQuranDialog> {
MoshafType selectedMoshafType = MoshafType.hafs;
late FocusNode _dialogFocusNode;

@override
void initState() {
super.initState();
_dialogFocusNode = FocusNode();
WidgetsBinding.instance.addPostFrameCallback((_) {
_checkForUpdate();
});
}

@override
void dispose() {
_dialogFocusNode.dispose();
super.dispose();
}

void _checkForUpdate() {
final notifier = ref.read(downloadQuranNotifierProvider.notifier);
// notifier.checkForUpdate(notifier.selectedMoshafType);
}

@override
Widget build(BuildContext context) {
final state = ref.watch(downloadQuranNotifierProvider);
return state.when(
data: (data) => _buildContent(context, data),
loading: () => Container(),
error: (error, _) => _buildErrorDialog(context, error),
final downloadState = ref.watch(downloadQuranNotifierProvider);

return downloadState.when(
data: (data) => _buildDialogContent(context, data),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => _buildErrorDialog(context, error),
);
}

Widget _buildDialogContent(BuildContext context, DownloadQuranState state) {
return switch (state) {
NeededDownloadedQuran() => _buildChooseDownloadMoshaf(context),
Downloading() => _buildDownloadingDialog(context, state),
Extracting() => _buildExtractingDialog(context, state),
Success() => _handleSuccess(context),
CancelDownload() => const SizedBox(),
_ => const SizedBox(),
};
}

Widget _handleSuccess(BuildContext context) {
// Auto close dialog on success
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
});
return const SizedBox();
}

Widget _buildContent(BuildContext context, DownloadQuranState state) {
// return Container();
return switch (state) {
NeededDownloadedQuran() => _buildChooseDownloadMoshaf(context),
// UpdateAvailable() => _buildUpdateAvailableDialog(context, state),
Downloading() => _buildDownloadingDialog(context, state),
Extracting() => _buildExtractingDialog(context, state),
Success() => _buildSuccessDialog(context, state),
Success() => _successDialog(context),
CancelDownload() => Container(),
// NoUpdate() => _buildNoUpdateDialog(context, state),
_ => Container(),
Expand Down Expand Up @@ -79,63 +107,68 @@ class _DownloadQuranDialogState extends ConsumerState<DownloadQuranDialog> {
}

Widget _buildDownloadingDialog(BuildContext context, Downloading state) {
return AlertDialog(
title: Text(S.of(context).downloadingQuran),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(value: state.progress / 100),
SizedBox(height: 16),
Text('${state.progress.toStringAsFixed(2)}%'),
],
),
actions: [
TextButton(
autofocus: true,
onPressed: () async {
final notifier = ref.read(downloadQuranNotifierProvider.notifier);
ref.read(moshafTypeNotifierProvider).maybeWhen(
orElse: () {},
data: (state) async {
state.selectedMoshaf.fold(() {
return null;
}, (selectedMoshaf) async {
await notifier.cancelDownload(selectedMoshaf); // Await cancellation
Navigator.pop(context); // Close dialog after cancel completes
});
},
);
},
child: Text(S.of(context).cancel),
return Focus(
focusNode: _dialogFocusNode,
child: AlertDialog(
title: Text(S.of(context).downloadingQuran),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(value: state.progress / 100),
SizedBox(height: 16),
Text('${state.progress.toStringAsFixed(2)}%'),
],
),
],
);
}
actions: [
TextButton(
autofocus: true,
onPressed: () async {
final notifier = ref.read(downloadQuranNotifierProvider.notifier);
final moshafType = ref.watch(moshafTypeNotifierProvider);

Widget _buildExtractingDialog(BuildContext context, Extracting state) {
return AlertDialog(
title: Text(S.of(context).extractingQuran),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(value: state.progress / 100),
SizedBox(height: 16),
Text('${state.progress.toStringAsFixed(2)}%'),
ref.read(moshafTypeNotifierProvider).maybeWhen(
orElse: () {},
data: (state) async {
state.selectedMoshaf.fold(() {
return null;
}, (selectedMoshaf) async {
await notifier.cancelDownload(selectedMoshaf); // Await cancellation
});
},
);
moshafType.when(
data: (data) {
if (data.isFirstTime) {
Navigator.popUntil(context, (route) => route.isFirst);
} else {
Navigator.pop(context);
}
},
error: (_, __) {},
loading: () {},
);
},
child: Text(S.of(context).cancel),
),
],
),
);
}

Widget _buildSuccessDialog(BuildContext context, Success state) {
return AlertDialog(
title: Text(S.of(context).quranDownloaded),
actions: [
TextButton(
autofocus: true,
onPressed: () => Navigator.pop(context),
child: Text(S.of(context).ok),
Widget _buildExtractingDialog(BuildContext context, Extracting state) {
return Focus(
focusNode: _dialogFocusNode,
child: AlertDialog(
title: Text(S.of(context).extractingQuran),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(value: state.progress / 100),
SizedBox(height: 16),
Text('${state.progress.toStringAsFixed(2)}%'),
],
),
],
),
);
}

Expand All @@ -152,41 +185,46 @@ class _DownloadQuranDialogState extends ConsumerState<DownloadQuranDialog> {
}

Widget _buildChooseDownloadMoshaf(BuildContext context) {
return AlertDialog(
title: Text(S.of(context).chooseQuranType),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildMoshafTypeRadio(
context,
title: S.of(context).warsh,
value: MoshafType.warsh,
setState: setState,
return Focus(
focusNode: _dialogFocusNode,
child: AlertDialog(
title: Text(S.of(context).chooseQuranType),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildMoshafTypeRadio(
context,
title: S.of(context).warsh,
value: MoshafType.warsh,
setState: setState,
autofocus: selectedMoshafType == MoshafType.warsh,
),
_buildMoshafTypeRadio(
context,
title: S.of(context).hafs,
value: MoshafType.hafs,
setState: setState,
autofocus: selectedMoshafType == MoshafType.hafs,
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(S.of(context).cancel),
),
_buildMoshafTypeRadio(
context,
title: S.of(context).hafs,
value: MoshafType.hafs,
setState: setState,
TextButton(
autofocus: true,
onPressed: () async {
Navigator.pop(context);
await ref.read(downloadQuranNotifierProvider.notifier).downloadQuran(selectedMoshafType);
},
child: Text(S.of(context).download),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(S.of(context).cancel),
),
TextButton(
autofocus: true,
onPressed: () async {
Navigator.pop(context);
await ref.read(downloadQuranNotifierProvider.notifier).downloadQuran(selectedMoshafType);
},
child: Text(S.of(context).download),
),
],
);
}

Expand All @@ -195,11 +233,12 @@ class _DownloadQuranDialogState extends ConsumerState<DownloadQuranDialog> {
required String title,
required MoshafType value,
required void Function(VoidCallback fn) setState,
bool autofocus = false,
}) {
return RadioListTile<MoshafType>(
title: Text(title),
value: value,
autofocus: true,
autofocus: autofocus,
groupValue: selectedMoshafType,
onChanged: (MoshafType? selected) {
setState(() {
Expand Down Expand Up @@ -232,4 +271,8 @@ class _DownloadQuranDialogState extends ConsumerState<DownloadQuranDialog> {
],
);
}

Widget _successDialog(BuildContext context) {
return Container();
}
}
Loading

0 comments on commit 69e6049

Please sign in to comment.