diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 74b2ecf10..67e459e03 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -247,7 +247,8 @@ class ChatController extends State // #Pangea // void requestHistory() async { Future requestHistory() async { - // #Pangea + if (timeline == null) return; + // Pangea# if (!timeline!.canRequestHistory) return; Logs().v('Requesting history...'); await timeline!.requestHistory(historyCount: _loadHistoryCount); @@ -425,7 +426,7 @@ class ChatController extends State numRequests++; } } - // #Pangea + // Pangea# } catch (e, s) { Logs().w('Unable to load timeline on event ID $eventContextId', e, s); if (!mounted) return; @@ -1575,13 +1576,27 @@ class ChatController extends State setPangeaMessageEvent(eventId); if (_pangeaMessageEvents[eventId] == null) return; } - _toolbarDisplayControllers[eventId] = ToolbarDisplayController( - targetId: event.eventId, - pangeaMessageEvent: _pangeaMessageEvents[eventId]!, - immersionMode: choreographer.immersionMode, - controller: this, - ); - _toolbarDisplayControllers[eventId]!.setToolbar(); + + try { + _toolbarDisplayControllers[eventId] = ToolbarDisplayController( + targetId: event.eventId, + pangeaMessageEvent: _pangeaMessageEvents[eventId]!, + immersionMode: choreographer.immersionMode, + controller: this, + ); + _toolbarDisplayControllers[eventId]!.setToolbar(); + } catch (e, s) { + ErrorHandler.logError( + e: e, + s: s, + m: "Failed to set toolbar display controller", + data: { + "eventId": eventId, + "event": event.toJson(), + "pangeaMessageEvent": _pangeaMessageEvents[eventId]?.toString(), + }, + ); + } } PangeaMessageEvent? getPangeaMessageEvent(String eventId) { diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 8e499deb8..417e1165c 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -256,7 +256,10 @@ class _SpaceViewState extends State { // #Pangea case SpaceChildContextAction.archive: widget.controller.cancelAction(); - widget.controller.toggleSelection(room!.id); + // #Pangea + if (room == null) return; + // Pangea# + widget.controller.toggleSelection(room.id); room.isSpace ? await showFutureLoadingDialog( context: context, @@ -273,7 +276,10 @@ class _SpaceViewState extends State { break; case SpaceChildContextAction.addToSpace: widget.controller.cancelAction(); - widget.controller.toggleSelection(room!.id); + // #Pangea + if (room == null) return; + // Pangea# + widget.controller.toggleSelection(room.id); await widget.controller.addToSpace(); break; // Pangea# diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index f8d90e65d..dd15538e2 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -119,7 +119,6 @@ class HomeserverPickerController extends State { // #Pangea // void ssoLoginAction(String id) async { void ssoLoginAction(IdentityProvider provider) async { - final id = provider.id!; //Pangea# final redirectUrl = kIsWeb // #Pangea @@ -131,7 +130,11 @@ class HomeserverPickerController extends State { : '${AppConfig.appOpenUrlScheme.toLowerCase()}://login'; //Pangea# final url = Matrix.of(context).getLoginClient().homeserver!.replace( - path: '/_matrix/client/v3/login/sso/redirect${id == null ? '' : '/$id'}', + // #Pangea + // path: '/_matrix/client/v3/login/sso/redirect${id == null ? '' : '/$id'}', + path: + '/_matrix/client/v3/login/sso/redirect${provider.id == null ? '' : '/${provider.id}'}', + // Pangea# queryParameters: {'redirectUrl': redirectUrl}, ); diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 62d84992c..402ea3e5b 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -192,8 +192,6 @@ class NewSpaceController extends State { // Pangea# ); // #Pangea - Room? room = Matrix.of(context).client.getRoomById(spaceId); - final List> futures = [ Matrix.of(context).client.waitForRoomInSync(spaceId, join: true), ]; @@ -202,8 +200,6 @@ class NewSpaceController extends State { } await Future.wait(futures); - room = Matrix.of(context).client.getRoomById(spaceId); - final newChatRoomId = await Matrix.of(context).client.createGroupChat( enableEncryption: false, preset: sdk.CreateRoomPreset.publicChat, @@ -212,7 +208,18 @@ class NewSpaceController extends State { ); GoogleAnalytics.createChat(newChatRoomId); - room!.setSpaceChild(newChatRoomId, suggested: true); + final Room? room = Matrix.of(context).client.getRoomById(spaceId); + if (room == null) { + ErrorHandler.logError( + e: 'Failed to get new space by id $spaceId', + ); + MatrixState.pangeaController.classController + .setActiveSpaceIdInChatListController(spaceId); + context.push('/spaces/$spaceId'); + return; + } + + room.setSpaceChild(newChatRoomId, suggested: true); newClassMode ? GoogleAnalytics.addParent( newChatRoomId, diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index b1f6aa0c0..162c75393 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -14,7 +14,6 @@ import 'package:fluffychat/pangea/extensions/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/message_data_models.dart'; -import 'package:fluffychat/pangea/models/widget_measurement.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/overlay.dart'; @@ -136,7 +135,7 @@ class Choreographer { _resetDebounceTimer() { if (debounceTimer != null) { - debounceTimer!.cancel(); + debounceTimer?.cancel(); debounceTimer = null; } } @@ -252,6 +251,7 @@ class Choreographer { s: StackTrace.current, ); MatrixState.pAnyState.closeOverlay(); + return; } if (igc.igcTextData!.matches[matchIndex].match.choices == null) { ErrorHandler.logError( @@ -259,6 +259,7 @@ class Choreographer { s: StackTrace.current, ); MatrixState.pAnyState.closeOverlay(); + return; } //if it's the wrong choice, return @@ -478,8 +479,6 @@ class Choreographer { } } - WidgetMeasurements get inputBarSize => _textController.measurements!; - bool get showIsError => !itController.isOpen && errorService.isError; LayerLinkAndKey get itBarLinkAndKey => diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart index ebe4522b7..f5e15a1e3 100644 --- a/lib/pangea/choreographer/controllers/igc_controller.dart +++ b/lib/pangea/choreographer/controllers/igc_controller.dart @@ -1,11 +1,6 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:sentry_flutter/sentry_flutter.dart'; - import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/models/igc_text_data_model.dart'; @@ -13,6 +8,10 @@ import 'package:fluffychat/pangea/models/pangea_match_model.dart'; import 'package:fluffychat/pangea/models/span_data.dart'; import 'package:fluffychat/pangea/repo/igc_repo.dart'; import 'package:fluffychat/pangea/widgets/igc/span_card.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + import '../../../widgets/matrix.dart'; import '../../models/language_detection_model.dart'; import '../../models/span_card_model.dart'; @@ -136,6 +135,21 @@ class IgcController { data: igcTextData!.toJson(), ); } + + if (choreographer.l1LangCode == null || + choreographer.l2LangCode == null) { + debugger(when: kDebugMode); + ErrorHandler.logError( + m: "l1LangCode and/or l2LangCode is null", + s: StackTrace.current, + data: { + "l1LangCode": choreographer.l1LangCode, + "l2LangCode": choreographer.l2LangCode, + }, + ); + return; + } + final TokensResponseModel res = await TokensRepo.tokenize( await choreographer.pangeaController.userController.accessToken, TokensRequestModel( @@ -155,7 +169,7 @@ class IgcController { ); ErrorHandler.logError(e: err, s: stack); } finally { - igcTextData!.loading = false; + igcTextData?.loading = false; choreographer.stopLoading(); } } @@ -184,7 +198,7 @@ class IgcController { cursorOffset: match.match.offset, ), onITStart: () { - if (choreographer.itEnabled) { + if (choreographer.itEnabled && igcTextData != null) { choreographer.onITStart(igcTextData!.matches[firstMatchIndex]); } }, diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index b4047e1e4..74d0b0159 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -119,7 +119,6 @@ class ITController { choreographer.startLoading(); final String currentText = choreographer.currentText; - final String? translationId = currentITStep?.translationId; if (sourceText == null) await _setSourceText(); @@ -264,6 +263,7 @@ class ITController { //maybe we store IT data in the same format? make a specific kind of match? void selectTranslation(int chosenIndex) { + if (currentITStep == null) return; final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex); completedITSteps.add(itStep); diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index 6f5cca545..2ab811008 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -225,6 +225,13 @@ class ITChoices extends StatelessWidget { Color? borderColor, String? choiceFeedback, ]) { + if (controller.currentITStep == null) { + ErrorHandler.logError( + m: "currentITStep is null in showCard", + s: StackTrace.current, + ); + return; + } OverlayUtil.showPositionedCard( context: context, cardToShow: choiceFeedback == null diff --git a/lib/pangea/choreographer/widgets/it_feedback_card.dart b/lib/pangea/choreographer/widgets/it_feedback_card.dart index cec2e9e2f..072d24e1a 100644 --- a/lib/pangea/choreographer/widgets/it_feedback_card.dart +++ b/lib/pangea/choreographer/widgets/it_feedback_card.dart @@ -1,4 +1,5 @@ import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/igc/why_button.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; @@ -62,6 +63,12 @@ class ITFeedbackCardController extends State { } Future translateFeedback() async { + if (res == null) { + ErrorHandler.logError( + m: "Cannot translate feedback because res is null", + ); + return; + } setState(() { isTranslating = true; }); diff --git a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart index 93d441aa4..9d3302910 100644 --- a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart +++ b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart @@ -1,17 +1,16 @@ 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/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.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'; -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/utils/error_handler.dart'; import '../../../widgets/matrix.dart'; class _ErrorCopy { @@ -72,6 +71,12 @@ class LanguagePermissionsButtons extends StatelessWidget { _ErrorCopy? getCopy(BuildContext context) { final bool itDisabled = !choreographer.itEnabled; final bool igcDisabled = !choreographer.igcEnabled; + if (roomID == null) { + ErrorHandler.logError( + e: Exception("Room ID is null in language permissions"), + ); + return null; + } final Room? room = Matrix.of(context).client.getRoomById(roomID!); final bool itDisabledByClass = choreographer diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 6f0649dbb..dd9764fb9 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -54,6 +54,8 @@ class ModelKey { static const String offset = "offset"; static const String length = "length"; static const String langCode = 'lang_code'; + // some old analytics rooms have langCode instead of lang_code in the room creation content + static const String oldLangCode = 'langCode'; static const String wordLang = "word_lang"; static const String lemma = "lemma"; static const String saveVocab = "save_vocab"; diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index 78c75b408..484627680 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -1,15 +1,14 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; - -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/models/headwords.dart'; import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics_page.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; + import '../constants/class_default_values.dart'; import '../extensions/client_extension.dart'; import '../extensions/pangea_room_extension.dart'; @@ -264,7 +263,17 @@ class AnalyticsController extends BaseController { ) { final Room? analyticsRoom = _pangeaController.matrixState.client .analyticsRoomLocal(langCode, studentId); - return analyticsRoom!.allConstructEvents; + if (analyticsRoom == null) { + ErrorHandler.logError( + m: "analyticsRoom missing in studentConstructs", + s: StackTrace.current, + data: { + "studentId": studentId, + "langCode": langCode, + }, + ); + } + return analyticsRoom?.allConstructEvents ?? Future.value([]); } Future> spaceMemberVocab(String spaceId) async { @@ -309,25 +318,24 @@ class AnalyticsController extends BaseController { // as long as a student isn't selected, we want the vocab events for the whole class final Room? classRoom = _pangeaController.matrixState.client.getRoomById(defaultSelected.id); - if (classRoom == null) { + if (classRoom?.classSettings == null) { throw Exception("classRoom missing in spaceMemberVocab"); } - langCode = classRoom.classSettings!.targetLanguage; - - if (selected?.type != AnalyticsEntryType.student) { - eventsFuture = spaceMemberVocab(defaultSelected.id); - } else { - eventsFuture = studentConstructs(selected!.id, langCode); - } + langCode = classRoom!.classSettings!.targetLanguage; + eventsFuture = selected?.type == AnalyticsEntryType.student + ? studentConstructs(selected!.id, langCode) + : spaceMemberVocab(defaultSelected.id); } else if (defaultSelected.type == AnalyticsEntryType.student) { // in this case, we're on an individual's own analytics page - if (selected?.type == AnalyticsEntryType.space || selected?.type == AnalyticsEntryType.student) { langCode = _pangeaController.languageController .activeL2Code(roomID: selected!.id)!; eventsFuture = myConstructs(langCode); } else { + if (_pangeaController.languageController.userL2 == null) { + throw Exception("userL2 missing in vocabHeadsByAnalyticsSelected"); + } langCode = _pangeaController.languageController.userL2!.langCode; eventsFuture = myConstructs(langCode); } @@ -361,11 +369,9 @@ class AnalyticsController extends BaseController { throw "No target language available"; } - if (selected?.type != AnalyticsEntryType.student) { - eventFutures = spaceMemberVocab(defaultSelected.id); - } else { - eventFutures = studentConstructs(selected!.id, langCode); - } + eventFutures = selected?.type == AnalyticsEntryType.student + ? studentConstructs(selected!.id, langCode) + : spaceMemberVocab(defaultSelected.id); } else if (defaultSelected.type == AnalyticsEntryType.student) { // in this case, we're on an individual's own analytics page @@ -375,6 +381,9 @@ class AnalyticsController extends BaseController { .activeL2Code(roomID: selected!.id)!; eventFutures = myConstructs(langCode); } else { + if (_pangeaController.languageController.userL2 == null) { + throw "userL2 missing in constuctEventsByAnalyticsSelected"; + } langCode = _pangeaController.languageController.userL2!.langCode; eventFutures = myConstructs(langCode); } diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index 1fbb15b6f..b95520aa7 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -1,13 +1,12 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter/foundation.dart'; - -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; + import '../extensions/client_extension.dart'; import '../extensions/pangea_room_extension.dart'; import '../models/constructs_analytics_model.dart'; @@ -62,8 +61,10 @@ class MyAnalyticsController { List spaces, ) async { final List> events = []; - for (final space in spaces) { - events.add(space.getStudentAnalytics(_userId!)); + if (_userId != null) { + for (final space in spaces) { + events.add(space.getStudentAnalytics(_userId!)); + } } return Future.wait(events); } @@ -80,6 +81,7 @@ class MyAnalyticsController { try { final Map> aggregatedVocabUse = {}; for (final use in allUses) { + if (use.lemma == null) continue; aggregatedVocabUse[use.lemma!] ??= []; aggregatedVocabUse[use.lemma]!.add(use); } diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index e60b44b40..79006aa9d 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -116,8 +116,6 @@ class PangeaController { matrixState.loginHomeserverSummary = await matrixState.getLoginClient().checkHomeserver(homeserver); - final ssoSupported = matrixState.loginHomeserverSummary!.loginFlows - .any((flow) => flow.type == 'm.login.sso'); try { await matrixState.getLoginClient().register(); diff --git a/lib/pangea/controllers/subscription_controller.dart b/lib/pangea/controllers/subscription_controller.dart index e2c181c4d..828fd5365 100644 --- a/lib/pangea/controllers/subscription_controller.dart +++ b/lib/pangea/controllers/subscription_controller.dart @@ -169,6 +169,13 @@ class SubscriptionController extends BaseController { } void setNewUserTrial() { + if (_pangeaController.userController.userModel?.profile == null) { + ErrorHandler.logError( + m: "Null user profile in subscription settings", + s: StackTrace.current, + ); + return; + } final String profileCreatedAt = _pangeaController.userController.userModel!.profile!.createdAt; final DateTime creationTimestamp = DateTime.parse(profileCreatedAt); diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index 4142a9bdd..183a3b045 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -2,14 +2,15 @@ import 'dart:async'; import 'dart:developer'; import 'package:collection/collection.dart'; -import 'package:jwt_decode/jwt_decode.dart'; -import 'package:matrix/matrix.dart' as matrix; - import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:matrix/matrix.dart' as matrix; + import '../constants/local.key.dart'; import '../models/user_model.dart'; import '../repo/user_repo.dart'; @@ -148,7 +149,11 @@ class UserController extends BaseController { } _savePUserModel(PUserModel? pUserModel) { - final jsonUser = pUserModel!.toJson(); + if (pUserModel == null) { + ErrorHandler.logError(e: "trying to save null userModel"); + return; + } + final jsonUser = pUserModel.toJson(); _pangeaController.pStoreService.save(PLocalKey.user, jsonUser); setState(data: pUserModel); diff --git a/lib/pangea/extensions/client_extension.dart b/lib/pangea/extensions/client_extension.dart index 090530f1a..98353d29b 100644 --- a/lib/pangea/extensions/client_extension.dart +++ b/lib/pangea/extensions/client_extension.dart @@ -104,7 +104,6 @@ extension PangeaClient on Client { }); if (analyticsRoom != null && analyticsRoom.membership == Membership.invite) { - final membership = analyticsRoom.membership; debugger(when: kDebugMode); analyticsRoom .join() diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index 85d68672f..1b6370ce1 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -192,6 +192,7 @@ extension PangeaRoom on Room { //a space show up in spaceChildren but the user has not been invited to them. List get childrenAndGrandChildrenDirectChatIds { final List nonDirectChatRoomIds = childrenAndGrandChildren + .where((child) => child.roomId != null) .map((e) => client.getRoomById(e.roomId!)) .where((r) => r != null && !r.isDirectChat) .map((e) => e!.id) @@ -280,11 +281,11 @@ extension PangeaRoom on Room { bool isMadeByUser(String userId) => getState(EventTypes.RoomCreate)?.senderId == userId; - bool isMadeForLang(String langCode) => - getState(EventTypes.RoomCreate) - ?.content - .tryGet(ModelKey.langCode) == - langCode; + bool isMadeForLang(String langCode) { + final creationContent = getState(EventTypes.RoomCreate)?.content; + return creationContent?.tryGet(ModelKey.langCode) == langCode || + creationContent?.tryGet(ModelKey.oldLangCode) == langCode; + } bool isAnalyticsRoomOfUser(String userId) => isAnalyticsRoom && isMadeByUser(userId); @@ -766,6 +767,9 @@ extension PangeaRoom on Room { /// update state event and return eventId Future updateStateEvent(Event stateEvent) { + if (stateEvent.stateKey == null) { + throw Exception("stateEvent.stateKey is null"); + } return client.setRoomStateWithKey( id, stateEvent.type, @@ -923,7 +927,8 @@ extension PangeaRoom on Room { if (isDirectChat) return false; if (!isSpace) { if (eventsDefaultPowerLevel == null) return null; - return eventsDefaultPowerLevel! >= ClassDefaultValues.powerLevelOfAdmin; + return (eventsDefaultPowerLevel ?? 0) >= + ClassDefaultValues.powerLevelOfAdmin; } for (final child in spaceChildren) { if (child.roomId == null) continue; diff --git a/lib/pangea/models/language_model.dart b/lib/pangea/models/language_model.dart index 24090c60e..ba7709d5e 100644 --- a/lib/pangea/models/language_model.dart +++ b/lib/pangea/models/language_model.dart @@ -709,7 +709,11 @@ class _LanguageLocal { // debugger(when: kDebugMode); // ErrorHandler.logError(m: "Bad language key $key", s: StackTrace.current); } - if (item == null) return key; + if (item == null || + (native && !item.containsKey("nativeName") || + (!native && !item.containsKey("name")))) { + return key; + } return (native ? item["nativeName"]! : item["name"]!).split(",")[0]; } @@ -720,7 +724,8 @@ class _LanguageLocal { final String searchName = name.toLowerCase().split(" ")[0]; for (final entry in isoLangs.entries) { - if (entry.value["name"]!.toLowerCase().contains(searchName)) { + if (entry.value.containsKey("name") && + entry.value["name"]!.toLowerCase().contains(searchName)) { return entry.key; } } diff --git a/lib/pangea/models/pangea_representation_event.dart b/lib/pangea/models/pangea_representation_event.dart index 857abb8f5..b79bdd014 100644 --- a/lib/pangea/models/pangea_representation_event.dart +++ b/lib/pangea/models/pangea_representation_event.dart @@ -95,7 +95,7 @@ class RepresentationEvent { _tokens = tokenEvents.first.getPangeaContent(); - return _tokens!.tokens; + return _tokens?.tokens; } Future?> tokensGlobal(BuildContext context) async { diff --git a/lib/pangea/models/web_subscriptions.dart b/lib/pangea/models/web_subscriptions.dart index 788465916..1a6cc722a 100644 --- a/lib/pangea/models/web_subscriptions.dart +++ b/lib/pangea/models/web_subscriptions.dart @@ -1,8 +1,7 @@ -import 'package:sentry_flutter/sentry_flutter.dart'; - import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/models/base_subscription_info.dart'; import 'package:fluffychat/pangea/repo/subscription_repo.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class WebSubscriptionInfo extends SubscriptionInfo { WebSubscriptionInfo({required super.pangeaController}) : super(); @@ -12,6 +11,14 @@ class WebSubscriptionInfo extends SubscriptionInfo { await setAppIds(); await setAllProducts(); await setCustomerInfo(); + + if (allProducts == null || appIds == null) { + Sentry.addBreadcrumb( + Breadcrumb(message: "No products found for current app"), + ); + return; + } + availableSubscriptions = allProducts! .where((product) => product.appId == appIds!.currentAppId) .toList(); diff --git a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart b/lib/pangea/pages/analytics/class_analytics/class_analytics.dart index 39acca938..0a3802d8b 100644 --- a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart +++ b/lib/pangea/pages/analytics/class_analytics/class_analytics.dart @@ -1,18 +1,17 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/chart_analytics_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'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + import '../../../../widgets/matrix.dart'; import '../../../controllers/pangea_controller.dart'; import '../../../utils/sync_status_util_v2.dart'; @@ -41,7 +40,7 @@ class ClassAnalyticsV2Controller extends State { void initState() { super.initState(); Future.delayed(Duration.zero, () async { - if (classRoom == null || !classRoom!.isSpace) { + if (classRoom == null || (!(classRoom?.isSpace ?? false))) { context.go('/rooms'); } stateSub = _pangeaController.matrixState.client.onRoomState.stream @@ -57,16 +56,17 @@ class ClassAnalyticsV2Controller extends State { Future getChatAndStudents() async { try { - await classRoom!.requestParticipants(); - - students = classRoom!.students; - - chats = classRoom!.spaceChildren - .where((element) => element.roomId != null) - .map((e) => Matrix.of(context).client.getRoomById(e.roomId!)) - .where((r) => r != null) - .cast() - .toList(); + await classRoom?.requestParticipants(); + + if (classRoom != null) { + students = classRoom!.students; + chats = classRoom!.spaceChildren + .where((element) => element.roomId != null) + .map((e) => Matrix.of(context).client.getRoomById(e.roomId!)) + .where((r) => r != null) + .cast() + .toList(); + } setState(() { _initialized = true; diff --git a/lib/pangea/pages/analytics/messages_bar_chart.dart b/lib/pangea/pages/analytics/messages_bar_chart.dart index 890dd0c3d..35b3c62e7 100644 --- a/lib/pangea/pages/analytics/messages_bar_chart.dart +++ b/lib/pangea/pages/analytics/messages_bar_chart.dart @@ -1,14 +1,13 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - import 'package:fl_chart/fl_chart.dart'; -import 'package:intl/intl.dart'; - import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/pages/analytics/bar_chart_placeholder_data.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + import '../../enum/time_span.dart'; import '../../enum/use_type.dart'; import '../../models/chart_analytics_model.dart'; @@ -245,11 +244,12 @@ class MessagesBarChartState extends State { final TimeSeriesInterval? last = intervalGroupings.isNotEmpty ? intervalGroupings.last.last : null; - if (isInSameGroup( - last, - timeSeriesInterval, - widget.chartAnalytics!.timeSpan, - )) { + if (widget.chartAnalytics != null && + isInSameGroup( + last, + timeSeriesInterval, + widget.chartAnalytics!.timeSpan, + )) { intervalGroupings.last.add(timeSeriesInterval); } else { intervalGroupings.add([timeSeriesInterval]); diff --git a/lib/pangea/pages/class_settings/p_class_widgets/class_show_edit_dialog.dart b/lib/pangea/pages/class_settings/p_class_widgets/class_show_edit_dialog.dart index d18d09c39..5689b1792 100644 --- a/lib/pangea/pages/class_settings/p_class_widgets/class_show_edit_dialog.dart +++ b/lib/pangea/pages/class_settings/p_class_widgets/class_show_edit_dialog.dart @@ -1,11 +1,9 @@ -import 'package:flutter/material.dart'; - import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; void showEditFieldDialog(BuildContext context, String title) async { - // final room = Matrix.of(context).client.getRoomById(roomId!)!; final input = await showTextInputDialog( useRootNavigator: false, context: context, diff --git a/lib/pangea/pages/exchange/add_exchange_to_class.dart b/lib/pangea/pages/exchange/add_exchange_to_class.dart index eb5e7d59a..25b448214 100644 --- a/lib/pangea/pages/exchange/add_exchange_to_class.dart +++ b/lib/pangea/pages/exchange/add_exchange_to_class.dart @@ -1,11 +1,9 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.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/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; class AddExchangeToClass extends StatefulWidget { const AddExchangeToClass({super.key}); @@ -19,8 +17,12 @@ class AddExchangeToClassState extends State { @override Widget build(BuildContext context) { - final String spaceId = - GoRouterState.of(context).pathParameters['exchangeid']!; + final String? spaceId = + GoRouterState.of(context).pathParameters['exchangeid']; + if (spaceId == null) { + return const SizedBox(); + } + return Scaffold( appBar: AppBar( centerTitle: true, diff --git a/lib/pangea/pages/settings_subscription/settings_subscription.dart b/lib/pangea/pages/settings_subscription/settings_subscription.dart index 4121f1897..b517d32a2 100644 --- a/lib/pangea/pages/settings_subscription/settings_subscription.dart +++ b/lib/pangea/pages/settings_subscription/settings_subscription.dart @@ -121,7 +121,7 @@ class SubscriptionManagementController extends State { managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}"; } final String? purchaseAppId = - subscriptionController.subscription?.currentSubscription?.appId!; + subscriptionController.subscription?.currentSubscription?.appId; if (purchaseAppId == null) return; final SubscriptionAppIds? appIds = diff --git a/lib/pangea/pages/sign_up/signup.dart b/lib/pangea/pages/sign_up/signup.dart index 33c4b1788..d860e442f 100644 --- a/lib/pangea/pages/sign_up/signup.dart +++ b/lib/pangea/pages/sign_up/signup.dart @@ -1,13 +1,11 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; - import 'package:fluffychat/pangea/pages/sign_up/signup_view.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; class SignupPage extends StatefulWidget { const SignupPage({super.key}); @@ -133,7 +131,7 @@ class SignupPageController extends State { //Pangea# // Set displayname - if (displayname != localPart) { + if (displayname != localPart && client.userID != null) { await client.setDisplayName( client.userID!, displayname, diff --git a/lib/pangea/repo/span_data_repo.dart b/lib/pangea/repo/span_data_repo.dart index 878ae5f44..c6025af6c 100644 --- a/lib/pangea/repo/span_data_repo.dart +++ b/lib/pangea/repo/span_data_repo.dart @@ -1,11 +1,11 @@ import 'dart:convert'; -import 'package:http/http.dart'; - import 'package:fluffychat/pangea/config/environment.dart'; import 'package:fluffychat/pangea/enum/span_choice_type.dart'; import 'package:fluffychat/pangea/enum/span_data_type.dart'; import 'package:fluffychat/pangea/models/span_data.dart'; +import 'package:http/http.dart'; + import '../constants/model_keys.dart'; import '../network/requests.dart'; import '../network/urls.dart'; @@ -31,15 +31,15 @@ class SpanDataRepo { } } -Future getMock(SpanDetailsRepoReqAndRes req) async { - await Future.delayed(const Duration(seconds: 2)); - if (req.span.choices != null && - req.span.choices!.any((element) => element.selected)) { - return req..span = mockReponseWithHintOne.span; - } else { - return req..span = mockReponseWithChoices.span; - } -} +// Future getMock(SpanDetailsRepoReqAndRes req) async { +// await Future.delayed(const Duration(seconds: 2)); +// if (req.span.choices != null && +// req.span.choices!.any((element) => element.selected)) { +// return req..span = mockReponseWithHintOne.span; +// } else { +// return req..span = mockReponseWithChoices.span; +// } +// } class SpanDetailsRepoReqAndRes { String userL1; @@ -105,9 +105,9 @@ SpanDetailsRepoReqAndRes get mockReponseWithChoices { return res; } -SpanDetailsRepoReqAndRes get mockReponseWithHintOne { - final SpanDetailsRepoReqAndRes res = mockReponseWithChoices; - res.span.choices![1].selected = true; - res.span.message = "Conjugation error"; - return res; -} +// SpanDetailsRepoReqAndRes get mockReponseWithHintOne { +// final SpanDetailsRepoReqAndRes res = mockReponseWithChoices; +// res.span.choices![1].selected = true; +// res.span.message = "Conjugation error"; +// return res; +// } diff --git a/lib/pangea/utils/firebase_analytics.dart b/lib/pangea/utils/firebase_analytics.dart index 001161e2b..374242d7b 100644 --- a/lib/pangea/utils/firebase_analytics.dart +++ b/lib/pangea/utils/firebase_analytics.dart @@ -1,9 +1,8 @@ -import 'package:flutter/widgets.dart'; - import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_core/firebase_core.dart'; - import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; +import 'package:flutter/widgets.dart'; + import '../../config/firebase_options.dart'; import '../enum/use_type.dart'; @@ -161,22 +160,26 @@ class GoogleAnalytics { ); } - static FirebaseAnalyticsObserver getAnalyticsObserver() => - FirebaseAnalyticsObserver( - analytics: analytics!, - routeFilter: (route) { - // By default firebase only tracks page routes - if (route is! PageRoute || - // No user logged in, so we dont track - route.settings.name == "login" || - route.settings.name == "/home" || - route.settings.name == "connect" || - route.settings.name == "signup") { - return false; - } - final String? name = route.settings.name; - debugPrint("navigating to route: $name"); - return true; - }, - ); + static FirebaseAnalyticsObserver getAnalyticsObserver() { + if (analytics == null) { + throw Exception("Firebase Analytics not initialized"); + } + return FirebaseAnalyticsObserver( + analytics: analytics!, + routeFilter: (route) { + // By default firebase only tracks page routes + if (route is! PageRoute || + // No user logged in, so we dont track + route.settings.name == "login" || + route.settings.name == "/home" || + route.settings.name == "connect" || + route.settings.name == "signup") { + return false; + } + final String? name = route.settings.name; + debugPrint("navigating to route: $name"); + return true; + }, + ); + } } diff --git a/lib/pangea/utils/join_all_space_chats.dart b/lib/pangea/utils/join_all_space_chats.dart deleted file mode 100644 index cd4ee87db..000000000 --- a/lib/pangea/utils/join_all_space_chats.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/pangea/utils/error_handler.dart'; - -// Used in lock space. Handles case when child rooms return null from client.getRoomById -// Because the user hasn't joined them -Future> joinAllSpaceChats(Room space, Client client) async { - final List childrenIds = - space.spaceChildren.map((x) => x.roomId!).toList(); - - final List children = []; - for (final String childId in childrenIds) { - final Room? child = client.getRoomById(childId); - if (child != null) { - children.add(child); - } - // child may be null if the user is not in the room - else { - final Room? child = await tryGetRoomById(childId, client); - if (child != null) { - children.add(child); - } - } - } - return children; -} - -Future tryGetRoomById(String roomId, Client client) async { - try { - await client.joinRoomById(roomId); - } catch (err) { - // caused when chat has been archived - debugPrint("Failed to join $roomId"); - return null; - } - await client.waitForRoomInSync(roomId); - final Room? room = client.getRoomById(roomId); - if (room != null) { - return room; - } else { - ErrorHandler.logError( - e: "Failed to fetch child room with id $roomId after joining", - s: StackTrace.current, - ); - } - return null; -} diff --git a/lib/pangea/utils/lock_room.dart b/lib/pangea/utils/lock_room.dart index b17fcf674..e21ec8aba 100644 --- a/lib/pangea/utils/lock_room.dart +++ b/lib/pangea/utils/lock_room.dart @@ -49,6 +49,7 @@ Future unlockChat(Room room, Client client) async { Future lockSpace(Room space, Client client) async { for (final spaceChild in space.spaceChildren) { + if (spaceChild.roomId == null) continue; Room? child = client.getRoomById(spaceChild.roomId!); if (child == null) { try { @@ -70,6 +71,7 @@ Future lockSpace(Room space, Client client) async { Future unlockSpace(Room space, Client client) async { for (final spaceChild in space.spaceChildren) { + if (spaceChild.roomId == null) continue; final Room? child = client.getRoomById(spaceChild.roomId!); if (child == null) continue; child.isSpace diff --git a/lib/pangea/utils/set_class_name.dart b/lib/pangea/utils/set_class_name.dart index 13a9c6c45..92909034a 100644 --- a/lib/pangea/utils/set_class_name.dart +++ b/lib/pangea/utils/set_class_name.dart @@ -1,13 +1,14 @@ +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import '../../utils/matrix_sdk_extensions/matrix_locals.dart'; void setClassDisplayname(BuildContext context, String? roomId) async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; + if (roomId == null) return; + final room = Matrix.of(context).client.getRoomById(roomId); + if (room == null) return; final TextEditingController textFieldController = TextEditingController( text: room.getLocalizedDisplayname( MatrixLocals( diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 84834c540..f0d1dcad3 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -68,6 +68,7 @@ class ToolbarDisplayController { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { Widget overlayEntry; + if (toolbar == null) return; try { overlayEntry = Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pangea/widgets/class/add_class_and_invite.dart b/lib/pangea/widgets/class/add_class_and_invite.dart index e33118c73..06025eaa1 100644 --- a/lib/pangea/widgets/class/add_class_and_invite.dart +++ b/lib/pangea/widgets/class/add_class_and_invite.dart @@ -1,15 +1,14 @@ import 'dart:developer'; +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import '../../../widgets/matrix.dart'; import '../../utils/error_handler.dart'; import '../../utils/firebase_analytics.dart'; @@ -92,7 +91,7 @@ class AddToClassAndInviteState extends State { Future _addSingleParent(String roomToAddId, Room newParent) async { GoogleAnalytics.addParent(roomToAddId, newParent.classCode); final List> existingMembers = await Future.wait([ - room!.requestParticipants(), + room?.requestParticipants() ?? Future.value([]), newParent.requestParticipants(), ]); final List roomMembers = existingMembers[0]; @@ -102,7 +101,7 @@ class AddToClassAndInviteState extends State { newParent.setSpaceChild(roomToAddId, suggested: true), ]; for (final spaceMember - in spaceMembers.where((element) => element.id != room!.client.userID)) { + in spaceMembers.where((element) => element.id != room?.client.userID)) { if (!roomMembers.any( (m) => m.id == spaceMember.id && m.membership == Membership.join, )) { @@ -118,7 +117,7 @@ class AddToClassAndInviteState extends State { //function for kicking single student and haandling error Future _kickSpaceMember(User spaceMember) async { try { - await room!.kick(spaceMember.id); + await room?.kick(spaceMember.id); debugPrint('Kicked ${spaceMember.id}'); } catch (e) { debugger(when: kDebugMode); @@ -129,7 +128,7 @@ class AddToClassAndInviteState extends State { //function for adding single student and haandling error Future _inviteSpaceMember(User spaceMember) async { try { - await room!.invite(spaceMember.id); + await room?.invite(spaceMember.id); debugPrint('added ${spaceMember.id}'); } catch (e) { debugger(when: kDebugMode); @@ -153,14 +152,14 @@ class AddToClassAndInviteState extends State { return; } final List> roomsMembers = await Future.wait([ - room!.requestParticipants(), + room?.requestParticipants() ?? Future.value([]), spaceToRemove.requestParticipants(), ]); final List toKick = roomsMembers[1] .where( (element) => - element.id != room!.client.userID && + element.id != room?.client.userID && roomsMembers[0].any((m) => m.id == element.id), ) .toList(); @@ -176,9 +175,11 @@ class AddToClassAndInviteState extends State { // if (widget.setParentState != null) { // widget.setParentState!(); // } - await room!.requestParticipants(); + await room?.requestParticipants(); - GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id); + if (room != null) { + GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id); + } return; } diff --git a/lib/pangea/widgets/igc/card_error_widget.dart b/lib/pangea/widgets/igc/card_error_widget.dart index 8c546c7f6..7b3385c68 100644 --- a/lib/pangea/widgets/igc/card_error_widget.dart +++ b/lib/pangea/widgets/igc/card_error_widget.dart @@ -1,10 +1,9 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart'; import 'package:fluffychat/pangea/widgets/igc/card_header.dart'; +import 'package:flutter/material.dart'; class CardErrorWidget extends StatelessWidget { final Object? error; @@ -28,11 +27,9 @@ class CardErrorWidget extends StatelessWidget { CardHeader( text: errorCopy.title, botExpression: BotExpression.addled, - onClose: () => choreographer != null - ? choreographer!.onMatchError( - cursorOffset: offset, - ) - : null, + onClose: () => choreographer?.onMatchError( + cursorOffset: offset, + ), ), const SizedBox(height: 10.0), Center( diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index ce1523d01..eb167da3b 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -66,20 +66,26 @@ class SpanCardState extends State { Future getSpanDetails() async { try { if (widget.scm.pangeaMatch?.isITStart ?? false) return; + + if (!mounted) return; setState(() { fetchingData = true; }); await widget.scm.choreographer.igc.getSpanDetails(widget.scm.matchIndex); - setState(() => fetchingData = false); + if (mounted) { + setState(() => fetchingData = false); + } } catch (e) { // debugger(when: kDebugMode); ErrorHandler.logError(e: e, s: StackTrace.current); - setState(() { - error = e; - fetchingData = false; - }); + if (mounted) { + setState(() { + error = e; + fetchingData = false; + }); + } } } @@ -105,10 +111,10 @@ class WordMatchContent extends StatelessWidget { .scm .choreographer .igc - .igcTextData! - .matches[controller.widget.scm.matchIndex] + .igcTextData + ?.matches[controller.widget.scm.matchIndex] .match - .choices![index] + .choices?[index] .selected = true; controller.setState(() => ()); @@ -291,6 +297,10 @@ class PromptAndFeedback extends StatelessWidget { @override Widget build(BuildContext context) { + if (controller.widget.scm.pangeaMatch == null) { + return const SizedBox(); + } + return Container( constraints: controller.widget.scm.pangeaMatch!.isITStart ? null diff --git a/lib/pangea/widgets/igc/word_data_card.dart b/lib/pangea/widgets/igc/word_data_card.dart index 2e77ef880..a6ef7b511 100644 --- a/lib/pangea/widgets/igc/word_data_card.dart +++ b/lib/pangea/widgets/igc/word_data_card.dart @@ -190,7 +190,10 @@ class WordDataCardView extends StatelessWidget { style: BotStyle.text(context), ), const SizedBox(height: 5.0), - if (controller.wordData != null && controller.wordNetError == null) + if (controller.wordData != null && + controller.wordNetError == null && + controller.activeL1 != null && + controller.activeL2 != null) WordNetInfo( wordData: controller.wordData!, activeL1: controller.activeL1!,