diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 70fa0fc97..8e38e6faf 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 13:54:18.388293", "about": "حول", "@about": { "type": "String", @@ -11107,5 +11107,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صوت بوت بانجيا", + "@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 d1ff0babf..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-20 12:31:06.570011", + "@@last_modified": "2026-01-21 13:54:07.402936", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11989,5 +11989,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голас Pangea Bot", + "@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 28c180422..d369b9e4a 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 13:54:31.328626", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11994,5 +11994,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "প্যাঙ্গিয়া বটের কণ্ঠ", + "@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 800c24594..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-20 12:31:44.969872", + "@@last_modified": "2026-01-21 13:54:28.767499", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10644,5 +10644,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot voz", + "@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 5eb437241..3c477421a 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 13:54:08.647836", "about": "Quant a", "@about": { "type": "String", @@ -10914,5 +10914,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Veu del bot Pangea", + "@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 321734d12..8c7cbe62d 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 13:54:04.800051", "about": "O aplikaci", "@about": { "type": "String", @@ -11497,5 +11497,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@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 372a6e7c8..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-20 12:30:10.761209", + "@@last_modified": "2026-01-21 13:53:34.885532", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11951,5 +11951,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stemme", + "@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 634b76211..d0cfb6194 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 13:53:56.595777", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10897,5 +10897,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot Stimme", + "@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 176fb6f07..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-20 12:31:59.503296", + "@@last_modified": "2026-01-21 13:54:37.783088", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Φωνή Pangea Bot", + "@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 abc4a3d63..ae9b71bed 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5056,5 +5056,7 @@ "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", + "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 5914ed46b..4d5067671 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 13:54:42.193114", "about": "Prio", "@about": { "type": "String", @@ -11979,5 +11979,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voĉo de Pangea Bot", + "@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 144e71870..a7ccb8c3c 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 13:53:29.857658", "about": "Acerca de", "@about": { "type": "String", @@ -8124,5 +8124,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz del bot Pangea", + "@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 5d4e3f6c2..d50c106b2 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 13:53:55.586614", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11161,5 +11161,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Boti hääl", + "@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 225d963e7..b8175726d 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 13:53:53.122879", "about": "Honi buruz", "@about": { "type": "String", @@ -10890,5 +10890,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot ahotsa", + "@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 977dd42b8..eb0feb5bd 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 13:54:33.096668", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11622,5 +11622,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صدای ربات پانژیا", + "@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 6184adfec..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-20 12:30:08.099637", + "@@last_modified": "2026-01-21 13:53:33.564588", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11513,5 +11513,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Botin ääni", + "@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 986aa4015..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-20 12:31:19.884620", + "@@last_modified": "2026-01-21 13:54:14.817261", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11866,5 +11866,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Boses ng Pangea Bot", + "@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 c279297f0..700921c07 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 13:54:48.318824", "about": "À propos", "@about": { "type": "String", @@ -11214,5 +11214,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@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 c923a3bbd..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-20 12:32:18.033824", + "@@last_modified": "2026-01-21 13:54:46.978792", "@customReaction": { "type": "String", "placeholders": {} @@ -10888,5 +10888,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "guth Pangea Bot", + "@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 3ebc2943d..23f338731 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 13:53:31.619299", "about": "Acerca de", "@about": { "type": "String", @@ -10887,5 +10887,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do bot Pangea", + "@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 7ad96af48..af9e0ad59 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 13:53:47.592162", "about": "אודות", "@about": { "type": "String", @@ -11939,5 +11939,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "קול של פנגיאה בוט", + "@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 a465a07b2..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-20 12:32:05.240015", + "@@last_modified": "2026-01-21 13:54:40.850433", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11975,5 +11975,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "पैंगिया बॉट की आवाज़", + "@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 64f6cccf8..6b8517e96 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 13:53:46.524141", "about": "Informacije", "@about": { "type": "String", @@ -11262,5 +11262,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot glas", + "@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 8d543fa7e..b0767225a 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 13:53:36.742109", "about": "Névjegy", "@about": { "type": "String", @@ -10891,5 +10891,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot hang", + "@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 4fda86d9e..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-20 12:30:35.012898", + "@@last_modified": "2026-01-21 13:53:49.028067", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11968,5 +11968,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@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 50917db2e..5db3bb912 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 13:53:37.899898", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10881,5 +10881,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Suara Pangea Bot", + "@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 17b5245cf..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-20 12:30:26.700297", + "@@last_modified": "2026-01-21 13:53:45.416677", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11864,5 +11864,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot guth", + "@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 5c8d17791..312c08f22 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 13:54:01.568848", "about": "Informazioni", "@about": { "type": "String", @@ -10893,5 +10893,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voce del bot Pangea", + "@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 30d520729..c511c0107 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 13:54:39.359536", "about": "このアプリについて", "@about": { "type": "String", @@ -11680,5 +11680,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "パンゲアボットの声", + "@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 8026eff56..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-20 12:32:12.611848", + "@@last_modified": "2026-01-21 13:54:44.486907", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11920,5 +11920,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "პანჯეა ბოტის ხმა", + "@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 f711d1304..655637228 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 13:53:28.417406", "about": "소개", "@about": { "type": "String", @@ -10998,5 +10998,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "판게아 봇 음성", + "@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 888944382..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-20 12:31:36.164653", + "@@last_modified": "2026-01-21 13:54:22.729769", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11695,5 +11695,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balsas", + "@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 79a2fd834..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-20 12:31:22.453166", + "@@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", @@ -10876,5 +10876,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balss", + "@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 8d8592bdb..f87cddbc8 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 13:54:06.232198", "about": "Om", "@about": { "type": "String", @@ -11983,5 +11983,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot-stemme", + "@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 755841dc9..ed586d553 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 13:54:27.649909", "about": "Over ons", "@about": { "type": "String", @@ -10890,5 +10890,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stem", + "@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 48ebf5396..d7d2c73a5 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 13:54:34.451816", "about": "O aplikacji", "@about": { "type": "String", @@ -10888,5 +10888,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Głos bota Pangea", + "@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 1ac41d131..d9b54e65a 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 13:53:54.148542", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11990,5 +11990,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@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 da1140756..11797eb83 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 13:53:51.587209", "about": "Sobre", "@about": { "type": "String", @@ -11248,5 +11248,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@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 c0a85355c..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-20 12:31:13.609297", + "@@last_modified": "2026-01-21 13:54:11.801274", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11919,5 +11919,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@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 5154c946d..3f458ad96 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 13:53:41.456181", "about": "Despre", "@about": { "type": "String", @@ -11625,5 +11625,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Vocea Pangea Bot", + "@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 ca196e82b..976018aac 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 13:54:43.229000", "about": "О проекте", "@about": { "type": "String", @@ -10995,5 +10995,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос бота Pangea", + "@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 ceb204959..510156730 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 13:53:43.706457", "about": "O aplikácii", "@about": { "type": "String", @@ -11974,5 +11974,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@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 9e4a1ff56..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-20 12:30:51.399294", + "@@last_modified": "2026-01-21 13:53:58.241980", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11971,5 +11971,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@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 fe98f2112..0b970dceb 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 13:54:45.561732", "about": "О програму", "@about": { "type": "String", @@ -11992,5 +11992,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@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 1d9224f70..302e97dde 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 13:54:35.931933", "about": "Om", "@about": { "type": "String", @@ -11368,5 +11368,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot röst", + "@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 42b73b4d5..82050d0e3 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 13:54:25.999605", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11114,5 +11114,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "பாஙேஆ பாட்டின் குரல்", + "@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 3043221dc..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-20 12:31:32.903548", + "@@last_modified": "2026-01-21 13:54:20.897642", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11979,5 +11979,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "పాంజియా బాట్ శబ్దం", + "@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 ceb9ff50a..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-20 12:31:11.891533", + "@@last_modified": "2026-01-21 13:54:10.758435", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "เสียงของ Pangea Bot", + "@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 8d86e681c..f38520405 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 13:54:19.467039", "about": "Hakkında", "@about": { "type": "String", @@ -11112,5 +11112,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot sesi", + "@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 b8330d92c..5dbfc2938 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 13:54:03.403456", "about": "Про застосунок", "@about": { "type": "String", @@ -10884,5 +10884,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос Pangea Bot", + "@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 69d334c03..89760d972 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 13:54:24.252269", "about": "Giới thiệu", "@about": { "type": "String", @@ -6460,5 +6460,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Giọng nói của Pangea Bot", + "@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 69867a6fc..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-20 12:30:53.854617", + "@@last_modified": "2026-01-21 13:53:59.885092", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11981,5 +11981,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 聲音", + "@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 0953fb23e..ffe826b05 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 13:54:29.681537", "about": "关于", "@about": { "type": "String", @@ -10881,5 +10881,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "潘吉亚机器人声音", + "@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 26f055262..fc92bb5dc 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 13:54:13.165623", "about": "關於", "@about": { "type": "String", @@ -10888,5 +10888,15 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 語音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} + }, + "knockDesc": "您的請求已發送給課程管理員!如果他們批准,您將被允許進入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 12eb0bb22..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(); @@ -2009,6 +2011,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 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/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/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/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(() {}); + }, + ); + } +} 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; diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index d2300633b..1b8b2c0e2 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -67,7 +67,7 @@ class SessionLoader extends AsyncLoader { } class AnalyticsPractice extends StatefulWidget { - static bool bypassExitConfirmation = false; + static bool bypassExitConfirmation = true; final ConstructTypeEnum type; const AnalyticsPractice({ @@ -201,18 +201,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; selectedMorphChoice.value = null; - } - - void _resetSessionState() { + enableChoicesNotifier.value = true; progressNotifier.value = 0.0; _queue.clear(); _choiceTexts.clear(); _choiceEmojis.clear(); activityState.value = const AsyncState.idle(); + + AnalyticsPractice.bypassExitConfirmation = true; } void updateElapsedTime(int seconds) { @@ -239,8 +239,7 @@ class AnalyticsPracticeState extends State Future _onLanguageUpdate() async { try { - _resetActivityState(); - _resetSessionState(); + _clearState(); await _analyticsService .updateDispatcher.constructUpdateStream.stream.first .timeout(const Duration(seconds: 10)); @@ -257,14 +256,14 @@ class AnalyticsPracticeState extends State Future _startSession() async { await _waitForAnalytics(); await _sessionLoader.load(); + if (_sessionLoader.isError) return; + progressNotifier.value = _sessionLoader.value!.progress; await _continueSession(); } Future reloadSession() async { - _resetActivityState(); - _resetSessionState(); - + _clearState(); _sessionLoader.reset(); await _startSession(); } @@ -324,8 +323,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; @@ -349,7 +350,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; diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index e7d908391..15cefb1a9 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'; @@ -15,10 +16,17 @@ 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, ) async { + if (MatrixState.pangeaController.subscriptionController.isSubscribed == + false) { + throw UnsubscribedException(); + } + final r = Random(); final activityTypes = ActivityTypeEnum.analyticsPracticeTypes(type); @@ -67,6 +75,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 ba8dddb82..44275cf4d 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -18,6 +18,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'; import 'package:flutter/material.dart'; @@ -84,7 +85,9 @@ class AnalyticsPracticeView extends StatelessWidget { builder: (context, state, __) { return switch (state) { AsyncError(:final error) => - ErrorIndicator(message: error.toString()), + ErrorIndicator( + message: error.toLocalizedString(context), + ), AsyncLoaded(:final value) => value.isComplete ? CompletedActivitySessionView(state.value, controller) 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, ), ], ); 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/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/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index b1339a3e4..e90d6d9a9 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; } @@ -112,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 @@ -122,9 +128,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 +146,6 @@ class PangeaController { ); } - AppConfig.fontSizeFactor = 1.0; await Future.wait(futures); } @@ -174,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/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/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; } 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( diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 22cf8ca1d..de462ed95 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -294,16 +294,18 @@ 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, 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 +315,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 +372,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 +428,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/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/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( 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, }; } 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/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..d285fc1ce 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; @@ -91,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 = @@ -105,7 +102,6 @@ class SelectModeController with LemmaEmojiSetter { _translationLoader.dispose(); _sttTranslationLoader.dispose(); _audioLoader.dispose(); - contentChangedStream.close(); } static List get _textModes => [ 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/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/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; 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; 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/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; 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)