5259 bot settings language settings (#5305)

* feat: add voice to user model

* update bot settings on language / learning settings update

* use room summary to determine member count

* translations
This commit is contained in:
ggurdin 2026-01-21 10:44:21 -05:00 committed by GitHub
parent 8a2f4747c9
commit 529f12e028
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 625 additions and 618 deletions

View file

@ -1,6 +1,6 @@
{
"@@locale": "ar",
"@@last_modified": "2026-01-20 12:31:24.671375",
"@@last_modified": "2026-01-21 10:42:52.513008",
"about": "حول",
"@about": {
"type": "String",
@ -11107,5 +11107,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "صوت بوت بانجيا",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1911,7 +1911,7 @@
"playWithAI": "Пакуль гуляйце з ШІ",
"courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!",
"@@locale": "be",
"@@last_modified": "2026-01-20 12:31:06.570011",
"@@last_modified": "2026-01-21 10:42:45.087111",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11989,5 +11989,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Голас Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:49.464406",
"@@last_modified": "2026-01-21 10:43:01.204800",
"about": "সম্পর্কে",
"@about": {
"type": "String",
@ -11994,5 +11994,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "প্যাঙ্গিয়া বটের কণ্ঠ",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4279,7 +4279,7 @@
"joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།",
"startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།",
"@@locale": "bo",
"@@last_modified": "2026-01-20 12:31:44.969872",
"@@last_modified": "2026-01-21 10:42:59.058441",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -10644,5 +10644,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot voz",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:09.126351",
"@@last_modified": "2026-01-21 10:42:46.152113",
"about": "Quant a",
"@about": {
"type": "String",
@ -10914,5 +10914,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Veu del bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "cs",
"@@last_modified": "2026-01-20 12:31:00.323945",
"@@last_modified": "2026-01-21 10:42:42.984636",
"about": "O aplikaci",
"@about": {
"type": "String",
@ -11497,5 +11497,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Hlas Pangea Bota",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1930,7 +1930,7 @@
"playWithAI": "Leg med AI for nu",
"courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!",
"@@locale": "da",
"@@last_modified": "2026-01-20 12:30:10.761209",
"@@last_modified": "2026-01-21 10:42:22.819549",
"@aboutHomeserver": {
"type": "String",
"placeholders": {
@ -11951,5 +11951,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot stemme",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "de",
"@@last_modified": "2026-01-20 12:30:47.434524",
"@@last_modified": "2026-01-21 10:42:37.466783",
"alwaysUse24HourFormat": "true",
"@alwaysUse24HourFormat": {
"description": "Set to true to always display time of day in 24 hour format."
@ -10897,5 +10897,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot Stimme",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4456,7 +4456,7 @@
"playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν",
"courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!",
"@@locale": "el",
"@@last_modified": "2026-01-20 12:31:59.503296",
"@@last_modified": "2026-01-21 10:43:05.204211",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11948,5 +11948,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Φωνή Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -5056,5 +5056,6 @@
"constructUseIncGEDesc": "Incorrect grammar error practice",
"fillInBlank": "Fill in the blank with the correct choice",
"learn": "Learn",
"languageUpdated": "Target language updated!"
"languageUpdated": "Target language updated!",
"voiceDropdownTitle": "Pangea Bot voice"
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:32:07.490560",
"@@last_modified": "2026-01-21 10:43:08.060333",
"about": "Prio",
"@about": {
"type": "String",
@ -11979,5 +11979,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voĉo de Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "es",
"@@last_modified": "2026-01-20 12:29:59.898184",
"@@last_modified": "2026-01-21 10:42:19.343875",
"about": "Acerca de",
"@about": {
"type": "String",
@ -8124,5 +8124,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voz del bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "et",
"@@last_modified": "2026-01-20 12:30:45.162738",
"@@last_modified": "2026-01-21 10:42:36.742735",
"about": "Rakenduse teave",
"@about": {
"type": "String",
@ -11161,5 +11161,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Boti hääl",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "eu",
"@@last_modified": "2026-01-20 12:30:40.144354",
"@@last_modified": "2026-01-21 10:42:34.277635",
"about": "Honi buruz",
"@about": {
"type": "String",
@ -10890,5 +10890,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot ahotsa",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:52.231221",
"@@last_modified": "2026-01-21 10:43:02.232868",
"repeatPassword": "تکرار رمزعبور",
"@repeatPassword": {},
"about": "درباره",
@ -11622,5 +11622,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "صدای ربات پانژیا",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4009,7 +4009,7 @@
"playWithAI": "Leiki tekoälyn kanssa nyt",
"courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!",
"@@locale": "fi",
"@@last_modified": "2026-01-20 12:30:08.099637",
"@@last_modified": "2026-01-21 10:42:21.857816",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11513,5 +11513,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Botin ääni",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -2787,7 +2787,7 @@
"selectAll": "Piliin lahat",
"deselectAll": "Huwag piliin lahat",
"@@locale": "fil",
"@@last_modified": "2026-01-20 12:31:19.884620",
"@@last_modified": "2026-01-21 10:42:50.349708",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@ -11866,5 +11866,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Boses ng Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "fr",
"@@last_modified": "2026-01-20 12:32:20.398562",
"@@last_modified": "2026-01-21 10:43:12.775361",
"about": "À propos",
"@about": {
"type": "String",
@ -11214,5 +11214,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voix du bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4517,7 +4517,7 @@
"playWithAI": "Imir le AI faoi láthair",
"courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!",
"@@locale": "ga",
"@@last_modified": "2026-01-20 12:32:18.033824",
"@@last_modified": "2026-01-21 10:43:11.760663",
"@customReaction": {
"type": "String",
"placeholders": {}
@ -10888,5 +10888,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "guth Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "gl",
"@@last_modified": "2026-01-20 12:30:05.234280",
"@@last_modified": "2026-01-21 10:42:20.142432",
"about": "Acerca de",
"@about": {
"type": "String",
@ -10887,5 +10887,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voz do bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:31.884050",
"@@last_modified": "2026-01-21 10:42:30.598205",
"about": "אודות",
"@about": {
"type": "String",
@ -11939,5 +11939,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "קול של פנגיאה בוט",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4483,7 +4483,7 @@
"playWithAI": "अभी के लिए एआई के साथ खेलें",
"courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!",
"@@locale": "hi",
"@@last_modified": "2026-01-20 12:32:05.240015",
"@@last_modified": "2026-01-21 10:43:07.054066",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11975,5 +11975,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "पैंगिया बॉट की आवाज़",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "hr",
"@@last_modified": "2026-01-20 12:30:29.823787",
"@@last_modified": "2026-01-21 10:42:29.581714",
"about": "Informacije",
"@about": {
"type": "String",
@ -11262,5 +11262,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot glas",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "hu",
"@@last_modified": "2026-01-20 12:30:13.762355",
"@@last_modified": "2026-01-21 10:42:24.298395",
"about": "Névjegy",
"@about": {
"type": "String",
@ -10891,5 +10891,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot hang",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1958,7 +1958,7 @@
"playWithAI": "Joca con le IA pro ora",
"courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!",
"@@locale": "ia",
"@@last_modified": "2026-01-20 12:30:35.012898",
"@@last_modified": "2026-01-21 10:42:31.403920",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11968,5 +11968,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voix du bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:15.788809",
"@@last_modified": "2026-01-21 10:42:25.871973",
"setAsCanonicalAlias": "Atur sebagai alias utama",
"@setAsCanonicalAlias": {
"type": "String",
@ -10881,5 +10881,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Suara Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4372,7 +4372,7 @@
"playWithAI": "Joca con AI pro ora",
"courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!",
"@@locale": "ie",
"@@last_modified": "2026-01-20 12:30:26.700297",
"@@last_modified": "2026-01-21 10:42:28.431424",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11864,5 +11864,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot guth",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:55.791406",
"@@last_modified": "2026-01-21 10:42:40.690823",
"about": "Informazioni",
"@about": {
"type": "String",
@ -10893,5 +10893,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voce del bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "ja",
"@@last_modified": "2026-01-20 12:32:02.883097",
"@@last_modified": "2026-01-21 10:43:06.128364",
"about": "このアプリについて",
"@about": {
"type": "String",
@ -11680,5 +11680,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "パンゲアボットの声",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -2594,7 +2594,7 @@
"playWithAI": "ამ დროისთვის ითამაშეთ AI-თან",
"courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!",
"@@locale": "ka",
"@@last_modified": "2026-01-20 12:32:12.611848",
"@@last_modified": "2026-01-21 10:43:09.701292",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11920,5 +11920,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "პანჯეა ბოტის ხმა",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:29:56.840054",
"@@last_modified": "2026-01-21 10:42:17.603097",
"about": "소개",
"@about": {
"type": "String",
@ -10998,5 +10998,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "판게아 봇 음성",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -3861,7 +3861,7 @@
"playWithAI": "Žaiskite su dirbtiniu intelektu dabar",
"courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!",
"@@locale": "lt",
"@@last_modified": "2026-01-20 12:31:36.164653",
"@@last_modified": "2026-01-21 10:42:55.447627",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11695,5 +11695,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot balsas",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4482,7 +4482,7 @@
"playWithAI": "Tagad spēlējiet ar AI",
"courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!",
"@@locale": "lv",
"@@last_modified": "2026-01-20 12:31:22.453166",
"@@last_modified": "2026-01-21 10:42:51.147364",
"analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti",
"analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.",
"accessRequestedTitle": "Pieprasījums piekļūt analītikai",
@ -10876,5 +10876,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot balss",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:02.821968",
"@@last_modified": "2026-01-21 10:42:43.891386",
"about": "Om",
"@about": {
"type": "String",
@ -11983,5 +11983,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot-stemme",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:42.507524",
"@@last_modified": "2026-01-21 10:42:58.326124",
"about": "Over ons",
"@about": {
"type": "String",
@ -10890,5 +10890,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot stem",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "pl",
"@@last_modified": "2026-01-20 12:31:54.796841",
"@@last_modified": "2026-01-21 10:43:03.257336",
"about": "O aplikacji",
"@about": {
"type": "String",
@ -10888,5 +10888,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Głos bota Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:42.601068",
"@@last_modified": "2026-01-21 10:42:35.392050",
"copiedToClipboard": "Copiada para a área de transferência",
"@copiedToClipboard": {
"type": "String",
@ -11990,5 +11990,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voz do Bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:37.380939",
"@@last_modified": "2026-01-21 10:42:32.545549",
"about": "Sobre",
"@about": {
"type": "String",
@ -11248,5 +11248,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voz do Bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -3331,7 +3331,7 @@
"selectAll": "Selecionar tudo",
"deselectAll": "Desmarcar tudo",
"@@locale": "pt_PT",
"@@last_modified": "2026-01-20 12:31:13.609297",
"@@last_modified": "2026-01-21 10:42:48.198355",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11919,5 +11919,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Voz do Bot Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:30:21.408747",
"@@last_modified": "2026-01-21 10:42:26.609980",
"about": "Despre",
"@about": {
"type": "String",
@ -11625,5 +11625,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Vocea Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "ru",
"@@last_modified": "2026-01-20 12:32:09.850624",
"@@last_modified": "2026-01-21 10:43:08.936913",
"about": "О проекте",
"@about": {
"type": "String",
@ -10995,5 +10995,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Голос бота Pangea",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "sk",
"@@last_modified": "2026-01-20 12:30:24.498564",
"@@last_modified": "2026-01-21 10:42:27.675920",
"about": "O aplikácii",
"@about": {
"type": "String",
@ -11974,5 +11974,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Hlas Pangea Bota",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -2464,7 +2464,7 @@
"playWithAI": "Za zdaj igrajte z AI-jem",
"courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!",
"@@locale": "sl",
"@@last_modified": "2026-01-20 12:30:51.399294",
"@@last_modified": "2026-01-21 10:42:38.801732",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11971,5 +11971,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Glas Pangea Bota",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:32:14.799697",
"@@last_modified": "2026-01-21 10:43:10.733270",
"about": "О програму",
"@about": {
"type": "String",
@ -11992,5 +11992,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Glas Pangea Bota",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:57.066428",
"@@last_modified": "2026-01-21 10:43:04.367889",
"about": "Om",
"@about": {
"type": "String",
@ -11368,5 +11368,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot röst",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:40.562260",
"@@last_modified": "2026-01-21 10:42:57.399546",
"acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது",
"@acceptedTheInvitation": {
"type": "String",
@ -11114,5 +11114,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "பாஙேஆ பாட்டின் குரல்",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1920,7 +1920,7 @@
"playWithAI": "ఇప్పుడే AI తో ఆడండి",
"courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!",
"@@locale": "te",
"@@last_modified": "2026-01-20 12:31:32.903548",
"@@last_modified": "2026-01-21 10:42:54.652537",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@ -11979,5 +11979,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "పాంజియా బాట్ శబ్దం",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -4456,7 +4456,7 @@
"playWithAI": "เล่นกับ AI ชั่วคราว",
"courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!",
"@@locale": "th",
"@@last_modified": "2026-01-20 12:31:11.891533",
"@@last_modified": "2026-01-21 10:42:47.175182",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11948,5 +11948,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "เสียงของ Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "tr",
"@@last_modified": "2026-01-20 12:31:28.469826",
"@@last_modified": "2026-01-21 10:42:53.424381",
"about": "Hakkında",
"@about": {
"type": "String",
@ -11112,5 +11112,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot sesi",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "uk",
"@@last_modified": "2026-01-20 12:30:57.869011",
"@@last_modified": "2026-01-21 10:42:41.558170",
"about": "Про застосунок",
"@about": {
"type": "String",
@ -10884,5 +10884,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Голос Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:38.101874",
"@@last_modified": "2026-01-21 10:42:56.498587",
"about": "Giới thiệu",
"@about": {
"type": "String",
@ -6460,5 +6460,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Giọng nói của Pangea Bot",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1856,7 +1856,7 @@
"selectAll": "全選",
"deselectAll": "取消全選",
"@@locale": "yue",
"@@last_modified": "2026-01-20 12:30:53.854617",
"@@last_modified": "2026-01-21 10:42:39.595731",
"@ignoreUser": {
"type": "String",
"placeholders": {}
@ -11981,5 +11981,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot 聲音",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "zh",
"@@last_modified": "2026-01-20 12:31:47.017242",
"@@last_modified": "2026-01-21 10:43:00.180549",
"about": "关于",
"@about": {
"type": "String",
@ -10881,5 +10881,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "潘吉亚机器人声音",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-20 12:31:16.140892",
"@@last_modified": "2026-01-21 10:42:49.437032",
"about": "關於",
"@about": {
"type": "String",
@ -10888,5 +10888,10 @@
"@languageUpdated": {
"type": "String",
"placeholders": {}
},
"voiceDropdownTitle": "Pangea Bot 語音",
"@voiceDropdownTitle": {
"type": "String",
"placeholders": {}
}
}

View file

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

View file

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

View file

@ -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<BotChatSettingsDialog> {
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<void> _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<void> _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<void> _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<String>(
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 ?? <String>[]).map((voice) {
return DropdownMenuItem(
value: voice,
child: Text(voice),
);
}).toList(),
onChanged: _setVoice,
value: _selectedVoice,
),
const SizedBox(),
],
),
);
}
}

View file

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

View file

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

View file

@ -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<Room> 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<String> startChatWithBot() => startDirectChat(
BotName.byEnvironment,
@ -45,27 +41,31 @@ extension BotClientExtension on Client {
],
);
Future<void> updateBotOptions() async {
if (!isLogged() || botDM == null) return;
Future<void> 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 = <Future>[];
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);
}
}

View file

@ -117,8 +117,9 @@ class PangeaController {
userController.languageStream.stream.listen(_onLanguageUpdate);
_settingsSubscription?.cancel();
_settingsSubscription = userController.settingsUpdateStream.stream
.listen((_) => matrixState.client.updateBotOptions());
_settingsSubscription = userController.settingsUpdateStream.stream.listen(
(update) => matrixState.client.updateBotOptions(update.userSettings),
);
_joinSpaceSubscription?.cancel();
_joinSpaceSubscription ??= matrixState.client.onSync.stream
@ -176,7 +177,7 @@ class PangeaController {
}
_clearCache(exclude: exclude);
matrixState.client.updateBotOptions();
matrixState.client.updateBotOptions(userController.profile.userSettings);
}
static final List<String> _storageKeys = [

View file

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

View file

@ -41,7 +41,6 @@ class SettingsLearningController extends State<SettingsLearning> {
PangeaController pangeaController = MatrixState.pangeaController;
late Profile _profile;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? languageMatchError;
final ScrollController scrollController = ScrollController();
@ -110,18 +109,16 @@ class SettingsLearningController extends State<SettingsLearning> {
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<void> resetInstructionTooltips() async {
@ -153,11 +150,12 @@ class SettingsLearningController extends State<SettingsLearning> {
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<SettingsLearning> {
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<SettingsLearning> {
LanguageLevelTypeEnum get cefrLevel => _profile.userSettings.cefrLevel;
String? get selectedVoice => _profile.userSettings.voice;
Country? get country =>
CountryService().findByName(_profile.userSettings.country);

View file

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

View file

@ -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<String>(
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 ?? <String>[]).map((voice) {
return DropdownMenuItem(
value: voice,
child: Text(voice),
);
}).toList(),
onChanged: onChanged,
value: value,
);
}
}

View file

@ -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<MessageAudioCard> {
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);

View file

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

View file

@ -431,6 +431,8 @@ class UserController {
: langModel;
}
String? get voice => profile.userSettings.voice;
bool get languagesSet =>
userL1Code != null &&
userL2Code != null &&

View file

@ -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<String, dynamic> json) => UserSettings(
@ -52,6 +54,7 @@ class UserSettings {
json[ModelKey.cefrLevel],
)
: LanguageLevelTypeEnum.a1,
voice: json[ModelKey.voice],
);
Map<String, dynamic> 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,
]);
}

View file

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

View file

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