chore: on play with bot timeout, show warning popup (#4312)

This commit is contained in:
ggurdin 2025-10-09 15:50:56 -04:00 committed by GitHub
parent 97d8c5fcf6
commit c07c0632bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 86 additions and 16 deletions

View file

@ -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."
}

View file

@ -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<ActivitySessionStartPage>
}
Future<void> 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<void> _playWithBot() async {
if (activityRoom == null) {
throw Exception("Room is null");
}
@ -439,16 +455,19 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage>
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

View file

@ -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

View file

@ -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),
),
],
),
),
);
}
}

View file

@ -22,6 +22,7 @@ Future<Result<T>> showFutureLoadingDialog<T>({
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<Result<T>> showFutureLoadingDialog<T>({
backLabel: backLabel,
exceptionContext: exceptionContext,
// #Pangea
showError: showError,
onError: onError,
onDismiss: onDismiss,
onSuccess: onSuccess,
@ -82,6 +84,7 @@ class LoadingDialog<T> extends StatefulWidget {
final Future<T> 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<T> extends StatefulWidget {
this.backLabel,
this.exceptionContext,
// #Pangea
this.showError,
this.onError,
this.onSuccess,
this.onDismiss,
@ -135,6 +139,13 @@ class LoadingDialogState<T> extends State<LoadingDialog> {
}
},
onError: (e, s) {
if (widget.showError != null && !widget.showError!(e)) {
if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop<Result<T>>(Result.error(e, s));
}
return;
}
if (mounted) {
setState(() {
exception = widget.onError?.call(e, s) ?? e;