Merge branch '375-introduce-bot-custom-mode' of https://github.com/pangeachat/client into 375-introduce-bot-custom-mode

pull/1384/head
WilsonLe 1 year ago
commit ab0c682399

@ -4014,9 +4014,9 @@
"wordsPerMinute": "Words per minute", "wordsPerMinute": "Words per minute",
"autoIGCToolName": "Run Language Assistance Automatically", "autoIGCToolName": "Run Language Assistance Automatically",
"autoIGCToolDescription": "Automatically run language assistance after typing messages", "autoIGCToolDescription": "Automatically run language assistance after typing messages",
"runGrammarCorrection": "Run grammar correction", "runGrammarCorrection": "Check message",
"grammarCorrectionFailed": "Issues to address", "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.", "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.", "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.", "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.",

@ -4609,9 +4609,9 @@
"enterNumber": "Introduzca un valor numérico entero.", "enterNumber": "Introduzca un valor numérico entero.",
"autoIGCToolName": "Ejecutar automáticamente la asistencia lingüística", "autoIGCToolName": "Ejecutar automáticamente la asistencia lingüística",
"autoIGCToolDescription": "Ejecutar automáticamente la asistencia lingüística después de escribir mensajes", "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", "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.", "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.", "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.", "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.",

@ -35,11 +35,26 @@ class ChatPermissionsSettingsView extends StatelessWidget {
final powerLevels = Map<String, dynamic>.from(powerLevelsContent) final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
// #Pangea // #Pangea
// ..removeWhere((k, v) => v is! int); // ..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# // Pangea#
final eventsPowerLevels = Map<String, int?>.from( final eventsPowerLevels = Map<String, int?>.from(
powerLevelsContent.tryGetMap<String, int?>('events') ?? {}, powerLevelsContent.tryGetMap<String, int?>('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( return Column(
children: [ children: [
Column( Column(
@ -57,51 +72,59 @@ class ChatPermissionsSettingsView extends StatelessWidget {
), ),
canEdit: room.canChangePowerLevel, canEdit: room.canChangePowerLevel,
), ),
Divider(color: Theme.of(context).dividerColor), // #Pangea
ListTile( // Why would teacher need to stop students from seeing notifications?
title: Text( // Divider(color: Theme.of(context).dividerColor),
L10n.of(context)!.notifications, // ListTile(
style: TextStyle( // title: Text(
color: Theme.of(context).colorScheme.primary, // L10n.of(context)!.notifications,
fontWeight: FontWeight.bold, // style: TextStyle(
), // color: Theme.of(context).colorScheme.primary,
), // fontWeight: FontWeight.bold,
), // ),
Builder( // ),
builder: (context) { // ),
const key = 'rooms'; // Builder(
final value = powerLevelsContent // builder: (context) {
.containsKey('notifications') // const key = 'rooms';
? powerLevelsContent // final value = powerLevelsContent
.tryGetMap<String, Object?>('notifications') // .containsKey('notifications')
?.tryGet<int>('rooms') ?? // ? powerLevelsContent
0 // .tryGetMap<String, Object?>('notifications')
: 0; // ?.tryGet<int>('rooms') ??
return PermissionsListTile( // 0
permissionKey: key, // : 0;
permission: value, // return PermissionsListTile(
category: 'notifications', // permissionKey: key,
canEdit: room.canChangePowerLevel, // permission: value,
onChanged: (level) => controller.editPowerLevel( // category: 'notifications',
context, // canEdit: room.canChangePowerLevel,
key, // onChanged: (level) => controller.editPowerLevel(
value, // context,
newLevel: level, // key,
category: 'notifications', // 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) for (final entry in eventsPowerLevels.entries)
PermissionsListTile( PermissionsListTile(
permissionKey: entry.key, permissionKey: entry.key,

@ -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/constants/language_keys.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/controllers/subscription_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/enum/edit_type.dart';
import 'package:fluffychat/pangea/models/it_step.dart'; import 'package:fluffychat/pangea/models/it_step.dart';
import 'package:fluffychat/pangea/models/language_detection_model.dart'; import 'package:fluffychat/pangea/models/language_detection_model.dart';
@ -570,13 +571,3 @@ class Choreographer {
return AssistanceState.complete; 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,
}

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.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/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:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -54,15 +53,15 @@ class StartIGCButtonState extends State<StartIGCButton>
setState(() => prevState = assistanceState); 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 @override
Widget build(BuildContext context) { 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 || if (!grammarCorrectionEnabled ||
widget.controller.choreographer.isAutoIGCEnabled || widget.controller.choreographer.isAutoIGCEnabled ||
widget.controller.choreographer.choreoMode == ChoreoMode.it) { widget.controller.choreographer.choreoMode == ChoreoMode.it) {
@ -89,7 +88,7 @@ class StartIGCButtonState extends State<StartIGCButton>
disabledElevation: 0, disabledElevation: 0,
shape: const CircleBorder(), shape: const CircleBorder(),
onPressed: () { onPressed: () {
if (assistanceState != AssistanceState.complete) { if (assistanceState != AssistanceState.fetching) {
widget.controller.choreographer widget.controller.choreographer
.getLanguageHelp( .getLanguageHelp(
false, false,
@ -142,32 +141,3 @@ class StartIGCButtonState extends State<StartIGCButton>
); );
} }
} }
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;
}
}
}

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

@ -4,14 +4,17 @@ import 'dart:developer';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/constants/class_default_values.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/model_keys.dart';
import 'package:fluffychat/pangea/constants/pangea_room_types.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/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/analytics/analytics_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/constructs_event.dart';
import 'package:fluffychat/pangea/models/analytics/summary_analytics_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/analytics/summary_analytics_model.dart';
import 'package:fluffychat/pangea/models/bot_options_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/space_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart';
@ -129,6 +132,9 @@ extension PangeaRoom on Room {
Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent; Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent;
Future<List<LanguageModel>> targetLanguages() async =>
await _targetLanguages();
// events // events
Future<bool> leaveIfFull() async => await _leaveIfFull(); Future<bool> leaveIfFull() async => await _leaveIfFull();

@ -92,6 +92,34 @@ extension SpaceRoomExtension on Room {
return null; return null;
} }
Future<List<LanguageModel>> _targetLanguages() async {
await requestParticipants();
final students = _students;
final Map<LanguageModel, int> langCounts = {};
final List<Room> 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 { // DateTime? get _languageSettingsUpdatedAt {
// if (!isSpace) return null; // if (!isSpace) return null;
// return languageSettingsStateEvent?.originServerTs ?? creationTime; // return languageSettingsStateEvent?.originServerTs ?? creationTime;

@ -568,10 +568,16 @@ class PangeaMessageEvent {
bool get hasUncompletedActivity { bool get hasUncompletedActivity {
if (l2Code == null) return false; if (l2Code == null) return false;
final List<PracticeActivityEvent> activities = practiceActivities(l2Code!); final List<PracticeActivityEvent> activities = practiceActivities(l2Code!);
if (activities.isEmpty) return false; if (activities.isEmpty) return false;
return !activities.every((activity) => activity.isComplete); // for now, only show the button if the event has no completed activities
// TODO - revert this after adding logic to show next activity
for (final activity in activities) {
if (activity.isComplete) return false;
}
return true;
// if (activities.isEmpty) return false;
// return !activities.every((activity) => activity.isComplete);
} }
String? get l2Code => String? get l2Code =>

@ -25,8 +25,9 @@ class BaseAnalyticsPage extends StatefulWidget {
final AnalyticsSelected defaultSelected; final AnalyticsSelected defaultSelected;
final AnalyticsSelected? alwaysSelected; final AnalyticsSelected? alwaysSelected;
final StudentAnalyticsController? myAnalyticsController; final StudentAnalyticsController? myAnalyticsController;
final List<LanguageModel> targetLanguages;
const BaseAnalyticsPage({ BaseAnalyticsPage({
super.key, super.key,
required this.pageTitle, required this.pageTitle,
required this.tabs, required this.tabs,
@ -34,7 +35,10 @@ class BaseAnalyticsPage extends StatefulWidget {
required this.defaultSelected, required this.defaultSelected,
this.selectedView, this.selectedView,
this.myAnalyticsController, this.myAnalyticsController,
}); targetLanguages,
}) : targetLanguages = (targetLanguages?.isNotEmpty ?? false)
? targetLanguages
: MatrixState.pangeaController.pLanguageStore.targetOptions;
@override @override
State<BaseAnalyticsPage> createState() => BaseAnalyticsController(); State<BaseAnalyticsPage> createState() => BaseAnalyticsController();

@ -128,8 +128,7 @@ class BaseAnalyticsView extends StatelessWidget {
value: controller.pangeaController.analytics value: controller.pangeaController.analytics
.currentAnalyticsSpaceLang, .currentAnalyticsSpaceLang,
onChange: (lang) => controller.toggleSpaceLang(lang), onChange: (lang) => controller.toggleSpaceLang(lang),
languages: controller languages: controller.widget.targetLanguages,
.pangeaController.pLanguageStore.targetOptions,
), ),
], ],
), ),

@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart';
import 'package:fluffychat/pangea/enum/bar_chart_view_enum.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/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/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart'; import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart';
import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart'; import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart';
@ -33,6 +34,18 @@ class SpaceAnalyticsV2Controller extends State<SpaceAnalyticsPage> {
List<User> students = []; List<User> students = [];
String? get spaceId => GoRouterState.of(context).pathParameters['spaceid']; String? get spaceId => GoRouterState.of(context).pathParameters['spaceid'];
Room? _spaceRoom; Room? _spaceRoom;
List<LanguageModel> targetLanguages = [];
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) {
context.go('/rooms');
}
getChatAndStudents();
});
}
Room? get spaceRoom { Room? get spaceRoom {
if (_spaceRoom == null || _spaceRoom!.id != spaceId) { if (_spaceRoom == null || _spaceRoom!.id != spaceId) {
@ -44,23 +57,11 @@ class SpaceAnalyticsV2Controller extends State<SpaceAnalyticsPage> {
context.go('/rooms/analytics'); context.go('/rooms/analytics');
return null; return null;
} }
getChatAndStudents(); getChatAndStudents().then((_) => setTargetLanguages());
} }
return _spaceRoom; 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<void> getChatAndStudents() async { Future<void> getChatAndStudents() async {
try { try {
await spaceRoom?.postLoad(); await spaceRoom?.postLoad();
@ -97,12 +98,12 @@ class SpaceAnalyticsV2Controller extends State<SpaceAnalyticsPage> {
} }
} }
// @override Future<void> setTargetLanguages() async {
// void dispose() { // get a list of language models, sorted by the
// super.dispose(); // number of students who are learning that language
// refreshTimer?.cancel(); targetLanguages = await spaceRoom?.targetLanguages() ?? [];
// stateSub?.cancel(); setState(() {});
// } }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

@ -59,6 +59,7 @@ class SpaceAnalyticsView extends StatelessWidget {
AnalyticsEntryType.space, AnalyticsEntryType.space,
controller.spaceRoom?.name ?? "", controller.spaceRoom?.name ?? "",
), ),
targetLanguages: controller.targetLanguages,
) )
: const SizedBox(); : const SizedBox();
} }

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/enum/time_span.dart';
import 'package:fluffychat/pangea/extensions/client_extension/client_extension.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/models/language_model.dart';
import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart'; import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -22,26 +23,47 @@ class AnalyticsSpaceList extends StatefulWidget {
class AnalyticsSpaceListController extends State<AnalyticsSpaceList> { class AnalyticsSpaceListController extends State<AnalyticsSpaceList> {
PangeaController pangeaController = MatrixState.pangeaController; PangeaController pangeaController = MatrixState.pangeaController;
List<Room> spaces = []; List<Room> spaces = [];
List<LanguageModel> targetLanguages = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Matrix.of(context).client.spacesImTeaching.then((spaceList) {
spaceList = spaceList setSpaceList().then((_) => setTargetLanguages());
.where(
(space) => !spaceList.any(
(parentSpace) => parentSpace.spaceChildren
.any((child) => child.roomId == space.id),
),
)
.toList();
spaces = spaceList;
setState(() {});
});
} }
StreamController refreshStream = StreamController.broadcast(); StreamController refreshStream = StreamController.broadcast();
Future<void> 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<void> setTargetLanguages() async {
if (spaces.isEmpty) return;
final Map<LanguageModel, int> langCounts = {};
for (final Room space in spaces) {
final List<LanguageModel> 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) { void toggleTimeSpan(BuildContext context, TimeSpan timeSpan) {
pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan); pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan);
refreshStream.add(false); refreshStream.add(false);

@ -45,7 +45,9 @@ class AnalyticsSpaceListView extends StatelessWidget {
value: value:
controller.pangeaController.analytics.currentAnalyticsSpaceLang, controller.pangeaController.analytics.currentAnalyticsSpaceLang,
onChange: (lang) => controller.toggleSpaceLang(lang), onChange: (lang) => controller.toggleSpaceLang(lang),
languages: controller.pangeaController.pLanguageStore.targetOptions, languages: controller.targetLanguages.isEmpty
? controller.pangeaController.pLanguageStore.targetOptions
: controller.targetLanguages,
), ),
], ],
), ),

@ -62,19 +62,22 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
if (langCode == null) return; if (langCode == null) return;
final List<PracticeActivityEvent> activities = final List<PracticeActivityEvent> activities =
widget.pangeaMessageEvent.practiceActivities(langCode!); widget.pangeaMessageEvent.practiceActivities(langCode!);
if (activities.isEmpty) return;
final List<PracticeActivityEvent> incompleteActivities = final List<PracticeActivityEvent> incompleteActivities =
activities.where((element) => !element.isComplete).toList(); activities.where((element) => !element.isComplete).toList();
debugPrint("total events: ${activities.length}"); debugPrint("total events: ${activities.length}");
debugPrint("incomplete practice events: ${incompleteActivities.length}"); debugPrint("incomplete practice events: ${incompleteActivities.length}");
// if an incomplete activity is found, show that // TODO update to show next activity
if (incompleteActivities.isNotEmpty) { practiceEvent = activities.first;
practiceEvent = incompleteActivities.first; // // if an incomplete activity is found, show that
} // if (incompleteActivities.isNotEmpty) {
// if no incomplete activity is found, show the last activity // practiceEvent = incompleteActivities.first;
else if (activities.isNotEmpty) { // }
practiceEvent = activities.last; // // if no incomplete activity is found, show the last activity
} // else if (activities.isNotEmpty) {
// practiceEvent = activities.last;
// }
setState(() {}); setState(() {});
} }

@ -109,7 +109,7 @@ class SubscriptionCard extends StatelessWidget {
title ?? subscription?.displayName(context) ?? '', title ?? subscription?.displayName(context) ?? '',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 20,
color: color:
enabled ? null : const Color.fromARGB(255, 174, 174, 174), enabled ? null : const Color.fromARGB(255, 174, 174, 174),
), ),

Loading…
Cancel
Save