feat: bring back old course pages (#5328)

This commit is contained in:
ggurdin 2026-01-22 09:41:10 -05:00 committed by GitHub
parent e8bf2645a6
commit 6675cebfec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 1502 additions and 145 deletions

View file

@ -22,6 +22,7 @@ import 'package:fluffychat/pages/device_settings/device_settings.dart';
import 'package:fluffychat/pages/login/login.dart';
import 'package:fluffychat/pages/new_group/new_group.dart';
import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart';
import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart';
import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart';
import 'package:fluffychat/pages/settings_chat/settings_chat.dart';
@ -50,9 +51,9 @@ import 'package:fluffychat/pangea/course_creation/course_invite_page.dart';
import 'package:fluffychat/pangea/course_creation/selected_course_page.dart';
import 'package:fluffychat/pangea/join_codes/join_with_link_page.dart';
import 'package:fluffychat/pangea/learning_settings/settings_learning.dart';
import 'package:fluffychat/pangea/login/pages/add_course_page.dart';
import 'package:fluffychat/pangea/login/pages/course_code_page.dart';
import 'package:fluffychat/pangea/login/pages/create_pangea_account_page.dart';
import 'package:fluffychat/pangea/login/pages/find_course_page.dart';
import 'package:fluffychat/pangea/login/pages/language_selection_page.dart';
import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart';
import 'package:fluffychat/pangea/login/pages/new_course_page.dart';
@ -213,93 +214,8 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
const AddCoursePage(route: 'registration'),
const SpaceCodeOnboarding(),
),
routes: [
GoRoute(
path: 'private',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
const CourseCodePage(),
);
},
),
GoRoute(
path: 'public',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
const PublicCoursesPage(
route: 'registration',
showFilters: false,
),
);
},
routes: [
GoRoute(
path: ':courseid',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
SelectedCourse(
state.pathParameters['courseid']!,
SelectedCourseMode.join,
roomChunk: state.extra as PublicRoomsChunk?,
),
);
},
),
],
),
GoRoute(
path: 'own',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
const NewCoursePage(
route: 'registration',
showFilters: false,
),
);
},
routes: [
GoRoute(
path: ':courseid',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
SelectedCourse(
state.pathParameters['courseid']!,
SelectedCourseMode.launch,
),
);
},
routes: [
GoRoute(
path: 'invite',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
CourseInvitePage(
state.pathParameters['courseid']!,
courseCreationCompleter:
state.extra as Completer<String>?,
),
);
},
),
],
),
],
),
],
),
],
),
@ -434,7 +350,7 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
const AddCoursePage(route: 'rooms'),
const FindCoursePage(),
),
routes: [
GoRoute(

View file

@ -1,6 +1,6 @@
{
"@@locale": "ar",
"@@last_modified": "2026-01-21 13:54:18.388293",
"@@last_modified": "2026-01-22 09:36:57.998979",
"about": "حول",
"@about": {
"type": "String",
@ -11117,5 +11117,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "هل لديك رمز دعوة أو رابط لدورة عامة؟",
"welcomeUser": "مرحبًا {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1911,7 +1911,7 @@
"playWithAI": "Пакуль гуляйце з ШІ",
"courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!",
"@@locale": "be",
"@@last_modified": "2026-01-21 13:54:07.402936",
"@@last_modified": "2026-01-22 09:36:46.926563",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11999,5 +11999,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Ці маеце вы код запрашэння або спасылку на публічны курс?",
"welcomeUser": "Сардэчна запрашаем, {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:31.328626",
"@@last_modified": "2026-01-22 09:37:11.081120",
"about": "সম্পর্কে",
"@about": {
"type": "String",
@ -12004,5 +12004,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "আপনার কি একটি আমন্ত্রণ কোড বা একটি পাবলিক কোর্সের লিঙ্ক আছে?",
"welcomeUser": "স্বাগতম {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -4279,7 +4279,7 @@
"joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།",
"startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།",
"@@locale": "bo",
"@@last_modified": "2026-01-21 13:54:28.767499",
"@@last_modified": "2026-01-22 09:37:08.423146",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -10654,5 +10654,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?",
"welcomeUser": "Welcome {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:08.647836",
"@@last_modified": "2026-01-22 09:36:49.492725",
"about": "Quant a",
"@about": {
"type": "String",
@ -10924,5 +10924,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Tens un codi d'invitació o un enllaç a un curs públic?",
"welcomeUser": "Benvingut {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "cs",
"@@last_modified": "2026-01-21 13:54:04.800051",
"@@last_modified": "2026-01-22 09:36:43.351703",
"about": "O aplikaci",
"@about": {
"type": "String",
@ -11507,5 +11507,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Máte pozvánkový kód nebo odkaz na veřejný kurz?",
"welcomeUser": "Vítejte {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:53:34.885532",
"@@last_modified": "2026-01-22 09:36:14.204217",
"@aboutHomeserver": {
"type": "String",
"placeholders": {
@ -11961,5 +11961,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Har du en invitationskode eller et link til et offentligt kursus?",
"welcomeUser": "Velkommen {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "de",
"@@last_modified": "2026-01-21 13:53:56.595777",
"@@last_modified": "2026-01-22 09:36:34.415651",
"alwaysUse24HourFormat": "true",
"@alwaysUse24HourFormat": {
"description": "Set to true to always display time of day in 24 hour format."
@ -10907,5 +10907,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Haben Sie einen Einladungscode oder einen Link zu einem öffentlichen Kurs?",
"welcomeUser": "Willkommen {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -4456,7 +4456,7 @@
"playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν",
"courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!",
"@@locale": "el",
"@@last_modified": "2026-01-21 13:54:37.783088",
"@@last_modified": "2026-01-22 09:37:17.865903",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11958,5 +11958,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Έχετε έναν κωδικό πρόσκλησης ή σύνδεσμο για ένα δημόσιο μάθημα;",
"welcomeUser": "Καλώς ήρθατε {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -5058,5 +5058,15 @@
"learn": "Learn",
"languageUpdated": "Target language updated!",
"voiceDropdownTitle": "Pangea Bot voice",
"knockDesc": "Your request has been sent to course admin! You'll be let in if they approve."
"knockDesc": "Your request has been sent to course admin! You'll be let in if they approve.",
"joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?",
"welcomeUser": "Welcome {user}",
"@welcomeUser": {
"placeholders": {
"user": {
"type": "String"
}
}
},
"findCourse": "Find a course"
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:42.193114",
"@@last_modified": "2026-01-22 09:37:22.593342",
"about": "Prio",
"@about": {
"type": "String",
@ -11989,5 +11989,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Ĉu vi havas invitkodon aŭ ligon al publika kurso?",
"welcomeUser": "Bonvenon {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "es",
"@@last_modified": "2026-01-21 13:53:29.857658",
"@@last_modified": "2026-01-22 09:36:09.573972",
"about": "Acerca de",
"@about": {
"type": "String",
@ -8134,5 +8134,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
"welcomeUser": "Bienvenido {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "et",
"@@last_modified": "2026-01-21 13:53:55.586614",
"@@last_modified": "2026-01-22 09:36:33.207413",
"about": "Rakenduse teave",
"@about": {
"type": "String",
@ -11171,5 +11171,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Kas sul on kutsekood või link avalikule kursusele?",
"welcomeUser": "Tere tulemast {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "eu",
"@@last_modified": "2026-01-21 13:53:53.122879",
"@@last_modified": "2026-01-22 09:36:30.783445",
"about": "Honi buruz",
"@about": {
"type": "String",
@ -10900,5 +10900,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Baduzu gonbidapen kodea edo lotura publiko baten ikastaroarentzako?",
"welcomeUser": "Ongi etorri {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:33.096668",
"@@last_modified": "2026-01-22 09:37:12.739208",
"repeatPassword": "تکرار رمزعبور",
"@repeatPassword": {},
"about": "درباره",
@ -11632,5 +11632,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "آیا کد دعوت یا لینکی به یک دوره عمومی دارید؟",
"welcomeUser": "خوش آمدید {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:53:33.564588",
"@@last_modified": "2026-01-22 09:36:12.968075",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11523,5 +11523,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Onko sinulla kutsukoodia tai linkkiä julkiseen kurssiin?",
"welcomeUser": "Tervetuloa {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -2787,7 +2787,7 @@
"selectAll": "Piliin lahat",
"deselectAll": "Huwag piliin lahat",
"@@locale": "fil",
"@@last_modified": "2026-01-21 13:54:14.817261",
"@@last_modified": "2026-01-22 09:36:55.313694",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@ -11876,5 +11876,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Mayroon ka bang invite code o link sa isang pampublikong kurso?",
"welcomeUser": "Maligayang pagdating {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "fr",
"@@last_modified": "2026-01-21 13:54:48.318824",
"@@last_modified": "2026-01-22 09:37:30.149848",
"about": "À propos",
"@about": {
"type": "String",
@ -11224,5 +11224,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Avez-vous un code d'invitation ou un lien vers un cours public ?",
"welcomeUser": "Bienvenue {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:54:46.978792",
"@@last_modified": "2026-01-22 09:37:28.516173",
"@customReaction": {
"type": "String",
"placeholders": {}
@ -10898,5 +10898,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "An bhfuil cód cuireadh nó nasc agat do chúrsa poiblí?",
"welcomeUser": "Fáilte {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "gl",
"@@last_modified": "2026-01-21 13:53:31.619299",
"@@last_modified": "2026-01-22 09:36:11.502910",
"about": "Acerca de",
"@about": {
"type": "String",
@ -10897,5 +10897,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Tes un código de invitación ou un enlace a un curso público?",
"welcomeUser": "Benvido {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:47.592162",
"@@last_modified": "2026-01-22 09:36:25.705220",
"about": "אודות",
"@about": {
"type": "String",
@ -11949,5 +11949,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "האם יש לך קוד הזמנה או קישור לקורס ציבורי?",
"welcomeUser": "ברוך הבא {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -4483,7 +4483,7 @@
"playWithAI": "अभी के लिए एआई के साथ खेलें",
"courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!",
"@@locale": "hi",
"@@last_modified": "2026-01-21 13:54:40.850433",
"@@last_modified": "2026-01-22 09:37:20.935542",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11985,5 +11985,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "क्या आपके पास एक आमंत्रण कोड या सार्वजनिक पाठ्यक्रम के लिए लिंक है?",
"welcomeUser": "स्वागत है {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "hr",
"@@last_modified": "2026-01-21 13:53:46.524141",
"@@last_modified": "2026-01-22 09:36:23.684413",
"about": "Informacije",
"@about": {
"type": "String",
@ -11272,5 +11272,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni tečaj?",
"welcomeUser": "Dobrodošli {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "hu",
"@@last_modified": "2026-01-21 13:53:36.742109",
"@@last_modified": "2026-01-22 09:36:15.788819",
"about": "Névjegy",
"@about": {
"type": "String",
@ -10901,5 +10901,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Van meghívó kódod vagy linked egy nyilvános kurzushoz?",
"welcomeUser": "Üdvözöljük {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:53:49.028067",
"@@last_modified": "2026-01-22 09:36:27.975426",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11978,5 +11978,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
"welcomeUser": "Bienvenido {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:37.899898",
"@@last_modified": "2026-01-22 09:36:18.048263",
"setAsCanonicalAlias": "Atur sebagai alias utama",
"@setAsCanonicalAlias": {
"type": "String",
@ -10891,5 +10891,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Apakah Anda memiliki kode undangan atau tautan ke kursus publik?",
"welcomeUser": "Selamat datang {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:53:45.416677",
"@@last_modified": "2026-01-22 09:36:22.149976",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11874,5 +11874,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
"welcomeUser": "Bienvenido {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:01.568848",
"@@last_modified": "2026-01-22 09:36:40.074415",
"about": "Informazioni",
"@about": {
"type": "String",
@ -10903,5 +10903,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Hai un codice di invito o un link per un corso pubblico?",
"welcomeUser": "Benvenuto {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "ja",
"@@last_modified": "2026-01-21 13:54:39.359536",
"@@last_modified": "2026-01-22 09:37:19.372006",
"about": "このアプリについて",
"@about": {
"type": "String",
@ -11690,5 +11690,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "招待コードまたは公開コースへのリンクはありますか?",
"welcomeUser": "ようこそ {user} さん",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -2594,7 +2594,7 @@
"playWithAI": "ამ დროისთვის ითამაშეთ AI-თან",
"courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!",
"@@locale": "ka",
"@@last_modified": "2026-01-21 13:54:44.486907",
"@@last_modified": "2026-01-22 09:37:25.958869",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11930,5 +11930,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "გაქვთ თუ არა მოწვევის კოდი ან ბმული საჯარო კურსზე?",
"welcomeUser": "კეთილი იყოს თქვენი მობრძანება {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:28.417406",
"@@last_modified": "2026-01-22 09:36:07.420159",
"about": "소개",
"@about": {
"type": "String",
@ -11008,5 +11008,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "공개 과정에 대한 초대 코드나 링크가 있습니까?",
"welcomeUser": "환영합니다 {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:54:22.729769",
"@@last_modified": "2026-01-22 09:37:02.748391",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11705,5 +11705,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Ar turite kvietimo kodą arba nuorodą į viešą kursą?",
"welcomeUser": "Sveiki atvykę, {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:54:16.883169",
"@@last_modified": "2026-01-22 09:36:56.779342",
"analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti",
"analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.",
"accessRequestedTitle": "Pieprasījums piekļūt analītikai",
@ -10886,5 +10886,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Vai jums ir uzaicinājuma kods vai saite uz publisku kursu?",
"welcomeUser": "Laipni lūdzam, {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:06.232198",
"@@last_modified": "2026-01-22 09:36:44.562587",
"about": "Om",
"@about": {
"type": "String",
@ -11993,5 +11993,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Har du en invitasjonskode eller lenke til et offentlig kurs?",
"welcomeUser": "Velkommen {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:27.649909",
"@@last_modified": "2026-01-22 09:37:07.354119",
"about": "Over ons",
"@about": {
"type": "String",
@ -10900,5 +10900,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Heb je een uitnodigingscode of link naar een openbare cursus?",
"welcomeUser": "Welkom {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "pl",
"@@last_modified": "2026-01-21 13:54:34.451816",
"@@last_modified": "2026-01-22 09:37:14.175391",
"about": "O aplikacji",
"@about": {
"type": "String",
@ -10898,5 +10898,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Czy masz kod zaproszenia lub link do publicznego kursu?",
"welcomeUser": "Witaj {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:54.148542",
"@@last_modified": "2026-01-22 09:36:31.935958",
"copiedToClipboard": "Copiada para a área de transferência",
"@copiedToClipboard": {
"type": "String",
@ -12000,5 +12000,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
"welcomeUser": "Bem-vindo {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:51.587209",
"@@last_modified": "2026-01-22 09:36:29.216087",
"about": "Sobre",
"@about": {
"type": "String",
@ -11258,5 +11258,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
"welcomeUser": "Bem-vindo {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -3331,7 +3331,7 @@
"selectAll": "Selecionar tudo",
"deselectAll": "Desmarcar tudo",
"@@locale": "pt_PT",
"@@last_modified": "2026-01-21 13:54:11.801274",
"@@last_modified": "2026-01-22 09:36:52.464279",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11929,5 +11929,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
"welcomeUser": "Bem-vindo {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:53:41.456181",
"@@last_modified": "2026-01-22 09:36:19.487915",
"about": "Despre",
"@about": {
"type": "String",
@ -11635,5 +11635,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Ai un cod de invitație sau un link pentru un curs public?",
"welcomeUser": "Bine ai venit {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "ru",
"@@last_modified": "2026-01-21 15:35:33.881278",
"@@last_modified": "2026-01-22 09:37:24.211755",
"about": "О проекте",
"@about": {
"type": "String",
@ -11008,5 +11008,19 @@
"@italicText": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "У вас есть код приглашения или ссылка на публичный курс?",
"welcomeUser": "Добро пожаловать {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "sk",
"@@last_modified": "2026-01-21 13:53:43.706457",
"@@last_modified": "2026-01-22 09:36:21.123706",
"about": "O aplikácii",
"@about": {
"type": "String",
@ -11984,5 +11984,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Máte pozývací kód alebo odkaz na verejný kurz?",
"welcomeUser": "Vitaj {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

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-21 13:53:58.241980",
"@@last_modified": "2026-01-22 09:36:36.017757",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11981,5 +11981,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Imate kodo za povabilo ali povezavo do javnega tečaja?",
"welcomeUser": "Dobrodošli {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:45.561732",
"@@last_modified": "2026-01-22 09:37:27.068492",
"about": "О програму",
"@about": {
"type": "String",
@ -12002,5 +12002,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni kurs?",
"welcomeUser": "Dobrodošli {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:35.931933",
"@@last_modified": "2026-01-22 09:37:16.018226",
"about": "Om",
"@about": {
"type": "String",
@ -11378,5 +11378,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Har du en inbjudningskod eller länk till en offentlig kurs?",
"welcomeUser": "Välkommen {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:25.999605",
"@@last_modified": "2026-01-22 09:37:06.101200",
"acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது",
"@acceptedTheInvitation": {
"type": "String",
@ -11124,5 +11124,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "உங்களுக்கு ஒரு அழைப்பு குறியீடு அல்லது பொது பாடத்திற்கு இணைப்பு உள்ளதா?",
"welcomeUser": "வரவேற்கிறேன் {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1920,7 +1920,7 @@
"playWithAI": "ఇప్పుడే AI తో ఆడండి",
"courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!",
"@@locale": "te",
"@@last_modified": "2026-01-21 13:54:20.897642",
"@@last_modified": "2026-01-22 09:37:01.069487",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@ -11989,5 +11989,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "మీకు పబ్లిక్ కోర్సుకు ఆహ్వాన కోడ్ లేదా లింక్ ఉందా?",
"welcomeUser": "స్వాగతం {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -4456,7 +4456,7 @@
"playWithAI": "เล่นกับ AI ชั่วคราว",
"courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!",
"@@locale": "th",
"@@last_modified": "2026-01-21 13:54:10.758435",
"@@last_modified": "2026-01-22 09:36:51.239616",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@ -11958,5 +11958,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "คุณมีรหัสเชิญหรือลิงก์ไปยังหลักสูตรสาธารณะหรือไม่?",
"welcomeUser": "ยินดีต้อนรับ {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "tr",
"@@last_modified": "2026-01-21 13:54:19.467039",
"@@last_modified": "2026-01-22 09:36:59.414494",
"about": "Hakkında",
"@about": {
"type": "String",
@ -11122,5 +11122,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Bir davet kodunuz veya halka açık bir kursa bağlantınız var mı?",
"welcomeUser": "Hoş geldin {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "uk",
"@@last_modified": "2026-01-21 13:54:03.403456",
"@@last_modified": "2026-01-22 09:36:41.798719",
"about": "Про застосунок",
"@about": {
"type": "String",
@ -10894,5 +10894,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "У вас є код запрошення або посилання на публічний курс?",
"welcomeUser": "Ласкаво просимо {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:24.252269",
"@@last_modified": "2026-01-22 09:37:04.643688",
"about": "Giới thiệu",
"@about": {
"type": "String",
@ -6470,5 +6470,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "Bạn có mã mời hoặc liên kết đến một khóa học công khai không?",
"welcomeUser": "Chào mừng {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1856,7 +1856,7 @@
"selectAll": "全選",
"deselectAll": "取消全選",
"@@locale": "yue",
"@@last_modified": "2026-01-21 13:53:59.885092",
"@@last_modified": "2026-01-22 09:36:38.546245",
"@ignoreUser": {
"type": "String",
"placeholders": {}
@ -11991,5 +11991,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "你有邀請碼或公共課程的鏈接嗎?",
"welcomeUser": "歡迎 {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"@@locale": "zh",
"@@last_modified": "2026-01-21 13:54:29.681537",
"@@last_modified": "2026-01-22 09:37:09.563669",
"about": "关于",
"@about": {
"type": "String",
@ -10891,5 +10891,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "您是否有邀请代码或公共课程的链接?",
"welcomeUser": "欢迎 {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -1,5 +1,5 @@
{
"@@last_modified": "2026-01-21 13:54:13.165623",
"@@last_modified": "2026-01-22 09:36:53.893925",
"about": "關於",
"@about": {
"type": "String",
@ -10898,5 +10898,19 @@
"@knockDesc": {
"type": "String",
"placeholders": {}
},
"joinSpaceOnboardingDesc": "您是否有邀請碼或公共課程的鏈接?",
"welcomeUser": "歡迎 {user}",
"@joinSpaceOnboardingDesc": {
"type": "String",
"placeholders": {}
},
"@welcomeUser": {
"type": "String",
"placeholders": {
"user": {
"type": "String"
}
}
}
}

View file

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/onboarding/space_code_onboarding_view.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/join_codes/space_code_controller.dart';
import 'package:fluffychat/pangea/spaces/space_constants.dart';
import 'package:fluffychat/widgets/matrix.dart';
class SpaceCodeOnboarding extends StatefulWidget {
const SpaceCodeOnboarding({super.key});
@override
State<SpaceCodeOnboarding> createState() => SpaceCodeOnboardingState();
}
class SpaceCodeOnboardingState extends State<SpaceCodeOnboarding> {
Profile? profile;
Client get client => Matrix.of(context).client;
final TextEditingController codeController = TextEditingController();
@override
void initState() {
_setProfile();
codeController.addListener(() {
if (mounted) setState(() {});
});
super.initState();
}
@override
void dispose() {
codeController.dispose();
super.dispose();
}
Future<void> _setProfile() async {
try {
profile = await client.getProfileFromUserId(
client.userID!,
);
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
'userId': client.userID,
},
);
} finally {
if (mounted) setState(() {});
}
}
Future<void> submitCode() async {
String code = codeController.text.trim();
if (code.isEmpty) return;
try {
final link = Uri.parse(Uri.parse(code).fragment);
if (link.queryParameters.containsKey(SpaceConstants.classCode)) {
code = link.queryParameters[SpaceConstants.classCode]!;
}
} catch (e) {
debugPrint("Text input is not a URL: $e");
}
final roomId = await SpaceCodeController.joinSpaceWithCode(context, code);
if (roomId != null) {
final room = Matrix.of(context).client.getRoomById(roomId);
room?.isSpace ?? true
? context.go('/rooms/spaces/$roomId/details')
: context.go('/rooms/$roomId');
}
}
@override
Widget build(BuildContext context) =>
SpaceCodeOnboardingView(controller: this);
}

View file

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart';
import 'package:fluffychat/pangea/authentication/p_logout.dart';
import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart';
class SpaceCodeOnboardingView extends StatelessWidget {
final SpaceCodeOnboardingState controller;
const SpaceCodeOnboardingView({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
return PangeaLoginScaffold(
customAppBar: AppBar(
leading: BackButton(
onPressed: () => pLogoutAction(
context,
bypassWarning: true,
),
),
),
showAppName: false,
mainAssetUrl: controller.profile?.avatarUrl,
children: [
Column(
spacing: 8.0,
children: [
Text(
L10n.of(context).welcomeUser(
controller.profile?.displayName ??
controller.client.userID?.localpart ??
"",
),
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
Text(
L10n.of(context).joinSpaceOnboardingDesc,
textAlign: TextAlign.center,
),
TextField(
decoration: InputDecoration(
hintText: L10n.of(context).enterCodeToJoin,
),
controller: controller.codeController,
onSubmitted: (_) => controller.submitCode,
),
ElevatedButton(
onPressed: controller.codeController.text.isNotEmpty
? controller.submitCode
: null,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onPrimaryContainer,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(L10n.of(context).join),
],
),
),
TextButton(
child: Text(L10n.of(context).skipForNow),
onPressed: () => context.go("/rooms"),
),
],
),
],
);
}
}

View file

@ -6,7 +6,7 @@ import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/join_codes/space_code_controller.dart';
import 'package:fluffychat/pangea/login/pages/add_course_page.dart';
import 'package:fluffychat/pangea/spaces/space_constants.dart';
import 'package:fluffychat/widgets/matrix.dart';
class CourseCodePage extends StatefulWidget {
@ -72,7 +72,7 @@ class CourseCodePageState extends State<CourseCodePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.network(
"${AppConfig.assetsBaseURL}/${AddCoursePage.mapUnlockFileName}",
"${AppConfig.assetsBaseURL}/${SpaceConstants.mapUnlockFileName}",
width: 100.0,
height: 100.0,
colorFilter: ColorFilter.mode(

View file

@ -0,0 +1,523 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart';
import 'package:fluffychat/pangea/course_creation/course_info_chip_widget.dart';
import 'package:fluffychat/pangea/course_creation/course_language_filter.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plan_model.dart';
import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart';
import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/spaces/public_course_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
class FindCoursePage extends StatefulWidget {
const FindCoursePage({super.key});
@override
State<FindCoursePage> createState() => FindCoursePageState();
}
class FindCoursePageState extends State<FindCoursePage> {
final TextEditingController searchController = TextEditingController();
bool loading = true;
bool _fullyLoaded = false;
Object? error;
Timer? _coolDown;
LanguageModel? targetLanguageFilter;
List<PublicCoursesChunk> discoveredCourses = [];
Map<String, CoursePlanModel> coursePlans = {};
String? nextBatch;
@override
void initState() {
super.initState();
final target = MatrixState.pangeaController.userController.userL2;
if (target != null) {
setTargetLanguageFilter(target);
}
_loadCourses();
}
@override
void dispose() {
searchController.dispose();
_coolDown?.cancel();
super.dispose();
}
void setTargetLanguageFilter(LanguageModel? language) {
if (targetLanguageFilter?.langCodeShort == language?.langCodeShort) return;
setState(() => targetLanguageFilter = language);
_loadCourses();
}
void onSearchEnter(String text, {bool globalSearch = true}) {
if (text.isEmpty) {
_loadCourses();
return;
}
_coolDown?.cancel();
_coolDown = Timer(const Duration(milliseconds: 500), _loadCourses);
}
List<PublicCoursesChunk> get filteredCourses {
List<PublicCoursesChunk> filtered = discoveredCourses
.where(
(c) =>
!Matrix.of(context).client.rooms.any(
(r) =>
r.id == c.room.roomId &&
r.membership == Membership.join,
) &&
coursePlans.containsKey(c.courseId),
)
.toList();
if (targetLanguageFilter != null) {
filtered = filtered.where(
(chunk) {
final course = coursePlans[chunk.courseId];
if (course == null) return false;
return course.targetLanguage.split('-').first ==
targetLanguageFilter!.langCodeShort;
},
).toList();
}
final searchText = searchController.text.trim().toLowerCase();
if (searchText.isNotEmpty) {
filtered = filtered.where(
(chunk) {
final course = coursePlans[chunk.courseId];
if (course == null) return false;
final name = chunk.room.name?.toLowerCase() ?? '';
final description = course.description.toLowerCase();
return name.contains(searchText) || description.contains(searchText);
},
).toList();
}
// sort by join rule, with knock rooms at the end
filtered.sort((a, b) {
final aKnock = a.room.joinRule == JoinRules.knock.name;
final bKnock = b.room.joinRule == JoinRules.knock.name;
if (aKnock && !bKnock) return 1;
if (!aKnock && bKnock) return -1;
return 0;
});
return filtered;
}
Future<void> _loadPublicSpaces() async {
try {
final resp = await Matrix.of(context).client.requestPublicCourses(
since: nextBatch,
);
for (final room in resp.courses) {
if (!discoveredCourses.any((e) => e.room.roomId == room.room.roomId)) {
discoveredCourses.add(room);
}
}
nextBatch = resp.nextBatch;
} catch (e, s) {
error = e;
ErrorHandler.logError(
e: e,
s: s,
data: {
'nextBatch': nextBatch,
},
);
}
}
Future<void> _loadCourses() async {
if (_fullyLoaded && nextBatch == null) {
return;
}
setState(() {
loading = true;
error = null;
});
await _loadPublicSpaces();
int timesLoaded = 0;
while (error == null && timesLoaded < 5 && nextBatch != null) {
await _loadPublicSpaces();
timesLoaded++;
}
if (nextBatch == null) {
_fullyLoaded = true;
}
try {
final resp = await CoursePlansRepo.search(
GetLocalizedCoursesRequest(
coursePlanIds:
discoveredCourses.map((c) => c.courseId).toSet().toList(),
l1: MatrixState.pangeaController.userController.userL1Code!,
),
);
final searchResult = resp.coursePlans;
coursePlans.clear();
for (final entry in searchResult.entries) {
coursePlans[entry.key] = entry.value;
}
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
'discoveredCourses':
discoveredCourses.map((c) => c.courseId).toList(),
},
);
} finally {
if (mounted) {
setState(() => loading = false);
}
}
}
@override
Widget build(BuildContext context) {
return FindCoursePageView(controller: this);
}
}
class FindCoursePageView extends StatelessWidget {
final FindCoursePageState controller;
const FindCoursePageView({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isColumnMode = FluffyThemes.isColumnMode(context);
return Scaffold(
appBar: AppBar(title: Text(L10n.of(context).findCourse)),
body: MaxWidthBody(
showBorder: false,
withScrolling: false,
maxWidth: 600.0,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
spacing: 16.0,
children: [
TextField(
controller: controller.searchController,
textInputAction: TextInputAction.search,
onChanged: controller.onSearchEnter,
decoration: InputDecoration(
filled: !isColumnMode,
fillColor: isColumnMode
? null
: theme.colorScheme.secondaryContainer,
border: OutlineInputBorder(
borderSide:
isColumnMode ? const BorderSide() : BorderSide.none,
borderRadius: BorderRadius.circular(100),
),
contentPadding: const EdgeInsets.fromLTRB(
0,
0,
20.0,
0,
),
hintText: L10n.of(context).findCourse,
hintStyle: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
fontWeight: FontWeight.normal,
fontSize: 16.0,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
prefixIcon: IconButton(
onPressed: () {},
icon: Icon(
Icons.search_outlined,
color: theme.colorScheme.onPrimaryContainer,
),
),
),
),
LayoutBuilder(
builder: (context, constrained) {
return Row(
spacing: 12.0,
children: [
Expanded(
child: CourseLanguageFilter(
value: controller.targetLanguageFilter,
onChanged: controller.setTargetLanguageFilter,
),
),
if (constrained.maxWidth >= 500) ...[
TextButton(
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.add),
Text(L10n.of(context).newCourse),
],
),
onPressed: () => context.go("/rooms/course/own"),
),
TextButton(
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.join_full),
Text(L10n.of(context).joinWithCode),
],
),
onPressed: () => context.go("/rooms/course/private"),
),
] else
PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (context) => [
PopupMenuItem(
onTap: () => context.go("/rooms/course/own"),
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.add),
Text(L10n.of(context).newCourse),
],
),
),
PopupMenuItem(
onTap: () => context.go("/rooms/course/private"),
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.join_full),
Text(L10n.of(context).joinWithCode),
],
),
),
],
),
],
);
},
),
ValueListenableBuilder(
valueListenable: controller.searchController,
builder: (context, _, __) {
if (controller.error != null) {
return ErrorIndicator(
message: L10n.of(context).oopsSomethingWentWrong,
);
}
if (controller.loading) {
return const CircularProgressIndicator.adaptive();
}
if (controller.filteredCourses.isEmpty) {
return Text(
L10n.of(context).nothingFound,
);
}
return Expanded(
child: ListView.builder(
itemCount: controller.filteredCourses.length,
itemBuilder: (context, index) {
final space = controller.filteredCourses[index];
return _PublicCourseTile(
chunk: space,
course: controller.coursePlans[space.courseId],
);
},
),
);
},
),
],
),
),
),
);
}
}
class _PublicCourseTile extends StatelessWidget {
final PublicCoursesChunk chunk;
final CoursePlanModel? course;
const _PublicCourseTile({
required this.chunk,
this.course,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isColumnMode = FluffyThemes.isColumnMode(context);
final space = chunk.room;
final courseId = chunk.courseId;
final displayname =
space.name ?? space.canonicalAlias ?? L10n.of(context).emptyChat;
return Padding(
padding: isColumnMode
? const EdgeInsets.only(
bottom: 32.0,
)
: const EdgeInsets.only(
bottom: 16.0,
),
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => context.go(
'/rooms/course/public/$courseId',
extra: space,
),
borderRadius: BorderRadius.circular(12.0),
child: Container(
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: theme.colorScheme.primary,
),
),
child: Column(
spacing: 4.0,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8.0,
children: [
ImageByUrl(
imageUrl: space.avatarUrl?.toString(),
width: 58.0,
borderRadius: BorderRadius.circular(10.0),
replacement: Avatar(
name: displayname,
borderRadius: BorderRadius.circular(
10.0,
),
size: 58.0,
),
),
Flexible(
child: Column(
spacing: 0.0,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
displayname,
style: theme.textTheme.bodyLarge,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Row(
spacing: 4.0,
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.group,
size: 16.0,
),
Text(
L10n.of(context).countParticipants(
space.numJoinedMembers,
),
style: theme.textTheme.bodyMedium,
),
],
),
],
),
),
],
),
if (course != null) ...[
CourseInfoChips(
courseId,
iconSize: 12.0,
fontSize: 12.0,
),
Text(
course!.description,
style: theme.textTheme.bodyMedium,
),
],
const SizedBox(height: 12.0),
HoverBuilder(
builder: (context, hovered) => ElevatedButton(
onPressed: () => context.go(
'/rooms/course/public/$courseId',
extra: space,
),
style: ElevatedButton.styleFrom(
backgroundColor:
theme.colorScheme.primaryContainer.withAlpha(
hovered ? 255 : 200,
),
foregroundColor: theme.colorScheme.onPrimaryContainer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
12.0,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
space.joinRule == JoinRules.knock.name
? L10n.of(
context,
).knock
: L10n.of(
context,
).join,
),
],
),
),
),
],
),
),
),
),
);
}
}

View file

@ -5,6 +5,7 @@ class SpaceConstants {
static const String classCode = 'classcode';
static const String introductionChatAlias = 'introductionChat';
static const String announcementsChatAlias = 'announcementsChat';
static String mapUnlockFileName = "unlock_trip.svg";
static List<String> introChatIcons = [
'${AppConfig.assetsBaseURL}/Introduction_1.jpg',

View file

@ -202,7 +202,7 @@ class SpacesNavigationRail extends StatelessWidget {
child: const Icon(Icons.add),
),
),
toolTip: L10n.of(context).addCourse,
toolTip: L10n.of(context).findCourse,
expanded: expanded,
// Pangea#
);