feat: show warning popup on l2/activity language mixup (#4229)
This commit is contained in:
parent
500e9670fa
commit
dc55796ea6
7 changed files with 174 additions and 3 deletions
|
|
@ -5300,5 +5300,7 @@
|
|||
"playWithAI": "Play with AI for now",
|
||||
"courseStartDesc": "Pangea Bot is ready to go anytime!\n\n...but learning is better with friends!",
|
||||
"activityDropdownDesc": "When you’re done with this activity, click below",
|
||||
"activityAnalyticsListBody": "These are your completed activities! After finishing activities, you can view them here."
|
||||
"activityAnalyticsListBody": "These are your completed activities! After finishing activities, you can view them here.",
|
||||
"languageMismatchTitle": "Language mismatch",
|
||||
"languageMismatchDesc": "Your target language doesn't match the language of this activity. Update your target language?"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import 'package:fluffychat/pangea/chat/widgets/event_too_large_dialog.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/models/choreo_record.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/utils/language_mismatch_repo.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/language_mismatch_popup.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/message_analytics_feedback.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
|
|
@ -2158,6 +2160,42 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
}
|
||||
|
||||
bool get shouldShowLanguageMismatchPopup {
|
||||
if (!LanguageMismatchRepo.shouldShow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final l2 = choreographer.l2Lang?.langCodeShort;
|
||||
final activityLang = room.activityPlan?.req.targetLanguage.split('-').first;
|
||||
return activityLang != null && l2 != null && l2 != activityLang;
|
||||
}
|
||||
|
||||
Future<void> showLanguageMismatchPopup() async {
|
||||
if (!shouldShowLanguageMismatchPopup) {
|
||||
return;
|
||||
}
|
||||
|
||||
final targetLanguage = room.activityPlan!.req.targetLanguage;
|
||||
LanguageMismatchRepo.set();
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: LanguageMismatchPopup(
|
||||
targetLanguage: targetLanguage,
|
||||
choreographer: choreographer,
|
||||
onUpdate: () async {
|
||||
await choreographer.getLanguageHelp(manual: true);
|
||||
final matches = choreographer.igc.igcTextData?.matches;
|
||||
if (matches?.isNotEmpty == true) {
|
||||
choreographer.igc.showFirstMatch(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
transformTargetId: choreographer.inputTransformTargetKey,
|
||||
);
|
||||
}
|
||||
|
||||
void _showAnalyticsFeedback(
|
||||
List<OneConstructUse> constructs,
|
||||
String eventId,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,11 @@ class Choreographer {
|
|||
return;
|
||||
}
|
||||
|
||||
if (chatController.shouldShowLanguageMismatchPopup) {
|
||||
chatController.showLanguageMismatchPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!igc.hasRelevantIGCTextData && !itController.dismissed) {
|
||||
getLanguageHelp().then((value) => _sendWithIGC(context));
|
||||
} else {
|
||||
|
|
|
|||
42
lib/pangea/choreographer/utils/language_mismatch_repo.dart
Normal file
42
lib/pangea/choreographer/utils/language_mismatch_repo.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
class LanguageMismatchRepo {
|
||||
static final GetStorage _storage = GetStorage('language_mismatch');
|
||||
static const String key = 'shown_timestamp';
|
||||
static const Duration displayInterval = Duration(minutes: 30);
|
||||
|
||||
static Future<void> set() async {
|
||||
await _storage.write(key, DateTime.now().toIso8601String());
|
||||
}
|
||||
|
||||
static DateTime? _get() {
|
||||
final entry = _storage.read(key);
|
||||
if (entry == null) return null;
|
||||
|
||||
try {
|
||||
final value = DateTime.tryParse(entry);
|
||||
if (value != null) {
|
||||
final timeSince = DateTime.now().difference(value);
|
||||
if (timeSince > displayInterval) {
|
||||
_delete();
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
} catch (_) {
|
||||
_delete();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> _delete() async {
|
||||
await _storage.remove(key);
|
||||
}
|
||||
|
||||
static bool get shouldShow {
|
||||
final lastShown = _get();
|
||||
if (lastShown == null) return true;
|
||||
return DateTime.now().difference(lastShown) >= displayInterval;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_header.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class LanguageMismatchPopup extends StatelessWidget {
|
||||
final String targetLanguage;
|
||||
final Choreographer choreographer;
|
||||
final VoidCallback onUpdate;
|
||||
|
||||
const LanguageMismatchPopup({
|
||||
super.key,
|
||||
required this.targetLanguage,
|
||||
required this.choreographer,
|
||||
required this.onUpdate,
|
||||
});
|
||||
|
||||
Future<void> _onConfirm(BuildContext context) async {
|
||||
await MatrixState.pangeaController.userController.updateProfile(
|
||||
(profile) {
|
||||
profile.userSettings.targetLanguage = targetLanguage;
|
||||
return profile;
|
||||
},
|
||||
waitForDataInSync: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CardHeader(
|
||||
text: L10n.of(context).languageMismatchTitle,
|
||||
botExpression: BotExpression.addled,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).languageMismatchDesc,
|
||||
style: BotStyle.text(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => _onConfirm(context),
|
||||
);
|
||||
MatrixState.pAnyState.closeOverlay();
|
||||
onUpdate();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all<Color>(
|
||||
(Theme.of(context).colorScheme.primary).withAlpha(25),
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).confirm),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -100,8 +100,12 @@ class StartIGCButtonState extends State<StartIGCButton>
|
|||
);
|
||||
return;
|
||||
case AssistanceState.notFetched:
|
||||
await widget.controller.choreographer.getLanguageHelp(manual: true);
|
||||
_showFirstMatch();
|
||||
if (widget.controller.shouldShowLanguageMismatchPopup) {
|
||||
widget.controller.showLanguageMismatchPopup();
|
||||
} else {
|
||||
await widget.controller.choreographer.getLanguageHelp(manual: true);
|
||||
_showFirstMatch();
|
||||
}
|
||||
return;
|
||||
case AssistanceState.fetched:
|
||||
_showFirstMatch();
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ class PangeaController {
|
|||
'course_location_storage',
|
||||
'course_activity_storage',
|
||||
'course_location_media_storage',
|
||||
'language_mismatch',
|
||||
];
|
||||
|
||||
Future<void> clearCache({List<String> exclude = const []}) async {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue