button to run IGC manually
This commit is contained in:
parent
c0c0a9c6a8
commit
a077cc4f6a
7 changed files with 234 additions and 16 deletions
|
|
@ -3948,5 +3948,10 @@
|
|||
"studentAnalyticsNotAvailable": "Student data not currently available",
|
||||
"roomDataMissing": "Some data may be missing from rooms in which you are not a member.",
|
||||
"updatePhoneOS": "You may need to update your device's OS version.",
|
||||
"wordsPerMinute": "Words per minute"
|
||||
"wordsPerMinute": "Words per minute",
|
||||
"autoIGCToolName": "Run Language Assistance Automatically",
|
||||
"autoIGCToolDescription": "Automatically run language assistance after typing messages",
|
||||
"runGrammarCorrection": "Run grammar correction",
|
||||
"grammarCorrectionFailed": "Grammar correction failed",
|
||||
"grammarCorrectionComplete": "Grammar correction complete"
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat/reactions_picker.dart';
|
|||
import 'package:fluffychat/pages/chat/reply_display.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/has_error_button.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/language_permissions_warning_buttons.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/pages/class_analytics/measure_able.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
|
|
@ -416,8 +417,8 @@ class ChatView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// if (controller.dragging)
|
||||
// Container(
|
||||
// color: Theme.of(context)
|
||||
// .scaffoldBackgroundColor
|
||||
// color: Theme.of(context)
|
||||
// .scaffoldBackgroundColor
|
||||
// .withOpacity(0.9),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
|
|
@ -425,6 +426,11 @@ class ChatView extends StatelessWidget {
|
|||
// size: 100,
|
||||
// ),
|
||||
// ),
|
||||
Positioned(
|
||||
left: 20,
|
||||
bottom: 75,
|
||||
child: StartIGCButton(controller: controller),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class Choreographer {
|
|||
// last checked by IGC or translation
|
||||
String? _lastChecked;
|
||||
ChoreoMode choreoMode = ChoreoMode.igc;
|
||||
final StreamController stateListener = StreamController();
|
||||
final StreamController stateListener = StreamController.broadcast();
|
||||
StreamSubscription? trialStream;
|
||||
|
||||
Choreographer(this.pangeaController, this.chatController) {
|
||||
|
|
@ -205,14 +205,18 @@ class Choreographer {
|
|||
textController.editType = EditType.keyboard;
|
||||
}
|
||||
|
||||
Future<void> getLanguageHelp([bool tokensOnly = false]) async {
|
||||
Future<void> getLanguageHelp([
|
||||
bool tokensOnly = false,
|
||||
bool manual = false,
|
||||
]) async {
|
||||
try {
|
||||
if (errorService.isError) return;
|
||||
final CanSendStatus canSendStatus =
|
||||
pangeaController.subscriptionController.canSendStatus;
|
||||
|
||||
if (canSendStatus != CanSendStatus.subscribed ||
|
||||
(!igcEnabled && !itEnabled)) {
|
||||
(!igcEnabled && !itEnabled) ||
|
||||
(!isAutoIGCEnabled && !manual && choreoMode != ChoreoMode.it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -535,4 +539,40 @@ class Choreographer {
|
|||
pangeaController.permissionsController.isWritingAssistanceEnabled(
|
||||
chatController.room,
|
||||
);
|
||||
|
||||
bool get isAutoIGCEnabled =>
|
||||
pangeaController.permissionsController.isToolEnabled(
|
||||
ToolSetting.autoIGC,
|
||||
chatController.room,
|
||||
);
|
||||
|
||||
AssistanceState get assistanceState {
|
||||
if (currentText.isEmpty && itController.sourceText == null) {
|
||||
return AssistanceState.noMessage;
|
||||
}
|
||||
|
||||
if (igc.igcTextData?.matches.isNotEmpty ?? false) {
|
||||
return AssistanceState.fetched;
|
||||
}
|
||||
|
||||
if (isFetching) {
|
||||
return AssistanceState.fetching;
|
||||
}
|
||||
|
||||
if (igc.igcTextData == null) {
|
||||
return AssistanceState.notFetched;
|
||||
}
|
||||
|
||||
return AssistanceState.complete;
|
||||
}
|
||||
}
|
||||
|
||||
// assistance state is, user has not typed a message, user has typed a message and IGC has not run,
|
||||
// IGC is running, IGC has run and there are remaining steps (either IT or IGC), or all steps are done
|
||||
enum AssistanceState {
|
||||
noMessage,
|
||||
notFetched,
|
||||
fetching,
|
||||
fetched,
|
||||
complete,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:fluffychat/pangea/constants/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/colors.dart';
|
||||
import '../../../pages/chat/chat.dart';
|
||||
|
||||
class ChoreographerSendButton extends StatelessWidget {
|
||||
|
|
@ -16,7 +15,8 @@ class ChoreographerSendButton extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// commit for cicd
|
||||
return controller.choreographer.isFetching
|
||||
return controller.choreographer.isFetching &&
|
||||
controller.choreographer.isAutoIGCEnabled
|
||||
? Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
|
|
@ -28,7 +28,8 @@ class ChoreographerSendButton extends StatelessWidget {
|
|||
alignment: Alignment.center,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.send_outlined),
|
||||
color: controller.choreographer.igc.canSendMessage
|
||||
color: controller.choreographer.igc.canSendMessage ||
|
||||
!controller.choreographer.isAutoIGCEnabled
|
||||
? null
|
||||
: PangeaColors.igcError,
|
||||
onPressed: () {
|
||||
|
|
|
|||
150
lib/pangea/choreographer/widgets/start_igc_button.dart
Normal file
150
lib/pangea/choreographer/widgets/start_igc_button.dart
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/constants/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../../../pages/chat/chat.dart';
|
||||
|
||||
class StartIGCButton extends StatefulWidget {
|
||||
const StartIGCButton({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
final ChatController controller;
|
||||
|
||||
@override
|
||||
State<StartIGCButton> createState() => StartIGCButtonState();
|
||||
}
|
||||
|
||||
class StartIGCButtonState extends State<StartIGCButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AssistanceState get assistanceState =>
|
||||
widget.controller.choreographer.assistanceState;
|
||||
AnimationController? _controller;
|
||||
StreamSubscription? choreoListener;
|
||||
AssistanceState? prevState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 1),
|
||||
);
|
||||
choreoListener = widget.controller.choreographer.stateListener.stream
|
||||
.listen(updateSpinnerState);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void updateSpinnerState(_) {
|
||||
if (prevState != AssistanceState.fetching &&
|
||||
assistanceState == AssistanceState.fetching) {
|
||||
_controller?.repeat();
|
||||
} else if (prevState == AssistanceState.fetching &&
|
||||
assistanceState != AssistanceState.fetching) {
|
||||
_controller?.stop();
|
||||
_controller?.reverse();
|
||||
}
|
||||
setState(() => prevState = assistanceState);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.controller.choreographer.isAutoIGCEnabled) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final Widget icon = Icon(
|
||||
Icons.autorenew_rounded,
|
||||
size: 46,
|
||||
color: assistanceState.stateColor,
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: FloatingActionButton(
|
||||
tooltip: assistanceState.tooltip(
|
||||
L10n.of(context)!,
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
disabledElevation: 0,
|
||||
shape: const CircleBorder(),
|
||||
onPressed: () {
|
||||
if (assistanceState != AssistanceState.complete) {
|
||||
widget.controller.choreographer.getLanguageHelp(
|
||||
false,
|
||||
true,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_controller != null
|
||||
? RotationTransition(
|
||||
turns: Tween(begin: 0.0, end: math.pi * 2)
|
||||
.animate(_controller!),
|
||||
child: icon,
|
||||
)
|
||||
: icon,
|
||||
Container(
|
||||
width: 26,
|
||||
height: 26,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: assistanceState.stateColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
size: 16,
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension AssistanceStateExtension on AssistanceState {
|
||||
Color get stateColor {
|
||||
switch (this) {
|
||||
case AssistanceState.noMessage:
|
||||
case AssistanceState.notFetched:
|
||||
case AssistanceState.fetching:
|
||||
return AppConfig.primaryColor;
|
||||
case AssistanceState.fetched:
|
||||
return PangeaColors.igcError;
|
||||
case AssistanceState.complete:
|
||||
return AppConfig.success;
|
||||
}
|
||||
}
|
||||
|
||||
String tooltip(L10n l10n) {
|
||||
switch (this) {
|
||||
case AssistanceState.noMessage:
|
||||
case AssistanceState.notFetched:
|
||||
return l10n.runGrammarCorrection;
|
||||
case AssistanceState.fetching:
|
||||
return "";
|
||||
case AssistanceState.fetched:
|
||||
return l10n.grammarCorrectionFailed;
|
||||
case AssistanceState.complete:
|
||||
return l10n.grammarCorrectionComplete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../constants/class_default_values.dart';
|
||||
import '../constants/language_keys.dart';
|
||||
import '../constants/pangea_event_types.dart';
|
||||
|
|
@ -124,6 +123,7 @@ class PangeaRoomRules {
|
|||
int immersionMode;
|
||||
int definitions;
|
||||
int translations;
|
||||
int autoIGC;
|
||||
|
||||
PangeaRoomRules({
|
||||
this.isPublic = false,
|
||||
|
|
@ -142,6 +142,7 @@ class PangeaRoomRules {
|
|||
this.immersionMode = ClassDefaultValues.languageToolPermissions,
|
||||
this.definitions = ClassDefaultValues.languageToolPermissions,
|
||||
this.translations = ClassDefaultValues.languageToolPermissions,
|
||||
this.autoIGC = ClassDefaultValues.languageToolPermissions,
|
||||
});
|
||||
|
||||
updatePermission(String key, bool value) {
|
||||
|
|
@ -201,6 +202,9 @@ class PangeaRoomRules {
|
|||
case ToolSetting.translations:
|
||||
translations = value;
|
||||
break;
|
||||
case ToolSetting.autoIGC:
|
||||
autoIGC = value;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Invalid key for setting permissions - $setting');
|
||||
}
|
||||
|
|
@ -235,6 +239,7 @@ class PangeaRoomRules {
|
|||
json['definitions'] ?? ClassDefaultValues.languageToolPermissions,
|
||||
translations:
|
||||
json['translations'] ?? ClassDefaultValues.languageToolPermissions,
|
||||
autoIGC: json['auto_igc'] ?? ClassDefaultValues.languageToolPermissions,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -256,6 +261,7 @@ class PangeaRoomRules {
|
|||
data['immersion_mode'] = immersionMode;
|
||||
data['definitions'] = definitions;
|
||||
data['translations'] = translations;
|
||||
data['auto_igc'] = autoIGC;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -271,6 +277,8 @@ class PangeaRoomRules {
|
|||
return definitions;
|
||||
case ToolSetting.translations:
|
||||
return translations;
|
||||
case ToolSetting.autoIGC:
|
||||
return autoIGC;
|
||||
default:
|
||||
throw Exception('Invalid key for setting permissions - $setting');
|
||||
}
|
||||
|
|
@ -299,6 +307,7 @@ enum ToolSetting {
|
|||
immersionMode,
|
||||
definitions,
|
||||
translations,
|
||||
autoIGC,
|
||||
}
|
||||
|
||||
extension SettingCopy on ToolSetting {
|
||||
|
|
@ -314,6 +323,8 @@ extension SettingCopy on ToolSetting {
|
|||
return L10n.of(context)!.definitionsToolName;
|
||||
case ToolSetting.translations:
|
||||
return L10n.of(context)!.messageTranslationsToolName;
|
||||
case ToolSetting.autoIGC:
|
||||
return L10n.of(context)!.autoIGCToolName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -330,6 +341,8 @@ extension SettingCopy on ToolSetting {
|
|||
return L10n.of(context)!.definitionsToolDescription;
|
||||
case ToolSetting.translations:
|
||||
return L10n.of(context)!.translationsToolDescrption;
|
||||
case ToolSetting.autoIGC:
|
||||
return L10n.of(context)!.autoIGCToolDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ enum MatrixProfile {
|
|||
sourceLanguage,
|
||||
country,
|
||||
publicProfile,
|
||||
autoIGC,
|
||||
}
|
||||
|
||||
extension MatrixProfileExtension on MatrixProfile {
|
||||
|
|
@ -89,6 +90,8 @@ extension MatrixProfileExtension on MatrixProfile {
|
|||
return ToolSetting.definitions.toString();
|
||||
case MatrixProfile.translations:
|
||||
return ToolSetting.translations.toString();
|
||||
case MatrixProfile.autoIGC:
|
||||
return ToolSetting.autoIGC.toString();
|
||||
case MatrixProfile.showedItInstructions:
|
||||
return InstructionsEnum.itInstructions.toString();
|
||||
case MatrixProfile.showedClickMessage:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue