Merge pull request #100 from pangeachat/null-fix

added handling for all cases of null check
pull/1011/head
ggurdin 2 years ago committed by GitHub
commit 66eb175a39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -247,7 +247,8 @@ class ChatController extends State<ChatPageWithRoom>
// #Pangea // #Pangea
// void requestHistory() async { // void requestHistory() async {
Future<void> requestHistory() async { Future<void> requestHistory() async {
// #Pangea if (timeline == null) return;
// Pangea#
if (!timeline!.canRequestHistory) return; if (!timeline!.canRequestHistory) return;
Logs().v('Requesting history...'); Logs().v('Requesting history...');
await timeline!.requestHistory(historyCount: _loadHistoryCount); await timeline!.requestHistory(historyCount: _loadHistoryCount);
@ -425,7 +426,7 @@ class ChatController extends State<ChatPageWithRoom>
numRequests++; numRequests++;
} }
} }
// #Pangea // Pangea#
} catch (e, s) { } catch (e, s) {
Logs().w('Unable to load timeline on event ID $eventContextId', e, s); Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
if (!mounted) return; if (!mounted) return;
@ -1575,13 +1576,27 @@ class ChatController extends State<ChatPageWithRoom>
setPangeaMessageEvent(eventId); setPangeaMessageEvent(eventId);
if (_pangeaMessageEvents[eventId] == null) return; if (_pangeaMessageEvents[eventId] == null) return;
} }
_toolbarDisplayControllers[eventId] = ToolbarDisplayController(
targetId: event.eventId, try {
pangeaMessageEvent: _pangeaMessageEvents[eventId]!, _toolbarDisplayControllers[eventId] = ToolbarDisplayController(
immersionMode: choreographer.immersionMode, targetId: event.eventId,
controller: this, pangeaMessageEvent: _pangeaMessageEvents[eventId]!,
); immersionMode: choreographer.immersionMode,
_toolbarDisplayControllers[eventId]!.setToolbar(); 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) { PangeaMessageEvent? getPangeaMessageEvent(String eventId) {

@ -256,7 +256,10 @@ class _SpaceViewState extends State<SpaceView> {
// #Pangea // #Pangea
case SpaceChildContextAction.archive: case SpaceChildContextAction.archive:
widget.controller.cancelAction(); widget.controller.cancelAction();
widget.controller.toggleSelection(room!.id); // #Pangea
if (room == null) return;
// Pangea#
widget.controller.toggleSelection(room.id);
room.isSpace room.isSpace
? await showFutureLoadingDialog( ? await showFutureLoadingDialog(
context: context, context: context,
@ -273,7 +276,10 @@ class _SpaceViewState extends State<SpaceView> {
break; break;
case SpaceChildContextAction.addToSpace: case SpaceChildContextAction.addToSpace:
widget.controller.cancelAction(); widget.controller.cancelAction();
widget.controller.toggleSelection(room!.id); // #Pangea
if (room == null) return;
// Pangea#
widget.controller.toggleSelection(room.id);
await widget.controller.addToSpace(); await widget.controller.addToSpace();
break; break;
// Pangea# // Pangea#

@ -119,7 +119,6 @@ class HomeserverPickerController extends State<HomeserverPicker> {
// #Pangea // #Pangea
// void ssoLoginAction(String id) async { // void ssoLoginAction(String id) async {
void ssoLoginAction(IdentityProvider provider) async { void ssoLoginAction(IdentityProvider provider) async {
final id = provider.id!;
//Pangea# //Pangea#
final redirectUrl = kIsWeb final redirectUrl = kIsWeb
// #Pangea // #Pangea
@ -131,7 +130,11 @@ class HomeserverPickerController extends State<HomeserverPicker> {
: '${AppConfig.appOpenUrlScheme.toLowerCase()}://login'; : '${AppConfig.appOpenUrlScheme.toLowerCase()}://login';
//Pangea# //Pangea#
final url = Matrix.of(context).getLoginClient().homeserver!.replace( 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}, queryParameters: {'redirectUrl': redirectUrl},
); );

@ -192,8 +192,6 @@ class NewSpaceController extends State<NewSpace> {
// Pangea# // Pangea#
); );
// #Pangea // #Pangea
Room? room = Matrix.of(context).client.getRoomById(spaceId);
final List<Future<dynamic>> futures = [ final List<Future<dynamic>> futures = [
Matrix.of(context).client.waitForRoomInSync(spaceId, join: true), Matrix.of(context).client.waitForRoomInSync(spaceId, join: true),
]; ];
@ -202,8 +200,6 @@ class NewSpaceController extends State<NewSpace> {
} }
await Future.wait(futures); await Future.wait(futures);
room = Matrix.of(context).client.getRoomById(spaceId);
final newChatRoomId = await Matrix.of(context).client.createGroupChat( final newChatRoomId = await Matrix.of(context).client.createGroupChat(
enableEncryption: false, enableEncryption: false,
preset: sdk.CreateRoomPreset.publicChat, preset: sdk.CreateRoomPreset.publicChat,
@ -212,7 +208,18 @@ class NewSpaceController extends State<NewSpace> {
); );
GoogleAnalytics.createChat(newChatRoomId); 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 newClassMode
? GoogleAnalytics.addParent( ? GoogleAnalytics.addParent(
newChatRoomId, newChatRoomId,

@ -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/class_model.dart';
import 'package:fluffychat/pangea/models/it_step.dart'; import 'package:fluffychat/pangea/models/it_step.dart';
import 'package:fluffychat/pangea/models/message_data_models.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/any_state_holder.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/overlay.dart'; import 'package:fluffychat/pangea/utils/overlay.dart';
@ -136,7 +135,7 @@ class Choreographer {
_resetDebounceTimer() { _resetDebounceTimer() {
if (debounceTimer != null) { if (debounceTimer != null) {
debounceTimer!.cancel(); debounceTimer?.cancel();
debounceTimer = null; debounceTimer = null;
} }
} }
@ -252,6 +251,7 @@ class Choreographer {
s: StackTrace.current, s: StackTrace.current,
); );
MatrixState.pAnyState.closeOverlay(); MatrixState.pAnyState.closeOverlay();
return;
} }
if (igc.igcTextData!.matches[matchIndex].match.choices == null) { if (igc.igcTextData!.matches[matchIndex].match.choices == null) {
ErrorHandler.logError( ErrorHandler.logError(
@ -259,6 +259,7 @@ class Choreographer {
s: StackTrace.current, s: StackTrace.current,
); );
MatrixState.pAnyState.closeOverlay(); MatrixState.pAnyState.closeOverlay();
return;
} }
//if it's the wrong choice, 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; bool get showIsError => !itController.isOpen && errorService.isError;
LayerLinkAndKey get itBarLinkAndKey => LayerLinkAndKey get itBarLinkAndKey =>

@ -1,11 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; 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/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
import 'package:fluffychat/pangea/models/igc_text_data_model.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/models/span_data.dart';
import 'package:fluffychat/pangea/repo/igc_repo.dart'; import 'package:fluffychat/pangea/repo/igc_repo.dart';
import 'package:fluffychat/pangea/widgets/igc/span_card.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 '../../../widgets/matrix.dart';
import '../../models/language_detection_model.dart'; import '../../models/language_detection_model.dart';
import '../../models/span_card_model.dart'; import '../../models/span_card_model.dart';
@ -136,6 +135,21 @@ class IgcController {
data: igcTextData!.toJson(), 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( final TokensResponseModel res = await TokensRepo.tokenize(
await choreographer.pangeaController.userController.accessToken, await choreographer.pangeaController.userController.accessToken,
TokensRequestModel( TokensRequestModel(
@ -155,7 +169,7 @@ class IgcController {
); );
ErrorHandler.logError(e: err, s: stack); ErrorHandler.logError(e: err, s: stack);
} finally { } finally {
igcTextData!.loading = false; igcTextData?.loading = false;
choreographer.stopLoading(); choreographer.stopLoading();
} }
} }
@ -184,7 +198,7 @@ class IgcController {
cursorOffset: match.match.offset, cursorOffset: match.match.offset,
), ),
onITStart: () { onITStart: () {
if (choreographer.itEnabled) { if (choreographer.itEnabled && igcTextData != null) {
choreographer.onITStart(igcTextData!.matches[firstMatchIndex]); choreographer.onITStart(igcTextData!.matches[firstMatchIndex]);
} }
}, },

@ -119,7 +119,6 @@ class ITController {
choreographer.startLoading(); choreographer.startLoading();
final String currentText = choreographer.currentText; final String currentText = choreographer.currentText;
final String? translationId = currentITStep?.translationId;
if (sourceText == null) await _setSourceText(); 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? //maybe we store IT data in the same format? make a specific kind of match?
void selectTranslation(int chosenIndex) { void selectTranslation(int chosenIndex) {
if (currentITStep == null) return;
final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex); final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex);
completedITSteps.add(itStep); completedITSteps.add(itStep);

@ -225,6 +225,13 @@ class ITChoices extends StatelessWidget {
Color? borderColor, Color? borderColor,
String? choiceFeedback, String? choiceFeedback,
]) { ]) {
if (controller.currentITStep == null) {
ErrorHandler.logError(
m: "currentITStep is null in showCard",
s: StackTrace.current,
);
return;
}
OverlayUtil.showPositionedCard( OverlayUtil.showPositionedCard(
context: context, context: context,
cardToShow: choiceFeedback == null cardToShow: choiceFeedback == null

@ -1,4 +1,5 @@
import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; 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:fluffychat/pangea/widgets/igc/why_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
@ -62,6 +63,12 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
} }
Future<void> translateFeedback() async { Future<void> translateFeedback() async {
if (res == null) {
ErrorHandler.logError(
m: "Cannot translate feedback because res is null",
);
return;
}
setState(() { setState(() {
isTranslating = true; isTranslating = true;
}); });

@ -1,17 +1,16 @@
import 'dart:developer'; 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/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.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';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.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'; import '../../../widgets/matrix.dart';
class _ErrorCopy { class _ErrorCopy {
@ -72,6 +71,12 @@ class LanguagePermissionsButtons extends StatelessWidget {
_ErrorCopy? getCopy(BuildContext context) { _ErrorCopy? getCopy(BuildContext context) {
final bool itDisabled = !choreographer.itEnabled; final bool itDisabled = !choreographer.itEnabled;
final bool igcDisabled = !choreographer.igcEnabled; 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 Room? room = Matrix.of(context).client.getRoomById(roomID!);
final bool itDisabledByClass = choreographer final bool itDisabledByClass = choreographer

@ -54,6 +54,8 @@ class ModelKey {
static const String offset = "offset"; static const String offset = "offset";
static const String length = "length"; static const String length = "length";
static const String langCode = 'lang_code'; 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 wordLang = "word_lang";
static const String lemma = "lemma"; static const String lemma = "lemma";
static const String saveVocab = "save_vocab"; static const String saveVocab = "save_vocab";

@ -1,15 +1,14 @@
import 'dart:developer'; 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/construct_type_enum.dart';
import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/enum/time_span.dart';
import 'package:fluffychat/pangea/models/headwords.dart'; import 'package:fluffychat/pangea/models/headwords.dart';
import 'package:fluffychat/pangea/models/student_analytics_summary_model.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/pages/analytics/base_analytics_page.dart';
import 'package:fluffychat/pangea/utils/error_handler.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 '../constants/class_default_values.dart';
import '../extensions/client_extension.dart'; import '../extensions/client_extension.dart';
import '../extensions/pangea_room_extension.dart'; import '../extensions/pangea_room_extension.dart';
@ -264,7 +263,17 @@ class AnalyticsController extends BaseController {
) { ) {
final Room? analyticsRoom = _pangeaController.matrixState.client final Room? analyticsRoom = _pangeaController.matrixState.client
.analyticsRoomLocal(langCode, studentId); .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<List<ConstructEvent>> spaceMemberVocab(String spaceId) async { Future<List<ConstructEvent>> 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 // as long as a student isn't selected, we want the vocab events for the whole class
final Room? classRoom = final Room? classRoom =
_pangeaController.matrixState.client.getRoomById(defaultSelected.id); _pangeaController.matrixState.client.getRoomById(defaultSelected.id);
if (classRoom == null) { if (classRoom?.classSettings == null) {
throw Exception("classRoom missing in spaceMemberVocab"); throw Exception("classRoom missing in spaceMemberVocab");
} }
langCode = classRoom.classSettings!.targetLanguage; langCode = classRoom!.classSettings!.targetLanguage;
eventsFuture = selected?.type == AnalyticsEntryType.student
if (selected?.type != AnalyticsEntryType.student) { ? studentConstructs(selected!.id, langCode)
eventsFuture = spaceMemberVocab(defaultSelected.id); : spaceMemberVocab(defaultSelected.id);
} else {
eventsFuture = studentConstructs(selected!.id, langCode);
}
} else if (defaultSelected.type == AnalyticsEntryType.student) { } else if (defaultSelected.type == AnalyticsEntryType.student) {
// in this case, we're on an individual's own analytics page // in this case, we're on an individual's own analytics page
if (selected?.type == AnalyticsEntryType.space || if (selected?.type == AnalyticsEntryType.space ||
selected?.type == AnalyticsEntryType.student) { selected?.type == AnalyticsEntryType.student) {
langCode = _pangeaController.languageController langCode = _pangeaController.languageController
.activeL2Code(roomID: selected!.id)!; .activeL2Code(roomID: selected!.id)!;
eventsFuture = myConstructs(langCode); eventsFuture = myConstructs(langCode);
} else { } else {
if (_pangeaController.languageController.userL2 == null) {
throw Exception("userL2 missing in vocabHeadsByAnalyticsSelected");
}
langCode = _pangeaController.languageController.userL2!.langCode; langCode = _pangeaController.languageController.userL2!.langCode;
eventsFuture = myConstructs(langCode); eventsFuture = myConstructs(langCode);
} }
@ -361,11 +369,9 @@ class AnalyticsController extends BaseController {
throw "No target language available"; throw "No target language available";
} }
if (selected?.type != AnalyticsEntryType.student) { eventFutures = selected?.type == AnalyticsEntryType.student
eventFutures = spaceMemberVocab(defaultSelected.id); ? studentConstructs(selected!.id, langCode)
} else { : spaceMemberVocab(defaultSelected.id);
eventFutures = studentConstructs(selected!.id, langCode);
}
} else if (defaultSelected.type == AnalyticsEntryType.student) { } else if (defaultSelected.type == AnalyticsEntryType.student) {
// in this case, we're on an individual's own analytics page // in this case, we're on an individual's own analytics page
@ -375,6 +381,9 @@ class AnalyticsController extends BaseController {
.activeL2Code(roomID: selected!.id)!; .activeL2Code(roomID: selected!.id)!;
eventFutures = myConstructs(langCode); eventFutures = myConstructs(langCode);
} else { } else {
if (_pangeaController.languageController.userL2 == null) {
throw "userL2 missing in constuctEventsByAnalyticsSelected";
}
langCode = _pangeaController.languageController.userL2!.langCode; langCode = _pangeaController.languageController.userL2!.langCode;
eventFutures = myConstructs(langCode); eventFutures = myConstructs(langCode);
} }

@ -1,13 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; 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/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart'; import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.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/client_extension.dart';
import '../extensions/pangea_room_extension.dart'; import '../extensions/pangea_room_extension.dart';
import '../models/constructs_analytics_model.dart'; import '../models/constructs_analytics_model.dart';
@ -62,8 +61,10 @@ class MyAnalyticsController {
List<Room> spaces, List<Room> spaces,
) async { ) async {
final List<Future<StudentAnalyticsEvent?>> events = []; final List<Future<StudentAnalyticsEvent?>> events = [];
for (final space in spaces) { if (_userId != null) {
events.add(space.getStudentAnalytics(_userId!)); for (final space in spaces) {
events.add(space.getStudentAnalytics(_userId!));
}
} }
return Future.wait(events); return Future.wait(events);
} }
@ -80,6 +81,7 @@ class MyAnalyticsController {
try { try {
final Map<String, List<OneConstructUse>> aggregatedVocabUse = {}; final Map<String, List<OneConstructUse>> aggregatedVocabUse = {};
for (final use in allUses) { for (final use in allUses) {
if (use.lemma == null) continue;
aggregatedVocabUse[use.lemma!] ??= []; aggregatedVocabUse[use.lemma!] ??= [];
aggregatedVocabUse[use.lemma]!.add(use); aggregatedVocabUse[use.lemma]!.add(use);
} }

@ -116,8 +116,6 @@ class PangeaController {
matrixState.loginHomeserverSummary = matrixState.loginHomeserverSummary =
await matrixState.getLoginClient().checkHomeserver(homeserver); await matrixState.getLoginClient().checkHomeserver(homeserver);
final ssoSupported = matrixState.loginHomeserverSummary!.loginFlows
.any((flow) => flow.type == 'm.login.sso');
try { try {
await matrixState.getLoginClient().register(); await matrixState.getLoginClient().register();

@ -169,6 +169,13 @@ class SubscriptionController extends BaseController {
} }
void setNewUserTrial() { void setNewUserTrial() {
if (_pangeaController.userController.userModel?.profile == null) {
ErrorHandler.logError(
m: "Null user profile in subscription settings",
s: StackTrace.current,
);
return;
}
final String profileCreatedAt = final String profileCreatedAt =
_pangeaController.userController.userModel!.profile!.createdAt; _pangeaController.userController.userModel!.profile!.createdAt;
final DateTime creationTimestamp = DateTime.parse(profileCreatedAt); final DateTime creationTimestamp = DateTime.parse(profileCreatedAt);

@ -2,14 +2,15 @@ import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'package:collection/collection.dart'; 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/language_keys.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_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: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 '../constants/local.key.dart';
import '../models/user_model.dart'; import '../models/user_model.dart';
import '../repo/user_repo.dart'; import '../repo/user_repo.dart';
@ -148,7 +149,11 @@ class UserController extends BaseController {
} }
_savePUserModel(PUserModel? pUserModel) { _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); _pangeaController.pStoreService.save(PLocalKey.user, jsonUser);
setState(data: pUserModel); setState(data: pUserModel);

@ -104,7 +104,6 @@ extension PangeaClient on Client {
}); });
if (analyticsRoom != null && if (analyticsRoom != null &&
analyticsRoom.membership == Membership.invite) { analyticsRoom.membership == Membership.invite) {
final membership = analyticsRoom.membership;
debugger(when: kDebugMode); debugger(when: kDebugMode);
analyticsRoom analyticsRoom
.join() .join()

@ -192,6 +192,7 @@ extension PangeaRoom on Room {
//a space show up in spaceChildren but the user has not been invited to them. //a space show up in spaceChildren but the user has not been invited to them.
List<String> get childrenAndGrandChildrenDirectChatIds { List<String> get childrenAndGrandChildrenDirectChatIds {
final List<String> nonDirectChatRoomIds = childrenAndGrandChildren final List<String> nonDirectChatRoomIds = childrenAndGrandChildren
.where((child) => child.roomId != null)
.map((e) => client.getRoomById(e.roomId!)) .map((e) => client.getRoomById(e.roomId!))
.where((r) => r != null && !r.isDirectChat) .where((r) => r != null && !r.isDirectChat)
.map((e) => e!.id) .map((e) => e!.id)
@ -280,11 +281,11 @@ extension PangeaRoom on Room {
bool isMadeByUser(String userId) => bool isMadeByUser(String userId) =>
getState(EventTypes.RoomCreate)?.senderId == userId; getState(EventTypes.RoomCreate)?.senderId == userId;
bool isMadeForLang(String langCode) => bool isMadeForLang(String langCode) {
getState(EventTypes.RoomCreate) final creationContent = getState(EventTypes.RoomCreate)?.content;
?.content return creationContent?.tryGet<String>(ModelKey.langCode) == langCode ||
.tryGet<String>(ModelKey.langCode) == creationContent?.tryGet<String>(ModelKey.oldLangCode) == langCode;
langCode; }
bool isAnalyticsRoomOfUser(String userId) => bool isAnalyticsRoomOfUser(String userId) =>
isAnalyticsRoom && isMadeByUser(userId); isAnalyticsRoom && isMadeByUser(userId);
@ -766,6 +767,9 @@ extension PangeaRoom on Room {
/// update state event and return eventId /// update state event and return eventId
Future<String> updateStateEvent(Event stateEvent) { Future<String> updateStateEvent(Event stateEvent) {
if (stateEvent.stateKey == null) {
throw Exception("stateEvent.stateKey is null");
}
return client.setRoomStateWithKey( return client.setRoomStateWithKey(
id, id,
stateEvent.type, stateEvent.type,
@ -923,7 +927,8 @@ extension PangeaRoom on Room {
if (isDirectChat) return false; if (isDirectChat) return false;
if (!isSpace) { if (!isSpace) {
if (eventsDefaultPowerLevel == null) return null; if (eventsDefaultPowerLevel == null) return null;
return eventsDefaultPowerLevel! >= ClassDefaultValues.powerLevelOfAdmin; return (eventsDefaultPowerLevel ?? 0) >=
ClassDefaultValues.powerLevelOfAdmin;
} }
for (final child in spaceChildren) { for (final child in spaceChildren) {
if (child.roomId == null) continue; if (child.roomId == null) continue;

@ -709,7 +709,11 @@ class _LanguageLocal {
// debugger(when: kDebugMode); // debugger(when: kDebugMode);
// ErrorHandler.logError(m: "Bad language key $key", s: StackTrace.current); // 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]; return (native ? item["nativeName"]! : item["name"]!).split(",")[0];
} }
@ -720,7 +724,8 @@ class _LanguageLocal {
final String searchName = name.toLowerCase().split(" ")[0]; final String searchName = name.toLowerCase().split(" ")[0];
for (final entry in isoLangs.entries) { 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; return entry.key;
} }
} }

@ -95,7 +95,7 @@ class RepresentationEvent {
_tokens = tokenEvents.first.getPangeaContent<PangeaMessageTokens>(); _tokens = tokenEvents.first.getPangeaContent<PangeaMessageTokens>();
return _tokens!.tokens; return _tokens?.tokens;
} }
Future<List<PangeaToken>?> tokensGlobal(BuildContext context) async { Future<List<PangeaToken>?> tokensGlobal(BuildContext context) async {

@ -1,8 +1,7 @@
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
import 'package:fluffychat/pangea/models/base_subscription_info.dart'; import 'package:fluffychat/pangea/models/base_subscription_info.dart';
import 'package:fluffychat/pangea/repo/subscription_repo.dart'; import 'package:fluffychat/pangea/repo/subscription_repo.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
class WebSubscriptionInfo extends SubscriptionInfo { class WebSubscriptionInfo extends SubscriptionInfo {
WebSubscriptionInfo({required super.pangeaController}) : super(); WebSubscriptionInfo({required super.pangeaController}) : super();
@ -12,6 +11,14 @@ class WebSubscriptionInfo extends SubscriptionInfo {
await setAppIds(); await setAppIds();
await setAllProducts(); await setAllProducts();
await setCustomerInfo(); await setCustomerInfo();
if (allProducts == null || appIds == null) {
Sentry.addBreadcrumb(
Breadcrumb(message: "No products found for current app"),
);
return;
}
availableSubscriptions = allProducts! availableSubscriptions = allProducts!
.where((product) => product.appId == appIds!.currentAppId) .where((product) => product.appId == appIds!.currentAppId)
.toList(); .toList();

@ -1,18 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; 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/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/models/chart_analytics_model.dart'; import 'package:fluffychat/pangea/models/chart_analytics_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';
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 '../../../../widgets/matrix.dart';
import '../../../controllers/pangea_controller.dart'; import '../../../controllers/pangea_controller.dart';
import '../../../utils/sync_status_util_v2.dart'; import '../../../utils/sync_status_util_v2.dart';
@ -41,7 +40,7 @@ class ClassAnalyticsV2Controller extends State<ClassAnalyticsPage> {
void initState() { void initState() {
super.initState(); super.initState();
Future.delayed(Duration.zero, () async { Future.delayed(Duration.zero, () async {
if (classRoom == null || !classRoom!.isSpace) { if (classRoom == null || (!(classRoom?.isSpace ?? false))) {
context.go('/rooms'); context.go('/rooms');
} }
stateSub = _pangeaController.matrixState.client.onRoomState.stream stateSub = _pangeaController.matrixState.client.onRoomState.stream
@ -57,16 +56,17 @@ class ClassAnalyticsV2Controller extends State<ClassAnalyticsPage> {
Future<void> getChatAndStudents() async { Future<void> getChatAndStudents() async {
try { try {
await classRoom!.requestParticipants(); await classRoom?.requestParticipants();
students = classRoom!.students; if (classRoom != null) {
students = classRoom!.students;
chats = classRoom!.spaceChildren chats = classRoom!.spaceChildren
.where((element) => element.roomId != null) .where((element) => element.roomId != null)
.map((e) => Matrix.of(context).client.getRoomById(e.roomId!)) .map((e) => Matrix.of(context).client.getRoomById(e.roomId!))
.where((r) => r != null) .where((r) => r != null)
.cast<Room>() .cast<Room>()
.toList(); .toList();
}
setState(() { setState(() {
_initialized = true; _initialized = true;

@ -1,14 +1,13 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:intl/intl.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/pages/analytics/bar_chart_placeholder_data.dart'; import 'package:fluffychat/pangea/pages/analytics/bar_chart_placeholder_data.dart';
import 'package:fluffychat/pangea/utils/error_handler.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/time_span.dart';
import '../../enum/use_type.dart'; import '../../enum/use_type.dart';
import '../../models/chart_analytics_model.dart'; import '../../models/chart_analytics_model.dart';
@ -245,11 +244,12 @@ class MessagesBarChartState extends State<MessagesBarChart> {
final TimeSeriesInterval? last = final TimeSeriesInterval? last =
intervalGroupings.isNotEmpty ? intervalGroupings.last.last : null; intervalGroupings.isNotEmpty ? intervalGroupings.last.last : null;
if (isInSameGroup( if (widget.chartAnalytics != null &&
last, isInSameGroup(
timeSeriesInterval, last,
widget.chartAnalytics!.timeSpan, timeSeriesInterval,
)) { widget.chartAnalytics!.timeSpan,
)) {
intervalGroupings.last.add(timeSeriesInterval); intervalGroupings.last.add(timeSeriesInterval);
} else { } else {
intervalGroupings.add([timeSeriesInterval]); intervalGroupings.add([timeSeriesInterval]);

@ -1,11 +1,9 @@
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
void showEditFieldDialog(BuildContext context, String title) async { void showEditFieldDialog(BuildContext context, String title) async {
// final room = Matrix.of(context).client.getRoomById(roomId!)!;
final input = await showTextInputDialog( final input = await showTextInputDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,

@ -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_class_and_invite.dart';
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
import 'package:fluffychat/widgets/matrix.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 { class AddExchangeToClass extends StatefulWidget {
const AddExchangeToClass({super.key}); const AddExchangeToClass({super.key});
@ -19,8 +17,12 @@ class AddExchangeToClassState extends State<AddExchangeToClass> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String spaceId = final String? spaceId =
GoRouterState.of(context).pathParameters['exchangeid']!; GoRouterState.of(context).pathParameters['exchangeid'];
if (spaceId == null) {
return const SizedBox();
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
centerTitle: true, centerTitle: true,

@ -121,7 +121,7 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}"; managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}";
} }
final String? purchaseAppId = final String? purchaseAppId =
subscriptionController.subscription?.currentSubscription?.appId!; subscriptionController.subscription?.currentSubscription?.appId;
if (purchaseAppId == null) return; if (purchaseAppId == null) return;
final SubscriptionAppIds? appIds = final SubscriptionAppIds? appIds =

@ -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/pages/sign_up/signup_view.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SignupPage extends StatefulWidget { class SignupPage extends StatefulWidget {
const SignupPage({super.key}); const SignupPage({super.key});
@ -133,7 +131,7 @@ class SignupPageController extends State<SignupPage> {
//Pangea# //Pangea#
// Set displayname // Set displayname
if (displayname != localPart) { if (displayname != localPart && client.userID != null) {
await client.setDisplayName( await client.setDisplayName(
client.userID!, client.userID!,
displayname, displayname,

@ -1,11 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/config/environment.dart'; import 'package:fluffychat/pangea/config/environment.dart';
import 'package:fluffychat/pangea/enum/span_choice_type.dart'; import 'package:fluffychat/pangea/enum/span_choice_type.dart';
import 'package:fluffychat/pangea/enum/span_data_type.dart'; import 'package:fluffychat/pangea/enum/span_data_type.dart';
import 'package:fluffychat/pangea/models/span_data.dart'; import 'package:fluffychat/pangea/models/span_data.dart';
import 'package:http/http.dart';
import '../constants/model_keys.dart'; import '../constants/model_keys.dart';
import '../network/requests.dart'; import '../network/requests.dart';
import '../network/urls.dart'; import '../network/urls.dart';
@ -31,15 +31,15 @@ class SpanDataRepo {
} }
} }
Future<SpanDetailsRepoReqAndRes> getMock(SpanDetailsRepoReqAndRes req) async { // Future<SpanDetailsRepoReqAndRes> getMock(SpanDetailsRepoReqAndRes req) async {
await Future.delayed(const Duration(seconds: 2)); // await Future.delayed(const Duration(seconds: 2));
if (req.span.choices != null && // if (req.span.choices != null &&
req.span.choices!.any((element) => element.selected)) { // req.span.choices!.any((element) => element.selected)) {
return req..span = mockReponseWithHintOne.span; // return req..span = mockReponseWithHintOne.span;
} else { // } else {
return req..span = mockReponseWithChoices.span; // return req..span = mockReponseWithChoices.span;
} // }
} // }
class SpanDetailsRepoReqAndRes { class SpanDetailsRepoReqAndRes {
String userL1; String userL1;
@ -105,9 +105,9 @@ SpanDetailsRepoReqAndRes get mockReponseWithChoices {
return res; return res;
} }
SpanDetailsRepoReqAndRes get mockReponseWithHintOne { // SpanDetailsRepoReqAndRes get mockReponseWithHintOne {
final SpanDetailsRepoReqAndRes res = mockReponseWithChoices; // final SpanDetailsRepoReqAndRes res = mockReponseWithChoices;
res.span.choices![1].selected = true; // res.span.choices![1].selected = true;
res.span.message = "Conjugation error"; // res.span.message = "Conjugation error";
return res; // return res;
} // }

@ -1,9 +1,8 @@
import 'package:flutter/widgets.dart';
import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
import 'package:flutter/widgets.dart';
import '../../config/firebase_options.dart'; import '../../config/firebase_options.dart';
import '../enum/use_type.dart'; import '../enum/use_type.dart';
@ -161,22 +160,26 @@ class GoogleAnalytics {
); );
} }
static FirebaseAnalyticsObserver getAnalyticsObserver() => static FirebaseAnalyticsObserver getAnalyticsObserver() {
FirebaseAnalyticsObserver( if (analytics == null) {
analytics: analytics!, throw Exception("Firebase Analytics not initialized");
routeFilter: (route) { }
// By default firebase only tracks page routes return FirebaseAnalyticsObserver(
if (route is! PageRoute || analytics: analytics!,
// No user logged in, so we dont track routeFilter: (route) {
route.settings.name == "login" || // By default firebase only tracks page routes
route.settings.name == "/home" || if (route is! PageRoute ||
route.settings.name == "connect" || // No user logged in, so we dont track
route.settings.name == "signup") { route.settings.name == "login" ||
return false; route.settings.name == "/home" ||
} route.settings.name == "connect" ||
final String? name = route.settings.name; route.settings.name == "signup") {
debugPrint("navigating to route: $name"); return false;
return true; }
}, final String? name = route.settings.name;
); debugPrint("navigating to route: $name");
return true;
},
);
}
} }

@ -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<List<Room>> joinAllSpaceChats(Room space, Client client) async {
final List<String> childrenIds =
space.spaceChildren.map((x) => x.roomId!).toList();
final List<Room> 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<Room?> 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;
}

@ -49,6 +49,7 @@ Future<void> unlockChat(Room room, Client client) async {
Future<void> lockSpace(Room space, Client client) async { Future<void> lockSpace(Room space, Client client) async {
for (final spaceChild in space.spaceChildren) { for (final spaceChild in space.spaceChildren) {
if (spaceChild.roomId == null) continue;
Room? child = client.getRoomById(spaceChild.roomId!); Room? child = client.getRoomById(spaceChild.roomId!);
if (child == null) { if (child == null) {
try { try {
@ -70,6 +71,7 @@ Future<void> lockSpace(Room space, Client client) async {
Future<void> unlockSpace(Room space, Client client) async { Future<void> unlockSpace(Room space, Client client) async {
for (final spaceChild in space.spaceChildren) { for (final spaceChild in space.spaceChildren) {
if (spaceChild.roomId == null) continue;
final Room? child = client.getRoomById(spaceChild.roomId!); final Room? child = client.getRoomById(spaceChild.roomId!);
if (child == null) continue; if (child == null) continue;
child.isSpace child.isSpace

@ -1,13 +1,14 @@
import 'package:fluffychat/widgets/matrix.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';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../utils/matrix_sdk_extensions/matrix_locals.dart';
void setClassDisplayname(BuildContext context, String? roomId) async { 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( final TextEditingController textFieldController = TextEditingController(
text: room.getLocalizedDisplayname( text: room.getLocalizedDisplayname(
MatrixLocals( MatrixLocals(

@ -68,6 +68,7 @@ class ToolbarDisplayController {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Widget overlayEntry; Widget overlayEntry;
if (toolbar == null) return;
try { try {
overlayEntry = Column( overlayEntry = Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

@ -1,15 +1,14 @@
import 'dart:developer'; 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/foundation.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';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.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 '../../../widgets/matrix.dart';
import '../../utils/error_handler.dart'; import '../../utils/error_handler.dart';
import '../../utils/firebase_analytics.dart'; import '../../utils/firebase_analytics.dart';
@ -92,7 +91,7 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
Future<void> _addSingleParent(String roomToAddId, Room newParent) async { Future<void> _addSingleParent(String roomToAddId, Room newParent) async {
GoogleAnalytics.addParent(roomToAddId, newParent.classCode); GoogleAnalytics.addParent(roomToAddId, newParent.classCode);
final List<List<User>> existingMembers = await Future.wait([ final List<List<User>> existingMembers = await Future.wait([
room!.requestParticipants(), room?.requestParticipants() ?? Future.value([]),
newParent.requestParticipants(), newParent.requestParticipants(),
]); ]);
final List<User> roomMembers = existingMembers[0]; final List<User> roomMembers = existingMembers[0];
@ -102,7 +101,7 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
newParent.setSpaceChild(roomToAddId, suggested: true), newParent.setSpaceChild(roomToAddId, suggested: true),
]; ];
for (final spaceMember for (final spaceMember
in spaceMembers.where((element) => element.id != room!.client.userID)) { in spaceMembers.where((element) => element.id != room?.client.userID)) {
if (!roomMembers.any( if (!roomMembers.any(
(m) => m.id == spaceMember.id && m.membership == Membership.join, (m) => m.id == spaceMember.id && m.membership == Membership.join,
)) { )) {
@ -118,7 +117,7 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
//function for kicking single student and haandling error //function for kicking single student and haandling error
Future<void> _kickSpaceMember(User spaceMember) async { Future<void> _kickSpaceMember(User spaceMember) async {
try { try {
await room!.kick(spaceMember.id); await room?.kick(spaceMember.id);
debugPrint('Kicked ${spaceMember.id}'); debugPrint('Kicked ${spaceMember.id}');
} catch (e) { } catch (e) {
debugger(when: kDebugMode); debugger(when: kDebugMode);
@ -129,7 +128,7 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
//function for adding single student and haandling error //function for adding single student and haandling error
Future<void> _inviteSpaceMember(User spaceMember) async { Future<void> _inviteSpaceMember(User spaceMember) async {
try { try {
await room!.invite(spaceMember.id); await room?.invite(spaceMember.id);
debugPrint('added ${spaceMember.id}'); debugPrint('added ${spaceMember.id}');
} catch (e) { } catch (e) {
debugger(when: kDebugMode); debugger(when: kDebugMode);
@ -153,14 +152,14 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
return; return;
} }
final List<List<User>> roomsMembers = await Future.wait([ final List<List<User>> roomsMembers = await Future.wait([
room!.requestParticipants(), room?.requestParticipants() ?? Future.value([]),
spaceToRemove.requestParticipants(), spaceToRemove.requestParticipants(),
]); ]);
final List<User> toKick = roomsMembers[1] final List<User> toKick = roomsMembers[1]
.where( .where(
(element) => (element) =>
element.id != room!.client.userID && element.id != room?.client.userID &&
roomsMembers[0].any((m) => m.id == element.id), roomsMembers[0].any((m) => m.id == element.id),
) )
.toList(); .toList();
@ -176,9 +175,11 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
// if (widget.setParentState != null) { // if (widget.setParentState != null) {
// widget.setParentState!(); // widget.setParentState!();
// } // }
await room!.requestParticipants(); await room?.requestParticipants();
GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id); if (room != null) {
GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id);
}
return; return;
} }

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart'; import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart';
import 'package:fluffychat/pangea/widgets/igc/card_header.dart'; import 'package:fluffychat/pangea/widgets/igc/card_header.dart';
import 'package:flutter/material.dart';
class CardErrorWidget extends StatelessWidget { class CardErrorWidget extends StatelessWidget {
final Object? error; final Object? error;
@ -28,11 +27,9 @@ class CardErrorWidget extends StatelessWidget {
CardHeader( CardHeader(
text: errorCopy.title, text: errorCopy.title,
botExpression: BotExpression.addled, botExpression: BotExpression.addled,
onClose: () => choreographer != null onClose: () => choreographer?.onMatchError(
? choreographer!.onMatchError( cursorOffset: offset,
cursorOffset: offset, ),
)
: null,
), ),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
Center( Center(

@ -66,20 +66,26 @@ class SpanCardState extends State<SpanCard> {
Future<void> getSpanDetails() async { Future<void> getSpanDetails() async {
try { try {
if (widget.scm.pangeaMatch?.isITStart ?? false) return; if (widget.scm.pangeaMatch?.isITStart ?? false) return;
if (!mounted) return;
setState(() { setState(() {
fetchingData = true; fetchingData = true;
}); });
await widget.scm.choreographer.igc.getSpanDetails(widget.scm.matchIndex); await widget.scm.choreographer.igc.getSpanDetails(widget.scm.matchIndex);
setState(() => fetchingData = false); if (mounted) {
setState(() => fetchingData = false);
}
} catch (e) { } catch (e) {
// debugger(when: kDebugMode); // debugger(when: kDebugMode);
ErrorHandler.logError(e: e, s: StackTrace.current); ErrorHandler.logError(e: e, s: StackTrace.current);
setState(() { if (mounted) {
error = e; setState(() {
fetchingData = false; error = e;
}); fetchingData = false;
});
}
} }
} }
@ -105,10 +111,10 @@ class WordMatchContent extends StatelessWidget {
.scm .scm
.choreographer .choreographer
.igc .igc
.igcTextData! .igcTextData
.matches[controller.widget.scm.matchIndex] ?.matches[controller.widget.scm.matchIndex]
.match .match
.choices![index] .choices?[index]
.selected = true; .selected = true;
controller.setState(() => ()); controller.setState(() => ());
@ -291,6 +297,10 @@ class PromptAndFeedback extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (controller.widget.scm.pangeaMatch == null) {
return const SizedBox();
}
return Container( return Container(
constraints: controller.widget.scm.pangeaMatch!.isITStart constraints: controller.widget.scm.pangeaMatch!.isITStart
? null ? null

@ -190,7 +190,10 @@ class WordDataCardView extends StatelessWidget {
style: BotStyle.text(context), style: BotStyle.text(context),
), ),
const SizedBox(height: 5.0), 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( WordNetInfo(
wordData: controller.wordData!, wordData: controller.wordData!,
activeL1: controller.activeL1!, activeL1: controller.activeL1!,

Loading…
Cancel
Save