diff --git a/lib/config/routes.dart b/lib/config/routes.dart index a2487a95a..392e1e6ac 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -9,6 +9,7 @@ import 'package:matrix/matrix_api_lite/generated/model.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/archive/archive.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart'; @@ -583,6 +584,67 @@ abstract class AppRoutes { const VocabPractice(), ); }, + onExit: (context, state) async { + // Check if bypass flag was set before navigation + if (VocabPractice.bypassExitConfirmation) { + VocabPractice.bypassExitConfirmation = false; + return true; + } + + final bool confirm = await showDialog( + context: context, + builder: (context) { + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 400, + ), + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + Center( + child: Text( + L10n.of(context).exitPractice, + style: const TextStyle( + fontSize: 28, + ), + ), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text(L10n.of(context).yes), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + Navigator.of(context) + .pop(false); + }, + child: Text(L10n.of(context).no), + ), + ], + ), + ), + ), + ), + ); + }, + ) ?? + false; + + return confirm; + }, ), GoRoute( path: ':construct', diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 487a6df31..a168acbcd 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5046,5 +5046,6 @@ "voice": "Voice", "youLeftTheChat": "🚪 You left the chat", "downloadInitiated": "Download initiated", - "webDownloadPermissionMessage": "If your browser blocks downloads, please enable downloads for this site." + "webDownloadPermissionMessage": "If your browser blocks downloads, please enable downloads for this site.", + "exitPractice": "Exit practice? Your progress will be lost." } diff --git a/lib/pangea/vocab_practice/completed_activity_session_view.dart b/lib/pangea/vocab_practice/completed_activity_session_view.dart index 5b9761f1f..b783c8a73 100644 --- a/lib/pangea/vocab_practice/completed_activity_session_view.dart +++ b/lib/pangea/vocab_practice/completed_activity_session_view.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/level_up/star_rain_widget.dart'; @@ -162,7 +164,12 @@ class CompletedActivitySessionView extends StatelessWidget { vertical: 8.0, ), ), - onPressed: () => Navigator.of(context).pop(), + onPressed: () { + VocabPractice.bypassExitConfirmation = true; + debugPrint( + "VocabPractice.bypassExitConfirmation set to ${VocabPractice.bypassExitConfirmation}"); + context.go('/rooms/analytics/vocab'); + }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/pangea/vocab_practice/vocab_practice_page.dart b/lib/pangea/vocab_practice/vocab_practice_page.dart index 4bfbfc111..79f0b6979 100644 --- a/lib/pangea/vocab_practice/vocab_practice_page.dart +++ b/lib/pangea/vocab_practice/vocab_practice_page.dart @@ -46,6 +46,8 @@ class SessionLoader extends AsyncLoader { } class VocabPractice extends StatefulWidget { + static bool bypassExitConfirmation = false; + const VocabPractice({super.key}); @override @@ -83,11 +85,7 @@ class VocabPracticeState extends State with AnalyticsUpdater { @override void dispose() { _languageStreamSubscription?.cancel(); - if (_isComplete) { - VocabPracticeSessionRepo.clear(); - } else { - _saveSession(); - } + VocabPracticeSessionRepo.clear(); _sessionLoader.dispose(); activityState.dispose(); activityConstructId.dispose(); @@ -185,12 +183,6 @@ class VocabPracticeState extends State with AnalyticsUpdater { } } - Future _saveSession() async { - if (_sessionLoader.isLoaded) { - await VocabPracticeSessionRepo.update(_sessionLoader.value!); - } - } - Future _waitForAnalytics() async { if (!_analyticsService.initCompleter.isCompleted) { MatrixState.pangeaController.initControllers(); @@ -225,7 +217,7 @@ class VocabPracticeState extends State with AnalyticsUpdater { Future reloadSession() async { _resetActivityState(); _resetSessionState(); - await VocabPracticeSessionRepo.clear(); + _sessionLoader.reset(); await _startSession(); } @@ -240,7 +232,6 @@ class VocabPracticeState extends State with AnalyticsUpdater { bonus, forceUpdate: true, ); - await _saveSession(); } bool _continuing = false; @@ -394,7 +385,6 @@ class VocabPracticeState extends State with AnalyticsUpdater { await _analyticsService.updateService .addAnalytics(choiceTargetId(choiceContent), [use]); - await _saveSession(); if (!correct) return; // Display the fact that the choice was correct before loading the next activity @@ -403,7 +393,6 @@ class VocabPracticeState extends State with AnalyticsUpdater { // Then mark this activity as completed, and either load the next or complete the session _sessionLoader.value!.completeActivity(); progressNotifier.value = _sessionLoader.value!.progress; - await _saveSession(); _isComplete ? await _completeSession() : await _continueSession(); } diff --git a/lib/pangea/vocab_practice/vocab_practice_session_repo.dart b/lib/pangea/vocab_practice/vocab_practice_session_repo.dart index bf780dda8..30877f42b 100644 --- a/lib/pangea/vocab_practice/vocab_practice_session_repo.dart +++ b/lib/pangea/vocab_practice/vocab_practice_session_repo.dart @@ -14,11 +14,6 @@ class VocabPracticeSessionRepo { static final GetStorage _storage = GetStorage('vocab_practice_session'); static Future get() async { - final cached = _getCached(); - if (cached != null) { - return cached; - } - final r = Random(); final activityTypes = [ ActivityTypeEnum.lemmaMeaning, @@ -46,15 +41,9 @@ class VocabPracticeSessionRepo { startedAt: DateTime.now(), practiceTargets: targets, ); - await _setCached(session); return session; } - static Future update( - VocabPracticeSessionModel session, - ) => - _setCached(session); - static Future clear() => _storage.erase(); static Future> _fetch() async { @@ -85,24 +74,4 @@ class VocabPracticeSessionRepo { } return targets; } - - static VocabPracticeSessionModel? _getCached() { - final keys = List.from(_storage.getKeys()); - if (keys.isEmpty) return null; - try { - final json = _storage.read(keys.first) as Map; - return VocabPracticeSessionModel.fromJson(json); - } catch (e) { - _storage.remove(keys.first); - return null; - } - } - - static Future _setCached(VocabPracticeSessionModel session) async { - await _storage.erase(); - await _storage.write( - session.startedAt.toIso8601String(), - session.toJson(), - ); - } }