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
// void requestHistory() async {
Future<void> 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<ChatPageWithRoom>
numRequests++;
}
}
// #Pangea
// Pangea#
} catch (e, s) {
Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
if (!mounted) return;
@ -1575,6 +1576,8 @@ class ChatController extends State<ChatPageWithRoom>
setPangeaMessageEvent(eventId);
if (_pangeaMessageEvents[eventId] == null) return;
}
try {
_toolbarDisplayControllers[eventId] = ToolbarDisplayController(
targetId: event.eventId,
pangeaMessageEvent: _pangeaMessageEvents[eventId]!,
@ -1582,6 +1585,18 @@ class ChatController extends State<ChatPageWithRoom>
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) {

@ -256,7 +256,10 @@ class _SpaceViewState extends State<SpaceView> {
// #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<SpaceView> {
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#

@ -119,7 +119,6 @@ class HomeserverPickerController extends State<HomeserverPicker> {
// #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<HomeserverPicker> {
: '${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},
);

@ -192,8 +192,6 @@ class NewSpaceController extends State<NewSpace> {
// Pangea#
);
// #Pangea
Room? room = Matrix.of(context).client.getRoomById(spaceId);
final List<Future<dynamic>> futures = [
Matrix.of(context).client.waitForRoomInSync(spaceId, join: true),
];
@ -202,8 +200,6 @@ class NewSpaceController extends State<NewSpace> {
}
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<NewSpace> {
);
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,

@ -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 =>

@ -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]);
}
},

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

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

@ -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<ITFeedbackCard> {
}
Future<void> translateFeedback() async {
if (res == null) {
ErrorHandler.logError(
m: "Cannot translate feedback because res is null",
);
return;
}
setState(() {
isTranslating = true;
});

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

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

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

@ -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,9 +61,11 @@ class MyAnalyticsController {
List<Room> spaces,
) async {
final List<Future<StudentAnalyticsEvent?>> events = [];
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<String, List<OneConstructUse>> aggregatedVocabUse = {};
for (final use in allUses) {
if (use.lemma == null) continue;
aggregatedVocabUse[use.lemma!] ??= [];
aggregatedVocabUse[use.lemma]!.add(use);
}

@ -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();

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

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

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

@ -192,6 +192,7 @@ extension PangeaRoom on Room {
//a space show up in spaceChildren but the user has not been invited to them.
List<String> get childrenAndGrandChildrenDirectChatIds {
final List<String> 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<String>(ModelKey.langCode) ==
langCode;
bool isMadeForLang(String langCode) {
final creationContent = getState(EventTypes.RoomCreate)?.content;
return creationContent?.tryGet<String>(ModelKey.langCode) == langCode ||
creationContent?.tryGet<String>(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<String> 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;

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

@ -95,7 +95,7 @@ class RepresentationEvent {
_tokens = tokenEvents.first.getPangeaContent<PangeaMessageTokens>();
return _tokens!.tokens;
return _tokens?.tokens;
}
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/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();

@ -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<ClassAnalyticsPage> {
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<ClassAnalyticsPage> {
Future<void> getChatAndStudents() async {
try {
await classRoom!.requestParticipants();
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<Room>()
.toList();
}
setState(() {
_initialized = true;

@ -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,7 +244,8 @@ class MessagesBarChartState extends State<MessagesBarChart> {
final TimeSeriesInterval? last =
intervalGroupings.isNotEmpty ? intervalGroupings.last.last : null;
if (isInSameGroup(
if (widget.chartAnalytics != null &&
isInSameGroup(
last,
timeSeriesInterval,
widget.chartAnalytics!.timeSpan,

@ -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,

@ -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<AddExchangeToClass> {
@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,

@ -121,7 +121,7 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}";
}
final String? purchaseAppId =
subscriptionController.subscription?.currentSubscription?.appId!;
subscriptionController.subscription?.currentSubscription?.appId;
if (purchaseAppId == null) return;
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/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<SignupPage> {
//Pangea#
// Set displayname
if (displayname != localPart) {
if (displayname != localPart && client.userID != null) {
await client.setDisplayName(
client.userID!,
displayname,

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

@ -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,8 +160,11 @@ class GoogleAnalytics {
);
}
static FirebaseAnalyticsObserver getAnalyticsObserver() =>
FirebaseAnalyticsObserver(
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
@ -180,3 +182,4 @@ class GoogleAnalytics {
},
);
}
}

@ -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 {
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<void> lockSpace(Room space, Client client) async {
Future<void> 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

@ -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(

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

@ -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<AddToClassAndInviteToggles> {
Future<void> _addSingleParent(String roomToAddId, Room newParent) async {
GoogleAnalytics.addParent(roomToAddId, newParent.classCode);
final List<List<User>> existingMembers = await Future.wait([
room!.requestParticipants(),
room?.requestParticipants() ?? Future.value([]),
newParent.requestParticipants(),
]);
final List<User> roomMembers = existingMembers[0];
@ -102,7 +101,7 @@ class AddToClassAndInviteState extends State<AddToClassAndInviteToggles> {
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<AddToClassAndInviteToggles> {
//function for kicking single student and haandling error
Future<void> _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<AddToClassAndInviteToggles> {
//function for adding single student and haandling error
Future<void> _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<AddToClassAndInviteToggles> {
return;
}
final List<List<User>> roomsMembers = await Future.wait([
room!.requestParticipants(),
room?.requestParticipants() ?? Future.value([]),
spaceToRemove.requestParticipants(),
]);
final List<User> 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<AddToClassAndInviteToggles> {
// if (widget.setParentState != null) {
// widget.setParentState!();
// }
await room!.requestParticipants();
await room?.requestParticipants();
if (room != null) {
GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id);
}
return;
}

@ -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(
onClose: () => choreographer?.onMatchError(
cursorOffset: offset,
)
: null,
),
),
const SizedBox(height: 10.0),
Center(

@ -66,22 +66,28 @@ class SpanCardState extends State<SpanCard> {
Future<void> 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);
if (mounted) {
setState(() => fetchingData = false);
}
} catch (e) {
// debugger(when: kDebugMode);
ErrorHandler.logError(e: e, s: StackTrace.current);
if (mounted) {
setState(() {
error = e;
fetchingData = false;
});
}
}
}
@override
Widget build(BuildContext context) {
@ -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

@ -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!,

Loading…
Cancel
Save