diff --git a/README.md b/README.md
index 7c27b6e2e..a1ad9f2b7 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@
# Special thanks
-* Pangea Chat is a fork of [FluffyChat](https://fluffychat.im), is an open source, nonprofit and cute [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). The goal of FluffyChat is to create an easy to use instant messenger which is open source and accessible for everyone. You can [support the primary maker of FluffyChat directly here.](https://ko-fi.com/C1C86VN53)
+* Pangea Chat is a fork of [FluffyChat](https://fluffychat.im) which is a [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). You can [support the primary maker of FluffyChat directly here.](https://ko-fi.com/C1C86VN53)
* Fabiyamada is a graphics designer and has made the fluffychat logo and the banner. Big thanks for her great designs.
diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb
index c66ef7ac6..cb319c9c7 100644
--- a/assets/l10n/intl_en.arb
+++ b/assets/l10n/intl_en.arb
@@ -410,7 +410,6 @@
"type": "text",
"placeholders": {}
},
- "classes": "Classes",
"chooseAStrongPassword": "Choose a strong password",
"@chooseAStrongPassword": {
"type": "text",
@@ -616,16 +615,11 @@
}
},
"createGroup": "Create group",
- "createNewSpace": "Create an exchange space",
"createNewGroup": "Create a new chat",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
- "@createNewSpace": {
- "type": "text",
- "placeholders": {}
- },
"currentlyActive": "Currently active",
"@currentlyActive": {
"type": "text",
@@ -2401,7 +2395,7 @@
"@noKeyForThisMessage": {},
"newGroup": "New chat",
"@newGroup": {},
- "newSpace": "New class",
+ "newSpace": "New space",
"@newSpace": {},
"enterSpace": "Enter space",
"@enterSpace": {},
@@ -2540,7 +2534,7 @@
"type": "text",
"placeholders": {}
},
- "interactiveTranslatorNotAllowedDesc": "Translation assistance is disabled in space group chats for all participants. This restriction does not apply to Class/Exchange Admin or direct chats.",
+ "interactiveTranslatorNotAllowedDesc": "Translation assistance is disabled in space group chats for all participants. This restriction does not apply to space admins or direct chats.",
"@interactiveTranslatorNotAllowedDesc": {
"type": "text",
"placeholders": {}
@@ -2550,7 +2544,7 @@
"type": "text",
"placeholders": {}
},
- "interactiveTranslatorRequiredDesc": "Students cannot turn off translation assistance. They can choose not to accept the translation suggestions. This restriction does not apply to Class/Exchange or direct chats.",
+ "interactiveTranslatorRequiredDesc": "Students cannot turn off translation assistance. They can choose not to accept the translation suggestions. This restriction does not apply to spaces or direct chats.",
"@interactiveTranslatorRequiredDesc": {
"type": "text",
"placeholders": {}
@@ -2560,12 +2554,7 @@
"type": "text",
"placeholders": {}
},
- "multiLingualClass": "Multilingual Class",
- "classAnalytics": "Class Analytics",
- "@classAnalytics": {
- "type": "text",
- "placeholders": {}
- },
+ "multiLingualSpace": "Multilingual Space",
"allClasses": "All Classes",
"@allClasses": {
"type": "text",
@@ -2601,18 +2590,8 @@
"type": "text",
"placeholders": {}
},
- "requestAnExchange": "Request an Exchange",
- "@requestAnExchange": {
- "type": "text",
- "placeholders": {}
- },
- "findLanguageExchange": "Find a class exchange partner",
- "@findLanguageExchange": {
- "type": "text",
- "placeholders": {}
- },
- "classAnalyticsDesc": "Detailed information on student engagement and language use",
- "@classAnalyticsDesc": {
+ "spaceAnalyticsDesc": "Detailed information on student engagement and language use",
+ "@spaceAnalyticsDesc": {
"type": "text",
"placeholders": {}
},
@@ -2629,28 +2608,28 @@
"type": "text",
"placeholders": {}
},
- "classSettings": "Class Settings",
- "@classSettings": {
+ "languageSettings": "Language Settings",
+ "@languageSettings": {
"type": "text",
"placeholders": {}
},
- "classSettingsDesc": "Edit class languages and proficiency level.",
- "@classSettingsDesc": {
+ "languageSettingsDesc": "Edit space languages and proficiency level.",
+ "@languageSettingsDesc": {
"type": "text",
"placeholders": {}
},
- "selectClassRoomDominantLanguage": "What is the base language of your class?",
- "@selectClassRoomDominantLanguage": {
+ "selectSpaceDominantLanguage": "What's the most common language of space members?",
+ "@selectSpaceDominantLanguage": {
"type": "text",
"placeholders": {}
},
- "selectTargetLanguage": "What language are you teaching?",
- "@selectTargetLanguage": {
+ "selectSpaceTargetLanguage": "What is the most common target language of the space?",
+ "@selectSpaceTargetLanguage": {
"type": "text",
"placeholders": {}
},
- "whatIsYourClassLanguageLevel": "What is the average language level of your class?",
- "@whatIsYourClassLanguageLevel": {
+ "whatIsYourSpaceLanguageLevel": "What is the average language level of the space?",
+ "@whatIsYourSpaceLanguageLevel": {
"type": "text",
"placeholders": {}
},
@@ -2679,7 +2658,7 @@
"type": "text",
"placeholders": {}
},
- "createGroupChatsDesc": "Toggle this on to allow students to create group chats within the class/exchange space.",
+ "createGroupChatsDesc": "Toggle this on to allow students to create group chats within the space.",
"@createGroupChatsDesc": {
"type": "text",
"placeholders": {}
@@ -2794,12 +2773,12 @@
"type": "text",
"placeholders": {}
},
- "joinWithClassCode": "Join class or exchange",
+ "joinWithClassCode": "Join space",
"@joinWithClassCode": {
"type": "text",
"placeholders": {}
},
- "joinWithClassCodeDesc": "Connect to a class or exchange space with the 6-digit invite code provided by the space administrator.",
+ "joinWithClassCodeDesc": "Connect to a space with the 6-digit invite code provided by the space administrator.",
"@joinWithClassCodeDesc": {
"type": "text",
"placeholders": {}
@@ -2809,7 +2788,7 @@
"type": "text",
"placeholders": {}
},
- "unableToFindClass": "We are unable to find the class or exchange. Please double-check the information with the space administrator. If you are still experiencing an issue, please contact support@pangea.chat.",
+ "unableToFindClass": "We are unable to find the space. Please double-check the information with the space administrator. If you are still experiencing an issue, please contact support@pangea.chat.",
"@unableToFindClass": {
"type": "text",
"placeholders": {}
@@ -2869,12 +2848,12 @@
"type": "text",
"placeholders": {}
},
- "welcomeToPangea18Plus": "Welcome to Pangea Chat! 🙂\nWhat's next?\nCreate or join a class!\nOr search for a conversation partner!",
+ "welcomeToPangea18Plus": "Welcome to Pangea Chat! 🙂\nWhat's next?\nCreate or join a space!\nOr search for a conversation partner!",
"@welcomeToPangea18Plus": {
"type": "text",
"placeholders": {}
},
- "welcomeToPangeaMinor": "Welcome to Pangea Chat! 🙂\nWhat's next?\nJoin a class!\nAsk your teacher for an invite code.",
+ "welcomeToPangeaMinor": "Welcome to Pangea Chat! 🙂\nWhat's next?\nJoin a space!\nAsk your teacher for an invite code.",
"@welcomeToPangeaMinor": {
"type": "text",
"placeholders": {}
@@ -3174,7 +3153,7 @@
"igcToggleDescription": "This language learning tool will identify common spelling, grammar and punctuation errors in your message and suggest corrections. Though rare, the AI can make correction errors.",
"sendOnEnterDescription": "Turn this off to be able to add line spaces in messages. When the toggle is off on the browser app, you can press Shift + Enter to start a new line. When the toggle is off on mobile apps, just Enter will start a new line.",
"alreadyInClass": "You are already in this space.",
- "pleaseLoginFirst": "Please login or sign up first and then you will be added to your class/exchange space.",
+ "pleaseLoginFirst": "Please login or sign up first and then you will be added to your space.",
"originalMessage": "Original Message",
"sentMessage": "Sent Message",
"useType": "Use Type",
@@ -3185,16 +3164,13 @@
"definitionsToolDescription": "When enabled, words underlined in blue can be clicked for definitions. Click messages to access definitions.",
"translationsToolDescrption": "When enabled, click a message and the translation icon to see a message in your base language.",
"welcomeBack": "Welcome back! If you were part of the 2023-2024 pilot, please contact us for your special pilot subscription. If you are a teacher who has (or whose institution has) purchased licenses for your class, contact us for your teacher subscription.",
- "classExchanges": "Exchanges",
"createNewClass": "New class space",
- "newExchange": "New exchange space",
"kickAllStudents": "Kick All Students",
"kickAllStudentsConfirmation": "Are you sure you want to kick all students?",
"inviteAllStudents": "Invite All Students",
"inviteAllStudentsConfirmation": "Are you sure you want to invite all students?",
"inviteStudentsFromOtherClasses": "Invite students from other spaces",
"inviteUsersFromPangea": "Add teachers",
- "allExchanges": "All Exchanges",
"redeemPromoCode": "Redeem Promo Code",
"enterPromoCode": "Enter Promo Code",
"downloadTxtFile": "Download Text File",
@@ -3652,22 +3628,24 @@
"pay": "Pay",
"allPrivateChats": "Direct chats",
"unknownPrivateChat": "Unknown private chat",
- "copyClassCodeDesc": "Students who are already in the app can 'Join class or exchange' via the main menu.",
- "addToClass": "Add exchange to class",
- "addToClassDesc": "Adding an exchange to a class will make the exchange appear within the class for students and give them access to all chats within the exchange.",
- "addToClassOrExchange": "Add chat to class or exchange",
- "addToClassOrExchangeDesc": "Adding a chat to a class or exchange will make the chat appear within the class or exchange for students and give them access.",
- "invitedToClassOrExchange": "{user} has invited you to join a space: {classOrExchange}! Do you wish to accept?",
- "@invitedToClassOrExchange": {
+ "copyClassCodeDesc": "Students who are already in the app can 'Join space' via the main menu.",
+ "addToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.",
+ "@addToSpaceDesc": {
"placeholders": {
- "classOrExchange": {},
+ "roomtype": {}
+ }
+ },
+ "invitedToSpace": "{user} has invited you to join a space: {space}! Do you wish to accept?",
+ "@invitedToSpace": {
+ "placeholders": {
+ "space": {},
"user": {}
}
},
"declinedInvitation": "Declined invitation",
"acceptedInvitation": "Accepted invitation",
"youreInvited": "📩 You're invited!",
- "studentPermissionsDesc": "Set permissions for this space. They will only apply to the class/exchange space. They will override individual user settings.",
+ "studentPermissionsDesc": "Set permissions for this space. They will only apply to the space. They will override individual user settings.",
"noEligibleSpaces": "There are no eligible spaces to add this to.",
"youAddedToSpace": "You added {child} to {space}",
"@youAddedToSpace": {
@@ -3705,9 +3683,9 @@
},
"emptyChatNameWarning": "Please enter a name for this chat",
"emptyClassNameWarning": "Please enter a name for this class",
- "emptyExchangeNameWarning": "Please enter a name for this exchange",
+ "emptySpaceNameWarning": "Please enter a name for this space",
"blurMeansTranslateTitle": "Why is the message blurred?",
- "blurMeansTranslateBody": "While Immersion Mode is on, messages that are sent in your base language will be blurred while Pangea Bot translates them to your target language. Immersion Mode can be toggled in individual and class settings.",
+ "blurMeansTranslateBody": "While Immersion Mode is on, messages that are sent in your base language will be blurred while Pangea Bot translates them to your target language. Immersion Mode can be toggled in individual and space settings.",
"someErrorTitle": "Hm, something's not right",
"someErrorBody": "It could be an error or something in your base language.",
"bestCorrectionFeedback": "That's correct!",
@@ -3717,7 +3695,7 @@
"practiceDefaultPrompt": "What is the best answer?",
"correctionDefaultPrompt": "What is the best replacement?",
"itStartDefaultPrompt": "Do you want help translating?",
- "languageLevelWarning": "Please select a class language level",
+ "languageLevelWarning": "Please select a space language level",
"lockedChatWarning": "🔒 This chat has been locked",
"lockSpace": "Lock Space",
"lockChat": "Lock Chat",
@@ -3730,7 +3708,6 @@
"why": "Why?",
"definition": "Definition",
"exampleSentence": "Example Sentence",
- "addToClassTitle": "Add Exchange to Class",
"reportToTeacher": "Who do you want to report this message to?",
"reportMessageTitle": "{reportingUserId} has reported a message from {reportedUserId} in the chat {roomName}",
"@reportMessageTitle": {
@@ -3777,7 +3754,6 @@
},
"searchChatsRooms": "Search for #chats, @users...",
"createClass": "Create class",
- "createExchange": "Create exchange",
"viewArchive": "View Archive",
"trialExpiration": "Your free trial expires on {expiration}",
"@trialExpiration": {
@@ -3787,10 +3763,10 @@
},
"freeTrialDesc": "New users recieve a one week free trial of Pangea Chat",
"activateTrial": "Activate Free Trial",
- "inNoSpaces": "You are not a member of any classes or exchanges",
+ "inNoSpaces": "You are not a member of any spaces",
"successfullySubscribed": "You have successfully subscribed!",
"clickToManageSubscription": "Click here to manage your subscription.",
- "emptyInviteWarning": "Add this chat to a class or exchange to invite other users.",
+ "emptyInviteWarning": "Add this chat to a space to invite other users.",
"errorGettingAudio": "Error getting audio. Please refresh and try again.",
"nothingFound": "Nothing found...",
"groupName": "Group name",
@@ -4034,9 +4010,9 @@
"wordsPerMinute": "Words per minute",
"autoIGCToolName": "Run Language Assistance Automatically",
"autoIGCToolDescription": "Automatically run language assistance after typing messages",
- "runGrammarCorrection": "Run grammar correction",
+ "runGrammarCorrection": "Check message",
"grammarCorrectionFailed": "Issues to address",
- "grammarCorrectionComplete": "Grammar correction complete",
+ "grammarCorrectionComplete": "Looks good!",
"leaveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.",
"archiveSpaceDescription": "All chats within this space will be moved to the archive for yourself and other non-admin users.",
"leaveSpaceDescription": "All chats within this space will be moved to the archive. Other users will be able to see that you have left the space.",
@@ -4068,5 +4044,19 @@
"@knockRestricted": {},
"nonexistentSelection": "Selection no longer exists.",
"cantAddSpaceChild": "You do not have permission to add a child to this space.",
- "roomAddedToSpace": "Room(s) have been added to the selected space."
+ "roomAddedToSpace": "Room(s) have been added to the selected space.",
+ "createNewSpace": "New space",
+ "@createNewSpace": {
+ "type": "text",
+ "placeholders": {}
+ },
+ "addChatToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.",
+ "addSpaceToSpaceDesc": "Adding a space to another space will make the child space appear within the parent space for students and give them access.",
+ "spaceAnalytics": "Space Analytics",
+ "changeAnalyticsLanguage": "Change Analytics Language",
+ "suggestToSpace": "Suggest this space",
+ "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces",
+ "practice": "Practice",
+ "noLanguagesSet": "No languages set",
+ "noActivitiesFound": "No practice activities found for this message"
}
\ No newline at end of file
diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb
index 44a03f0e1..dc328294a 100644
--- a/assets/l10n/intl_es.arb
+++ b/assets/l10n/intl_es.arb
@@ -2752,31 +2752,11 @@
"type": "text",
"placeholders": {}
},
- "openToExchanges": "¿Abierto a intercambios?",
- "@openToExchanges": {
- "type": "text",
- "placeholders": {}
- },
- "oneToOneChatsWithinExchanges": "Chats Privados dentro de Intercambios",
- "@oneToOneChatsWithinExchanges": {
- "type": "text",
- "placeholders": {}
- },
"createGroupChats": "Crear Chats Grupales",
"@createGroupChats": {
"type": "text",
"placeholders": {}
},
- "createGroupChatsInExchanges": "Crear Chats Grupales en Intercambios",
- "@createGroupChatsInExchanges": {
- "type": "text",
- "placeholders": {}
- },
- "createGroupChatsInExchangesDesc": "Active esta opción para permitir que los estudiantes creen chats grupales dentro de los intercambios.",
- "@createGroupChatsInExchangesDesc": {
- "type": "text",
- "placeholders": {}
- },
"shareStories": "Subir historias",
"@shareStories": {
"type": "text",
@@ -2917,21 +2897,11 @@
"type": "text",
"placeholders": {}
},
- "findLanguageExchange": "Encuentre una clase de intercambio",
- "@findLanguageExchange": {
- "type": "text",
- "placeholders": {}
- },
"requestToEnroll": "Solicitar inscripción",
"@requestToEnroll": {
"type": "text",
"placeholders": {}
},
- "requestAnExchange": "Solicitar un intercambio",
- "@requestAnExchange": {
- "type": "text",
- "placeholders": {}
- },
"targetLanguage": "Idioma a aprender",
"@targetLanguage": {
"type": "text",
@@ -2982,11 +2952,6 @@
"type": "text",
"placeholders": {}
},
- "classSettings": "Ajustes de clase",
- "@classSettings": {
- "type": "text",
- "placeholders": {}
- },
"suggestToClass": "Sugiera el chat a",
"@suggestToClass": {
"type": "text",
@@ -3032,8 +2997,8 @@
"type": "text",
"placeholders": {}
},
- "classAnalyticsDesc": "Información detallada sobre la participación de los estudiantes y el uso del idioma",
- "@classAnalyticsDesc": {
+ "spaceAnalyticsDesc": "Información detallada sobre la participación de los estudiantes y el uso del idioma",
+ "@spaceAnalyticsDesc": {
"type": "text",
"placeholders": {}
},
@@ -3815,7 +3780,6 @@
"sentMessage": "Mensaje enviado",
"useType": "Tipo de Uso",
"notAvailable": "No disponible",
- "classExchanges": "Intercambios",
"kickAllStudents": "Patear a todos los estudiantes",
"kickAllStudentsConfirmation": "¿Estás seguro de que quieres echar a todos los estudiantes?",
"inviteAllStudents": "Invitar a todos los estudiantes",
@@ -4296,7 +4260,6 @@
"copyClassLink": "Copiar enlace de invitación",
"copyClassLinkDesc": "Al hacer clic en este enlace, los usuarios accederán a la aplicación, se abrirán una cuenta y se unirán automáticamente a este espacio.",
"copyClassCode": "Copiar código de invitación",
- "classSettingsDesc": "Editar idiomas de clase y nivel",
"createGroupChatsDesc": "Active esta opción para permitir a los estudiantes crear chats de grupo dentro del espacio de clase/intercambio.",
"joinWithClassCode": "Únete a una clase o a un intercambio",
"joinWithClassCodeDesc": "Conéctese a una clase o espacio de intercambio con el código de invitación de 6 dígitos proporcionado por el administrador del espacio.",
@@ -4304,8 +4267,6 @@
"unableToFindClass": "No podemos encontrar la clase o el intercambio. Por favor, vuelva a comprobar la información con el administrador del espacio. Si sigue teniendo problemas, póngase en contacto con support@pangea.chat.",
"welcomeToYourNewClass": "Bienvenido 🙂",
"welcomeToClass": "Bienvenido! 🙂\n- ¡Prueba a unirte a un chat!\n- ¡Diviértete chateando!",
- "welcomeToPangea18Plus": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Crea una clase o únete a ella!\n¡O busca un compañero de conversación!",
- "welcomeToPangeaMinor": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Únete a una clase!\nPide a tu profesor un código de invitación.",
"unableToFindClassCode": "No se puede encontrar el código.",
"errorDisableITClassDesc": "La ayuda a la traducción está desactivada para el espacio en el que se encuentra este chat.",
"errorDisableIGCClassDesc": "La asistencia gramatical está desactivada para el espacio en el que se encuentra este chat.",
@@ -4317,26 +4278,14 @@
"pleaseLoginFirst": "Inicie sesión o regístrese primero y, a continuación, se le añadirá a su clase/espacio de intercambio.",
"welcomeBack": "¡Bienvenido de nuevo! Si formó parte del piloto 2023-2024, póngase en contacto con nosotros para obtener su suscripción especial de piloto. Si es usted un profesor que ha adquirido (o cuya institución ha adquirido) licencias para su clase, póngase en contacto con nosotros para obtener su suscripción de profesor.",
"createNewClass": "Nuevo espacio para clases",
- "newExchange": "Nuevo espacio de intercambio",
"inviteStudentsFromOtherClasses": "Invitar a estudiantes de otros espacios",
- "allExchanges": "Todos los intercambios",
"creatingSpacePleaseWait": "Creando espacio. Por favor, espere...",
"pay": "Pagar",
"copyClassCodeDesc": "Los estudiantes que ya están en la aplicación pueden 'Unirse a una clase o a un intercambio' a través del menú principal.",
"inviteUsersFromPangea": "Añadir profesores",
"addToClass": "Añadir intercambio a la clase",
- "addToClassOrExchange": "Añadir chat a la clase o al intercambio",
- "classAnalytics": "Análisis de clase",
"myLearning": "Mis análisis",
"addToClassDesc": "Añadir un intercambio a una clase hará que el intercambio aparezca dentro de la clase para los estudiantes y les dará acceso a todos los chats dentro del intercambio.",
- "addToClassOrExchangeDesc": "Añadir un chat a una clase o intercambio hará que el chat aparezca dentro de la clase o intercambio para los estudiantes y les dará acceso.",
- "invitedToClassOrExchange": "{user} te ha invitado a unirte a ¡{classOrExchange}! ¿Deseas aceptar?",
- "@invitedToClassOrExchange": {
- "placeholders": {
- "classOrExchange": {},
- "user": {}
- }
- },
"decline": "Disminución",
"declinedInvitation": "Invitación rechazada",
"acceptedInvitation": "Invitación aceptada",
@@ -4369,14 +4318,11 @@
},
"emptyChatNameWarning": "Introduzca un nombre para este chat",
"emptyClassNameWarning": "Introduzca un nombre para esta clase",
- "emptyExchangeNameWarning": "Introduzca un nombre para este intercambio",
"blurMeansTranslateTitle": "¿Por qué está borroso el mensaje?",
- "blurMeansTranslateBody": "Mientras el Modo Inmersión esté activado, los mensajes que se envíen en su idioma base aparecerán borrosos mientras Pangea Bot los traduce a su idioma de destino. El modo inmersión puede activarse en los ajustes individuales y de clase.",
"monthlySubscription": "Mensualmente",
"yearlySubscription": "Anualmente",
"defaultSubscription": "Suscripción al chat de Pangea",
"freeTrial": "Prueba gratuita",
- "languageLevelWarning": "Seleccione un nivel de idioma de clase",
"lockedChatWarning": "🔒 Este chat ha sido bloqueado",
"lockSpace": "Espacio de bloqueo",
"lockChat": "Chat de bloqueo",
@@ -4393,7 +4339,6 @@
"itStartDefaultPrompt": "¿Quiere ayuda para traducir?",
"suggestTo": "Sugerir a {spaceName}",
"suggestChatDesc": "Los chats sugeridos aparecerán en la lista de chats de {spaceName}.",
- "suggestExchangeDesc": "Los intercambios sugeridos aparecerán en la lista de chat de {spaceName}.",
"acceptSelection": "Aceptar corrección",
"acceptSelectionAnyway": "Use esto de todos modos",
"makingActivity": "Actividad de fabricación",
@@ -4460,7 +4405,6 @@
"groupCanBeFoundViaSearch": "El grupo se puede encontrar a través de la búsqueda",
"inNoSpaces": "No es miembro de ninguna clase o bolsa",
"createClass": "Crear clase",
- "createExchange": "Crear intercambio",
"viewArchive": "Ver archivo",
"trialExpiration": "Su prueba gratuita caduca el {expiration}.",
"@trialExpiration": {
@@ -4665,9 +4609,9 @@
"enterNumber": "Introduzca un valor numérico entero.",
"autoIGCToolName": "Ejecutar automáticamente la asistencia lingüística",
"autoIGCToolDescription": "Ejecutar automáticamente la asistencia lingüística después de escribir mensajes",
- "runGrammarCorrection": "Corregir la gramática",
+ "runGrammarCorrection": "Comprobar mensaje",
"grammarCorrectionFailed": "Cuestiones a tratar",
- "grammarCorrectionComplete": "Corrección gramatical completa",
+ "grammarCorrectionComplete": "¡Se ve bien!",
"leaveRoomDescription": "El chat se moverá al archivo. Los demás usuarios podrán ver que has abandonado el chat.",
"archiveSpaceDescription": "Todos los chats de este espacio se moverán al archivo para ti y otros usuarios que no sean administradores.",
"leaveSpaceDescription": "Todos los chats dentro de este espacio se moverán al archivo. Los demás usuarios podrán ver que has abandonado el espacio.",
@@ -4675,5 +4619,86 @@
"tooltipInstructionsTitle": "¿No sabes para qué sirve?",
"tooltipInstructionsMobileBody": "Mantenga pulsados los elementos para ver la información sobre herramientas.",
"tooltipInstructionsBrowserBody": "Pase el ratón sobre los elementos para ver información sobre herramientas.",
- "buildTranslation": "Construye tu traducción a partir de las opciones anteriores"
+ "buildTranslation": "Construye tu traducción a partir de las opciones anteriores",
+ "appLockDescription": "Bloquea la aplicación cuando no la uses con un código pin",
+ "swipeRightToLeftToReply": "Desliza el dedo de derecha a izquierda para responder",
+ "globalChatId": "ID global del chat",
+ "accessAndVisibility": "Acceso y visibilidad",
+ "accessAndVisibilityDescription": "Quién puede participar en este chat y cómo se puede descubrir el chat.",
+ "calls": "Llamadas",
+ "customEmojisAndStickers": "Emojis y pegatinas personalizados",
+ "customEmojisAndStickersBody": "Añade o comparte emojis o stickers personalizados que podrás utilizar en cualquier chat.",
+ "hideRedactedMessages": "Ocultar mensajes redactados",
+ "hideRedactedMessagesBody": "Si alguien borra un mensaje, éste ya no será visible en el chat.",
+ "hideInvalidOrUnknownMessageFormats": "Ocultar formatos de mensaje no válidos o desconocidos",
+ "hideMemberChangesInPublicChats": "Ocultar los cambios de los miembros en los chats públicos",
+ "hideMemberChangesInPublicChatsBody": "No mostrar en la línea de tiempo del chat si alguien se une o abandona un chat público para mejorar la legibilidad.",
+ "overview": "Visión general",
+ "notifyMeFor": "Notificarme para",
+ "passwordRecoverySettings": "Configuración de recuperación de contraseña",
+ "usersMustKnock": "Los usuarios deben golpear",
+ "userWouldLikeToChangeTheChat": "{user} desea unirse al chat.",
+ "@userWouldLikeToChangeTheChat": {
+ "placeholders": {
+ "user": {}
+ }
+ },
+ "noPublicLinkHasBeenCreatedYet": "Aún no se ha creado ningún enlace público",
+ "knock": "Knock",
+ "multiLingualSpace": "Espacio multilingüe",
+ "languageSettings": "Ajustes de idioma",
+ "languageSettingsDesc": "Editar idiomas espaciales y nivel de competencia.",
+ "selectSpaceDominantLanguage": "¿Cuál es la lengua más común de los miembros del espacio?",
+ "selectSpaceTargetLanguage": "¿Cuál es la lengua de destino más común del espacio?",
+ "whatIsYourSpaceLanguageLevel": "¿Cuál es el nivel lingüístico medio del espacio?",
+ "welcomeToPangea18Plus": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Crea o únete a un espacio!\n¡O busca un compañero de conversación!",
+ "welcomeToPangeaMinor": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Únete a un espacio!\nPide a tu profesor un código de invitación.",
+ "addToSpaceDesc": "Añadir un chat a un espacio hará que el chat aparezca dentro del espacio para los estudiantes y les dará acceso.",
+ "invitedToSpace": "{user} te ha invitado a unirte a un espacio: ¡{space}! ¿Deseas aceptar?",
+ "@invitedToSpace": {
+ "placeholders": {
+ "space": {},
+ "user": {}
+ }
+ },
+ "emptySpaceNameWarning": "Introduzca un nombre para este espacio",
+ "blurMeansTranslateBody": "Mientras el Modo Inmersión esté activado, los mensajes que se envíen en tu idioma base aparecerán borrosos mientras Pangea Bot los traduce a tu idioma de destino. El Modo Inmersión puede activarse en los ajustes individuales y espaciales.",
+ "languageLevelWarning": "Seleccione un nivel de lengua espacial",
+ "knocking": "Golpeando",
+ "chatCanBeDiscoveredViaSearchOnServer": "El chat puede descubrirse mediante la búsqueda en {server}.",
+ "@chatCanBeDiscoveredViaSearchOnServer": {
+ "type": "text",
+ "placeholders": {
+ "server": {}
+ }
+ },
+ "publicChatAddresses": "Direcciones de chat públicas",
+ "createNewAddress": "Crear una nueva dirección",
+ "userRole": "Función del usuario",
+ "minimumPowerLevel": "{level} es el nivel mínimo de potencia.",
+ "@minimumPowerLevel": {
+ "type": "text",
+ "placeholders": {
+ "level": {}
+ }
+ },
+ "searchMore": "Buscar más...",
+ "gallery": "Galería",
+ "files": "Archivos",
+ "addSpaceToSpaceDescription": "Seleccione un espacio para añadir como padre",
+ "noDatabaseEncryption": "La encriptación de la base de datos no es compatible con esta plataforma",
+ "thereAreCountUsersBlocked": "Ahora mismo hay {count} usuarios bloqueados.",
+ "@thereAreCountUsersBlocked": {
+ "type": "text",
+ "count": {}
+ },
+ "restricted": "Restringido",
+ "knockRestricted": "Golpe restringido",
+ "nonexistentSelection": "La selección ya no existe.",
+ "cantAddSpaceChild": "No tiene permiso para añadir un niño a este espacio.",
+ "roomAddedToSpace": "Se han añadido habitaciones al espacio seleccionado.",
+ "addChatToSpaceDesc": "Añadir un chat a un espacio hará que el chat aparezca dentro del espacio para los estudiantes y les dará acceso.",
+ "addSpaceToSpaceDesc": "Añadir un espacio a otro espacio hará que el espacio hijo aparezca dentro del espacio padre para los estudiantes y les dará acceso.",
+ "spaceAnalytics": "Analítica espacial",
+ "changeAnalyticsLanguage": "Cambiar el lenguaje analítico"
}
\ No newline at end of file
diff --git a/lib/config/routes.dart b/lib/config/routes.dart
index debc48968..eba1ae511 100644
--- a/lib/config/routes.dart
+++ b/lib/config/routes.dart
@@ -29,7 +29,6 @@ import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart';
import 'package:fluffychat/pangea/guard/p_vguard.dart';
import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart';
-import 'package:fluffychat/pangea/pages/exchange/add_exchange_to_class.dart';
import 'package:fluffychat/pangea/pages/find_partner/find_partner.dart';
import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart';
import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart';
@@ -43,8 +42,8 @@ import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
-import '../pangea/pages/analytics/class_analytics/class_analytics.dart';
-import '../pangea/pages/analytics/class_list/class_list.dart';
+import '../pangea/pages/analytics/space_analytics/space_analytics.dart';
+import '../pangea/pages/analytics/space_list/space_list.dart';
abstract class AppRoutes {
static FutureOr loggedInRedirect(
@@ -202,17 +201,17 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const AnalyticsClassList(),
+ const AnalyticsSpaceList(),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
- path: ':classid',
+ path: ':spaceid',
redirect: loggedOutRedirect,
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const ClassAnalyticsPage(),
+ const SpaceAnalyticsPage(),
),
routes: [
GoRoute(
@@ -220,7 +219,7 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const ClassAnalyticsPage(
+ const SpaceAnalyticsPage(
selectedView: BarChartViewSelection.messages,
),
),
@@ -230,7 +229,7 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const ClassAnalyticsPage(
+ const SpaceAnalyticsPage(
selectedView: BarChartViewSelection.grammar,
),
),
@@ -311,24 +310,6 @@ abstract class AppRoutes {
redirect: loggedOutRedirect,
),
// #Pangea
- GoRoute(
- path: 'newspace/:newexchange',
- pageBuilder: (context, state) => defaultPageBuilder(
- context,
- state,
- const NewSpace(),
- ),
- redirect: loggedOutRedirect,
- ),
- GoRoute(
- path: 'join_exchange/:exchangeid',
- pageBuilder: (context, state) => defaultPageBuilder(
- context,
- state,
- const AddExchangeToClass(),
- ),
- redirect: loggedOutRedirect,
- ),
GoRoute(
path: 'partner',
pageBuilder: (context, state) => defaultPageBuilder(
diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart
index b199a1b67..13f09ffb6 100644
--- a/lib/pages/chat/chat.dart
+++ b/lib/pages/chat/chat.dart
@@ -20,8 +20,8 @@ import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/choreo_record.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
@@ -317,10 +317,10 @@ class ChatController extends State
Future.delayed(const Duration(seconds: 1), () async {
if (!mounted) return;
debugPrint(
- "chat.dart l1 ${pangeaController.languageController.activeL1Code(roomID: roomId)}",
+ "chat.dart l1 ${pangeaController.languageController.userL1?.langCode}",
);
debugPrint(
- "chat.dart l2 ${pangeaController.languageController.activeL2Code(roomID: roomId)}",
+ "chat.dart l2 ${pangeaController.languageController.userL2?.langCode}",
);
if (mounted) {
pangeaController.languageController.showDialogOnEmptyLanguage(
@@ -654,8 +654,6 @@ class ChatController extends State
);
return;
}
- // ensure that analytics room exists / is created for the active langCode
- await room.ensureAnalyticsRoomExists();
},
onError: (err, stack) => ErrorHandler.logError(e: err, s: stack),
);
diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart
index dc129a192..c5756438c 100644
--- a/lib/pages/chat/events/message.dart
+++ b/lib/pages/chat/events/message.dart
@@ -3,6 +3,7 @@ import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
+import 'package:fluffychat/pangea/widgets/chat/message_buttons.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/string_color.dart';
@@ -524,7 +525,14 @@ class Message extends StatelessWidget {
Widget container;
final showReceiptsRow =
event.hasAggregatedEvents(timeline, RelationshipTypes.reaction);
- if (showReceiptsRow || displayTime || selected || displayReadMarker) {
+ // #Pangea
+ // if (showReceiptsRow || displayTime || selected || displayReadMarker) {
+ if (showReceiptsRow ||
+ displayTime ||
+ selected ||
+ displayReadMarker ||
+ (pangeaMessageEvent?.showMessageButtons ?? false)) {
+ // Pangea#
container = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
@@ -561,7 +569,11 @@ class Message extends StatelessWidget {
AnimatedSize(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
- child: !showReceiptsRow
+ // #Pangea
+ child: !showReceiptsRow &&
+ !(pangeaMessageEvent?.showMessageButtons ?? false)
+ // child: !showReceiptsRow
+ // Pangea#
? const SizedBox.shrink()
: Padding(
padding: EdgeInsets.only(
@@ -569,7 +581,19 @@ class Message extends StatelessWidget {
left: (ownMessage ? 0 : Avatar.defaultSize) + 12.0,
right: ownMessage ? 0 : 12.0,
),
- child: MessageReactions(event, timeline),
+ // #Pangea
+ child: Row(
+ mainAxisAlignment: ownMessage
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.start,
+ children: [
+ if (pangeaMessageEvent?.showMessageButtons ?? false)
+ MessageButtons(toolbarController: toolbarController),
+ MessageReactions(event, timeline),
+ ],
+ ),
+ // child: MessageReactions(event, timeline),
+ // Pangea#
),
),
if (displayReadMarker)
diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart
index 93496beee..4f4e091d0 100644
--- a/lib/pages/chat_details/chat_details_view.dart
+++ b/lib/pages/chat_details/chat_details_view.dart
@@ -11,10 +11,8 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_nam
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart';
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart';
import 'package:fluffychat/pangea/utils/lock_room.dart';
-import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart';
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart';
-import 'package:fluffychat/pangea/widgets/space/class_settings.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart';
@@ -105,7 +103,7 @@ class ChatDetailsView extends StatelessWidget {
),
body: MaxWidthBody(
// #Pangea
- // chat description title has its own scrollbar so we disable the parent one
+ // Chat description title has its own scrollbar so we disable the parent one
// otherwise they scroll with each other
child: ScrollConfiguration(
behavior:
@@ -265,11 +263,10 @@ class ChatDetailsView extends StatelessWidget {
controller: controller,
),
// Pangea#
- if ((room.isPangeaClass || room.isExchange) &&
- room.isRoomAdmin)
+ if (room.isSpace && room.isRoomAdmin)
ListTile(
title: Text(
- L10n.of(context)!.classAnalytics,
+ L10n.of(context)!.spaceAnalytics,
style: TextStyle(
color:
Theme.of(context).colorScheme.secondary,
@@ -288,11 +285,12 @@ class ChatDetailsView extends StatelessWidget {
'/rooms/analytics/${room.id}',
),
),
- if (room.classSettings != null && room.isRoomAdmin)
- ClassSettings(
- roomId: controller.roomId,
- startOpen: false,
- ),
+ // commenting out language settings in spaces for now
+ // if (room.languageSettings != null && room.isRoomAdmin)
+ // LanguageSettings(
+ // roomId: controller.roomId,
+ // startOpen: false,
+ // ),
if (room.pangeaRoomRules != null)
RoomRulesEditor(
roomId: controller.roomId,
@@ -474,16 +472,11 @@ class ChatDetailsView extends StatelessWidget {
room: room,
),
const Divider(height: 1),
- if (!room.isPangeaClass &&
- !room.isDirectChat &&
- room.isRoomAdmin)
+ if (!room.isDirectChat && room.isRoomAdmin)
AddToSpaceToggles(
roomId: room.id,
key: controller.addToSpaceKey,
startOpen: false,
- mode: room.isExchange
- ? AddToClassMode.exchange
- : AddToClassMode.chat,
),
const Divider(height: 1),
if (!room.isDirectChat)
diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart
index 9d7ff2440..569eba880 100644
--- a/lib/pages/chat_list/chat_list.dart
+++ b/lib/pages/chat_list/chat_list.dart
@@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:io';
import 'package:adaptive_dialog/adaptive_dialog.dart';
-import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
@@ -213,32 +212,12 @@ class ChatListController extends State
}
List get filteredRooms => Matrix.of(context)
- .client
- .rooms
- .where(
- getRoomFilterByActiveFilter(activeFilter),
- )
- // #Pangea
- .sorted((roomA, roomB) {
- // put rooms with unread messages at the top of the list
- if (roomA.membership == Membership.invite &&
- roomB.membership != Membership.invite) {
- return -1;
- }
- if (roomA.membership != Membership.invite &&
- roomB.membership == Membership.invite) {
- return 1;
- }
-
- final bool aUnread = roomA.notificationCount > 0 || roomA.markedUnread;
- final bool bUnread = roomB.notificationCount > 0 || roomB.markedUnread;
- if (aUnread && !bUnread) return -1;
- if (!aUnread && bUnread) return 1;
-
- return 0;
- })
- // Pangea#
- .toList();
+ .client
+ .rooms
+ .where(
+ getRoomFilterByActiveFilter(activeFilter),
+ )
+ .toList();
bool isSearchMode = false;
Future? publicRoomsResponse;
diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart
index 56aa568c0..4ad107f6b 100644
--- a/lib/pages/chat_list/client_chooser_button.dart
+++ b/lib/pages/chat_list/client_chooser_button.dart
@@ -1,8 +1,9 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:fluffychat/pangea/constants/class_default_values.dart';
-import 'package:fluffychat/pangea/utils/class_code.dart';
+import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart';
import 'package:fluffychat/pangea/utils/logout.dart';
+import 'package:fluffychat/pangea/utils/space_code.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -58,17 +59,19 @@ class ClientChooserButton extends StatelessWidget {
room.isSpace &&
room.ownPowerLevel >= ClassDefaultValues.powerLevelOfAdmin,
),
- value: SettingsAction.classAnalytics,
+ value: SettingsAction.spaceAnalytics,
child: Row(
children: [
const Icon(Icons.analytics_outlined),
const SizedBox(width: 18),
- Expanded(child: Text(L10n.of(context)!.classAnalytics)),
+ Expanded(child: Text(L10n.of(context)!.spaceAnalytics)),
],
),
),
PopupMenuItem(
- enabled: matrix.client.rooms.isNotEmpty,
+ enabled: matrix.client.rooms.any(
+ (room) => !room.isSpace && !room.isArchived && !room.isAnalyticsRoom,
+ ),
value: SettingsAction.myAnalytics,
child: Row(
children: [
@@ -84,7 +87,7 @@ class ClientChooserButton extends StatelessWidget {
children: [
const Icon(Icons.school),
const SizedBox(width: 18),
- Expanded(child: Text(L10n.of(context)!.createNewClass)),
+ Expanded(child: Text(L10n.of(context)!.createNewSpace)),
],
),
),
@@ -98,16 +101,6 @@ class ClientChooserButton extends StatelessWidget {
// ],
// ),
// ),
- PopupMenuItem(
- value: SettingsAction.newExchange,
- child: Row(
- children: [
- const Icon(Icons.connecting_airports),
- const SizedBox(width: 18),
- Expanded(child: Text(L10n.of(context)!.newExchange)),
- ],
- ),
- ),
if (controller.pangeaController.permissionsController.isUser18())
PopupMenuItem(
value: SettingsAction.findAConversationPartner,
@@ -397,11 +390,8 @@ class ClientChooserButton extends StatelessWidget {
case SettingsAction.newClass:
context.go('/rooms/newspace');
break;
- case SettingsAction.newExchange:
- context.go('/rooms/newspace/exchange');
- break;
case SettingsAction.joinWithClassCode:
- ClassCodeUtil.joinWithClassCodeDialog(
+ SpaceCodeUtil.joinWithSpaceCodeDialog(
context,
controller.pangeaController,
);
@@ -412,7 +402,7 @@ class ClientChooserButton extends StatelessWidget {
controller.pangeaController,
);
break;
- case SettingsAction.classAnalytics:
+ case SettingsAction.spaceAnalytics:
context.go('/rooms/analytics');
break;
case SettingsAction.myAnalytics:
@@ -507,11 +497,10 @@ enum SettingsAction {
// #Pangea
learning,
joinWithClassCode,
- classAnalytics,
+ spaceAnalytics,
myAnalytics,
findAConversationPartner,
logout,
newClass,
- newExchange
// Pangea#
}
diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart
index ea81c842c..2b1dff187 100644
--- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart
+++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart
@@ -35,11 +35,26 @@ class ChatPermissionsSettingsView extends StatelessWidget {
final powerLevels = Map.from(powerLevelsContent)
// #Pangea
// ..removeWhere((k, v) => v is! int);
- ..removeWhere((k, v) => v is! int || k.equals("m.call.invite"));
+ ..removeWhere(
+ (k, v) =>
+ v is! int ||
+ k.equals("m.call.invite") ||
+ k.equals("historical") ||
+ k.equals("state_default"),
+ );
// Pangea#
final eventsPowerLevels = Map.from(
powerLevelsContent.tryGetMap('events') ?? {},
- )..removeWhere((k, v) => v is! int);
+ // #Pangea
+ )..removeWhere(
+ (k, v) =>
+ v is! int ||
+ k.equals("m.space.child") ||
+ k.equals("pangea.usranalytics") ||
+ k.equals(EventTypes.RoomPowerLevels),
+ );
+ // )..removeWhere((k, v) => v is! int);
+ // Pangea#
return Column(
children: [
Column(
@@ -57,51 +72,59 @@ class ChatPermissionsSettingsView extends StatelessWidget {
),
canEdit: room.canChangePowerLevel,
),
- Divider(color: Theme.of(context).dividerColor),
- ListTile(
- title: Text(
- L10n.of(context)!.notifications,
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- Builder(
- builder: (context) {
- const key = 'rooms';
- final value = powerLevelsContent
- .containsKey('notifications')
- ? powerLevelsContent
- .tryGetMap('notifications')
- ?.tryGet('rooms') ??
- 0
- : 0;
- return PermissionsListTile(
- permissionKey: key,
- permission: value,
- category: 'notifications',
- canEdit: room.canChangePowerLevel,
- onChanged: (level) => controller.editPowerLevel(
- context,
- key,
- value,
- newLevel: level,
- category: 'notifications',
+ // #Pangea
+ // Why would teacher need to stop students from seeing notifications?
+ // Divider(color: Theme.of(context).dividerColor),
+ // ListTile(
+ // title: Text(
+ // L10n.of(context)!.notifications,
+ // style: TextStyle(
+ // color: Theme.of(context).colorScheme.primary,
+ // fontWeight: FontWeight.bold,
+ // ),
+ // ),
+ // ),
+ // Builder(
+ // builder: (context) {
+ // const key = 'rooms';
+ // final value = powerLevelsContent
+ // .containsKey('notifications')
+ // ? powerLevelsContent
+ // .tryGetMap('notifications')
+ // ?.tryGet('rooms') ??
+ // 0
+ // : 0;
+ // return PermissionsListTile(
+ // permissionKey: key,
+ // permission: value,
+ // category: 'notifications',
+ // canEdit: room.canChangePowerLevel,
+ // onChanged: (level) => controller.editPowerLevel(
+ // context,
+ // key,
+ // value,
+ // newLevel: level,
+ // category: 'notifications',
+ // ),
+ // );
+ // },
+ // ),
+ // Only show if there are actually items in this category
+ if (eventsPowerLevels.isNotEmpty)
+ // Pangea#
+ Divider(color: Theme.of(context).dividerColor),
+ // #Pangea
+ if (eventsPowerLevels.isNotEmpty)
+ // Pangea#
+ ListTile(
+ title: Text(
+ L10n.of(context)!.configureChat,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ fontWeight: FontWeight.bold,
),
- );
- },
- ),
- Divider(color: Theme.of(context).dividerColor),
- ListTile(
- title: Text(
- L10n.of(context)!.configureChat,
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
- fontWeight: FontWeight.bold,
),
),
- ),
for (final entry in eventsPowerLevels.entries)
PermissionsListTile(
permissionKey: entry.key,
diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart
index b3a1703af..7e77efe56 100644
--- a/lib/pages/new_group/new_group.dart
+++ b/lib/pages/new_group/new_group.dart
@@ -174,7 +174,7 @@ class NewGroupController extends State {
void initState() {
Future.delayed(Duration.zero, () {
chatTopic.langCode =
- pangeaController.languageController.activeL2Code(roomID: null) ??
+ pangeaController.languageController.userL2?.langCode ??
pangeaController.pLanguageStore.targetOptions.first.langCode;
setState(() {});
});
diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart
index 277a7402f..b8b43c3f1 100644
--- a/lib/pages/new_group/new_group_view.dart
+++ b/lib/pages/new_group/new_group_view.dart
@@ -1,7 +1,6 @@
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/new_group/new_group.dart';
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart';
-import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart';
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
@@ -100,7 +99,6 @@ class NewGroupView extends StatelessWidget {
key: controller.addToSpaceKey,
startOpen: true,
activeSpaceId: controller.activeSpaceId,
- mode: AddToClassMode.chat,
),
// const SizedBox(height: 16),
// SwitchListTile.adaptive(
diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart
index 59818d3b2..7a7759349 100644
--- a/lib/pages/new_space/new_space.dart
+++ b/lib/pages/new_space/new_space.dart
@@ -8,16 +8,14 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capa
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart';
import 'package:fluffychat/pangea/utils/bot_name.dart';
import 'package:fluffychat/pangea/utils/class_chat_power_levels.dart';
-import 'package:fluffychat/pangea/utils/class_code.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
+import 'package:fluffychat/pangea/utils/space_code.dart';
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
-import 'package:fluffychat/pangea/widgets/space/class_settings.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
-import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
@@ -36,8 +34,9 @@ class NewSpaceController extends State {
bool publicGroup = true;
final GlobalKey rulesEditorKey = GlobalKey();
final GlobalKey addToSpaceKey = GlobalKey();
- final GlobalKey classSettingsKey =
- GlobalKey();
+ // commenting out language settings in spaces for now
+ // final GlobalKey languageSettingsKey =
+ // GlobalKey();
final GlobalKey addCapacityKey =
GlobalKey();
@@ -68,8 +67,6 @@ class NewSpaceController extends State {
void setPublicGroup(bool b) => setState(() => publicGroup = b);
// #Pangea
- bool newClassMode = true;
-
List get initialState {
final events = [];
@@ -95,11 +92,11 @@ class NewSpaceController extends State {
} else {
debugger(when: kDebugMode);
}
- if (classSettingsKey.currentState != null) {
- events.add(classSettingsKey.currentState!.classSettings.toStateEvent);
- } else {
- debugger(when: kDebugMode && newClassMode);
- }
+ // commenting out language settings in spaces for now
+ // if (languageSettingsKey.currentState != null) {
+ // events
+ // .add(languageSettingsKey.currentState!.languageSettings.toStateEvent);
+ // }
return events;
}
@@ -117,33 +114,30 @@ class NewSpaceController extends State {
debugger(when: kDebugMode);
return;
}
- if (classSettingsKey.currentState != null &&
- classSettingsKey.currentState!.sameLanguages) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(L10n.of(context)!.noIdenticalLanguages),
- ),
- );
- return;
- }
- if (newClassMode) {
- final int? languageLevel =
- classSettingsKey.currentState!.classSettings.languageLevel;
- if (languageLevel == null) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(L10n.of(context)!.languageLevelWarning)),
- );
- return;
- }
- }
+ // commenting out language settings in spaces for now
+ // if (languageSettingsKey.currentState != null &&
+ // languageSettingsKey.currentState!.sameLanguages) {
+ // ScaffoldMessenger.of(context).showSnackBar(
+ // SnackBar(
+ // content: Text(L10n.of(context)!.noIdenticalLanguages),
+ // ),
+ // );
+ // return;
+ // }
+ // final int? languageLevel =
+ // languageSettingsKey.currentState!.languageSettings.languageLevel;
+ // if (languageLevel == null) {
+ // ScaffoldMessenger.of(context).showSnackBar(
+ // SnackBar(content: Text(L10n.of(context)!.languageLevelWarning)),
+ // );
+ // return;
+ // }
// Pangea#
if (nameController.text.isEmpty) {
setState(() {
// #Pangea
// nameError = L10n.of(context)!.pleaseChoose;
- final String warning = newClassMode
- ? L10n.of(context)!.emptyClassNameWarning
- : L10n.of(context)!.emptyExchangeNameWarning;
+ final String warning = L10n.of(context)!.emptySpaceNameWarning;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(warning)),
);
@@ -168,7 +162,7 @@ class NewSpaceController extends State {
// roomAliasName: publicGroup
// ? nameController.text.trim().toLowerCase().replaceAll(' ', '_')
// : null,
- roomAliasName: ClassCodeUtil.generateClassCode(),
+ roomAliasName: SpaceCodeUtil.generateSpaceCode(),
// Pangea#
name: nameController.text.trim(),
topic: topicController.text.isEmpty ? null : topicController.text,
@@ -224,15 +218,10 @@ class NewSpaceController extends State {
}
room.setSpaceChild(newChatRoomId, suggested: true);
- newClassMode
- ? GoogleAnalytics.addParent(
- newChatRoomId,
- room.classCode,
- )
- : GoogleAnalytics.addChatToExchange(
- newChatRoomId,
- room.classCode,
- );
+ GoogleAnalytics.addParent(
+ newChatRoomId,
+ room.classCode,
+ );
GoogleAnalytics.createClass(room.name, room.classCode);
try {
@@ -267,8 +256,6 @@ class NewSpaceController extends State {
// #Pangea
// Widget build(BuildContext context) => NewSpaceView(this);
Widget build(BuildContext context) {
- newClassMode =
- GoRouterState.of(context).pathParameters['newexchange'] != 'exchange';
return NewSpaceView(this);
}
// Pangea#
diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart
index 09abb7066..f8c7baf4e 100644
--- a/lib/pages/new_space/new_space_view.dart
+++ b/lib/pages/new_space/new_space_view.dart
@@ -1,17 +1,14 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart';
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart';
-import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart';
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
-import 'package:fluffychat/pangea/widgets/space/class_settings.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
-import 'package:go_router/go_router.dart';
import 'new_space.dart';
@@ -32,29 +29,8 @@ class NewSpaceView extends StatelessWidget {
appBar: AppBar(
// #Pangea
centerTitle: true,
- title: Text(
- controller.newClassMode
- ? L10n.of(context)!.createNewClass
- : L10n.of(context)!.newExchange,
- ),
- actions: [
- IconButton(
- icon: const Icon(Icons.class_outlined),
- selectedIcon: const Icon(Icons.class_),
- color: controller.newClassMode ? activeColor : null,
- isSelected: controller.newClassMode,
- onPressed: () => context.go('/rooms/newspace'),
- ),
- IconButton(
- icon: const Icon(Icons.connecting_airports),
- selectedIcon: const Icon(Icons.connecting_airports),
- color: !controller.newClassMode ? activeColor : null,
- isSelected: !controller.newClassMode,
- onPressed: () => context.go('/rooms/newspace/exchange'),
- ),
- ],
- // title: Text(L10n.of(context)!.createNewSpace),
// Pangea#
+ title: Text(L10n.of(context)!.createNewSpace),
),
body: MaxWidthBody(
child: Column(
@@ -135,42 +111,48 @@ class NewSpaceView extends StatelessWidget {
RoomCapacityButton(
key: controller.addCapacityKey,
),
- if (controller.newClassMode)
- ClassSettings(
- key: controller.classSettingsKey,
- roomId: null,
- startOpen: true,
- initialSettings:
- Matrix.of(context).client.lastUpdatedClassSettings,
- ),
- if (!controller.newClassMode)
- AddToSpaceToggles(
- key: controller.addToSpaceKey,
- startOpen: true,
- mode: !controller.newClassMode
- ? AddToClassMode.exchange
- : AddToClassMode.chat,
- ),
- FutureBuilder(
- future: Matrix.of(context).client.lastUpdatedRoomRules,
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.done) {
- return RoomRulesEditor(
- key: controller.rulesEditorKey,
- roomId: null,
- startOpen: false,
- initialRules: snapshot.data,
- );
- } else {
- return const Padding(
- padding: EdgeInsets.all(16.0),
- child: Center(
- child: CircularProgressIndicator.adaptive(strokeWidth: 2),
- ),
- );
- }
- },
+ // commenting out language settings in spaces for now
+ // LanguageSettings(
+ // key: controller.languageSettingsKey,
+ // roomId: null,
+ // startOpen: true,
+ // initialSettings:
+ // Matrix.of(context).client.lastUpdatedLanguageSettings,
+ // ),
+ AddToSpaceToggles(
+ key: controller.addToSpaceKey,
+ startOpen: true,
+ spaceMode: true,
),
+ if (controller.rulesEditorKey.currentState != null)
+ RoomRulesEditor(
+ key: controller.rulesEditorKey,
+ roomId: null,
+ startOpen: false,
+ initialRules: controller.rulesEditorKey.currentState!.rules,
+ ),
+ if (controller.rulesEditorKey.currentState == null)
+ FutureBuilder(
+ future: Matrix.of(context).client.lastUpdatedRoomRules,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return RoomRulesEditor(
+ key: controller.rulesEditorKey,
+ roomId: null,
+ startOpen: false,
+ initialRules: snapshot.data,
+ );
+ } else {
+ return const Padding(
+ padding: EdgeInsets.all(16.0),
+ child: Center(
+ child:
+ CircularProgressIndicator.adaptive(strokeWidth: 2),
+ ),
+ );
+ }
+ },
+ ),
// SwitchListTile.adaptive(
// title: Text(L10n.of(context)!.spaceIsPublic),
// value: controller.publicGroup,
@@ -194,12 +176,7 @@ class NewSpaceView extends StatelessWidget {
children: [
Expanded(
child: Text(
- // #Pangea
- // L10n.of(context)!.createNewSpace,
- controller.newClassMode
- ? L10n.of(context)!.createClass
- : L10n.of(context)!.createExchange,
- // Pangea#
+ L10n.of(context)!.createNewSpace,
),
),
Icon(Icons.adaptive.arrow_forward_outlined),
diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart
index c4a57a267..9ff495328 100644
--- a/lib/pages/settings/settings_view.dart
+++ b/lib/pages/settings/settings_view.dart
@@ -194,6 +194,12 @@ class SettingsView extends StatelessWidget {
Icons.chevron_right_outlined,
),
),
+ ListTile(
+ leading: const Icon(Icons.psychology_outlined),
+ title: Text(L10n.of(context)!.learningSettings),
+ onTap: () => context.go('/rooms/settings/learning'),
+ trailing: const Icon(Icons.chevron_right_outlined),
+ ),
// Pangea#
ListTile(
leading: const Icon(Icons.shield_outlined),
diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart
index 3d84b0e1d..a0c6a5f6b 100644
--- a/lib/pangea/choreographer/controllers/choreographer.dart
+++ b/lib/pangea/choreographer/controllers/choreographer.dart
@@ -6,15 +6,14 @@ import 'package:fluffychat/pangea/choreographer/controllers/alternative_translat
import 'package:fluffychat/pangea/choreographer/controllers/igc_controller.dart';
import 'package:fluffychat/pangea/choreographer/controllers/message_options.dart';
import 'package:fluffychat/pangea/constants/language_keys.dart';
-import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
+import 'package:fluffychat/pangea/enum/assistance_state_enum.dart';
import 'package:fluffychat/pangea/enum/edit_type.dart';
-import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/it_step.dart';
import 'package:fluffychat/pangea/models/language_detection_model.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
@@ -443,25 +442,15 @@ class Choreographer {
}
LanguageModel? get l2Lang {
- return pangeaController.languageController.activeL2Model(
- roomID: roomId,
- );
+ return pangeaController.languageController.activeL2Model();
}
String? get l2LangCode => l2Lang?.langCode;
LanguageModel? get l1Lang =>
- pangeaController.languageController.activeL1Model(
- roomID: roomId,
- );
- String? get l1LangCode => l1Lang?.langCode;
+ pangeaController.languageController.activeL1Model();
- String? get classId => roomId != null
- ? pangeaController.matrixState.client
- .getRoomById(roomId)
- ?.firstParentWithState(PangeaEventTypes.classSettings)
- ?.id
- : null;
+ String? get l1LangCode => l1Lang?.langCode;
String? get userId => pangeaController.userController.userId;
@@ -582,13 +571,3 @@ class Choreographer {
return AssistanceState.complete;
}
}
-
-// assistance state is, user has not typed a message, user has typed a message and IGC has not run,
-// IGC is running, IGC has run and there are remaining steps (either IT or IGC), or all steps are done
-enum AssistanceState {
- noMessage,
- notFetched,
- fetching,
- fetched,
- complete,
-}
diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart
index 61c8c2312..632db7e9b 100644
--- a/lib/pangea/choreographer/controllers/it_controller.dart
+++ b/lib/pangea/choreographer/controllers/it_controller.dart
@@ -265,7 +265,6 @@ class ITController {
targetLangCode: targetLangCode,
userId: choreographer.userId!,
roomId: choreographer.roomId!,
- classId: choreographer.classId,
goldTranslation: goldRouteTracker.fullTranslation,
goldContinuances: goldRouteTracker.continuances,
),
@@ -283,7 +282,6 @@ class ITController {
translationId: translationId,
targetLangCode: targetLangCode,
sourceLangCode: sourceLangCode,
- classId: choreographer.classId,
),
);
diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart
index 54fd601b9..c26fd706d 100644
--- a/lib/pangea/choreographer/widgets/choice_array.dart
+++ b/lib/pangea/choreographer/widgets/choice_array.dart
@@ -3,9 +3,7 @@ import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
-
import 'package:flutter_gen/gen_l10n/l10n.dart';
-import 'package:matrix/matrix.dart';
import '../../utils/bot_style.dart';
import 'it_shimmer.dart';
@@ -18,6 +16,10 @@ class ChoicesArray extends StatelessWidget {
final int? selectedChoiceIndex;
final String originalSpan;
final String Function(int) uniqueKeyForLayerLink;
+
+ /// some uses of this widget want to disable the choices
+ final bool isActive;
+
const ChoicesArray({
super.key,
required this.isLoading,
@@ -26,6 +28,7 @@ class ChoicesArray extends StatelessWidget {
required this.originalSpan,
required this.uniqueKeyForLayerLink,
required this.selectedChoiceIndex,
+ this.isActive = true,
this.onLongPress,
});
@@ -42,8 +45,8 @@ class ChoicesArray extends StatelessWidget {
.map(
(entry) => ChoiceItem(
theme: theme,
- onLongPress: onLongPress,
- onPressed: onPressed,
+ onLongPress: isActive ? onLongPress : null,
+ onPressed: isActive ? onPressed : (_) {},
entry: entry,
isSelected: selectedChoiceIndex == entry.key,
),
@@ -109,19 +112,19 @@ class ChoiceItem extends StatelessWidget {
: null,
child: TextButton(
style: ButtonStyle(
- padding: MaterialStateProperty.all(
+ padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(horizontal: 7),
),
//if index is selected, then give the background a slight primary color
- backgroundColor: MaterialStateProperty.all(
+ backgroundColor: WidgetStateProperty.all(
entry.value.color != null
? entry.value.color!.withOpacity(0.2)
: theme.colorScheme.primary.withOpacity(0.1),
),
- textStyle: MaterialStateProperty.all(
+ textStyle: WidgetStateProperty.all(
BotStyle.text(context),
),
- shape: MaterialStateProperty.all(
+ shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
@@ -177,21 +180,21 @@ class ChoiceAnimationWidgetState extends State
);
_animation = widget.isGold
- ? Tween(begin: 1.0, end: 1.2).animate(_controller)
- : TweenSequence([
- TweenSequenceItem(
- tween: Tween(begin: 0, end: -8 * pi / 180),
- weight: 1.0,
- ),
- TweenSequenceItem(
- tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180),
- weight: 2.0,
- ),
- TweenSequenceItem(
- tween: Tween(begin: 16 * pi / 180, end: 0),
- weight: 1.0,
- ),
- ]).animate(_controller);
+ ? Tween(begin: 1.0, end: 1.2).animate(_controller)
+ : TweenSequence([
+ TweenSequenceItem(
+ tween: Tween(begin: 0, end: -8 * pi / 180),
+ weight: 1.0,
+ ),
+ TweenSequenceItem(
+ tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180),
+ weight: 2.0,
+ ),
+ TweenSequenceItem(
+ tween: Tween(begin: 16 * pi / 180, end: 0),
+ weight: 1.0,
+ ),
+ ]).animate(_controller);
if (widget.selected && !animationPlayed) {
_controller.forward();
@@ -221,28 +224,28 @@ class ChoiceAnimationWidgetState extends State
@override
Widget build(BuildContext context) {
return widget.isGold
- ? AnimatedBuilder(
- key: UniqueKey(),
- animation: _animation,
- builder: (context, child) {
- return Transform.scale(
- scale: _animation.value,
- child: child,
- );
- },
- child: widget.child,
- )
- : AnimatedBuilder(
- key: UniqueKey(),
- animation: _animation,
- builder: (context, child) {
- return Transform.rotate(
- angle: _animation.value,
- child: child,
- );
- },
- child: widget.child,
- );
+ ? AnimatedBuilder(
+ key: UniqueKey(),
+ animation: _animation,
+ builder: (context, child) {
+ return Transform.scale(
+ scale: _animation.value,
+ child: child,
+ );
+ },
+ child: widget.child,
+ )
+ : AnimatedBuilder(
+ key: UniqueKey(),
+ animation: _animation,
+ builder: (context, child) {
+ return Transform.rotate(
+ angle: _animation.value,
+ child: child,
+ );
+ },
+ child: widget.child,
+ );
}
@override
diff --git a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart
index 9d3302910..0abe90925 100644
--- a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart
+++ b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart
@@ -2,7 +2,7 @@ import 'dart:developer';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
diff --git a/lib/pangea/choreographer/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart
index ceb5af193..877158dd2 100644
--- a/lib/pangea/choreographer/widgets/start_igc_button.dart
+++ b/lib/pangea/choreographer/widgets/start_igc_button.dart
@@ -1,10 +1,10 @@
import 'dart:async';
import 'dart:math' as math;
-import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
-import 'package:fluffychat/pangea/constants/colors.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
+import 'package:fluffychat/pangea/enum/assistance_state_enum.dart';
+import 'package:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@@ -53,15 +53,15 @@ class StartIGCButtonState extends State
setState(() => prevState = assistanceState);
}
+ bool get itEnabled => widget.controller.choreographer.itEnabled;
+ bool get igcEnabled => widget.controller.choreographer.igcEnabled;
+ CanSendStatus get canSendStatus =>
+ widget.controller.pangeaController.subscriptionController.canSendStatus;
+ bool get grammarCorrectionEnabled =>
+ (itEnabled || igcEnabled) && canSendStatus == CanSendStatus.subscribed;
+
@override
Widget build(BuildContext context) {
- final bool itEnabled = widget.controller.choreographer.itEnabled;
- final bool igcEnabled = widget.controller.choreographer.igcEnabled;
- final CanSendStatus canSendStatus =
- widget.controller.pangeaController.subscriptionController.canSendStatus;
- final bool grammarCorrectionEnabled =
- (itEnabled || igcEnabled) && canSendStatus == CanSendStatus.subscribed;
-
if (!grammarCorrectionEnabled ||
widget.controller.choreographer.isAutoIGCEnabled ||
widget.controller.choreographer.choreoMode == ChoreoMode.it) {
@@ -77,92 +77,67 @@ class StartIGCButtonState extends State
return SizedBox(
height: 50,
width: 50,
- child: FloatingActionButton(
- tooltip: assistanceState.tooltip(
- L10n.of(context)!,
- ),
- backgroundColor: Theme.of(context).scaffoldBackgroundColor,
- disabledElevation: 0,
- shape: const CircleBorder(),
- onPressed: () {
- if (assistanceState != AssistanceState.complete) {
- widget.controller.choreographer
- .getLanguageHelp(
- false,
- true,
- )
- .then((_) {
- if (widget.controller.choreographer.igc.igcTextData != null &&
- widget.controller.choreographer.igc.igcTextData!.matches
- .isNotEmpty) {
- widget.controller.choreographer.igc.showFirstMatch(context);
- }
- });
- }
- },
- child: Stack(
- alignment: Alignment.center,
- children: [
- _controller != null
- ? RotationTransition(
- turns: Tween(begin: 0.0, end: math.pi * 2)
- .animate(_controller!),
- child: icon,
- )
- : icon,
- Container(
- width: 26,
- height: 26,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
+ child: InkWell(
+ customBorder: const CircleBorder(),
+ onLongPress: () => pLanguageDialog(context, () {}),
+ child: FloatingActionButton(
+ tooltip: assistanceState.tooltip(
+ L10n.of(context)!,
+ ),
+ backgroundColor: Theme.of(context).scaffoldBackgroundColor,
+ disabledElevation: 0,
+ shape: const CircleBorder(),
+ onPressed: () {
+ if (assistanceState != AssistanceState.fetching) {
+ widget.controller.choreographer
+ .getLanguageHelp(
+ false,
+ true,
+ )
+ .then((_) {
+ if (widget.controller.choreographer.igc.igcTextData != null &&
+ widget.controller.choreographer.igc.igcTextData!.matches
+ .isNotEmpty) {
+ widget.controller.choreographer.igc.showFirstMatch(context);
+ }
+ });
+ }
+ },
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ _controller != null
+ ? RotationTransition(
+ turns: Tween(begin: 0.0, end: math.pi * 2)
+ .animate(_controller!),
+ child: icon,
+ )
+ : icon,
+ Container(
+ width: 26,
+ height: 26,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: Theme.of(context).scaffoldBackgroundColor,
+ ),
+ ),
+ Container(
+ width: 20,
+ height: 20,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: assistanceState.stateColor(context),
+ ),
+ ),
+ Icon(
+ size: 16,
+ Icons.check,
color: Theme.of(context).scaffoldBackgroundColor,
),
- ),
- Container(
- width: 20,
- height: 20,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: assistanceState.stateColor(context),
- ),
- ),
- Icon(
- size: 16,
- Icons.check,
- color: Theme.of(context).scaffoldBackgroundColor,
- ),
- ],
+ ],
+ ),
),
),
);
}
}
-
-extension AssistanceStateExtension on AssistanceState {
- Color stateColor(context) {
- switch (this) {
- case AssistanceState.noMessage:
- case AssistanceState.notFetched:
- case AssistanceState.fetching:
- return Theme.of(context).colorScheme.primary;
- case AssistanceState.fetched:
- return PangeaColors.igcError;
- case AssistanceState.complete:
- return AppConfig.success;
- }
- }
-
- String tooltip(L10n l10n) {
- switch (this) {
- case AssistanceState.noMessage:
- case AssistanceState.notFetched:
- return l10n.runGrammarCorrection;
- case AssistanceState.fetching:
- return "";
- case AssistanceState.fetched:
- return l10n.grammarCorrectionFailed;
- case AssistanceState.complete:
- return l10n.grammarCorrectionComplete;
- }
- }
-}
diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart
index 0cd14e5a4..c85c8a7bd 100644
--- a/lib/pangea/constants/model_keys.dart
+++ b/lib/pangea/constants/model_keys.dart
@@ -27,11 +27,8 @@ class ModelKey {
static const String clientIsPublic = "isPublic";
static const String clientIsOpenEnrollment = 'isOpenEnrollment';
- static const String clientIsOpenExchange = 'isOpenExchange';
static const String clientIsOneToOneChatClass = 'oneToOneChatClass';
- static const String clientIsOneToOneChatExchange = 'oneToOneChatExchange';
static const String clientIsCreateRooms = 'isCreateRooms';
- static const String clientIsCreateRoomsExchange = 'isCreateRoomsExchange';
static const String clientIsShareVideo = 'isShareVideo';
static const String clientIsSharePhoto = 'isSharePhoto';
static const String clientIsShareFiles = 'isShareFiles';
@@ -39,7 +36,6 @@ class ModelKey {
static const String clientIsCreateStories = 'isCreateStories';
static const String clientIsVoiceNotes = 'isVoiceNotes';
static const String clientIsInviteOnlyStudents = 'isInviteOnlyStudents';
- static const String clientIsInviteOnlyExchanges = 'isInviteOnlyExchanges';
static const String userL1 = "user_l1";
static const String userL2 = "user_l2";
diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart
index a182728dd..27b177a35 100644
--- a/lib/pangea/constants/pangea_event_types.dart
+++ b/lib/pangea/constants/pangea_event_types.dart
@@ -1,6 +1,5 @@
class PangeaEventTypes {
- static const classSettings = "pangea.class";
- static const pangeaExchange = "p.exchange";
+ static const languageSettings = "pangea.class";
static const transcript = "pangea.transcript";
@@ -26,4 +25,8 @@ class PangeaEventTypes {
static const String report = 'm.report';
static const textToSpeechRule = "p.rule.text_to_speech";
+
+ static const pangeaActivityRes = "pangea.activity_res";
+ static const acitivtyRequest = "pangea.activity_req";
+ static const activityRecord = "pangea.activity_completion";
}
diff --git a/lib/pangea/constants/pangea_room_types.dart b/lib/pangea/constants/pangea_room_types.dart
index dcceadd36..804d2be86 100644
--- a/lib/pangea/constants/pangea_room_types.dart
+++ b/lib/pangea/constants/pangea_room_types.dart
@@ -1,4 +1,3 @@
class PangeaRoomTypes {
static const analytics = 'p.analytics';
- static const exchange = 'p.exchange';
}
diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart
index a3f1aa268..b4bc18b87 100644
--- a/lib/pangea/controllers/class_controller.dart
+++ b/lib/pangea/controllers/class_controller.dart
@@ -7,9 +7,9 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
-import 'package:fluffychat/pangea/utils/class_code.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
+import 'package:fluffychat/pangea/utils/space_code.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@@ -34,8 +34,8 @@ class ClassController extends BaseController {
Future fixClassPowerLevels() async {
try {
- final teacherSpaces = await _pangeaController
- .matrixState.client.classesAndExchangesImTeaching;
+ final teacherSpaces =
+ await _pangeaController.matrixState.client.spacesImTeaching;
final List> classFixes = List.from(teacherSpaces)
.map((adminSpace) => adminSpace.setClassPowerLevels())
.toList();
@@ -64,7 +64,7 @@ class ClassController extends BaseController {
classCode,
).onError(
(error, stackTrace) =>
- ClassCodeUtil.messageSnack(context, ErrorCopy(context, error).body),
+ SpaceCodeUtil.messageSnack(context, ErrorCopy(context, error).body),
);
}
}
@@ -77,8 +77,7 @@ class ClassController extends BaseController {
if (!room.isDirectChat) return [];
final List existingParentsIds =
room.pangeaSpaceParents.map((e) => e.id).toList();
- final List spaces =
- _pangeaController.matrixState.client.classesAndExchangesImIn;
+ final List spaces = _pangeaController.matrixState.client.spacesImIn;
//make sure we have the latest participants
await Future.wait(spaces.map((e) => e.requestParticipants()));
@@ -120,7 +119,7 @@ class ClassController extends BaseController {
});
if (classChunk == null) {
- ClassCodeUtil.messageSnack(
+ SpaceCodeUtil.messageSnack(
context,
L10n.of(context)!.unableToFindClass,
);
@@ -130,7 +129,7 @@ class ClassController extends BaseController {
if (_pangeaController.matrixState.client.rooms
.any((room) => room.id == classChunk.roomId)) {
setActiveSpaceIdInChatListController(classChunk.roomId);
- ClassCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass);
+ SpaceCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass);
return;
}
@@ -169,9 +168,6 @@ class ClassController extends BaseController {
final Room? joinedSpace =
_pangeaController.matrixState.client.getRoomById(classChunk.roomId);
- // ensure that the user has an analytics room for this space's language
- await joinedSpace?.ensureAnalyticsRoomExists();
-
// when possible, add user's analytics room the to space they joined
await joinedSpace?.addAnalyticsRoomsToSpace();
@@ -180,7 +176,7 @@ class ClassController extends BaseController {
GoogleAnalytics.joinClass(classCode);
return;
} catch (err) {
- ClassCodeUtil.messageSnack(
+ SpaceCodeUtil.messageSnack(
context,
ErrorCopy(context, err).body,
);
@@ -198,7 +194,7 @@ class ClassController extends BaseController {
final Room? room = _pangeaController.matrixState.client.getRoomById(roomId);
if (room == null) return;
- if (room.classSettings != null && room.pangeaRoomRules == null) {
+ if (room.isSpace && room.isRoomAdmin && room.pangeaRoomRules == null) {
try {
await _pangeaController.matrixState.client.setRoomStateWithKey(
roomId,
diff --git a/lib/pangea/controllers/language_controller.dart b/lib/pangea/controllers/language_controller.dart
index e11053fe2..c906e6b4e 100644
--- a/lib/pangea/controllers/language_controller.dart
+++ b/lib/pangea/controllers/language_controller.dart
@@ -3,12 +3,9 @@ import 'dart:developer';
import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
-import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
-import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../widgets/user_settings/p_language_dialog.dart';
@@ -63,50 +60,51 @@ class LanguageController {
return _userL2Code != null ? PangeaLanguage.byLangCode(_userL2Code!) : null;
}
- String? activeL1Code({String? roomID}) {
- final String? activeL2 = activeL2Code(roomID: roomID);
- if (roomID == null || activeL2 != _userL1Code) {
- return _userL1Code;
- }
- final ClassSettingsModel? classContext = _pangeaController
- .matrixState.client
- .getRoomById(roomID)
- ?.firstLanguageSettings;
- final String? classL1 = classContext?.dominantLanguage;
- if (classL1 == LanguageKeys.mixedLanguage ||
- classL1 == LanguageKeys.multiLanguage ||
- classL1 == null) {
- if (_userL2Code != _userL1Code) {
- return _userL2Code;
- }
- return LanguageKeys.unknownLanguage;
- }
- return classL1;
+ String? activeL1Code() {
+ return _userL1Code;
+ // final String? activeL2 = activeL2Code(roomID: roomID);
+ // if (roomID == null || activeL2 != _userL1Code) {
+ // return _userL1Code;
+ // }
+ // final LanguageSettingsModel? classContext = _pangeaController
+ // .matrixState.client
+ // .getRoomById(roomID)
+ // ?.firstLanguageSettings;
+ // final String? classL1 = classContext?.dominantLanguage;
+ // if (classL1 == LanguageKeys.mixedLanguage ||
+ // classL1 == LanguageKeys.multiLanguage ||
+ // classL1 == null) {
+ // if (_userL2Code != _userL1Code) {
+ // return _userL2Code;
+ // }
+ // return LanguageKeys.unknownLanguage;
+ // }
+ // return classL1;
}
/// Class languages override user languages within a class context
- String? activeL2Code({String? roomID}) {
- if (roomID == null) {
- return _userL2Code;
- }
- final ClassSettingsModel? classContext = _pangeaController
- .matrixState.client
- .getRoomById(roomID)
- ?.firstLanguageSettings;
- return classContext?.targetLanguage ?? _userL2Code;
+ String? activeL2Code() {
+ return _userL2Code;
+ // if (roomID == null) {
+ // return _userL2Code;
+ // }
+ // final LanguageSettingsModel? classContext = _pangeaController
+ // .matrixState.client
+ // .getRoomById(roomID)
+ // ?.firstLanguageSettings;
+ // return classContext?.targetLanguage ?? _userL2Code;
}
- LanguageModel? activeL1Model({String? roomID}) {
- final activeL1 = activeL1Code(roomID: roomID);
- return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null;
+ LanguageModel? activeL1Model() {
+ return userL1;
+ // final activeL1 = activeL1Code(roomID: roomID);
+ // return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null;
}
- LanguageModel? activeL2Model({String? roomID}) {
- final activeL2 = activeL2Code(roomID: roomID);
- final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null;
- return model;
+ LanguageModel? activeL2Model() {
+ return userL2;
+ // final activeL2 = activeL2Code(roomID: roomID);
+ // final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null;
+ // return model;
}
-
- bool equalActiveL1AndActiveL2({Room? room}) =>
- activeL1Code() == activeL2Code(roomID: room?.id);
}
diff --git a/lib/pangea/controllers/language_list_controller.dart b/lib/pangea/controllers/language_list_controller.dart
index 345d5b5e7..59c3cce88 100644
--- a/lib/pangea/controllers/language_list_controller.dart
+++ b/lib/pangea/controllers/language_list_controller.dart
@@ -27,7 +27,7 @@ class PangeaLanguage {
static Future initialize() async {
try {
- _langList = await _getCahedFlags();
+ _langList = await _getCachedFlags();
if (await _shouldFetch || _langList.isEmpty) {
_langList = await LanguageRepo.fetchLanguages();
@@ -77,7 +77,7 @@ class PangeaLanguage {
await MyShared.saveJson(PrefKey.flags, flagMap);
}
- static Future> _getCahedFlags() async {
+ static Future> _getCachedFlags() async {
final Map? flagsMap =
await MyShared.readJson(PrefKey.flags);
if (flagsMap == null) {
diff --git a/lib/pangea/controllers/local_settings.dart b/lib/pangea/controllers/local_settings.dart
index 2dc30cfe7..95b88ae14 100644
--- a/lib/pangea/controllers/local_settings.dart
+++ b/lib/pangea/controllers/local_settings.dart
@@ -1,5 +1,5 @@
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
-import 'package:fluffychat/pangea/models/class_model.dart';
+import 'package:fluffychat/pangea/models/space_model.dart';
class LocalSettings {
late PangeaController _pangeaController;
diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart
index c0c8ecd1a..7083efbfd 100644
--- a/lib/pangea/controllers/message_analytics_controller.dart
+++ b/lib/pangea/controllers/message_analytics_controller.dart
@@ -4,11 +4,13 @@ import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/constants/match_rule_ids.dart';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
+import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
import 'package:fluffychat/pangea/enum/time_span.dart';
import 'package:fluffychat/pangea/models/analytics/analytics_event.dart';
import 'package:fluffychat/pangea/models/analytics/constructs_event.dart';
import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart';
+import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
@@ -62,6 +64,33 @@ class AnalyticsController extends BaseController {
);
}
+ ///////// SPACE ANALYTICS LANGUAGES //////////
+ String get _analyticsSpaceLangKey => "ANALYTICS_SPACE_LANG_KEY";
+
+ LanguageModel get currentAnalyticsSpaceLang {
+ try {
+ final String? str = _pangeaController.pStoreService.read(
+ _analyticsSpaceLangKey,
+ local: true,
+ );
+ return str != null
+ ? PangeaLanguage.byLangCode(str)
+ : _pangeaController.languageController.userL2 ??
+ _pangeaController.pLanguageStore.targetOptions.first;
+ } catch (err) {
+ debugger(when: kDebugMode);
+ return _pangeaController.pLanguageStore.targetOptions.first;
+ }
+ }
+
+ Future setCurrentAnalyticsSpaceLang(LanguageModel lang) async {
+ await _pangeaController.pStoreService.save(
+ _analyticsSpaceLangKey,
+ lang.langCode,
+ local: true,
+ );
+ }
+
Future myAnalyticsLastUpdated(String type) async {
// given an analytics event type, get the last updated times
// for each of the user's analytics rooms and return the most recent
@@ -96,7 +125,6 @@ class AnalyticsController extends BaseController {
Future spaceAnalyticsLastUpdated(
String type,
Room space,
- String langCode,
) async {
// check if any students have recently updated their analytics
// if any have, then the cache needs to be updated
@@ -106,7 +134,7 @@ class AnalyticsController extends BaseController {
final List> lastUpdatedFutures = [];
for (final student in space.students) {
final Room? analyticsRoom = _pangeaController.matrixState.client
- .analyticsRoomLocal(langCode, student.id);
+ .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
if (analyticsRoom == null) continue;
lastUpdatedFutures.add(
analyticsRoom.analyticsLastUpdated(
@@ -132,11 +160,21 @@ class AnalyticsController extends BaseController {
// private chat analytics to determine which children are already visible
// in the chat list
final Map> _lastFetchedHierarchies = {};
+
void setLatestHierarchy(String spaceId, GetSpaceHierarchyResponse resp) {
final List roomIds = resp.rooms.map((room) => room.roomId).toList();
_lastFetchedHierarchies[spaceId] = roomIds;
}
+ Future> getLatestSpaceHierarchy(String spaceId) async {
+ if (!_lastFetchedHierarchies.containsKey(spaceId)) {
+ final resp =
+ await _pangeaController.matrixState.client.getSpaceHierarchy(spaceId);
+ setLatestHierarchy(spaceId, resp);
+ }
+ return _lastFetchedHierarchies[spaceId] ?? [];
+ }
+
//////////////////////////// MESSAGE SUMMARY ANALYTICS ////////////////////////////
Future> mySummaryAnalytics() async {
@@ -168,9 +206,6 @@ class AnalyticsController extends BaseController {
) async {
// gets all the summary analytics events for the students
// in a space since the current timespace's cut off date
- final langCode = _pangeaController.languageController.activeL2Code(
- roomID: space.id,
- );
// ensure that all the space's events are loaded (mainly the for langCode)
// and that the participants are loaded
@@ -181,7 +216,7 @@ class AnalyticsController extends BaseController {
final List analyticsEvents = [];
for (final student in space.students) {
final Room? analyticsRoom = _pangeaController.matrixState.client
- .analyticsRoomLocal(langCode, student.id);
+ .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
if (analyticsRoom != null) {
final List? roomEvents =
@@ -225,7 +260,8 @@ class AnalyticsController extends BaseController {
(e.defaultSelected.id == defaultSelected.id) &&
(e.defaultSelected.type == defaultSelected.type) &&
(e.selected?.id == selected?.id) &&
- (e.selected?.type == selected?.type),
+ (e.selected?.type == selected?.type) &&
+ (e.langCode == currentAnalyticsSpaceLang.langCode),
);
if (index != -1) {
@@ -253,6 +289,7 @@ class AnalyticsController extends BaseController {
chartAnalyticsModel: chartAnalyticsModel,
defaultSelected: defaultSelected,
selected: selected,
+ langCode: currentAnalyticsSpaceLang.langCode,
),
);
}
@@ -267,16 +304,16 @@ class AnalyticsController extends BaseController {
return filtered;
}
- List filterRoomAnalytics(
+ Future> filterRoomAnalytics(
List unfiltered,
String? roomID,
- ) {
+ ) async {
List filtered = [...unfiltered];
Room? room;
if (roomID != null) {
room = _pangeaController.matrixState.client.getRoomById(roomID);
if (room?.isSpace == true) {
- return filterSpaceAnalytics(unfiltered, roomID);
+ return await filterSpaceAnalytics(unfiltered, roomID);
}
}
@@ -295,16 +332,10 @@ class AnalyticsController extends BaseController {
Future> filterPrivateChatAnalytics(
List unfiltered,
- Room? space,
+ Room space,
) async {
- if (space != null && !_lastFetchedHierarchies.containsKey(space.id)) {
- final resp = await _pangeaController.matrixState.client
- .getSpaceHierarchy(space.id);
- setLatestHierarchy(space.id, resp);
- }
-
- final List privateChatIds = space?.allSpaceChildRoomIds ?? [];
- final List lastFetched = _lastFetchedHierarchies[space!.id] ?? [];
+ final List privateChatIds = space.allSpaceChildRoomIds;
+ final List lastFetched = await getLatestSpaceHierarchy(space.id);
for (final id in lastFetched) {
privateChatIds.removeWhere((e) => e == id);
}
@@ -324,19 +355,11 @@ class AnalyticsController extends BaseController {
return filtered;
}
- List filterSpaceAnalytics(
+ Future> filterSpaceAnalytics(
List unfiltered,
String spaceId,
- ) {
- final selectedSpace =
- _pangeaController.matrixState.client.getRoomById(spaceId);
- final List chatIds = selectedSpace?.spaceChildren
- .map((e) => e.roomId)
- .where((e) => e != null)
- .cast()
- .toList() ??
- [];
-
+ ) async {
+ final List chatIds = await getLatestSpaceHierarchy(spaceId);
List filtered =
List.from(unfiltered);
@@ -384,12 +407,15 @@ class AnalyticsController extends BaseController {
if (defaultSelected.type == AnalyticsEntryType.student) {
throw "private chat filtering not available for my analytics";
}
+ if (space == null) {
+ throw "space is null in filterAnalytics with selected type privateChats";
+ }
return await filterPrivateChatAnalytics(
unfilteredAnalytics,
space,
);
case AnalyticsEntryType.space:
- return filterSpaceAnalytics(unfilteredAnalytics, selected!.id);
+ return await filterSpaceAnalytics(unfilteredAnalytics, selected!.id);
default:
throw Exception("invalid filter type - ${selected?.type}");
}
@@ -405,7 +431,6 @@ class AnalyticsController extends BaseController {
// if the user is looking at space analytics, then fetch the space
Room? space;
- String? langCode;
if (defaultSelected.type == AnalyticsEntryType.space) {
space = _pangeaController.matrixState.client.getRoomById(
defaultSelected.id,
@@ -423,23 +448,7 @@ class AnalyticsController extends BaseController {
timeSpan: currentAnalyticsTimeSpan,
);
}
-
await space.postLoad();
- langCode = _pangeaController.languageController.activeL2Code(
- roomID: space.id,
- );
- if (langCode == null) {
- ErrorHandler.logError(
- m: "langCode missing in getAnalytics",
- data: {
- "space": space,
- },
- );
- return ChartAnalyticsModel(
- msgs: [],
- timeSpan: currentAnalyticsTimeSpan,
- );
- }
}
DateTime? lastUpdated;
@@ -456,7 +465,6 @@ class AnalyticsController extends BaseController {
lastUpdated = await spaceAnalyticsLastUpdated(
PangeaEventTypes.summaryAnalytics,
space!,
- langCode!,
);
}
@@ -467,8 +475,10 @@ class AnalyticsController extends BaseController {
lastUpdated: lastUpdated,
);
if (local != null && !forceUpdate) {
+ debugPrint("returning local analytics");
return local;
}
+ debugPrint("fetching new analytics");
// get all the relevant summary analytics events for the current timespan
final List summaryEvents =
@@ -548,24 +558,10 @@ class AnalyticsController extends BaseController {
) async {
await space.postLoad();
await space.requestParticipants();
- final String? langCode = _pangeaController.languageController.activeL2Code(
- roomID: space.id,
- );
-
- if (langCode == null) {
- ErrorHandler.logError(
- m: "langCode missing in allSpaceMemberConstructs",
- data: {
- "space": space,
- },
- );
- return [];
- }
-
final List constructEvents = [];
for (final student in space.students) {
final Room? analyticsRoom = _pangeaController.matrixState.client
- .analyticsRoomLocal(langCode, student.id);
+ .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
if (analyticsRoom != null) {
final List? roomEvents =
(await analyticsRoom.getAnalyticsEvents(
@@ -614,31 +610,30 @@ class AnalyticsController extends BaseController {
return filtered;
}
- List filterPrivateChatConstructs(
+ Future> filterPrivateChatConstructs(
List unfilteredConstructs,
- Room parentSpace,
- ) {
- final List directChatIds = [];
+ Room space,
+ ) async {
+ final List privateChatIds = space.allSpaceChildRoomIds;
+ final List lastFetched = await getLatestSpaceHierarchy(space.id);
+ for (final id in lastFetched) {
+ privateChatIds.removeWhere((e) => e == id);
+ }
final List filtered =
List.from(unfilteredConstructs);
for (final construct in filtered) {
construct.content.uses.removeWhere(
- (use) => !directChatIds.contains(use.chatId),
+ (use) => !privateChatIds.contains(use.chatId),
);
}
return filtered;
}
- List filterSpaceConstructs(
+ Future> filterSpaceConstructs(
List unfilteredConstructs,
Room space,
- ) {
- final List chatIds = space.spaceChildren
- .map((e) => e.roomId)
- .where((e) => e != null)
- .cast()
- .toList();
-
+ ) async {
+ final List chatIds = await getLatestSpaceHierarchy(space.id);
final List filtered =
List.from(unfilteredConstructs);
@@ -665,7 +660,8 @@ class AnalyticsController extends BaseController {
e.defaultSelected.id == defaultSelected.id &&
e.defaultSelected.type == defaultSelected.type &&
e.selected?.id == selected?.id &&
- e.selected?.type == selected?.type,
+ e.selected?.type == selected?.type &&
+ e.langCode == currentAnalyticsSpaceLang.langCode,
);
if (index > -1) {
@@ -691,6 +687,7 @@ class AnalyticsController extends BaseController {
events: List.from(events),
defaultSelected: defaultSelected,
selected: selected,
+ langCode: currentAnalyticsSpaceLang.langCode,
);
_cachedConstructs.add(entry);
}
@@ -769,9 +766,9 @@ class AnalyticsController extends BaseController {
case AnalyticsEntryType.privateChats:
return defaultSelected.type == AnalyticsEntryType.student
? throw "private chat filtering not available for my analytics"
- : filterPrivateChatConstructs(unfilteredConstructs, space!);
+ : await filterPrivateChatConstructs(unfilteredConstructs, space!);
case AnalyticsEntryType.space:
- return filterSpaceConstructs(unfilteredConstructs, space!);
+ return await filterSpaceConstructs(unfilteredConstructs, space!);
default:
throw Exception("invalid filter type - ${selected?.type}");
}
@@ -784,10 +781,10 @@ class AnalyticsController extends BaseController {
bool removeIT = true,
bool forceUpdate = false,
}) async {
+ debugPrint("getting constructs");
await _pangeaController.matrixState.client.roomsLoading;
Room? space;
- String? langCode;
if (defaultSelected.type == AnalyticsEntryType.space) {
space = _pangeaController.matrixState.client.getRoomById(
defaultSelected.id,
@@ -803,18 +800,6 @@ class AnalyticsController extends BaseController {
return [];
}
await space.postLoad();
- langCode = _pangeaController.languageController.activeL2Code(
- roomID: space.id,
- );
- if (langCode == null) {
- ErrorHandler.logError(
- m: "langCode missing in setConstructs",
- data: {
- "space": space,
- },
- );
- return [];
- }
}
DateTime? lastUpdated;
@@ -831,7 +816,6 @@ class AnalyticsController extends BaseController {
lastUpdated = await spaceAnalyticsLastUpdated(
PangeaEventTypes.construct,
space!,
- langCode!,
);
}
@@ -843,8 +827,10 @@ class AnalyticsController extends BaseController {
lastUpdated: lastUpdated,
);
if (local != null && !forceUpdate) {
+ debugPrint("returning local constructs");
return local;
}
+ debugPrint("fetching new constructs");
final filteredConstructs = space == null
? await getMyConstructs(
@@ -884,6 +870,7 @@ class AnalyticsController extends BaseController {
}
abstract class CacheEntry {
+ final String langCode;
final TimeSpan timeSpan;
final AnalyticsSelected defaultSelected;
AnalyticsSelected? selected;
@@ -892,6 +879,7 @@ abstract class CacheEntry {
CacheEntry({
required this.timeSpan,
required this.defaultSelected,
+ required this.langCode,
this.selected,
}) {
_createdAt = DateTime.now();
@@ -924,17 +912,19 @@ class ConstructCacheEntry extends CacheEntry {
required this.type,
required this.events,
required super.timeSpan,
+ required super.langCode,
required super.defaultSelected,
super.selected,
});
}
class AnalyticsCacheModel extends CacheEntry {
- ChartAnalyticsModel chartAnalyticsModel;
+ final ChartAnalyticsModel chartAnalyticsModel;
AnalyticsCacheModel({
required this.chartAnalyticsModel,
required super.timeSpan,
+ required super.langCode,
required super.defaultSelected,
super.selected,
});
diff --git a/lib/pangea/controllers/message_data_controller.dart b/lib/pangea/controllers/message_data_controller.dart
index 4a0668880..7b3a3e6d2 100644
--- a/lib/pangea/controllers/message_data_controller.dart
+++ b/lib/pangea/controllers/message_data_controller.dart
@@ -176,14 +176,20 @@ class MessageDataController extends BaseController {
required String target,
required Room room,
}) async {
+ if (_pangeaController.languageController.userL2 == null ||
+ _pangeaController.languageController.userL1 == null) {
+ ErrorHandler.logError(
+ e: "userL1 or userL2 is null in _getPangeaRepresentation",
+ s: StackTrace.current,
+ );
+ return null;
+ }
final req = FullTextTranslationRequestModel(
text: text,
tgtLang: target,
srcLang: source,
- userL2:
- _pangeaController.languageController.activeL2Code(roomID: room.id)!,
- userL1:
- _pangeaController.languageController.activeL1Code(roomID: room.id)!,
+ userL2: _pangeaController.languageController.userL2!.langCode,
+ userL1: _pangeaController.languageController.userL1!.langCode,
);
try {
diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart
index 77a644863..614fcf9db 100644
--- a/lib/pangea/controllers/my_analytics_controller.dart
+++ b/lib/pangea/controllers/my_analytics_controller.dart
@@ -1,13 +1,16 @@
import 'dart:async';
import 'dart:developer';
+import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/constants/local.key.dart';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
-import 'package:fluffychat/pangea/models/analytics/analytics_event.dart';
-import 'package:fluffychat/pangea/models/analytics/analytics_model.dart';
+import 'package:fluffychat/pangea/models/analytics/constructs_event.dart';
+import 'package:fluffychat/pangea/models/analytics/constructs_model.dart';
+import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart';
+import 'package:fluffychat/pangea/models/analytics/summary_analytics_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
@@ -22,20 +25,23 @@ class MyAnalyticsController extends BaseController {
final int _maxMessagesCached = 10;
final int _minutesBeforeUpdate = 5;
+ /// the time since the last update that will trigger an automatic update
+ final Duration _timeSinceUpdate = const Duration(days: 1);
+
MyAnalyticsController(PangeaController pangeaController) {
_pangeaController = pangeaController;
}
- // adds the listener that handles when to run automatic updates
- // to analytics - either after a certain number of messages sent/
- // received or after a certain amount of time without an update
+ /// adds the listener that handles when to run automatic updates
+ /// to analytics - either after a certain number of messages sent/
+ /// received or after a certain amount of time [_timeSinceUpdate] without an update
Future addEventsListener() async {
final Client client = _pangeaController.matrixState.client;
// if analytics haven't been updated in the last day, update them
DateTime? lastUpdated = await _pangeaController.analytics
.myAnalyticsLastUpdated(PangeaEventTypes.summaryAnalytics);
- final DateTime yesterday = DateTime.now().subtract(const Duration(days: 1));
+ final DateTime yesterday = DateTime.now().subtract(_timeSinceUpdate);
if (lastUpdated?.isBefore(yesterday) ?? true) {
debugPrint("analytics out-of-date, updating");
await updateAnalytics();
@@ -50,9 +56,9 @@ class MyAnalyticsController extends BaseController {
});
}
- // given an update from sync stream, check if the update contains
- // messages for which analytics will be saved. If so, reset the timer
- // and add the event ID to the cache of un-added event IDs
+ /// given an update from sync stream, check if the update contains
+ /// messages for which analytics will be saved. If so, reset the timer
+ /// and add the event ID to the cache of un-added event IDs
void updateAnalyticsTimer(SyncUpdate update, DateTime? lastUpdated) {
for (final entry in update.rooms!.join!.entries) {
final Room room =
@@ -157,6 +163,7 @@ class MyAnalyticsController extends BaseController {
_updateCompleter = Completer();
try {
await _updateAnalytics();
+ clearMessagesSinceUpdate();
} catch (err, s) {
ErrorHandler.logError(
e: err,
@@ -169,126 +176,148 @@ class MyAnalyticsController extends BaseController {
}
}
+ // top level analytics sending function. Send analytics
+ // for each type of analytics event
+ // to each of the applicable analytics rooms
Future _updateAnalytics() async {
- // top level analytics sending function. Send analytics
- // for each type of analytics event
- // to each of the applicable analytics rooms
- clearMessagesSinceUpdate();
+ // if the user's l2 is not sent, don't send analytics
+ final String? userL2 = _pangeaController.languageController.activeL2Code();
+ if (userL2 == null) {
+ return;
+ }
// fetch a list of all the chats that the user is studying
// and a list of all the spaces in which the user is studying
await setStudentChats();
await setStudentSpaces();
- // get all the analytics rooms that the user has
- // and create any missing analytics rooms (if the user is studying
- // in a class but doesn't have an analytics room for that class's L2)
- final List analyticsRooms =
- _pangeaController.matrixState.client.allMyAnalyticsRooms;
- analyticsRooms.addAll(await createMissingAnalyticsRooms());
+ // get the last updated time for each analytics room
+ // and the least recent update, which will be used to determine
+ // how far to go back in the chat history to get messages
+ final Map lastUpdatedMap = await _pangeaController
+ .matrixState.client
+ .allAnalyticsRoomsLastUpdated();
+ final List lastUpdates = lastUpdatedMap.values
+ .where((lastUpdate) => lastUpdate != null)
+ .cast()
+ .toList();
- // finally, send an analytics event for each analytics room and
- // each type of analytics event
- for (final Room analyticsRoom in analyticsRooms) {
- for (final String type in AnalyticsEvent.analyticsEventTypes) {
- await sendAnalyticsEvent(analyticsRoom, type);
- }
+ /// Get the last time that analytics to for current target language
+ /// were updated. This my present a problem is the user has analytics
+ /// rooms for multiple languages, and a non-target language was updated
+ /// less recently than the target language. In this case, some data may
+ /// be missing, but a case like that seems relatively rare, and could
+ /// result in unnecessaily going too far back in the chat history
+ DateTime? l2AnalyticsLastUpdated = lastUpdatedMap[userL2];
+ if (l2AnalyticsLastUpdated == null) {
+ /// if the target language has never been updated, use the least
+ /// recent update time
+ lastUpdates.sort((a, b) => a.compareTo(b));
+ l2AnalyticsLastUpdated =
+ lastUpdates.isNotEmpty ? lastUpdates.first : null;
+ }
+
+ // for each chat the user is studying in, get all the messages
+ // since the least recent update analytics update, and sort them
+ // by their langCodes
+ final Map> langCodeToMsgs =
+ await getLangCodesToMsgs(
+ userL2,
+ l2AnalyticsLastUpdated,
+ );
+
+ final List langCodes = langCodeToMsgs.keys.toList();
+ for (final String langCode in langCodes) {
+ // for each of the langs that the user has sent message in, get
+ // the corresponding analytics room (or create it)
+ final Room analyticsRoom = await _pangeaController.matrixState.client
+ .getMyAnalyticsRoom(langCode);
+
+ // if there is no analytics room for this langCode, then user hadn't sent
+ // message in this language at the time of the last analytics update
+ // so fallback to the least recent update time
+ final DateTime? lastUpdated =
+ lastUpdatedMap[analyticsRoom.id] ?? l2AnalyticsLastUpdated;
+
+ // get the corresponding list of recent messages for this langCode
+ final List recentMsgs =
+ langCodeToMsgs[langCode] ?? [];
+
+ // finally, send the analytics events to the analytics room
+ await sendAnalyticsEvents(
+ analyticsRoom,
+ recentMsgs,
+ lastUpdated,
+ );
}
}
- Future sendAnalyticsEvent(
- Room analyticsRoom,
- String type,
+ Future