From a4cd5d702b1bdcf1c35ab0f0c05c047d4677d805 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 24 Jun 2024 12:05:57 -0400 Subject: [PATCH 1/6] Let multiple overlays coexist --- .../controllers/language_list_controller.dart | 4 ++-- lib/pangea/utils/any_state_holder.dart | 24 ++++++++++++------- lib/pangea/utils/instructions.dart | 1 + lib/pangea/utils/overlay.dart | 12 +++++++--- lib/pangea/widgets/chat/message_toolbar.dart | 11 +++++---- 5 files changed, 34 insertions(+), 18 deletions(-) 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/utils/any_state_holder.dart b/lib/pangea/utils/any_state_holder.dart index bd09c7131..9705a9ca1 100644 --- a/lib/pangea/utils/any_state_holder.dart +++ b/lib/pangea/utils/any_state_holder.dart @@ -4,7 +4,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; class PangeaAnyState { final Map _layerLinkAndKeys = {}; - OverlayEntry? overlay; + List entries = []; dispose() { closeOverlay(); @@ -32,26 +32,32 @@ class PangeaAnyState { _layerLinkAndKeys.remove(transformTargetId); } - void openOverlay(OverlayEntry entry, BuildContext context) { - closeOverlay(); - overlay = entry; - Overlay.of(context).insert(overlay!); + void openOverlay( + OverlayEntry entry, + BuildContext context, { + bool closePrevOverlay = true, + }) { + if (closePrevOverlay) { + closeOverlay(); + } + entries.add(entry); + Overlay.of(context).insert(entry); } void closeOverlay() { - if (overlay != null) { + if (entries.isNotEmpty) { try { - overlay?.remove(); + entries.last.remove(); } catch (err, s) { ErrorHandler.logError( e: err, s: s, data: { - "overlay": overlay, + "overlay": entries.last, }, ); } - overlay = null; + entries.removeLast(); } } diff --git a/lib/pangea/utils/instructions.dart b/lib/pangea/utils/instructions.dart index 43350f518..b9eecd799 100644 --- a/lib/pangea/utils/instructions.dart +++ b/lib/pangea/utils/instructions.dart @@ -94,6 +94,7 @@ class InstructionsController { ), cardSize: const Size(300.0, 300.0), transformTargetId: transformTargetKey, + closePrevOverlay: false, ), ); } diff --git a/lib/pangea/utils/overlay.dart b/lib/pangea/utils/overlay.dart index 07eab5133..b0ab82025 100644 --- a/lib/pangea/utils/overlay.dart +++ b/lib/pangea/utils/overlay.dart @@ -25,9 +25,12 @@ class OverlayUtil { Color? backgroundColor, Alignment? targetAnchor, Alignment? followerAnchor, + bool closePrevOverlay = true, }) { try { - MatrixState.pAnyState.closeOverlay(); + if (closePrevOverlay) { + MatrixState.pAnyState.closeOverlay(); + } final LayerLinkAndKey layerLinkAndKey = MatrixState.pAnyState.layerLinkAndKey(transformTargetId); @@ -58,7 +61,8 @@ class OverlayUtil { ), ); - MatrixState.pAnyState.openOverlay(entry, context); + MatrixState.pAnyState + .openOverlay(entry, context, closePrevOverlay: closePrevOverlay); } catch (err, stack) { debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: stack); @@ -72,6 +76,7 @@ class OverlayUtil { required String transformTargetId, backDropToDismiss = true, Color? borderColor, + bool closePrevOverlay = true, }) { try { final LayerLinkAndKey layerLinkAndKey = @@ -101,6 +106,7 @@ class OverlayUtil { offset: cardOffset, backDropToDismiss: backDropToDismiss, borderColor: borderColor, + closePrevOverlay: closePrevOverlay, ); } catch (err, stack) { debugger(when: kDebugMode); @@ -176,7 +182,7 @@ class OverlayUtil { return Offset(dx, dy); } - static bool get isOverlayOpen => MatrixState.pAnyState.overlay != null; + static bool get isOverlayOpen => MatrixState.pAnyState.entries.isNotEmpty; } class TransparentBackdrop extends StatelessWidget { diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index ba508906b..a120669ab 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -135,8 +135,8 @@ class ToolbarDisplayController { backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100), ); - if (MatrixState.pAnyState.overlay != null) { - overlayId = MatrixState.pAnyState.overlay.hashCode.toString(); + if (MatrixState.pAnyState.entries.isNotEmpty) { + overlayId = MatrixState.pAnyState.entries.last.hashCode.toString(); } if (mode != null) { @@ -150,8 +150,11 @@ class ToolbarDisplayController { bool get highlighted { if (overlayId == null) return false; - if (MatrixState.pAnyState.overlay == null) overlayId = null; - return MatrixState.pAnyState.overlay.hashCode.toString() == overlayId; + if (MatrixState.pAnyState.entries.isEmpty) { + overlayId = null; + return false; + } + return MatrixState.pAnyState.entries.last.hashCode.toString() == overlayId; } } From e29951d5a4a2244140fd5be0d3b8ad8ab04fb016 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Mon, 24 Jun 2024 12:49:12 -0400 Subject: [PATCH 2/6] fix iOS version error after updating firebase packages --- ios/Podfile | 2 +- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile b/ios/Podfile index c8df069d3..fac4d8176 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4d1ab7555..98f0adda2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -475,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -568,7 +568,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -617,7 +617,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From 76d26f1c0ccf2d533144e491c2d2f3c88803d019 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 15:19:27 -0400 Subject: [PATCH 3/6] populate space analytics language dropdown from space student's analytics rooms --- .../pangea_room_extension.dart | 6 +++ .../space_settings_extension.dart | 28 +++++++++++ .../pages/analytics/base_analytics.dart | 8 +++- .../pages/analytics/base_analytics_view.dart | 3 +- .../space_analytics/space_analytics.dart | 39 ++++++++-------- .../space_analytics/space_analytics_view.dart | 1 + .../analytics/space_list/space_list.dart | 46 ++++++++++++++----- .../analytics/space_list/space_list_view.dart | 4 +- 8 files changed, 99 insertions(+), 36 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index 6219c62d2..b8dae7ab3 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -4,14 +4,17 @@ import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; +import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; +import 'package:fluffychat/pangea/controllers/language_list_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/constructs_event.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/models/bot_options_model.dart'; +import 'package:fluffychat/pangea/models/language_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/bot_name.dart'; @@ -129,6 +132,9 @@ extension PangeaRoom on Room { Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent; + Future> targetLanguages() async => + await _targetLanguages(); + // events Future leaveIfFull() async => await _leaveIfFull(); diff --git a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart index 00efb6773..5799631b1 100644 --- a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart @@ -92,6 +92,34 @@ extension SpaceRoomExtension on Room { return null; } + Future> _targetLanguages() async { + await requestParticipants(); + final students = _students; + + final Map langCounts = {}; + final List allRooms = client.rooms; + for (final User student in students) { + for (final Room room in allRooms) { + if (!room.isAnalyticsRoomOfUser(student.id)) continue; + final String? langCode = room.madeForLang; + if (langCode == null || + langCode.isEmpty || + langCode == LanguageKeys.unknownLanguage) { + continue; + } + final LanguageModel lang = PangeaLanguage.byLangCode(langCode); + langCounts[lang] ??= 0; + langCounts[lang] = langCounts[lang]! + 1; + } + } + // get a list of language models, sorted + // by the number of students who are learning that language + return langCounts.entries.map((entry) => entry.key).toList() + ..sort( + (a, b) => langCounts[b]!.compareTo(langCounts[a]!), + ); + } + // DateTime? get _languageSettingsUpdatedAt { // if (!isSpace) return null; // return languageSettingsStateEvent?.originServerTs ?? creationTime; diff --git a/lib/pangea/pages/analytics/base_analytics.dart b/lib/pangea/pages/analytics/base_analytics.dart index f62e5f6b5..3ab8fa0f2 100644 --- a/lib/pangea/pages/analytics/base_analytics.dart +++ b/lib/pangea/pages/analytics/base_analytics.dart @@ -25,8 +25,9 @@ class BaseAnalyticsPage extends StatefulWidget { final AnalyticsSelected defaultSelected; final AnalyticsSelected? alwaysSelected; final StudentAnalyticsController? myAnalyticsController; + final List targetLanguages; - const BaseAnalyticsPage({ + BaseAnalyticsPage({ super.key, required this.pageTitle, required this.tabs, @@ -34,7 +35,10 @@ class BaseAnalyticsPage extends StatefulWidget { required this.defaultSelected, this.selectedView, this.myAnalyticsController, - }); + targetLanguages, + }) : targetLanguages = (targetLanguages?.isNotEmpty ?? false) + ? targetLanguages + : MatrixState.pangeaController.pLanguageStore.targetOptions; @override State createState() => BaseAnalyticsController(); diff --git a/lib/pangea/pages/analytics/base_analytics_view.dart b/lib/pangea/pages/analytics/base_analytics_view.dart index 1c0445d5a..8cab95396 100644 --- a/lib/pangea/pages/analytics/base_analytics_view.dart +++ b/lib/pangea/pages/analytics/base_analytics_view.dart @@ -128,8 +128,7 @@ class BaseAnalyticsView extends StatelessWidget { value: controller.pangeaController.analytics .currentAnalyticsSpaceLang, onChange: (lang) => controller.toggleSpaceLang(lang), - languages: controller - .pangeaController.pLanguageStore.targetOptions, + languages: controller.widget.targetLanguages, ), ], ), diff --git a/lib/pangea/pages/analytics/space_analytics/space_analytics.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart index 64875bfba..b32780761 100644 --- a/lib/pangea/pages/analytics/space_analytics/space_analytics.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart'; import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart'; @@ -33,6 +34,18 @@ class SpaceAnalyticsV2Controller extends State { List students = []; String? get spaceId => GoRouterState.of(context).pathParameters['spaceid']; Room? _spaceRoom; + List targetLanguages = []; + + @override + void initState() { + super.initState(); + Future.delayed(Duration.zero, () async { + if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) { + context.go('/rooms'); + } + getChatAndStudents(); + }); + } Room? get spaceRoom { if (_spaceRoom == null || _spaceRoom!.id != spaceId) { @@ -44,23 +57,11 @@ class SpaceAnalyticsV2Controller extends State { context.go('/rooms/analytics'); return null; } - getChatAndStudents(); + getChatAndStudents().then((_) => setTargetLanguages()); } return _spaceRoom; } - @override - void initState() { - super.initState(); - debugPrint("init space analytics"); - Future.delayed(Duration.zero, () async { - if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) { - context.go('/rooms'); - } - getChatAndStudents(); - }); - } - Future getChatAndStudents() async { try { await spaceRoom?.postLoad(); @@ -97,12 +98,12 @@ class SpaceAnalyticsV2Controller extends State { } } - // @override - // void dispose() { - // super.dispose(); - // refreshTimer?.cancel(); - // stateSub?.cancel(); - // } + Future setTargetLanguages() async { + // get a list of language models, sorted by the + // number of students who are learning that language + targetLanguages = await spaceRoom?.targetLanguages() ?? []; + setState(() {}); + } @override Widget build(BuildContext context) { diff --git a/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart index 5e0008555..c72ec3c26 100644 --- a/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart @@ -59,6 +59,7 @@ class SpaceAnalyticsView extends StatelessWidget { AnalyticsEntryType.space, controller.spaceRoom?.name ?? "", ), + targetLanguages: controller.targetLanguages, ) : const SizedBox(); } diff --git a/lib/pangea/pages/analytics/space_list/space_list.dart b/lib/pangea/pages/analytics/space_list/space_list.dart index 058d54e63..e325e1e94 100644 --- a/lib/pangea/pages/analytics/space_list/space_list.dart +++ b/lib/pangea/pages/analytics/space_list/space_list.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:fluffychat/pangea/enum/time_span.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/language_model.dart'; import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart'; import 'package:flutter/material.dart'; @@ -22,26 +23,47 @@ class AnalyticsSpaceList extends StatefulWidget { class AnalyticsSpaceListController extends State { PangeaController pangeaController = MatrixState.pangeaController; List spaces = []; + List targetLanguages = []; @override void initState() { super.initState(); - Matrix.of(context).client.spacesImTeaching.then((spaceList) { - spaceList = spaceList - .where( - (space) => !spaceList.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) - .toList(); - spaces = spaceList; - setState(() {}); - }); + + setSpaceList().then((_) => setTargetLanguages()); } StreamController refreshStream = StreamController.broadcast(); + Future setSpaceList() async { + final spaceList = await Matrix.of(context).client.spacesImTeaching; + spaces = spaceList + .where( + (space) => !spaceList.any( + (parentSpace) => parentSpace.spaceChildren + .any((child) => child.roomId == space.id), + ), + ) + .toList(); + setState(() {}); + } + + Future setTargetLanguages() async { + if (spaces.isEmpty) return; + final Map langCounts = {}; + for (final Room space in spaces) { + final List targetLangs = await space.targetLanguages(); + for (final LanguageModel lang in targetLangs) { + langCounts[lang] ??= 0; + langCounts[lang] = langCounts[lang]! + 1; + } + } + targetLanguages = langCounts.entries.map((entry) => entry.key).toList() + ..sort( + (a, b) => langCounts[b]!.compareTo(langCounts[a]!), + ); + setState(() {}); + } + void toggleTimeSpan(BuildContext context, TimeSpan timeSpan) { pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan); refreshStream.add(false); diff --git a/lib/pangea/pages/analytics/space_list/space_list_view.dart b/lib/pangea/pages/analytics/space_list/space_list_view.dart index 5f5bf22da..1837d35f2 100644 --- a/lib/pangea/pages/analytics/space_list/space_list_view.dart +++ b/lib/pangea/pages/analytics/space_list/space_list_view.dart @@ -45,7 +45,9 @@ class AnalyticsSpaceListView extends StatelessWidget { value: controller.pangeaController.analytics.currentAnalyticsSpaceLang, onChange: (lang) => controller.toggleSpaceLang(lang), - languages: controller.pangeaController.pLanguageStore.targetOptions, + languages: controller.targetLanguages.isEmpty + ? controller.pangeaController.pLanguageStore.targetOptions + : controller.targetLanguages, ), ], ), From c9d1b72773857611d8fe4848d0984b2b514ff018 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 26 Jun 2024 11:34:41 -0400 Subject: [PATCH 4/6] igc tweaks --- assets/l10n/intl_en.arb | 4 +- assets/l10n/intl_es.arb | 4 +- .../controllers/choreographer.dart | 11 +---- .../widgets/start_igc_button.dart | 48 ++++--------------- lib/pangea/enum/assistance_state_enum.dart | 43 +++++++++++++++++ 5 files changed, 57 insertions(+), 53 deletions(-) create mode 100644 lib/pangea/enum/assistance_state_enum.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 056c6a347..cb319c9c7 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4010,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.", diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index 0658bff50..dc328294a 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4609,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.", diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 6ea029a6f..a0c6a5f6b 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/choreographer/controllers/message_options.dart import 'package:fluffychat/pangea/constants/language_keys.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/models/it_step.dart'; import 'package:fluffychat/pangea/models/language_detection_model.dart'; @@ -570,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/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart index 49e4b078d..877158dd2 100644 --- a/lib/pangea/choreographer/widgets/start_igc_button.dart +++ b/lib/pangea/choreographer/widgets/start_igc_button.dart @@ -1,10 +1,9 @@ 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'; @@ -54,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) { @@ -89,7 +88,7 @@ class StartIGCButtonState extends State disabledElevation: 0, shape: const CircleBorder(), onPressed: () { - if (assistanceState != AssistanceState.complete) { + if (assistanceState != AssistanceState.fetching) { widget.controller.choreographer .getLanguageHelp( false, @@ -142,32 +141,3 @@ class StartIGCButtonState extends State ); } } - -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/enum/assistance_state_enum.dart b/lib/pangea/enum/assistance_state_enum.dart new file mode 100644 index 000000000..6d3a853da --- /dev/null +++ b/lib/pangea/enum/assistance_state_enum.dart @@ -0,0 +1,43 @@ +// 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 +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/constants/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum AssistanceState { + noMessage, + notFetched, + fetching, + fetched, + complete, +} + +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; + } + } +} From 59e521b69e758cc31c339de93369ce4f4a0971be Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 26 Jun 2024 12:27:45 -0400 Subject: [PATCH 5/6] Hide some Chat permission options --- .../chat_permissions_settings_view.dart | 111 +++++++++++------- .../subscription/subscription_options.dart | 2 +- 2 files changed, 68 insertions(+), 45 deletions(-) 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/pangea/widgets/subscription/subscription_options.dart b/lib/pangea/widgets/subscription/subscription_options.dart index 41c8cbe4f..d40f68023 100644 --- a/lib/pangea/widgets/subscription/subscription_options.dart +++ b/lib/pangea/widgets/subscription/subscription_options.dart @@ -109,7 +109,7 @@ class SubscriptionCard extends StatelessWidget { title ?? subscription?.displayName(context) ?? '', textAlign: TextAlign.center, style: TextStyle( - fontSize: 24, + fontSize: 20, color: enabled ? null : const Color.fromARGB(255, 174, 174, 174), ), From 941508222731b9053a0df471769a65b4d4ca01f9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 27 Jun 2024 10:21:33 -0400 Subject: [PATCH 6/6] base last updated on target lang --- .../controllers/my_analytics_controller.dart | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index ea0d06c56..614fcf9db 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -25,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(); @@ -53,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 = @@ -160,6 +163,7 @@ class MyAnalyticsController extends BaseController { _updateCompleter = Completer(); try { await _updateAnalytics(); + clearMessagesSinceUpdate(); } catch (err, s) { ErrorHandler.logError( e: err, @@ -172,6 +176,9 @@ 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 { // if the user's l2 is not sent, don't send analytics final String? userL2 = _pangeaController.languageController.activeL2Code(); @@ -179,11 +186,6 @@ class MyAnalyticsController extends BaseController { return; } - // top level analytics sending function. Send analytics - // for each type of analytics event - // to each of the applicable analytics rooms - clearMessagesSinceUpdate(); - // 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(); @@ -199,9 +201,21 @@ class MyAnalyticsController extends BaseController { .where((lastUpdate) => lastUpdate != null) .cast() .toList(); - lastUpdates.sort((a, b) => a.compareTo(b)); - final DateTime? leastRecentUpdate = - lastUpdates.isNotEmpty ? lastUpdates.first : null; + + /// 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 @@ -209,7 +223,7 @@ class MyAnalyticsController extends BaseController { final Map> langCodeToMsgs = await getLangCodesToMsgs( userL2, - leastRecentUpdate, + l2AnalyticsLastUpdated, ); final List langCodes = langCodeToMsgs.keys.toList(); @@ -223,7 +237,7 @@ class MyAnalyticsController extends BaseController { // 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] ?? leastRecentUpdate; + lastUpdatedMap[analyticsRoom.id] ?? l2AnalyticsLastUpdated; // get the corresponding list of recent messages for this langCode final List recentMsgs =