Merge branch 'main' into 5208-exiting-practice
This commit is contained in:
commit
b21173e482
9 changed files with 100 additions and 36 deletions
|
|
@ -29,8 +29,10 @@ class StateMessage extends StatelessWidget {
|
|||
// event.calcLocalizedBodyFallback(
|
||||
// MatrixLocals(L10n.of(context)),
|
||||
// ),
|
||||
event.type == EventTypes.RoomMember &&
|
||||
event.roomMemberChangeType == RoomMemberChangeType.leave
|
||||
(event.type == EventTypes.RoomMember) &&
|
||||
(event.roomMemberChangeType ==
|
||||
RoomMemberChangeType.leave) &&
|
||||
(event.stateKey == event.room.client.userID)
|
||||
? L10n.of(context).youLeftTheChat
|
||||
: event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:diacritic/diacritic.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
|
||||
|
|
@ -248,10 +249,11 @@ Widget _buildVocabPracticeButton(BuildContext context) {
|
|||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!hasEnoughVocab) ...[
|
||||
const Icon(Icons.lock_outline, size: 18),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
Icon(
|
||||
hasEnoughVocab ? Symbols.fitness_center : Icons.lock_outline,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(L10n.of(context).practiceVocab),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -24,16 +24,19 @@ class LemmaUsageDots extends StatelessWidget {
|
|||
});
|
||||
|
||||
/// Find lemma uses for the given exercise type, to create dot list
|
||||
List<bool> sortedUses(LearningSkillsEnum category) {
|
||||
final List<bool> useList = [];
|
||||
List<Color> sortedUses(LearningSkillsEnum category) {
|
||||
final List<Color> useList = [];
|
||||
for (final OneConstructUse use in construct.cappedUses) {
|
||||
if (use.xp == 0) {
|
||||
continue;
|
||||
}
|
||||
// If the use type matches the given category, save to list
|
||||
// Usage with positive XP is saved as true, else false
|
||||
if (category == use.useType.skillsEnumType) {
|
||||
useList.add(use.xp > 0);
|
||||
useList.add(
|
||||
switch (use.xp) {
|
||||
> 0 => AppConfig.success,
|
||||
< 0 => Colors.red,
|
||||
_ => Colors.grey[400]!,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return useList;
|
||||
|
|
@ -42,13 +45,13 @@ class LemmaUsageDots extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> dots = [];
|
||||
for (final bool use in sortedUses(category)) {
|
||||
for (final Color color in sortedUses(category)) {
|
||||
dots.add(
|
||||
Container(
|
||||
width: 15.0,
|
||||
height: 15.0,
|
||||
decoration: BoxDecoration(
|
||||
color: use ? AppConfig.success : Colors.red,
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -175,7 +175,9 @@ class LearningProgressIndicators extends StatelessWidget {
|
|||
builder: (context, hovered) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: hovered && canSelect
|
||||
color: (hovered && canSelect) ||
|
||||
(selected ==
|
||||
ProgressIndicatorEnum.level)
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ class ChoreoConstants {
|
|||
static const int msBeforeIGCStart = 10000;
|
||||
static const int maxLength = 1000;
|
||||
static const String inputTransformTargetKey = 'input_text_field';
|
||||
static const int defaultErrorBackoffSeconds = 5;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/assistance_state_enum.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/choreo_mode_enum.dart';
|
||||
|
|
@ -45,6 +47,11 @@ class Choreographer extends ChangeNotifier {
|
|||
String? _lastChecked;
|
||||
ChoreoModeEnum _choreoMode = ChoreoModeEnum.igc;
|
||||
|
||||
DateTime? _lastIgcError;
|
||||
DateTime? _lastTokensError;
|
||||
int _igcErrorBackoff = ChoreoConstants.defaultErrorBackoffSeconds;
|
||||
int _tokenErrorBackoff = ChoreoConstants.defaultErrorBackoffSeconds;
|
||||
|
||||
StreamSubscription? _languageSub;
|
||||
StreamSubscription? _settingsUpdateSub;
|
||||
StreamSubscription? _acceptedContinuanceSub;
|
||||
|
|
@ -68,6 +75,12 @@ class Choreographer extends ChangeNotifier {
|
|||
openMatches: [],
|
||||
);
|
||||
|
||||
bool _backoffRequest(DateTime? error, int backoffSeconds) {
|
||||
if (error == null) return false;
|
||||
final secondsSinceError = DateTime.now().difference(error).inSeconds;
|
||||
return secondsSinceError <= backoffSeconds;
|
||||
}
|
||||
|
||||
void _initialize() {
|
||||
textController = PangeaTextController(choreographer: this);
|
||||
textController.addListener(_onChange);
|
||||
|
|
@ -82,7 +95,14 @@ class Choreographer extends ChangeNotifier {
|
|||
itController.editing.addListener(_onSubmitSourceTextEdits);
|
||||
|
||||
igcController = IgcController(
|
||||
(e) => errorService.setErrorAndLock(ChoreoError(raw: e)),
|
||||
(e) {
|
||||
errorService.setErrorAndLock(ChoreoError(raw: e));
|
||||
_lastIgcError = DateTime.now();
|
||||
_igcErrorBackoff *= 2;
|
||||
},
|
||||
() {
|
||||
_igcErrorBackoff = ChoreoConstants.defaultErrorBackoffSeconds;
|
||||
},
|
||||
);
|
||||
|
||||
_languageSub ??= MatrixState
|
||||
|
|
@ -233,7 +253,8 @@ class Choreographer extends ChangeNotifier {
|
|||
!ToolSetting.interactiveTranslator.enabled) ||
|
||||
(!ToolSetting.autoIGC.enabled &&
|
||||
!manual &&
|
||||
_choreoMode != ChoreoModeEnum.it)) {
|
||||
_choreoMode != ChoreoModeEnum.it) ||
|
||||
_backoffRequest(_lastIgcError, _igcErrorBackoff)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +296,9 @@ class Choreographer extends ChangeNotifier {
|
|||
MatrixState.pangeaController.userController.userL2?.langCode;
|
||||
final l1LangCode =
|
||||
MatrixState.pangeaController.userController.userL1?.langCode;
|
||||
if (l1LangCode != null && l2LangCode != null) {
|
||||
if (l1LangCode != null &&
|
||||
l2LangCode != null &&
|
||||
!_backoffRequest(_lastTokensError, _tokenErrorBackoff)) {
|
||||
final res = await TokensRepo.get(
|
||||
MatrixState.pangeaController.userController.accessToken,
|
||||
TokensRequestModel(
|
||||
|
|
@ -283,7 +306,21 @@ class Choreographer extends ChangeNotifier {
|
|||
senderL1: l1LangCode,
|
||||
senderL2: l2LangCode,
|
||||
),
|
||||
).timeout(
|
||||
const Duration(seconds: 10),
|
||||
onTimeout: () {
|
||||
return Result.error("Token request timed out");
|
||||
},
|
||||
);
|
||||
|
||||
if (res.isError) {
|
||||
_lastTokensError = DateTime.now();
|
||||
_tokenErrorBackoff *= 2;
|
||||
} else {
|
||||
// reset backoff on success
|
||||
_tokenErrorBackoff = ChoreoConstants.defaultErrorBackoffSeconds;
|
||||
}
|
||||
|
||||
tokensResp = res.isValue ? res.result : null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||
|
||||
class IgcController {
|
||||
final Function(Object) onError;
|
||||
IgcController(this.onError);
|
||||
final VoidCallback onFetch;
|
||||
IgcController(this.onError, this.onFetch);
|
||||
|
||||
bool _isFetching = false;
|
||||
String? _currentText;
|
||||
|
|
@ -321,6 +322,8 @@ class IgcController {
|
|||
onError(res.asError!);
|
||||
clear();
|
||||
return;
|
||||
} else {
|
||||
onFetch();
|
||||
}
|
||||
|
||||
if (!_isFetching) return;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
|
|
@ -62,7 +63,18 @@ class TokensRepo {
|
|||
final Map<String, dynamic> json =
|
||||
jsonDecode(utf8.decode(res.bodyBytes).toString());
|
||||
|
||||
return TokensResponseModel.fromJson(json);
|
||||
final tokens = TokensResponseModel.fromJson(json);
|
||||
if (tokens.tokens.any((t) => t.pos == 'other')) {
|
||||
ErrorHandler.logError(
|
||||
e: Exception('Received token with pos "other"'),
|
||||
data: {
|
||||
"request": request.toJson(),
|
||||
"response": json,
|
||||
},
|
||||
level: SentryLevel.warning,
|
||||
);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static Future<Result<TokensResponseModel>> _getResult(
|
||||
|
|
|
|||
|
|
@ -67,26 +67,28 @@ class VocabPracticeView extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
withScrolling: false,
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
showBorder: false,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: controller.sessionState,
|
||||
builder: (context, state, __) {
|
||||
return switch (state) {
|
||||
AsyncError<VocabPracticeSessionModel>(:final error) =>
|
||||
ErrorIndicator(message: error.toString()),
|
||||
AsyncLoaded<VocabPracticeSessionModel>(:final value) =>
|
||||
value.isComplete
|
||||
? CompletedActivitySessionView(state.value, controller)
|
||||
: _VocabActivityView(controller),
|
||||
_ => loading,
|
||||
};
|
||||
},
|
||||
child: MaxWidthBody(
|
||||
withScrolling: false,
|
||||
showBorder: false,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: controller.sessionState,
|
||||
builder: (context, state, __) {
|
||||
return switch (state) {
|
||||
AsyncError<VocabPracticeSessionModel>(:final error) =>
|
||||
ErrorIndicator(message: error.toString()),
|
||||
AsyncLoaded<VocabPracticeSessionModel>(:final value) =>
|
||||
value.isComplete
|
||||
? CompletedActivitySessionView(state.value, controller)
|
||||
: _VocabActivityView(controller),
|
||||
_ => loading,
|
||||
};
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue