formatting once more

pull/795/head
Brord van Wierst 2 years ago
parent a72c9a1b4f
commit a30234db67
No known key found for this signature in database
GPG Key ID: 20E7ACBD8E02BC11

@ -23,11 +23,11 @@ jobs:
- name: Prepare web
run: ./scripts/prepare-web.sh
- name: Build Release Web
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/client/web/"
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/client/"
- name: Build Website
run: |
mkdir public
mv build/web/ public/web
mv build/web/ public/
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:

@ -57,14 +57,15 @@ class DefaultFirebaseOptions {
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyAyWBbl83WXzbVr6txyCmlUsZhpWomQfdg',
appId: '1:545984292675:android:d808acce7a80c20bb931f6',
messagingSenderId: '545984292675',
projectId: 'pangea-chat-936ee',
databaseURL: 'https://pangea-chat-936ee-default-rtdb.firebaseio.com',
storageBucket: 'pangea-chat-936ee.appspot.com',
androidClientId:
'545984292675-2amsnoan1mt6lec1fld1a7eagu6gej7o.apps.googleusercontent.com',);
apiKey: 'AIzaSyAyWBbl83WXzbVr6txyCmlUsZhpWomQfdg',
appId: '1:545984292675:android:d808acce7a80c20bb931f6',
messagingSenderId: '545984292675',
projectId: 'pangea-chat-936ee',
databaseURL: 'https://pangea-chat-936ee-default-rtdb.firebaseio.com',
storageBucket: 'pangea-chat-936ee.appspot.com',
androidClientId:
'545984292675-2amsnoan1mt6lec1fld1a7eagu6gej7o.apps.googleusercontent.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyCl8QZd9_PnaqJY2zLHCwlsmSWdq7hnH-U',

@ -228,10 +228,11 @@ class Choreographer {
void onITChoiceSelect(ITStep step) {
choreoRecord.addRecord(_textController.text, step: step);
_textController.setSystemText(
_textController.text + step.continuances[step.chosen!].text,
step.continuances[step.chosen!].gold
? EditType.itGold
: EditType.itStandard,);
_textController.text + step.continuances[step.chosen!].text,
step.continuances[step.chosen!].gold
? EditType.itGold
: EditType.itStandard,
);
_textController.selection =
TextSelection.collapsed(offset: _textController.text.length);
giveInputFocus();
@ -328,15 +329,18 @@ class Choreographer {
}
igc.igcTextData!.matches[matchIndex].status = PangeaMatchStatus.ignored;
choreoRecord.addRecord(_textController.text,
match: igc.igcTextData!.matches[matchIndex],);
choreoRecord.addRecord(
_textController.text,
match: igc.igcTextData!.matches[matchIndex],
);
igc.igcTextData!.matches.removeAt(matchIndex);
} catch (err, stack) {
debugger(when: kDebugMode);
Sentry.addBreadcrumb(
Breadcrumb.fromJson(
{"igcTextData": igc.igcTextData?.toJson(), "offset": cursorOffset},),
{"igcTextData": igc.igcTextData?.toJson(), "offset": cursorOffset},
),
);
ErrorHandler.logError(
e: err,

@ -202,7 +202,8 @@ class IgcController {
if (igcTextData!.originalInput != choreographer.currentText) {
debugPrint(
"returning isIGCTextDataRelevant false because text has changed",);
"returning isIGCTextDataRelevant false because text has changed",
);
return false;
}
return true;

@ -74,10 +74,13 @@ class ITController {
// try {
if (_itStartData == null || _itStartData!.text.isEmpty) {
Sentry.addBreadcrumb(
Breadcrumb(message: "choreo context", data: {
"igcTextData": choreographer.igc.igcTextData?.toJson(),
"currentText": choreographer.currentText,
},),
Breadcrumb(
message: "choreo context",
data: {
"igcTextData": choreographer.igc.igcTextData?.toJson(),
"currentText": choreographer.currentText,
},
),
);
throw Exception("null _itStartData or empty text in _setSourceText");
}
@ -122,10 +125,12 @@ class ITController {
if (sourceText == null) await _setSourceText();
if (useCustomInput && currentITStep != null) {
completedITSteps.add(ITStep(
currentITStep!.continuances,
customInput: currentText,
),);
completedITSteps.add(
ITStep(
currentITStep!.continuances,
customInput: currentText,
),
);
}
currentITStep = null;

@ -63,13 +63,14 @@ class Choice {
}
class ChoiceItem extends StatelessWidget {
const ChoiceItem(
{super.key,
required this.theme,
required this.onLongPress,
required this.onPressed,
required this.entry,
required this.isSelected,});
const ChoiceItem({
super.key,
required this.theme,
required this.onLongPress,
required this.onPressed,
required this.entry,
required this.isSelected,
});
final MapEntry<int, Choice> entry;
final ThemeData theme;
@ -101,7 +102,8 @@ class ChoiceItem extends StatelessWidget {
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
const EdgeInsets.symmetric(horizontal: 7),),
const EdgeInsets.symmetric(horizontal: 7),
),
//if index is selected, then give the background a slight primary color
backgroundColor: MaterialStateProperty.all<Color>(
entry.value.color != null

@ -24,7 +24,8 @@ class ChoreographerHasErrorButton extends StatelessWidget {
SnackBar(
duration: const Duration(seconds: 5),
content: Text(
"${error.title(context)} ${error.description(context)}",),
"${error.title(context)} ${error.description(context)}",
),
),
);
} else if (error.type == ChoreoErrorType.unsubscribed) {

@ -30,13 +30,14 @@ class ITBar extends StatelessWidget {
child: Container(
key: choreographer.itBarLinkAndKey.key,
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.light
? Colors.white
: Colors.black,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppConfig.borderRadius),
topRight: Radius.circular(AppConfig.borderRadius),
),),
color: Theme.of(context).brightness == Brightness.light
? Colors.white
: Colors.black,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppConfig.borderRadius),
topRight: Radius.circular(AppConfig.borderRadius),
),
),
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
child: Stack(
@ -89,7 +90,8 @@ class ITBar extends StatelessWidget {
? ChoiceFeedbackText(controller: controller)
: controller.isTranslationDone
? TranslationFeedback(
controller: controller,)
controller: controller,
)
: ITChoices(controller: controller),
),
),
@ -152,10 +154,11 @@ class OriginalText extends StatelessWidget {
constraints: const BoxConstraints(minHeight: 50),
padding: const EdgeInsets.only(left: 60.0, right: 40.0),
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(AppConfig.borderRadius),
topRight: Radius.circular(AppConfig.borderRadius),
),),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(AppConfig.borderRadius),
topRight: Radius.circular(AppConfig.borderRadius),
),
),
child: Row(
//PTODO - does this already update after reset or we need to setState?
mainAxisAlignment: MainAxisAlignment.center,
@ -214,20 +217,25 @@ class ITChoices extends StatelessWidget {
return controller.sourceText;
}
void showCard(BuildContext context, int index,
[Color? borderColor, String? choiceFeedback,]) =>
void showCard(
BuildContext context,
int index, [
Color? borderColor,
String? choiceFeedback,
]) =>
OverlayUtil.showPositionedCard(
context: context,
cardToShow: WordDataCard(
word: controller.currentITStep!.continuances[index].text,
wordLang: controller.targetLangCode,
fullText: sourceText ?? controller.choreographer.currentText,
fullTextLang: sourceText != null
? controller.sourceLangCode
: controller.targetLangCode,
hasInfo: controller.currentITStep!.continuances[index].hasInfo,
choiceFeedback: choiceFeedback,
room: controller.choreographer.chatController.room,),
word: controller.currentITStep!.continuances[index].text,
wordLang: controller.targetLangCode,
fullText: sourceText ?? controller.choreographer.currentText,
fullTextLang: sourceText != null
? controller.sourceLangCode
: controller.targetLangCode,
hasInfo: controller.currentITStep!.continuances[index].hasInfo,
choiceFeedback: choiceFeedback,
room: controller.choreographer.chatController.room,
),
cardSize: const Size(300, 300),
borderColor: borderColor,
transformTargetId: controller.choreographer.itBarTransformTargetKey,
@ -259,8 +267,10 @@ class ITChoices extends StatelessWidget {
controller.currentITStep!.continuances[index];
debugPrint("is gold? ${continuance.gold}");
if (continuance.level == 1 || continuance.wasClicked) {
Future.delayed(const Duration(milliseconds: 500),
() => controller.selectTranslation(index),);
Future.delayed(
const Duration(milliseconds: 500),
() => controller.selectTranslation(index),
);
} else {
showCard(
context,
@ -307,8 +317,9 @@ class ITError extends StatelessWidget {
"${errorCopy.title}\n${errorCopy.body}",
// Haga clic en su mensaje para ver los significados de las palabras.
style: TextStyle(
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,),
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,
),
),
),
ITRestartButton(controller: controller),

@ -9,15 +9,19 @@ class ItShimmer extends StatelessWidget {
final String originalSpan;
Iterable<Widget> renderShimmerIfListEmpty(BuildContext context,
{int noOfBars = 3,}) {
Iterable<Widget> renderShimmerIfListEmpty(
BuildContext context, {
int noOfBars = 3,
}) {
final List<String> dummyStrings = [];
for (int i = 0; i < noOfBars; i++) {
dummyStrings.add(originalSpan);
}
return dummyStrings.map((e) => ITShimmerElement(
text: e,
),);
return dummyStrings.map(
(e) => ITShimmerElement(
text: e,
),
);
}
// PTODO - bring this back, make it shimmer
@ -57,14 +61,16 @@ class ITShimmerElement extends StatelessWidget {
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
const EdgeInsets.symmetric(horizontal: 7),),
const EdgeInsets.symmetric(horizontal: 7),
),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
backgroundColor: MaterialStateProperty.all<Color>(
AppConfig.primaryColor.withOpacity(0.2),),
AppConfig.primaryColor.withOpacity(0.2),
),
),
onPressed: () {},
child: Text(

@ -77,7 +77,8 @@ class AlternativeTranslations extends StatelessWidget {
// choices: controller.choreographer.altTranslator.translations,
onPressed: (int index) {
controller.choreographer.onSelectAlternativeTranslation(
controller.choreographer.altTranslator.translations[index],);
controller.choreographer.altTranslator.translations[index],
);
},
uniqueKeyForLayerLink: (int index) => "altTranslation$index",
selectedChoiceIndex: null,

@ -1,7 +1,5 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
class Environment {
static bool get itIsTime =>
DateTime.utc(2023, 1, 25).isBefore(DateTime.now());

@ -22,12 +22,15 @@ class ContextualDefinitionController {
}
_ContextualDefinitionCacheItem? _getLocal(
ContextualDefinitionRequestModel req,) =>
ContextualDefinitionRequestModel req,
) =>
_definitions.firstWhereOrNull(
(e) => e.word == req.word && e.fullText == req.fullText,);
(e) => e.word == req.word && e.fullText == req.fullText,
);
Future<ContextualDefinitionResponseModel?> get(
ContextualDefinitionRequestModel req,) {
ContextualDefinitionRequestModel req,
) {
final _ContextualDefinitionCacheItem? localItem = _getLocal(req);
if (localItem != null) return localItem.data;
@ -55,7 +58,8 @@ class ContextualDefinitionController {
return res;
} catch (err, stack) {
debugPrint(
"error getting contextual definition for ${request.word} in '${request.fullText}'",);
"error getting contextual definition for ${request.word} in '${request.fullText}'",
);
ErrorHandler.logError(e: err, s: stack, data: request.toJson());
return null;
}
@ -115,12 +119,13 @@ class ContextualDefinitionRequestModel {
final String fullTextLang;
final String wordLang;
ContextualDefinitionRequestModel(
{required this.fullText,
required this.word,
required this.feedbackLang,
required this.fullTextLang,
required this.wordLang,});
ContextualDefinitionRequestModel({
required this.fullText,
required this.word,
required this.feedbackLang,
required this.fullTextLang,
required this.wordLang,
});
Map<String, dynamic> toJson() => {
ModelKey.fullText: fullText,

@ -34,9 +34,11 @@ class PangeaLanguage {
await _saveFlags(_langList);
await saveLastFetchDate();
}
_langList.removeWhere((element) =>
LanguageModel.codeFromNameOrCode(element.langCode) ==
LanguageKeys.unknownLanguage,);
_langList.removeWhere(
(element) =>
LanguageModel.codeFromNameOrCode(element.langCode) ==
LanguageKeys.unknownLanguage,
);
_langList.sort((a, b) => a.displayName.compareTo(b.displayName));
_langList.insert(0, LanguageModel.multiLingual());
} catch (err, stack) {

@ -153,13 +153,15 @@ class AnalyticsController extends BaseController {
chatId: chatId,
);
_cachedModels.add(CacheModel(
timeSpan: timeSpan,
classId: classRoom?.id,
studentId: studentId,
chatId: chatId,
chartAnalyticsModel: newModel,
),);
_cachedModels.add(
CacheModel(
timeSpan: timeSpan,
classId: classRoom?.id,
studentId: studentId,
chatId: chatId,
chartAnalyticsModel: newModel,
),
);
return newModel;
} catch (err, s) {
@ -170,8 +172,10 @@ class AnalyticsController extends BaseController {
}
Future<VocabHeadwords> vocabHeadwordsWithTotals(
String langCode, List<ConstructEvent> vocab,
[String? chatId,]) async {
String langCode,
List<ConstructEvent> vocab, [
String? chatId,
]) async {
final VocabHeadwords vocabHeadwords =
await VocabHeadwords.getHeadwords(langCode);
for (final vocabList in vocabHeadwords.lists) {
@ -213,8 +217,10 @@ class AnalyticsController extends BaseController {
final List<RecentMessageRecord> msgs = [];
for (final event in studentAnalyticsSummaryEvents) {
if (event != null) {
msgs.addAll(event.content.messages
.where((m) => directChatIds.contains(m.chatId)),);
msgs.addAll(
event.content.messages
.where((m) => directChatIds.contains(m.chatId)),
);
} else {
debugPrint("studentAnalyticsSummaryEvent is null");
}
@ -225,13 +231,15 @@ class AnalyticsController extends BaseController {
chatId: null,
);
_cachedModels.add(CacheModel(
timeSpan: timeSpan,
classId: classRoom.id,
studentId: null,
chatId: AnalyticsEntryType.privateChats.toString(),
chartAnalyticsModel: newModel,
),);
_cachedModels.add(
CacheModel(
timeSpan: timeSpan,
classId: classRoom.id,
studentId: null,
chatId: AnalyticsEntryType.privateChats.toString(),
chartAnalyticsModel: newModel,
),
);
return newModel;
} catch (err, s) {
@ -251,7 +259,9 @@ class AnalyticsController extends BaseController {
}
Future<List<ConstructEvent>> studentConstructs(
String studentId, String langCode,) {
String studentId,
String langCode,
) {
final Room? analyticsRoom = _pangeaController.matrixState.client
.analyticsRoomLocal(langCode, studentId);
return analyticsRoom!.allConstructEvents;
@ -382,17 +392,21 @@ class AnalyticsController extends BaseController {
if (selected?.type == AnalyticsEntryType.room) {
chatIdsToFilterBy.add(selected!.id);
} else if (selected?.type == AnalyticsEntryType.privateChats) {
chatIdsToFilterBy.addAll(_pangeaController.matrixState.client
.getRoomById(defaultSelected.id)
?.childrenAndGrandChildrenDirectChatIds ??
[],);
chatIdsToFilterBy.addAll(
_pangeaController.matrixState.client
.getRoomById(defaultSelected.id)
?.childrenAndGrandChildrenDirectChatIds ??
[],
);
} else if (defaultSelected.type == AnalyticsEntryType.space) {
chatIdsToFilterBy.addAll(_pangeaController.matrixState.client
.getRoomById(defaultSelected.id)
?.childrenAndGrandChildren
.where((e) => e.roomId != null)
.map((e) => e.roomId!) ??
[],);
chatIdsToFilterBy.addAll(
_pangeaController.matrixState.client
.getRoomById(defaultSelected.id)
?.childrenAndGrandChildren
.where((e) => e.roomId != null)
.map((e) => e.roomId!) ??
[],
);
}
if (chatIdsToFilterBy.isNotEmpty) {
for (final event in events) {

@ -30,8 +30,10 @@ class MessageDataController extends BaseController {
}
CacheItem? getItem(String parentId, String type, String langCode) =>
_cache.firstWhereOrNull((e) =>
e.parentId == parentId && e.type == type && e.langCode == langCode,);
_cache.firstWhereOrNull(
(e) =>
e.parentId == parentId && e.type == type && e.langCode == langCode,
);
Future<PangeaMessageTokens?> _getTokens(
TokensRequestModel req,
@ -66,7 +68,8 @@ class MessageDataController extends BaseController {
} catch (err, stack) {
Sentry.addBreadcrumb(
Breadcrumb(
message: "err in _getTokenEvent with repEventId $repEventId",),
message: "err in _getTokenEvent with repEventId $repEventId",
),
);
Sentry.addBreadcrumb(
Breadcrumb.fromJson({"req": req.toJson()}),
@ -89,17 +92,19 @@ class MessageDataController extends BaseController {
getItem(repEventId, PangeaEventTypes.tokens, req.userL2);
if (item != null) return item.data;
_cache.add(CacheItem(
repEventId,
PangeaEventTypes.tokens,
req.userL2,
_getTokenEvent(
context: context,
repEventId: repEventId,
req: req,
room: room,
_cache.add(
CacheItem(
repEventId,
PangeaEventTypes.tokens,
req.userL2,
_getTokenEvent(
context: context,
repEventId: repEventId,
req: req,
room: room,
),
),
),);
);
return _cache.last.data;
}
@ -201,9 +206,11 @@ class MessageDataQueueItem {
UseType useType;
MessageDataQueueItem(
this.transactionId, this.repTokensAndRecords, this.useType,
// required this.recentMessageRecord,
);
this.transactionId,
this.repTokensAndRecords,
this.useType,
// required this.recentMessageRecord,
);
}
class RepTokensAndRecord {

@ -24,13 +24,17 @@ class MyAnalyticsController {
//PTODO - locally cache and update periodically
Future<void> handleMessage(
Room room, RecentMessageRecord messageRecord,) async {
Room room,
RecentMessageRecord messageRecord,
) async {
try {
debugPrint("in handle message with type ${messageRecord.useType}");
if (_userId == null) {
debugger(when: kDebugMode);
ErrorHandler.logError(
m: "null userId in updateAnalytics", s: StackTrace.current,);
m: "null userId in updateAnalytics",
s: StackTrace.current,
);
return;
}
@ -55,7 +59,8 @@ class MyAnalyticsController {
}
Future<List<StudentAnalyticsEvent?>> analyticsEvents(
List<Room> spaces,) async {
List<Room> spaces,
) async {
final List<Future<StudentAnalyticsEvent?>> events = [];
for (final space in spaces) {
events.add(space.getStudentAnalytics(_userId!));
@ -69,7 +74,9 @@ class MyAnalyticsController {
);
Future<void> saveConstructsMixed(
List<OneConstructUse> allUses, String langCode,) async {
List<OneConstructUse> allUses,
String langCode,
) async {
try {
final Map<String, List<OneConstructUse>> aggregatedVocabUse = {};
for (final use in allUses) {
@ -84,7 +91,10 @@ class MyAnalyticsController {
debugPrint("saving of type ${uses.value.first.constructType}");
saveFutures.add(
analyticsRoom.saveConstructUsesSameLemma(
uses.key, uses.value.first.constructType!, uses.value,),
uses.key,
uses.value.first.constructType!,
uses.value,
),
);
}

@ -87,8 +87,10 @@ class SubscriptionController extends BaseController {
setState();
}
Future<void> showPaywall(BuildContext context,
[bool forceShow = false,]) async {
Future<void> showPaywall(
BuildContext context, [
bool forceShow = false,
]) async {
try {
if (!initialized) {
await initialize();
@ -156,8 +158,10 @@ class SubscriptionController extends BaseController {
}
void submitSubscriptionChange(
SubscriptionDetails? selectedSubscription, BuildContext context,
{bool isPromo = false,}) async {
SubscriptionDetails? selectedSubscription,
BuildContext context, {
bool isPromo = false,
}) async {
if (selectedSubscription != null) {
if (kIsWeb) {
if (selectedSubscription.duration == null) {
@ -187,7 +191,9 @@ class SubscriptionController extends BaseController {
}
try {
GoogleAnalytics.beginPurchaseSubscription(
selectedSubscription, context,);
selectedSubscription,
context,
);
await Purchases.purchasePackage(selectedSubscription.package!);
GoogleAnalytics.updateUserSubscriptionStatus(true);
} catch (err) {

@ -22,12 +22,14 @@ class WordController extends BaseController {
required String? userL1,
required String? userL2,
}) =>
_wordData.firstWhereOrNull((e) => e.isMatch(
w: word,
f: fullText,
l1: userL1,
l2: userL2,
),);
_wordData.firstWhereOrNull(
(e) => e.isMatch(
w: word,
f: fullText,
l1: userL1,
l2: userL2,
),
);
Future<WordData> getWordDataGlobal({
required String word,

@ -17,27 +17,35 @@ extension PangeaClient on Client {
List<Room> get classes => rooms.where((e) => e.isPangeaClass).toList();
List<Room> get classesImTeaching => rooms
.where((e) =>
e.isPangeaClass &&
e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin,)
.where(
(e) =>
e.isPangeaClass &&
e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin,
)
.toList();
List<Room> get classesAndExchangesImTeaching => rooms
.where((e) =>
(e.isPangeaClass || e.isExchange) &&
e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin,)
.where(
(e) =>
(e.isPangeaClass || e.isExchange) &&
e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin,
)
.toList();
List<Room> get classesImIn => rooms
.where((e) =>
e.isPangeaClass &&
e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin,)
.where(
(e) =>
e.isPangeaClass &&
e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin,
)
.toList();
List<Room> get classesAndExchangesImStudyingIn => rooms
.where((e) =>
(e.isPangeaClass || e.isExchange) &&
e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin,)
.where(
(e) =>
(e.isPangeaClass || e.isExchange) &&
e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin,
)
.toList();
List<Room> get classesAndExchangesImIn =>
@ -100,8 +108,10 @@ extension PangeaClient on Client {
debugger(when: kDebugMode);
analyticsRoom
.join()
.onError((error, stackTrace) =>
ErrorHandler.logError(e: error, s: stackTrace),)
.onError(
(error, stackTrace) =>
ErrorHandler.logError(e: error, s: stackTrace),
)
.then((value) => analyticsRoom.postLoad());
return analyticsRoom;
}

@ -752,8 +752,7 @@ extension PangeaRoom on Room {
}
final toAdd = [
...getParticipants([Membership.invite, Membership.join])
.map((e) => e.id)
,
.map((e) => e.id),
BotName.byEnvironment,
];
for (final teacher in await client.myTeachers) {

@ -4,8 +4,12 @@ class ChoreoResponseModel {
String? route;
String? feedbackMessage;
int? payloadId;
ChoreoResponseModel(
{this.grammarData, this.detectedLang, this.route, this.feedbackMessage,});
ChoreoResponseModel({
this.grammarData,
this.detectedLang,
this.route,
this.feedbackMessage,
});
ChoreoResponseModel.fromJson(Map<String, dynamic> json) {
grammarData = json['grammar_data'] != null

@ -67,8 +67,13 @@ class ChoreoRecord {
if (match != null && step != null) {
throw Exception("match and step should not both be defined");
}
choreoSteps.add(ChoreoRecordStep(
text: text, acceptedOrIgnoredMatch: match, itStep: step,),);
choreoSteps.add(
ChoreoRecordStep(
text: text,
acceptedOrIgnoredMatch: match,
itStep: step,
),
);
}
bool get hasAcceptedMatches => choreoSteps.any(
@ -118,23 +123,30 @@ class ChoreoRecord {
/// for each continuance
/// if not within the final message, save ignIT/incIT
List<OneConstructUse> toVocabUse(
List<PangeaToken> tokens, String chatId, String msgId,) {
List<PangeaToken> tokens,
String chatId,
String msgId,
) {
final List<OneConstructUse> uses = [];
final DateTime now = DateTime.now();
List<OneConstructUse> lemmasToVocabUses(
List<Lemma> lemmas, ConstructUseType type,) {
List<Lemma> lemmas,
ConstructUseType type,
) {
final List<OneConstructUse> uses = [];
for (final lemma in lemmas) {
if (lemma.saveVocab) {
uses.add(OneConstructUse(
useType: type,
chatId: chatId,
timeStamp: now,
lemma: lemma.text,
form: lemma.form,
msgId: msgId,
constructType: ConstructType.vocab,
),);
uses.add(
OneConstructUse(
useType: type,
chatId: chatId,
timeStamp: now,
lemma: lemma.text,
form: lemma.form,
msgId: msgId,
constructType: ConstructType.vocab,
),
);
}
}
return uses;
@ -145,9 +157,11 @@ class ChoreoRecord {
/// if 1) accepted match 2) token is in the replacement and 3) replacement
/// is in the overall step text, then token was a ga
if (step.acceptedOrIgnoredMatch?.status == PangeaMatchStatus.accepted &&
(step.acceptedOrIgnoredMatch!.match.choices?.any((r) =>
r.value.contains(token.text.content) &&
step.text.contains(r.value),) ??
(step.acceptedOrIgnoredMatch!.match.choices?.any(
(r) =>
r.value.contains(token.text.content) &&
step.text.contains(r.value),
) ??
false)) {
return lemmasToVocabUses(token.lemmas, ConstructUseType.ga);
}
@ -204,15 +218,17 @@ class ChoreoRecord {
final String name = step.acceptedOrIgnoredMatch!.match.rule?.id ??
step.acceptedOrIgnoredMatch!.match.shortMessage ??
step.acceptedOrIgnoredMatch!.match.type.typeName.name;
uses.add(OneConstructUse(
useType: ConstructUseType.ga,
chatId: chatId,
timeStamp: now,
lemma: name,
form: name,
msgId: msgId,
constructType: ConstructType.grammar,
),);
uses.add(
OneConstructUse(
useType: ConstructUseType.ga,
chatId: chatId,
timeStamp: now,
lemma: name,
form: name,
msgId: msgId,
constructType: ConstructType.grammar,
),
);
}
}
return uses;
@ -252,11 +268,15 @@ class ChoreoRecordStep {
ITStep? itStep;
ChoreoRecordStep(
{required this.text, this.acceptedOrIgnoredMatch, this.itStep,}) {
ChoreoRecordStep({
required this.text,
this.acceptedOrIgnoredMatch,
this.itStep,
}) {
if (itStep != null && acceptedOrIgnoredMatch != null) {
throw Exception(
"itStep and acceptedOrIgnoredMatch should not both be defined",);
"itStep and acceptedOrIgnoredMatch should not both be defined",
);
}
}

@ -44,9 +44,11 @@ class ClassSettingsModel {
city: json['city'],
country: json['country'],
dominantLanguage: LanguageModel.codeFromNameOrCode(
json['dominant_language'] ?? LanguageKeys.unknownLanguage,),
json['dominant_language'] ?? LanguageKeys.unknownLanguage,
),
targetLanguage: LanguageModel.codeFromNameOrCode(
json['target_language'] ?? LanguageKeys.unknownLanguage,),
json['target_language'] ?? LanguageKeys.unknownLanguage,
),
languageLevel: json['language_level'],
schoolName: json['school_name'],
);

@ -28,13 +28,15 @@ class ConstructUses {
factory ConstructUses.fromJson(Map<String, dynamic> json) {
// try {
debugger(
when: kDebugMode &&
(json['uses'] == null || json[ModelKey.lemma] == null),);
when:
kDebugMode && (json['uses'] == null || json[ModelKey.lemma] == null),
);
return ConstructUses(
lemma: json[ModelKey.lemma],
uses: (json['uses'] as Iterable)
.map<OneConstructUse?>(
(use) => use != null ? OneConstructUse.fromJson(use) : null,)
(use) => use != null ? OneConstructUse.fromJson(use) : null,
)
.where((element) => element != null)
.cast<OneConstructUse>()
.toList(),

@ -19,10 +19,12 @@ class VocabHeadwords {
factory VocabHeadwords.fromJson(Map<String, dynamic> json) {
final List<VocabList> lists = [];
for (final entry in json.entries) {
lists.add(VocabList(
name: entry.key,
lemmas: (entry.value as Iterable).cast<String>().toList(),
),);
lists.add(
VocabList(
name: entry.key,
lemmas: (entry.value as Iterable).cast<String>().toList(),
),
);
}
return VocabHeadwords(lists: lists);
}

@ -292,21 +292,23 @@ class IGCTextData {
? topTokenMatch.textStyle(defaultStyle)
: hasDefinitionStyle(defaultStyle);
items.add(TextSpan(
text: token.text.content,
style: tokenStyle,
recognizer: handleClick
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize: topTokenMatch?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
))
: null,
),);
items.add(
TextSpan(
text: token.text.content,
style: tokenStyle,
recognizer: handleClick
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize: topTokenMatch?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
))
: null,
),
);
final int charBetween = getAfterTokenSpacingByIndex(
index,

@ -74,12 +74,13 @@ class LanguageModel {
//PTODO - add flag for unknown
static LanguageModel get unknown => LanguageModel(
langCode: LanguageKeys.unknownLanguage,
languageType: 1,
languageFlag: "",
displayName: "Unknown",
l2: false,
l1: false,);
langCode: LanguageKeys.unknownLanguage,
languageType: 1,
languageFlag: "",
displayName: "Unknown",
l2: false,
l1: false,
);
static LanguageModel multiLingual([BuildContext? context]) => LanguageModel(
displayName: context != null

@ -157,17 +157,22 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
);
}
final List<EntitlementInfo> activeEntitlements = info
.entitlements.all.entries
.where((MapEntry<String, EntitlementInfo> entry) =>
entry.value.expirationDate == null ||
DateTime.parse(entry.value.expirationDate!).isAfter(DateTime.now()),)
.map((MapEntry<String, EntitlementInfo> entry) => entry.value)
.toList();
final List<EntitlementInfo> activeEntitlements =
info.entitlements.all.entries
.where(
(MapEntry<String, EntitlementInfo> entry) =>
entry.value.expirationDate == null ||
DateTime.parse(entry.value.expirationDate!)
.isAfter(DateTime.now()),
)
.map((MapEntry<String, EntitlementInfo> entry) => entry.value)
.toList();
allEntitlements = info.entitlements.all.entries
.map((MapEntry<String, EntitlementInfo> entry) =>
entry.value.productIdentifier,)
.map(
(MapEntry<String, EntitlementInfo> entry) =>
entry.value.productIdentifier,
)
.cast<String>()
.toList();

@ -98,8 +98,9 @@ class PangeaMatch {
end = match.fullText.length;
debugger(when: kDebugMode);
ErrorHandler.logError(
m: "match.offset + match.length > match.fullText.length",
data: match.toJson(),);
m: "match.offset + match.length > match.fullText.length",
data: match.toJson(),
);
} else {
end = match.offset + match.length;
}

@ -3,11 +3,12 @@ class PTextTapModel {
late String word;
late bool isHighLighted;
late int textAtOffSet;
PTextTapModel(
{required this.cursorOffset,
required this.isHighLighted,
required this.textAtOffSet,
required this.word,});
PTextTapModel({
required this.cursorOffset,
required this.isHighLighted,
required this.textAtOffSet,
required this.word,
});
toJson() {
return {
'cursorOffset': cursorOffset,

@ -72,15 +72,19 @@ class PangeaTokenText {
String content;
int length;
PangeaTokenText(
{required this.offset, required this.content, required this.length,});
PangeaTokenText({
required this.offset,
required this.content,
required this.length,
});
factory PangeaTokenText.fromJson(Map<String, dynamic> json) {
debugger(when: kDebugMode && json[_offsetKey] == null);
return PangeaTokenText(
offset: json[_offsetKey],
content: json[_contentKey],
length: json[_lengthKey] ?? (json[_contentKey] as String).length,);
offset: json[_offsetKey],
content: json[_contentKey],
length: json[_lengthKey] ?? (json[_contentKey] as String).length,
);
}
static const String _offsetKey = "offset";

@ -111,7 +111,8 @@ class SpanChoice {
value: json['value'] as String,
type: json['type'] != null
? SpanChoiceType.values.firstWhereOrNull(
(element) => element.name == json['type'],) ??
(element) => element.name == json['type'],
) ??
SpanChoiceType.bestCorrection
: SpanChoiceType.bestCorrection,
feedback: json['feedback'],

@ -112,7 +112,10 @@ class StudentAnalyticsEvent {
}
Future<TimeSeriesInterval> getTimeServiesInterval(
DateTime start, DateTime end, String? chatId,) async {
DateTime start,
DateTime end,
String? chatId,
) async {
final TimeSeriesInterval interval = TimeSeriesInterval(
start: start,
end: end,

@ -66,9 +66,11 @@ class Profile {
factory Profile.fromJson(Map<String, dynamic> json) {
final l2 = LanguageModel.codeFromNameOrCode(
json[ModelKey.l2LanguageKey] ?? LanguageKeys.unknownLanguage,);
json[ModelKey.l2LanguageKey] ?? LanguageKeys.unknownLanguage,
);
final l1 = LanguageModel.codeFromNameOrCode(
json[ModelKey.l1LanguageKey] ?? LanguageKeys.unknownLanguage,);
json[ModelKey.l1LanguageKey] ?? LanguageKeys.unknownLanguage,
);
return Profile(
// fullName: json[ModelKey.userFullName],

@ -1,4 +1,3 @@
import 'user_model.dart';
class UserProfileSearchResponse {

@ -6,7 +6,10 @@ class WidgetMeasurements {
static WidgetMeasurements defaultFromKey(String key) {
if (_fromKey[key] == null) {
_fromKey[key] = WidgetMeasurements(
position: const Offset(0, 0), size: const Size(0, 0), uid: key,);
position: const Offset(0, 0),
size: const Size(0, 0),
uid: key,
);
}
final WidgetMeasurements? weg = _fromKey[key];
@ -16,8 +19,11 @@ class WidgetMeasurements {
Offset? position;
Size? size;
String? uid;
WidgetMeasurements(
{required this.position, required this.size, required this.uid,});
WidgetMeasurements({
required this.position,
required this.size,
required this.uid,
});
toJson() => {'position': position, 'size': size, 'uid': uid};
}

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import '../utils/p_toast.dart';
@ -19,7 +18,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Unauthorized access", success: false,);
msg: "Exception: Unauthorized access",
success: false,
);
return;
case 403:
@ -28,7 +29,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Don't have permissions!", success: false,);
msg: "Exception: Don't have permissions!",
success: false,
);
return;
case 500:
if (kDebugMode) {
@ -36,7 +39,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Internal Server Error", success: false,);
msg: "Exception: Internal Server Error",
success: false,
);
return;
case 502:
if (kDebugMode) {
@ -44,7 +49,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Bad Gateway", success: false,);
msg: "Exception: Bad Gateway",
success: false,
);
return;
case 503:
@ -53,7 +60,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Service Unavailable", success: false,);
msg: "Exception: Service Unavailable",
success: false,
);
return;
case 504:
@ -62,7 +71,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Exception: Gateway timeout error!", success: false,);
msg: "Exception: Gateway timeout error!",
success: false,
);
return;
default:
@ -71,7 +82,9 @@ class ApiException {
debugPrint(statusCode.toString());
}
PToastController.toastMsg(
msg: "Unknown exception accrued!", success: false,);
msg: "Unknown exception accrued!",
success: false,
);
return;
}
}

@ -11,14 +11,17 @@ class Requests {
late String? matrixAccessToken;
late String? choreoApiKey;
//Question: How can we make baseUrl optional?
Requests(
{this.accessToken,
this.baseUrl = '',
this.matrixAccessToken,
this.choreoApiKey,});
Future<http.Response> post(
{required String url, required Map<dynamic, dynamic> body,}) async {
Requests({
this.accessToken,
this.baseUrl = '',
this.matrixAccessToken,
this.choreoApiKey,
});
Future<http.Response> post({
required String url,
required Map<dynamic, dynamic> body,
}) async {
dynamic encoded;
encoded = jsonEncode(body);
@ -34,8 +37,10 @@ class Requests {
return response;
}
Future<http.Response> put(
{required String url, required Map<dynamic, dynamic> body,}) async {
Future<http.Response> put({
required String url,
required Map<dynamic, dynamic> body,
}) async {
dynamic encoded;
encoded = jsonEncode(body);
@ -84,8 +89,11 @@ class Requests {
return json;
}
void handleError(http.Response response,
{Map<dynamic, dynamic>? body, String? objectId,}) {
void handleError(
http.Response response, {
Map<dynamic, dynamic>? body,
String? objectId,
}) {
//PTODO - handle 401 error - unauthorized call
//kick them back to login?

@ -6,7 +6,9 @@ import '../../enum/use_type.dart';
class BarChartPlaceHolderData {
static BarChartRodData randomBarChartRodData(
BuildContext context, int index,) {
BuildContext context,
int index,
) {
// final total = Random().nextInt(100);
// final it = total != 0 ? Random().nextInt(max(total - index, 1)) : 0;
// final igc = total != 0 ? Random().nextInt(max(total - it - index, 1)) : 0;
@ -75,7 +77,10 @@ class BarChartPlaceHolderData {
}
static List<BarChartGroupData> getData(
Color dark, Color normal, Color light,) {
Color dark,
Color normal,
Color light,
) {
const double barSpace = 16;
return [

@ -53,7 +53,9 @@ class BaseAnalyticsController extends State<BaseAnalyticsPage> {
bool isSelected(String chatOrStudentId) => chatOrStudentId == selected?.id;
ChartAnalyticsModel? chartData(
BuildContext context, AnalyticsSelected? selectedParam,) {
BuildContext context,
AnalyticsSelected? selectedParam,
) {
final AnalyticsSelected analyticsSelected =
selectedParam ?? widget.defaultAnalyticsSelected;
@ -161,9 +163,10 @@ class BaseAnalyticsView extends StatelessWidget {
title: Text(
controller.widget.pageTitle,
style: TextStyle(
color: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 18,
fontWeight: FontWeight.w700,),
color: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 18,
fontWeight: FontWeight.w700,
),
overflow: TextOverflow.clip,
textAlign: TextAlign.center,
),
@ -230,39 +233,35 @@ class BaseAnalyticsView extends StatelessWidget {
child: SingleChildScrollView(
child: SizedBox(
height: max(
controller.widget.tabData1.items.length + 1,
controller.widget.tabData2.items.length,) *
controller.widget.tabData1.items.length + 1,
controller.widget.tabData2.items.length,
) *
72,
child: TabBarView(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
...controller.widget.tabData1.items
.map(
(item) => AnalyticsListTile(
avatar: item.avatar,
model: controller.chartData(
context,
AnalyticsSelected(
item.id,
controller.widget.tabData1.type,
"",
),
),
displayName: item.displayName,
id: item.id,
type: controller.widget.tabData1.type,
selected:
controller.isSelected(item.id),
onTap: controller.toggleSelection,
allowNavigateOnSelect: controller
.widget
.tabData1
.allowNavigateOnSelect,
...controller.widget.tabData1.items.map(
(item) => AnalyticsListTile(
avatar: item.avatar,
model: controller.chartData(
context,
AnalyticsSelected(
item.id,
controller.widget.tabData1.type,
"",
),
)
,
),
displayName: item.displayName,
id: item.id,
type: controller.widget.tabData1.type,
selected: controller.isSelected(item.id),
onTap: controller.toggleSelection,
allowNavigateOnSelect: controller.widget
.tabData1.allowNavigateOnSelect,
),
),
if (controller.widget.defaultAnalyticsSelected
.type ==
AnalyticsEntryType.space)
@ -282,8 +281,10 @@ class BaseAnalyticsView extends StatelessWidget {
id: controller
.widget.defaultAnalyticsSelected.id,
type: AnalyticsEntryType.privateChats,
selected: controller.isSelected(controller
.widget.defaultAnalyticsSelected.id,),
selected: controller.isSelected(
controller
.widget.defaultAnalyticsSelected.id,
),
onTap: controller.toggleSelection,
allowNavigateOnSelect: false,
),
@ -337,11 +338,12 @@ class TabData {
List<TabItem> items;
bool allowNavigateOnSelect;
TabData(
{required this.type,
required this.items,
required this.icon,
this.allowNavigateOnSelect = true,});
TabData({
required this.type,
required this.items,
required this.icon,
this.allowNavigateOnSelect = true,
});
}
class TabItem {

@ -7,8 +7,11 @@ import '../../enum/bar_chart_view_enum.dart';
class ChartViewPickerButton extends StatelessWidget {
final BarChartViewSelection selected;
final void Function(BarChartViewSelection) onChange;
const ChartViewPickerButton(
{super.key, required this.selected, required this.onChange,});
const ChartViewPickerButton({
super.key,
required this.selected,
required this.onChange,
});
@override
Widget build(BuildContext context) {

@ -33,11 +33,13 @@ class ClassAnalyticsView extends StatelessWidget {
type: AnalyticsEntryType.student,
icon: Icons.people_outline,
items: controller.students
.map((s) => TabItem(
avatar: s.avatarUrl,
displayName: s.displayName ?? "unknown",
id: s.id,
),)
.map(
(s) => TabItem(
avatar: s.avatarUrl,
displayName: s.displayName ?? "unknown",
id: s.id,
),
)
.toList(),
);

@ -32,7 +32,8 @@ class AnalyticsClassListController extends State<AnalyticsClassList> {
Future.delayed(Duration.zero, () async {
stateSub = pangeaController.matrixState.client.onRoomState.stream
.where(
(event) => event.type == PangeaEventTypes.studentAnalyticsSummary,)
(event) => event.type == PangeaEventTypes.studentAnalyticsSummary,
)
.listen(onStateUpdate);
});
}

@ -56,14 +56,15 @@ class ListSummaryAnalytics extends StatelessWidget {
text: TextSpan(
children: [
spacerIconText(
L10n.of(context) != null
? L10n.of(context)!.totalMessages
: "Total messages sent",
"",
Icons.chat_bubble,
totals!.all,
Theme.of(context).textTheme.bodyLarge!.color,
false,),
L10n.of(context) != null
? L10n.of(context)!.totalMessages
: "Total messages sent",
"",
Icons.chat_bubble,
totals!.all,
Theme.of(context).textTheme.bodyLarge!.color,
false,
),
if (totals!.all != 0) ...[
spacerIconText(
l10n != null ? l10n.taTooltip : "With translation assistance",

@ -18,8 +18,11 @@ class MessagesBarChart extends StatefulWidget {
final ChartAnalyticsModel? chartAnalytics;
final String barChartTitle;
const MessagesBarChart(
{super.key, required this.chartAnalytics, required this.barChartTitle,});
const MessagesBarChart({
super.key,
required this.chartAnalytics,
required this.barChartTitle,
});
@override
State<StatefulWidget> createState() => MessagesBarChartState();
@ -181,7 +184,10 @@ class MessagesBarChartState extends State<MessagesBarChart> {
}
bool isInSameGroup(
TimeSeriesInterval? t1, TimeSeriesInterval t2, TimeSpan timeSpan,) {
TimeSeriesInterval? t1,
TimeSeriesInterval t2,
TimeSpan timeSpan,
) {
final DateTime? date1 = t1?.end;
final DateTime date2 = t2.end;
if (timeSpan == TimeSpan.sixmonths || timeSpan == TimeSpan.year) {
@ -230,12 +236,14 @@ class MessagesBarChartState extends State<MessagesBarChart> {
final List<BarChartGroupData> chartData = [];
intervalGroupings.asMap().forEach((index, intervalGroup) {
chartData.add(BarChartGroupData(
x: index,
barsSpace: barSpace,
// barRods: intervalGroup.map(constructBarChartRodData).toList(),
barRods: constructBarChartRodData(intervalGroup),
),);
chartData.add(
BarChartGroupData(
x: index,
barsSpace: barSpace,
// barRods: intervalGroup.map(constructBarChartRodData).toList(),
barRods: constructBarChartRodData(intervalGroup),
),
);
});
return chartData;
}
@ -258,7 +266,8 @@ class MessagesBarChartState extends State<MessagesBarChart> {
// }
List<BarChartRodData> constructBarChartRodData(
List<TimeSeriesInterval> timeSeriesIntervalGroup,) {
List<TimeSeriesInterval> timeSeriesIntervalGroup,
) {
int y1 = 0;
int y2 = 0;
int y3 = 0;
@ -276,11 +285,20 @@ class MessagesBarChartState extends State<MessagesBarChart> {
rodStackItems: [
BarChartRodStackItem(0, y1.toDouble(), UseType.ta.color(context)),
BarChartRodStackItem(
y1.toDouble(), y2.toDouble(), UseType.ga.color(context),),
y1.toDouble(),
y2.toDouble(),
UseType.ga.color(context),
),
BarChartRodStackItem(
y2.toDouble(), y3.toDouble(), UseType.wa.color(context),),
y2.toDouble(),
y3.toDouble(),
UseType.wa.color(context),
),
BarChartRodStackItem(
y3.toDouble(), y4.toDouble(), UseType.un.color(context),),
y3.toDouble(),
y4.toDouble(),
UseType.un.color(context),
),
],
borderRadius: BorderRadius.zero,
),

@ -31,7 +31,8 @@ class StudentAnalyticsController extends State<StudentAnalyticsPage> {
void initState() {
_pangeaController.matrixState.client
.updateMyLearningAnalyticsForAllClassesImIn(
_pangeaController.pStoreService,);
_pangeaController.pStoreService,
);
super.initState();
}

@ -21,27 +21,32 @@ class StudentAnalyticsView extends StatelessWidget {
type: AnalyticsEntryType.room,
icon: Icons.chat_bubble_outline,
items: chats
.map((c) => TabItem(
avatar: c.avatar,
displayName:
c.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
id: c.id,
),)
.map(
(c) => TabItem(
avatar: c.avatar,
displayName:
c.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
id: c.id,
),
)
.toList(),
allowNavigateOnSelect: false,
);
final TabData classTabData = TabData(
type: AnalyticsEntryType.space,
icon: Icons.workspaces,
items: spaces
.map((c) => TabItem(
avatar: c.avatar,
displayName: c
.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
id: c.id,
),)
.toList(),
allowNavigateOnSelect: false,);
type: AnalyticsEntryType.space,
icon: Icons.workspaces,
items: spaces
.map(
(c) => TabItem(
avatar: c.avatar,
displayName:
c.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
id: c.id,
),
)
.toList(),
allowNavigateOnSelect: false,
);
return controller.userId != null
? BaseAnalyticsPage(
@ -49,14 +54,16 @@ class StudentAnalyticsView extends StatelessWidget {
tabData1: chatTabData,
tabData2: classTabData,
defaultAnalyticsSelected: AnalyticsSelected(
controller.userId!,
AnalyticsEntryType.student,
L10n.of(context)!.allChatsAndClasses,),
controller.userId!,
AnalyticsEntryType.student,
L10n.of(context)!.allChatsAndClasses,
),
refreshData: controller.getClassAndChatAnalytics,
alwaysSelected: AnalyticsSelected(
controller.userId!,
AnalyticsEntryType.student,
L10n.of(context)!.allChatsAndClasses,),
controller.userId!,
AnalyticsEntryType.student,
L10n.of(context)!.allChatsAndClasses,
),
)
: const SizedBox();
}

@ -7,8 +7,11 @@ import '../../enum/time_span.dart';
class TimeSpanMenuButton extends StatelessWidget {
final TimeSpan value;
final void Function(TimeSpan) onChange;
const TimeSpanMenuButton(
{super.key, required this.value, required this.onChange,});
const TimeSpanMenuButton({
super.key,
required this.value,
required this.onChange,
});
@override
Widget build(BuildContext context) {

@ -140,12 +140,14 @@ class VocabBarChartState extends State<VocabBarChart> {
final List<BarChartGroupData> chartData = [];
vocabHeadwords.lists.asMap().forEach((index, intervalGroup) {
chartData.add(BarChartGroupData(
x: index,
barsSpace: barSpace,
// barRods: intervalGroup.map(constructBarChartRodData).toList(),
barRods: constructBarChartRodData(intervalGroup),
),);
chartData.add(
BarChartGroupData(
x: index,
barsSpace: barSpace,
// barRods: intervalGroup.map(constructBarChartRodData).toList(),
barRods: constructBarChartRodData(intervalGroup),
),
);
});
return chartData;
}

@ -31,10 +31,12 @@ class ClassNameHeader extends StatelessWidget {
color: Theme.of(context).colorScheme.onBackground,
),
),
icon: room.nameAndRoomTypeIcon(TextStyle(
fontSize: 20,
color: Theme.of(context).textTheme.bodyLarge!.color,
),),
icon: room.nameAndRoomTypeIcon(
TextStyle(
fontSize: 20,
color: Theme.of(context).textTheme.bodyLarge!.color,
),
),
// icon: Text(
// room.getLocalizedDisplayname(
// MatrixLocals(L10n.of(context)!),

@ -29,9 +29,11 @@ class SpaceDetailsToggleAddStudentsTile extends StatelessWidget {
Icons.add,
),
),
trailing: Icon(controller.displayAddStudentOptions
? Icons.keyboard_arrow_down_outlined
: Icons.keyboard_arrow_right_outlined,),
trailing: Icon(
controller.displayAddStudentOptions
? Icons.keyboard_arrow_down_outlined
: Icons.keyboard_arrow_right_outlined,
),
onTap: controller.toggleAddStudentOptions,
);
}

@ -28,7 +28,10 @@ void showEditFieldDialog(BuildContext context, String title) async {
future: () async => null,
);
if (success.error == null) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(L10n.of(context)!.groupDescriptionHasBeenChanged),),);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.groupDescriptionHasBeenChanged),
),
);
}
}

@ -15,8 +15,12 @@ class RoomRulesEditor extends StatefulWidget {
final bool startOpen;
final bool showAdd;
const RoomRulesEditor(
{super.key, this.roomId, this.startOpen = true, this.showAdd = false,});
const RoomRulesEditor({
super.key,
this.roomId,
this.startOpen = true,
this.showAdd = false,
});
@override
RoomRulesState createState() => RoomRulesState();
@ -157,14 +161,18 @@ class RoomRulesState extends State<RoomRulesEditor> {
onChanged: (value) {
updatePermission(() {
rules.setLanguageToolSetting(
setting, value.toInt(),);
setting,
value.toInt(),
);
});
},
divisions: 2,
max: 2,
min: 0,
label: rules.languageToolPermissionsText(
context, setting,),
context,
setting,
),
),
),
],

@ -13,10 +13,12 @@
class PClassAnalyticsRepo {
/// deprecated in favor of new analytics
static Future<dynamic> repoGetAnalyticsByIds(
String accessToken, String timeSpan,
{List<String>? classIds,
List<String>? userIds,
List<String>? chatIds,}) async {
String accessToken,
String timeSpan, {
List<String>? classIds,
List<String>? userIds,
List<String>? chatIds,
}) async {
// if (!AnalyticsUtil.isValidSpan(timeSpan)) throw "Invalid span";
// final Requests req = Requests(

@ -31,7 +31,9 @@ class PClassRepo {
//Question for Lala: In this widget, controller, repo framework, where are
// errors handled? How are they passed?
static Future<ClassSettingsModel?> getClassByCode(
String classCode, String accessToken,) async {
String classCode,
String accessToken,
) async {
final Requests req =
Requests(baseUrl: PApiUrls.baseAPI, accessToken: accessToken);
final Response res =
@ -49,8 +51,11 @@ class PClassRepo {
static searchClass(String text) async {}
static sendEmailToJoinClass(List<ClassEmailInviteData> data, String roomId,
String teacherName,) async {}
static sendEmailToJoinClass(
List<ClassEmailInviteData> data,
String roomId,
String teacherName,
) async {}
static inviteAction(BuildContext context, String id, String roomId) async {}

@ -95,8 +95,9 @@ class ContextTranslationResponseModel {
if (trans.isEmpty) {
Sentry.addBreadcrumb(
Breadcrumb(
message: "ContextTranslationResponseModel with empty translations",
data: {"response": json},),
message: "ContextTranslationResponseModel with empty translations",
data: {"response": json},
),
);
}

@ -4,11 +4,12 @@ class PExchangeRepo {
static fetchExchangeClassInfo(String exchangePangeaId) async {}
static saveExchangeRecord(
String requestFromClass,
String requestToClass,
String requestTeacher,
String requestToClassAuthor,
String exchangePangeaId,) async {}
String requestFromClass,
String requestToClass,
String requestTeacher,
String requestToClassAuthor,
String exchangePangeaId,
) async {}
static exchangeRejectRequest(String roomId, String teacherName) async {}
@ -26,5 +27,8 @@ class PExchangeRepo {
}) async {}
static isExchange(
BuildContext context, String accessToken, String exchangeId,) async {}
BuildContext context,
String accessToken,
String exchangeId,
) async {}
}

@ -14,8 +14,10 @@ import '../network/requests.dart';
import '../network/urls.dart';
class IgcRepo {
static Future<IGCTextData> getIGC(String? accessToken,
{required IGCRequestBody igcRequest,}) async {
static Future<IGCTextData> getIGC(
String? accessToken, {
required IGCRequestBody igcRequest,
}) async {
final Requests req = Requests(
accessToken: accessToken,
choreoApiKey: Environment.choreoApiKey,
@ -50,21 +52,26 @@ class IgcRepo {
lemmas: [Lemma(form: "be", text: "be", saveVocab: true)],
),
PangeaToken(
text: PangeaTokenText(content: "a", offset: 8, length: 1),
hasInfo: false,
lemmas: [],),
text: PangeaTokenText(content: "a", offset: 8, length: 1),
hasInfo: false,
lemmas: [],
),
PangeaToken(
text: PangeaTokenText(content: "sample", offset: 10, length: 6),
hasInfo: false,
lemmas: [],),
text: PangeaTokenText(content: "sample", offset: 10, length: 6),
hasInfo: false,
lemmas: [],
),
PangeaToken(
text: PangeaTokenText(content: "text", offset: 17, length: 4),
hasInfo: false,
lemmas: [],),
text: PangeaTokenText(content: "text", offset: 17, length: 4),
hasInfo: false,
lemmas: [],
),
],
matches: [
PangeaMatch(
match: spanDataRepomockSpan, status: PangeaMatchStatus.open,),
match: spanDataRepomockSpan,
status: PangeaMatchStatus.open,
),
],
originalInput: "This be a sample text",
fullTextCorrection: "This is a sample text",

@ -11,10 +11,12 @@ import '../network/urls.dart';
class ITRepo {
static Future<ITResponseModel> customInputTranslate(
CustomInputRequestModel initalText,) async {
CustomInputRequestModel initalText,
) async {
final Requests req = Requests(
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,);
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,
);
final Response res =
await req.post(url: PApiUrls.firstStep, body: initalText.toJson());
@ -24,10 +26,12 @@ class ITRepo {
}
static Future<ITResponseModel> systemChoiceTranslate(
SystemChoiceRequestModel subseqText,) async {
SystemChoiceRequestModel subseqText,
) async {
final Requests req = Requests(
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,);
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,
);
final Response res =
await req.post(url: PApiUrls.subseqStep, body: subseqText.toJson());

@ -4,10 +4,13 @@ import '../network/urls.dart';
class MessageServiceRepo {
static Future<void> sendPayloads(
MessageServiceModel serviceModel, String messageId,) async {
MessageServiceModel serviceModel,
String messageId,
) async {
final Requests req = Requests(
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,);
baseUrl: PApiUrls.choreoBaseApi,
choreoApiKey: Environment.choreoApiKey,
);
final json = serviceModel.toJson();
json["msg_id"] = messageId;

@ -11,8 +11,10 @@ import '../network/requests.dart';
import '../network/urls.dart';
class SpanDataRepo {
static Future<SpanDetailsRepoReqAndRes> getSpanDetails(String? accessToken,
{required SpanDetailsRepoReqAndRes request,}) async {
static Future<SpanDetailsRepoReqAndRes> getSpanDetails(
String? accessToken, {
required SpanDetailsRepoReqAndRes request,
}) async {
final Requests req = Requests(
accessToken: accessToken,
choreoApiKey: Environment.choreoApiKey,

@ -117,12 +117,14 @@ class RCProductsResponseModel {
Map<String, dynamic> metadata,
) {
return packageDetails['products']['items']
.map((productDetails) => SubscriptionDetails(
price: double.parse(metadata['$packageId.price']),
duration: metadata['$packageId.duration'],
id: productDetails['product']['store_identifier'],
appId: productDetails['product']['app_id'],
),)
.map(
(productDetails) => SubscriptionDetails(
price: double.parse(metadata['$packageId.price']),
duration: metadata['$packageId.duration'],
id: productDetails['product']['store_identifier'],
appId: productDetails['product']['app_id'],
),
)
.toList()
.cast<SubscriptionDetails>();
}

@ -70,11 +70,12 @@ class TokensResponseModel {
Map<String, dynamic> json,
) =>
TokensResponseModel(
tokens: (json[ModelKey.tokens] as Iterable)
.map<PangeaToken>(
(e) => PangeaToken.fromJson(e as Map<String, dynamic>),
)
.toList()
.cast<PangeaToken>(),
lang: json[ModelKey.lang],);
tokens: (json[ModelKey.tokens] as Iterable)
.map<PangeaToken>(
(e) => PangeaToken.fromJson(e as Map<String, dynamic>),
)
.toList()
.cast<PangeaToken>(),
lang: json[ModelKey.lang],
);
}

@ -11,8 +11,10 @@ import '../network/urls.dart';
/// accepts ChatTopic and calls an API for a list of Lemma
class TopicDataRepo {
static Future<ChatTopic> generate(String? accessToken,
{required TopicDataRequest request,}) async {
static Future<ChatTopic> generate(
String? accessToken, {
required TopicDataRequest request,
}) async {
final Requests req = Requests(
accessToken: accessToken,
choreoApiKey: Environment.choreoApiKey,

@ -17,13 +17,18 @@ class WordRepo {
required String userL2,
}) async {
final Requests req = Requests(
choreoApiKey: Environment.choreoApiKey, accessToken: accessToken,);
final Response res = await req.post(url: PApiUrls.wordNet, body: {
ModelKey.word: word,
ModelKey.fullText: fullText,
ModelKey.userL1: userL1,
ModelKey.userL2: userL2,
},);
choreoApiKey: Environment.choreoApiKey,
accessToken: accessToken,
);
final Response res = await req.post(
url: PApiUrls.wordNet,
body: {
ModelKey.word: word,
ModelKey.fullText: fullText,
ModelKey.userL1: userL1,
ModelKey.userL2: userL2,
},
);
final json = jsonDecode(utf8.decode(res.bodyBytes));

@ -17,8 +17,10 @@ class PangeaAnyState {
_layerLinkAndKeys.clear();
}
LayerLinkAndKey layerLinkAndKey(String transformTargetId,
[throwErrorIfNotThere = false,]) {
LayerLinkAndKey layerLinkAndKey(
String transformTargetId, [
throwErrorIfNotThere = false,
]) {
if (_layerLinkAndKeys[transformTargetId] == null) {
if (throwErrorIfNotThere) {
Sentry.addBreadcrumb(Breadcrumb.fromJson(_layerLinkAndKeys));

@ -9,7 +9,9 @@ import '../extensions/pangea_room_extension.dart';
class ClassChatPowerLevels {
static Future<Map<String, dynamic>> powerLevelOverrideForClassChat(
BuildContext context, List<Room> spaceParents,) async {
BuildContext context,
List<Room> spaceParents,
) async {
final Client client = Matrix.of(context).client;
final Map<String, dynamic> powerLevelOverride = {};
powerLevelOverride['events'] = {

@ -22,8 +22,13 @@ import '../models/choreo_record.dart';
enum DownloadType { txt, csv, xlsx }
Future<void> downloadChat(Room room, ClassSettingsModel classSettings,
DownloadType type, Client client, BuildContext context,) async {
Future<void> downloadChat(
Room room,
ClassSettingsModel classSettings,
DownloadType type,
Client client,
BuildContext context,
) async {
List<PangeaMessageEvent> allPangeaMessages;
try {
@ -43,7 +48,8 @@ Future<void> downloadChat(Room room, ClassSettingsModel classSettings,
} catch (err) {
ErrorHandler.logError(
e: Exception(
"Failed to fetch messages for chat ${room.id} in while downloading chat",),
"Failed to fetch messages for chat ${room.id} in while downloading chat",
),
s: StackTrace.current,
);
ScaffoldMessenger.of(context).showSnackBar(
@ -114,7 +120,11 @@ Future<List<Event>> getAllEvents(Room room, Client client) async {
}
List<PangeaMessageEvent> getPangeaMessageEvents(
List<Event> events, Timeline timeline, Room room, String? targetLang,) {
List<Event> events,
Timeline timeline,
Room room,
String? targetLang,
) {
final List<PangeaMessageEvent> allPangeaMessages = events
.where(
(Event event) =>
@ -186,7 +196,10 @@ String mimetype(DownloadType fileType) {
}
Future<void> downloadFile(
contents, String filename, DownloadType fileType,) async {
contents,
String filename,
DownloadType fileType,
) async {
if (kIsWeb) {
final blob = webFile.Blob([contents], mimetype(fileType), 'native');
webFile.AnchorElement(
@ -227,8 +240,12 @@ Future<void> downloadFile(
}
}
String getTxtContent(List<PangeaMessageEvent> messages, BuildContext context,
String filename, Room room,) {
String getTxtContent(
List<PangeaMessageEvent> messages,
BuildContext context,
String filename,
Room room,
) {
String formattedInfo = "";
for (final PangeaMessageEvent message in messages) {
final String timestamp =
@ -265,7 +282,10 @@ String getTxtContent(List<PangeaMessageEvent> messages, BuildContext context,
}
String getCSVContent(
List<PangeaMessageEvent> messages, BuildContext context, String fileName,) {
List<PangeaMessageEvent> messages,
BuildContext context,
String fileName,
) {
final List<List<String>> csvData = [
[
L10n.of(context)!.sender,
@ -313,7 +333,10 @@ String getCSVContent(
}
List<int> getExcelContent(
List<PangeaMessageEvent> messages, BuildContext context, String filename,) {
List<PangeaMessageEvent> messages,
BuildContext context,
String filename,
) {
final Workbook workbook = Workbook();
final Worksheet sheet = workbook.worksheets[0];

@ -14,7 +14,9 @@ class ErrorHandler {
static Future<void> initialize() async {
FutureOr<void> Function(Scope)? withScope(
Scope scope, FlutterErrorDetails details,) {
Scope scope,
FlutterErrorDetails details,
) {
// if (details.exception is http.Response) {
// final res = details.exception as http.Response;
// scope.addBreadcrumb(
@ -60,24 +62,30 @@ class ErrorHandler {
};
}
static logError(
{Object? e, StackTrace? s, String? m, Map<String, dynamic>? data,}) async {
static logError({
Object? e,
StackTrace? s,
String? m,
Map<String, dynamic>? data,
}) async {
if ((e ?? m) != null) debugPrint("error: ${e?.toString() ?? m}");
if (data != null) {
Sentry.addBreadcrumb(Breadcrumb.fromJson(data));
}
FlutterError.reportError(FlutterErrorDetails(
exception: e ?? Exception(m ?? "no message supplied"),
stack: s,
library: 'Pangea',
context: ErrorSummary(e?.toString() ?? "error not defined"),
stackFilter: (input) => input.where(
(e) => !(e.contains("org-dartlang-sdk") ||
e.contains("future_impl") ||
e.contains("microtask") ||
e.contains("async_patch")),
FlutterError.reportError(
FlutterErrorDetails(
exception: e ?? Exception(m ?? "no message supplied"),
stack: s,
library: 'Pangea',
context: ErrorSummary(e?.toString() ?? "error not defined"),
stackFilter: (input) => input.where(
(e) => !(e.contains("org-dartlang-sdk") ||
e.contains("future_impl") ||
e.contains("microtask") ||
e.contains("async_patch")),
),
),
),);
);
}
}

@ -63,13 +63,17 @@ class GoogleAnalytics {
}
static createClass(String className, String classCode) {
logEvent('create_class',
parameters: {'name': className, 'group_id': classCode},);
logEvent(
'create_class',
parameters: {'name': className, 'group_id': classCode},
);
}
static createExchange(String exchangeName, String classCode) {
logEvent('create_exchange',
parameters: {'name': exchangeName, 'group_id': classCode},);
logEvent(
'create_exchange',
parameters: {'name': exchangeName, 'group_id': classCode},
);
}
static createChat(String newChatRoomId) {
@ -77,28 +81,38 @@ class GoogleAnalytics {
}
static addParent(String chatRoomId, String classCode) {
logEvent('add_room_to_class',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},);
logEvent(
'add_room_to_class',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},
);
}
static removeChatFromClass(String chatRoomId, String classCode) {
logEvent('remove_room_from_class',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},);
logEvent(
'remove_room_from_class',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},
);
}
static addChatToExchange(String chatRoomId, String classCode) {
logEvent('add_chat_to_exchange',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},);
logEvent(
'add_chat_to_exchange',
parameters: {"chat_id": chatRoomId, 'group_id': classCode},
);
}
static inviteClassToExchange(String classId, String exchangeId) {
logEvent('invite_class_to_exchange',
parameters: {'group_id': classId, 'exchange_id': exchangeId},);
logEvent(
'invite_class_to_exchange',
parameters: {'group_id': classId, 'exchange_id': exchangeId},
);
}
static kickClassFromExchange(String classId, String exchangeId) {
logEvent('kick_class_from_exchange',
parameters: {'group_id': classId, 'exchange_id': exchangeId},);
logEvent(
'kick_class_from_exchange',
parameters: {'group_id': classId, 'exchange_id': exchangeId},
);
}
static joinClass(String classCode) {
@ -106,11 +120,14 @@ class GoogleAnalytics {
}
static sendMessage(String chatRoomId, String classCode, UseType useType) {
logEvent('sent_message', parameters: {
"chat_id": chatRoomId,
'group_id': classCode,
"message_type": useType.toString(),
},);
logEvent(
'sent_message',
parameters: {
"chat_id": chatRoomId,
'group_id': classCode,
"message_type": useType.toString(),
},
);
}
static contextualRequest() {
@ -122,38 +139,44 @@ class GoogleAnalytics {
}
static beginPurchaseSubscription(
SubscriptionDetails details, BuildContext context,) {
logEvent('begin_checkout', parameters: {
"currency": "USD",
'value': details.price,
'transaction_id': details.id,
'items': [
{
'item_id': details.package!.identifier,
'item_name': details.displayName(context),
'price': details.price,
'item_category': "subscription",
'quantity': 1,
}
],
},);
SubscriptionDetails details,
BuildContext context,
) {
logEvent(
'begin_checkout',
parameters: {
"currency": "USD",
'value': details.price,
'transaction_id': details.id,
'items': [
{
'item_id': details.package!.identifier,
'item_name': details.displayName(context),
'price': details.price,
'item_category': "subscription",
'quantity': 1,
}
],
},
);
}
static FirebaseAnalyticsObserver getAnalyticsObserver() =>
FirebaseAnalyticsObserver(
analytics: analytics!,
routeFilter: (route) {
// By default firebase only tracks page routes
if (route is! PageRoute ||
// No user logged in, so we dont track
route.settings.name == "login" ||
route.settings.name == "/home" ||
route.settings.name == "connect" ||
route.settings.name == "signup") {
return false;
}
final String? name = route.settings.name;
debugPrint("navigating to route: $name");
return true;
},);
analytics: analytics!,
routeFilter: (route) {
// By default firebase only tracks page routes
if (route is! PageRoute ||
// No user logged in, so we dont track
route.settings.name == "login" ||
route.settings.name == "/home" ||
route.settings.name == "connect" ||
route.settings.name == "signup") {
return false;
}
final String? name = route.settings.name;
debugPrint("navigating to route: $name");
return true;
},
);
}

@ -47,7 +47,9 @@ class OverlayUtil {
color: Colors.transparent,
clipBehavior: Clip.antiAlias,
child: OverlayContainer(
cardToShow: cardToShow, borderColor: borderColor,),
cardToShow: cardToShow,
borderColor: borderColor,
),
),
),
),
@ -191,8 +193,10 @@ class ChatViewConstraints implements OverlayConstraints {
x1 = mediaQueryData.size.width -
max(mediaQueryData.viewPadding.right, mediaQueryData.viewInsets.right);
y1 = mediaQueryData.size.height -
max(mediaQueryData.viewPadding.bottom,
mediaQueryData.viewInsets.bottom,);
max(
mediaQueryData.viewPadding.bottom,
mediaQueryData.viewInsets.bottom,
);
// https://medium.com/flutter-community/a-flutter-guide-to-visual-overlap-padding-viewpadding-and-viewinsets-a63e214be6e8
// debugPrint(

@ -9,8 +9,11 @@ class PLocalStore {
PLocalStore({required this.pangeaController});
/// save data in local
Future<void> save(String key, dynamic data,
{bool addClientIdToKey = true,}) async {
Future<void> save(
String key,
dynamic data, {
bool addClientIdToKey = true,
}) async {
await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data);
}

@ -83,8 +83,10 @@ Future<List<SpaceTeacher>> getReportTeachers(
final List<Room> reportRoomParentSpaces = room.spaceParents
.where((parentSpace) => parentSpace.roomId != null)
.map((parentSpace) =>
Matrix.of(context).client.getRoomById(parentSpace.roomId!),)
.map(
(parentSpace) =>
Matrix.of(context).client.getRoomById(parentSpace.roomId!),
)
.where((parentSpace) => parentSpace != null)
.cast<Room>()
.toList();

@ -9,11 +9,12 @@ import '../../utils/matrix_sdk_extensions/matrix_locals.dart';
void setClassDisplayname(BuildContext context, String? roomId) async {
final room = Matrix.of(context).client.getRoomById(roomId!)!;
final TextEditingController textFieldController = TextEditingController(
text: room.getLocalizedDisplayname(
MatrixLocals(
L10n.of(context)!,
text: room.getLocalizedDisplayname(
MatrixLocals(
L10n.of(context)!,
),
),
),);
);
showDialog(
context: context,
@ -43,8 +44,11 @@ void setClassDisplayname(BuildContext context, String? roomId) async {
future: () => room.setName(textFieldController.text),
);
if (success.error == null) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(L10n.of(context)!.displaynameHasBeenChanged),),);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.displaynameHasBeenChanged),
),
);
Navigator.of(context).pop();
}
},

@ -13,8 +13,12 @@ class PLoadingStatus extends StatefulWidget {
final Widget child;
final Widget? shimmerChild;
const PLoadingStatus(
{super.key, required this.child, this.onFinish, this.shimmerChild,});
const PLoadingStatus({
super.key,
required this.child,
this.onFinish,
this.shimmerChild,
});
@override
PLoadingStatusState createState() => PLoadingStatusState();

@ -12,8 +12,12 @@ class PLoadingStatusV2 extends StatefulWidget {
final Widget child;
final Widget? shimmerChild;
const PLoadingStatusV2(
{super.key, required this.child, this.onFinish, this.shimmerChild,});
const PLoadingStatusV2({
super.key,
required this.child,
this.onFinish,
this.shimmerChild,
});
@override
PLoadingStatusStateV2 createState() => PLoadingStatusStateV2();

@ -37,23 +37,24 @@ Future<T?> showAlignedDialog<T>({
position = overlay.localToGlobal(followerAnchor.alongSize(overlay.size));
}
return Navigator.of(context, rootNavigator: useRootNavigator)
.push<T>(AlignedDialogRoute<T>(
followerAlignment: followerAnchor,
position: position,
context: context,
builder: builder,
barrierColor: barrierColor,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: isGlobal == true,
settings: routeSettings,
themes: themes,
transitionsBuilder: transitionsBuilder,
duration: duration,
avoidOverflow: avoidOverflow,
offset: offset,
),);
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(
AlignedDialogRoute<T>(
followerAlignment: followerAnchor,
position: position,
context: context,
builder: builder,
barrierColor: barrierColor,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: isGlobal == true,
settings: routeSettings,
themes: themes,
transitionsBuilder: transitionsBuilder,
duration: duration,
avoidOverflow: avoidOverflow,
offset: offset,
),
);
}
class AlignedDialogRoute<T> extends RawDialogRoute<T> {
@ -76,8 +77,11 @@ class AlignedDialogRoute<T> extends RawDialogRoute<T> {
bool avoidOverflow = false,
Offset offset = Offset.zero,
}) : super(
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation,) {
pageBuilder: (
BuildContext buildContext,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget pageChild = Builder(builder: builder);
Widget dialog = Builder(
builder: (BuildContext context) {
@ -174,10 +178,11 @@ class _FollowerDialogRouteLayout extends SingleChildLayoutDelegate {
}
Widget _buildMaterialDialogTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,) {
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
@ -187,10 +192,15 @@ Widget _buildMaterialDialogTransitions(
);
}
Offset _buildOffSet(BuildContext context,
{required Size refChildSize, required Offset offset,}) {
Offset _buildOffSet(
BuildContext context, {
required Size refChildSize,
required Offset offset,
}) {
final Size screenSize = MediaQuery.of(context).size;
final Size maxAvilableArea = Size(screenSize.width - refChildSize.width,
screenSize.height - refChildSize.height,);
final Size maxAvilableArea = Size(
screenSize.width - refChildSize.width,
screenSize.height - refChildSize.height,
);
return const Offset(0, 0);
}

@ -11,9 +11,10 @@ class PCircular extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: size ?? 25,
width: size ?? 25,
child: const CircularProgressIndicator(),),
height: size ?? 25,
width: size ?? 25,
child: const CircularProgressIndicator(),
),
],
);
}

@ -8,12 +8,13 @@ class StarRating extends StatelessWidget {
final RatingChangeCallback? onRatingChanged;
final Color color;
const StarRating(
{super.key,
this.starCount = 5,
this.rating = 0,
this.onRatingChanged,
required this.color,});
const StarRating({
super.key,
this.starCount = 5,
this.rating = 0,
this.onRatingChanged,
required this.color,
});
Widget buildStar(BuildContext context, int index) {
Icon icon;
@ -48,8 +49,8 @@ class StarRating extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children:
List.generate(starCount, (index) => buildStar(context, index)),);
mainAxisSize: MainAxisSize.min,
children: List.generate(starCount, (index) => buildStar(context, index)),
);
}
}

@ -6,11 +6,12 @@ class EditClassListTile extends StatefulWidget {
String title = '';
Function() onTap;
String subtitle = "";
EditClassListTile(
{super.key,
required this.title,
required this.onTap,
required this.subtitle,});
EditClassListTile({
super.key,
required this.title,
required this.onTap,
required this.subtitle,
});
@override
State<EditClassListTile> createState() => _EditClassListTileState();
@ -27,10 +28,13 @@ class _EditClassListTileState extends State<EditClassListTile> {
radius: Avatar.defaultSize / 2,
child: const Icon(Icons.edit_outlined),
),
title: Text('${widget.title}:',
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,),),
title: Text(
'${widget.title}:',
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
),
subtitle: Text(
widget.subtitle,
style: TextStyle(

@ -1,17 +1,17 @@
import 'package:flutter/material.dart';
class PInputTextField extends StatelessWidget {
TextEditingController controller;
Function(String) onSubmit;
String labelText;
String hintText;
PInputTextField(
{super.key,
required this.controller,
required this.onSubmit,
required this.labelText,
required this.hintText,});
PInputTextField({
super.key,
required this.controller,
required this.onSubmit,
required this.labelText,
required this.hintText,
});
@override
Widget build(BuildContext context) {
@ -24,9 +24,10 @@ class PInputTextField extends StatelessWidget {
textInputAction: TextInputAction.go,
onSubmitted: onSubmit,
decoration: InputDecoration(
labelText: labelText,
prefixIcon: const Icon(Icons.people_outlined),
hintText: hintText,),
labelText: labelText,
prefixIcon: const Icon(Icons.people_outlined),
hintText: hintText,
),
),
);
}

@ -150,7 +150,8 @@ class PangeaRichTextState extends State<PangeaRichText> {
if (repEvent.event?.eventId.contains("web") ?? false) {
Sentry.addBreadcrumb(
Breadcrumb.fromJson({"repEvent.event": repEvent.event?.toJson()}),);
Breadcrumb.fromJson({"repEvent.event": repEvent.event?.toJson()}),
);
Sentry.addBreadcrumb(
Breadcrumb(
message:
@ -206,7 +207,9 @@ class PangeaRichTextState extends State<PangeaRichText> {
];
List<TextSpan> textWithBotStyle(
RepresentationEvent repEvent, BuildContext context,) =>
RepresentationEvent repEvent,
BuildContext context,
) =>
[
TextSpan(
text: repEvent.text,
@ -218,8 +221,11 @@ class PangeaRichTextState extends State<PangeaRichText> {
// !repEvent.botAuthored
true
? widget.existingStyle
: BotStyle.text(context,
existingStyle: widget.existingStyle, setColor: false,);
: BotStyle.text(
context,
existingStyle: widget.existingStyle,
setColor: false,
);
bool get areLanguagesSet =>
userL2LangCode != null && userL2LangCode != LanguageKeys.unknownLanguage;
@ -243,7 +249,9 @@ class PangeaRichTextState extends State<PangeaRichText> {
}
Future<void> onReplacementSelect(
PangeaMatch pangeaMatch, String replacement,) async {
PangeaMatch pangeaMatch,
String replacement,
) async {
debugPrint("PTODO implement onReplacementSelect");
}

@ -69,7 +69,8 @@ class PangeaTextController extends TextEditingController {
debugPrint("onSentenceRewrite $tokenIndex $sentenceRewrite");
}),
onIgnore: () => choreographer.onIgnoreMatch(
cursorOffset: selection.baseOffset,),
cursorOffset: selection.baseOffset,
),
onITStart: () {
choreographer.onITStart(
choreographer.igc.igcTextData!.matches[matchIndex],

@ -184,10 +184,12 @@ class WordMatchContent extends StatelessWidget {
isLoading: controller.fetchingData,
choices:
controller.widget.scm.pangeaMatch!.match.choices
?.map((e) => Choice(
text: e.value,
color: e.selected ? e.type.color : null,
),)
?.map(
(e) => Choice(
text: e.value,
color: e.selected ? e.type.color : null,
),
)
.toList(),
onPressed: onChoiceSelect,
uniqueKeyForLayerLink: (int index) => "wordMatch$index",
@ -210,12 +212,15 @@ class WordMatchContent extends StatelessWidget {
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
AppConfig.primaryColor.withOpacity(0.1),),
AppConfig.primaryColor.withOpacity(0.1),
),
),
onPressed: () {
MatrixState.pAnyState.closeOverlay();
Future.delayed(Duration.zero,
() => controller.widget.scm.onIgnore(),);
Future.delayed(
Duration.zero,
() => controller.widget.scm.onIgnore(),
);
},
child: Center(
child: Text(L10n.of(context)!.ignoreInThisText),
@ -234,10 +239,11 @@ class WordMatchContent extends StatelessWidget {
: null,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
(controller.selectedChoice != null
? controller.selectedChoice!.color
: AppConfig.primaryColor)
.withOpacity(0.2),),
(controller.selectedChoice != null
? controller.selectedChoice!.color
: AppConfig.primaryColor)
.withOpacity(0.2),
),
),
child: Text(L10n.of(context)!.replace),
),
@ -249,12 +255,15 @@ class WordMatchContent extends StatelessWidget {
child: TextButton(
onPressed: () {
MatrixState.pAnyState.closeOverlay();
Future.delayed(Duration.zero,
() => controller.widget.scm.onITStart(),);
Future.delayed(
Duration.zero,
() => controller.widget.scm.onITStart(),
);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),),
(AppConfig.primaryColor).withOpacity(0.1),
),
),
child: Text(L10n.of(context)!.helpMeTranslate),
),
@ -313,7 +322,8 @@ class PromptAndFeedback extends StatelessWidget {
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
AppConfig.primaryColor.withOpacity(0.1),),
AppConfig.primaryColor.withOpacity(0.1),
),
),
child: SizedBox(
width: 150, // set the width of the button contents here

@ -39,11 +39,13 @@ class SpanData {
shortMessage: json['shortMessage'],
choices: json['choices'] != null
? List<SpanChoice>.from(
json['choices'].map((x) => SpanChoice.fromJson(x)),)
json['choices'].map((x) => SpanChoice.fromJson(x)),
)
: null,
replacements: json['replacements'] != null
? List<Replacement>.from(
json['replacements'].map((x) => Replacement.fromJson(x)),)
json['replacements'].map((x) => Replacement.fromJson(x)),
)
: null,
offset: json['offset'],
length: json['length'],
@ -51,7 +53,8 @@ class SpanData {
context:
json['context'] != null ? Context.fromJson(json['context']) : null,
type: SpanDataTypeEnum.values.firstWhereOrNull(
(e) => e.toString() == 'SpanDataTypeEnum.${json['type']}',) ??
(e) => e.toString() == 'SpanDataTypeEnum.${json['type']}',
) ??
SpanDataTypeEnum.correction,
rule: json['rule'] != null ? Rule.fromJson(json['rule']) : null,
);

@ -166,8 +166,9 @@ class WordDataCardView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CardHeader(
text: controller.widget.word,
botExpression: BotExpression.down,),
text: controller.widget.word,
botExpression: BotExpression.down,
),
if (controller.widget.choiceFeedback != null)
Text(
controller.widget.choiceFeedback!,

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import '../../../config/app_config.dart';
import '../common/pangea_logo_svg.dart';

@ -134,8 +134,8 @@ class ClassSettingsState extends State<ClassSettings> {
child: Column(
children: [
PQuestionContainer(
title:
L10n.of(context)!.selectClassRoomDominantLanguage,),
title: L10n.of(context)!.selectClassRoomDominantLanguage,
),
PLanguageDropdown(
onChange: (p0) => updatePermission(() {
classSettings.dominantLanguage = p0.langCode;
@ -148,7 +148,8 @@ class ClassSettingsState extends State<ClassSettings> {
showMultilingual: true,
),
PQuestionContainer(
title: L10n.of(context)!.selectTargetLanguage,),
title: L10n.of(context)!.selectTargetLanguage,
),
PLanguageDropdown(
onChange: (p0) => updatePermission(() {
classSettings.targetLanguage = p0.langCode;
@ -160,7 +161,8 @@ class ClassSettingsState extends State<ClassSettings> {
languages: pangeaController.pLanguageStore.targetOptions,
),
PQuestionContainer(
title: L10n.of(context)!.whatIsYourClassLanguageLevel,),
title: L10n.of(context)!.whatIsYourClassLanguageLevel,
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Container(
@ -205,7 +207,9 @@ class ClassSettingsState extends State<ClassSettings> {
value: levelOption,
child: Text(
LanguageLevelTextPicker.languageLevelText(
context, levelOption,),
context,
levelOption,
),
style: const TextStyle().copyWith(
color: Theme.of(context)
.textTheme

@ -47,34 +47,39 @@ class LanguageTile extends StatelessWidget {
// ]),
title: Text(L10n.of(context)!.myLanguages),
subtitle: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
LanguageFlag(
language: sourceLanguage,
),
const SizedBox(
width: 10,
),
Text(sourceLanguage?.getDisplayName(context) ??
L10n.of(context)!.sourceLanguage,),
const SizedBox(
width: 10,
),
const Icon(Icons.arrow_right_alt_outlined, size: 20),
const SizedBox(
width: 10,
),
LanguageFlag(
language: targetLanguage,
),
const SizedBox(
width: 10,
),
Text(targetLanguage?.getDisplayName(context) ??
L10n.of(context)!.targetLanguage,),
],),
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
LanguageFlag(
language: sourceLanguage,
),
const SizedBox(
width: 10,
),
Text(
sourceLanguage?.getDisplayName(context) ??
L10n.of(context)!.sourceLanguage,
),
const SizedBox(
width: 10,
),
const Icon(Icons.arrow_right_alt_outlined, size: 20),
const SizedBox(
width: 10,
),
LanguageFlag(
language: targetLanguage,
),
const SizedBox(
width: 10,
),
Text(
targetLanguage?.getDisplayName(context) ??
L10n.of(context)!.targetLanguage,
),
],
),
trailing: const Icon(Icons.edit_outlined),
onTap: () => pLanguageDialog(context, () {}),
);

@ -40,7 +40,8 @@ pLanguageDialog(BuildContext parentContext, Function callback) {
mainAxisSize: MainAxisSize.min,
children: [
PQuestionContainer(
title: L10n.of(parentContext)!.whatIsYourBaseLanguage,),
title: L10n.of(parentContext)!.whatIsYourBaseLanguage,
),
PLanguageDropdown(
onChange: (p0) =>
setState(() => selectedSourceLanguage = p0),
@ -48,8 +49,8 @@ pLanguageDialog(BuildContext parentContext, Function callback) {
languages: pangeaController.pLanguageStore.baseOptions,
),
PQuestionContainer(
title:
L10n.of(parentContext)!.whatLanguageYouWantToLearn,),
title: L10n.of(parentContext)!.whatLanguageYouWantToLearn,
),
PLanguageDropdown(
onChange: (p0) =>
setState(() => selectedTargetLanguage = p0),
@ -95,7 +96,8 @@ pLanguageDialog(BuildContext parentContext, Function callback) {
: ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
L10n.of(parentContext)!.noIdenticalLanguages,),
L10n.of(parentContext)!.noIdenticalLanguages,
),
backgroundColor:
Theme.of(context).colorScheme.primary,
),

@ -13,7 +13,9 @@ class PQuestionContainer extends StatelessWidget {
child: Text(
title,
style: const TextStyle().copyWith(
color: Theme.of(context).textTheme.bodyLarge!.color, fontSize: 14,),
color: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 14,
),
overflow: TextOverflow.clip,
textAlign: TextAlign.left,
),

@ -65,7 +65,10 @@ class _WordCloudTapViewState extends State<WordCloudTapView> {
wordcloudsetting.setMapSize(widget.mapwidth, widget.mapheight);
wordcloudsetting.setFont(
widget.fontFamily, widget.fontStyle, widget.fontWeight,);
widget.fontFamily,
widget.fontStyle,
widget.fontWeight,
);
wordcloudsetting.setColorList(widget.colorlist);
wordcloudsetting.setInitial();
wordcloudsetting.drawTextOptimized();
@ -115,9 +118,12 @@ class WCTpaint extends CustomPainter {
for (var i = 0; i < wordcloudpaint.getDataLength(); i++) {
if (wordcloudpaint.isdrawed[i]) {
wordcloudpaint.getTextPainter()[i].paint(
canvas,
Offset(wordcloudpaint.getWordPoint()[i][0],
wordcloudpaint.getWordPoint()[i][1],),);
canvas,
Offset(
wordcloudpaint.getWordPoint()[i][0],
wordcloudpaint.getWordPoint()[i][1],
),
);
}
}
}

@ -62,7 +62,10 @@ class _WordCloudViewState extends State<WordCloudView> {
wordcloudsetting.setMapSize(widget.mapwidth, widget.mapheight);
wordcloudsetting.setFont(
widget.fontFamily, widget.fontStyle, widget.fontWeight,);
widget.fontFamily,
widget.fontStyle,
widget.fontWeight,
);
wordcloudsetting.setColorList(widget.colorlist);
wordcloudsetting.setInitial();
@ -94,9 +97,12 @@ class WCpaint extends CustomPainter {
for (var i = 0; i < wordcloudpaint.getDataLength(); i++) {
if (wordcloudpaint.isdrawed[i]) {
wordcloudpaint.getTextPainter()[i].paint(
canvas,
Offset(wordcloudpaint.getWordPoint()[i][0],
wordcloudpaint.getWordPoint()[i][1],),);
canvas,
Offset(
wordcloudpaint.getWordPoint()[i][0],
wordcloudpaint.getWordPoint()[i][1],
),
);
}
}
}

Loading…
Cancel
Save