From c07c0632bcbaabf3c7deb3daab1d07284381b209 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:50:56 -0400 Subject: [PATCH] chore: on play with bot timeout, show warning popup (#4312) --- lib/l10n/intl_en.arb | 3 +- .../activity_session_start_page.dart | 37 ++++++++++++---- .../activity_sessions_start_view.dart | 8 +--- .../bot_join_error_dialog.dart | 43 +++++++++++++++++++ lib/widgets/future_loading_dialog.dart | 11 +++++ 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 8c6e3e59a..a5ba7527d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5309,5 +5309,6 @@ "tokenInfoFeedbackDialogTitle": "Word Information Feedback", "tokenInfoFeedbackDialogDesc": "AI makes mistakes. Please describe any issues you found with the information above.", "noPublicCoursesFound": "No public courses found. Would you like to create one?", - "noCourseTemplatesFound": "We couldn't find any courses for your target language. You can chat with Pangea Bot in the meantime, and check back later for more courses." + "noCourseTemplatesFound": "We couldn't find any courses for your target language. You can chat with Pangea Bot in the meantime, and check back later for more courses.", + "botActivityJoinFailMessage": "Pangea Bot is taking a while to respond. Please try again later, or invite a friend." } diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart index fe355f09a..fdb108aa5 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart'; +import 'package:fluffychat/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/course_plans/activity_summaries_provider.dart'; import 'package:fluffychat/pangea/course_plans/course_activity_repo.dart'; @@ -431,6 +432,21 @@ class ActivitySessionStartController extends State } Future playWithBot() async { + final resp = await showFutureLoadingDialog( + context: context, + future: _playWithBot, + showError: (e) => e is! TimeoutException, + ); + + if (resp.isError && resp.error is TimeoutException) { + showDialog( + context: context, + builder: (_) => const BotJoinErrorDialog(), + ); + } + } + + Future _playWithBot() async { if (activityRoom == null) { throw Exception("Room is null"); } @@ -439,16 +455,19 @@ class ActivitySessionStartController extends State throw Exception("Bot is a member of the room"); } - final future = activityRoom!.client.onRoomState.stream - .where( - (state) => - state.roomId == activityRoom!.id && - state.state.type == PangeaEventTypes.activityRole && - state.state.senderId == BotName.byEnvironment, - ) - .first; + final Future<({String roomId, StrippedStateEvent state})?> future = + activityRoom!.client.onRoomState.stream + .where( + (state) => + state.roomId == activityRoom!.id && + state.state.type == PangeaEventTypes.activityRole && + state.state.senderId == BotName.byEnvironment, + ) + .first; activityRoom!.invite(BotName.byEnvironment); - await future.timeout(const Duration(seconds: 30)); + await future.timeout( + const Duration(seconds: 5), + ); } @override diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart index 099da28dd..dfe694501 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart @@ -222,12 +222,8 @@ class ActivitySessionStartView extends StatelessWidget { if (!controller.isBotRoomMember) ElevatedButton( style: buttonStyle, - onPressed: () => - showFutureLoadingDialog( - context: context, - future: - controller.playWithBot, - ), + onPressed: + controller.playWithBot, child: Row( mainAxisAlignment: MainAxisAlignment diff --git a/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart b/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart new file mode 100644 index 000000000..6a3ee5572 --- /dev/null +++ b/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart'; + +class BotJoinErrorDialog extends StatelessWidget { + const BotJoinErrorDialog({super.key}); + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + child: Container( + width: 300.0, + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const BotFace( + width: 100, + expression: BotExpression.addled, + ), + const SizedBox(height: 8), + Text( + L10n.of(context).botActivityJoinFailMessage, + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context).close), + ), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/future_loading_dialog.dart b/lib/widgets/future_loading_dialog.dart index a681bfcc2..47aa5ad57 100644 --- a/lib/widgets/future_loading_dialog.dart +++ b/lib/widgets/future_loading_dialog.dart @@ -22,6 +22,7 @@ Future> showFutureLoadingDialog({ ExceptionContext? exceptionContext, bool ignoreError = false, // #Pangea + bool Function(Object)? showError, Object? Function(Object, StackTrace?)? onError, String? Function()? onSuccess, VoidCallback? onDismiss, @@ -55,6 +56,7 @@ Future> showFutureLoadingDialog({ backLabel: backLabel, exceptionContext: exceptionContext, // #Pangea + showError: showError, onError: onError, onDismiss: onDismiss, onSuccess: onSuccess, @@ -82,6 +84,7 @@ class LoadingDialog extends StatefulWidget { final Future future; final ExceptionContext? exceptionContext; // #Pangea + final bool Function(Object)? showError; final Object? Function(Object, StackTrace?)? onError; final String? Function()? onSuccess; final VoidCallback? onDismiss; @@ -94,6 +97,7 @@ class LoadingDialog extends StatefulWidget { this.backLabel, this.exceptionContext, // #Pangea + this.showError, this.onError, this.onSuccess, this.onDismiss, @@ -135,6 +139,13 @@ class LoadingDialogState extends State { } }, onError: (e, s) { + if (widget.showError != null && !widget.showError!(e)) { + if (mounted && Navigator.of(context).canPop()) { + Navigator.of(context).pop>(Result.error(e, s)); + } + return; + } + if (mounted) { setState(() { exception = widget.onError?.call(e, s) ?? e;