resolve merge conflcits

This commit is contained in:
ggurdin 2025-11-05 14:29:31 -05:00
commit f880115ac4
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
71 changed files with 486 additions and 479 deletions

View file

@ -4836,7 +4836,7 @@
"loginToAccount": "تسجيل الدخول إلى حسابي",
"appDescription": "تعلم لغة\nأثناء مراسلة أصدقائك.",
"languages": "اللغات",
"chooseLanguage": "اختر لغة.",
"chooseLanguage": "اختر لغة الهدف.",
"planTrip": "خطط لرحلتك",
"howAreYouTraveling": "كيف تسافر؟",
"unlockPrivateTrip": "افتح رحلة خاصة",

View file

@ -2353,7 +2353,7 @@
"loginToAccount": "Увайсці ў мой рахунак",
"appDescription": "Вучыце мову\nпраз адпраўку тэкставых паведамленняў сябрам.",
"languages": "Мовы",
"chooseLanguage": "Выберыце мову.",
"chooseLanguage": "Выберыце мову мэты.",
"planTrip": "Плануйце сваю паездку",
"howAreYouTraveling": "Як вы падарожнічаеце?",
"unlockPrivateTrip": "Разблакаваць прыватную паездку",

View file

@ -4944,7 +4944,7 @@
"loginToAccount": "আমার অ্যাকাউন্টে লগইন করুন",
"appDescription": "একটি ভাষা শিখুন\nআপনার বন্ধুদের সাথে মেসেজিং করার সময়।",
"languages": "ভাষাসমূহ",
"chooseLanguage": "একটি ভাষা নির্বাচন করুন।",
"chooseLanguage": "একটি লক্ষ্য ভাষা নির্বাচন করুন।",
"planTrip": "আপনার যাত্রা পরিকল্পনা করুন",
"howAreYouTraveling": "আপনি কিভাবে ভ্রমণ করছেন?",
"unlockPrivateTrip": "একটি ব্যক্তিগত যাত্রা আনলক করুন",

View file

@ -4700,7 +4700,7 @@
"loginToAccount": "ངའི་ཁུལ་གཅིག་ལ་སྤྱོད་ལམ་སྤྱོད་འབད།",
"appDescription": "ཚོགས་ཚོགས་སྐད་ཡིག་སློབ་ཚན་ལས་སྤྱོད་སྒོམ།\nཁྱེད་ཀྱི་གྲོགས་མཚན་ལ་རྟག་པ་སྤྱོད་སྒོམ་ལས་སྤྱོད་སྒོམ།",
"languages": "ཚོད་ལྟ",
"chooseLanguage": "ཚོད་ལྟ་གཅིག་འཇུག་གི་སྐད་ཡིག་གཟིགས།",
"chooseLanguage": "Pilih bahasa target.",
"planTrip": "འགྱོ་རོགས་ལྟར་བཟོ།",
"howAreYouTraveling": "ཁྱེད་ཀྱི་འགྱོ་རོགས་ག་རེ་ཡོད།",
"unlockPrivateTrip": "རང་གི་སྤྱོད་ལམ་ལ་ལོག་འབད།",

View file

@ -4923,7 +4923,7 @@
"loginToAccount": "Inicia sessió al meu compte",
"appDescription": "Aprèn un idioma\nmentre xatejes amb els teus amics.",
"languages": "Idiomes",
"chooseLanguage": "Tria un idioma.",
"chooseLanguage": "Trieu un idioma de destinació.",
"planTrip": "Planifica el teu viatge",
"howAreYouTraveling": "Com viatges?",
"unlockPrivateTrip": "Desbloqueja un viatge privat",

View file

@ -4480,7 +4480,7 @@
"loginToAccount": "Přihlásit se ke svému účtu",
"appDescription": "Učte se jazyk\npři psaní zpráv přátelům.",
"languages": "Jazyky",
"chooseLanguage": "Vyberte jazyk.",
"chooseLanguage": "Vyberte cílový jazyk.",
"planTrip": "Naplánujte si výlet",
"howAreYouTraveling": "Jak cestujete?",
"unlockPrivateTrip": "Odemknout soukromý výlet",

View file

@ -2372,7 +2372,7 @@
"loginToAccount": "Log ind på min konto",
"appDescription": "Lær et sprog\nmens du sender beskeder til dine venner.",
"languages": "Sprog",
"chooseLanguage": "Vælg et sprog.",
"chooseLanguage": "Vælg et målsprog.",
"planTrip": "Planlæg din rejse",
"howAreYouTraveling": "Hvordan rejser du?",
"unlockPrivateTrip": "Lås op for en privat rejse",

View file

@ -4958,7 +4958,7 @@
"loginToAccount": "In mein Konto einloggen",
"appDescription": "Lerne eine Sprache\nwährend du deinen Freunden schreibst.",
"languages": "Sprachen",
"chooseLanguage": "Wähle eine Sprache.",
"chooseLanguage": "Wählen Sie eine Zielsprache.",
"planTrip": "Plane deine Reise",
"howAreYouTraveling": "Wie reist du?",
"unlockPrivateTrip": "Einen privaten Trip freischalten",

View file

@ -4898,7 +4898,7 @@
"loginToAccount": "Συνδεθείτε στον λογαριασμό μου",
"appDescription": "Μάθε μια γλώσσα\nενώ στέλνεις μηνύματα στους φίλους σου.",
"languages": "Γλώσσες",
"chooseLanguage": "Επιλέξτε μια γλώσσα.",
"chooseLanguage": "Επιλέξτε μια γλώσσα στόχου.",
"planTrip": "Προγραμματίστε το ταξίδι σας",
"howAreYouTraveling": "Πώς ταξιδεύετε;",
"unlockPrivateTrip": "Ξεκλειδώστε ένα ιδιωτικό ταξίδι",

View file

@ -5320,5 +5320,6 @@
"feedbackDialogDesc": "I make mistakes too! Anything to help me improve?",
"getStartedFriendsButton": "Invite a friend",
"contactHasBeenInvitedToTheCourse": "Contact has been invited to the course",
"inviteFriends": "Invite friends"
"inviteFriends": "Invite friends",
"activityStatsButtonTooltip": "Activity info"
}

View file

@ -4077,7 +4077,7 @@
"loginToAccount": "Ensaluti al mia konto",
"appDescription": "Lernu lingvon\ndum tekstdonado al viaj amikojn.",
"languages": "Lingvoj",
"chooseLanguage": "Elektu lingvon.",
"chooseLanguage": "Elektu celan lingvon.",
"planTrip": "Planadu vian vojaĝon",
"howAreYouTraveling": "Kiel vi vojaĝas?",
"unlockPrivateTrip": "Malŝlosu privatecan vojaĝon",

View file

@ -6460,7 +6460,7 @@
"loginToAccount": "Iniciar sesión en mi cuenta",
"appDescription": "Aprende un idioma\nmientras envías mensajes a tus amigos.",
"languages": "Idiomas",
"chooseLanguage": "Elige un idioma.",
"chooseLanguage": "Elige un idioma de destino.",
"planTrip": "Planifica tu viaje",
"howAreYouTraveling": "¿Cómo viajas?",
"unlockPrivateTrip": "Desbloquear un viaje privado",

View file

@ -11187,7 +11187,7 @@
"loginToAccount": "Logi sisse oma kontole",
"appDescription": "Õpi keelt\nsõpradega sõnumite saatmise ajal.",
"languages": "Keeled",
"chooseLanguage": "Vali keel.",
"chooseLanguage": "Vali sihtkeel.",
"planTrip": "Planeeri oma reis",
"howAreYouTraveling": "Kuidas sa reisid?",
"unlockPrivateTrip": "Ava privaatne reis",

View file

@ -4951,7 +4951,7 @@
"loginToAccount": "Hasi saioa nire kontuan",
"appDescription": "Ikasi hizkuntza\nzure lagunekin mezularitza egiten bitartean.",
"languages": "Hizkuntzak",
"chooseLanguage": "Hizkuntza aukeratu.",
"chooseLanguage": "Hautatu helburu hizkuntza bat.",
"planTrip": "Planifikatu zure bidaiaren",
"howAreYouTraveling": "Nola bidaiatzen ari zara?",
"unlockPrivateTrip": "Galdetu bidaia pribatua",

View file

@ -4371,7 +4371,7 @@
"loginToAccount": "وارد حساب کاربری من شوید",
"appDescription": "یادگیری زبان\nدر حین پیام‌رسانی به دوستانتان.",
"languages": "زبان‌ها",
"chooseLanguage": "یک زبان را انتخاب کنید.",
"chooseLanguage": "زبان هدف را انتخاب کنید.",
"planTrip": "برنامه‌ریزی سفر خود",
"howAreYouTraveling": "چگونه سفر می‌کنید؟",
"unlockPrivateTrip": "باز کردن سفر خصوصی",

View file

@ -4473,7 +4473,7 @@
"loginToAccount": "Kirjaudu tililleni",
"appDescription": "Opiskele kieltä\nlähettämällä viestejä ystävillesi.",
"languages": "Kielet",
"chooseLanguage": "Valitse kieli.",
"chooseLanguage": "Valitse kohdekieli.",
"planTrip": "Suunnittele matkasi",
"howAreYouTraveling": "Miten matkustat?",
"unlockPrivateTrip": "Avaa yksityinen matka",

View file

@ -3218,7 +3218,7 @@
"loginToAccount": "Mag-login sa aking account",
"appDescription": "Matuto ng isang wika\nhabang nagte-text sa iyong mga kaibigan.",
"languages": "Mga Wika",
"chooseLanguage": "Pumili ng isang wika.",
"chooseLanguage": "Pumili ng target na wika.",
"planTrip": "Planuhin ang iyong biyahe",
"howAreYouTraveling": "Kamusta ang iyong paglalakbay?",
"unlockPrivateTrip": "I-unlock ang pribadong biyahe",

View file

@ -4781,7 +4781,7 @@
"loginToAccount": "Se connecter à mon compte",
"appDescription": "Apprenez une langue\ntout en envoyant des messages à vos amis.",
"languages": "Langues",
"chooseLanguage": "Choisissez une langue.",
"chooseLanguage": "Choisissez une langue cible.",
"planTrip": "Planifiez votre voyage",
"howAreYouTraveling": "Comment voyagez-vous ?",
"unlockPrivateTrip": "Déverrouiller un voyage privé",

View file

@ -4959,7 +4959,7 @@
"loginToAccount": "Logáil isteach i mo chuntas",
"appDescription": "Foghlaim teanga\nag an am céanna le do chairde.",
"languages": "Teangacha",
"chooseLanguage": "Roghnaigh teanga.",
"chooseLanguage": "Roghnaigh teanga sprioc.",
"planTrip": "Pleanáil do thuras",
"howAreYouTraveling": "Conas atá tú ag taisteal?",
"unlockPrivateTrip": "Díghlasáil turas príobháideach",

View file

@ -4952,7 +4952,7 @@
"loginToAccount": "Inicia sesión na miña conta",
"appDescription": "Aprende un idioma mentres envías mensaxes aos teus amigos.",
"languages": "Idiomas",
"chooseLanguage": "Elixe un idioma.",
"chooseLanguage": "Elixe unha lingua de destino.",
"planTrip": "Planifica a túa viaxe",
"howAreYouTraveling": "Como estás viaxando?",
"unlockPrivateTrip": "Desbloquea unha viaxe privada",

View file

@ -3537,7 +3537,7 @@
"loginToAccount": "התחבר לחשבוני",
"appDescription": "למד שפה\nבזמן שליחת הודעות לחברים שלך.",
"languages": "שפות",
"chooseLanguage": "בחר שפה.",
"chooseLanguage": "בחר שפה יעד.",
"planTrip": "תכנן את הטיול שלך",
"howAreYouTraveling": "איך אתה מטייל?",
"unlockPrivateTrip": "פתח טיול פרטי",

View file

@ -4925,7 +4925,7 @@
"loginToAccount": "मेरे खाते में लॉगिन करें",
"appDescription": "एक भाषा सीखें\nजब आप अपने दोस्तों को मैसेज कर रहे हों।",
"languages": "भाषाएँ",
"chooseLanguage": "एक भाषा चुनें।",
"chooseLanguage": "लक्षित भाषा चुनें।",
"planTrip": "अपनी यात्रा की योजना बनाएं",
"howAreYouTraveling": "आप कैसे यात्रा कर रहे हैं?",
"unlockPrivateTrip": "एक निजी यात्रा अनलॉक करें",

View file

@ -4707,7 +4707,7 @@
"loginToAccount": "Prijavite se na svoj račun",
"appDescription": "Učite jezik\ndok šaljete poruke prijateljima.",
"languages": "Jezici",
"chooseLanguage": "Odaberite jezik.",
"chooseLanguage": "Odaberite ciljani jezik.",
"planTrip": "Isplanirajte svoje putovanje",
"howAreYouTraveling": "Kako putujete?",
"unlockPrivateTrip": "Otključajte privatno putovanje",

View file

@ -4952,7 +4952,7 @@
"loginToAccount": "Jelentkezzen be a fiókomba",
"appDescription": "Tanulj egy nyelvet\nüzenetküldés közben a barátaidnak.",
"languages": "Nyelvek",
"chooseLanguage": "Válassz egy nyelvet.",
"chooseLanguage": "Válasszon ki egy célnyelvet.",
"planTrip": "Tervezd meg az utadat",
"howAreYouTraveling": "Hogyan utazol?",
"unlockPrivateTrip": "Privát utazás feloldása",

View file

@ -2400,7 +2400,7 @@
"loginToAccount": "Intra in mi conto",
"appDescription": "Impara un idioma\ndum il texta a tu amicos.",
"languages": "Linguas",
"chooseLanguage": "Selige un lingua.",
"chooseLanguage": "Sèla un lingue de destin.",
"planTrip": "Planifica tu viage",
"howAreYouTraveling": "Como tu ha planificate viajar?",
"unlockPrivateTrip": "Desbloquea un viage private",

View file

@ -4953,7 +4953,7 @@
"loginToAccount": "Masuk ke akun saya",
"appDescription": "Pelajari bahasa\nsambil mengirim pesan kepada teman.",
"languages": "Bahasa",
"chooseLanguage": "Pilih bahasa.",
"chooseLanguage": "Pilih bahasa target.",
"planTrip": "Rencanakan perjalanan Anda",
"howAreYouTraveling": "Bagaimana Anda bepergian?",
"unlockPrivateTrip": "Buka perjalanan pribadi",

View file

@ -4814,7 +4814,7 @@
"loginToAccount": "Login a mi conto",
"appDescription": "Impara un linguage\ndurante scriver a tui amicos.",
"languages": "Linguages",
"chooseLanguage": "Selige un linguage.",
"chooseLanguage": "Rogha teanga sprioc.",
"planTrip": "Planifica tui viage",
"howAreYouTraveling": "Con que te viages?",
"unlockPrivateTrip": "Desblocca un viage privat",

View file

@ -4930,7 +4930,7 @@
"loginToAccount": "Accedi al mio account",
"appDescription": "Impara una lingua\nmentre scrivi ai tuoi amici.",
"languages": "Lingue",
"chooseLanguage": "Scegli una lingua.",
"chooseLanguage": "Scegli una lingua di destinazione.",
"planTrip": "Pianifica il tuo viaggio",
"howAreYouTraveling": "Come stai viaggiando?",
"unlockPrivateTrip": "Sblocca un viaggio privato",

View file

@ -4294,7 +4294,7 @@
"loginToAccount": "マイアカウントにログイン",
"appDescription": "友達とメッセージを送りながら言語を学びましょう。",
"languages": "言語",
"chooseLanguage": "言語を選択してください。",
"chooseLanguage": "ターゲット言語を選択してください。",
"planTrip": "旅行を計画する",
"howAreYouTraveling": "どのように旅行していますか?",
"unlockPrivateTrip": "プライベート旅行を解除",

View file

@ -3036,7 +3036,7 @@
"loginToAccount": "შესვლა ჩემს ანგარიშზე",
"appDescription": "სწავლა ენის\nმეგობრებთან მესენჯერში საუბრის დროს.",
"languages": "ენები",
"chooseLanguage": "აირჩიეთ ენა.",
"chooseLanguage": "აირჩიეთ მიზნობრივი ენა.",
"planTrip": "გეგმა თქვენი მოგზაურობის",
"howAreYouTraveling": "როგორ მოგზაურობთ?",
"unlockPrivateTrip": "გახსნათ პირადი მოგზაურობა",

View file

@ -4887,7 +4887,7 @@
"loginToAccount": "내 계정에 로그인",
"appDescription": "친구들과 문자 메시지를 보내며 언어를 배우세요.",
"languages": "언어",
"chooseLanguage": "언어를 선택하세요.",
"chooseLanguage": "대상 언어를 선택하세요.",
"planTrip": "여행 계획 세우기",
"howAreYouTraveling": "어떻게 여행하고 계세요?",
"unlockPrivateTrip": "개인 여행 잠금 해제",

View file

@ -4303,7 +4303,7 @@
"loginToAccount": "Prisijungti prie mano paskyros",
"appDescription": "Išmokite kalbą\nnaudodamiesi tekstais su draugais.",
"languages": "Kalbos",
"chooseLanguage": "Pasirinkite kalbą.",
"chooseLanguage": "Pasirinkite tikslinę kalbą.",
"planTrip": "Planuoti kelionę",
"howAreYouTraveling": "Kaip keliaujate?",
"unlockPrivateTrip": "Atrakinti privačią kelionę",

View file

@ -4919,7 +4919,7 @@
"loginToAccount": "Pierakstieties savā kontā",
"appDescription": "Mācieties valodu\nrakstot ziņas draugiem.",
"languages": "Valodas",
"chooseLanguage": "Izvēlieties valodu.",
"chooseLanguage": "Izvēlieties mērķa valodu.",
"planTrip": "Plānojiet savu ceļojumu",
"howAreYouTraveling": "Kā jūs ceļojat?",
"unlockPrivateTrip": "Atbloķēt privātu ceļojumu",

View file

@ -3866,7 +3866,7 @@
"loginToAccount": "Logg inn på kontoen min",
"appDescription": "Lær et språk\nmens du sender tekst til vennene dine.",
"languages": "Språk",
"chooseLanguage": "Velg et språk.",
"chooseLanguage": "Velg et målspråk.",
"planTrip": "Planlegg reisen din",
"howAreYouTraveling": "Hvordan reiser du?",
"unlockPrivateTrip": "Lås opp en privat reise",

View file

@ -4950,7 +4950,7 @@
"loginToAccount": "Log in op mijn account",
"appDescription": "Leer een taal\nterwijl je met je vrienden chat.",
"languages": "Talen",
"chooseLanguage": "Kies een taal.",
"chooseLanguage": "Kies een doeltaal.",
"planTrip": "Plan je reis",
"howAreYouTraveling": "Hoe reis je?",
"unlockPrivateTrip": "Ontgrendel een privéreis",

View file

@ -4951,7 +4951,7 @@
"loginToAccount": "Zaloguj się do mojego konta",
"appDescription": "Ucz się języka\npoprzez pisanie wiadomości do znajomych.",
"languages": "Języki",
"chooseLanguage": "Wybierz język.",
"chooseLanguage": "Wybierz język docelowy.",
"planTrip": "Zaplanować podróż",
"howAreYouTraveling": "Jak zamierzasz podróżować?",
"unlockPrivateTrip": "Odblokuj prywatną podróż",

View file

@ -4941,7 +4941,7 @@
"loginToAccount": "Faça login na minha conta",
"appDescription": "Aprenda um idioma\nenquanto envia mensagens aos seus amigos.",
"languages": "Idiomas",
"chooseLanguage": "Escolha um idioma.",
"chooseLanguage": "Escolha um idioma de destino.",
"planTrip": "Planeje sua viagem",
"howAreYouTraveling": "Como você está viajando?",
"unlockPrivateTrip": "Desbloqueie uma viagem privada",

View file

@ -4699,7 +4699,7 @@
"loginToAccount": "Faça login na minha conta",
"appDescription": "Aprenda um idioma\nenquanto envia mensagens para seus amigos.",
"languages": "Idiomas",
"chooseLanguage": "Escolha um idioma.",
"chooseLanguage": "Escolha um idioma de destino.",
"planTrip": "Planeje sua viagem",
"howAreYouTraveling": "Como você está viajando?",
"unlockPrivateTrip": "Desbloqueie uma viagem privada",

View file

@ -3762,7 +3762,7 @@
"loginToAccount": "Entrar na minha conta",
"appDescription": "Aprenda uma língua\nenquanto envia mensagens aos seus amigos.",
"languages": "Línguas",
"chooseLanguage": "Escolha uma língua.",
"chooseLanguage": "Escolha um idioma de destino.",
"planTrip": "Planear a sua viagem",
"howAreYouTraveling": "Como está a viajar?",
"unlockPrivateTrip": "Desbloquear uma viagem privada",

View file

@ -4370,7 +4370,7 @@
"loginToAccount": "Autentifică-te în contul meu",
"appDescription": "Învață o limbă\nîn timp ce trimiți mesaje prietenilor.",
"languages": "Limbi",
"chooseLanguage": "Alege o limbă.",
"chooseLanguage": "Alegeți o limbă țintă.",
"planTrip": "Planifică-ți călătoria",
"howAreYouTraveling": "Cum călătorești?",
"unlockPrivateTrip": "Deblochează o călătorie privată",

View file

@ -4889,7 +4889,7 @@
"loginToAccount": "войти в мой установки",
"appDescription": "Совседанно наступайте человеческий язык,например, в котором написате друзям.",
"languages": "Конфедерации",
"chooseLanguage": "Выберите человеческий язык.",
"chooseLanguage": "Выберите целевой язык.",
"planTrip": "Планите поездку",
"howAreYouTraveling": "Как вы проезжаете?",
"unlockPrivateTrip": "Обекращиваете свой событие",

View file

@ -3574,7 +3574,7 @@
"loginToAccount": "Prihlásiť sa do môjho účtu",
"appDescription": "Naučte sa jazyk\npočas písania správ priateľom.",
"languages": "Jazyky",
"chooseLanguage": "Vyberte jazyk.",
"chooseLanguage": "Vyberte cieľový jazyk.",
"planTrip": "Naplánujte si svoju cestu",
"howAreYouTraveling": "Ako cestujete?",
"unlockPrivateTrip": "Odomknúť súkromnú cestu",

View file

@ -2906,7 +2906,7 @@
"loginToAccount": "Prijavite se v svoj račun",
"appDescription": "Učite se jezika\nmedtem ko pošiljate sporočila prijateljem.",
"languages": "Jeziki",
"chooseLanguage": "Izberite jezik.",
"chooseLanguage": "Izberite ciljni jezik.",
"planTrip": "Načrtujte svoje potovanje",
"howAreYouTraveling": "Kako potujete?",
"unlockPrivateTrip": "Odklenite zasebno potovanje",

View file

@ -3987,7 +3987,7 @@
"loginToAccount": "Prijavi se na svoj nalog",
"appDescription": "Uči jezik\ndok šalješ poruke prijateljima.",
"languages": "Jezici",
"chooseLanguage": "Izaberite jezik.",
"chooseLanguage": "Izaberite ciljni jezik.",
"planTrip": "Planirajte svoje putovanje",
"howAreYouTraveling": "Kako putujete?",
"unlockPrivateTrip": "Otključajte privatno putovanje",

View file

@ -4612,7 +4612,7 @@
"loginToAccount": "Logga in på mitt konto",
"appDescription": "Lär dig ett språk\nmedan du skickar meddelanden till dina vänner.",
"languages": "Språk",
"chooseLanguage": "Välj ett språk.",
"chooseLanguage": "Välj ett målspråk.",
"planTrip": "Planera din resa",
"howAreYouTraveling": "Hur reser du?",
"unlockPrivateTrip": "Lås upp en privat resa",

View file

@ -4827,7 +4827,7 @@
"loginToAccount": "என் கணக்கில் உள்நுழையவும்",
"appDescription": "ஒரு மொழியை கற்றுக்கொள்ளுங்கள்\nஉங்கள் நண்பர்களுடன் உரையாடும் போது.",
"languages": "மொழிகள்",
"chooseLanguage": "ஒரு மொழியை தேர்ந்தெடுக்கவும்.",
"chooseLanguage": "ஒரு இலக்கு மொழியை தேர்வு செய்யவும்.",
"planTrip": "உங்கள் பயணத்தை திட்டமிடுங்கள்",
"howAreYouTraveling": "நீங்கள் எப்படி பயணம் செய்து கொண்டிருக்கிறீர்கள்?",
"unlockPrivateTrip": "தனிப்பட்ட பயணத்தை திறக்கவும்",

View file

@ -2362,7 +2362,7 @@
"loginToAccount": "నా ఖాతాలో లాగిన్ అవ్వండి",
"appDescription": "భాష నేర్చుకోండి\nమీ స్నేహితులతో సందేశాలు పంపుతూ.",
"languages": "భాషలు",
"chooseLanguage": "ఒక భాషను ఎంచుకోండి.",
"chooseLanguage": "ఒక లక్ష్య భాషను ఎంచుకోండి.",
"planTrip": "మీ ప్రయాణాన్ని ప్రణాళిక చేయండి",
"howAreYouTraveling": "మీరు ఎలా ప్రయాణిస్తున్నారు?",
"unlockPrivateTrip": "ప్రైవేట్ ట్రిప్‌ను అన్లాక్ చేయండి",

View file

@ -4898,7 +4898,7 @@
"loginToAccount": "เข้าสู่ระบบบัญชีของฉัน",
"appDescription": "เรียนรู้ภาษา\nในขณะส่งข้อความถึงเพื่อนของคุณ",
"languages": "ภาษา",
"chooseLanguage": "เลือกภาษา",
"chooseLanguage": "เลือกภาษาที่ต้องการ",
"planTrip": "วางแผนการเดินทาง",
"howAreYouTraveling": "คุณเดินทางอย่างไร",
"unlockPrivateTrip": "ปลดล็อคการเดินทางส่วนตัว",

View file

@ -4834,7 +4834,7 @@
"loginToAccount": "Hesabınıza giriş yapın",
"appDescription": "Bir dil öğrenin\narkadaşlarınıza mesaj atarken.",
"languages": "Diller",
"chooseLanguage": "Bir dil seçin.",
"chooseLanguage": "Hedef bir dil seçin.",
"planTrip": "Seyahatinizi planlayın",
"howAreYouTraveling": "Nasıl seyahat ediyorsunuz?",
"unlockPrivateTrip": "Özel bir seyahati açın",

View file

@ -4953,7 +4953,7 @@
"loginToAccount": "Увійти до мого облікового запису",
"appDescription": "Вивчайте мову\nпишучи повідомлення друзям.",
"languages": "Мови",
"chooseLanguage": "Виберіть мову.",
"chooseLanguage": "Виберіть мову призначення.",
"planTrip": "Сплануйте свою поїздку",
"howAreYouTraveling": "Як ви подорожуєте?",
"unlockPrivateTrip": "Розблокувати приватну поїздку",

View file

@ -4742,7 +4742,7 @@
"loginToAccount": "Đăng nhập vào tài khoản của tôi",
"appDescription": "Học một ngôn ngữ\ntrong khi nhắn tin với bạn bè.",
"languages": "Ngôn ngữ",
"chooseLanguage": "Chọn một ngôn ngữ.",
"chooseLanguage": "Chọn một ngôn ngữ mục tiêu.",
"planTrip": "Lập kế hoạch chuyến đi",
"howAreYouTraveling": "Bạn đi du lịch bằng phương tiện gì?",
"unlockPrivateTrip": "Mở khóa chuyến đi riêng tư",

View file

@ -2287,7 +2287,7 @@
"loginToAccount": "登入我的帳戶",
"appDescription": "學習一種語言\n同時與朋友發訊息。",
"languages": "語言",
"chooseLanguage": "選擇一種語言。",
"chooseLanguage": "選擇目標語言。",
"planTrip": "規劃您的旅程",
"howAreYouTraveling": "你打算怎樣旅行?",
"unlockPrivateTrip": "解鎖私人行程",

View file

@ -4954,7 +4954,7 @@
"loginToAccount": "登录我的账户",
"appDescription": "学习一门语言\n同时给朋友发消息。",
"languages": "语言",
"chooseLanguage": "选择一种语言。",
"chooseLanguage": "选择目标语言。",
"planTrip": "规划您的旅行",
"howAreYouTraveling": "你打算怎么旅行?",
"unlockPrivateTrip": "解锁私人旅行",

View file

@ -4931,7 +4931,7 @@
"loginToAccount": "登录我的账户",
"appDescription": "学习一门语言\n同时给朋友发短信。",
"languages": "語言",
"chooseLanguage": "選擇一種語言。",
"chooseLanguage": "選擇目標語言。",
"planTrip": "規劃您的旅程",
"howAreYouTraveling": "您打算如何旅行?",
"unlockPrivateTrip": "解鎖私人行程",

View file

@ -2285,8 +2285,8 @@ class ChatController extends State<ChatPageWithRoom>
}
bool showActivityDropdown = false;
void setShowDropdown(bool show) async {
setState(() => showActivityDropdown = show);
void toggleShowDropdown() async {
setState(() => showActivityDropdown = !showActivityDropdown);
}
bool hasRainedConfetti = false;

View file

@ -6,8 +6,6 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_stats_button.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/sync_status_localization.dart';
@ -30,23 +28,6 @@ class ChatAppBarTitle extends StatelessWidget {
// ),
// );
// }
if (controller.room.showActivityChatUI) {
return Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
spacing: 4.0,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
controller.room.getLocalizedDisplayname(),
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
ActivityStatsButton(controller: controller),
],
),
);
}
// Pangea#
return InkWell(
hoverColor: Colors.transparent,

View file

@ -15,6 +15,8 @@ import 'package:fluffychat/pages/chat/chat_event_list.dart';
import 'package:fluffychat/pages/chat/pinned_events.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_menu_button.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_session_popup_menu.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_stats_menu.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up/star_rain_widget.dart';
@ -122,46 +124,53 @@ class ChatView extends StatelessWidget {
// ],
// ),
// ];
// } else
// if (!controller.room.isArchived) {
// return [
// if (AppConfig.experimentalVoip &&
// Matrix.of(context).voipPlugin != null &&
// controller.room.isDirectChat)
// IconButton(
// onPressed: controller.onPhoneButtonTap,
// icon: const Icon(Icons.call_outlined),
// tooltip: L10n.of(context).placeCall,
// ),
// EncryptionButton(controller.room),
// ChatSettingsPopupMenu(controller.room, true),
// ];
if (!(controller.room.isArchived || controller.room.hasArchivedActivity)) {
return [
if (controller.room.activityPlan == null ||
!controller.room.showActivityChatUI)
IconButton(
icon: const Icon(Icons.search_outlined),
tooltip: L10n.of(context).search,
onPressed: () {
context.go('/rooms/${controller.room.id}/search');
},
),
IconButton(
icon: const Icon(Icons.settings_outlined),
tooltip: L10n.of(context).chatDetails,
onPressed: () {
if (GoRouterState.of(context).uri.path.endsWith('/details')) {
context.go('/rooms/${controller.room.id}');
} else {
context.go('/rooms/${controller.room.id}/details');
}
},
),
];
// Pangea#
// } else if (!controller.room.isArchived) {
// return [
// if (AppConfig.experimentalVoip &&
// Matrix.of(context).voipPlugin != null &&
// controller.room.isDirectChat)
// IconButton(
// onPressed: controller.onPhoneButtonTap,
// icon: const Icon(Icons.call_outlined),
// tooltip: L10n.of(context).placeCall,
// ),
// EncryptionButton(controller.room),
// ChatSettingsPopupMenu(controller.room, true),
// ];
// }
// return [];
if (controller.room.isArchived || controller.room.hasArchivedActivity) {
return [];
}
return [];
if (controller.room.showActivityChatUI) {
return [
ActivityMenuButton(controller: controller),
ActivitySessionPopupMenu(controller.room),
];
}
return [
IconButton(
icon: const Icon(Icons.search_outlined),
tooltip: L10n.of(context).search,
onPressed: () {
context.go('/rooms/${controller.room.id}/search');
},
),
IconButton(
icon: const Icon(Icons.settings_outlined),
tooltip: L10n.of(context).chatDetails,
onPressed: () {
if (GoRouterState.of(context).uri.path.endsWith('/details')) {
context.go('/rooms/${controller.room.id}');
} else {
context.go('/rooms/${controller.room.id}/details');
}
},
),
];
// Pangea#
}
@override
@ -216,9 +225,6 @@ class ChatView extends StatelessWidget {
// backgroundColor: controller.selectedEvents.isEmpty
// ? null
// : theme.colorScheme.tertiaryContainer,
toolbarHeight:
controller.room.showActivityChatUI ? 106.0 : null,
centerTitle: controller.room.showActivityChatUI,
// Pangea#
automaticallyImplyLeading: false,
leading: controller.selectMode

View file

@ -9,6 +9,7 @@ import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat_details/chat_download_provider.dart';
import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
import 'package:fluffychat/pangea/chat_settings/pages/pangea_room_details.dart';
@ -16,8 +17,6 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/course_plans/course_activities/activity_summaries_provider.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart';
import 'package:fluffychat/pangea/download/download_room_extension.dart';
import 'package:fluffychat/pangea/download/download_type_enum.dart';
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/utils/file_selector.dart';
@ -53,7 +52,7 @@ class ChatDetails extends StatefulWidget {
// #Pangea
// class ChatDetailsController extends State<ChatDetails> {
class ChatDetailsController extends State<ChatDetails>
with ActivitySummariesProvider, CoursePlanProvider {
with ActivitySummariesProvider, CoursePlanProvider, ChatDownloadProvider {
bool loadingActivities = true;
bool loadingCourseSummary = true;
@ -246,52 +245,6 @@ class ChatDetailsController extends State<ChatDetails>
}
// #Pangea
void downloadChatAction() async {
if (roomId == null) return;
final Room? room = Matrix.of(context).client.getRoomById(roomId!);
if (room == null) return;
final type = await showModalActionPopup(
context: context,
title: L10n.of(context).downloadGroupText,
actions: [
AdaptiveModalAction(
value: DownloadType.csv,
label: L10n.of(context).downloadCSVFile,
),
AdaptiveModalAction(
value: DownloadType.txt,
label: L10n.of(context).downloadTxtFile,
),
AdaptiveModalAction(
value: DownloadType.xlsx,
label: L10n.of(context).downloadXLSXFile,
),
],
);
if (type == null) return;
try {
await room.download(type, context);
} on EmptyChatException {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
L10n.of(context).emptyChatDownloadWarning,
),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"${L10n.of(context).oopsSomethingWentWrong} ${L10n.of(context).errorPleaseRefresh}",
),
),
);
}
}
Future<void> setRoomCapacity() async {
if (roomId == null) return;
final Room? room = Matrix.of(context).client.getRoomById(roomId!);

View file

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/download/download_room_extension.dart';
import 'package:fluffychat/pangea/download/download_type_enum.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
import 'package:fluffychat/widgets/matrix.dart';
mixin ChatDownloadProvider {
void downloadChatAction(String roomId, BuildContext context) async {
final Room? room = Matrix.of(context).client.getRoomById(roomId);
if (room == null) return;
final type = await showModalActionPopup(
context: context,
title: L10n.of(context).downloadGroupText,
actions: [
AdaptiveModalAction(
value: DownloadType.csv,
label: L10n.of(context).downloadCSVFile,
),
AdaptiveModalAction(
value: DownloadType.txt,
label: L10n.of(context).downloadTxtFile,
),
AdaptiveModalAction(
value: DownloadType.xlsx,
label: L10n.of(context).downloadXLSXFile,
),
],
);
if (type == null) return;
try {
await room.download(type, context);
} on EmptyChatException {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
L10n.of(context).emptyChatDownloadWarning,
),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"${L10n.of(context).oopsSomethingWentWrong} ${L10n.of(context).errorPleaseRefresh}",
),
),
);
}
}
}

View file

@ -0,0 +1,36 @@
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
extension ActivityMenuLogic on ChatController {
bool get shouldShowActivityInstructions {
if (InstructionsEnum.activityStatsMenu.isToggledOff ||
MatrixState.pAnyState.isOverlayOpen(RegExp(r"^word-zoom-card-.*$")) ||
timeline == null) {
return false;
}
final userID = Matrix.of(context).client.userID!;
final activityRoles = room.activityRoles?.roles.values ?? [];
final finishedRoles = activityRoles.where((r) => r.isFinished);
if (finishedRoles.isNotEmpty) {
return !finishedRoles.any((r) => r.userId == userID);
}
final count = timeline!.events
.where(
(event) =>
event.senderId == userID &&
event.type == EventTypes.Message &&
{MessageTypes.Text, MessageTypes.Audio}
.contains(event.messageType),
)
.length;
return count >= 3;
}
}

View file

@ -0,0 +1,111 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/pangea/common/widgets/tutorial_overlay_message.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
class ActivityMenuButton extends StatefulWidget {
final ChatController controller;
const ActivityMenuButton({
super.key,
required this.controller,
});
@override
State<ActivityMenuButton> createState() => _ActivityMenuButtonState();
}
class _ActivityMenuButtonState extends State<ActivityMenuButton> {
bool _showShimmer = false;
StreamSubscription? _rolesSubscription;
StreamSubscription? _analyticsSubscription;
@override
void initState() {
super.initState();
_analyticsSubscription = widget
.controller.pangeaController.getAnalytics.analyticsStream.stream
.listen(_showStatsMenuDropdownInstructions);
_rolesSubscription = widget.controller.room.client.onRoomState.stream
.where(
(u) =>
u.roomId == widget.controller.room.id &&
u.state.type == PangeaEventTypes.activityRole,
)
.listen(_showStatsMenuDropdownInstructions);
}
@override
void dispose() {
_analyticsSubscription?.cancel();
_rolesSubscription?.cancel();
super.dispose();
}
/// Show a tutorial overlay that blocks the screen and points
/// to the stats menu button with an explanation of what it does.
void _showStatsMenuDropdownInstructions(_) {
if (!mounted) return;
if (!widget.controller.shouldShowActivityInstructions) {
return;
}
final renderObject = context.findRenderObject() as RenderBox;
final offset = renderObject.localToGlobal(Offset.zero);
final cellRect = Rect.fromLTWH(
offset.dx,
offset.dy,
renderObject.size.width,
renderObject.size.height,
);
OverlayUtil.showTutorialOverlay(
context,
overlayContent: TutorialOverlayMessage(
L10n.of(context).activityStatsButtonInstruction,
),
overlayKey: "activity_stats_menu_instruction",
anchorRect: cellRect,
borderRadius: 12.0,
padding: 8.0,
onClick: () {
setState(() => _showShimmer = false);
InstructionsEnum.activityStatsMenu.setToggledOff(true);
widget.controller.toggleShowDropdown();
},
);
setState(() => _showShimmer = true);
}
@override
Widget build(BuildContext context) {
final content = IconButton(
icon: const Icon(Icons.radar_outlined),
tooltip: L10n.of(context).activityStatsButtonTooltip,
onPressed: widget.controller.toggleShowDropdown,
);
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: _showShimmer
? Shimmer.fromColors(
enabled: _showShimmer,
baseColor: Theme.of(context).iconTheme.color!,
highlightColor: AppConfig.gold,
child: content,
)
: content,
);
}
}

View file

@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat_details/chat_download_provider.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
enum ActivityPopupMenuActions { invite, leave, download }
class ActivitySessionPopupMenu extends StatefulWidget {
final Room room;
const ActivitySessionPopupMenu(this.room, {super.key});
@override
ActivitySessionPopupMenuState createState() =>
ActivitySessionPopupMenuState();
}
class ActivitySessionPopupMenuState extends State<ActivitySessionPopupMenu>
with ChatDownloadProvider {
@override
Widget build(BuildContext context) {
return PopupMenuButton<ActivityPopupMenuActions>(
useRootNavigator: true,
onSelected: (choice) async {
switch (choice) {
case ActivityPopupMenuActions.leave:
final router = GoRouter.of(context);
final confirmed = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).areYouSure,
message: L10n.of(context).leaveRoomDescription,
okLabel: L10n.of(context).leave,
cancelLabel: L10n.of(context).cancel,
isDestructive: true,
);
if (confirmed != OkCancelResult.ok) return;
final result = await showFutureLoadingDialog(
context: context,
future: () => widget.room.leave(),
);
if (result.error == null) {
router.go('/rooms');
}
break;
case ActivityPopupMenuActions.invite:
context.go(
widget.room.courseParent != null
? '/rooms/spaces/${widget.room.courseParent!.id}/${widget.room.id}/invite'
: '/rooms/${widget.room.id}/invite',
);
break;
case ActivityPopupMenuActions.download:
downloadChatAction(widget.room.id, context);
break;
}
},
itemBuilder: (BuildContext context) => [
PopupMenuItem<ActivityPopupMenuActions>(
value: ActivityPopupMenuActions.invite,
child: Row(
children: [
const Icon(Icons.person_add_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).invite),
],
),
),
PopupMenuItem<ActivityPopupMenuActions>(
value: ActivityPopupMenuActions.download,
child: Row(
children: [
const Icon(Icons.download_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).download),
],
),
),
PopupMenuItem<ActivityPopupMenuActions>(
value: ActivityPopupMenuActions.leave,
child: Row(
children: [
const Icon(Icons.delete_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).leave),
],
),
),
],
);
}
}

View file

@ -1,288 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ActivityStatsButton extends StatefulWidget {
final ChatController controller;
const ActivityStatsButton({
super.key,
required this.controller,
});
@override
State<ActivityStatsButton> createState() => _ActivityStatsButtonState();
}
class _ActivityStatsButtonState extends State<ActivityStatsButton> {
StreamSubscription? _analyticsSubscription;
StreamSubscription? _rolesSubscription;
ActivitySummaryAnalyticsModel? analytics;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _updateAnalytics(),
);
_analyticsSubscription = widget
.controller.pangeaController.getAnalytics.analyticsStream.stream
.listen((_) => _updateAnalytics());
_rolesSubscription = widget.controller.room.client.onRoomState.stream
.where(
(u) =>
u.roomId == widget.controller.room.id &&
u.state.type == PangeaEventTypes.activityRole,
)
.listen((_) {
_showStatsMenuDropdownInstructions();
});
}
@override
void dispose() {
_analyticsSubscription?.cancel();
_rolesSubscription?.cancel();
super.dispose();
}
Client get _client => widget.controller.room.client;
bool get _shouldShowInstructions {
if (InstructionsEnum.activityStatsMenu.isToggledOff ||
MatrixState.pAnyState.isOverlayOpen(
RegExp(r"^word-zoom-card-.*$"),
) ||
widget.controller.timeline == null) {
return false;
}
// if someone has finished the activity, enable the tooltip
final activityRoles =
widget.controller.room.activityRoles?.roles.values.toList() ?? [];
final finishedRoles = activityRoles.where((r) => r.isFinished).toList();
if (finishedRoles.isNotEmpty) {
return !finishedRoles.any((r) => r.userId == _client.userID);
}
// otherwise, if no one has finished, only show if the user has sent >= 3 messages
int count = 0;
for (final event in widget.controller.timeline!.events) {
if (event.senderId == _client.userID &&
event.type == EventTypes.Message &&
[
MessageTypes.Text,
MessageTypes.Audio,
].contains(event.messageType)) {
count++;
}
if (count >= 3) return true;
}
return false;
}
int get _xpCount =>
analytics?.totalXPForUser(
_client.userID!,
) ??
0;
int? get _vocabCount => analytics?.uniqueConstructCountForUser(
_client.userID!,
ConstructTypeEnum.vocab,
);
int? get _grammarCount => analytics?.uniqueConstructCountForUser(
_client.userID!,
ConstructTypeEnum.morph,
);
/// Show a tutorial overlay that blocks the screen and points
/// to the stats menu button with an explanation of what it does.
void _showStatsMenuDropdownInstructions() {
if (!_shouldShowInstructions) {
return;
}
final renderObject = context.findRenderObject() as RenderBox;
final offset = renderObject.localToGlobal(Offset.zero);
final cellRect = Rect.fromLTWH(
offset.dx,
offset.dy,
renderObject.size.width,
renderObject.size.height,
);
OverlayUtil.showTutorialOverlay(
context,
overlayContent: Center(
child: Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onSurface,
borderRadius: BorderRadius.circular(12.0),
),
width: 200,
alignment: Alignment.center,
child: RichText(
text: TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.surface,
),
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.info_outlined,
size: 16.0,
color: Theme.of(context).colorScheme.surface,
),
),
const WidgetSpan(child: SizedBox(width: 4.0)),
TextSpan(
text: L10n.of(context).activityStatsButtonInstruction,
),
],
),
textAlign: TextAlign.center,
),
),
),
overlayKey: "activity_stats_menu_instruction",
anchorRect: cellRect,
borderRadius: 12.0,
padding: 8.0,
onClick: () {
InstructionsEnum.activityStatsMenu.setToggledOff(true);
widget.controller.setShowDropdown(true);
},
);
}
Future<void> _updateAnalytics() async {
final analytics = await widget.controller.room.getActivityAnalytics();
if (mounted) {
setState(() => this.analytics = analytics);
_showStatsMenuDropdownInstructions();
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final roleState =
widget.controller.room.activityRoles?.roles.values.toList() ?? [];
final enabled = _xpCount > 0 || roleState.any((r) => r.isFinished);
return PressableButton(
onPressed: () => widget.controller.setShowDropdown(
!widget.controller.showActivityDropdown,
),
borderRadius: BorderRadius.circular(12),
color: enabled
? Color.alphaBlend(
Theme.of(context).colorScheme.surface.withAlpha(70),
AppConfig.gold,
)
: theme.colorScheme.surface,
depressed: !enabled || widget.controller.showActivityDropdown,
child: AnimatedContainer(
duration: FluffyThemes.animationDuration,
width: 280,
height: 40,
decoration: BoxDecoration(
color: enabled
? Color.alphaBlend(
Theme.of(context).colorScheme.surface.withAlpha(70),
AppConfig.gold,
)
: theme.colorScheme.surface,
borderRadius: BorderRadius.circular(12),
),
child: analytics == null
? const CircularProgressIndicator.adaptive()
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_StatsBadge(
icon: Icons.star,
value: "$_xpCount XP",
),
_StatsBadge(
icon: Symbols.dictionary,
value: "$_vocabCount",
),
_StatsBadge(
icon: Symbols.toys_and_games,
value: "$_grammarCount",
),
],
),
),
);
}
}
class _StatsBadge extends StatelessWidget {
final IconData icon;
final String value;
const _StatsBadge({
required this.icon,
required this.value,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final baseStyle = theme.textTheme.bodyMedium;
final double fontSize = FluffyThemes.isColumnMode(context) ? 18 : 14;
final double iconSize = FluffyThemes.isColumnMode(context) ? 22 : 18;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: iconSize,
color: theme.colorScheme.onSurface,
),
const SizedBox(width: 4),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
value,
style: baseStyle?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.onSurface,
fontSize: fontSize,
),
),
],
),
],
);
}
}

View file

@ -98,7 +98,7 @@ class ActivityStatsMenuState extends State<ActivityStatsMenu> {
? await room.finishActivityForAll()
: await room.finishActivity();
if (mounted) {
widget.controller.setShowDropdown(false);
widget.controller.toggleShowDropdown();
}
},
);
@ -150,7 +150,7 @@ class ActivityStatsMenuState extends State<ActivityStatsMenu> {
child: GestureDetector(
onPanUpdate: (details) {
if (details.delta.dy < -2) {
widget.controller.setShowDropdown(false);
widget.controller.toggleShowDropdown();
}
},
child: Container(
@ -263,7 +263,7 @@ class ActivityStatsMenuState extends State<ActivityStatsMenu> {
if (widget.controller.showActivityDropdown)
Expanded(
child: GestureDetector(
onTap: () => widget.controller.setShowDropdown(false),
onTap: widget.controller.toggleShowDropdown,
child: Container(color: Colors.black.withAlpha(100)),
),
),

View file

@ -110,7 +110,7 @@ class ChatDetailsButtonRowState extends State<ChatDetailsButtonRow> {
ButtonDetails(
title: l10n.download,
icon: const Icon(Icons.download_outlined, size: 30.0),
onPressed: widget.controller.downloadChatAction,
onPressed: () => widget.controller.downloadChatAction(room.id, context),
visible: kIsWeb,
enabled: room.ownPowerLevel >= 50,
showInMainView: false,

View file

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
class TutorialOverlayMessage extends StatelessWidget {
final String message;
const TutorialOverlayMessage(
this.message, {
super.key,
});
@override
Widget build(BuildContext context) {
return Center(
child: Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onSurface,
borderRadius: BorderRadius.circular(12.0),
),
width: 200,
alignment: Alignment.center,
child: RichText(
text: TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.surface,
),
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.info_outlined,
size: 16.0,
color: Theme.of(context).colorScheme.surface,
),
),
const WidgetSpan(child: SizedBox(width: 4.0)),
TextSpan(
text: message,
),
],
),
textAlign: TextAlign.center,
),
),
);
}
}

View file

@ -55,22 +55,6 @@ class ActivityTemplateChatListItem extends StatelessWidget {
),
),
),
trailing: Row(
spacing: 2.0,
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.chat_bubble_outline,
size: 8.0,
),
Text(
"${sessions.length}",
style: const TextStyle(
fontSize: 8.0,
),
),
],
),
title: Row(
children: [
Expanded(

View file

@ -16,6 +16,8 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/course_chats/course_chats_view.dart';
import 'package:fluffychat/pangea/course_chats/extended_space_rooms_chunk.dart';
import 'package:fluffychat/pangea/course_plans/course_activities/activity_summaries_provider.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
@ -42,7 +44,7 @@ class CourseChats extends StatefulWidget {
}
class CourseChatsController extends State<CourseChats>
with ActivitySummariesProvider {
with ActivitySummariesProvider, CoursePlanProvider {
String get roomId => widget.roomId;
Room? get room => widget.client.getRoomById(widget.roomId);
@ -117,6 +119,7 @@ class CourseChatsController extends State<CourseChats>
final Map<ActivityPlanModel, List<ExtendedSpaceRoomsChunk>> sessionsMap =
{};
final validIDs = course?.activityIDs ?? {};
for (final chunk in discoveredChildren!) {
if (chunk.roomType?.startsWith(PangeaRoomTypes.activitySession) != true) {
continue;
@ -130,6 +133,11 @@ class CourseChatsController extends State<CourseChats>
final activity = summary.activityPlan;
final users =
summary.activityRoles.roles.values.map((r) => r.userId).toList();
if (users.isEmpty || !validIDs.contains(activity.activityId)) {
continue;
}
if (activity.req.numberOfParticipants <= users.length) {
// Don't show full activities
continue;
@ -198,9 +206,19 @@ class CourseChatsController extends State<CourseChats>
await _loadHierarchy(activeSpace: room, reload: reload);
if (mounted) await _joinDefaultChats();
if (mounted) {
await loadRoomSummaries(
room.spaceChildren.map((c) => c.roomId).whereType<String>().toList(),
);
final futures = [
loadRoomSummaries(
room.spaceChildren
.map((c) => c.roomId)
.whereType<String>()
.toList(),
),
if (room.coursePlan?.uuid != null) loadCourse(room.coursePlan!.uuid),
];
await Future.wait(futures);
if (mounted) {
await loadTopics();
}
}
} catch (e, s) {
Logs().w('Unable to load hierarchy', e, s);

View file

@ -99,6 +99,9 @@ class CoursePlanModel {
),
).topics;
Set<String> get activityIDs =>
loadedTopics.values.expand((topic) => topic.activityIds).toSet();
Future<Map<String, CourseTopicModel>> fetchTopics() async {
final resp = await CourseTopicRepo.get(
TranslateTopicRequest(

View file

@ -63,7 +63,8 @@ extension IsStateExtension on Event {
// #Pangea
bool get isVisibleInPangeaGui {
if (!room.showActivityChatUI) {
return true;
return type != EventTypes.RoomMember ||
roomMemberChangeType != RoomMemberChangeType.avatar;
}
return type != EventTypes.RoomMember;

View file

@ -451,6 +451,7 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
pip install openai
python translate_keys.py --keys about accept account
python -m scripts.translate.translate_keys.py --keys-file scripts.translate.keys_to_translate.txt
""",