From 445ed0338d9cdd60f2db4f227af266018abc1320 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 12:04:30 -0500 Subject: [PATCH 01/42] chore: move regeneration button down to toolbar --- lib/pages/chat/events/message.dart | 14 ----- .../widgets/request_regeneration_button.dart | 59 ------------------- .../toolbar/layout/overlay_message.dart | 6 -- .../select_mode_buttons.dart | 19 +++++- .../select_mode_controller.dart | 1 + 5 files changed, 18 insertions(+), 81 deletions(-) delete mode 100644 lib/pangea/chat/widgets/request_regeneration_button.dart diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d3ac2306b..fe4f9bf1f 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -16,7 +16,6 @@ import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/pangea/chat/extensions/custom_room_display_extension.dart'; -import 'package:fluffychat/pangea/chat/widgets/request_regeneration_button.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; @@ -822,20 +821,7 @@ class Message extends StatelessWidget { ), ], ), - ) - // #Pangea - else if (canRefresh) - RequestRegenerationButton( - textColor: - textColor, - onPressed: () => - controller - .requestRegeneration( - event - .eventId, - ), ), - // Pangea# ], ), ), diff --git a/lib/pangea/chat/widgets/request_regeneration_button.dart b/lib/pangea/chat/widgets/request_regeneration_button.dart deleted file mode 100644 index eaa4cc56b..000000000 --- a/lib/pangea/chat/widgets/request_regeneration_button.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; - -class RequestRegenerationButton extends StatelessWidget { - final Color textColor; - final VoidCallback onPressed; - - const RequestRegenerationButton({ - super.key, - required this.textColor, - required this.onPressed, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - left: 16.0, - right: 16.0, - ), - child: TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: const Size( - 0, - 0, - ), - ), - onPressed: onPressed, - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 4.0, - children: [ - Icon( - Icons.refresh, - color: textColor.withAlpha( - 164, - ), - size: 14, - ), - Text( - L10n.of( - context, - ).requestRegeneration, - style: TextStyle( - color: textColor.withAlpha( - 164, - ), - fontSize: 11, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/pangea/toolbar/layout/overlay_message.dart b/lib/pangea/toolbar/layout/overlay_message.dart index ad575ca0e..2e0b134f3 100644 --- a/lib/pangea/toolbar/layout/overlay_message.dart +++ b/lib/pangea/toolbar/layout/overlay_message.dart @@ -10,7 +10,6 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/message_content.dart'; import 'package:fluffychat/pages/chat/events/reply_content.dart'; -import 'package:fluffychat/pangea/chat/widgets/request_regeneration_button.dart'; import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; @@ -258,11 +257,6 @@ class OverlayMessage extends StatelessWidget { ), ], ), - ) - else if (canRefresh) - RequestRegenerationButton( - textColor: textColor, - onPressed: () => controller.requestRegeneration(event.eventId), ), ], ), diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart index 116fd451d..6549403a9 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart @@ -29,7 +29,8 @@ enum SelectMode { translate(Icons.translate), practice(Symbols.fitness_center), emoji(Icons.add_reaction_outlined), - speechTranslation(Icons.translate); + speechTranslation(Icons.translate), + requestRegenerate(Icons.replay); final IconData icon; const SelectMode(this.icon); @@ -46,6 +47,8 @@ enum SelectMode { return l10n.practice; case SelectMode.emoji: return l10n.emojiView; + case SelectMode.requestRegenerate: + return l10n.requestRegeneration; } } } @@ -214,6 +217,12 @@ class SelectModeButtonsState extends State { if (updatedMode == SelectMode.speechTranslation) { await controller.fetchSpeechTranslation(); } + + if (updatedMode == SelectMode.requestRegenerate) { + widget.controller.requestRegeneration( + messageEvent.eventId, + ); + } } Future modeDisabled() async { @@ -348,7 +357,13 @@ class SelectModeButtonsState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final modes = controller.readingAssistanceModes; - final allModes = controller.allModes; + final allModes = controller.allModes + .where( + (mode) => + mode != SelectMode.requestRegenerate || + messageEvent.eventId == widget.controller.refreshEventID, + ) + .toList(); return Material( type: MaterialType.transparency, child: SizedBox( diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index eee6bdcbc..e5de6dfaa 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -113,6 +113,7 @@ class SelectModeController with LemmaEmojiSetter { SelectMode.translate, SelectMode.practice, SelectMode.emoji, + SelectMode.requestRegenerate, ]; static List get _audioModes => [ From 0e92428327da1909e7ca815b7ce4731f72af0ec8 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 12:22:33 -0500 Subject: [PATCH 02/42] chore: only show shimmer on most recent message --- lib/pages/chat/events/message.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d3ac2306b..6344caa36 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -606,9 +606,10 @@ class Message extends StatelessWidget { // #Pangea child: ShimmerBackground( enabled: controller - .showMessageShimmer( - event, - ), + .showMessageShimmer( + event, + ) && + isButton, // Pangea# child: Container( decoration: From eafa8f60f0f8a6aff260d6e4527666aa5c9b1807 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 12:33:25 -0500 Subject: [PATCH 03/42] chore: replace message toolbar language mismatch popup with button in disabled snackbar --- lib/l10n/intl_ar.arb | 27 ++++++++- lib/l10n/intl_be.arb | 27 ++++++++- lib/l10n/intl_bn.arb | 27 ++++++++- lib/l10n/intl_bo.arb | 27 ++++++++- lib/l10n/intl_ca.arb | 27 ++++++++- lib/l10n/intl_cs.arb | 27 ++++++++- lib/l10n/intl_da.arb | 27 ++++++++- lib/l10n/intl_de.arb | 27 ++++++++- lib/l10n/intl_el.arb | 27 ++++++++- lib/l10n/intl_en.arb | 4 +- lib/l10n/intl_eo.arb | 27 ++++++++- lib/l10n/intl_es.arb | 27 ++++++++- lib/l10n/intl_et.arb | 27 ++++++++- lib/l10n/intl_eu.arb | 27 ++++++++- lib/l10n/intl_fa.arb | 27 ++++++++- lib/l10n/intl_fi.arb | 27 ++++++++- lib/l10n/intl_fil.arb | 27 ++++++++- lib/l10n/intl_fr.arb | 27 ++++++++- lib/l10n/intl_ga.arb | 27 ++++++++- lib/l10n/intl_gl.arb | 27 ++++++++- lib/l10n/intl_he.arb | 27 ++++++++- lib/l10n/intl_hi.arb | 27 ++++++++- lib/l10n/intl_hr.arb | 27 ++++++++- lib/l10n/intl_hu.arb | 27 ++++++++- lib/l10n/intl_ia.arb | 27 ++++++++- lib/l10n/intl_id.arb | 27 ++++++++- lib/l10n/intl_ie.arb | 27 ++++++++- lib/l10n/intl_it.arb | 27 ++++++++- lib/l10n/intl_ja.arb | 27 ++++++++- lib/l10n/intl_ka.arb | 27 ++++++++- lib/l10n/intl_ko.arb | 27 ++++++++- lib/l10n/intl_lt.arb | 27 ++++++++- lib/l10n/intl_lv.arb | 27 ++++++++- lib/l10n/intl_nb.arb | 27 ++++++++- lib/l10n/intl_nl.arb | 27 ++++++++- lib/l10n/intl_pl.arb | 27 ++++++++- lib/l10n/intl_pt.arb | 27 ++++++++- lib/l10n/intl_pt_BR.arb | 27 ++++++++- lib/l10n/intl_pt_PT.arb | 27 ++++++++- lib/l10n/intl_ro.arb | 27 ++++++++- lib/l10n/intl_ru.arb | 27 ++++++++- lib/l10n/intl_sk.arb | 27 ++++++++- lib/l10n/intl_sl.arb | 27 ++++++++- lib/l10n/intl_sr.arb | 27 ++++++++- lib/l10n/intl_sv.arb | 27 ++++++++- lib/l10n/intl_ta.arb | 27 ++++++++- lib/l10n/intl_te.arb | 27 ++++++++- lib/l10n/intl_th.arb | 27 ++++++++- lib/l10n/intl_tr.arb | 27 ++++++++- lib/l10n/intl_uk.arb | 27 ++++++++- lib/l10n/intl_vi.arb | 27 ++++++++- lib/l10n/intl_yue.arb | 27 ++++++++- lib/l10n/intl_zh.arb | 27 ++++++++- lib/l10n/intl_zh_Hant.arb | 27 ++++++++- lib/pages/chat/chat.dart | 56 ++++++++++--------- .../language_mismatch_repo.dart | 4 -- .../select_mode_buttons.dart | 34 +++++++++-- 57 files changed, 1441 insertions(+), 88 deletions(-) diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 3f762775a..70fa0fc97 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-16 14:33:24.230348", + "@@last_modified": "2026-01-20 12:31:24.671375", "about": "حول", "@about": { "type": "String", @@ -11082,5 +11082,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "ممارسة تصحيح أخطاء القواعد", + "constructUseIncGEDesc": "ممارسة أخطاء القواعد غير الصحيحة", + "fillInBlank": "املأ الفراغ بالخيار الصحيح", + "learn": "تعلم", + "languageUpdated": "تم تحديث اللغة المستهدفة!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index 985752f67..d1ff0babf 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-16 14:33:05.869532", + "@@last_modified": "2026-01-20 12:31:06.570011", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11964,5 +11964,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Практыка правільнага выкарыстання граматычных памылак", + "constructUseIncGEDesc": "Практыка няправільнага выкарыстання граматычных памылак", + "fillInBlank": "Запоўніце прабел правільным выбарам", + "learn": "Навучыцца", + "languageUpdated": "Мэтавая мова абноўлена!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 4cf1d6221..28c180422 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:49.084569", + "@@last_modified": "2026-01-20 12:31:49.464406", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11969,5 +11969,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "সঠিক ব্যাকরণ ত্রুটি অনুশীলন", + "constructUseIncGEDesc": "ভুল ব্যাকরণ ত্রুটি অনুশীলন", + "fillInBlank": "সঠিক পছন্দ দিয়ে ফাঁকা স্থান পূরণ করুন", + "learn": "শিখুন", + "languageUpdated": "লক্ষ্য ভাষা আপডেট করা হয়েছে!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index 067627647..800c24594 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-16 14:33:43.824560", + "@@last_modified": "2026-01-20 12:31:44.969872", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10619,5 +10619,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Praktik kesalahan tata bahasa yang benar", + "constructUseIncGEDesc": "Praktik kesalahan tata bahasa yang salah", + "fillInBlank": "Isi kekosongan dengan pilihan yang benar", + "learn": "Belajar", + "languageUpdated": "Bahasa target diperbarui!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 80d465783..5eb437241 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:08.940071", + "@@last_modified": "2026-01-20 12:31:09.126351", "about": "Quant a", "@about": { "type": "String", @@ -10889,5 +10889,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Pràctica d'errors gramaticals correctes", + "constructUseIncGEDesc": "Pràctica d'errors gramaticals incorrectes", + "fillInBlank": "Omple el buit amb l'elecció correcta", + "learn": "Aprendre", + "languageUpdated": "Idioma objectiu actualitzat!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 300a94f0d..321734d12 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-16 14:32:59.173160", + "@@last_modified": "2026-01-20 12:31:00.323945", "about": "O aplikaci", "@about": { "type": "String", @@ -11472,5 +11472,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Cvičení správné gramatiky", + "constructUseIncGEDesc": "Cvičení nesprávné gramatiky", + "fillInBlank": "Doplňte prázdné místo správnou volbou", + "learn": "Učit se", + "languageUpdated": "Cílový jazyk byl aktualizován!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index ae7a9ef5a..372a6e7c8 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-16 14:32:06.899995", + "@@last_modified": "2026-01-20 12:30:10.761209", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11926,5 +11926,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Korrekt grammatikfejl praksis", + "constructUseIncGEDesc": "Ukorrrekt grammatikfejl praksis", + "fillInBlank": "Udfyld det tomme felt med det korrekte valg", + "learn": "Lær", + "languageUpdated": "Mål sprog opdateret!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 0f581a3a0..634b76211 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-16 14:32:44.617501", + "@@last_modified": "2026-01-20 12:30:47.434524", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10872,5 +10872,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Korrekte Grammatikfehlerübung", + "constructUseIncGEDesc": "Falsche Grammatikfehlerübung", + "fillInBlank": "Füllen Sie die Lücke mit der richtigen Wahl aus", + "learn": "Lernen", + "languageUpdated": "Zielsprache aktualisiert!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index dfb2f4a86..176fb6f07 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-16 14:33:59.313114", + "@@last_modified": "2026-01-20 12:31:59.503296", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11923,5 +11923,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Πρακτική διόρθωσης γραμματικών λαθών", + "constructUseIncGEDesc": "Πρακτική λανθασμένων γραμματικών λαθών", + "fillInBlank": "Συμπληρώστε το κενό με τη σωστή επιλογή", + "learn": "Μάθετε", + "languageUpdated": "Η γλώσσα στόχος ενημερώθηκε!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b1bcf4e02..abc4a3d63 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5054,5 +5054,7 @@ "constructUseIncGCDesc": "Incorrect grammar category practice", "constructUseCorGEDesc": "Correct grammar error practice", "constructUseIncGEDesc": "Incorrect grammar error practice", - "fillInBlank": "Fill in the blank with the correct choice" + "fillInBlank": "Fill in the blank with the correct choice", + "learn": "Learn", + "languageUpdated": "Target language updated!" } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 39b6c61c0..5914ed46b 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:34:08.644512", + "@@last_modified": "2026-01-20 12:32:07.490560", "about": "Prio", "@about": { "type": "String", @@ -11954,5 +11954,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Praktiko pri ĝusta gramatika eraro", + "constructUseIncGEDesc": "Praktiko pri malĝusta gramatika eraro", + "fillInBlank": "Plenigu la malplenan lokon per la ĝusta elekto", + "learn": "Lerni", + "languageUpdated": "Celo lingvo ĝisdatigita!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 4ff6e98aa..144e71870 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-16 14:31:58.653004", + "@@last_modified": "2026-01-20 12:29:59.898184", "about": "Acerca de", "@about": { "type": "String", @@ -8099,5 +8099,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Práctica de errores gramaticales correctos", + "constructUseIncGEDesc": "Práctica de errores gramaticales incorrectos", + "fillInBlank": "Completa el espacio en blanco con la opción correcta", + "learn": "Aprender", + "languageUpdated": "¡Idioma objetivo actualizado!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index a1d03040b..5d4e3f6c2 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-16 14:32:41.585481", + "@@last_modified": "2026-01-20 12:30:45.162738", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11136,5 +11136,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Õige grammatika vea harjutamine", + "constructUseIncGEDesc": "Vale grammatika vea harjutamine", + "fillInBlank": "Täida tühik õige valikuga", + "learn": "Õpi", + "languageUpdated": "Sihtkeel on uuendatud!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index 253e41e61..225d963e7 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-16 14:32:36.050138", + "@@last_modified": "2026-01-20 12:30:40.144354", "about": "Honi buruz", "@about": { "type": "String", @@ -10865,5 +10865,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Gramatika akats zuzenketa praktika", + "constructUseIncGEDesc": "Gramatika akats okerra praktika", + "fillInBlank": "Betekoa bete aukerarik egokienarekin", + "learn": "Ikasi", + "languageUpdated": "Helmuga hizkuntza eguneratua!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 5a64b9826..977dd42b8 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:51.512389", + "@@last_modified": "2026-01-20 12:31:52.231221", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11597,5 +11597,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "تمرین خطای گرامری صحیح", + "constructUseIncGEDesc": "تمرین خطای گرامری نادرست", + "fillInBlank": "جای خالی را با گزینه صحیح پر کنید", + "learn": "یاد بگیرید", + "languageUpdated": "زبان هدف به‌روزرسانی شد!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 0c8f48151..6184adfec 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-16 14:32:03.875087", + "@@last_modified": "2026-01-20 12:30:08.099637", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11488,5 +11488,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Oikean kielioppivirheen harjoittelu", + "constructUseIncGEDesc": "Väärän kielioppivirheen harjoittelu", + "fillInBlank": "Täytä tyhjä kohta oikealla valinnalla", + "learn": "Oppia", + "languageUpdated": "Kohdekieli päivitetty!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index 0a79ce37a..986aa4015 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-16 14:33:18.880694", + "@@last_modified": "2026-01-20 12:31:19.884620", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11841,5 +11841,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Pagsasanay sa tamang pagkakamali sa gramatika", + "constructUseIncGEDesc": "Pagsasanay sa maling pagkakamali sa gramatika", + "fillInBlank": "Punan ang blangko ng tamang pagpipilian", + "learn": "Matuto", + "languageUpdated": "Na-update ang target na wika!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 0820d0126..c279297f0 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-16 14:34:20.722657", + "@@last_modified": "2026-01-20 12:32:20.398562", "about": "À propos", "@about": { "type": "String", @@ -11189,5 +11189,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Pratique de correction des erreurs grammaticales", + "constructUseIncGEDesc": "Pratique des erreurs grammaticales incorrectes", + "fillInBlank": "Remplissez le blanc avec le choix correct", + "learn": "Apprendre", + "languageUpdated": "Langue cible mise à jour !", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index 3b91b85c4..c923a3bbd 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-16 14:34:18.473304", + "@@last_modified": "2026-01-20 12:32:18.033824", "@customReaction": { "type": "String", "placeholders": {} @@ -10863,5 +10863,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Cleachtadh ar earráidí gramadaí ceart", + "constructUseIncGEDesc": "Cleachtadh ar earráidí gramadaí míchruinn", + "fillInBlank": "Líon isteach an folt le rogha cheart", + "learn": "Foghlaim", + "languageUpdated": "Teanga sprioc nuashonraithe!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index e4e4a6407..3ebc2943d 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-16 14:32:01.213945", + "@@last_modified": "2026-01-20 12:30:05.234280", "about": "Acerca de", "@about": { "type": "String", @@ -10862,5 +10862,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Práctica de erro gramatical correcto", + "constructUseIncGEDesc": "Práctica de erro gramatical incorrecto", + "fillInBlank": "Completa o espazo en branco coa opción correcta", + "learn": "Aprender", + "languageUpdated": "Idioma de destino actualizado!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index 12dd52832..7ad96af48 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:26.413376", + "@@last_modified": "2026-01-20 12:30:31.884050", "about": "אודות", "@about": { "type": "String", @@ -11914,5 +11914,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "תרגול תיקון שגיאות דקדוק", + "constructUseIncGEDesc": "תרגול שגיאות דקדוק לא נכונות", + "fillInBlank": "מלא את החסר עם הבחירה הנכונה", + "learn": "ללמוד", + "languageUpdated": "שפת היעד עודכנה!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index a95ad48fc..a465a07b2 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-16 14:34:05.778231", + "@@last_modified": "2026-01-20 12:32:05.240015", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11950,5 +11950,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "व्याकरण त्रुटि सुधार अभ्यास", + "constructUseIncGEDesc": "व्याकरण त्रुटि गलत अभ्यास", + "fillInBlank": "सही विकल्प के साथ रिक्त स्थान भरें", + "learn": "सीखें", + "languageUpdated": "लक्षित भाषा अपडेट की गई!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index e05faf2de..64f6cccf8 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-16 14:32:23.849622", + "@@last_modified": "2026-01-20 12:30:29.823787", "about": "Informacije", "@about": { "type": "String", @@ -11237,5 +11237,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Prakticiranje ispravne gramatičke greške", + "constructUseIncGEDesc": "Prakticiranje pogrešne gramatičke greške", + "fillInBlank": "Ispunite prazno mjesto s ispravnim izborom", + "learn": "Učite", + "languageUpdated": "Ciljani jezik ažuriran!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 857440581..8d543fa7e 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-16 14:32:10.796625", + "@@last_modified": "2026-01-20 12:30:13.762355", "about": "Névjegy", "@about": { "type": "String", @@ -10866,5 +10866,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Helyes nyelvtani hiba gyakorlás", + "constructUseIncGEDesc": "Helytelen nyelvtani hiba gyakorlás", + "fillInBlank": "Töltsd ki a hiányzó részt a helyes választással", + "learn": "Tanulj", + "languageUpdated": "Cél nyelv frissítve!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 88b1df62c..4fda86d9e 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-16 14:32:31.137719", + "@@last_modified": "2026-01-20 12:30:35.012898", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11943,5 +11943,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Praktiko de ĝusta gramatika eraro", + "constructUseIncGEDesc": "Praktiko de malĝusta gramatika eraro", + "fillInBlank": "Plenigu la malplenan lokon kun la ĝusta elekto", + "learn": "Lerni", + "languageUpdated": "Celo lingvo ĝisdatigita!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index b1c17332d..50917db2e 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:13.610901", + "@@last_modified": "2026-01-20 12:30:15.788809", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10856,5 +10856,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Latihan kesalahan tata bahasa yang benar", + "constructUseIncGEDesc": "Latihan kesalahan tata bahasa yang salah", + "fillInBlank": "Isi kekosongan dengan pilihan yang benar", + "learn": "Belajar", + "languageUpdated": "Bahasa target diperbarui!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index e53212b6d..17b5245cf 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-16 14:32:21.289462", + "@@last_modified": "2026-01-20 12:30:26.700297", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11839,5 +11839,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Praktika korrekta gramatikfehler", + "constructUseIncGEDesc": "Praktika inkorrekt gramatikfehler", + "fillInBlank": "Fyll i tomrummet med det korrekta valget", + "learn": "Lær", + "languageUpdated": "Mål sprog opdateret!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 9987104e8..5c8d17791 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:53.055805", + "@@last_modified": "2026-01-20 12:30:55.791406", "about": "Informazioni", "@about": { "type": "String", @@ -10868,5 +10868,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Pratica degli errori grammaticali corretti", + "constructUseIncGEDesc": "Pratica degli errori grammaticali scorretti", + "fillInBlank": "Compila lo spazio vuoto con la scelta corretta", + "learn": "Impara", + "languageUpdated": "Lingua target aggiornata!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index fda648203..30d520729 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-16 14:34:03.101722", + "@@last_modified": "2026-01-20 12:32:02.883097", "about": "このアプリについて", "@about": { "type": "String", @@ -11655,5 +11655,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "文法エラーの正しい練習", + "constructUseIncGEDesc": "文法エラーの不正確な練習", + "fillInBlank": "正しい選択肢で空欄を埋めてください", + "learn": "学ぶ", + "languageUpdated": "ターゲット言語が更新されました!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 9b1de6575..8026eff56 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-16 14:34:13.710408", + "@@last_modified": "2026-01-20 12:32:12.611848", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11895,5 +11895,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "მართებული გრამატიკული შეცდომების პრაქტიკა", + "constructUseIncGEDesc": "არასწორი გრამატიკული შეცდომების პრაქტიკა", + "fillInBlank": "შეავსეთ ცარიელი ადგილი სწორი არჩევანით", + "learn": "სწავლა", + "languageUpdated": "მიზნობრივი ენა განახლებულია!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index f2b772efe..f711d1304 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:31:56.042093", + "@@last_modified": "2026-01-20 12:29:56.840054", "about": "소개", "@about": { "type": "String", @@ -10973,5 +10973,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "문법 오류 수정 연습", + "constructUseIncGEDesc": "문법 오류 비정상 연습", + "fillInBlank": "올바른 선택으로 빈칸을 채우세요", + "learn": "배우다", + "languageUpdated": "목표 언어가 업데이트되었습니다!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index c46982084..888944382 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-16 14:33:32.087889", + "@@last_modified": "2026-01-20 12:31:36.164653", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11670,5 +11670,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Teisingos gramatikos klaidų praktika", + "constructUseIncGEDesc": "Neteisingos gramatikos klaidų praktika", + "fillInBlank": "Užpildykite tuščią vietą teisingu pasirinkimu", + "learn": "Mokytis", + "languageUpdated": "Tikslo kalba atnaujinta!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 66ea87a12..79a2fd834 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-16 14:33:21.744947", + "@@last_modified": "2026-01-20 12:31:22.453166", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10851,5 +10851,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Pareiza gramatikas kļūdu prakse", + "constructUseIncGEDesc": "Nepareiza gramatikas kļūdu prakse", + "fillInBlank": "Aizpildiet tukšo vietu ar pareizo izvēli", + "learn": "Mācīties", + "languageUpdated": "Mērķa valoda atjaunota!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 6e7c933f3..8d8592bdb 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:02.881359", + "@@last_modified": "2026-01-20 12:31:02.821968", "about": "Om", "@about": { "type": "String", @@ -11958,5 +11958,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Korrekt grammatikkfeil praksis", + "constructUseIncGEDesc": "Feil grammatikkfeil praksis", + "fillInBlank": "Fyll inn blanketten med riktig valg", + "learn": "Lær", + "languageUpdated": "Mål språk oppdatert!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 818b6bc60..755841dc9 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:40.784777", + "@@last_modified": "2026-01-20 12:31:42.507524", "about": "Over ons", "@about": { "type": "String", @@ -10865,5 +10865,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Oefening voor correcte grammatica", + "constructUseIncGEDesc": "Oefening voor onjuiste grammatica", + "fillInBlank": "Vul de lege ruimte in met de juiste keuze", + "learn": "Leren", + "languageUpdated": "Doeltaal bijgewerkt!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index a8f3c5d85..48ebf5396 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-16 14:33:54.131854", + "@@last_modified": "2026-01-20 12:31:54.796841", "about": "O aplikacji", "@about": { "type": "String", @@ -10863,5 +10863,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Ćwiczenie poprawnych błędów gramatycznych", + "constructUseIncGEDesc": "Ćwiczenie niepoprawnych błędów gramatycznych", + "fillInBlank": "Uzupełnij lukę poprawnym wyborem", + "learn": "Ucz się", + "languageUpdated": "Język docelowy zaktualizowany!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 186e08e77..1ac41d131 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:38.304072", + "@@last_modified": "2026-01-20 12:30:42.601068", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11965,5 +11965,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Prática de erro gramatical correto", + "constructUseIncGEDesc": "Prática de erro gramatical incorreto", + "fillInBlank": "Preencha a lacuna com a escolha correta", + "learn": "Aprender", + "languageUpdated": "Idioma de destino atualizado!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index a351a7185..da1140756 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:33.894131", + "@@last_modified": "2026-01-20 12:30:37.380939", "about": "Sobre", "@about": { "type": "String", @@ -11223,5 +11223,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Prática de erro gramatical correto", + "constructUseIncGEDesc": "Prática de erro gramatical incorreto", + "fillInBlank": "Preencha a lacuna com a escolha correta", + "learn": "Aprender", + "languageUpdated": "Idioma de destino atualizado!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 6439f1fd4..c0a85355c 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-16 14:33:14.369639", + "@@last_modified": "2026-01-20 12:31:13.609297", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11894,5 +11894,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Prática de erro gramatical correto", + "constructUseIncGEDesc": "Prática de erro gramatical incorreto", + "fillInBlank": "Preencha a lacuna com a escolha correta", + "learn": "Aprender", + "languageUpdated": "Idioma de destino atualizado!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index c39285495..5154c946d 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:32:16.042822", + "@@last_modified": "2026-01-20 12:30:21.408747", "about": "Despre", "@about": { "type": "String", @@ -11600,5 +11600,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Practică corectă a erorilor de gramatică", + "constructUseIncGEDesc": "Practică incorectă a erorilor de gramatică", + "fillInBlank": "Completați spațiul gol cu alegerea corectă", + "learn": "Învățați", + "languageUpdated": "Limba țintă a fost actualizată!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 42793afeb..ca196e82b 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-16 14:34:10.739852", + "@@last_modified": "2026-01-20 12:32:09.850624", "about": "О проекте", "@about": { "type": "String", @@ -10970,5 +10970,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Практика исправления грамматических ошибок", + "constructUseIncGEDesc": "Практика неправильных грамматических ошибок", + "fillInBlank": "Заполните пропуск правильным вариантом", + "learn": "Учить", + "languageUpdated": "Целевой язык обновлен!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index fa42e9927..ceb204959 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-16 14:32:18.425596", + "@@last_modified": "2026-01-20 12:30:24.498564", "about": "O aplikácii", "@about": { "type": "String", @@ -11949,5 +11949,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Cvičenie na opravu gramatických chýb", + "constructUseIncGEDesc": "Cvičenie na nesprávne gramatické chyby", + "fillInBlank": "Doplňte prázdne miesto správnou voľbou", + "learn": "Učte sa", + "languageUpdated": "Cieľový jazyk bol aktualizovaný!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index 782e7cbc3..9e4a1ff56 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-16 14:32:46.504404", + "@@last_modified": "2026-01-20 12:30:51.399294", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11946,5 +11946,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Praksa pravilne rabe slovnice", + "constructUseIncGEDesc": "Praksa nepravilne rabe slovnice", + "fillInBlank": "Izpolnite prazno mesto s pravilno izbiro", + "learn": "Učite se", + "languageUpdated": "Ciljni jezik je posodobljen!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index 481093692..fe98f2112 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:34:16.018548", + "@@last_modified": "2026-01-20 12:32:14.799697", "about": "О програму", "@about": { "type": "String", @@ -11967,5 +11967,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Vežba ispravne gramatike", + "constructUseIncGEDesc": "Vežba nepravilne gramatike", + "fillInBlank": "Popunite prazno mesto sa ispravnim izborom", + "learn": "Učite", + "languageUpdated": "Ciljni jezik je ažuriran!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 117b86faa..1d9224f70 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:56.060391", + "@@last_modified": "2026-01-20 12:31:57.066428", "about": "Om", "@about": { "type": "String", @@ -11343,5 +11343,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Korrekt grammatikfel övning", + "constructUseIncGEDesc": "Inkorrekt grammatikfel övning", + "fillInBlank": "Fyll i det tomma med rätt val", + "learn": "Lär dig", + "languageUpdated": "Målspråk uppdaterat!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 029b620fe..42b73b4d5 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:37.303345", + "@@last_modified": "2026-01-20 12:31:40.562260", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11089,5 +11089,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "சரியான இலக்கண பிழை பயிற்சி", + "constructUseIncGEDesc": "தவறான இலக்கண பிழை பயிற்சி", + "fillInBlank": "சரியான தேர்வுடன் காலியை நிரப்பவும்", + "learn": "கற்றுக்கொள்ளுங்கள்", + "languageUpdated": "இலக்கு மொழி புதுப்பிக்கப்பட்டது!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 89018dac2..3043221dc 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-16 14:33:29.527681", + "@@last_modified": "2026-01-20 12:31:32.903548", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11954,5 +11954,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "సరైన వ్యాకరణ దోషం అభ్యాసం", + "constructUseIncGEDesc": "తప్పు వ్యాకరణ దోషం అభ్యాసం", + "fillInBlank": "సరైన ఎంపికతో ఖాళీని నింపండి", + "learn": "కలవు", + "languageUpdated": "లక్ష్య భాష నవీకరించబడింది!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index e6d5c7213..ceb9ff50a 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-16 14:33:11.816510", + "@@last_modified": "2026-01-20 12:31:11.891533", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11923,5 +11923,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "การฝึกฝนข้อผิดพลาดทางไวยากรณ์ที่ถูกต้อง", + "constructUseIncGEDesc": "การฝึกฝนข้อผิดพลาดทางไวยากรณ์ที่ไม่ถูกต้อง", + "fillInBlank": "กรอกข้อมูลในช่องว่างด้วยตัวเลือกที่ถูกต้อง", + "learn": "เรียนรู้", + "languageUpdated": "อัปเดตภาษาที่ต้องการแล้ว!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index b3abdfecc..8d86e681c 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-16 14:33:26.649412", + "@@last_modified": "2026-01-20 12:31:28.469826", "about": "Hakkında", "@about": { "type": "String", @@ -11087,5 +11087,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Doğru dil bilgisi hatası pratiği", + "constructUseIncGEDesc": "Yanlış dil bilgisi hatası pratiği", + "fillInBlank": "Boşluğu doğru seçimle doldurun", + "learn": "Öğren", + "languageUpdated": "Hedef dil güncellendi!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index d4c6ba2bc..b8330d92c 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-16 14:32:56.338443", + "@@last_modified": "2026-01-20 12:30:57.869011", "about": "Про застосунок", "@about": { "type": "String", @@ -10859,5 +10859,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Практика виправлення граматичних помилок", + "constructUseIncGEDesc": "Практика неправильних граматичних помилок", + "fillInBlank": "Заповніть пропуск правильним вибором", + "learn": "Вчити", + "languageUpdated": "Цільова мова оновлена!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 22a5c5696..69d334c03 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:34.535886", + "@@last_modified": "2026-01-20 12:31:38.101874", "about": "Giới thiệu", "@about": { "type": "String", @@ -6435,5 +6435,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "Thực hành lỗi ngữ pháp đúng", + "constructUseIncGEDesc": "Thực hành lỗi ngữ pháp sai", + "fillInBlank": "Điền vào chỗ trống với lựa chọn đúng", + "learn": "Học", + "languageUpdated": "Ngôn ngữ mục tiêu đã được cập nhật!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index e51a60484..69867a6fc 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-16 14:32:49.483939", + "@@last_modified": "2026-01-20 12:30:53.854617", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11956,5 +11956,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "正確語法錯誤練習", + "constructUseIncGEDesc": "不正確語法錯誤練習", + "fillInBlank": "用正確的選擇填空", + "learn": "學習", + "languageUpdated": "目標語言已更新!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 212d37e2b..0953fb23e 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-16 14:33:45.909821", + "@@last_modified": "2026-01-20 12:31:47.017242", "about": "关于", "@about": { "type": "String", @@ -10856,5 +10856,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "正确语法错误练习", + "constructUseIncGEDesc": "不正确语法错误练习", + "fillInBlank": "用正确的选项填空", + "learn": "学习", + "languageUpdated": "目标语言已更新!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 01b708716..26f055262 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-16 14:33:16.316521", + "@@last_modified": "2026-01-20 12:31:16.140892", "about": "關於", "@about": { "type": "String", @@ -10863,5 +10863,30 @@ "@constructUseIncGCDesc": { "type": "String", "placeholders": {} + }, + "constructUseCorGEDesc": "正確語法錯誤練習", + "constructUseIncGEDesc": "不正確語法錯誤練習", + "fillInBlank": "用正確的選擇填空", + "learn": "學習", + "languageUpdated": "目標語言已更新!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 18a21f926..1dce476c6 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -32,7 +32,6 @@ import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activi import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart'; import 'package:fluffychat/pangea/analytics_data/analytics_update_dispatcher.dart'; import 'package:fluffychat/pangea/analytics_data/analytics_updater_mixin.dart'; -import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/analytics_misc/level_up/level_up_banner.dart'; @@ -2046,31 +2045,6 @@ class ChatController extends State return; } - final langCode = - pangeaMessageEvent?.originalSent?.langCode.split('-').first; - - if (LanguageMismatchRepo.shouldShowByEvent(event.eventId) && - langCode != null && - pangeaMessageEvent?.originalSent?.content.langCodeMatchesL2 == false && - room.client.allMyAnalyticsRooms.any((r) => r.madeForLang == langCode)) { - LanguageMismatchRepo.setEvent(event.eventId); - OverlayUtil.showLanguageMismatchPopup( - context: context, - targetId: event.eventId, - message: L10n.of(context).messageLanguageMismatchMessage, - targetLanguage: pangeaMessageEvent!.originalSent!.langCode, - onConfirm: () => showToolbar( - event, - pangeaMessageEvent: pangeaMessageEvent, - selectedToken: selectedToken, - mode: mode, - nextEvent: nextEvent, - prevEvent: prevEvent, - ), - ); - return; - } - final overlayEntry = MessageSelectionOverlay( chatController: this, event: event, @@ -2294,6 +2268,36 @@ class ChatController extends State ); } + Future updateLanguageOnMismatch(String target) async { + final messenger = ScaffoldMessenger.of(context); + messenger.hideCurrentSnackBar(); + final resp = await showFutureLoadingDialog( + context: context, + future: () async { + clearSelectedEvents(); + await MatrixState.pangeaController.userController.updateProfile( + (profile) { + profile.userSettings.targetLanguage = target; + return profile; + }, + waitForDataInSync: true, + ); + }, + ); + if (resp.isError) return; + if (mounted) { + messenger.hideCurrentSnackBar(); + messenger.showSnackBar( + SnackBar( + content: Text( + L10n.of(context).languageUpdated, + textAlign: TextAlign.center, + ), + ), + ); + } + } + void _onCloseIT() { if (choreographer.timesDismissedIT.value >= 3) { showDisableLanguageToolsPopup(); diff --git a/lib/pangea/learning_settings/language_mismatch_repo.dart b/lib/pangea/learning_settings/language_mismatch_repo.dart index 0096bff42..430f3a586 100644 --- a/lib/pangea/learning_settings/language_mismatch_repo.dart +++ b/lib/pangea/learning_settings/language_mismatch_repo.dart @@ -5,14 +5,10 @@ class LanguageMismatchRepo { static const Duration displayInterval = Duration(minutes: 30); static String _roomKey(String roomId) => 'language_mismatch_room_$roomId'; - static String _eventKey(String eventId) => 'language_mismatch_event_$eventId'; static bool shouldShowByRoom(String roomId) => _get(_roomKey(roomId)); - static bool shouldShowByEvent(String eventId) => _get(_eventKey(eventId)); static Future setRoom(String roomId) async => _set(_roomKey(roomId)); - static Future setEvent(String eventId) async => - _set(_eventKey(eventId)); static Future _set(String key) async { await _storage.write( diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart index 116fd451d..82f6f2e26 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart @@ -217,10 +217,32 @@ class SelectModeButtonsState extends State { } Future modeDisabled() async { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar( + final target = controller.messageEvent.originalSent?.langCode; + final messenger = ScaffoldMessenger.of(context); + messenger.hideCurrentSnackBar(); + messenger.showSnackBar( SnackBar( - content: Text(L10n.of(context).modeDisabled), + content: Row( + spacing: 12.0, + children: [ + Flexible( + child: Text( + L10n.of(context).modeDisabled, + textAlign: TextAlign.center, + ), + ), + if (target != null) + TextButton( + style: TextButton.styleFrom( + foregroundColor: + Theme.of(context).colorScheme.primaryContainer, + ), + onPressed: () => + widget.controller.updateLanguageOnMismatch(target), + child: Text(L10n.of(context).learn), + ), + ], + ), ), ); } @@ -405,6 +427,7 @@ class SelectModeButtonsState extends State { loading: controller.isLoading && mode == selectedMode, playing: mode == SelectMode.audio && playing, + color: theme.colorScheme.onPrimaryContainer, ), ), ), @@ -435,11 +458,13 @@ class _SelectModeButtonIcon extends StatelessWidget { final SelectMode mode; final bool loading; final bool playing; + final Color color; const _SelectModeButtonIcon({ required this.mode, this.loading = false, this.playing = false, + required this.color, }); @override @@ -458,10 +483,11 @@ class _SelectModeButtonIcon extends StatelessWidget { return Icon( playing ? Icons.pause_outlined : Icons.volume_up, size: 20, + color: color, ); } - return Icon(mode.icon, size: 20); + return Icon(mode.icon, size: 20, color: color); } } From 2b68f4a1fbee3b663de076b1629da192e1a86197 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 12:56:10 -0500 Subject: [PATCH 04/42] fix: disable other practice choices immeadiatley after correct choice made --- .../analytics_practice_page.dart | 8 ++++ .../analytics_practice_view.dart | 47 ++++++++++++------- .../choice_cards/game_choice_card.dart | 3 +- .../choice_cards/grammar_choice_card.dart | 3 ++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 67b7e2d26..d80bd7800 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -83,6 +83,7 @@ class AnalyticsPracticeState extends State ValueNotifier(null); final ValueNotifier progressNotifier = ValueNotifier(0.0); + final ValueNotifier enableChoicesNotifier = ValueNotifier(true); final Map> _choiceTexts = {}; final Map> _choiceEmojis = {}; @@ -106,6 +107,7 @@ class AnalyticsPracticeState extends State activityState.dispose(); activityTarget.dispose(); progressNotifier.dispose(); + enableChoicesNotifier.dispose(); super.dispose(); } @@ -190,6 +192,7 @@ class AnalyticsPracticeState extends State void _resetActivityState() { activityState.value = const AsyncState.loading(); activityTarget.value = null; + enableChoicesNotifier.value = true; } void _resetSessionState() { @@ -272,6 +275,7 @@ class AnalyticsPracticeState extends State Future _continueSession() async { if (_continuing) return; _continuing = true; + enableChoicesNotifier.value = true; try { if (activityState.value @@ -403,6 +407,10 @@ class AnalyticsPracticeState extends State ) async { if (_currentActivity == null) return; final activity = _currentActivity!; + final isCorrect = activity.multipleChoiceContent.isCorrect(choiceContent); + if (isCorrect) { + enableChoicesNotifier.value = false; + } // Update activity record PracticeRecordController.onSelectChoice( diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index 9b46524a9..6cbef9eae 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -346,25 +346,29 @@ class _ActivityChoicesWidget extends StatelessWidget { return Container( constraints: const BoxConstraints(maxHeight: 400.0), - child: Column( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - children: choices - .map( - (choice) => _ChoiceCard( - activity: value, - targetId: - controller.choiceTargetId(choice.choiceId), - choiceId: choice.choiceId, - onPressed: () => controller.onSelectChoice( - choice.choiceId, + child: ValueListenableBuilder( + valueListenable: controller.enableChoicesNotifier, + builder: (context, enabled, __) => Column( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + children: choices + .map( + (choice) => _ChoiceCard( + activity: value, + targetId: + controller.choiceTargetId(choice.choiceId), + choiceId: choice.choiceId, + onPressed: () => controller.onSelectChoice( + choice.choiceId, + ), + cardHeight: cardHeight, + choiceText: choice.choiceText, + choiceEmoji: choice.choiceEmoji, + enabled: enabled, ), - cardHeight: cardHeight, - choiceText: choice.choiceText, - choiceEmoji: choice.choiceEmoji, - ), - ) - .toList(), + ) + .toList(), + ), ), ); }, @@ -390,6 +394,7 @@ class _ChoiceCard extends StatelessWidget { final String choiceText; final String? choiceEmoji; + final bool enabled; const _ChoiceCard({ required this.activity, @@ -399,6 +404,7 @@ class _ChoiceCard extends StatelessWidget { required this.cardHeight, required this.choiceText, required this.choiceEmoji, + this.enabled = true, }); @override @@ -420,6 +426,7 @@ class _ChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: cardHeight, + isEnabled: enabled, ); case ActivityTypeEnum.lemmaAudio: @@ -432,6 +439,7 @@ class _ChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: cardHeight, + isEnabled: enabled, ); case ActivityTypeEnum.grammarCategory: @@ -445,6 +453,7 @@ class _ChoiceCard extends StatelessWidget { tag: choiceText, onPressed: onPressed, isCorrect: isCorrect, + enabled: enabled, ); case ActivityTypeEnum.grammarError: @@ -458,6 +467,7 @@ class _ChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: cardHeight, + isEnabled: enabled, child: Text(choiceText), ); @@ -471,6 +481,7 @@ class _ChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: cardHeight, + isEnabled: enabled, child: Text(choiceText), ); } diff --git a/lib/pangea/analytics_practice/choice_cards/game_choice_card.dart b/lib/pangea/analytics_practice/choice_cards/game_choice_card.dart index ad2e5c461..58c5095a1 100644 --- a/lib/pangea/analytics_practice/choice_cards/game_choice_card.dart +++ b/lib/pangea/analytics_practice/choice_cards/game_choice_card.dart @@ -62,6 +62,7 @@ class _GameChoiceCardState extends State Future _handleTap() async { if (!widget.isEnabled) return; + widget.onPressed(); if (widget.shouldFlip) { if (_controller.isAnimating || _revealed) return; @@ -73,8 +74,6 @@ class _GameChoiceCardState extends State if (_clicked) return; setState(() => _clicked = true); } - - widget.onPressed(); } @override diff --git a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart index c1299b611..1bfa5c831 100644 --- a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart +++ b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart @@ -15,6 +15,7 @@ class GrammarChoiceCard extends StatelessWidget { final VoidCallback onPressed; final bool isCorrect; final double height; + final bool enabled; const GrammarChoiceCard({ required this.choiceId, @@ -24,6 +25,7 @@ class GrammarChoiceCard extends StatelessWidget { required this.onPressed, required this.isCorrect, this.height = 72.0, + this.enabled = true, super.key, }); @@ -42,6 +44,7 @@ class GrammarChoiceCard extends StatelessWidget { onPressed: onPressed, isCorrect: isCorrect, height: height, + isEnabled: enabled, child: Text(copy), ); } From 86b69a67fab28e8546e6193f236f0444b932e0be Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 13:12:25 -0500 Subject: [PATCH 05/42] fix: pass manual IGC status after showing language mismatch popup --- lib/pages/chat/chat.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 1dce476c6..918a52709 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -2237,7 +2237,7 @@ class ChatController extends State bool autosend = false, }) async { if (shouldShowLanguageMismatchPopupByActivity) { - return showLanguageMismatchPopup(); + return showLanguageMismatchPopup(manual: manual); } await choreographer.requestWritingAssistance(manual: manual); @@ -2250,7 +2250,7 @@ class ChatController extends State } } - void showLanguageMismatchPopup() { + void showLanguageMismatchPopup({bool manual = false}) { if (!shouldShowLanguageMismatchPopupByActivity) { return; } @@ -2263,7 +2263,7 @@ class ChatController extends State message: L10n.of(context).languageMismatchDesc, targetLanguage: targetLanguage, onConfirm: () => WidgetsBinding.instance.addPostFrameCallback( - (_) => onRequestWritingAssistance(manual: false, autosend: true), + (_) => onRequestWritingAssistance(manual: manual, autosend: true), ), ); } From fecf8de443aa2708e622d797c9f07fb67c627b88 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 13:32:50 -0500 Subject: [PATCH 06/42] fix: restrict height of dropdowns in user menu popup --- lib/pangea/bot/widgets/bot_chat_settings_dialog.dart | 4 ++-- lib/pangea/chat_settings/widgets/language_level_dropdown.dart | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart index 47aa375e6..a748fdccc 100644 --- a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart +++ b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; @@ -155,6 +154,7 @@ class BotChatSettingsDialogState extends State { onChanged: _setLevel, enabled: !widget.room.isActivitySession, width: 300, + maxHeight: 300, ), DropdownButtonFormField2( customButton: _selectedVoice != null @@ -171,7 +171,7 @@ class BotChatSettingsDialogState extends State { ), isExpanded: true, dropdownStyleData: DropdownStyleData( - maxHeight: kIsWeb ? 250 : null, + maxHeight: 250, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(14.0), diff --git a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart index 11e9cd0d8..19bd2aa15 100644 --- a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart +++ b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart @@ -14,6 +14,7 @@ class LanguageLevelDropdown extends StatelessWidget { final bool enabled; final Color? backgroundColor; final double? width; + final double? maxHeight; const LanguageLevelDropdown({ super.key, @@ -23,6 +24,7 @@ class LanguageLevelDropdown extends StatelessWidget { this.enabled = true, this.backgroundColor, this.width, + this.maxHeight, }); @override @@ -46,7 +48,7 @@ class LanguageLevelDropdown extends StatelessWidget { ), isExpanded: true, dropdownStyleData: DropdownStyleData( - maxHeight: kIsWeb ? 500 : null, + maxHeight: maxHeight ?? (kIsWeb ? 500 : null), decoration: BoxDecoration( color: backgroundColor ?? Theme.of(context).colorScheme.surfaceContainerHigh, From 0c9767307fb3650b3ff515ee8b9a0233ba0bca01 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 13:35:57 -0500 Subject: [PATCH 07/42] chore: make sso button order consistent --- lib/pangea/login/pages/signup_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/login/pages/signup_view.dart b/lib/pangea/login/pages/signup_view.dart index 8492775ca..fdac63894 100644 --- a/lib/pangea/login/pages/signup_view.dart +++ b/lib/pangea/login/pages/signup_view.dart @@ -46,8 +46,8 @@ class SignupPageView extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - const PangeaSsoButton(provider: SSOProvider.google), const PangeaSsoButton(provider: SSOProvider.apple), + const PangeaSsoButton(provider: SSOProvider.google), ElevatedButton( onPressed: () => context.go( '/home/language/signup/email', From bf0c49035d7afa21acd29bc89f6740236791c1c6 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 14:08:11 -0500 Subject: [PATCH 08/42] fix: use latest edit to make representations --- .../event_wrappers/pangea_message_event.dart | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index f1322fdf2..22cf8ca1d 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -114,7 +114,7 @@ class PangeaMessageEvent { .map( (e) => RepresentationEvent( event: e, - parentMessageEvent: _event, + parentMessageEvent: _latestEdit, timeline: timeline, ), ) @@ -181,14 +181,14 @@ class PangeaMessageEvent { final original = PangeaRepresentation( langCode: lang ?? LanguageKeys.unknownLanguage, - text: _event.body, + text: _latestEdit.body, originalSent: true, originalWritten: false, ); _representations!.add( RepresentationEvent( - parentMessageEvent: _event, + parentMessageEvent: _latestEdit, content: original, tokens: tokens, choreo: _embeddedChoreo, @@ -202,7 +202,7 @@ class PangeaMessageEvent { e: err, s: s, data: { - "event": _event.toJson(), + "event": _latestEdit.toJson(), }, ); } @@ -211,7 +211,7 @@ class PangeaMessageEvent { try { _representations!.add( RepresentationEvent( - parentMessageEvent: _event, + parentMessageEvent: _latestEdit, content: PangeaRepresentation.fromJson( _latestEdit.content[ModelKey.originalWritten] as Map, @@ -229,7 +229,7 @@ class PangeaMessageEvent { e: err, s: s, data: { - "event": _event.toJson(), + "event": _latestEdit.toJson(), }, ); } @@ -278,7 +278,8 @@ class PangeaMessageEvent { /// Gets the message display text for the current language code. /// If the message display text is not available for the current language code, /// it returns the message body. - String get messageDisplayText => messageDisplayRepresentation?.text ?? body; + String get messageDisplayText => + messageDisplayRepresentation?.text ?? _latestEdit.body; TextDirection get textDirection => LanguageConstants.rtlLanguageCodes.contains(messageDisplayLangCode) @@ -293,12 +294,14 @@ class PangeaMessageEvent { RepresentationEvent? representationByLanguage( String langCode, { bool Function(RepresentationEvent)? filter, - }) => - representations.firstWhereOrNull( - (element) => - element.langCode.split("-")[0] == langCode.split("-")[0] && - (filter?.call(element) ?? true), - ); + }) { + representations.firstWhereOrNull( + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), + ); + return null; + } Event? getTextToSpeechLocal(String langCode, String text) { for (final audio in allAudio) { @@ -492,7 +495,7 @@ class PangeaMessageEvent { _representations = null; return room.sendPangeaEvent( content: representation.toJson(), - parentEventId: eventId, + parentEventId: _latestEdit.eventId, type: PangeaEventTypes.representation, ); } @@ -586,6 +589,7 @@ class PangeaMessageEvent { } Future requestRespresentationByL1() async { + debugPrint("LATEST EDIT: ${_latestEdit.toJson()}"); if (_l1Code == null || _l2Code == null) { throw Exception("Missing language codes"); } @@ -597,7 +601,9 @@ class PangeaMessageEvent { RepresentationEvent? rep; if (!includedIT) { // if the message didn't go through translation, get any l1 rep + debugPrint("REPRESENTATIONS: ${representations.length}"); rep = representationByLanguage(_l1Code!); + debugPrint("REP: $rep"); } else { // if the message went through translation, get the non-original // l1 rep since originalWritten could contain some l2 words @@ -614,6 +620,13 @@ class PangeaMessageEvent { ? (originalWritten?.langCode ?? _l1Code!) : (originalSent?.langCode ?? _l2Code!); + debugPrint("Original written content: $originalWrittenContent"); + debugPrint("Message display text: $messageDisplayText"); + debugPrint("Original sent: ${originalSent?.content.toJson()}"); + debugPrint( + "Message display rep: ${representationByLanguage(messageDisplayLangCode)?.content.toJson()}", + ); + final resp = await _requestRepresentation( includedIT ? originalWrittenContent : messageDisplayText, _l1Code!, @@ -661,7 +674,7 @@ class PangeaMessageEvent { ) async { final repEvent = await room.sendPangeaEvent( content: representation.toJson(), - parentEventId: eventId, + parentEventId: _latestEdit.eventId, type: PangeaEventTypes.representation, ); return repEvent?.eventId; From 68ec80a25a6af5327a0f197eb35c9af16987ac80 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 14:17:09 -0500 Subject: [PATCH 09/42] chore: show tooltip on full phonetic transcription widget --- .../phonetic_transcription_widget.dart | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index de6a7c494..8f5737125 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -69,60 +69,59 @@ class _PhoneticTranscriptionWidgetState final targetId = 'phonetic-transcription-${widget.text}-$hashCode'; return HoverBuilder( builder: (context, hovering) { - return GestureDetector( - onTap: () => _handleAudioTap(targetId), - child: AnimatedContainer( - duration: const Duration(milliseconds: 150), - decoration: BoxDecoration( - color: hovering - ? Colors.grey.withAlpha((0.2 * 255).round()) - : Colors.transparent, - borderRadius: BorderRadius.circular(6), - ), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: CompositedTransformTarget( - link: MatrixState.pAnyState.layerLinkAndKey(targetId).link, - child: PhoneticTranscriptionBuilder( - key: MatrixState.pAnyState.layerLinkAndKey(targetId).key, - textLanguage: widget.textLanguage, - text: widget.text, - reloadNotifier: widget.reloadNotifier, - builder: (context, controller) { - return switch (controller.state) { - AsyncError(error: final error) => - error is UnsubscribedException - ? ErrorIndicator( - message: L10n.of(context) - .subscribeToUnlockTranscriptions, - onTap: () { - MatrixState - .pangeaController.subscriptionController - .showPaywall(context); - }, - ) - : ErrorIndicator( - message: - L10n.of(context).failedToFetchTranscription, + return Tooltip( + message: + _isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio, + child: GestureDetector( + onTap: () => _handleAudioTap(targetId), + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + decoration: BoxDecoration( + color: hovering + ? Colors.grey.withAlpha((0.2 * 255).round()) + : Colors.transparent, + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: CompositedTransformTarget( + link: MatrixState.pAnyState.layerLinkAndKey(targetId).link, + child: PhoneticTranscriptionBuilder( + key: MatrixState.pAnyState.layerLinkAndKey(targetId).key, + textLanguage: widget.textLanguage, + text: widget.text, + reloadNotifier: widget.reloadNotifier, + builder: (context, controller) { + return switch (controller.state) { + AsyncError(error: final error) => + error is UnsubscribedException + ? ErrorIndicator( + message: L10n.of(context) + .subscribeToUnlockTranscriptions, + onTap: () { + MatrixState + .pangeaController.subscriptionController + .showPaywall(context); + }, + ) + : ErrorIndicator( + message: + L10n.of(context).failedToFetchTranscription, + ), + AsyncLoaded(value: final transcription) => Row( + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + transcription, + textScaler: TextScaler.noScaling, + style: widget.style ?? + Theme.of(context).textTheme.bodyMedium, + maxLines: widget.maxLines, + overflow: TextOverflow.ellipsis, + ), ), - AsyncLoaded(value: final transcription) => Row( - spacing: 8.0, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Text( - transcription, - textScaler: TextScaler.noScaling, - style: widget.style ?? - Theme.of(context).textTheme.bodyMedium, - maxLines: widget.maxLines, - overflow: TextOverflow.ellipsis, - ), - ), - Tooltip( - message: _isPlaying - ? L10n.of(context).stop - : L10n.of(context).playAudio, - child: Icon( + Icon( _isPlaying ? Icons.pause_outlined : Icons.volume_up, @@ -130,15 +129,15 @@ class _PhoneticTranscriptionWidgetState color: widget.iconColor ?? Theme.of(context).iconTheme.color, ), - ), - ], - ), - _ => const TextLoadingShimmer( - width: 125.0, - height: 20.0, - ), - }; - }, + ], + ), + _ => const TextLoadingShimmer( + width: 125.0, + height: 20.0, + ), + }; + }, + ), ), ), ), From 3f9f8867b87cb242e1308d07260e343b5ec53cd5 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:25:16 -0500 Subject: [PATCH 10/42] chore: shrink tooltip text size Also give it maxTimelineWidth in chat to match other widgets placement, and give slightly less padding between icons --- lib/pangea/chat/widgets/chat_input_bar.dart | 32 +++++++++++++------ .../instructions_inline_tooltip.dart | 17 ++++++---- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/lib/pangea/chat/widgets/chat_input_bar.dart b/lib/pangea/chat/widgets/chat_input_bar.dart index 78a0cdf4f..599c25abb 100644 --- a/lib/pangea/chat/widgets/chat_input_bar.dart +++ b/lib/pangea/chat/widgets/chat_input_bar.dart @@ -31,18 +31,30 @@ class ChatInputBar extends StatelessWidget { valueListenable: controller.choreographer.itController.open, builder: (context, open, __) { return open - ? InstructionsInlineTooltip( - instructionsEnum: InstructionsEnum.clickBestOption, - animate: false, - padding: EdgeInsets.only( - left: 16.0, - right: 16.0, - top: FluffyThemes.isColumnMode(context) ? 16.0 : 8.0, + ? Container( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), + alignment: Alignment.center, + child: InstructionsInlineTooltip( + instructionsEnum: InstructionsEnum.clickBestOption, + animate: false, + padding: EdgeInsets.only( + left: 16.0, + right: 16.0, + top: FluffyThemes.isColumnMode(context) ? 16.0 : 8.0, + ), ), ) - : ActivityRoleTooltip( - room: controller.room, - hide: controller.choreographer.itController.open, + : Container( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), + alignment: Alignment.center, + child: ActivityRoleTooltip( + room: controller.room, + hide: controller.choreographer.itController.open, + ), ); }, ), diff --git a/lib/pangea/instructions/instructions_inline_tooltip.dart b/lib/pangea/instructions/instructions_inline_tooltip.dart index 6059cb90a..5839d5657 100644 --- a/lib/pangea/instructions/instructions_inline_tooltip.dart +++ b/lib/pangea/instructions/instructions_inline_tooltip.dart @@ -134,25 +134,28 @@ class InlineTooltipState extends State crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Icon( - Icons.lightbulb, - size: 20, - color: Theme.of(context).colorScheme.onSurface, + Padding( + padding: const EdgeInsets.only(right: 6.0), + child: Icon( + Icons.lightbulb, + size: 20, + color: Theme.of(context).colorScheme.onSurface, + ), ), - const SizedBox(width: 8), Flexible( child: Center( child: Text( widget.message, style: widget.textStyle ?? (FluffyThemes.isColumnMode(context) - ? Theme.of(context).textTheme.titleLarge - : Theme.of(context).textTheme.bodyLarge), + ? Theme.of(context).textTheme.titleSmall + : Theme.of(context).textTheme.bodyMedium), textAlign: TextAlign.center, ), ), ), IconButton( + padding: const EdgeInsets.only(left: 6.0), constraints: const BoxConstraints(), icon: Icon( Icons.close_outlined, From ee882d3ea45c938939cf8141dc458dc3ef8159dc Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 14:31:15 -0500 Subject: [PATCH 11/42] feat: show audio message transcripts in vocab practice --- .../analytics_misc/example_message_util.dart | 28 ++++++++++++++----- .../analytics_practice_page.dart | 7 +---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/pangea/analytics_misc/example_message_util.dart b/lib/pangea/analytics_misc/example_message_util.dart index 43a3e659c..10012f75f 100644 --- a/lib/pangea/analytics_misc/example_message_util.dart +++ b/lib/pangea/analytics_misc/example_message_util.dart @@ -6,6 +6,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; class ExampleMessageUtil { static Future?> getExampleMessage( @@ -49,14 +50,27 @@ class ExampleMessageUtil { String? form, PangeaMessageEvent messageEvent, ) { - final tokens = messageEvent.messageDisplayRepresentation?.tokens; - if (tokens == null || tokens.isEmpty) return null; - final token = tokens.firstWhereOrNull( - (token) => token.text.content == form, - ); - if (token == null) return null; + PangeaToken? token; + String? text; - final text = messageEvent.messageDisplayText; + if (messageEvent.isAudioMessage) { + final stt = messageEvent.getSpeechToTextLocal(); + if (stt == null) return null; + final tokens = stt.transcript.sttTokens.map((t) => t.token).toList(); + token = tokens.firstWhereOrNull( + (token) => token.text.content == form, + ); + text = stt.transcript.text; + } else { + final tokens = messageEvent.messageDisplayRepresentation?.tokens; + if (tokens == null || tokens.isEmpty) return null; + token = tokens.firstWhereOrNull( + (token) => token.text.content == form, + ); + text = messageEvent.messageDisplayText; + } + + if (token == null) return null; final tokenText = token.text.content; int tokenIndex = text.indexOf(tokenText); if (tokenIndex == -1) return null; diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index d80bd7800..97ca1d1a7 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -442,12 +442,7 @@ class AnalyticsPracticeState extends State PracticeTarget target, ) async { final token = target.tokens.first; - final construct = switch (widget.type) { - ConstructTypeEnum.vocab => token.vocabConstructID, - ConstructTypeEnum.morph => token.morphIdByFeature(target.morphFeature!), - }; - - if (construct == null) return null; + final construct = target.targetTokenConstructID(token); String? form; if (widget.type == ConstructTypeEnum.morph) { From 79926a9aded0b46c1fdc14175da5d78b1b70fe8d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 14:51:44 -0500 Subject: [PATCH 12/42] moved some logic around --- lib/pages/chat/chat.dart | 2 ++ .../select_mode_buttons.dart | 19 ++++++++------- .../select_mode_controller.dart | 24 ++++++++++++++----- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 18a21f926..f96091ab4 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -2414,6 +2414,8 @@ class ChatController extends State ); if (reason == null) return; + + clearSelectedEvents(); await showFutureLoadingDialog( context: context, future: () => room.sendEvent( diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart index 6549403a9..b97537288 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart @@ -183,6 +183,9 @@ class SelectModeButtonsState extends State { SelectModeController get controller => widget.overlayController.selectModeController; + bool get _canRefresh => + messageEvent.eventId == widget.controller.refreshEventID; + Future updateMode(SelectMode? mode) async { if (mode == null) { matrix?.audioPlayer?.stop(); @@ -219,9 +222,13 @@ class SelectModeButtonsState extends State { } if (updatedMode == SelectMode.requestRegenerate) { - widget.controller.requestRegeneration( + await widget.controller.requestRegeneration( messageEvent.eventId, ); + + if (mounted) { + controller.setSelectMode(null); + } } } @@ -357,13 +364,7 @@ class SelectModeButtonsState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final modes = controller.readingAssistanceModes; - final allModes = controller.allModes - .where( - (mode) => - mode != SelectMode.requestRegenerate || - messageEvent.eventId == widget.controller.refreshEventID, - ) - .toList(); + final allModes = controller.allModes(enableRefresh: _canRefresh); return Material( type: MaterialType.transparency, child: SizedBox( @@ -373,7 +374,7 @@ class SelectModeButtonsState extends State { children: List.generate(allModes.length + 1, (index) { if (index < allModes.length) { final mode = allModes[index]; - final enabled = modes.contains(mode); + final enabled = modes(enableRefresh: _canRefresh).contains(mode); return Container( width: 45.0, alignment: Alignment.center, diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index e5de6dfaa..a5f243674 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -113,7 +113,6 @@ class SelectModeController with LemmaEmojiSetter { SelectMode.translate, SelectMode.practice, SelectMode.emoji, - SelectMode.requestRegenerate, ]; static List get _audioModes => [ @@ -131,7 +130,7 @@ class SelectModeController with LemmaEmojiSetter { (PangeaAudioFile, File?)? get audioFile => _audioLoader.value; - List get allModes { + List allModes({bool enableRefresh = false}) { final validTypes = {MessageTypes.Text, MessageTypes.Audio}; if (!messageEvent.event.status.isSent || messageEvent.event.type != EventTypes.Message || @@ -139,12 +138,18 @@ class SelectModeController with LemmaEmojiSetter { return []; } - return messageEvent.event.messageType == MessageTypes.Text + final types = messageEvent.event.messageType == MessageTypes.Text ? _textModes : _audioModes; + + if (enableRefresh) { + return [...types, SelectMode.requestRegenerate]; + } + + return types; } - List get readingAssistanceModes { + List readingAssistanceModes({bool enableRefresh = false}) { final validTypes = {MessageTypes.Text, MessageTypes.Audio}; if (!messageEvent.event.status.isSent || messageEvent.event.type != EventTypes.Message || @@ -152,6 +157,7 @@ class SelectModeController with LemmaEmojiSetter { return []; } + List modes = []; if (messageEvent.event.messageType == MessageTypes.Text) { final lang = messageEvent.messageDisplayLangCode.split("-").first; @@ -161,14 +167,20 @@ class SelectModeController with LemmaEmojiSetter { final matchesL1 = lang == MatrixState.pangeaController.userController.userL1!.langCodeShort; - return matchesL2 + modes = matchesL2 ? _textModes : matchesL1 ? [] : [SelectMode.translate]; + } else { + modes = _audioModes; } - return _audioModes; + if (enableRefresh) { + modes = [...modes, SelectMode.requestRegenerate]; + } + + return modes; } bool get isLoading => currentModeStateNotifier?.value is AsyncLoading; From bf5b75a2560d4dd5c0fd5142604a16f81f9fba70 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:54:58 -0500 Subject: [PATCH 13/42] chore: check for button in showMessageShimmer --- lib/pages/chat/chat.dart | 1 + lib/pages/chat/events/message.dart | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 18a21f926..045344fcf 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -2010,6 +2010,7 @@ class ChatController extends State bool showMessageShimmer(Event event) { if (event.type != EventTypes.Message) return false; + if (!(event.eventId == buttonEventID)) return false; if (event.messageType == MessageTypes.Text) { return !InstructionsEnum.clickTextMessages.isToggledOff; } diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 6344caa36..d3ac2306b 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -606,10 +606,9 @@ class Message extends StatelessWidget { // #Pangea child: ShimmerBackground( enabled: controller - .showMessageShimmer( - event, - ) && - isButton, + .showMessageShimmer( + event, + ), // Pangea# child: Container( decoration: From da8b99b78b5475c76e6490e6c2bb4c0a472e6e54 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:02:03 -0500 Subject: [PATCH 14/42] fix: show error message when not enough data for practice --- .../analytics_practice_session_repo.dart | 6 ++++++ lib/pangea/analytics_practice/analytics_practice_view.dart | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index 378dc4486..d50407c1b 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart @@ -15,6 +15,8 @@ import 'package:fluffychat/pangea/practice_activities/message_activity_request.d import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/widgets/matrix.dart'; +class InsufficientDataException implements Exception {} + class AnalyticsPracticeSessionRepo { static Future get( ConstructTypeEnum type, @@ -67,6 +69,10 @@ class AnalyticsPracticeSessionRepo { } } + if (targets.isEmpty) { + throw InsufficientDataException(); + } + final session = AnalyticsPracticeSessionModel( userL1: MatrixState.pangeaController.userController.userL1!.langCode, userL2: MatrixState.pangeaController.userController.userL2!.langCode, diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index 6cbef9eae..e5e57404b 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; +import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/audio_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/game_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/grammar_choice_card.dart'; @@ -84,7 +85,11 @@ class AnalyticsPracticeView extends StatelessWidget { builder: (context, state, __) { return switch (state) { AsyncError(:final error) => - ErrorIndicator(message: error.toString()), + ErrorIndicator( + message: error is InsufficientDataException + ? L10n.of(context).notEnoughToPractice + : error.toString(), + ), AsyncLoaded(:final value) => value.isComplete ? CompletedActivitySessionView(state.value, controller) From 5008ce70557d954c8fecf008b1d6d86ee87267db Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:18:31 -0500 Subject: [PATCH 15/42] fix: clear selected token in activity vocab display on word card dismissed --- .../activity_vocab_widget.dart | 81 ++++++++++++++----- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart index a7fe9e7f6..0a05e5c47 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart @@ -106,27 +106,17 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { OverlayUtil.showPositionedCard( overlayKey: target, context: context, - cardToShow: StatefulBuilder( - builder: (context, setState) => WordZoomWidget( - token: PangeaTokenText( - content: v.lemma, - length: v.lemma.characters.length, - offset: 0, - ), - construct: ConstructIdentifier( - lemma: v.lemma, - type: ConstructTypeEnum.vocab, - category: v.pos, - ), - langCode: widget.langCode, - onClose: () { - MatrixState.pAnyState.closeOverlay(target); - setState(() => _selectedVocab = null); - }, - onDismissNewWordOverlay: () { - if (mounted) setState(() {}); - }, - ), + cardToShow: _WordCardWrapper( + v: v, + langCode: widget.langCode, + target: target, + onClose: () { + if (mounted) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() => _selectedVocab = null), + ); + } + }, ), transformTargetId: target, closePrevOverlay: false, @@ -204,3 +194,52 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { ); } } + +class _WordCardWrapper extends StatefulWidget { + final Vocab v; + final String langCode; + final String target; + final VoidCallback onClose; + + const _WordCardWrapper({ + required this.v, + required this.langCode, + required this.target, + required this.onClose, + }); + + @override + State<_WordCardWrapper> createState() => _WordCardWrapperState(); +} + +class _WordCardWrapperState extends State<_WordCardWrapper> { + @override + void dispose() { + widget.onClose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return WordZoomWidget( + token: PangeaTokenText( + content: widget.v.lemma, + length: widget.v.lemma.characters.length, + offset: 0, + ), + construct: ConstructIdentifier( + lemma: widget.v.lemma, + type: ConstructTypeEnum.vocab, + category: widget.v.pos, + ), + langCode: widget.langCode, + onClose: () { + MatrixState.pAnyState.closeOverlay(widget.target); + widget.onClose(); + }, + onDismissNewWordOverlay: () { + if (mounted) setState(() {}); + }, + ); + } +} From a8c4b1d7c2583d0d5d63a4b4fb5c2bdcb68357c3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:24:38 -0500 Subject: [PATCH 16/42] chore: throw expection while loading practice session is user is unsubscribed --- lib/pangea/analytics_practice/analytics_practice_page.dart | 2 ++ .../analytics_practice/analytics_practice_session_repo.dart | 6 ++++++ lib/pangea/analytics_practice/analytics_practice_view.dart | 6 ++---- lib/utils/localized_exception_extension.dart | 5 +++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 97ca1d1a7..9f3c0573e 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -245,6 +245,8 @@ class AnalyticsPracticeState extends State Future _startSession() async { await _waitForAnalytics(); await _sessionLoader.load(); + if (_sessionLoader.isError) return; + progressNotifier.value = _sessionLoader.value!.progress; await _continueSession(); } diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index d50407c1b..37fde47fd 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_constants.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; +import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; @@ -21,6 +22,11 @@ class AnalyticsPracticeSessionRepo { static Future get( ConstructTypeEnum type, ) async { + if (MatrixState.pangeaController.subscriptionController.isSubscribed == + false) { + throw UnsubscribedException(); + } + final r = Random(); final activityTypes = ActivityTypeEnum.analyticsPracticeTypes(type); diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index e5e57404b..1d7c04031 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -5,7 +5,6 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; -import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/audio_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/game_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/grammar_choice_card.dart'; @@ -20,6 +19,7 @@ import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart' import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -86,9 +86,7 @@ class AnalyticsPracticeView extends StatelessWidget { return switch (state) { AsyncError(:final error) => ErrorIndicator( - message: error is InsufficientDataException - ? L10n.of(context).notEnoughToPractice - : error.toString(), + message: error.toLocalizedString(context), ), AsyncLoaded(:final value) => value.isComplete diff --git a/lib/utils/localized_exception_extension.dart b/lib/utils/localized_exception_extension.dart index d50dbbbfa..17df8cf58 100644 --- a/lib/utils/localized_exception_extension.dart +++ b/lib/utils/localized_exception_extension.dart @@ -8,6 +8,7 @@ import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/utils/other_party_can_receive.dart'; import 'uia_request_manager.dart'; @@ -34,6 +35,10 @@ extension LocalizedExceptionExtension on Object { if (this is UnsubscribedException) { return L10n.of(context).unsubscribedResponseError; } + + if (this is InsufficientDataException) { + return L10n.of(context).notEnoughToPractice; + } // Pangea# if (this is FileTooBigMatrixException) { final exception = this as FileTooBigMatrixException; From e31f4d5b418382231d51b6232337c4caafcbc8b4 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:03:30 -0500 Subject: [PATCH 17/42] fix: account for blocked and capped constructs in analytics download model --- .../space_analytics_summary_model.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart index ddccf10e9..a176fff8d 100644 --- a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart +++ b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart @@ -97,7 +97,6 @@ class SpaceAnalyticsSummaryModel { Set blockedConstructs, int numCompletedActivities, ) { - int totalXP = 0; int numWordsTyped = 0; int numChoicesCorrect = 0; int numChoicesIncorrect = 0; @@ -114,7 +113,9 @@ class SpaceAnalyticsSummaryModel { mergeTable.addConstructsByUses(e.content.uses, blockedConstructs); for (final use in e.content.uses) { - totalXP += use.xp; + final id = use.identifier; + if (blockedConstructs.contains(id)) continue; + allUses.add(use); if (use.useType.summaryEnumType == @@ -132,8 +133,7 @@ class SpaceAnalyticsSummaryModel { sentEventIds.add(use.metadata.eventId!); } - final id = use.identifier; - final existing = id.type == ConstructTypeEnum.vocab + final existing = use.identifier.type == ConstructTypeEnum.vocab ? aggregatedVocab[id] : aggregatedMorph[id]; @@ -189,6 +189,10 @@ class SpaceAnalyticsSummaryModel { } } + final totalXP = cleanedVocab.values + .fold(0, (sum, entry) => sum + entry.points) + + cleanedMorph.values.fold(0, (sum, entry) => sum + entry.points); + final level = DerivedAnalyticsDataModel.calculateLevelWithXp(totalXP); final uniqueVocabCount = cleanedVocab.length; final uniqueMorphCount = cleanedMorph.length; From 2701f747596949f6abc6675a018f089044ddcfd2 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:15:32 -0500 Subject: [PATCH 18/42] chore: save voice in TTS events and re-request if requested voice doesn't match saved voice --- lib/pangea/common/constants/model_keys.dart | 1 + .../event_wrappers/pangea_message_event.dart | 14 ++++++++++---- .../text_to_speech_response_model.dart | 11 ++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pangea/common/constants/model_keys.dart b/lib/pangea/common/constants/model_keys.dart index 9296826b9..d248e680e 100644 --- a/lib/pangea/common/constants/model_keys.dart +++ b/lib/pangea/common/constants/model_keys.dart @@ -109,6 +109,7 @@ class ModelKey { static const String transcription = "transcription"; static const String botTranscription = 'bot_transcription'; + static const String voice = "voice"; // bot options static const String languageLevel = "difficulty"; diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 22cf8ca1d..66d2395d1 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -303,7 +303,11 @@ class PangeaMessageEvent { return null; } - Event? getTextToSpeechLocal(String langCode, String text) { + Event? getTextToSpeechLocal( + String langCode, + String text, + String? voice, + ) { for (final audio in allAudio) { final dataMap = audio.content.tryGetMap(ModelKey.transcription); if (dataMap == null || !dataMap.containsKey(ModelKey.tokens)) continue; @@ -313,7 +317,9 @@ class PangeaMessageEvent { dataMap as dynamic, ); - if (audioData.langCode == langCode && audioData.text == text) { + if (audioData.langCode == langCode && + audioData.text == text && + audioData.voice == voice) { return audio; } } catch (e, s) { @@ -368,7 +374,7 @@ class PangeaMessageEvent { String langCode, String? voice, ) async { - final local = getTextToSpeechLocal(langCode, messageDisplayText); + final local = getTextToSpeechLocal(langCode, messageDisplayText, voice); if (local != null) { final file = await local.getPangeaAudioFile(); if (file != null) return file; @@ -424,7 +430,7 @@ class PangeaMessageEvent { 'waveform': response.waveform, }, ModelKey.transcription: response - .toPangeaAudioEventData(rep?.text ?? body, langCode) + .toPangeaAudioEventData(rep?.text ?? body, langCode, voice) .toJson(), }, ).then((eventId) async { diff --git a/lib/pangea/text_to_speech/text_to_speech_response_model.dart b/lib/pangea/text_to_speech/text_to_speech_response_model.dart index 24cc71e1a..645005a3d 100644 --- a/lib/pangea/text_to_speech/text_to_speech_response_model.dart +++ b/lib/pangea/text_to_speech/text_to_speech_response_model.dart @@ -41,11 +41,16 @@ class TextToSpeechResponseModel { "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), }; - PangeaAudioEventData toPangeaAudioEventData(String text, String langCode) { + PangeaAudioEventData toPangeaAudioEventData( + String text, + String langCode, + String? voice, + ) { return PangeaAudioEventData( text: text, langCode: langCode, tokens: ttsTokens, + voice: voice, ); } } @@ -91,11 +96,13 @@ class PangeaAudioEventData { final String text; final String langCode; final List tokens; + final String? voice; PangeaAudioEventData({ required this.text, required this.langCode, required this.tokens, + this.voice, }); factory PangeaAudioEventData.fromJson(dynamic json) => PangeaAudioEventData( @@ -106,6 +113,7 @@ class PangeaAudioEventData { .map((x) => TTSToken.fromJson(x)) .toList(), ), + voice: json[ModelKey.voice] as String?, ); Map toJson() => { @@ -113,5 +121,6 @@ class PangeaAudioEventData { ModelKey.langCode: langCode, ModelKey.tokens: List>.from(tokens.map((x) => x.toJson())), + if (voice != null) ModelKey.voice: voice, }; } From 0d346269961e08e90dfcb61ac4e8615b6d18edc9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:28:17 -0500 Subject: [PATCH 19/42] fix: filter RoomMemberChangeType.other events from timeline --- lib/pangea/course_chats/course_chats_page.dart | 5 +++-- .../matrix_sdk_extensions/filtered_timeline_extension.dart | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pangea/course_chats/course_chats_page.dart b/lib/pangea/course_chats/course_chats_page.dart index 6945aadfe..1b962497b 100644 --- a/lib/pangea/course_chats/course_chats_page.dart +++ b/lib/pangea/course_chats/course_chats_page.dart @@ -57,7 +57,9 @@ class CourseChatsController extends State @override void initState() { - loadHierarchy(reload: true); + loadHierarchy(reload: true).then( + (_) => _joinDefaultChats(), + ); // Listen for changes to the activeSpace's hierarchy, // and reload the hierarchy when they come through @@ -212,7 +214,6 @@ class CourseChatsController extends State try { await _loadHierarchy(activeSpace: room, reload: reload); - if (mounted) await _joinDefaultChats(); if (mounted) { final futures = [ loadRoomSummaries( diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 65bd3119c..f15e1f119 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -64,7 +64,8 @@ extension IsStateExtension on Event { bool get isVisibleInPangeaGui { if (!room.showActivityChatUI) { return type != EventTypes.RoomMember || - roomMemberChangeType != RoomMemberChangeType.avatar; + (roomMemberChangeType != RoomMemberChangeType.avatar && + roomMemberChangeType != RoomMemberChangeType.other); } return type != EventTypes.RoomMember; From 819527cbd86a44e539b389b9b6ccf74b21fb228a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:52:37 -0500 Subject: [PATCH 20/42] chore: store font size settings per-user --- lib/pages/settings_style/settings_style.dart | 16 +++++--- .../common/controllers/pangea_controller.dart | 14 ++++--- lib/pangea/user/style_settings_repo.dart | 40 +++++++++++++++++++ lib/widgets/matrix.dart | 14 +++++-- 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 lib/pangea/user/style_settings_repo.dart diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 49b0b003c..5a0092a9a 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -157,10 +157,16 @@ class SettingsStyleController extends State { void changeFontSizeFactor(double d) { setState(() => AppConfig.fontSizeFactor = d); - Matrix.of(context).store.setString( - SettingKeys.fontSizeFactor, - AppConfig.fontSizeFactor.toString(), - ); + // #Pangea + // Matrix.of(context).store.setString( + // SettingKeys.fontSizeFactor, + // AppConfig.fontSizeFactor.toString(), + // ); + StyleSettingsRepo.setFontSizeFactor( + Matrix.of(context).client.userID!, + AppConfig.fontSizeFactor, + ); + // Pangea# } @override diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index b1339a3e4..5257753d3 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/pangea_push_rules_extension.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/pangea/user/user_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../utils/firebase_analytics.dart'; @@ -55,7 +56,7 @@ class PangeaController { TtsController.setAvailableLanguages(); } - void _onLogin(BuildContext context) { + void _onLogin(BuildContext context, String? userID) { initControllers(); _registerSubscriptions(); @@ -64,6 +65,10 @@ class PangeaController { Provider.of(context, listen: false).setLocale(l1); }); subscriptionController.reinitialize(); + + StyleSettingsRepo.fontSizeFactor(userID!).then((factor) { + AppConfig.fontSizeFactor = factor; + }); } void _onLogout(BuildContext context) { @@ -91,7 +96,7 @@ class PangeaController { _onLogout(context); break; case LoginState.loggedIn: - _onLogin(context); + _onLogin(context, userID); break; } @@ -122,9 +127,7 @@ class PangeaController { } Future _clearCache({List exclude = const []}) async { - final List> futures = [ - matrixState.store.setString(SettingKeys.fontSizeFactor, ''), - ]; + final List> futures = []; for (final key in _storageKeys) { if (exclude.contains(key)) continue; futures.add(GetStorage(key).erase()); @@ -142,7 +145,6 @@ class PangeaController { ); } - AppConfig.fontSizeFactor = 1.0; await Future.wait(futures); } diff --git a/lib/pangea/user/style_settings_repo.dart b/lib/pangea/user/style_settings_repo.dart new file mode 100644 index 000000000..16fae1236 --- /dev/null +++ b/lib/pangea/user/style_settings_repo.dart @@ -0,0 +1,40 @@ +import 'package:get_storage/get_storage.dart'; + +class _StyleSettings { + final double fontSizeFactor; + + const _StyleSettings({ + this.fontSizeFactor = 1.0, + }); + + Map toJson() { + return { + 'fontSizeFactor': fontSizeFactor, + }; + } + + factory _StyleSettings.fromJson(Map json) { + return _StyleSettings( + fontSizeFactor: (json['fontSizeFactor'] as num?)?.toDouble() ?? 1.0, + ); + } +} + +class StyleSettingsRepo { + static final GetStorage _storage = GetStorage("style_settings"); + + static Future fontSizeFactor(String userId) async { + await GetStorage.init("style_settings"); + final json = + _storage.read>('${userId}_style_settings'); + final settings = + json != null ? _StyleSettings.fromJson(json) : const _StyleSettings(); + return settings.fontSizeFactor; + } + + static Future setFontSizeFactor(String userId, double factor) async { + await GetStorage.init("style_settings"); + final settings = _StyleSettings(fontSizeFactor: factor); + await _storage.write('${userId}_style_settings', settings.toJson()); + } +} diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index ef3a4b537..78544c300 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -26,6 +26,7 @@ import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/join_codes/space_code_controller.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -553,9 +554,16 @@ class MatrixState extends State with WidgetsBindingObserver { } void initSettings() { - AppConfig.fontSizeFactor = - double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? - AppConfig.fontSizeFactor; + // #Pangea + // AppConfig.fontSizeFactor = + // double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? + // AppConfig.fontSizeFactor; + if (client.isLogged()) { + StyleSettingsRepo.fontSizeFactor(client.userID!).then((factor) { + AppConfig.fontSizeFactor = factor; + }); + } + // Pangea# AppConfig.renderHtml = store.getBool(SettingKeys.renderHtml) ?? AppConfig.renderHtml; From 8a2f4747c99068fdaef552938937ccac8af6fc42 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 09:16:55 -0500 Subject: [PATCH 21/42] fix: oops, don't return null from representationByLanguage (#5301) --- .../event_wrappers/pangea_message_event.dart | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 66d2395d1..de462ed95 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -294,14 +294,12 @@ class PangeaMessageEvent { RepresentationEvent? representationByLanguage( String langCode, { bool Function(RepresentationEvent)? filter, - }) { - representations.firstWhereOrNull( - (element) => - element.langCode.split("-")[0] == langCode.split("-")[0] && - (filter?.call(element) ?? true), - ); - return null; - } + }) => + representations.firstWhereOrNull( + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), + ); Event? getTextToSpeechLocal( String langCode, From 529f12e0288c10a10e99863bcc6ee02bc919b6f1 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:44:21 -0500 Subject: [PATCH 22/42] 5259 bot settings language settings (#5305) * feat: add voice to user model * update bot settings on language / learning settings update * use room summary to determine member count * translations --- lib/l10n/intl_ar.arb | 7 +- lib/l10n/intl_be.arb | 7 +- lib/l10n/intl_bn.arb | 7 +- lib/l10n/intl_bo.arb | 7 +- lib/l10n/intl_ca.arb | 7 +- lib/l10n/intl_cs.arb | 7 +- lib/l10n/intl_da.arb | 7 +- lib/l10n/intl_de.arb | 7 +- lib/l10n/intl_el.arb | 7 +- lib/l10n/intl_en.arb | 3 +- lib/l10n/intl_eo.arb | 7 +- lib/l10n/intl_es.arb | 7 +- lib/l10n/intl_et.arb | 7 +- lib/l10n/intl_eu.arb | 7 +- lib/l10n/intl_fa.arb | 7 +- lib/l10n/intl_fi.arb | 7 +- lib/l10n/intl_fil.arb | 7 +- lib/l10n/intl_fr.arb | 7 +- lib/l10n/intl_ga.arb | 7 +- lib/l10n/intl_gl.arb | 7 +- lib/l10n/intl_he.arb | 7 +- lib/l10n/intl_hi.arb | 7 +- lib/l10n/intl_hr.arb | 7 +- lib/l10n/intl_hu.arb | 7 +- lib/l10n/intl_ia.arb | 7 +- lib/l10n/intl_id.arb | 7 +- lib/l10n/intl_ie.arb | 7 +- lib/l10n/intl_it.arb | 7 +- lib/l10n/intl_ja.arb | 7 +- lib/l10n/intl_ka.arb | 7 +- lib/l10n/intl_ko.arb | 7 +- lib/l10n/intl_lt.arb | 7 +- lib/l10n/intl_lv.arb | 7 +- lib/l10n/intl_nb.arb | 7 +- lib/l10n/intl_nl.arb | 7 +- lib/l10n/intl_pl.arb | 7 +- lib/l10n/intl_pt.arb | 7 +- lib/l10n/intl_pt_BR.arb | 7 +- lib/l10n/intl_pt_PT.arb | 7 +- lib/l10n/intl_ro.arb | 7 +- lib/l10n/intl_ru.arb | 7 +- lib/l10n/intl_sk.arb | 7 +- lib/l10n/intl_sl.arb | 7 +- lib/l10n/intl_sr.arb | 7 +- lib/l10n/intl_sv.arb | 7 +- lib/l10n/intl_ta.arb | 7 +- lib/l10n/intl_te.arb | 7 +- lib/l10n/intl_th.arb | 7 +- lib/l10n/intl_tr.arb | 7 +- lib/l10n/intl_uk.arb | 7 +- lib/l10n/intl_vi.arb | 7 +- lib/l10n/intl_yue.arb | 7 +- lib/l10n/intl_zh.arb | 7 +- lib/l10n/intl_zh_Hant.arb | 7 +- lib/pages/chat/events/message.dart | 14 - .../activity_participant_indicator.dart | 10 - .../bot/widgets/bot_chat_settings_dialog.dart | 194 ------------- .../widgets/bot_settings_language_icon.dart | 57 ---- .../models/bot_options_model.dart | 117 ++++---- .../utils/bot_client_extension.dart | 60 ++-- .../common/controllers/pangea_controller.dart | 7 +- .../p_settings_switch_list_tile.dart | 1 - .../learning_settings/settings_learning.dart | 35 ++- .../settings_learning_view.dart | 273 ++++++++---------- .../learning_settings/voice_dropdown.dart | 53 ++++ .../message_practice/message_audio_card.dart | 4 +- .../select_mode_controller.dart | 3 +- lib/pangea/user/user_controller.dart | 2 + lib/pangea/user/user_model.dart | 9 +- lib/widgets/avatar.dart | 10 +- .../member_actions_popup_menu_button.dart | 20 +- 71 files changed, 625 insertions(+), 618 deletions(-) delete mode 100644 lib/pangea/bot/widgets/bot_chat_settings_dialog.dart delete mode 100644 lib/pangea/bot/widgets/bot_settings_language_icon.dart create mode 100644 lib/pangea/learning_settings/voice_dropdown.dart diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 70fa0fc97..3642fb278 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-20 12:31:24.671375", + "@@last_modified": "2026-01-21 10:42:52.513008", "about": "حول", "@about": { "type": "String", @@ -11107,5 +11107,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صوت بوت بانجيا", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index d1ff0babf..f7593b805 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-20 12:31:06.570011", + "@@last_modified": "2026-01-21 10:42:45.087111", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11989,5 +11989,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голас Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 28c180422..451b639ab 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:49.464406", + "@@last_modified": "2026-01-21 10:43:01.204800", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11994,5 +11994,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "প্যাঙ্গিয়া বটের কণ্ঠ", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index 800c24594..a87317500 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-20 12:31:44.969872", + "@@last_modified": "2026-01-21 10:42:59.058441", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10644,5 +10644,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot voz", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 5eb437241..7c6d2d7fa 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:09.126351", + "@@last_modified": "2026-01-21 10:42:46.152113", "about": "Quant a", "@about": { "type": "String", @@ -10914,5 +10914,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Veu del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 321734d12..fef398790 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-20 12:31:00.323945", + "@@last_modified": "2026-01-21 10:42:42.984636", "about": "O aplikaci", "@about": { "type": "String", @@ -11497,5 +11497,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 372a6e7c8..46dbe3d2c 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-20 12:30:10.761209", + "@@last_modified": "2026-01-21 10:42:22.819549", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11951,5 +11951,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stemme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 634b76211..2f9214e0b 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-20 12:30:47.434524", + "@@last_modified": "2026-01-21 10:42:37.466783", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10897,5 +10897,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot Stimme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 176fb6f07..b90f11cdb 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-20 12:31:59.503296", + "@@last_modified": "2026-01-21 10:43:05.204211", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Φωνή Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index abc4a3d63..e90dfca6b 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5056,5 +5056,6 @@ "constructUseIncGEDesc": "Incorrect grammar error practice", "fillInBlank": "Fill in the blank with the correct choice", "learn": "Learn", - "languageUpdated": "Target language updated!" + "languageUpdated": "Target language updated!", + "voiceDropdownTitle": "Pangea Bot voice" } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 5914ed46b..942654f9f 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:32:07.490560", + "@@last_modified": "2026-01-21 10:43:08.060333", "about": "Prio", "@about": { "type": "String", @@ -11979,5 +11979,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voĉo de Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 144e71870..4e77d447b 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-20 12:29:59.898184", + "@@last_modified": "2026-01-21 10:42:19.343875", "about": "Acerca de", "@about": { "type": "String", @@ -8124,5 +8124,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 5d4e3f6c2..30c19ef49 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-20 12:30:45.162738", + "@@last_modified": "2026-01-21 10:42:36.742735", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11161,5 +11161,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Boti hääl", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index 225d963e7..d913d4db3 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-20 12:30:40.144354", + "@@last_modified": "2026-01-21 10:42:34.277635", "about": "Honi buruz", "@about": { "type": "String", @@ -10890,5 +10890,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot ahotsa", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 977dd42b8..6e395bb02 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:52.231221", + "@@last_modified": "2026-01-21 10:43:02.232868", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11622,5 +11622,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صدای ربات پانژیا", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 6184adfec..707872200 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-20 12:30:08.099637", + "@@last_modified": "2026-01-21 10:42:21.857816", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11513,5 +11513,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Botin ääni", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index 986aa4015..a51993f5e 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-20 12:31:19.884620", + "@@last_modified": "2026-01-21 10:42:50.349708", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11866,5 +11866,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Boses ng Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index c279297f0..58d082c74 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-20 12:32:20.398562", + "@@last_modified": "2026-01-21 10:43:12.775361", "about": "À propos", "@about": { "type": "String", @@ -11214,5 +11214,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index c923a3bbd..b6e3263eb 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-20 12:32:18.033824", + "@@last_modified": "2026-01-21 10:43:11.760663", "@customReaction": { "type": "String", "placeholders": {} @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "guth Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 3ebc2943d..28655e165 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-20 12:30:05.234280", + "@@last_modified": "2026-01-21 10:42:20.142432", "about": "Acerca de", "@about": { "type": "String", @@ -10887,5 +10887,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index 7ad96af48..f36caa914 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:31.884050", + "@@last_modified": "2026-01-21 10:42:30.598205", "about": "אודות", "@about": { "type": "String", @@ -11939,5 +11939,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "קול של פנגיאה בוט", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index a465a07b2..611db19dd 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-20 12:32:05.240015", + "@@last_modified": "2026-01-21 10:43:07.054066", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11975,5 +11975,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "पैंगिया बॉट की आवाज़", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 64f6cccf8..17ea4d22e 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-20 12:30:29.823787", + "@@last_modified": "2026-01-21 10:42:29.581714", "about": "Informacije", "@about": { "type": "String", @@ -11262,5 +11262,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot glas", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 8d543fa7e..490d3b22f 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-20 12:30:13.762355", + "@@last_modified": "2026-01-21 10:42:24.298395", "about": "Névjegy", "@about": { "type": "String", @@ -10891,5 +10891,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot hang", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 4fda86d9e..54974eb51 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-20 12:30:35.012898", + "@@last_modified": "2026-01-21 10:42:31.403920", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11968,5 +11968,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 50917db2e..2d02858ce 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:15.788809", + "@@last_modified": "2026-01-21 10:42:25.871973", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10881,5 +10881,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Suara Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index 17b5245cf..caf59b6b4 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-20 12:30:26.700297", + "@@last_modified": "2026-01-21 10:42:28.431424", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11864,5 +11864,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot guth", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 5c8d17791..9284e09b3 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:55.791406", + "@@last_modified": "2026-01-21 10:42:40.690823", "about": "Informazioni", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voce del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index 30d520729..c83be27b4 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-20 12:32:02.883097", + "@@last_modified": "2026-01-21 10:43:06.128364", "about": "このアプリについて", "@about": { "type": "String", @@ -11680,5 +11680,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "パンゲアボットの声", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 8026eff56..d8e600706 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-20 12:32:12.611848", + "@@last_modified": "2026-01-21 10:43:09.701292", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11920,5 +11920,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "პანჯეა ბოტის ხმა", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index f711d1304..ed5289391 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:29:56.840054", + "@@last_modified": "2026-01-21 10:42:17.603097", "about": "소개", "@about": { "type": "String", @@ -10998,5 +10998,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "판게아 봇 음성", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 888944382..35ca44e54 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-20 12:31:36.164653", + "@@last_modified": "2026-01-21 10:42:55.447627", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11695,5 +11695,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balsas", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 79a2fd834..64bbaf040 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-20 12:31:22.453166", + "@@last_modified": "2026-01-21 10:42:51.147364", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10876,5 +10876,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balss", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 8d8592bdb..5615f0550 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:02.821968", + "@@last_modified": "2026-01-21 10:42:43.891386", "about": "Om", "@about": { "type": "String", @@ -11983,5 +11983,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot-stemme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 755841dc9..b021f9ad1 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:42.507524", + "@@last_modified": "2026-01-21 10:42:58.326124", "about": "Over ons", "@about": { "type": "String", @@ -10890,5 +10890,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stem", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 48ebf5396..9e3d32b91 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-20 12:31:54.796841", + "@@last_modified": "2026-01-21 10:43:03.257336", "about": "O aplikacji", "@about": { "type": "String", @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Głos bota Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 1ac41d131..f80792528 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:42.601068", + "@@last_modified": "2026-01-21 10:42:35.392050", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11990,5 +11990,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index da1140756..933c9f8f3 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:37.380939", + "@@last_modified": "2026-01-21 10:42:32.545549", "about": "Sobre", "@about": { "type": "String", @@ -11248,5 +11248,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index c0a85355c..643c67fee 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-20 12:31:13.609297", + "@@last_modified": "2026-01-21 10:42:48.198355", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11919,5 +11919,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 5154c946d..7e868c193 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:21.408747", + "@@last_modified": "2026-01-21 10:42:26.609980", "about": "Despre", "@about": { "type": "String", @@ -11625,5 +11625,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Vocea Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index ca196e82b..e08d12d6e 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-20 12:32:09.850624", + "@@last_modified": "2026-01-21 10:43:08.936913", "about": "О проекте", "@about": { "type": "String", @@ -10995,5 +10995,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос бота Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index ceb204959..26dd291f9 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-20 12:30:24.498564", + "@@last_modified": "2026-01-21 10:42:27.675920", "about": "O aplikácii", "@about": { "type": "String", @@ -11974,5 +11974,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index 9e4a1ff56..a9889e3a1 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-20 12:30:51.399294", + "@@last_modified": "2026-01-21 10:42:38.801732", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11971,5 +11971,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index fe98f2112..c32c01635 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:32:14.799697", + "@@last_modified": "2026-01-21 10:43:10.733270", "about": "О програму", "@about": { "type": "String", @@ -11992,5 +11992,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 1d9224f70..9bffc0e88 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:57.066428", + "@@last_modified": "2026-01-21 10:43:04.367889", "about": "Om", "@about": { "type": "String", @@ -11368,5 +11368,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot röst", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 42b73b4d5..5592467b6 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:40.562260", + "@@last_modified": "2026-01-21 10:42:57.399546", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11114,5 +11114,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "பாஙேஆ பாட்டின் குரல்", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 3043221dc..9e1e5fe94 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-20 12:31:32.903548", + "@@last_modified": "2026-01-21 10:42:54.652537", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11979,5 +11979,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "పాంజియా బాట్ శబ్దం", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index ceb9ff50a..b07d69284 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-20 12:31:11.891533", + "@@last_modified": "2026-01-21 10:42:47.175182", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "เสียงของ Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 8d86e681c..fe247f360 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-20 12:31:28.469826", + "@@last_modified": "2026-01-21 10:42:53.424381", "about": "Hakkında", "@about": { "type": "String", @@ -11112,5 +11112,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot sesi", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index b8330d92c..7734ef4fe 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-20 12:30:57.869011", + "@@last_modified": "2026-01-21 10:42:41.558170", "about": "Про застосунок", "@about": { "type": "String", @@ -10884,5 +10884,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 69d334c03..0d9c741ab 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:38.101874", + "@@last_modified": "2026-01-21 10:42:56.498587", "about": "Giới thiệu", "@about": { "type": "String", @@ -6460,5 +6460,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Giọng nói của Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 69867a6fc..15558c19a 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-20 12:30:53.854617", + "@@last_modified": "2026-01-21 10:42:39.595731", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11981,5 +11981,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 聲音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 0953fb23e..e3b4d480b 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-20 12:31:47.017242", + "@@last_modified": "2026-01-21 10:43:00.180549", "about": "关于", "@about": { "type": "String", @@ -10881,5 +10881,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "潘吉亚机器人声音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 26f055262..3491a8f78 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:16.140892", + "@@last_modified": "2026-01-21 10:42:49.437032", "about": "關於", "@about": { "type": "String", @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 語音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index fe4f9bf1f..fc2ea9d32 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -13,8 +13,6 @@ import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/pangea/chat/extensions/custom_room_display_extension.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart'; @@ -477,18 +475,6 @@ class Message extends StatelessWidget { presenceBackgroundColor: wallpaperMode ? Colors.transparent : null, - // #Pangea - miniIcon: - user.id == BotName.byEnvironment - ? BotSettingsLanguageIcon( - user: user, - ) - : null, - presenceOffset: - user.id == BotName.byEnvironment - ? const Offset(0, 0) - : null, - // Pangea# ); }, ), diff --git a/lib/pangea/activity_sessions/activity_participant_indicator.dart b/lib/pangea/activity_sessions/activity_participant_indicator.dart index 58fd0403f..61370cec7 100644 --- a/lib/pangea/activity_sessions/activity_participant_indicator.dart +++ b/lib/pangea/activity_sessions/activity_participant_indicator.dart @@ -6,8 +6,6 @@ import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; @@ -70,14 +68,6 @@ class ActivityParticipantIndicator extends StatelessWidget { name: userId!.localpart, size: 60.0, userId: userId, - miniIcon: - room != null && userId == BotName.byEnvironment - ? BotSettingsLanguageIcon(user: user!) - : null, - presenceOffset: - room != null && userId == BotName.byEnvironment - ? const Offset(0, 0) - : null, ) : ClipRRect( borderRadius: BorderRadius.circular(30), diff --git a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart deleted file mode 100644 index a748fdccc..000000000 --- a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; -import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; -import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; -import 'package:fluffychat/pangea/languages/language_model.dart'; -import 'package:fluffychat/pangea/languages/p_language_store.dart'; -import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -class BotChatSettingsDialog extends StatefulWidget { - final Room room; - - const BotChatSettingsDialog({ - required this.room, - super.key, - }); - - @override - BotChatSettingsDialogState createState() => BotChatSettingsDialogState(); -} - -class BotChatSettingsDialogState extends State { - LanguageModel? _selectedLang; - LanguageLevelTypeEnum? _selectedLevel; - String? _selectedVoice; - - @override - void initState() { - final botSettings = widget.room.botOptions; - final activityPlan = _isActivity ? widget.room.activityPlan : null; - - _selectedLevel = activityPlan?.req.cefrLevel ?? botSettings?.languageLevel; - _selectedVoice = botSettings?.targetVoice; - final lang = - activityPlan?.req.targetLanguage ?? botSettings?.targetLanguage; - if (lang != null) { - _selectedLang = PLanguageStore.byLangCode(lang); - } - super.initState(); - } - - bool get _isActivity => widget.room.isActivitySession; - - Future _setLanguage(LanguageModel? lang) async { - setState(() { - _selectedLang = lang; - _selectedVoice = null; - }); - - final model = widget.room.botOptions ?? BotOptionsModel(); - model.targetLanguage = lang?.langCode; - model.targetVoice = null; - - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'langCode': lang?.langCode, - }, - ); - } - } - - Future _setLevel(LanguageLevelTypeEnum? level) async { - if (level == null) return; - - setState(() => _selectedLevel = level); - final model = widget.room.botOptions ?? BotOptionsModel(); - model.languageLevel = level; - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'level': level.name, - }, - ); - } - } - - Future _setVoice(String? voice) async { - setState(() => _selectedVoice = voice); - final model = widget.room.botOptions ?? BotOptionsModel(); - model.targetVoice = voice; - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'voice': voice, - }, - ); - } - } - - @override - Widget build(BuildContext context) { - return Material( - type: MaterialType.transparency, - child: Column( - spacing: 12.0, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (widget.room.isActivitySession) - ListTile( - contentPadding: const EdgeInsets.all(0.0), - minLeadingWidth: 12.0, - leading: Icon( - Icons.info_outline, - size: 12.0, - color: Theme.of(context).disabledColor, - ), - title: Text( - L10n.of(context).activitySettingsOverrideWarning, - style: TextStyle( - color: Theme.of(context).disabledColor, - fontSize: 12.0, - ), - ), - ) - else - const SizedBox(), - PLanguageDropdown( - onChange: _setLanguage, - initialLanguage: _selectedLang, - languages: - MatrixState.pangeaController.pLanguageStore.targetOptions, - isL2List: true, - decorationText: L10n.of(context).targetLanguage, - enabled: !widget.room.isActivitySession, - ), - LanguageLevelDropdown( - initialLevel: _selectedLevel, - onChanged: _setLevel, - enabled: !widget.room.isActivitySession, - width: 300, - maxHeight: 300, - ), - DropdownButtonFormField2( - customButton: _selectedVoice != null - ? CustomDropdownTextButton(text: _selectedVoice!) - : null, - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - ), - decoration: InputDecoration( - labelText: L10n.of(context).voice, - ), - isExpanded: true, - dropdownStyleData: DropdownStyleData( - maxHeight: 250, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(14.0), - ), - ), - items: (_selectedLang?.voices ?? []).map((voice) { - return DropdownMenuItem( - value: voice, - child: Text(voice), - ); - }).toList(), - onChanged: _setVoice, - value: _selectedVoice, - ), - const SizedBox(), - ], - ), - ); - } -} diff --git a/lib/pangea/bot/widgets/bot_settings_language_icon.dart b/lib/pangea/bot/widgets/bot_settings_language_icon.dart deleted file mode 100644 index 837bba59f..000000000 --- a/lib/pangea/bot/widgets/bot_settings_language_icon.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; - -class BotSettingsLanguageIcon extends StatelessWidget { - final User user; - - const BotSettingsLanguageIcon({ - super.key, - required this.user, - }); - - @override - Widget build(BuildContext context) { - final room = user.room; - String? langCode = room.botOptions?.targetLanguage; - if (room.isActivitySession && room.activityPlan != null) { - langCode = room.activityPlan!.req.targetLanguage; - } - if (langCode == null) { - return const SizedBox(); - } - - langCode = langCode.split('-').first; - return InkWell( - borderRadius: BorderRadius.circular(32.0), - onTap: room.isRoomAdmin - ? () => showMemberActionsPopupMenu( - context: context, - user: user, - room: room, - ) - : null, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.0), - color: Theme.of(context).colorScheme.primaryContainer, - ), - padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: Text( - langCode, - style: TextStyle( - fontSize: 10.0, - color: Theme.of(context).colorScheme.onPrimaryContainer, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - ); - } -} diff --git a/lib/pangea/chat_settings/models/bot_options_model.dart b/lib/pangea/chat_settings/models/bot_options_model.dart index 4108890d4..e21f141b9 100644 --- a/lib/pangea/chat_settings/models/bot_options_model.dart +++ b/lib/pangea/chat_settings/models/bot_options_model.dart @@ -8,23 +8,23 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class BotOptionsModel { - LanguageLevelTypeEnum languageLevel; - String topic; - List keywords; - bool safetyModeration; - String mode; - String? discussionTopic; - String? discussionKeywords; - bool? discussionTriggerReactionEnabled; - String? discussionTriggerReactionKey; - String? customSystemPrompt; - bool? customTriggerReactionEnabled; - String? customTriggerReactionKey; - String? textAdventureGameMasterInstructions; - String? targetLanguage; - String? targetVoice; + final LanguageLevelTypeEnum languageLevel; + final String topic; + final List keywords; + final bool safetyModeration; + final String mode; + final String? discussionTopic; + final String? discussionKeywords; + final bool? discussionTriggerReactionEnabled; + final String? discussionTriggerReactionKey; + final String? customSystemPrompt; + final bool? customTriggerReactionEnabled; + final String? customTriggerReactionKey; + final String? textAdventureGameMasterInstructions; + final String? targetLanguage; + final String? targetVoice; - BotOptionsModel({ + const BotOptionsModel({ //////////////////////////////////////////////////////////////////////////// // General Bot Options //////////////////////////////////////////////////////////////////////////// @@ -133,50 +133,45 @@ class BotOptionsModel { } } - //TODO: define enum with all possible values - updateBotOption(String key, dynamic value) { - switch (key) { - case ModelKey.languageLevel: - languageLevel = value; - break; - case ModelKey.safetyModeration: - safetyModeration = value; - break; - case ModelKey.mode: - mode = value; - break; - case ModelKey.discussionTopic: - discussionTopic = value; - break; - case ModelKey.discussionKeywords: - discussionKeywords = value; - break; - case ModelKey.discussionTriggerReactionEnabled: - discussionTriggerReactionEnabled = value; - break; - case ModelKey.discussionTriggerReactionKey: - discussionTriggerReactionKey = value; - break; - case ModelKey.customSystemPrompt: - customSystemPrompt = value; - break; - case ModelKey.customTriggerReactionEnabled: - customTriggerReactionEnabled = value; - break; - case ModelKey.customTriggerReactionKey: - customTriggerReactionKey = value; - break; - case ModelKey.textAdventureGameMasterInstructions: - textAdventureGameMasterInstructions = value; - break; - case ModelKey.targetLanguage: - targetLanguage = value; - break; - case ModelKey.targetVoice: - targetVoice = value; - break; - default: - throw Exception('Invalid key for bot options - $key'); - } + BotOptionsModel copyWith({ + LanguageLevelTypeEnum? languageLevel, + String? topic, + List? keywords, + bool? safetyModeration, + String? mode, + String? discussionTopic, + String? discussionKeywords, + bool? discussionTriggerReactionEnabled, + String? discussionTriggerReactionKey, + String? customSystemPrompt, + bool? customTriggerReactionEnabled, + String? customTriggerReactionKey, + String? textAdventureGameMasterInstructions, + String? targetLanguage, + String? targetVoice, + }) { + return BotOptionsModel( + languageLevel: languageLevel ?? this.languageLevel, + topic: topic ?? this.topic, + keywords: keywords ?? this.keywords, + safetyModeration: safetyModeration ?? this.safetyModeration, + mode: mode ?? this.mode, + discussionTopic: discussionTopic ?? this.discussionTopic, + discussionKeywords: discussionKeywords ?? this.discussionKeywords, + discussionTriggerReactionEnabled: discussionTriggerReactionEnabled ?? + this.discussionTriggerReactionEnabled, + discussionTriggerReactionKey: + discussionTriggerReactionKey ?? this.discussionTriggerReactionKey, + customSystemPrompt: customSystemPrompt ?? this.customSystemPrompt, + customTriggerReactionEnabled: + customTriggerReactionEnabled ?? this.customTriggerReactionEnabled, + customTriggerReactionKey: + customTriggerReactionKey ?? this.customTriggerReactionKey, + textAdventureGameMasterInstructions: + textAdventureGameMasterInstructions ?? + this.textAdventureGameMasterInstructions, + targetLanguage: targetLanguage ?? this.targetLanguage, + targetVoice: targetVoice ?? this.targetVoice, + ); } } diff --git a/lib/pangea/chat_settings/utils/bot_client_extension.dart b/lib/pangea/chat_settings/utils/bot_client_extension.dart index 6fbb1c636..5021d19ed 100644 --- a/lib/pangea/chat_settings/utils/bot_client_extension.dart +++ b/lib/pangea/chat_settings/utils/bot_client_extension.dart @@ -7,23 +7,19 @@ import 'package:fluffychat/pangea/chat/constants/default_power_level.dart'; import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart'; import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/user/user_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; extension BotClientExtension on Client { bool get hasBotDM => rooms.any((r) => r.isBotDM); + Room? get botDM => rooms.firstWhereOrNull((r) => r.isBotDM); - Room? get botDM => rooms.firstWhereOrNull( - (room) { - if (room.isDirectChat && - room.directChatMatrixID == BotName.byEnvironment) { - return true; - } - if (room.botOptions?.mode == BotMode.directChat) { - return true; - } - return false; - }, - ); + // All 2-member rooms with the bot + List get targetBotChats => rooms.where((r) { + if (r.isBotDM) return true; + if (r.summary.mJoinedMemberCount != 2) return false; + return r.getParticipants().any((u) => u.id == BotName.byEnvironment); + }).toList(); Future startChatWithBot() => startDirectChat( BotName.byEnvironment, @@ -45,27 +41,31 @@ extension BotClientExtension on Client { ], ); - Future updateBotOptions() async { - if (!isLogged() || botDM == null) return; + Future updateBotOptions(UserSettings userSettings) async { + final rooms = targetBotChats; + if (rooms.isEmpty) return; - final targetLanguage = - MatrixState.pangeaController.userController.userL2?.langCode; - final cefrLevel = MatrixState - .pangeaController.userController.profile.userSettings.cefrLevel; - final updateBotOptions = botDM!.botOptions ?? BotOptionsModel(); + final futures = []; + for (final room in rooms) { + final botOptions = room.botOptions ?? const BotOptionsModel(); + final targetLanguage = userSettings.targetLanguage; + final languageLevel = userSettings.cefrLevel; + final voice = userSettings.voice; - if (updateBotOptions.targetLanguage == targetLanguage && - updateBotOptions.languageLevel == cefrLevel) { - return; + if (botOptions.targetLanguage == targetLanguage && + botOptions.languageLevel == languageLevel && + botOptions.targetVoice == voice) { + continue; + } + + final updated = botOptions.copyWith( + targetLanguage: targetLanguage, + languageLevel: languageLevel, + targetVoice: voice, + ); + futures.add(room.setBotOptions(updated)); } - if (targetLanguage != null && - updateBotOptions.targetLanguage != targetLanguage) { - updateBotOptions.targetVoice = null; - } - - updateBotOptions.targetLanguage = targetLanguage; - updateBotOptions.languageLevel = cefrLevel; - await botDM!.setBotOptions(updateBotOptions); + await Future.wait(futures); } } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 5257753d3..e90d6d9a9 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -117,8 +117,9 @@ class PangeaController { userController.languageStream.stream.listen(_onLanguageUpdate); _settingsSubscription?.cancel(); - _settingsSubscription = userController.settingsUpdateStream.stream - .listen((_) => matrixState.client.updateBotOptions()); + _settingsSubscription = userController.settingsUpdateStream.stream.listen( + (update) => matrixState.client.updateBotOptions(update.userSettings), + ); _joinSpaceSubscription?.cancel(); _joinSpaceSubscription ??= matrixState.client.onSync.stream @@ -176,7 +177,7 @@ class PangeaController { } _clearCache(exclude: exclude); - matrixState.client.updateBotOptions(); + matrixState.client.updateBotOptions(userController.profile.userSettings); } static final List _storageKeys = [ diff --git a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart index 188debae1..a0e928def 100644 --- a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart +++ b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart @@ -44,7 +44,6 @@ class PSettingsSwitchListTileState @override Widget build(BuildContext context) { return SwitchListTile.adaptive( - contentPadding: EdgeInsets.zero, value: currentValue, title: Text(widget.title), activeThumbColor: AppConfig.activeToggleColor, diff --git a/lib/pangea/learning_settings/settings_learning.dart b/lib/pangea/learning_settings/settings_learning.dart index f3e5f8765..d9bdcfa69 100644 --- a/lib/pangea/learning_settings/settings_learning.dart +++ b/lib/pangea/learning_settings/settings_learning.dart @@ -41,7 +41,6 @@ class SettingsLearningController extends State { PangeaController pangeaController = MatrixState.pangeaController; late Profile _profile; - final GlobalKey formKey = GlobalKey(); String? languageMatchError; final ScrollController scrollController = ScrollController(); @@ -110,18 +109,16 @@ class SettingsLearningController extends State { updateToolSetting(ToolSetting.enableTTS, false); } - if (formKey.currentState!.validate()) { - await showFutureLoadingDialog( - context: context, - future: () async => pangeaController.userController - .updateProfile( - (_) => _profile, - waitForDataInSync: true, - ) - .timeout(const Duration(seconds: 15)), - ); - Navigator.of(context).pop(); - } + await showFutureLoadingDialog( + context: context, + future: () async => pangeaController.userController + .updateProfile( + (_) => _profile, + waitForDataInSync: true, + ) + .timeout(const Duration(seconds: 15)), + ); + Navigator.of(context).pop(); } Future resetInstructionTooltips() async { @@ -153,11 +150,12 @@ class SettingsLearningController extends State { LanguageModel? sourceLanguage, LanguageModel? targetLanguage, }) async { - if (sourceLanguage != null) { + if (sourceLanguage != null && sourceLanguage != selectedSourceLanguage) { _profile.userSettings.sourceLanguage = sourceLanguage.langCode; } - if (targetLanguage != null) { + if (targetLanguage != null && targetLanguage != selectedTargetLanguage) { _profile.userSettings.targetLanguage = targetLanguage.langCode; + _profile.userSettings.voice = null; if (!_profile.toolSettings.enableTTS && isTTSSupported) { updateToolSetting(ToolSetting.enableTTS, true); } @@ -181,6 +179,11 @@ class SettingsLearningController extends State { if (mounted) setState(() {}); } + void setVoice(String? voice) { + _profile.userSettings.voice = voice; + if (mounted) setState(() {}); + } + void changeCountry(Country? country) { _profile.userSettings.country = country?.name; if (mounted) setState(() {}); @@ -343,6 +346,8 @@ class SettingsLearningController extends State { LanguageLevelTypeEnum get cefrLevel => _profile.userSettings.cefrLevel; + String? get selectedVoice => _profile.userSettings.voice; + Country? get country => CountryService().findByName(_profile.userSettings.country); diff --git a/lib/pangea/learning_settings/settings_learning_view.dart b/lib/pangea/learning_settings/settings_learning_view.dart index d6037fc87..1f11f2872 100644 --- a/lib/pangea/learning_settings/settings_learning_view.dart +++ b/lib/pangea/learning_settings/settings_learning_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; @@ -14,6 +13,7 @@ import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; import 'package:fluffychat/pangea/learning_settings/p_settings_switch_list_tile.dart'; import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/voice_dropdown.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -45,21 +45,23 @@ class SettingsLearningView extends StatelessWidget { ) : null, ), - body: Form( - key: controller.formKey, - child: ListTileTheme( - iconColor: Theme.of(context).textTheme.bodyLarge!.color, - child: MaxWidthBody( - withScrolling: false, - child: Column( - children: [ - Expanded( - child: SingleChildScrollView( - controller: controller.scrollController, + body: ListTileTheme( + iconColor: Theme.of(context).textTheme.bodyLarge!.color, + child: MaxWidthBody( + withScrolling: false, + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + controller: controller.scrollController, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32.0), child: Column( + spacing: 16.0, children: [ Padding( - padding: const EdgeInsets.all(16.0), + padding: + const EdgeInsets.symmetric(horizontal: 16.0), child: Column( spacing: 16.0, children: [ @@ -99,171 +101,112 @@ class SettingsLearningView extends StatelessWidget { .colorScheme .surfaceContainerHigh, ), - AnimatedSize( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: controller.userL1?.langCodeShort == - controller.userL2?.langCodeShort - ? Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, + if (controller.userL1?.langCodeShort == + controller.userL2?.langCodeShort) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Row( + spacing: 8.0, + children: [ + Icon( + Icons.info_outlined, + color: Theme.of(context) + .colorScheme + .error, + ), + Flexible( + child: Text( + L10n.of(context) + .noIdenticalLanguages, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .error, + ), ), - child: Row( - spacing: 8.0, - children: [ - Icon( - Icons.info_outlined, - color: Theme.of(context) - .colorScheme - .error, - ), - Flexible( - child: Text( - L10n.of(context) - .noIdenticalLanguages, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .error, - ), - ), - ), - ], - ), - ) - : const SizedBox.shrink(), - ), - CountryPickerDropdown(controller), + ), + ], + ), + ), LanguageLevelDropdown( initialLevel: controller.cefrLevel, onChanged: controller.setCefrLevel, ), + VoiceDropdown( + value: controller.selectedVoice, + language: controller.selectedTargetLanguage, + onChanged: controller.setVoice, + ), + CountryPickerDropdown(controller), GenderDropdown( initialGender: controller.gender, onChanged: controller.setGender, ), - Container( - decoration: BoxDecoration( - border: Border.all( - color: Colors.white54, - ), - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - ProfileSettingsSwitchListTile.adaptive( - defaultValue: - controller.getToolSetting( - ToolSetting.autoIGC, - ), - title: ToolSetting.autoIGC - .toolName(context), - subtitle: ToolSetting.autoIGC - .toolDescription(context), - onChange: (bool value) => - controller.updateToolSetting( - ToolSetting.autoIGC, - value, - ), - enabled: true, - ), - ProfileSettingsSwitchListTile.adaptive( - defaultValue: - controller.getToolSetting( - ToolSetting.enableAutocorrect, - ), - title: ToolSetting.enableAutocorrect - .toolName(context), - subtitle: ToolSetting - .enableAutocorrect - .toolDescription(context), - onChange: (bool value) { - controller.updateToolSetting( - ToolSetting.enableAutocorrect, - value, - ); - if (value) { - controller - .showKeyboardSettingsDialog(); - } - }, - enabled: true, - ), - ], - ), - ), - for (final toolSetting - in ToolSetting.values.where( - (tool) => - tool.isAvailableSetting && - tool != ToolSetting.autoIGC && - tool != ToolSetting.enableAutocorrect, - )) - Column( - children: [ - ProfileSettingsSwitchListTile.adaptive( - defaultValue: controller - .getToolSetting(toolSetting), - title: toolSetting.toolName(context), - subtitle: toolSetting == - ToolSetting.enableTTS && - !controller.isTTSSupported - ? null - : toolSetting - .toolDescription(context), - onChange: (bool value) => - controller.updateToolSetting( - toolSetting, - value, - ), - ), - ], - ), - SwitchListTile.adaptive( - value: controller.publicProfile, - onChanged: controller.setPublicProfile, - title: Text( - L10n.of(context).publicProfileTitle, - ), - subtitle: Text( - L10n.of(context).publicProfileDesc, - ), - activeThumbColor: - AppConfig.activeToggleColor, - contentPadding: EdgeInsets.zero, - ), - ResetInstructionsListTile( - controller: controller, - ), ], ), ), + ...ToolSetting.values + .where( + (tool) => tool.isAvailableSetting, + ) + .map( + (toolSetting) => _ProfileSwitchTile( + value: + controller.getToolSetting(toolSetting), + setting: toolSetting, + onChanged: (v) { + controller.updateToolSetting( + toolSetting, + v, + ); + if (v && + toolSetting == + ToolSetting.enableTTS) { + controller.showKeyboardSettingsDialog(); + } + }, + ), + ), + SwitchListTile.adaptive( + value: controller.publicProfile, + onChanged: controller.setPublicProfile, + title: Text( + L10n.of(context).publicProfileTitle, + ), + subtitle: Text( + L10n.of(context).publicProfileDesc, + ), + activeThumbColor: AppConfig.activeToggleColor, + ), + ResetInstructionsListTile( + controller: controller, + ), ], ), ), ), - Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: controller.haveSettingsBeenChanged - ? controller.submit - : null, - child: Text(L10n.of(context).saveChanges), - ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: controller.haveSettingsBeenChanged + ? controller.submit + : null, + child: Text(L10n.of(context).saveChanges), ), ), - ], - ), + ), + ], ), ), ), ); if (!controller.widget.isDialog) return dialogContent; - return FullWidthDialog( dialogContent: dialogContent, maxWidth: 600, @@ -273,3 +216,25 @@ class SettingsLearningView extends StatelessWidget { ); } } + +class _ProfileSwitchTile extends StatelessWidget { + final bool value; + final ToolSetting setting; + final Function(bool) onChanged; + + const _ProfileSwitchTile({ + required this.value, + required this.setting, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return ProfileSettingsSwitchListTile.adaptive( + defaultValue: value, + title: setting.toolName(context), + subtitle: setting.toolDescription(context), + onChange: onChanged, + ); + } +} diff --git a/lib/pangea/learning_settings/voice_dropdown.dart b/lib/pangea/learning_settings/voice_dropdown.dart new file mode 100644 index 000000000..4c6b77133 --- /dev/null +++ b/lib/pangea/learning_settings/voice_dropdown.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +import 'package:dropdown_button2/dropdown_button2.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; + +class VoiceDropdown extends StatelessWidget { + final String? value; + final LanguageModel? language; + final Function(String?) onChanged; + + const VoiceDropdown({ + super.key, + this.value, + this.language, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return DropdownButtonFormField2( + customButton: + value != null ? CustomDropdownTextButton(text: value!) : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + ), + decoration: InputDecoration( + labelText: L10n.of(context).voiceDropdownTitle, + ), + isExpanded: true, + dropdownStyleData: DropdownStyleData( + maxHeight: 250, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(14.0), + ), + ), + items: (language?.voices ?? []).map((voice) { + return DropdownMenuItem( + value: voice, + child: Text(voice), + ); + }).toList(), + onChanged: onChanged, + value: value, + ); + } +} diff --git a/lib/pangea/toolbar/message_practice/message_audio_card.dart b/lib/pangea/toolbar/message_practice/message_audio_card.dart index bf57035aa..6a3661b61 100644 --- a/lib/pangea/toolbar/message_practice/message_audio_card.dart +++ b/lib/pangea/toolbar/message_practice/message_audio_card.dart @@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; +import 'package:fluffychat/widgets/matrix.dart'; class MessageAudioCard extends StatefulWidget { final PangeaMessageEvent messageEvent; @@ -44,7 +44,7 @@ class MessageAudioCardState extends State { try { audioFile = await widget.messageEvent.requestTextToSpeech( widget.messageEvent.messageDisplayLangCode, - widget.messageEvent.room.botOptions?.targetVoice, + MatrixState.pangeaController.userController.voice, ); debugPrint("audio file is now: $audioFile. setting starts and ends..."); if (mounted) setState(() => _isLoading = false); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index a5f243674..f7abe6588 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -7,7 +7,6 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/pangea/analytics_misc/lemma_emoji_setter_mixin.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; @@ -58,7 +57,7 @@ class _AudioLoader extends AsyncLoader<(PangeaAudioFile, File?)> { Future<(PangeaAudioFile, File?)> fetch() async { final audioBytes = await messageEvent.requestTextToSpeech( messageEvent.messageDisplayLangCode, - messageEvent.room.botOptions?.targetVoice, + MatrixState.pangeaController.userController.voice, ); File? audioFile; diff --git a/lib/pangea/user/user_controller.dart b/lib/pangea/user/user_controller.dart index 43b9736ea..2e3be4226 100644 --- a/lib/pangea/user/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -431,6 +431,8 @@ class UserController { : langModel; } + String? get voice => profile.userSettings.voice; + bool get languagesSet => userL1Code != null && userL2Code != null && diff --git a/lib/pangea/user/user_model.dart b/lib/pangea/user/user_model.dart index 60ce1873a..ff0184627 100644 --- a/lib/pangea/user/user_model.dart +++ b/lib/pangea/user/user_model.dart @@ -19,6 +19,7 @@ class UserSettings { GenderEnum gender; String? country; LanguageLevelTypeEnum cefrLevel; + String? voice; UserSettings({ this.dateOfBirth, @@ -29,6 +30,7 @@ class UserSettings { this.gender = GenderEnum.unselected, this.country, this.cefrLevel = LanguageLevelTypeEnum.a1, + this.voice, }); factory UserSettings.fromJson(Map json) => UserSettings( @@ -52,6 +54,7 @@ class UserSettings { json[ModelKey.cefrLevel], ) : LanguageLevelTypeEnum.a1, + voice: json[ModelKey.voice], ); Map toJson() { @@ -64,6 +67,7 @@ class UserSettings { data[ModelKey.userGender] = gender.string; data[ModelKey.userCountry] = country; data[ModelKey.cefrLevel] = cefrLevel.string; + data[ModelKey.voice] = voice; return data; } @@ -123,6 +127,7 @@ class UserSettings { gender: gender, country: country, cefrLevel: cefrLevel, + voice: voice, ); } @@ -138,7 +143,8 @@ class UserSettings { other.sourceLanguage == sourceLanguage && other.gender == gender && other.country == country && - other.cefrLevel == cefrLevel; + other.cefrLevel == cefrLevel && + other.voice == voice; } @override @@ -151,6 +157,7 @@ class UserSettings { gender.hashCode, country.hashCode, cefrLevel.hashCode, + voice.hashCode, ]); } diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index bfba786b1..ae0e10471 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -28,7 +28,6 @@ class Avatar extends StatelessWidget { final double? presenceSize; final Offset? presenceOffset; - final Widget? miniIcon; // Pangea# const Avatar({ @@ -48,7 +47,6 @@ class Avatar extends StatelessWidget { this.userId, this.presenceSize, this.presenceOffset, - this.miniIcon, // Pangea# super.key, }); @@ -140,13 +138,7 @@ class Avatar extends StatelessWidget { ), // #Pangea // if (presenceUserId != null) - if (miniIcon != null) - Positioned( - bottom: presenceOffset?.dy ?? -3, - right: presenceOffset?.dx ?? -3, - child: miniIcon!, - ) - else if (presenceUserId != null && size >= 32.0 && showPresence) + if (presenceUserId != null && size >= 32.0 && showPresence) // Pangea# PresenceBuilder( client: client, diff --git a/lib/widgets/member_actions_popup_menu_button.dart b/lib/widgets/member_actions_popup_menu_button.dart index 75376e351..c80524b5a 100644 --- a/lib/widgets/member_actions_popup_menu_button.dart +++ b/lib/widgets/member_actions_popup_menu_button.dart @@ -6,8 +6,6 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_chat_settings_dialog.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/permission_slider_dialog.dart'; import 'adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -108,15 +106,15 @@ void showMemberActionsPopupMenu({ ], ), ), - if (user.id == BotName.byEnvironment && room != null && room.isRoomAdmin) - PopupMenuItem( - enabled: false, - padding: const EdgeInsets.only( - left: 12.0, - right: 12.0, - ), - child: BotChatSettingsDialog(room: room), - ), + // if (user.id == BotName.byEnvironment && room != null && room.isRoomAdmin) + // PopupMenuItem( + // enabled: false, + // padding: const EdgeInsets.only( + // left: 12.0, + // right: 12.0, + // ), + // child: BotChatSettingsDialog(room: room), + // ), const PopupMenuDivider(), // #Pangea if (user.room.client.userID != user.id) From ed1ade783aaa162cf5f83f5134b0b4530e065348 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:53:16 -0500 Subject: [PATCH 23/42] chore: Remove sentence-level pronunciation (#5306) --- .../toolbar/layout/overlay_message.dart | 29 +++++++++---------- .../select_mode_controller.dart | 3 -- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/pangea/toolbar/layout/overlay_message.dart b/lib/pangea/toolbar/layout/overlay_message.dart index 2e0b134f3..13008833f 100644 --- a/lib/pangea/toolbar/layout/overlay_message.dart +++ b/lib/pangea/toolbar/layout/overlay_message.dart @@ -14,9 +14,6 @@ import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/languages/language_model.dart'; -import 'package:fluffychat/pangea/languages/p_language_store.dart'; -import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/toolbar/layout/reading_assistance_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/select_mode_buttons.dart'; @@ -500,19 +497,19 @@ class _MessageBubbleTranscription extends StatelessWidget { onClick: onTokenSelected, isSelected: isTokenSelected, ), - if (MatrixState - .pangeaController.userController.showTranscription) - PhoneticTranscriptionWidget( - text: transcription.transcript.text, - textLanguage: PLanguageStore.byLangCode( - transcription.langCode, - ) ?? - LanguageModel.unknown, - style: style, - iconColor: style.color, - onTranscriptionFetched: () => - controller.contentChangedStream.add(true), - ), + // if (MatrixState + // .pangeaController.userController.showTranscription) + // PhoneticTranscriptionWidget( + // text: transcription.transcript.text, + // textLanguage: PLanguageStore.byLangCode( + // transcription.langCode, + // ) ?? + // LanguageModel.unknown, + // style: style, + // iconColor: style.color, + // onTranscriptionFetched: () => + // controller.contentChangedStream.add(true), + // ), ], ), ); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index f7abe6588..d285fc1ce 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -90,8 +90,6 @@ class SelectModeController with LemmaEmojiSetter { ValueNotifier selectedMode = ValueNotifier(null); - final StreamController contentChangedStream = StreamController.broadcast(); - // Sometimes the same token is clicked twice. Setting it to the same value // won't trigger the notifier, so use the bool for force it to trigger. ValueNotifier<(PangeaTokenText?, bool)> playTokenNotifier = @@ -104,7 +102,6 @@ class SelectModeController with LemmaEmojiSetter { _translationLoader.dispose(); _sttTranslationLoader.dispose(); _audioLoader.dispose(); - contentChangedStream.close(); } static List get _textModes => [ From ea0c1afb10bb0b0622134200a3318a704961e841 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:23:17 -0500 Subject: [PATCH 24/42] fix: use sync stream to update analytics requests indicator (#5307) --- .../analytics_request_indicator.dart | 41 ++++++++++++++----- .../analytics_requests_repo.dart | 4 ++ .../space_analytics/space_analytics.dart | 1 + 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/lib/pangea/space_analytics/analytics_request_indicator.dart b/lib/pangea/space_analytics/analytics_request_indicator.dart index 9ba4e910b..6b9d854a8 100644 --- a/lib/pangea/space_analytics/analytics_request_indicator.dart +++ b/lib/pangea/space_analytics/analytics_request_indicator.dart @@ -10,7 +10,6 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_requested_dialog.dart'; -import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; class AnalyticsRequestIndicator extends StatefulWidget { @@ -60,18 +59,38 @@ class AnalyticsRequestIndicatorState extends State { ); await Future.wait(futures); - final analyicsRoomIds = analyticsRooms.map((r) => r.id).toSet(); + final analyticsRoomIds = analyticsRooms.map((r) => r.id).toSet(); _analyticsRoomSub?.cancel(); - _analyticsRoomSub = widget.room.client.onRoomState.stream - .where( - (event) => - analyicsRoomIds.contains(event.roomId) && - event.state.type == EventTypes.RoomMember, - ) - .rateLimit(const Duration(seconds: 1)) - .listen((_) => setState(() {})); + _analyticsRoomSub = widget.room.client.onSync.stream.listen((update) async { + final joined = update.rooms?.join?.entries + .where((e) => analyticsRoomIds.contains(e.key)); - if (mounted) setState(() {}); + if (joined == null || joined.isEmpty) return; + final Set updatedRoomIds = {}; + for (final entry in joined) { + final memberEvents = entry.value.timeline?.events?.where( + (e) => e.type == EventTypes.RoomMember, + ); + if (memberEvents != null && memberEvents.isNotEmpty) { + updatedRoomIds.add(entry.key); + } + } + + if (updatedRoomIds.isEmpty) return; + for (final roomId in updatedRoomIds) { + final room = widget.room.client.getRoomById(roomId); + if (room == null) continue; + await room.requestParticipants( + [Membership.join, Membership.invite, Membership.knock], + false, + true, + ); + } + + if (mounted) { + setState(() {}); + } + }); } Map> get _knockingAdmins { diff --git a/lib/pangea/space_analytics/analytics_requests_repo.dart b/lib/pangea/space_analytics/analytics_requests_repo.dart index ef6bfe1a5..2c3b39ffd 100644 --- a/lib/pangea/space_analytics/analytics_requests_repo.dart +++ b/lib/pangea/space_analytics/analytics_requests_repo.dart @@ -94,4 +94,8 @@ class AnalyticsRequestsRepo { final key = _storageKey(userId, language); await _requestStorage.remove(key); } + + static Future clear() async { + await _requestStorage.erase(); + } } diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index bef031cf5..5db3b2248 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -189,6 +189,7 @@ class SpaceAnalyticsState extends State { Future refresh() async { if (room == null || !room!.isSpace || selectedLanguage == null) return; + await AnalyticsRequestsRepo.clear(); setState(() { downloads = Map.fromEntries( From 2f5d67e2020c1341b8e4d78386507a31cf8177ee Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:23:52 -0500 Subject: [PATCH 25/42] fix: disable text scaling in learning progress indicators (#5313) --- lib/pangea/analytics_summary/learning_progress_indicators.dart | 2 ++ lib/pangea/analytics_summary/progress_indicator.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index 8c7079d2c..885ffd0e0 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -147,6 +147,7 @@ class LearningProgressIndicators extends StatelessWidget { .colorScheme .primary, ), + textScaler: TextScaler.noScaling, ), if (userL1 != null && userL2 != null) const Icon(Icons.chevron_right_outlined), @@ -162,6 +163,7 @@ class LearningProgressIndicators extends StatelessWidget { .colorScheme .primary, ), + textScaler: TextScaler.noScaling, ), ], ), diff --git a/lib/pangea/analytics_summary/progress_indicator.dart b/lib/pangea/analytics_summary/progress_indicator.dart index 579ba205f..4a7ac798b 100644 --- a/lib/pangea/analytics_summary/progress_indicator.dart +++ b/lib/pangea/analytics_summary/progress_indicator.dart @@ -125,6 +125,7 @@ class AnimatedFloatingNumberState extends State Text( widget.number.toString(), style: indicatorStyle, + textScaler: TextScaler.noScaling, ), ], ); From 7458f8f55973513cb8f7939719e827753c5b1541 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:34:20 -0500 Subject: [PATCH 26/42] fix: don't auto-play bot audio message if another audio message is playing (#5315) --- lib/pages/chat/chat.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 215fb2063..58947f941 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -496,6 +496,8 @@ class ChatController extends State if (botAudioEvent == null) return; final matrix = Matrix.of(context); + if (matrix.voiceMessageEventId.value != null) return; + matrix.voiceMessageEventId.value = botAudioEvent.eventId; matrix.audioPlayer?.dispose(); matrix.audioPlayer = AudioPlayer(); From 1c6c2ee44a8f2d6c0ab6c9d2673f48c1ed5bc94b Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:45:36 -0500 Subject: [PATCH 27/42] fix: restrict when analytics practice session loss popup is shown (#5316) --- .../analytics_practice_page.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 9f3c0573e..6a7cc6fe5 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -58,7 +58,7 @@ class SessionLoader extends AsyncLoader { } class AnalyticsPractice extends StatefulWidget { - static bool bypassExitConfirmation = false; + static bool bypassExitConfirmation = true; final ConstructTypeEnum type; const AnalyticsPractice({ @@ -189,18 +189,18 @@ class AnalyticsPracticeState extends State String choiceTargetId(String choiceId) => '${widget.type.name}-choice-card-${choiceId.replaceAll(' ', '_')}'; - void _resetActivityState() { + void _clearState() { activityState.value = const AsyncState.loading(); activityTarget.value = null; enableChoicesNotifier.value = true; - } - void _resetSessionState() { progressNotifier.value = 0.0; _queue.clear(); _choiceTexts.clear(); _choiceEmojis.clear(); activityState.value = const AsyncState.idle(); + + AnalyticsPractice.bypassExitConfirmation = true; } void updateElapsedTime(int seconds) { @@ -227,8 +227,7 @@ class AnalyticsPracticeState extends State Future _onLanguageUpdate() async { try { - _resetActivityState(); - _resetSessionState(); + _clearState(); await _analyticsService .updateDispatcher.constructUpdateStream.stream.first .timeout(const Duration(seconds: 10)); @@ -252,9 +251,7 @@ class AnalyticsPracticeState extends State } Future reloadSession() async { - _resetActivityState(); - _resetSessionState(); - + _clearState(); _sessionLoader.reset(); await _startSession(); } @@ -294,8 +291,10 @@ class AnalyticsPracticeState extends State final activity = await nextActivityCompleter.completer.future; activityState.value = AsyncState.loaded(activity); + AnalyticsPractice.bypassExitConfirmation = false; } } catch (e) { + AnalyticsPractice.bypassExitConfirmation = true; activityState.value = AsyncState.error(e); } finally { _continuing = false; @@ -319,7 +318,9 @@ class AnalyticsPracticeState extends State if (!mounted) return; activityState.value = AsyncState.loaded(res); + AnalyticsPractice.bypassExitConfirmation = false; } catch (e) { + AnalyticsPractice.bypassExitConfirmation = true; if (!mounted) return; activityState.value = AsyncState.error(e); return; From 0d9982534e81f2aaaf6b1918e49d61a577af54c5 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:49:18 -0500 Subject: [PATCH 28/42] fix: hide info about course editing in join mode (#5317) --- .../course_creation/selected_course_view.dart | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/lib/pangea/course_creation/selected_course_view.dart b/lib/pangea/course_creation/selected_course_view.dart index 6f54c0363..f94832370 100644 --- a/lib/pangea/course_creation/selected_course_view.dart +++ b/lib/pangea/course_creation/selected_course_view.dart @@ -234,40 +234,43 @@ class SelectedCourseView extends StatelessWidget { spacing: 8.0, mainAxisSize: MainAxisSize.min, children: [ - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.edit, - size: mediumIconSize, - ), - Flexible( - child: Text( - L10n.of(context).editCourseLater, - style: const TextStyle( - fontSize: descFontSize, + if (controller.widget.mode != + SelectedCourseMode.join) ...[ + Row( + spacing: 12.0, + children: [ + const Icon( + Icons.edit, + size: mediumIconSize, + ), + Flexible( + child: Text( + L10n.of(context).editCourseLater, + style: const TextStyle( + fontSize: descFontSize, + ), ), ), - ), - ], - ), - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.shield, - size: mediumIconSize, - ), - Flexible( - child: Text( - L10n.of(context).newCourseAccess, - style: const TextStyle( - fontSize: descFontSize, + ], + ), + Row( + spacing: 12.0, + children: [ + const Icon( + Icons.shield, + size: mediumIconSize, + ), + Flexible( + child: Text( + L10n.of(context).newCourseAccess, + style: const TextStyle( + fontSize: descFontSize, + ), ), ), - ), - ], - ), + ], + ), + ], Padding( padding: const EdgeInsets.only(top: 8.0), child: ElevatedButton( From 2df61c6a730082c38ebbcf1b34b564e50a4bae84 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:56:48 -0500 Subject: [PATCH 29/42] chore: update knock copy (#5318) --- lib/l10n/intl_ar.arb | 7 ++++++- lib/l10n/intl_be.arb | 7 ++++++- lib/l10n/intl_bn.arb | 7 ++++++- lib/l10n/intl_bo.arb | 7 ++++++- lib/l10n/intl_ca.arb | 7 ++++++- lib/l10n/intl_cs.arb | 7 ++++++- lib/l10n/intl_da.arb | 7 ++++++- lib/l10n/intl_de.arb | 7 ++++++- lib/l10n/intl_el.arb | 7 ++++++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_eo.arb | 7 ++++++- lib/l10n/intl_es.arb | 7 ++++++- lib/l10n/intl_et.arb | 7 ++++++- lib/l10n/intl_eu.arb | 7 ++++++- lib/l10n/intl_fa.arb | 7 ++++++- lib/l10n/intl_fi.arb | 7 ++++++- lib/l10n/intl_fil.arb | 7 ++++++- lib/l10n/intl_fr.arb | 7 ++++++- lib/l10n/intl_ga.arb | 7 ++++++- lib/l10n/intl_gl.arb | 7 ++++++- lib/l10n/intl_he.arb | 7 ++++++- lib/l10n/intl_hi.arb | 7 ++++++- lib/l10n/intl_hr.arb | 7 ++++++- lib/l10n/intl_hu.arb | 7 ++++++- lib/l10n/intl_ia.arb | 7 ++++++- lib/l10n/intl_id.arb | 7 ++++++- lib/l10n/intl_ie.arb | 7 ++++++- lib/l10n/intl_it.arb | 7 ++++++- lib/l10n/intl_ja.arb | 7 ++++++- lib/l10n/intl_ka.arb | 7 ++++++- lib/l10n/intl_ko.arb | 7 ++++++- lib/l10n/intl_lt.arb | 7 ++++++- lib/l10n/intl_lv.arb | 7 ++++++- lib/l10n/intl_nb.arb | 7 ++++++- lib/l10n/intl_nl.arb | 7 ++++++- lib/l10n/intl_pl.arb | 7 ++++++- lib/l10n/intl_pt.arb | 7 ++++++- lib/l10n/intl_pt_BR.arb | 7 ++++++- lib/l10n/intl_pt_PT.arb | 7 ++++++- lib/l10n/intl_ro.arb | 7 ++++++- lib/l10n/intl_ru.arb | 7 ++++++- lib/l10n/intl_sk.arb | 7 ++++++- lib/l10n/intl_sl.arb | 7 ++++++- lib/l10n/intl_sr.arb | 7 ++++++- lib/l10n/intl_sv.arb | 7 ++++++- lib/l10n/intl_ta.arb | 7 ++++++- lib/l10n/intl_te.arb | 7 ++++++- lib/l10n/intl_th.arb | 7 ++++++- lib/l10n/intl_tr.arb | 7 ++++++- lib/l10n/intl_uk.arb | 7 ++++++- lib/l10n/intl_vi.arb | 7 ++++++- lib/l10n/intl_yue.arb | 7 ++++++- lib/l10n/intl_zh.arb | 7 ++++++- lib/l10n/intl_zh_Hant.arb | 7 ++++++- lib/pangea/course_creation/selected_course_page.dart | 2 +- 55 files changed, 321 insertions(+), 55 deletions(-) diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 3642fb278..8e38e6faf 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-21 10:42:52.513008", + "@@last_modified": "2026-01-21 13:54:18.388293", "about": "حول", "@about": { "type": "String", @@ -11112,5 +11112,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "تم إرسال طلبك إلى إدارة الدورة! سيتم السماح لك بالدخول إذا وافقوا.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index f7593b805..5cfc00f97 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-21 10:42:45.087111", + "@@last_modified": "2026-01-21 13:54:07.402936", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11994,5 +11994,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запыт быў адпраўлены адміністрацыі курса! Вы будзеце дапушчаны, калі яны зацвердзяць.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 451b639ab..d369b9e4a 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:01.204800", + "@@last_modified": "2026-01-21 13:54:31.328626", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11999,5 +11999,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "আপনার অনুরোধ কোর্স প্রশাসকের কাছে পাঠানো হয়েছে! তারা অনুমোদন করলে আপনাকে প্রবেশ করতে দেওয়া হবে।", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index a87317500..39ccde987 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-21 10:42:59.058441", + "@@last_modified": "2026-01-21 13:54:28.767499", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10649,5 +10649,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Yor requst has been sent to course admin! Yu'll be let in if dey approve.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 7c6d2d7fa..3c477421a 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:46.152113", + "@@last_modified": "2026-01-21 13:54:08.647836", "about": "Quant a", "@about": { "type": "String", @@ -10919,5 +10919,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "La teva sol·licitud s'ha enviat a l'administrador del curs! Et deixaran entrar si ho aproven.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index fef398790..8c7cbe62d 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-21 10:42:42.984636", + "@@last_modified": "2026-01-21 13:54:04.800051", "about": "O aplikaci", "@about": { "type": "String", @@ -11502,5 +11502,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaše žádost byla odeslána administrátorovi kurzu! Budete vpuštěni, pokud ji schválí.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 46dbe3d2c..84a867b38 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-21 10:42:22.819549", + "@@last_modified": "2026-01-21 13:53:34.885532", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11956,5 +11956,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din anmodning er sendt til kursusadministratoren! Du vil blive lukket ind, hvis de godkender.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 2f9214e0b..d0cfb6194 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-21 10:42:37.466783", + "@@last_modified": "2026-01-21 13:53:56.595777", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10902,5 +10902,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ihre Anfrage wurde an den Kursadministrator gesendet! Sie werden eingelassen, wenn sie zustimmen.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index b90f11cdb..333511f61 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-21 10:43:05.204211", + "@@last_modified": "2026-01-21 13:54:37.783088", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11953,5 +11953,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Το αίτημά σας έχει σταλεί στον διαχειριστή του μαθήματος! Θα σας επιτρέψουν να μπείτε αν το εγκρίνουν.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e90dfca6b..ae9b71bed 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5057,5 +5057,6 @@ "fillInBlank": "Fill in the blank with the correct choice", "learn": "Learn", "languageUpdated": "Target language updated!", - "voiceDropdownTitle": "Pangea Bot voice" + "voiceDropdownTitle": "Pangea Bot voice", + "knockDesc": "Your request has been sent to course admin! You'll be let in if they approve." } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 942654f9f..4d5067671 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:08.060333", + "@@last_modified": "2026-01-21 13:54:42.193114", "about": "Prio", "@about": { "type": "String", @@ -11984,5 +11984,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Via peto estis sendita al la kursa administranto! Vi estos enirita se ili aprobas.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 4e77d447b..a7ccb8c3c 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-21 10:42:19.343875", + "@@last_modified": "2026-01-21 13:53:29.857658", "about": "Acerca de", "@about": { "type": "String", @@ -8129,5 +8129,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "¡Tu solicitud ha sido enviada al administrador del curso! Te dejarán entrar si la aprueban.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 30c19ef49..d50c106b2 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-21 10:42:36.742735", + "@@last_modified": "2026-01-21 13:53:55.586614", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11166,5 +11166,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Teie taotlus on saadetud kursuse administraatorile! Teid lastakse sisse, kui nad heaks kiidavad.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index d913d4db3..b8175726d 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-21 10:42:34.277635", + "@@last_modified": "2026-01-21 13:53:53.122879", "about": "Honi buruz", "@about": { "type": "String", @@ -10895,5 +10895,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Zure eskaera ikastaroaren administratzaileari bidali zaio! Onartzen badute, sartuko zara.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 6e395bb02..eb0feb5bd 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:02.232868", + "@@last_modified": "2026-01-21 13:54:33.096668", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11627,5 +11627,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "درخواست شما به مدیر دوره ارسال شده است! اگر آنها تأیید کنند، شما وارد خواهید شد.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 707872200..8684fcf0d 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-21 10:42:21.857816", + "@@last_modified": "2026-01-21 13:53:33.564588", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11518,5 +11518,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Pyyntösi on lähetetty kurssin ylläpitäjälle! Sinut päästetään sisään, jos he hyväksyvät sen.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index a51993f5e..2489ec5b6 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-21 10:42:50.349708", + "@@last_modified": "2026-01-21 13:54:14.817261", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11871,5 +11871,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ang iyong kahilingan ay naipadala sa admin ng kurso! Papayagan ka nilang pumasok kung sila ay mag-aapruba.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 58d082c74..700921c07 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-21 10:43:12.775361", + "@@last_modified": "2026-01-21 13:54:48.318824", "about": "À propos", "@about": { "type": "String", @@ -11219,5 +11219,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Votre demande a été envoyée à l'administrateur du cours ! Vous serez admis s'ils approuvent.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index b6e3263eb..9abadb3a0 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-21 10:43:11.760663", + "@@last_modified": "2026-01-21 13:54:46.978792", "@customReaction": { "type": "String", "placeholders": {} @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tá do hiarratas curtha chuig an riarachán cúrsa! Cuirfear isteach thú má cheadaíonn siad é.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 28655e165..23f338731 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-21 10:42:20.142432", + "@@last_modified": "2026-01-21 13:53:31.619299", "about": "Acerca de", "@about": { "type": "String", @@ -10892,5 +10892,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "A túa solicitude foi enviada ao administrador do curso! Serás admitido se a aproban.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index f36caa914..af9e0ad59 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:30.598205", + "@@last_modified": "2026-01-21 13:53:47.592162", "about": "אודות", "@about": { "type": "String", @@ -11944,5 +11944,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "הבקשה שלך נשלחה למנהל הקורס! תורשה להיכנס אם הם יאשרו.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index 611db19dd..064cbe283 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-21 10:43:07.054066", + "@@last_modified": "2026-01-21 13:54:40.850433", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11980,5 +11980,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "आपका अनुरोध पाठ्यक्रम प्रशासन को भेज दिया गया है! यदि वे स्वीकृत करते हैं, तो आपको अंदर जाने दिया जाएगा।", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 17ea4d22e..6b8517e96 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-21 10:42:29.581714", + "@@last_modified": "2026-01-21 13:53:46.524141", "about": "Informacije", "@about": { "type": "String", @@ -11267,5 +11267,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaš zahtjev je poslan administratoru tečaja! Bit ćete primljeni ako odobre.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 490d3b22f..b0767225a 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-21 10:42:24.298395", + "@@last_modified": "2026-01-21 13:53:36.742109", "about": "Névjegy", "@about": { "type": "String", @@ -10896,5 +10896,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "A kérésed el lett küldve a kurzus adminisztrátorának! Be fogsz engedni, ha jóváhagyják.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 54974eb51..506658635 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-21 10:42:31.403920", + "@@last_modified": "2026-01-21 13:53:49.028067", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11973,5 +11973,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tua peticio est missa ad administratorem cursuum! Te admittent si illi approbant.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 2d02858ce..5db3bb912 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:25.871973", + "@@last_modified": "2026-01-21 13:53:37.899898", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10886,5 +10886,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Permintaan Anda telah dikirim ke admin kursus! Anda akan diizinkan masuk jika mereka menyetujuinya.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index caf59b6b4..3d38e5615 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-21 10:42:28.431424", + "@@last_modified": "2026-01-21 13:53:45.416677", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11869,5 +11869,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tua iarrtas a chaidh a chur gu rianachd a' chùrsa! Thèid thu a leigeil a-steach ma tha iad a' freagairt gu math.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 9284e09b3..312c08f22 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:40.690823", + "@@last_modified": "2026-01-21 13:54:01.568848", "about": "Informazioni", "@about": { "type": "String", @@ -10898,5 +10898,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "La tua richiesta è stata inviata all'amministratore del corso! Sarai ammesso se approvano.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index c83be27b4..c511c0107 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-21 10:43:06.128364", + "@@last_modified": "2026-01-21 13:54:39.359536", "about": "このアプリについて", "@about": { "type": "String", @@ -11685,5 +11685,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "あなたのリクエストはコース管理者に送信されました! 彼らが承認すれば、入ることができます。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index d8e600706..7c3ce8707 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-21 10:43:09.701292", + "@@last_modified": "2026-01-21 13:54:44.486907", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11925,5 +11925,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "თქვენი მოთხოვნა გაგზავნილია კურსის ადმინისტრატორთან! თქვენ შეგიშვებენ, თუ ისინი დაამტკიცებენ.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index ed5289391..655637228 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:17.603097", + "@@last_modified": "2026-01-21 13:53:28.417406", "about": "소개", "@about": { "type": "String", @@ -11003,5 +11003,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "귀하의 요청이 과정 관리자에게 전송되었습니다! 그들이 승인하면 들어갈 수 있습니다.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 35ca44e54..3473469a9 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-21 10:42:55.447627", + "@@last_modified": "2026-01-21 13:54:22.729769", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11700,5 +11700,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Jūsų prašymas buvo išsiųstas kurso administratoriui! Būsite įleistas, jei jie patvirtins.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 64bbaf040..feff58b67 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-21 10:42:51.147364", + "@@last_modified": "2026-01-21 13:54:16.883169", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10881,5 +10881,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Jūsu pieprasījums ir nosūtīts kursa administratoram! Jūs tiksiet iekšā, ja viņi apstiprinās.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 5615f0550..f87cddbc8 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:43.891386", + "@@last_modified": "2026-01-21 13:54:06.232198", "about": "Om", "@about": { "type": "String", @@ -11988,5 +11988,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din forespørsel har blitt sendt til kursadministratoren! Du vil bli sluppet inn hvis de godkjenner.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index b021f9ad1..ed586d553 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:58.326124", + "@@last_modified": "2026-01-21 13:54:27.649909", "about": "Over ons", "@about": { "type": "String", @@ -10895,5 +10895,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Je verzoek is verzonden naar de cursusbeheerder! Je wordt toegelaten als ze goedkeuren.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 9e3d32b91..d7d2c73a5 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-21 10:43:03.257336", + "@@last_modified": "2026-01-21 13:54:34.451816", "about": "O aplikacji", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Twoja prośba została wysłana do administratora kursu! Zostaniesz wpuszczony, jeśli ją zatwierdzą.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index f80792528..d9b54e65a 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:35.392050", + "@@last_modified": "2026-01-21 13:53:54.148542", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11995,5 +11995,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 933c9f8f3..11797eb83 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:32.545549", + "@@last_modified": "2026-01-21 13:53:51.587209", "about": "Sobre", "@about": { "type": "String", @@ -11253,5 +11253,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 643c67fee..bc97480dc 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-21 10:42:48.198355", + "@@last_modified": "2026-01-21 13:54:11.801274", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11924,5 +11924,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 7e868c193..3f458ad96 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:26.609980", + "@@last_modified": "2026-01-21 13:53:41.456181", "about": "Despre", "@about": { "type": "String", @@ -11630,5 +11630,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Cererea ta a fost trimisă administratorului cursului! Vei fi lăsat să intri dacă ei aprobă.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index e08d12d6e..976018aac 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-21 10:43:08.936913", + "@@last_modified": "2026-01-21 13:54:43.229000", "about": "О проекте", "@about": { "type": "String", @@ -11000,5 +11000,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запрос отправлен администратору курса! Вы будете допущены, если они одобрят.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 26dd291f9..510156730 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-21 10:42:27.675920", + "@@last_modified": "2026-01-21 13:53:43.706457", "about": "O aplikácii", "@about": { "type": "String", @@ -11979,5 +11979,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaša žiadosť bola odoslaná administrátorovi kurzu! Budete vpustený, ak ju schvália.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index a9889e3a1..9736fb5f5 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-21 10:42:38.801732", + "@@last_modified": "2026-01-21 13:53:58.241980", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11976,5 +11976,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaša zahteva je bila poslana skrbniku tečaja! Vstopili boste, če jo odobrijo.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index c32c01635..0b970dceb 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:10.733270", + "@@last_modified": "2026-01-21 13:54:45.561732", "about": "О програму", "@about": { "type": "String", @@ -11997,5 +11997,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaš zahtev je poslat administratoru kursa! Bićete primljeni ako odobre.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 9bffc0e88..302e97dde 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:04.367889", + "@@last_modified": "2026-01-21 13:54:35.931933", "about": "Om", "@about": { "type": "String", @@ -11373,5 +11373,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din begäran har skickats till kursadministratören! Du kommer att släppas in om de godkänner.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 5592467b6..82050d0e3 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:57.399546", + "@@last_modified": "2026-01-21 13:54:25.999605", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11119,5 +11119,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "உங்கள் கோரிக்கை பாடம் நிர்வாகிக்கு அனுப்பப்பட்டுள்ளது! அவர்கள் ஒப்புதலளித்தால் நீங்கள் உள்ளே அனுமதிக்கப்படுவீர்கள்.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 9e1e5fe94..e457db572 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-21 10:42:54.652537", + "@@last_modified": "2026-01-21 13:54:20.897642", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11984,5 +11984,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "మీ అభ్యర్థన కోర్సు నిర్వాహకుడికి పంపబడింది! వారు ఆమోదిస్తే, మీరు లోపలికి రానున్నారు.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index b07d69284..397aaa446 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-21 10:42:47.175182", + "@@last_modified": "2026-01-21 13:54:10.758435", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11953,5 +11953,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "คำขอของคุณได้ถูกส่งไปยังผู้ดูแลหลักสูตรแล้ว! คุณจะได้รับอนุญาตให้เข้าหากพวกเขาอนุมัติ.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index fe247f360..f38520405 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-21 10:42:53.424381", + "@@last_modified": "2026-01-21 13:54:19.467039", "about": "Hakkında", "@about": { "type": "String", @@ -11117,5 +11117,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Talebiniz kurs yöneticisine gönderildi! Onaylarlarsa içeri alınacaksınız.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index 7734ef4fe..5dbfc2938 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-21 10:42:41.558170", + "@@last_modified": "2026-01-21 13:54:03.403456", "about": "Про застосунок", "@about": { "type": "String", @@ -10889,5 +10889,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запит надіслано адміністратору курсу! Ви будете допущені, якщо вони схвалять.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 0d9c741ab..89760d972 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:56.498587", + "@@last_modified": "2026-01-21 13:54:24.252269", "about": "Giới thiệu", "@about": { "type": "String", @@ -6465,5 +6465,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Yêu cầu của bạn đã được gửi đến quản trị viên khóa học! Bạn sẽ được cho vào nếu họ chấp thuận.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 15558c19a..b280b4a62 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-21 10:42:39.595731", + "@@last_modified": "2026-01-21 13:53:59.885092", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11986,5 +11986,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "你的請求已經發送給課程管理員!如果他們批准,你將被允許進入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index e3b4d480b..ffe826b05 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-21 10:43:00.180549", + "@@last_modified": "2026-01-21 13:54:29.681537", "about": "关于", "@about": { "type": "String", @@ -10886,5 +10886,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "您的请求已发送给课程管理员!如果他们批准,您将被允许进入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 3491a8f78..fc92bb5dc 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:49.437032", + "@@last_modified": "2026-01-21 13:54:13.165623", "about": "關於", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "您的請求已發送給課程管理員!如果他們批准,您將被允許進入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pangea/course_creation/selected_course_page.dart b/lib/pangea/course_creation/selected_course_page.dart index 299c784f4..34f913b08 100644 --- a/lib/pangea/course_creation/selected_course_page.dart +++ b/lib/pangea/course_creation/selected_course_page.dart @@ -179,7 +179,7 @@ class SelectedCourseController extends State await showOkAlertDialog( context: context, title: L10n.of(context).youHaveKnocked, - message: L10n.of(context).pleaseWaitUntilInvited, + message: L10n.of(context).knockDesc, ); return; } From 236af2ec54a446f4c88c417fe8e20f4ccbfad304 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:57:17 -0500 Subject: [PATCH 30/42] fix: switch back to flutter's built in dropdown for cerf level dropdown menu (#5322) --- .../widgets/language_level_dropdown.dart | 112 +++++++----------- 1 file changed, 43 insertions(+), 69 deletions(-) diff --git a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart index 19bd2aa15..35bcc4525 100644 --- a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart +++ b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart @@ -1,94 +1,68 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; - import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class LanguageLevelDropdown extends StatelessWidget { final LanguageLevelTypeEnum? initialLevel; final Function(LanguageLevelTypeEnum)? onChanged; - final FormFieldValidator? validator; - final bool enabled; - final Color? backgroundColor; - final double? width; - final double? maxHeight; const LanguageLevelDropdown({ super.key, this.initialLevel = LanguageLevelTypeEnum.a1, this.onChanged, - this.validator, - this.enabled = true, - this.backgroundColor, - this.width, - this.maxHeight, }); @override Widget build(BuildContext context) { final l10n = L10n.of(context); - return DropdownButtonFormField2( - customButton: initialLevel != null && - LanguageLevelTypeEnum.values.contains(initialLevel) - ? CustomDropdownTextButton(text: initialLevel!.title(context)) - : null, - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, + return ButtonTheme( + alignedDropdown: true, + child: DropdownButtonFormField( + itemHeight: null, + decoration: InputDecoration( + labelText: l10n.cefrLevelLabel, ), - height: 100.0, - ), - decoration: InputDecoration( - labelText: l10n.cefrLevelLabel, - ), - isExpanded: true, - dropdownStyleData: DropdownStyleData( - maxHeight: maxHeight ?? (kIsWeb ? 500 : null), - decoration: BoxDecoration( - color: backgroundColor ?? - Theme.of(context).colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(14.0), - ), - width: width, - ), - items: - LanguageLevelTypeEnum.values.map((LanguageLevelTypeEnum levelOption) { - return DropdownMenuItem( - value: levelOption, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - Text(levelOption.title(context)), - Flexible( - child: Text( - levelOption.description(context), - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontSize: 14, + selectedItemBuilder: (context) => LanguageLevelTypeEnum.values + .map((levelOption) => Text(levelOption.title(context))) + .toList(), + isExpanded: true, + dropdownColor: Theme.of(context).colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(14.0), + onChanged: (value) { + if (value != null) onChanged?.call(value); + }, + initialValue: initialLevel, + items: LanguageLevelTypeEnum.values + .map((LanguageLevelTypeEnum levelOption) { + return DropdownMenuItem( + value: levelOption, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Text(levelOption.title(context)), + Flexible( + child: Text( + levelOption.description(context), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + fontSize: 14, + ), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), ), - maxLines: 5, - overflow: TextOverflow.ellipsis, - ), + ], ), - ], - ), - ); - }).toList(), - onChanged: enabled - ? (value) { - if (value != null) onChanged?.call(value); - } - : null, - value: initialLevel, - validator: validator, - enableFeedback: enabled, + ), + ); + }).toList(), + ), ); } } From 9c7ec313f1b5500c0b119338b6a1a1284af57ab7 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:19:16 -0500 Subject: [PATCH 31/42] fix: fix public room sheet navigation (#5323) --- .../widgets/public_room_bottom_sheet.dart | 26 ++++--------------- .../course_chats/course_chats_page.dart | 6 +++-- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart b/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart index 65d8587a8..b2894fc27 100644 --- a/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart +++ b/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart @@ -8,7 +8,6 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; import 'package:fluffychat/pangea/join_codes/space_code_controller.dart'; -import 'package:fluffychat/pangea/navigation/navigation_util.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -30,7 +29,7 @@ class PublicRoomBottomSheet extends StatefulWidget { assert(roomAlias != null || chunk != null); } - static Future show({ + static Future show({ required BuildContext context, String? roomAlias, PublicRoomsChunk? chunk, @@ -91,26 +90,13 @@ class PublicRoomBottomSheetState extends State { notFoundError: L10n.of(context).notTheCodeError, ); if (resp != null) { - Navigator.of(context).pop(true); - } - } - - void _goToRoom(String roomID) { - if (chunk?.roomType != 'm.space' && !client.getRoomById(roomID)!.isSpace) { - NavigationUtil.goToSpaceRoute( - roomID, - [], - context, - ); - } else { - context.go('/rooms/spaces/$roomID/details'); + Navigator.of(context).pop(resp); } } Future _joinRoom() async { if (_isRoomMember) { - _goToRoom(room!.id); - Navigator.of(context).pop(); + Navigator.of(context).pop(room!.id); return; } @@ -131,15 +117,13 @@ class PublicRoomBottomSheetState extends State { ); if (result.result != null) { - _goToRoom(result.result!); - Navigator.of(context).pop(true); + Navigator.of(context).pop(result.result!); } } Future _knockRoom() async { if (_isRoomMember) { - _goToRoom(room!.id); - Navigator.of(context).pop(); + Navigator.of(context).pop(room!.id); return; } diff --git a/lib/pangea/course_chats/course_chats_page.dart b/lib/pangea/course_chats/course_chats_page.dart index 1b962497b..b01f8d04c 100644 --- a/lib/pangea/course_chats/course_chats_page.dart +++ b/lib/pangea/course_chats/course_chats_page.dart @@ -438,7 +438,7 @@ class CourseChatsController extends State void joinChildRoom(SpaceRoomsChunk item) async { final space = widget.client.getRoomById(widget.roomId); - final joined = await PublicRoomBottomSheet.show( + final roomId = await PublicRoomBottomSheet.show( context: context, chunk: item, via: space?.spaceChildren @@ -447,10 +447,12 @@ class CourseChatsController extends State ) ?.via, ); - if (mounted && joined == true) { + if (mounted && roomId != null) { setState(() { discoveredChildren?.remove(item); }); + + NavigationUtil.goToSpaceRoute(roomId, [], context); } } From e8bf2645a61860d25a0827b0fff5ee303457e726 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:37:09 -0500 Subject: [PATCH 32/42] fix: update some Russion translations (#5324) --- lib/l10n/intl_ru.arb | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 976018aac..3cbd203a9 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-21 13:54:43.229000", + "@@last_modified": "2026-01-21 15:35:33.881278", "about": "О проекте", "@about": { "type": "String", @@ -3132,8 +3132,6 @@ "@invalidUrl": {}, "addLink": "Добавить ссылку", "@addLink": {}, - "italicText": "Italic", - "@italicText": {}, "unableToJoinChat": "Невозможно присоединиться к чату. Возможно, другая сторона уже закончила разговор.", "@unableToJoinChat": {}, "serverLimitReached": "Ограничения сервера. Ожидайте{seconds} секунд...", @@ -3689,13 +3687,6 @@ "acceptSelection": "Принять исправление", "why": "Почему?", "definition": "Определение", - "exampleSentence": "Приклад речення", - "reportToTeacher": "Кому ви хочете повідомити про це повідомлення?", - "reportMessageTitle": "{reportingUserId} повідомив про повідомлення від {reportedUserId} у чаті {roomName}", - "reportMessageBody": "Повідомлення: {reportedMessage}\nПричина: {reason}", - "noTeachersFound": "Вчителі не знайдені для повідомлення", - "trialExpiration": "Ваша безкоштовна пробна версія закінчується {expiration}", - "freeTrialDesc": "Нові користувачі отримують тижневу безкоштовну пробну версію Pangea Chat", "activateTrial": "Безкоштовна 7-денна пробна версія", "successfullySubscribed": "Ви успішно підписалися!", "clickToManageSubscription": "Натисніть тут, щоб керувати підпискою.", @@ -4078,13 +4069,6 @@ "constructUseIncMDesc": "Некорректно в деятельности по грамматике", "constructUseIgnMDesc": "Игнорируется в деятельности по грамматике", "constructUseEmojiDesc": "Правильно в деятельности по эмодзи", - "constructUseCollected": "Thu thập trong trò chuyện", - "constructUseNanDesc": "Không áp dụng được", - "xpIntoLevel": "{currentXP} / {maxXP} XP", - "enableTTSToolName": "Bật chuyển đổi văn bản thành giọng nói", - "enableTTSToolDescription": "Cho phép ứng dụng tạo ra đầu ra chuyển đổi văn bản thành giọng nói cho các phần của văn bản bằng ngôn ngữ mục tiêu của bạn.", - "yourUsername": "Tên người dùng của bạn", - "yourEmail": "Email của bạn", "iWantToLearn": "Я хочу учиться", "pleaseEnterEmail": "Пожалуйста, введите действительный адрес электронной почты.", "myBaseLanguage": "Мой основной язык", @@ -11005,5 +10989,24 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "italicText": "Курсивный текст", + "exampleSentence": "Пример предложения", + "reportToTeacher": "Кому вы хотите пожаловаться на это сообщение?", + "reportMessageTitle": "{reportingUserId} пожаловался на сообщение от {reportedUserId} в чате {roomName}", + "reportMessageBody": "Сообщение: {reportedMessage}\nПричина: {reason}", + "noTeachersFound": "Учителя для жалобы не найдены", + "trialExpiration": "Ваш бесплатный пробный период истекает {expiration}", + "freeTrialDesc": "Новые пользователи получают бесплатную пробную неделю в Pangea Chat", + "constructUseCollected": "Собрано в чате", + "constructUseNanDesc": "Не применимо", + "xpIntoLevel": "{currentXP} / {maxXP} XP", + "enableTTSToolName": "Включен преобразователь текста в речь", + "enableTTSToolDescription": "Позволяет приложению генерировать озвучивание текста на вашем целевом языке.", + "yourUsername": "Ваше имя пользователя", + "yourEmail": "Ваш email", + "@italicText": { + "type": "String", + "placeholders": {} } } \ No newline at end of file From 6675cebfecf6ee333035a0c91a4bc27efea99fb2 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:41:10 -0500 Subject: [PATCH 33/42] feat: bring back old course pages (#5328) --- lib/config/routes.dart | 92 +-- lib/l10n/intl_ar.arb | 16 +- lib/l10n/intl_be.arb | 16 +- lib/l10n/intl_bn.arb | 16 +- lib/l10n/intl_bo.arb | 16 +- lib/l10n/intl_ca.arb | 16 +- lib/l10n/intl_cs.arb | 16 +- lib/l10n/intl_da.arb | 16 +- lib/l10n/intl_de.arb | 16 +- lib/l10n/intl_el.arb | 16 +- lib/l10n/intl_en.arb | 12 +- lib/l10n/intl_eo.arb | 16 +- lib/l10n/intl_es.arb | 16 +- lib/l10n/intl_et.arb | 16 +- lib/l10n/intl_eu.arb | 16 +- lib/l10n/intl_fa.arb | 16 +- lib/l10n/intl_fi.arb | 16 +- lib/l10n/intl_fil.arb | 16 +- lib/l10n/intl_fr.arb | 16 +- lib/l10n/intl_ga.arb | 16 +- lib/l10n/intl_gl.arb | 16 +- lib/l10n/intl_he.arb | 16 +- lib/l10n/intl_hi.arb | 16 +- lib/l10n/intl_hr.arb | 16 +- lib/l10n/intl_hu.arb | 16 +- lib/l10n/intl_ia.arb | 16 +- lib/l10n/intl_id.arb | 16 +- lib/l10n/intl_ie.arb | 16 +- lib/l10n/intl_it.arb | 16 +- lib/l10n/intl_ja.arb | 16 +- lib/l10n/intl_ka.arb | 16 +- lib/l10n/intl_ko.arb | 16 +- lib/l10n/intl_lt.arb | 16 +- lib/l10n/intl_lv.arb | 16 +- lib/l10n/intl_nb.arb | 16 +- lib/l10n/intl_nl.arb | 16 +- lib/l10n/intl_pl.arb | 16 +- lib/l10n/intl_pt.arb | 16 +- lib/l10n/intl_pt_BR.arb | 16 +- lib/l10n/intl_pt_PT.arb | 16 +- lib/l10n/intl_ro.arb | 16 +- lib/l10n/intl_ru.arb | 16 +- lib/l10n/intl_sk.arb | 16 +- lib/l10n/intl_sl.arb | 16 +- lib/l10n/intl_sr.arb | 16 +- lib/l10n/intl_sv.arb | 16 +- lib/l10n/intl_ta.arb | 16 +- lib/l10n/intl_te.arb | 16 +- lib/l10n/intl_th.arb | 16 +- lib/l10n/intl_tr.arb | 16 +- lib/l10n/intl_uk.arb | 16 +- lib/l10n/intl_vi.arb | 16 +- lib/l10n/intl_yue.arb | 16 +- lib/l10n/intl_zh.arb | 16 +- lib/l10n/intl_zh_Hant.arb | 16 +- .../onboarding/space_code_onboarding.dart | 83 +++ .../space_code_onboarding_view.dart | 82 +++ lib/pangea/login/pages/course_code_page.dart | 4 +- lib/pangea/login/pages/find_course_page.dart | 523 ++++++++++++++++++ lib/pangea/spaces/space_constants.dart | 1 + lib/widgets/navigation_rail.dart | 2 +- 61 files changed, 1502 insertions(+), 145 deletions(-) create mode 100644 lib/pages/onboarding/space_code_onboarding.dart create mode 100644 lib/pages/onboarding/space_code_onboarding_view.dart create mode 100644 lib/pangea/login/pages/find_course_page.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index decc16606..55a8ba0f4 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -22,6 +22,7 @@ import 'package:fluffychat/pages/device_settings/device_settings.dart'; import 'package:fluffychat/pages/login/login.dart'; import 'package:fluffychat/pages/new_group/new_group.dart'; import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart'; +import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart'; import 'package:fluffychat/pages/settings/settings.dart'; import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart'; import 'package:fluffychat/pages/settings_chat/settings_chat.dart'; @@ -50,9 +51,9 @@ import 'package:fluffychat/pangea/course_creation/course_invite_page.dart'; import 'package:fluffychat/pangea/course_creation/selected_course_page.dart'; import 'package:fluffychat/pangea/join_codes/join_with_link_page.dart'; import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; -import 'package:fluffychat/pangea/login/pages/add_course_page.dart'; import 'package:fluffychat/pangea/login/pages/course_code_page.dart'; import 'package:fluffychat/pangea/login/pages/create_pangea_account_page.dart'; +import 'package:fluffychat/pangea/login/pages/find_course_page.dart'; import 'package:fluffychat/pangea/login/pages/language_selection_page.dart'; import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart'; import 'package:fluffychat/pangea/login/pages/new_course_page.dart'; @@ -213,93 +214,8 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const AddCoursePage(route: 'registration'), + const SpaceCodeOnboarding(), ), - routes: [ - GoRoute( - path: 'private', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - const CourseCodePage(), - ); - }, - ), - GoRoute( - path: 'public', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - const PublicCoursesPage( - route: 'registration', - showFilters: false, - ), - ); - }, - routes: [ - GoRoute( - path: ':courseid', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - SelectedCourse( - state.pathParameters['courseid']!, - SelectedCourseMode.join, - roomChunk: state.extra as PublicRoomsChunk?, - ), - ); - }, - ), - ], - ), - GoRoute( - path: 'own', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - const NewCoursePage( - route: 'registration', - showFilters: false, - ), - ); - }, - routes: [ - GoRoute( - path: ':courseid', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - SelectedCourse( - state.pathParameters['courseid']!, - SelectedCourseMode.launch, - ), - ); - }, - routes: [ - GoRoute( - path: 'invite', - pageBuilder: (context, state) { - return defaultPageBuilder( - context, - state, - CourseInvitePage( - state.pathParameters['courseid']!, - courseCreationCompleter: - state.extra as Completer?, - ), - ); - }, - ), - ], - ), - ], - ), - ], ), ], ), @@ -434,7 +350,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const AddCoursePage(route: 'rooms'), + const FindCoursePage(), ), routes: [ GoRoute( diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 8e38e6faf..0a5a0fce8 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-21 13:54:18.388293", + "@@last_modified": "2026-01-22 09:36:57.998979", "about": "حول", "@about": { "type": "String", @@ -11117,5 +11117,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "هل لديك رمز دعوة أو رابط لدورة عامة؟", + "welcomeUser": "مرحبًا {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index 5cfc00f97..2dce40286 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-21 13:54:07.402936", + "@@last_modified": "2026-01-22 09:36:46.926563", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11999,5 +11999,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Ці маеце вы код запрашэння або спасылку на публічны курс?", + "welcomeUser": "Сардэчна запрашаем, {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index d369b9e4a..8f050fc45 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:31.328626", + "@@last_modified": "2026-01-22 09:37:11.081120", "about": "সম্পর্কে", "@about": { "type": "String", @@ -12004,5 +12004,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "আপনার কি একটি আমন্ত্রণ কোড বা একটি পাবলিক কোর্সের লিঙ্ক আছে?", + "welcomeUser": "স্বাগতম {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index 39ccde987..25f3812e6 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-21 13:54:28.767499", + "@@last_modified": "2026-01-22 09:37:08.423146", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10654,5 +10654,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?", + "welcomeUser": "Welcome {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 3c477421a..f156261dc 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:08.647836", + "@@last_modified": "2026-01-22 09:36:49.492725", "about": "Quant a", "@about": { "type": "String", @@ -10924,5 +10924,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Tens un codi d'invitació o un enllaç a un curs públic?", + "welcomeUser": "Benvingut {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 8c7cbe62d..02f3599bb 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-21 13:54:04.800051", + "@@last_modified": "2026-01-22 09:36:43.351703", "about": "O aplikaci", "@about": { "type": "String", @@ -11507,5 +11507,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Máte pozvánkový kód nebo odkaz na veřejný kurz?", + "welcomeUser": "Vítejte {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 84a867b38..cdc417761 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-21 13:53:34.885532", + "@@last_modified": "2026-01-22 09:36:14.204217", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11961,5 +11961,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Har du en invitationskode eller et link til et offentligt kursus?", + "welcomeUser": "Velkommen {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index d0cfb6194..485190272 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-21 13:53:56.595777", + "@@last_modified": "2026-01-22 09:36:34.415651", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10907,5 +10907,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Haben Sie einen Einladungscode oder einen Link zu einem öffentlichen Kurs?", + "welcomeUser": "Willkommen {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 333511f61..f7acd58b4 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-21 13:54:37.783088", + "@@last_modified": "2026-01-22 09:37:17.865903", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11958,5 +11958,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Έχετε έναν κωδικό πρόσκλησης ή σύνδεσμο για ένα δημόσιο μάθημα;", + "welcomeUser": "Καλώς ήρθατε {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ae9b71bed..d1997a9e4 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5058,5 +5058,15 @@ "learn": "Learn", "languageUpdated": "Target language updated!", "voiceDropdownTitle": "Pangea Bot voice", - "knockDesc": "Your request has been sent to course admin! You'll be let in if they approve." + "knockDesc": "Your request has been sent to course admin! You'll be let in if they approve.", + "joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?", + "welcomeUser": "Welcome {user}", + "@welcomeUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "findCourse": "Find a course" } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 4d5067671..11ffe3622 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:42.193114", + "@@last_modified": "2026-01-22 09:37:22.593342", "about": "Prio", "@about": { "type": "String", @@ -11989,5 +11989,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Ĉu vi havas invitkodon aŭ ligon al publika kurso?", + "welcomeUser": "Bonvenon {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index a7ccb8c3c..e564ce294 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-21 13:53:29.857658", + "@@last_modified": "2026-01-22 09:36:09.573972", "about": "Acerca de", "@about": { "type": "String", @@ -8134,5 +8134,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?", + "welcomeUser": "Bienvenido {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index d50c106b2..7a04b0cb1 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-21 13:53:55.586614", + "@@last_modified": "2026-01-22 09:36:33.207413", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11171,5 +11171,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Kas sul on kutsekood või link avalikule kursusele?", + "welcomeUser": "Tere tulemast {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index b8175726d..f5e4ef341 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-21 13:53:53.122879", + "@@last_modified": "2026-01-22 09:36:30.783445", "about": "Honi buruz", "@about": { "type": "String", @@ -10900,5 +10900,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Baduzu gonbidapen kodea edo lotura publiko baten ikastaroarentzako?", + "welcomeUser": "Ongi etorri {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index eb0feb5bd..968b983de 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:33.096668", + "@@last_modified": "2026-01-22 09:37:12.739208", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11632,5 +11632,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "آیا کد دعوت یا لینکی به یک دوره عمومی دارید؟", + "welcomeUser": "خوش آمدید {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 8684fcf0d..15045df42 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-21 13:53:33.564588", + "@@last_modified": "2026-01-22 09:36:12.968075", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11523,5 +11523,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Onko sinulla kutsukoodia tai linkkiä julkiseen kurssiin?", + "welcomeUser": "Tervetuloa {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index 2489ec5b6..b23a5a12d 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-21 13:54:14.817261", + "@@last_modified": "2026-01-22 09:36:55.313694", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11876,5 +11876,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Mayroon ka bang invite code o link sa isang pampublikong kurso?", + "welcomeUser": "Maligayang pagdating {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 700921c07..aa79d94c5 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-21 13:54:48.318824", + "@@last_modified": "2026-01-22 09:37:30.149848", "about": "À propos", "@about": { "type": "String", @@ -11224,5 +11224,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Avez-vous un code d'invitation ou un lien vers un cours public ?", + "welcomeUser": "Bienvenue {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index 9abadb3a0..9977cc5db 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-21 13:54:46.978792", + "@@last_modified": "2026-01-22 09:37:28.516173", "@customReaction": { "type": "String", "placeholders": {} @@ -10898,5 +10898,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "An bhfuil cód cuireadh nó nasc agat do chúrsa poiblí?", + "welcomeUser": "Fáilte {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 23f338731..4ca3a571a 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-21 13:53:31.619299", + "@@last_modified": "2026-01-22 09:36:11.502910", "about": "Acerca de", "@about": { "type": "String", @@ -10897,5 +10897,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Tes un código de invitación ou un enlace a un curso público?", + "welcomeUser": "Benvido {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index af9e0ad59..34b8e27e6 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:47.592162", + "@@last_modified": "2026-01-22 09:36:25.705220", "about": "אודות", "@about": { "type": "String", @@ -11949,5 +11949,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "האם יש לך קוד הזמנה או קישור לקורס ציבורי?", + "welcomeUser": "ברוך הבא {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index 064cbe283..f2fcd84f3 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-21 13:54:40.850433", + "@@last_modified": "2026-01-22 09:37:20.935542", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11985,5 +11985,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "क्या आपके पास एक आमंत्रण कोड या सार्वजनिक पाठ्यक्रम के लिए लिंक है?", + "welcomeUser": "स्वागत है {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 6b8517e96..c324f5510 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-21 13:53:46.524141", + "@@last_modified": "2026-01-22 09:36:23.684413", "about": "Informacije", "@about": { "type": "String", @@ -11272,5 +11272,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni tečaj?", + "welcomeUser": "Dobrodošli {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index b0767225a..335644fa3 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-21 13:53:36.742109", + "@@last_modified": "2026-01-22 09:36:15.788819", "about": "Névjegy", "@about": { "type": "String", @@ -10901,5 +10901,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Van meghívó kódod vagy linked egy nyilvános kurzushoz?", + "welcomeUser": "Üdvözöljük {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 506658635..8cf7d88de 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-21 13:53:49.028067", + "@@last_modified": "2026-01-22 09:36:27.975426", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11978,5 +11978,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?", + "welcomeUser": "Bienvenido {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 5db3bb912..315eb4ad9 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:37.899898", + "@@last_modified": "2026-01-22 09:36:18.048263", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10891,5 +10891,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Apakah Anda memiliki kode undangan atau tautan ke kursus publik?", + "welcomeUser": "Selamat datang {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index 3d38e5615..95e9c50e4 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-21 13:53:45.416677", + "@@last_modified": "2026-01-22 09:36:22.149976", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11874,5 +11874,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?", + "welcomeUser": "Bienvenido {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 312c08f22..9892fd75d 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:01.568848", + "@@last_modified": "2026-01-22 09:36:40.074415", "about": "Informazioni", "@about": { "type": "String", @@ -10903,5 +10903,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Hai un codice di invito o un link per un corso pubblico?", + "welcomeUser": "Benvenuto {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index c511c0107..47408d661 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-21 13:54:39.359536", + "@@last_modified": "2026-01-22 09:37:19.372006", "about": "このアプリについて", "@about": { "type": "String", @@ -11690,5 +11690,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "招待コードまたは公開コースへのリンクはありますか?", + "welcomeUser": "ようこそ {user} さん", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 7c3ce8707..593c8768a 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-21 13:54:44.486907", + "@@last_modified": "2026-01-22 09:37:25.958869", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11930,5 +11930,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "გაქვთ თუ არა მოწვევის კოდი ან ბმული საჯარო კურსზე?", + "welcomeUser": "კეთილი იყოს თქვენი მობრძანება {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index 655637228..14be02641 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:28.417406", + "@@last_modified": "2026-01-22 09:36:07.420159", "about": "소개", "@about": { "type": "String", @@ -11008,5 +11008,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "공개 과정에 대한 초대 코드나 링크가 있습니까?", + "welcomeUser": "환영합니다 {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 3473469a9..234781a80 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-21 13:54:22.729769", + "@@last_modified": "2026-01-22 09:37:02.748391", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11705,5 +11705,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Ar turite kvietimo kodą arba nuorodą į viešą kursą?", + "welcomeUser": "Sveiki atvykę, {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index feff58b67..c061cdd82 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-21 13:54:16.883169", + "@@last_modified": "2026-01-22 09:36:56.779342", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10886,5 +10886,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Vai jums ir uzaicinājuma kods vai saite uz publisku kursu?", + "welcomeUser": "Laipni lūdzam, {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index f87cddbc8..2d8a774c6 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:06.232198", + "@@last_modified": "2026-01-22 09:36:44.562587", "about": "Om", "@about": { "type": "String", @@ -11993,5 +11993,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Har du en invitasjonskode eller lenke til et offentlig kurs?", + "welcomeUser": "Velkommen {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index ed586d553..9d3e13803 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:27.649909", + "@@last_modified": "2026-01-22 09:37:07.354119", "about": "Over ons", "@about": { "type": "String", @@ -10900,5 +10900,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Heb je een uitnodigingscode of link naar een openbare cursus?", + "welcomeUser": "Welkom {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index d7d2c73a5..578c63e83 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-21 13:54:34.451816", + "@@last_modified": "2026-01-22 09:37:14.175391", "about": "O aplikacji", "@about": { "type": "String", @@ -10898,5 +10898,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Czy masz kod zaproszenia lub link do publicznego kursu?", + "welcomeUser": "Witaj {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index d9b54e65a..d7aa4ddd5 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:54.148542", + "@@last_modified": "2026-01-22 09:36:31.935958", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -12000,5 +12000,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?", + "welcomeUser": "Bem-vindo {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 11797eb83..2bf65492d 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:51.587209", + "@@last_modified": "2026-01-22 09:36:29.216087", "about": "Sobre", "@about": { "type": "String", @@ -11258,5 +11258,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?", + "welcomeUser": "Bem-vindo {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index bc97480dc..989bed4ba 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-21 13:54:11.801274", + "@@last_modified": "2026-01-22 09:36:52.464279", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11929,5 +11929,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?", + "welcomeUser": "Bem-vindo {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 3f458ad96..4e6931a1b 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:53:41.456181", + "@@last_modified": "2026-01-22 09:36:19.487915", "about": "Despre", "@about": { "type": "String", @@ -11635,5 +11635,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Ai un cod de invitație sau un link pentru un curs public?", + "welcomeUser": "Bine ai venit {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 3cbd203a9..fbf558e17 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-21 15:35:33.881278", + "@@last_modified": "2026-01-22 09:37:24.211755", "about": "О проекте", "@about": { "type": "String", @@ -11008,5 +11008,19 @@ "@italicText": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "У вас есть код приглашения или ссылка на публичный курс?", + "welcomeUser": "Добро пожаловать {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 510156730..41ba5c0ac 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-21 13:53:43.706457", + "@@last_modified": "2026-01-22 09:36:21.123706", "about": "O aplikácii", "@about": { "type": "String", @@ -11984,5 +11984,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Máte pozývací kód alebo odkaz na verejný kurz?", + "welcomeUser": "Vitaj {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index 9736fb5f5..f21e4e190 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-21 13:53:58.241980", + "@@last_modified": "2026-01-22 09:36:36.017757", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11981,5 +11981,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Imate kodo za povabilo ali povezavo do javnega tečaja?", + "welcomeUser": "Dobrodošli {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index 0b970dceb..e3a0cad71 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:45.561732", + "@@last_modified": "2026-01-22 09:37:27.068492", "about": "О програму", "@about": { "type": "String", @@ -12002,5 +12002,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni kurs?", + "welcomeUser": "Dobrodošli {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 302e97dde..bf3877dd1 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:35.931933", + "@@last_modified": "2026-01-22 09:37:16.018226", "about": "Om", "@about": { "type": "String", @@ -11378,5 +11378,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Har du en inbjudningskod eller länk till en offentlig kurs?", + "welcomeUser": "Välkommen {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 82050d0e3..f4d1fadf9 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:25.999605", + "@@last_modified": "2026-01-22 09:37:06.101200", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11124,5 +11124,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "உங்களுக்கு ஒரு அழைப்பு குறியீடு அல்லது பொது பாடத்திற்கு இணைப்பு உள்ளதா?", + "welcomeUser": "வரவேற்கிறேன் {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index e457db572..27d5345db 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-21 13:54:20.897642", + "@@last_modified": "2026-01-22 09:37:01.069487", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11989,5 +11989,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "మీకు పబ్లిక్ కోర్సుకు ఆహ్వాన కోడ్ లేదా లింక్ ఉందా?", + "welcomeUser": "స్వాగతం {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index 397aaa446..5fbd2a2e6 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-21 13:54:10.758435", + "@@last_modified": "2026-01-22 09:36:51.239616", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11958,5 +11958,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "คุณมีรหัสเชิญหรือลิงก์ไปยังหลักสูตรสาธารณะหรือไม่?", + "welcomeUser": "ยินดีต้อนรับ {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index f38520405..49bfe664c 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-21 13:54:19.467039", + "@@last_modified": "2026-01-22 09:36:59.414494", "about": "Hakkında", "@about": { "type": "String", @@ -11122,5 +11122,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Bir davet kodunuz veya halka açık bir kursa bağlantınız var mı?", + "welcomeUser": "Hoş geldin {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index 5dbfc2938..71f9cbbb6 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-21 13:54:03.403456", + "@@last_modified": "2026-01-22 09:36:41.798719", "about": "Про застосунок", "@about": { "type": "String", @@ -10894,5 +10894,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "У вас є код запрошення або посилання на публічний курс?", + "welcomeUser": "Ласкаво просимо {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 89760d972..9c66dc600 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:24.252269", + "@@last_modified": "2026-01-22 09:37:04.643688", "about": "Giới thiệu", "@about": { "type": "String", @@ -6470,5 +6470,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Bạn có mã mời hoặc liên kết đến một khóa học công khai không?", + "welcomeUser": "Chào mừng {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index b280b4a62..95063be1f 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-21 13:53:59.885092", + "@@last_modified": "2026-01-22 09:36:38.546245", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11991,5 +11991,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "你有邀請碼或公共課程的鏈接嗎?", + "welcomeUser": "歡迎 {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index ffe826b05..e4c85be4a 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-21 13:54:29.681537", + "@@last_modified": "2026-01-22 09:37:09.563669", "about": "关于", "@about": { "type": "String", @@ -10891,5 +10891,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "您是否有邀请代码或公共课程的链接?", + "welcomeUser": "欢迎 {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index fc92bb5dc..5434d1cc6 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 13:54:13.165623", + "@@last_modified": "2026-01-22 09:36:53.893925", "about": "關於", "@about": { "type": "String", @@ -10898,5 +10898,19 @@ "@knockDesc": { "type": "String", "placeholders": {} + }, + "joinSpaceOnboardingDesc": "您是否有邀請碼或公共課程的鏈接?", + "welcomeUser": "歡迎 {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } } } \ No newline at end of file diff --git a/lib/pages/onboarding/space_code_onboarding.dart b/lib/pages/onboarding/space_code_onboarding.dart new file mode 100644 index 000000000..54c50f557 --- /dev/null +++ b/lib/pages/onboarding/space_code_onboarding.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pages/onboarding/space_code_onboarding_view.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/join_codes/space_code_controller.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +class SpaceCodeOnboarding extends StatefulWidget { + const SpaceCodeOnboarding({super.key}); + + @override + State createState() => SpaceCodeOnboardingState(); +} + +class SpaceCodeOnboardingState extends State { + Profile? profile; + Client get client => Matrix.of(context).client; + + final TextEditingController codeController = TextEditingController(); + + @override + void initState() { + _setProfile(); + codeController.addListener(() { + if (mounted) setState(() {}); + }); + super.initState(); + } + + @override + void dispose() { + codeController.dispose(); + super.dispose(); + } + + Future _setProfile() async { + try { + profile = await client.getProfileFromUserId( + client.userID!, + ); + } catch (e, s) { + ErrorHandler.logError( + e: e, + s: s, + data: { + 'userId': client.userID, + }, + ); + } finally { + if (mounted) setState(() {}); + } + } + + Future submitCode() async { + String code = codeController.text.trim(); + if (code.isEmpty) return; + + try { + final link = Uri.parse(Uri.parse(code).fragment); + if (link.queryParameters.containsKey(SpaceConstants.classCode)) { + code = link.queryParameters[SpaceConstants.classCode]!; + } + } catch (e) { + debugPrint("Text input is not a URL: $e"); + } + + final roomId = await SpaceCodeController.joinSpaceWithCode(context, code); + if (roomId != null) { + final room = Matrix.of(context).client.getRoomById(roomId); + room?.isSpace ?? true + ? context.go('/rooms/spaces/$roomId/details') + : context.go('/rooms/$roomId'); + } + } + + @override + Widget build(BuildContext context) => + SpaceCodeOnboardingView(controller: this); +} diff --git a/lib/pages/onboarding/space_code_onboarding_view.dart b/lib/pages/onboarding/space_code_onboarding_view.dart new file mode 100644 index 000000000..37dc648f9 --- /dev/null +++ b/lib/pages/onboarding/space_code_onboarding_view.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart'; +import 'package:fluffychat/pangea/authentication/p_logout.dart'; +import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart'; + +class SpaceCodeOnboardingView extends StatelessWidget { + final SpaceCodeOnboardingState controller; + const SpaceCodeOnboardingView({ + super.key, + required this.controller, + }); + + @override + Widget build(BuildContext context) { + return PangeaLoginScaffold( + customAppBar: AppBar( + leading: BackButton( + onPressed: () => pLogoutAction( + context, + bypassWarning: true, + ), + ), + ), + showAppName: false, + mainAssetUrl: controller.profile?.avatarUrl, + children: [ + Column( + spacing: 8.0, + children: [ + Text( + L10n.of(context).welcomeUser( + controller.profile?.displayName ?? + controller.client.userID?.localpart ?? + "", + ), + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + Text( + L10n.of(context).joinSpaceOnboardingDesc, + textAlign: TextAlign.center, + ), + TextField( + decoration: InputDecoration( + hintText: L10n.of(context).enterCodeToJoin, + ), + controller: controller.codeController, + onSubmitted: (_) => controller.submitCode, + ), + ElevatedButton( + onPressed: controller.codeController.text.isNotEmpty + ? controller.submitCode + : null, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primaryContainer, + foregroundColor: + Theme.of(context).colorScheme.onPrimaryContainer, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(L10n.of(context).join), + ], + ), + ), + TextButton( + child: Text(L10n.of(context).skipForNow), + onPressed: () => context.go("/rooms"), + ), + ], + ), + ], + ); + } +} diff --git a/lib/pangea/login/pages/course_code_page.dart b/lib/pangea/login/pages/course_code_page.dart index 6d3cd55ce..fd2e36cb3 100644 --- a/lib/pangea/login/pages/course_code_page.dart +++ b/lib/pangea/login/pages/course_code_page.dart @@ -6,7 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/join_codes/space_code_controller.dart'; -import 'package:fluffychat/pangea/login/pages/add_course_page.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CourseCodePage extends StatefulWidget { @@ -72,7 +72,7 @@ class CourseCodePageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SvgPicture.network( - "${AppConfig.assetsBaseURL}/${AddCoursePage.mapUnlockFileName}", + "${AppConfig.assetsBaseURL}/${SpaceConstants.mapUnlockFileName}", width: 100.0, height: 100.0, colorFilter: ColorFilter.mode( diff --git a/lib/pangea/login/pages/find_course_page.dart b/lib/pangea/login/pages/find_course_page.dart new file mode 100644 index 000000000..df2e4c00e --- /dev/null +++ b/lib/pangea/login/pages/find_course_page.dart @@ -0,0 +1,523 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; +import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart'; +import 'package:fluffychat/pangea/course_creation/course_info_chip_widget.dart'; +import 'package:fluffychat/pangea/course_creation/course_language_filter.dart'; +import 'package:fluffychat/pangea/course_plans/courses/course_plan_model.dart'; +import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; +import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/spaces/public_course_extension.dart'; +import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; +import 'package:fluffychat/widgets/layouts/max_width_body.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +class FindCoursePage extends StatefulWidget { + const FindCoursePage({super.key}); + + @override + State createState() => FindCoursePageState(); +} + +class FindCoursePageState extends State { + final TextEditingController searchController = TextEditingController(); + + bool loading = true; + bool _fullyLoaded = false; + Object? error; + Timer? _coolDown; + + LanguageModel? targetLanguageFilter; + + List discoveredCourses = []; + Map coursePlans = {}; + String? nextBatch; + + @override + void initState() { + super.initState(); + + final target = MatrixState.pangeaController.userController.userL2; + if (target != null) { + setTargetLanguageFilter(target); + } + + _loadCourses(); + } + + @override + void dispose() { + searchController.dispose(); + _coolDown?.cancel(); + super.dispose(); + } + + void setTargetLanguageFilter(LanguageModel? language) { + if (targetLanguageFilter?.langCodeShort == language?.langCodeShort) return; + setState(() => targetLanguageFilter = language); + _loadCourses(); + } + + void onSearchEnter(String text, {bool globalSearch = true}) { + if (text.isEmpty) { + _loadCourses(); + return; + } + + _coolDown?.cancel(); + _coolDown = Timer(const Duration(milliseconds: 500), _loadCourses); + } + + List get filteredCourses { + List filtered = discoveredCourses + .where( + (c) => + !Matrix.of(context).client.rooms.any( + (r) => + r.id == c.room.roomId && + r.membership == Membership.join, + ) && + coursePlans.containsKey(c.courseId), + ) + .toList(); + + if (targetLanguageFilter != null) { + filtered = filtered.where( + (chunk) { + final course = coursePlans[chunk.courseId]; + if (course == null) return false; + return course.targetLanguage.split('-').first == + targetLanguageFilter!.langCodeShort; + }, + ).toList(); + } + + final searchText = searchController.text.trim().toLowerCase(); + if (searchText.isNotEmpty) { + filtered = filtered.where( + (chunk) { + final course = coursePlans[chunk.courseId]; + if (course == null) return false; + final name = chunk.room.name?.toLowerCase() ?? ''; + final description = course.description.toLowerCase(); + return name.contains(searchText) || description.contains(searchText); + }, + ).toList(); + } + + // sort by join rule, with knock rooms at the end + filtered.sort((a, b) { + final aKnock = a.room.joinRule == JoinRules.knock.name; + final bKnock = b.room.joinRule == JoinRules.knock.name; + if (aKnock && !bKnock) return 1; + if (!aKnock && bKnock) return -1; + return 0; + }); + + return filtered; + } + + Future _loadPublicSpaces() async { + try { + final resp = await Matrix.of(context).client.requestPublicCourses( + since: nextBatch, + ); + + for (final room in resp.courses) { + if (!discoveredCourses.any((e) => e.room.roomId == room.room.roomId)) { + discoveredCourses.add(room); + } + } + + nextBatch = resp.nextBatch; + } catch (e, s) { + error = e; + ErrorHandler.logError( + e: e, + s: s, + data: { + 'nextBatch': nextBatch, + }, + ); + } + } + + Future _loadCourses() async { + if (_fullyLoaded && nextBatch == null) { + return; + } + + setState(() { + loading = true; + error = null; + }); + + await _loadPublicSpaces(); + + int timesLoaded = 0; + while (error == null && timesLoaded < 5 && nextBatch != null) { + await _loadPublicSpaces(); + timesLoaded++; + } + + if (nextBatch == null) { + _fullyLoaded = true; + } + + try { + final resp = await CoursePlansRepo.search( + GetLocalizedCoursesRequest( + coursePlanIds: + discoveredCourses.map((c) => c.courseId).toSet().toList(), + l1: MatrixState.pangeaController.userController.userL1Code!, + ), + ); + final searchResult = resp.coursePlans; + + coursePlans.clear(); + for (final entry in searchResult.entries) { + coursePlans[entry.key] = entry.value; + } + } catch (e, s) { + ErrorHandler.logError( + e: e, + s: s, + data: { + 'discoveredCourses': + discoveredCourses.map((c) => c.courseId).toList(), + }, + ); + } finally { + if (mounted) { + setState(() => loading = false); + } + } + } + + @override + Widget build(BuildContext context) { + return FindCoursePageView(controller: this); + } +} + +class FindCoursePageView extends StatelessWidget { + final FindCoursePageState controller; + + const FindCoursePageView({ + super.key, + required this.controller, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final isColumnMode = FluffyThemes.isColumnMode(context); + + return Scaffold( + appBar: AppBar(title: Text(L10n.of(context).findCourse)), + body: MaxWidthBody( + showBorder: false, + withScrolling: false, + maxWidth: 600.0, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + spacing: 16.0, + children: [ + TextField( + controller: controller.searchController, + textInputAction: TextInputAction.search, + onChanged: controller.onSearchEnter, + decoration: InputDecoration( + filled: !isColumnMode, + fillColor: isColumnMode + ? null + : theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: + isColumnMode ? const BorderSide() : BorderSide.none, + borderRadius: BorderRadius.circular(100), + ), + contentPadding: const EdgeInsets.fromLTRB( + 0, + 0, + 20.0, + 0, + ), + hintText: L10n.of(context).findCourse, + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + fontSize: 16.0, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: IconButton( + onPressed: () {}, + icon: Icon( + Icons.search_outlined, + color: theme.colorScheme.onPrimaryContainer, + ), + ), + ), + ), + LayoutBuilder( + builder: (context, constrained) { + return Row( + spacing: 12.0, + children: [ + Expanded( + child: CourseLanguageFilter( + value: controller.targetLanguageFilter, + onChanged: controller.setTargetLanguageFilter, + ), + ), + if (constrained.maxWidth >= 500) ...[ + TextButton( + child: Row( + spacing: 8.0, + children: [ + const Icon(Icons.add), + Text(L10n.of(context).newCourse), + ], + ), + onPressed: () => context.go("/rooms/course/own"), + ), + TextButton( + child: Row( + spacing: 8.0, + children: [ + const Icon(Icons.join_full), + Text(L10n.of(context).joinWithCode), + ], + ), + onPressed: () => context.go("/rooms/course/private"), + ), + ] else + PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (context) => [ + PopupMenuItem( + onTap: () => context.go("/rooms/course/own"), + child: Row( + spacing: 8.0, + children: [ + const Icon(Icons.add), + Text(L10n.of(context).newCourse), + ], + ), + ), + PopupMenuItem( + onTap: () => context.go("/rooms/course/private"), + child: Row( + spacing: 8.0, + children: [ + const Icon(Icons.join_full), + Text(L10n.of(context).joinWithCode), + ], + ), + ), + ], + ), + ], + ); + }, + ), + ValueListenableBuilder( + valueListenable: controller.searchController, + builder: (context, _, __) { + if (controller.error != null) { + return ErrorIndicator( + message: L10n.of(context).oopsSomethingWentWrong, + ); + } + + if (controller.loading) { + return const CircularProgressIndicator.adaptive(); + } + + if (controller.filteredCourses.isEmpty) { + return Text( + L10n.of(context).nothingFound, + ); + } + + return Expanded( + child: ListView.builder( + itemCount: controller.filteredCourses.length, + itemBuilder: (context, index) { + final space = controller.filteredCourses[index]; + return _PublicCourseTile( + chunk: space, + course: controller.coursePlans[space.courseId], + ); + }, + ), + ); + }, + ), + ], + ), + ), + ), + ); + } +} + +class _PublicCourseTile extends StatelessWidget { + final PublicCoursesChunk chunk; + final CoursePlanModel? course; + + const _PublicCourseTile({ + required this.chunk, + this.course, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final isColumnMode = FluffyThemes.isColumnMode(context); + final space = chunk.room; + final courseId = chunk.courseId; + final displayname = + space.name ?? space.canonicalAlias ?? L10n.of(context).emptyChat; + + return Padding( + padding: isColumnMode + ? const EdgeInsets.only( + bottom: 32.0, + ) + : const EdgeInsets.only( + bottom: 16.0, + ), + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: () => context.go( + '/rooms/course/public/$courseId', + extra: space, + ), + borderRadius: BorderRadius.circular(12.0), + child: Container( + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + border: Border.all( + color: theme.colorScheme.primary, + ), + ), + child: Column( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + spacing: 8.0, + children: [ + ImageByUrl( + imageUrl: space.avatarUrl?.toString(), + width: 58.0, + borderRadius: BorderRadius.circular(10.0), + replacement: Avatar( + name: displayname, + borderRadius: BorderRadius.circular( + 10.0, + ), + size: 58.0, + ), + ), + Flexible( + child: Column( + spacing: 0.0, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + displayname, + style: theme.textTheme.bodyLarge, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Row( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.group, + size: 16.0, + ), + Text( + L10n.of(context).countParticipants( + space.numJoinedMembers, + ), + style: theme.textTheme.bodyMedium, + ), + ], + ), + ], + ), + ), + ], + ), + if (course != null) ...[ + CourseInfoChips( + courseId, + iconSize: 12.0, + fontSize: 12.0, + ), + Text( + course!.description, + style: theme.textTheme.bodyMedium, + ), + ], + const SizedBox(height: 12.0), + HoverBuilder( + builder: (context, hovered) => ElevatedButton( + onPressed: () => context.go( + '/rooms/course/public/$courseId', + extra: space, + ), + style: ElevatedButton.styleFrom( + backgroundColor: + theme.colorScheme.primaryContainer.withAlpha( + hovered ? 255 : 200, + ), + foregroundColor: theme.colorScheme.onPrimaryContainer, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 12.0, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + space.joinRule == JoinRules.knock.name + ? L10n.of( + context, + ).knock + : L10n.of( + context, + ).join, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pangea/spaces/space_constants.dart b/lib/pangea/spaces/space_constants.dart index f6315356f..a387fbcbf 100644 --- a/lib/pangea/spaces/space_constants.dart +++ b/lib/pangea/spaces/space_constants.dart @@ -5,6 +5,7 @@ class SpaceConstants { static const String classCode = 'classcode'; static const String introductionChatAlias = 'introductionChat'; static const String announcementsChatAlias = 'announcementsChat'; + static String mapUnlockFileName = "unlock_trip.svg"; static List introChatIcons = [ '${AppConfig.assetsBaseURL}/Introduction_1.jpg', diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index 04f81476a..cb8b08b6c 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -202,7 +202,7 @@ class SpacesNavigationRail extends StatelessWidget { child: const Icon(Icons.add), ), ), - toolTip: L10n.of(context).addCourse, + toolTip: L10n.of(context).findCourse, expanded: expanded, // Pangea# ); From baa9324229403a7edd15fe6c57d39990d37c22be Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:02:51 -0500 Subject: [PATCH 34/42] fix: add more space between text and underline for highlighted tokens (#5332) --- .../toolbar/reading_assistance/underline_text_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/toolbar/reading_assistance/underline_text_widget.dart b/lib/pangea/toolbar/reading_assistance/underline_text_widget.dart index b64c4e410..be680a5c2 100644 --- a/lib/pangea/toolbar/reading_assistance/underline_text_widget.dart +++ b/lib/pangea/toolbar/reading_assistance/underline_text_widget.dart @@ -39,7 +39,7 @@ class UnderlineText extends StatelessWidget { ), ), Positioned( - bottom: 2, // fixed distance from baseline + bottom: 0, left: 0, right: 0, child: Container( From 3fa027f702a3f71a9fc0d7fe95b11db395de2e67 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:30:52 -0500 Subject: [PATCH 35/42] chore: close emoji picker on send message (#5336) --- lib/pages/chat/chat.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 58947f941..5e6be0394 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -952,6 +952,9 @@ class ChatController extends State } final previousEdit = editEvent; + if (showEmojiPicker) { + hideEmojiPicker(); + } room .pangeaSendTextEvent( From aa855bcc71cd72dd2357179de9f691fa672ae9b0 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:03:38 -0500 Subject: [PATCH 36/42] chore: add copy asking user to search for users in invite public tab (#5338) --- lib/l10n/intl_ar.arb | 12 +++++- lib/l10n/intl_be.arb | 12 +++++- lib/l10n/intl_bn.arb | 12 +++++- lib/l10n/intl_bo.arb | 12 +++++- lib/l10n/intl_ca.arb | 12 +++++- lib/l10n/intl_cs.arb | 12 +++++- lib/l10n/intl_da.arb | 12 +++++- lib/l10n/intl_de.arb | 12 +++++- lib/l10n/intl_el.arb | 12 +++++- lib/l10n/intl_en.arb | 4 +- lib/l10n/intl_eo.arb | 12 +++++- lib/l10n/intl_es.arb | 12 +++++- lib/l10n/intl_et.arb | 12 +++++- lib/l10n/intl_eu.arb | 12 +++++- lib/l10n/intl_fa.arb | 12 +++++- lib/l10n/intl_fi.arb | 12 +++++- lib/l10n/intl_fil.arb | 12 +++++- lib/l10n/intl_fr.arb | 12 +++++- lib/l10n/intl_ga.arb | 12 +++++- lib/l10n/intl_gl.arb | 12 +++++- lib/l10n/intl_he.arb | 12 +++++- lib/l10n/intl_hi.arb | 12 +++++- lib/l10n/intl_hr.arb | 12 +++++- lib/l10n/intl_hu.arb | 12 +++++- lib/l10n/intl_ia.arb | 12 +++++- lib/l10n/intl_id.arb | 12 +++++- lib/l10n/intl_ie.arb | 12 +++++- lib/l10n/intl_it.arb | 12 +++++- lib/l10n/intl_ja.arb | 12 +++++- lib/l10n/intl_ka.arb | 12 +++++- lib/l10n/intl_ko.arb | 12 +++++- lib/l10n/intl_lt.arb | 12 +++++- lib/l10n/intl_lv.arb | 12 +++++- lib/l10n/intl_nb.arb | 12 +++++- lib/l10n/intl_nl.arb | 12 +++++- lib/l10n/intl_pl.arb | 12 +++++- lib/l10n/intl_pt.arb | 12 +++++- lib/l10n/intl_pt_BR.arb | 12 +++++- lib/l10n/intl_pt_PT.arb | 12 +++++- lib/l10n/intl_ro.arb | 12 +++++- lib/l10n/intl_ru.arb | 12 +++++- lib/l10n/intl_sk.arb | 12 +++++- lib/l10n/intl_sl.arb | 12 +++++- lib/l10n/intl_sr.arb | 12 +++++- lib/l10n/intl_sv.arb | 12 +++++- lib/l10n/intl_ta.arb | 12 +++++- lib/l10n/intl_te.arb | 12 +++++- lib/l10n/intl_th.arb | 12 +++++- lib/l10n/intl_tr.arb | 12 +++++- lib/l10n/intl_uk.arb | 12 +++++- lib/l10n/intl_vi.arb | 12 +++++- lib/l10n/intl_yue.arb | 12 +++++- lib/l10n/intl_zh.arb | 12 +++++- lib/l10n/intl_zh_Hant.arb | 12 +++++- .../pangea_invitation_selection_view.dart | 37 ++++++++++++------- 55 files changed, 609 insertions(+), 68 deletions(-) diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 0a5a0fce8..555419459 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-22 09:36:57.998979", + "@@last_modified": "2026-01-22 12:01:48.002470", "about": "حول", "@about": { "type": "String", @@ -11131,5 +11131,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "ابحث عن المستخدمين لدعوتهم إلى هذه الدردشة.", + "publicInviteDescSpace": "ابحث عن المستخدمين لدعوتهم إلى هذا الفضاء.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index 2dce40286..308a7c937 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-22 09:36:46.926563", + "@@last_modified": "2026-01-22 12:01:33.641094", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -12013,5 +12013,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Шукаць карыстальнікаў, каб запрасіць іх у гэты чат.", + "publicInviteDescSpace": "Шукаць карыстальнікаў, каб запрасіць іх у гэтае прастору.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 8f050fc45..159b35f1c 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:11.081120", + "@@last_modified": "2026-01-22 12:02:02.719528", "about": "সম্পর্কে", "@about": { "type": "String", @@ -12018,5 +12018,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "এই চ্যাটে আমন্ত্রণ জানানোর জন্য ব্যবহারকারীদের খুঁজুন।", + "publicInviteDescSpace": "এই স্পেসে আমন্ত্রণ জানানোর জন্য ব্যবহারকারীদের খুঁজুন।", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index 25f3812e6..ddf1789ee 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-22 09:37:08.423146", + "@@last_modified": "2026-01-22 12:01:59.937396", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10668,5 +10668,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Searc for users to invite them to this chat.", + "publicInviteDescSpace": "Searc for users to invite them to this space.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index f156261dc..f36ac6e93 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:49.492725", + "@@last_modified": "2026-01-22 12:01:35.625715", "about": "Quant a", "@about": { "type": "String", @@ -10938,5 +10938,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cerca usuaris per convidar-los a aquest xat.", + "publicInviteDescSpace": "Cerca usuaris per convidar-los a aquest espai.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 02f3599bb..ae04b94ef 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-22 09:36:43.351703", + "@@last_modified": "2026-01-22 12:01:30.360444", "about": "O aplikaci", "@about": { "type": "String", @@ -11521,5 +11521,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Hledejte uživatele, které chcete pozvat do tohoto chatu.", + "publicInviteDescSpace": "Hledejte uživatele, které chcete pozvat do tohoto prostoru.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index cdc417761..348f745c2 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-22 09:36:14.204217", + "@@last_modified": "2026-01-22 12:00:57.389253", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11975,5 +11975,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Søg efter brugere for at invitere dem til denne chat.", + "publicInviteDescSpace": "Søg efter brugere for at invitere dem til dette rum.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 485190272..e3279533c 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-22 09:36:34.415651", + "@@last_modified": "2026-01-22 12:01:22.683777", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10921,5 +10921,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Suchen Sie nach Benutzern, um sie zu diesem Chat einzuladen.", + "publicInviteDescSpace": "Suchen Sie nach Benutzern, um sie zu diesem Raum einzuladen.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index f7acd58b4..1b2e51664 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-22 09:37:17.865903", + "@@last_modified": "2026-01-22 12:02:10.279313", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11972,5 +11972,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτήν την συνομιλία.", + "publicInviteDescSpace": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτόν τον χώρο.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d1997a9e4..56d4cacf1 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5068,5 +5068,7 @@ } } }, - "findCourse": "Find a course" + "findCourse": "Find a course", + "publicInviteDescChat": "Search for users to invite them to this chat.", + "publicInviteDescSpace": "Search for users to invite them to this space." } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 11ffe3622..176cadd19 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:22.593342", + "@@last_modified": "2026-01-22 12:02:15.725740", "about": "Prio", "@about": { "type": "String", @@ -12003,5 +12003,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Serĉu uzantojn por inviti ilin al ĉi tiu konversacio.", + "publicInviteDescSpace": "Serĉu uzantojn por inviti ilin al ĉi tiu spaco.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index e564ce294..e326183e3 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-22 09:36:09.573972", + "@@last_modified": "2026-01-22 12:00:51.625942", "about": "Acerca de", "@about": { "type": "String", @@ -8148,5 +8148,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Busca usuarios para invitarlos a este chat.", + "publicInviteDescSpace": "Busca usuarios para invitarlos a este espacio.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 7a04b0cb1..4f42e7ed9 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-22 09:36:33.207413", + "@@last_modified": "2026-01-22 12:01:20.400166", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11185,5 +11185,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Otsi kasutajaid, et neid sellesse vestlusse kutsuda.", + "publicInviteDescSpace": "Otsi kasutajaid, et neid sellesse ruumi kutsuda.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index f5e4ef341..9a3d88d7d 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-22 09:36:30.783445", + "@@last_modified": "2026-01-22 12:01:17.457241", "about": "Honi buruz", "@about": { "type": "String", @@ -10914,5 +10914,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Bilatu erabiltzaileak txat honetara gonbidatzeko.", + "publicInviteDescSpace": "Bilatu erabiltzaileak espazio honetara gonbidatzeko.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 968b983de..ce6a3a003 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:12.739208", + "@@last_modified": "2026-01-22 12:02:04.083596", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11646,5 +11646,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "برای دعوت کاربران به این چت، جستجو کنید.", + "publicInviteDescSpace": "برای دعوت کاربران به این فضا، جستجو کنید.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 15045df42..fa5c3314f 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-22 09:36:12.968075", + "@@last_modified": "2026-01-22 12:00:55.098205", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11537,5 +11537,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Etsi käyttäjiä kutsuaksesi heidät tähän keskusteluun.", + "publicInviteDescSpace": "Etsi käyttäjiä kutsuaksesi heidät tähän tilaan.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index b23a5a12d..486555076 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-22 09:36:55.313694", + "@@last_modified": "2026-01-22 12:01:44.028462", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11890,5 +11890,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Maghanap ng mga gumagamit upang imbitahan sila sa chat na ito.", + "publicInviteDescSpace": "Maghanap ng mga gumagamit upang imbitahan sila sa espasyong ito.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index aa79d94c5..83a0abc8d 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-22 09:37:30.149848", + "@@last_modified": "2026-01-22 12:02:23.383738", "about": "À propos", "@about": { "type": "String", @@ -11238,5 +11238,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Recherchez des utilisateurs pour les inviter à ce chat.", + "publicInviteDescSpace": "Recherchez des utilisateurs pour les inviter à cet espace.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index 9977cc5db..9991ef9b8 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-22 09:37:28.516173", + "@@last_modified": "2026-01-22 12:02:22.009738", "@customReaction": { "type": "String", "placeholders": {} @@ -10912,5 +10912,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cuardaigh úsáideoirí le cuireadh a thabhairt dóibh chuig an gcomhrá seo.", + "publicInviteDescSpace": "Cuardaigh úsáideoirí le cuireadh a thabhairt dóibh chuig an spás seo.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 4ca3a571a..05f43ba44 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-22 09:36:11.502910", + "@@last_modified": "2026-01-22 12:00:52.883998", "about": "Acerca de", "@about": { "type": "String", @@ -10911,5 +10911,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Busca usuarios para convidalos a este chat.", + "publicInviteDescSpace": "Busca usuarios para convidalos a este espazo.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index 34b8e27e6..7d9437b60 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:25.705220", + "@@last_modified": "2026-01-22 12:01:11.911490", "about": "אודות", "@about": { "type": "String", @@ -11963,5 +11963,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "חפש משתמשים כדי להזמין אותם לצ'אט הזה.", + "publicInviteDescSpace": "חפש משתמשים כדי להזמין אותם למקום הזה.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index f2fcd84f3..38977f931 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-22 09:37:20.935542", + "@@last_modified": "2026-01-22 12:02:13.864252", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11999,5 +11999,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "इस चैट में आमंत्रित करने के लिए उपयोगकर्ताओं की खोज करें।", + "publicInviteDescSpace": "इस स्थान में आमंत्रित करने के लिए उपयोगकर्ताओं की खोज करें।", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index c324f5510..bcc8aa863 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-22 09:36:23.684413", + "@@last_modified": "2026-01-22 12:01:10.402528", "about": "Informacije", "@about": { "type": "String", @@ -11286,5 +11286,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Pretražite korisnike kako biste ih pozvali u ovaj chat.", + "publicInviteDescSpace": "Pretražite korisnike kako biste ih pozvali u ovaj prostor.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 335644fa3..6fdc3d8a8 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-22 09:36:15.788819", + "@@last_modified": "2026-01-22 12:01:00.971468", "about": "Névjegy", "@about": { "type": "String", @@ -10915,5 +10915,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Keresd meg a felhasználókat, hogy meghívd őket erre a csevegésre.", + "publicInviteDescSpace": "Keresd meg a felhasználókat, hogy meghívd őket erre a térre.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 8cf7d88de..6ceeecb3b 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-22 09:36:27.975426", + "@@last_modified": "2026-01-22 12:01:13.730279", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11992,5 +11992,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cerca per utenti per invitarli a questa chat.", + "publicInviteDescSpace": "Cerca per utenti per invitarli a questo spazio.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 315eb4ad9..110d3de09 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:18.048263", + "@@last_modified": "2026-01-22 12:01:02.729155", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10905,5 +10905,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cari pengguna untuk mengundang mereka ke obrolan ini.", + "publicInviteDescSpace": "Cari pengguna untuk mengundang mereka ke ruang ini.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index 95e9c50e4..b51ae1616 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-22 09:36:22.149976", + "@@last_modified": "2026-01-22 12:01:08.955250", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11888,5 +11888,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cuir fiosrúcháin ar úsáideoirí chun iad a gcuir isteach sa chomhrá seo.", + "publicInviteDescSpace": "Cuir fiosrúcháin ar úsáideoirí chun iad a gcuir isteach sa spás seo.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 9892fd75d..9fd23dccf 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:40.074415", + "@@last_modified": "2026-01-22 12:01:27.366543", "about": "Informazioni", "@about": { "type": "String", @@ -10917,5 +10917,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Cerca utenti per invitarli a questa chat.", + "publicInviteDescSpace": "Cerca utenti per invitarli a questo spazio.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index 47408d661..1966fb920 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-22 09:37:19.372006", + "@@last_modified": "2026-01-22 12:02:12.048814", "about": "このアプリについて", "@about": { "type": "String", @@ -11704,5 +11704,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "このチャットに招待するユーザーを検索します。", + "publicInviteDescSpace": "このスペースに招待するユーザーを検索します。", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 593c8768a..134c82710 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-22 09:37:25.958869", + "@@last_modified": "2026-01-22 12:02:18.860564", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11944,5 +11944,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "მომხმარებლების ძიება, რათა მათ ამ ჩატში მოიწვიოთ.", + "publicInviteDescSpace": "მომხმარებლების ძიება, რათა მათ ამ სივრცეში მოიწვიოთ.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index 14be02641..72016f903 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:07.420159", + "@@last_modified": "2026-01-22 12:00:49.883642", "about": "소개", "@about": { "type": "String", @@ -11022,5 +11022,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "이 채팅에 초대할 사용자를 검색하세요.", + "publicInviteDescSpace": "이 공간에 초대할 사용자를 검색하세요.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 234781a80..24ebdd026 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-22 09:37:02.748391", + "@@last_modified": "2026-01-22 12:01:53.612206", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11719,5 +11719,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Ieškokite vartotojų, kad juos pakviestumėte į šį pokalbį.", + "publicInviteDescSpace": "Ieškokite vartotojų, kad juos pakviestumėte į šią erdvę.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index c061cdd82..7d3d6b90a 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-22 09:36:56.779342", + "@@last_modified": "2026-01-22 12:01:46.451812", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10900,5 +10900,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Meklējiet lietotājus, lai viņus aicinātu uz šo čatu.", + "publicInviteDescSpace": "Meklējiet lietotājus, lai viņus aicinātu uz šo telpu.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 2d8a774c6..c42588912 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:44.562587", + "@@last_modified": "2026-01-22 12:01:31.934487", "about": "Om", "@about": { "type": "String", @@ -12007,5 +12007,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Søk etter brukere for å invitere dem til denne chatten.", + "publicInviteDescSpace": "Søk etter brukere for å invitere dem til dette rommet.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 9d3e13803..269e937a6 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:07.354119", + "@@last_modified": "2026-01-22 12:01:58.109571", "about": "Over ons", "@about": { "type": "String", @@ -10914,5 +10914,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Zoek naar gebruikers om ze uit te nodigen voor deze chat.", + "publicInviteDescSpace": "Zoek naar gebruikers om ze uit te nodigen voor deze ruimte.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 578c63e83..350bd73a4 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-22 09:37:14.175391", + "@@last_modified": "2026-01-22 12:02:06.402870", "about": "O aplikacji", "@about": { "type": "String", @@ -10912,5 +10912,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Szukaj użytkowników, aby zaprosić ich do tej rozmowy.", + "publicInviteDescSpace": "Szukaj użytkowników, aby zaprosić ich do tej przestrzeni.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index d7aa4ddd5..83c82206b 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:31.935958", + "@@last_modified": "2026-01-22 12:01:18.883594", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -12014,5 +12014,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Procure usuários para convidá-los para este chat.", + "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 2bf65492d..ad162fafa 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:29.216087", + "@@last_modified": "2026-01-22 12:01:15.782911", "about": "Sobre", "@about": { "type": "String", @@ -11272,5 +11272,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Procure usuários para convidá-los para este chat.", + "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 989bed4ba..7a8af8328 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-22 09:36:52.464279", + "@@last_modified": "2026-01-22 12:01:38.348686", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11943,5 +11943,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Procure usuários para convidá-los para este chat.", + "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 4e6931a1b..8b9d43bf7 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:19.487915", + "@@last_modified": "2026-01-22 12:01:04.721303", "about": "Despre", "@about": { "type": "String", @@ -11649,5 +11649,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Caută utilizatori pentru a-i invita în acest chat.", + "publicInviteDescSpace": "Caută utilizatori pentru a-i invita în acest spațiu.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index fbf558e17..b3d6d7301 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-22 09:37:24.211755", + "@@last_modified": "2026-01-22 12:02:17.097388", "about": "О проекте", "@about": { "type": "String", @@ -11022,5 +11022,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Ищите пользователей, чтобы пригласить их в этот чат.", + "publicInviteDescSpace": "Ищите пользователей, чтобы пригласить их в это пространство.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 41ba5c0ac..9d14338d6 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-22 09:36:21.123706", + "@@last_modified": "2026-01-22 12:01:06.427460", "about": "O aplikácii", "@about": { "type": "String", @@ -11998,5 +11998,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Hľadajte používateľov, aby ste ich pozvali do tohto chatu.", + "publicInviteDescSpace": "Hľadajte používateľov, aby ste ich pozvali do tohto priestoru.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index f21e4e190..4c1c163e9 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-22 09:36:36.017757", + "@@last_modified": "2026-01-22 12:01:24.219945", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11995,5 +11995,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Išči uporabnike, da jih povabiš v ta klepet.", + "publicInviteDescSpace": "Išči uporabnike, da jih povabiš v ta prostor.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index e3a0cad71..3f4594559 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:27.068492", + "@@last_modified": "2026-01-22 12:02:20.533801", "about": "О програму", "@about": { "type": "String", @@ -12016,5 +12016,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Pretražite korisnike da ih pozovete u ovaj čat.", + "publicInviteDescSpace": "Pretražite korisnike da ih pozovete u ovaj prostor.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index bf3877dd1..80464da9a 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:16.018226", + "@@last_modified": "2026-01-22 12:02:08.064498", "about": "Om", "@about": { "type": "String", @@ -11392,5 +11392,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Sök efter användare för att bjuda in dem till den här chatten.", + "publicInviteDescSpace": "Sök efter användare för att bjuda in dem till det här utrymmet.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index f4d1fadf9..91a5b53bc 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:06.101200", + "@@last_modified": "2026-01-22 12:01:56.352823", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11138,5 +11138,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "இந்த உரையாடலுக்கு அழைக்க பயனர்களை தேடுங்கள்.", + "publicInviteDescSpace": "இந்த இடத்திற்கு அழைக்க பயனர்களை தேடுங்கள்.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 27d5345db..68ba8f1da 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-22 09:37:01.069487", + "@@last_modified": "2026-01-22 12:01:51.350363", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -12003,5 +12003,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "ఈ చాట్లో ఆహ్వానించడానికి వినియోగదారులను శోధించండి.", + "publicInviteDescSpace": "ఈ స్థలంలో ఆహ్వానించడానికి వినియోగదారులను శోధించండి.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index 5fbd2a2e6..64d87c964 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-22 09:36:51.239616", + "@@last_modified": "2026-01-22 12:01:36.919446", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11972,5 +11972,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "ค้นหาผู้ใช้เพื่อนำไปเชิญเข้าร่วมแชทนี้。", + "publicInviteDescSpace": "ค้นหาผู้ใช้เพื่อนำไปเชิญเข้าร่วมพื้นที่นี้。", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 49bfe664c..5606a51f4 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-22 09:36:59.414494", + "@@last_modified": "2026-01-22 12:01:49.339268", "about": "Hakkında", "@about": { "type": "String", @@ -11136,5 +11136,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Bu sohbete davet etmek için kullanıcıları arayın.", + "publicInviteDescSpace": "Bu alana davet etmek için kullanıcıları arayın.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index 71f9cbbb6..4b2b95e3d 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-22 09:36:41.798719", + "@@last_modified": "2026-01-22 12:01:28.947095", "about": "Про застосунок", "@about": { "type": "String", @@ -10908,5 +10908,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Шукайте користувачів, щоб запросити їх до цього чату.", + "publicInviteDescSpace": "Шукайте користувачів, щоб запросити їх до цього простору.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 9c66dc600..24a4cb0b6 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:37:04.643688", + "@@last_modified": "2026-01-22 12:01:54.904291", "about": "Giới thiệu", "@about": { "type": "String", @@ -6484,5 +6484,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "Tìm kiếm người dùng để mời họ tham gia trò chuyện này.", + "publicInviteDescSpace": "Tìm kiếm người dùng để mời họ tham gia không gian này.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 95063be1f..2718a4361 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-22 09:36:38.546245", + "@@last_modified": "2026-01-22 12:01:25.863585", "@ignoreUser": { "type": "String", "placeholders": {} @@ -12005,5 +12005,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "搜尋用戶以邀請他們加入此聊天。", + "publicInviteDescSpace": "搜尋用戶以邀請他們加入此空間。", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index e4c85be4a..ffb93c57c 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-22 09:37:09.563669", + "@@last_modified": "2026-01-22 12:02:00.962577", "about": "关于", "@about": { "type": "String", @@ -10905,5 +10905,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "搜索用户以邀请他们加入此聊天。", + "publicInviteDescSpace": "搜索用户以邀请他们加入此空间。", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 5434d1cc6..abdde04dc 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-22 09:36:53.893925", + "@@last_modified": "2026-01-22 12:01:42.417971", "about": "關於", "@about": { "type": "String", @@ -10912,5 +10912,15 @@ "type": "String" } } + }, + "publicInviteDescChat": "搜尋用戶以邀請他們加入此聊天。", + "publicInviteDescSpace": "搜尋用戶以邀請他們加入此空間。", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart index d4515a353..f06160d88 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart @@ -157,20 +157,29 @@ class PangeaInvitationSelectionView extends StatelessWidget { final participants = room.getParticipants().map((user) => user.id).toSet(); return controller.filter == InvitationFilter.public - ? ListView.builder( - itemCount: controller.foundProfiles.length, - itemBuilder: (BuildContext context, int i) => - _InviteContactListTile( - profile: controller.foundProfiles[i], - isMember: participants.contains( - controller.foundProfiles[i].userId, - ), - onTap: () => controller.inviteAction( - controller.foundProfiles[i].userId, - ), - controller: controller, - ), - ) + ? controller.foundProfiles.isEmpty + ? Padding( + padding: const EdgeInsets.all(24.0), + child: Text( + room.isSpace + ? L10n.of(context).publicInviteDescSpace + : L10n.of(context).publicInviteDescChat, + ), + ) + : ListView.builder( + itemCount: controller.foundProfiles.length, + itemBuilder: (BuildContext context, int i) => + _InviteContactListTile( + profile: controller.foundProfiles[i], + isMember: participants.contains( + controller.foundProfiles[i].userId, + ), + onTap: () => controller.inviteAction( + controller.foundProfiles[i].userId, + ), + controller: controller, + ), + ) : ListView.builder( itemCount: contacts.length + 2, itemBuilder: (BuildContext context, int i) { From 6cfdd35f589e8e08c86e6c9b21dc5a19713eb738 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:13:24 -0500 Subject: [PATCH 37/42] chore: hide invite all in space button if everyone from space is already in room (#5340) --- .../pages/pangea_invitation_selection.dart | 14 ++++++++++++++ .../pages/pangea_invitation_selection_view.dart | 4 +--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart index abcbf06c4..e7d5e5d92 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart @@ -148,6 +148,20 @@ class PangeaInvitationSelectionController return parents.first; } + bool get showInviteAllInSpaceButton { + final roomParticipants = participants; + if (roomParticipants == null || + filter != InvitationFilter.space || + spaceParent == null) { + return false; + } + + final spaceParticipants = spaceParent!.getParticipants(); + return spaceParticipants.any( + (participant) => !roomParticipants.any((p) => p.id == participant.id), + ); + } + List get availableFilters => InvitationFilter.values .where( (f) => switch (f) { diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart index f06160d88..0b050a1ed 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart @@ -184,9 +184,7 @@ class PangeaInvitationSelectionView extends StatelessWidget { itemCount: contacts.length + 2, itemBuilder: (BuildContext context, int i) { if (i == 0) { - return controller.filter == - InvitationFilter.space && - controller.spaceParent != null + return controller.showInviteAllInSpaceButton ? ListTile( leading: ClipPath( clipper: MapClipper(), From 9551d39966b9415185b2274b3144b6a3a33f8274 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:15:44 -0500 Subject: [PATCH 38/42] fix: enable language mismatch popup for activity langs that match l1 (#5341) --- .../activity_session_chat/activity_chat_extension.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index dda7b9159..582fddb85 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -44,15 +44,9 @@ extension ActivityMenuLogic on ChatController { return false; } - final l1 = - MatrixState.pangeaController.userController.userL1?.langCodeShort; final l2 = MatrixState.pangeaController.userController.userL2?.langCodeShort; final activityLang = room.activityPlan?.req.targetLanguage.split('-').first; - - return activityLang != null && - l2 != null && - l2 != activityLang && - l1 != activityLang; + return activityLang != null && l2 != activityLang; } } From 2affcab436d9dda9a5d9798ccb059842f354e00c Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:20:21 -0500 Subject: [PATCH 39/42] chore: remove set status button in settings (#5343) --- lib/pages/settings/settings_view.dart | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 3cbd371e4..64d0f0eaa 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -147,9 +147,7 @@ class SettingsView extends StatelessWidget { displayname, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 18, - ), + style: const TextStyle(fontSize: 18), ), ), TextButton.icon( @@ -171,25 +169,6 @@ class SettingsView extends StatelessWidget { // style: const TextStyle(fontSize: 12), ), ), - // #Pangea - TextButton.icon( - onPressed: controller.setStatus, - icon: const Icon( - Icons.add, - size: 14, - ), - style: TextButton.styleFrom( - foregroundColor: - theme.colorScheme.secondary, - iconColor: theme.colorScheme.secondary, - ), - label: Text( - L10n.of(context).setStatus, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - // Pangea# ], ), ), From 0f91cb51d24dee7b787503d9f1ca367d64959bf1 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:23:03 -0500 Subject: [PATCH 40/42] chore: hide option to seperate chat types (#5345) --- lib/pages/chat_list/chat_list.dart | 10 +++++++--- lib/pages/settings_style/settings_style_view.dart | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index d0f2d8948..e30861b57 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -43,6 +43,7 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; + enum PopupMenuAction { settings, invite, @@ -111,9 +112,12 @@ class ChatListController extends State // StreamSubscription? _intentUriStreamSubscription; // Pangea# - ActiveFilter activeFilter = AppConfig.separateChatTypes - ? ActiveFilter.messages - : ActiveFilter.allChats; + // #Pangea + // ActiveFilter activeFilter = AppConfig.separateChatTypes + // ? ActiveFilter.messages + // : ActiveFilter.allChats; + ActiveFilter activeFilter = ActiveFilter.allChats; + // Pangea# // #Pangea String? get activeSpaceId => widget.activeSpaceId; diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 1c11c0f67..fcc35f365 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -353,14 +353,14 @@ class SettingsStyleView extends StatelessWidget { storeKey: SettingKeys.showPresences, defaultValue: AppConfig.showPresences, ), - SettingsSwitchListTile.adaptive( - title: L10n.of(context).separateChatTypes, - onChanged: (b) => AppConfig.separateChatTypes = b, - storeKey: SettingKeys.separateChatTypes, - defaultValue: AppConfig.separateChatTypes, - ), // #Pangea // SettingsSwitchListTile.adaptive( + // title: L10n.of(context).separateChatTypes, + // onChanged: (b) => AppConfig.separateChatTypes = b, + // storeKey: SettingKeys.separateChatTypes, + // defaultValue: AppConfig.separateChatTypes, + // ), + // SettingsSwitchListTile.adaptive( // title: L10n.of(context).displayNavigationRail, // onChanged: (b) => AppConfig.displayNavigationRail = b, // storeKey: SettingKeys.displayNavigationRail, From ba7114b85e56867ad4d0c1f25c36a9c8b0e20301 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:33:52 -0500 Subject: [PATCH 41/42] feat: enable emoji search (#5350) --- lib/pages/chat/chat_emoji_picker.dart | 26 ++++++++++++++++++++++++-- lib/pages/chat_list/chat_list.dart | 1 - 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index d154b0839..734d082e4 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -49,8 +49,19 @@ class ChatEmojiPicker extends StatelessWidget { backgroundColor: theme.colorScheme.onInverseSurface, ), - bottomActionBarConfig: const BottomActionBarConfig( - enabled: false, + bottomActionBarConfig: BottomActionBarConfig( + // #Pangea + // enabled: false, + showBackspaceButton: false, + backgroundColor: Theme.of(context) + .colorScheme + .surfaceContainer, + buttonColor: Theme.of(context) + .colorScheme + .surfaceContainer, + buttonIconColor: + Theme.of(context).colorScheme.onSurface, + // Pangea# ), categoryViewConfig: CategoryViewConfig( backspaceColor: theme.colorScheme.primary, @@ -68,6 +79,17 @@ class ChatEmojiPicker extends StatelessWidget { )!, indicatorColor: theme.colorScheme.onSurface, ), + // #Pangea + viewOrderConfig: const ViewOrderConfig( + middle: EmojiPickerItem.searchBar, + top: EmojiPickerItem.categoryBar, + bottom: EmojiPickerItem.emojiView, + ), + searchViewConfig: SearchViewConfig( + backgroundColor: theme.colorScheme.surface, + buttonIconColor: theme.colorScheme.onSurface, + ), + // Pangea# ), ), // #Pangea diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index e30861b57..42617c405 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -43,7 +43,6 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; - enum PopupMenuAction { settings, invite, From 3aca3e1138a5ece15f03178eb3befd5deb2d3033 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:00:17 -0500 Subject: [PATCH 42/42] fix: reset audio player after auto-playing bot voice message (#5353) --- lib/pages/chat/events/audio_player.dart | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index ee6490a5f..b10397e23 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -151,6 +151,7 @@ class AudioPlayerState extends State { audioPlayer.pause(); audioPlayer.dispose(); matrix.voiceMessageEventId.value = matrix.audioPlayer = null; + matrix.voiceMessageEventId.removeListener(_onPlayerChange); // #Pangea _onAudioStateChanged?.cancel(); // Pangea# @@ -173,6 +174,14 @@ class AudioPlayerState extends State { if (currentPlayer != null) { // #Pangea currentPlayer.setSpeed(playbackSpeed); + _onAudioStateChanged?.cancel(); + _onAudioStateChanged = + matrix.audioPlayer!.playerStateStream.listen((state) { + if (state.processingState == ProcessingState.completed) { + matrix.audioPlayer!.stop(); + matrix.audioPlayer!.seek(Duration.zero); + } + }); // Pangea# if (currentPlayer.isAtEndPosition) { currentPlayer.seek(Duration.zero); @@ -382,10 +391,26 @@ class AudioPlayerState extends State { return eventWaveForm.map((i) => i > 1024 ? 1024 : i).toList(); } + // #Pangea + void _onPlayerChange() { + if (matrix.audioPlayer == null) return; + _onAudioStateChanged?.cancel(); + _onAudioStateChanged = + matrix.audioPlayer?.playerStateStream.listen((state) { + if (state.processingState == ProcessingState.completed) { + matrix.audioPlayer?.stop(); + matrix.audioPlayer?.seek(Duration.zero); + } + }); + } + // Pangea# + @override void initState() { super.initState(); matrix = Matrix.of(context); + WidgetsBinding.instance.addPostFrameCallback((_) => _onPlayerChange()); + matrix.voiceMessageEventId.addListener(_onPlayerChange); _waveform = _getWaveform(); // #Pangea