removed excessive calls to setState in chat.dart and replaced them with smaller, stateful widgets
This commit is contained in:
parent
efa325fa43
commit
280915fc96
16 changed files with 472 additions and 704 deletions
|
|
@ -20,7 +20,6 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_e
|
|||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/models/choreo_record.dart';
|
||||
import 'package:fluffychat/pangea/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
|
||||
|
|
@ -294,10 +293,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
bool showPermissionsError = false;
|
||||
// #Pangea
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
scrollController.addListener(_updateScrollController);
|
||||
|
|
@ -327,31 +322,12 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
context,
|
||||
() => Future.delayed(
|
||||
Duration.zero,
|
||||
() => setState(
|
||||
() {},
|
||||
),
|
||||
() => setState(() {}),
|
||||
),
|
||||
);
|
||||
}
|
||||
await Matrix.of(context).client.roomsLoading;
|
||||
choreographer.setRoomId(roomId);
|
||||
choreographer.messageOptions.resetSelectedDisplayLang();
|
||||
choreographer.stateListener.stream.listen((event) {
|
||||
debugPrint("chat.dart choreo event $event");
|
||||
setState(() {});
|
||||
});
|
||||
showPermissionsError = !pangeaController.permissionsController
|
||||
.isToolEnabled(ToolSetting.interactiveTranslator, room) ||
|
||||
!pangeaController.permissionsController
|
||||
.isToolEnabled(ToolSetting.interactiveGrammar, room);
|
||||
});
|
||||
|
||||
Future.delayed(
|
||||
const Duration(seconds: 5),
|
||||
() {
|
||||
if (mounted) setState(() => showPermissionsError = false);
|
||||
},
|
||||
);
|
||||
// Pangea#
|
||||
_tryLoadTimeline();
|
||||
if (kIsWeb) {
|
||||
|
|
@ -602,10 +578,9 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
|
||||
// #Pangea
|
||||
final List<String> edittingEvents = [];
|
||||
void clearEdittingEvent(String eventId) {
|
||||
edittingEvents.remove(eventId);
|
||||
setState(() {});
|
||||
Event? pangeaEditingEvent;
|
||||
void clearEditingEvent() {
|
||||
pangeaEditingEvent = null;
|
||||
}
|
||||
|
||||
// Future<void> send() async {
|
||||
|
|
@ -665,11 +640,9 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
.then(
|
||||
(String? msgEventId) async {
|
||||
// #Pangea
|
||||
setState(() {
|
||||
if (previousEdit != null) {
|
||||
edittingEvents.add(previousEdit.eventId);
|
||||
}
|
||||
});
|
||||
if (previousEdit != null) {
|
||||
pangeaEditingEvent = previousEdit;
|
||||
}
|
||||
|
||||
GoogleAnalytics.sendMessage(
|
||||
room.id,
|
||||
|
|
@ -1262,9 +1235,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
void clearSelectedEvents() => setState(() {
|
||||
selectedEvents.clear();
|
||||
showEmojiPicker = false;
|
||||
//#Pangea
|
||||
choreographer.messageOptions.resetSelectedDisplayLang();
|
||||
//Pangea#
|
||||
});
|
||||
|
||||
void clearSingleSelectedEvent() {
|
||||
|
|
@ -1336,19 +1306,19 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
// Pangea#
|
||||
if (!event.redacted) {
|
||||
// #Pangea
|
||||
// If previous selectedEvent has same eventId, delete previous selectedEvent
|
||||
final matches =
|
||||
selectedEvents.where((e) => e.eventId == event.eventId).toList();
|
||||
// if (selectedEvents.contains(event)) {
|
||||
// setState(
|
||||
// () => selectedEvents.remove(event),
|
||||
// );
|
||||
// }
|
||||
|
||||
// If delete first selected event with the selected eventID
|
||||
final matches = selectedEvents.where((e) => e.eventId == event.eventId);
|
||||
if (matches.isNotEmpty) {
|
||||
// if (selectedEvents.contains(event)) {
|
||||
// Pangea#
|
||||
setState(
|
||||
// #Pangea
|
||||
() => selectedEvents.remove(matches.first),
|
||||
// () => selectedEvents.remove(event),
|
||||
// Pangea#
|
||||
);
|
||||
} else {
|
||||
setState(() => selectedEvents.remove(matches.first));
|
||||
}
|
||||
// Pangea#
|
||||
else {
|
||||
setState(
|
||||
() => selectedEvents.add(event),
|
||||
);
|
||||
|
|
@ -1557,35 +1527,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
|
||||
// #Pangea
|
||||
double? availableSpace;
|
||||
double? inputRowSize;
|
||||
bool? lastState;
|
||||
bool get isRowScrollable {
|
||||
if (availableSpace == null || inputRowSize == null) {
|
||||
if (lastState == null) {
|
||||
lastState = false;
|
||||
Future.delayed(Duration.zero, () {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const double offSetValue = 10;
|
||||
final bool currentState = inputRowSize! > (availableSpace! - offSetValue);
|
||||
if (!lastState! && currentState) {
|
||||
Future.delayed(Duration.zero, () {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
if (lastState! && !currentState) {
|
||||
Future.delayed(Duration.zero, () {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
lastState = currentState;
|
||||
return currentState;
|
||||
}
|
||||
|
||||
final Map<String, PangeaMessageEvent> _pangeaMessageEvents = {};
|
||||
final Map<String, ToolbarDisplayController> _toolbarDisplayControllers = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -170,8 +170,6 @@ class ChatEventList extends StatelessWidget {
|
|||
controller.scrollToEventId(eventId),
|
||||
longPressSelect: controller.selectedEvents.isNotEmpty,
|
||||
// #Pangea
|
||||
selectedDisplayLang:
|
||||
controller.choreographer.messageOptions.selectedDisplayLang,
|
||||
immersionMode: controller.choreographer.immersionMode,
|
||||
definitions: controller.choreographer.definitionsEnabled,
|
||||
controller: controller,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/input_bar_wrapper.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -12,7 +13,6 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import '../../config/themes.dart';
|
||||
import 'chat.dart';
|
||||
import 'input_bar.dart';
|
||||
|
||||
class ChatInputRow extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
|
@ -322,7 +322,10 @@ class ChatInputRow extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 0.0),
|
||||
child: InputBar(
|
||||
// #Pangea
|
||||
// child: InputBar(
|
||||
child: InputBarWrapper(
|
||||
// Pangea#
|
||||
room: controller.room,
|
||||
minLines: 1,
|
||||
maxLines: 8,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@ import 'package:fluffychat/pages/chat/chat_event_list.dart';
|
|||
import 'package:fluffychat/pages/chat/pinned_events.dart';
|
||||
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/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/pages/class_analytics/measure_able.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
||||
import 'package:fluffychat/widgets/connection_status_header.dart';
|
||||
|
|
@ -266,32 +264,20 @@ class ChatView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// floatingActionButton: controller.showScrollDownButton &&
|
||||
// controller.selectedEvents.isEmpty
|
||||
floatingActionButton: controller.selectedEvents.isEmpty
|
||||
? (controller.showScrollDownButton
|
||||
// Pangea#
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(bottom: 56.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: controller.scrollDown,
|
||||
heroTag: null,
|
||||
mini: true,
|
||||
child: const Icon(Icons.arrow_downward_outlined),
|
||||
),
|
||||
)
|
||||
// #Pangea
|
||||
: controller.choreographer.errorService.error != null
|
||||
? ChoreographerHasErrorButton(
|
||||
controller.pangeaController,
|
||||
controller.choreographer.errorService.error!,
|
||||
)
|
||||
: controller.showPermissionsError
|
||||
? LanguagePermissionsButtons(
|
||||
choreographer: controller.choreographer,
|
||||
roomID: controller.roomId,
|
||||
)
|
||||
: null)
|
||||
// #Pangea
|
||||
: null,
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.only(bottom: 56.0),
|
||||
// child: FloatingActionButton(
|
||||
// onPressed: controller.scrollDown,
|
||||
// heroTag: null,
|
||||
// mini: true,
|
||||
// child: const Icon(Icons.arrow_downward_outlined),
|
||||
// ),
|
||||
// )
|
||||
// : null,
|
||||
floatingActionButton: ChatFloatingActionButton(
|
||||
controller: controller,
|
||||
),
|
||||
// Pangea#
|
||||
body:
|
||||
// #Pangea
|
||||
// DropTarget(
|
||||
|
|
@ -338,120 +324,100 @@ class ChatView extends StatelessWidget {
|
|||
),
|
||||
if (controller.room.canSendDefaultMessages &&
|
||||
controller.room.membership == Membership.join)
|
||||
// #Pangea
|
||||
// Container(
|
||||
ConditionalFlexible(
|
||||
isScroll: controller.isRowScrollable,
|
||||
child: ConditionalScroll(
|
||||
isScroll: controller.isRowScrollable,
|
||||
child: MeasurableWidget(
|
||||
onChange: (size, position) {
|
||||
controller.inputRowSize = size!.height;
|
||||
},
|
||||
child: Container(
|
||||
// Pangea#
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
// ignore: deprecated_member_use
|
||||
.surfaceVariant,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: controller.room.isAbandonedDMRoom ==
|
||||
true
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (controller.room.isRoomAdmin)
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding:
|
||||
const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed:
|
||||
controller.archiveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.archive,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
// #Pangea
|
||||
// Icons.archive_outlined,
|
||||
Icons.arrow_forward,
|
||||
// Pangea#
|
||||
),
|
||||
onPressed: controller.leaveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.leave,
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.forum_outlined,
|
||||
),
|
||||
onPressed:
|
||||
controller.recreateChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.reopenChat,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ConnectionStatusHeader(),
|
||||
ITBar(
|
||||
choreographer:
|
||||
controller.choreographer,
|
||||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
ChatEmojiPicker(controller),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
// ignore: deprecated_member_use
|
||||
.surfaceVariant,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: controller.room.isAbandonedDMRoom == true
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (controller.room.isRoomAdmin)
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed: controller.archiveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.archive,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
// #Pangea
|
||||
// Icons.archive_outlined,
|
||||
Icons.arrow_forward,
|
||||
// Pangea#
|
||||
),
|
||||
onPressed: controller.leaveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.leave,
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.forum_outlined,
|
||||
),
|
||||
onPressed: controller.recreateChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.reopenChat,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ConnectionStatusHeader(),
|
||||
ITBar(
|
||||
choreographer:
|
||||
controller.choreographer,
|
||||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
ChatEmojiPicker(controller),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -484,35 +450,3 @@ class ChatView extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
Widget ConditionalFlexible({required bool isScroll, required Widget child}) {
|
||||
if (isScroll) {
|
||||
return Flexible(
|
||||
flex: 9999999,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
class ConditionalScroll extends StatelessWidget {
|
||||
final bool isScroll;
|
||||
final Widget child;
|
||||
const ConditionalScroll({
|
||||
super.key,
|
||||
required this.isScroll,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isScroll) {
|
||||
return SingleChildScrollView(
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/enum/use_type.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_buttons.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
|
|
@ -39,7 +38,6 @@ class Message extends StatelessWidget {
|
|||
final bool animateIn;
|
||||
final void Function()? resetAnimateIn;
|
||||
// #Pangea
|
||||
final LanguageModel? selectedDisplayLang;
|
||||
final bool immersionMode;
|
||||
final bool definitions;
|
||||
final ChatController controller;
|
||||
|
|
@ -64,7 +62,6 @@ class Message extends StatelessWidget {
|
|||
this.resetAnimateIn,
|
||||
this.avatarPresenceBackgroundColor,
|
||||
// #Pangea
|
||||
required this.selectedDisplayLang,
|
||||
required this.immersionMode,
|
||||
required this.definitions,
|
||||
required this.controller,
|
||||
|
|
@ -82,9 +79,9 @@ class Message extends StatelessWidget {
|
|||
// #Pangea
|
||||
debugPrint('Message.build()');
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (controller.edittingEvents.contains(event.eventId)) {
|
||||
if (controller.pangeaEditingEvent?.eventId == event.eventId) {
|
||||
pangeaMessageEvent?.updateLatestEdit();
|
||||
controller.clearEdittingEvent(event.eventId);
|
||||
controller.clearEditingEvent();
|
||||
}
|
||||
});
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'dart:developer';
|
|||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/alternative_translator.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/igc_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/message_options.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
|
||||
|
|
@ -38,7 +37,6 @@ class Choreographer {
|
|||
late PangeaTextController _textController;
|
||||
late ITController itController;
|
||||
late IgcController igc;
|
||||
late MessageOptions messageOptions;
|
||||
late AlternativeTranslator altTranslator;
|
||||
late ErrorService errorService;
|
||||
|
||||
|
|
@ -59,7 +57,6 @@ class Choreographer {
|
|||
_textController = PangeaTextController(choreographer: this);
|
||||
itController = ITController(this);
|
||||
igc = IgcController(this);
|
||||
messageOptions = MessageOptions(this);
|
||||
errorService = ErrorService(this);
|
||||
altTranslator = AlternativeTranslator(this);
|
||||
_textController.addListener(_onChangeListener);
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
|
||||
|
||||
class MessageOptions {
|
||||
Choreographer choreographer;
|
||||
LanguageModel? _selectedDisplayLang;
|
||||
|
||||
MessageOptions(this.choreographer);
|
||||
|
||||
LanguageModel? get selectedDisplayLang {
|
||||
if (_selectedDisplayLang != null &&
|
||||
_selectedDisplayLang!.langCode != LanguageKeys.unknownLanguage) {
|
||||
return _selectedDisplayLang;
|
||||
}
|
||||
_selectedDisplayLang = choreographer.l2Lang;
|
||||
return _selectedDisplayLang;
|
||||
}
|
||||
|
||||
bool get isTranslationOn =>
|
||||
_selectedDisplayLang?.langCode != choreographer.l2LangCode;
|
||||
|
||||
// void setSelectedDisplayLang(LanguageModel? newLang) {
|
||||
// _selectedDisplayLang = newLang;
|
||||
// choreographer.setState();
|
||||
// }
|
||||
|
||||
void toggleSelectedDisplayLang() {
|
||||
if (_selectedDisplayLang?.langCode == choreographer.l2LangCode) {
|
||||
_selectedDisplayLang = choreographer.l1Lang;
|
||||
} else {
|
||||
_selectedDisplayLang = choreographer.l2Lang;
|
||||
}
|
||||
debugPrint('toggleSelectedDisplayLang: ${_selectedDisplayLang?.langCode}');
|
||||
choreographer.setState();
|
||||
GoogleAnalytics.messageTranslate();
|
||||
}
|
||||
|
||||
void resetSelectedDisplayLang() {
|
||||
_selectedDisplayLang = choreographer.l2Lang;
|
||||
choreographer.setState();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
|
|
@ -7,7 +8,6 @@ import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/widgets/translation_finished_flow.dart';
|
||||
import 'package:fluffychat/pangea/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
|
@ -18,112 +18,140 @@ import '../../utils/overlay.dart';
|
|||
import '../../widgets/igc/word_data_card.dart';
|
||||
import 'choice_array.dart';
|
||||
|
||||
class ITBar extends StatelessWidget {
|
||||
class ITBar extends StatefulWidget {
|
||||
final Choreographer choreographer;
|
||||
const ITBar({super.key, required this.choreographer});
|
||||
|
||||
ITController get itController => choreographer.itController;
|
||||
@override
|
||||
ITBarState createState() => ITBarState();
|
||||
}
|
||||
|
||||
class ITBarState extends State<ITBar> {
|
||||
ITController get itController => widget.choreographer.itController;
|
||||
StreamSubscription? _choreoSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// Rebuild the widget each time there's an update from choreo.
|
||||
_choreoSub = widget.choreographer.stateListener.stream.listen((_) {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_choreoSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return AnimatedSize(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
clipBehavior: Clip.none,
|
||||
child: !itController.willOpen
|
||||
? const SizedBox()
|
||||
: CompositedTransformTarget(
|
||||
link: choreographer.itBarLinkAndKey.link,
|
||||
child: AnimatedOpacity(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
opacity: itController.willOpen ? 1.0 : 0.0,
|
||||
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),
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // Row(
|
||||
// // mainAxisAlignment: MainAxisAlignment.start,
|
||||
// // crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// // children: [
|
||||
// // CounterDisplay(
|
||||
// // correct: controller.correctChoices,
|
||||
// // custom: controller.customChoices,
|
||||
// // incorrect: controller.incorrectChoices,
|
||||
// // yellow: controller.wildcardChoices,
|
||||
// // ),
|
||||
// // CompositedTransformTarget(
|
||||
// // link: choreographer.itBotLayerLinkAndKey.link,
|
||||
// // child: ITBotButton(
|
||||
// // key: choreographer.itBotLayerLinkAndKey.key,
|
||||
// // choreographer: choreographer,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// ITCloseButton(choreographer: choreographer),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 40.0),
|
||||
OriginalText(controller: itController),
|
||||
const SizedBox(height: 7.0),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 80),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Center(
|
||||
child: itController.choreographer.errorService.isError
|
||||
? ITError(
|
||||
error: itController
|
||||
.choreographer.errorService.error!,
|
||||
controller: itController,
|
||||
)
|
||||
: itController.showChoiceFeedback
|
||||
? ChoiceFeedbackText(controller: itController)
|
||||
: itController.isTranslationDone
|
||||
? TranslationFeedback(
|
||||
controller: itController,
|
||||
)
|
||||
: ITChoices(controller: itController),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
? const SizedBox()
|
||||
: CompositedTransformTarget(
|
||||
link: widget.choreographer.itBarLinkAndKey.link,
|
||||
child: AnimatedOpacity(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
opacity: itController.willOpen ? 1.0 : 0.0,
|
||||
child: Container(
|
||||
key: widget.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),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
child: ITCloseButton(choreographer: choreographer),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // Row(
|
||||
// // mainAxisAlignment: MainAxisAlignment.start,
|
||||
// // crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// // children: [
|
||||
// // CounterDisplay(
|
||||
// // correct: controller.correctChoices,
|
||||
// // custom: controller.customChoices,
|
||||
// // incorrect: controller.incorrectChoices,
|
||||
// // yellow: controller.wildcardChoices,
|
||||
// // ),
|
||||
// // CompositedTransformTarget(
|
||||
// // link: choreographer.itBotLayerLinkAndKey.link,
|
||||
// // child: ITBotButton(
|
||||
// // key: choreographer.itBotLayerLinkAndKey.key,
|
||||
// // choreographer: choreographer,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// ITCloseButton(choreographer: choreographer),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 40.0),
|
||||
OriginalText(controller: itController),
|
||||
const SizedBox(height: 7.0),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
constraints:
|
||||
const BoxConstraints(minHeight: 80),
|
||||
width: double.infinity,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Center(
|
||||
child: itController
|
||||
.choreographer.errorService.isError
|
||||
? ITError(
|
||||
error: itController.choreographer
|
||||
.errorService.error!,
|
||||
controller: itController,
|
||||
)
|
||||
: itController.showChoiceFeedback
|
||||
? ChoiceFeedbackText(
|
||||
controller: itController,
|
||||
)
|
||||
: itController.isTranslationDone
|
||||
? TranslationFeedback(
|
||||
controller: itController,
|
||||
)
|
||||
: ITChoices(
|
||||
controller: itController,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
child:
|
||||
ITCloseButton(choreographer: widget.choreographer),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -199,20 +227,16 @@ class OriginalText extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (
|
||||
!controller.isEditingSourceText
|
||||
&& controller.sourceText != null
|
||||
)
|
||||
if (!controller.isEditingSourceText && controller.sourceText != null)
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
opacity: controller.nextITStep != null
|
||||
? 1.0
|
||||
: 0.0,
|
||||
opacity: controller.nextITStep != null ? 1.0 : 0.0,
|
||||
child: IconButton(
|
||||
onPressed: () => {
|
||||
if (controller.nextITStep != null) {
|
||||
controller.setIsEditingSourceText(true),
|
||||
},
|
||||
if (controller.nextITStep != null)
|
||||
{
|
||||
controller.setIsEditingSourceText(true),
|
||||
},
|
||||
},
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
),
|
||||
|
|
@ -309,9 +333,9 @@ class ITChoices extends StatelessWidget {
|
|||
choices: controller.currentITStep!.continuances.map((e) {
|
||||
try {
|
||||
return Choice(
|
||||
text: e.text.trim(),
|
||||
color: e.color,
|
||||
isGold: e.description == "best",
|
||||
text: e.text.trim(),
|
||||
color: e.color,
|
||||
isGold: e.description == "best",
|
||||
);
|
||||
} catch (e) {
|
||||
debugger(when: kDebugMode);
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../../../config/app_config.dart';
|
||||
import '../../../pages/chat/chat.dart';
|
||||
|
||||
class LanguageDisplayToggle extends StatelessWidget {
|
||||
const LanguageDisplayToggle({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
final ChatController controller;
|
||||
|
||||
get onPressed =>
|
||||
controller.choreographer.messageOptions.toggleSelectedDisplayLang;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// if (!controller.choreographer.translationEnabled) {
|
||||
// return const SizedBox();
|
||||
// }
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: controller.choreographer.messageOptions.isTranslationOn
|
||||
? AppConfig.primaryColor
|
||||
: null,
|
||||
),
|
||||
child: IconButton(
|
||||
tooltip: L10n.of(context)!.toggleLanguages,
|
||||
onPressed: onPressed,
|
||||
icon: const Icon(Icons.translate_outlined),
|
||||
selectedIcon: const Icon(Icons.translate),
|
||||
isSelected: controller.choreographer.messageOptions.isTranslationOn,
|
||||
),
|
||||
);
|
||||
// return Tooltip(
|
||||
// message: L10n.of(context)!.toggleLanguages,
|
||||
// waitDuration: const Duration(milliseconds: 1000),
|
||||
// child: FloatingActionButton(
|
||||
// onPressed: onPressed,
|
||||
// backgroundColor: Colors.white,
|
||||
// mini: false,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(200), // <-- Radius
|
||||
// ),
|
||||
// child: LanguageFlag(
|
||||
// flagUrl: controller
|
||||
// .choreographer.messageOptions.displayLang?.languageFlag,
|
||||
// size: 50,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,47 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fluffychat/pangea/enum/assistance_state_enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../../../pages/chat/chat.dart';
|
||||
|
||||
class ChoreographerSendButton extends StatelessWidget {
|
||||
class ChoreographerSendButton extends StatefulWidget {
|
||||
const ChoreographerSendButton({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
final ChatController controller;
|
||||
|
||||
@override
|
||||
State<ChoreographerSendButton> createState() =>
|
||||
ChoreographerSendButtonState();
|
||||
}
|
||||
|
||||
class ChoreographerSendButtonState extends State<ChoreographerSendButton> {
|
||||
StreamSubscription? _choreoSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// Rebuild the widget each time there's an update from
|
||||
// choreo. This keeps the spin up-to-date.
|
||||
_choreoSub =
|
||||
widget.controller.choreographer.stateListener.stream.listen((_) {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_choreoSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// commit for cicd
|
||||
return controller.choreographer.isFetching &&
|
||||
controller.choreographer.isAutoIGCEnabled
|
||||
return widget.controller.choreographer.isFetching &&
|
||||
widget.controller.choreographer.isAutoIGCEnabled
|
||||
? Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
|
|
@ -28,10 +53,10 @@ class ChoreographerSendButton extends StatelessWidget {
|
|||
alignment: Alignment.center,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.send_outlined),
|
||||
color:
|
||||
controller.choreographer.assistanceState.stateColor(context),
|
||||
color: widget.controller.choreographer.assistanceState
|
||||
.stateColor(context),
|
||||
onPressed: () {
|
||||
controller.choreographer.send(context);
|
||||
widget.controller.choreographer.send(context);
|
||||
},
|
||||
tooltip: L10n.of(context)!.send,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -82,10 +82,9 @@ class PangeaMessageEvent {
|
|||
.firstOrNull ??
|
||||
_event;
|
||||
|
||||
Event updateLatestEdit() {
|
||||
void updateLatestEdit() {
|
||||
_latestEditCache = null;
|
||||
_representations = null;
|
||||
return _latestEdit;
|
||||
}
|
||||
|
||||
Future<PangeaAudioFile> getMatrixAudioFile(
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
class MeasurableWidget extends StatefulWidget {
|
||||
final Widget child;
|
||||
final Function(Size? size, Offset? position) onChange;
|
||||
|
||||
const MeasurableWidget({
|
||||
super.key,
|
||||
required this.onChange,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
_WidgetSizeState createState() => _WidgetSizeState();
|
||||
}
|
||||
|
||||
class _WidgetSizeState extends State<MeasurableWidget> {
|
||||
var widgetKey = GlobalKey();
|
||||
Offset? oldPosition;
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void postFrameCallback(_) {
|
||||
final context = widgetKey.currentContext;
|
||||
if (context == null) return;
|
||||
|
||||
final RenderBox? box =
|
||||
widgetKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
|
||||
if (box != null && box.hasSize) {
|
||||
final Offset position = box.localToGlobal(Offset.zero);
|
||||
|
||||
if (oldPosition != null) {
|
||||
if (oldPosition!.dx == position.dx && oldPosition!.dy == position.dy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
oldPosition = position;
|
||||
|
||||
final newSize = context.size;
|
||||
widget.onChange(newSize, position);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
|
||||
return Container(
|
||||
key: widgetKey,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
94
lib/pangea/widgets/chat/chat_floating_action_button.dart
Normal file
94
lib/pangea/widgets/chat/chat_floating_action_button.dart
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fluffychat/pages/chat/chat.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/models/space_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChatFloatingActionButton extends StatefulWidget {
|
||||
final ChatController controller;
|
||||
const ChatFloatingActionButton({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
ChatFloatingActionButtonState createState() =>
|
||||
ChatFloatingActionButtonState();
|
||||
}
|
||||
|
||||
class ChatFloatingActionButtonState extends State<ChatFloatingActionButton> {
|
||||
bool showPermissionsError = false;
|
||||
StreamSubscription? _choreoSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final permissionsController =
|
||||
widget.controller.pangeaController.permissionsController;
|
||||
final itEnabled = permissionsController.isToolEnabled(
|
||||
ToolSetting.interactiveTranslator,
|
||||
widget.controller.room,
|
||||
);
|
||||
final igcEnabled = permissionsController.isToolEnabled(
|
||||
ToolSetting.interactiveGrammar,
|
||||
widget.controller.room,
|
||||
);
|
||||
showPermissionsError = !itEnabled || !igcEnabled;
|
||||
debugPrint("showPermissionsError: $showPermissionsError");
|
||||
|
||||
if (showPermissionsError) {
|
||||
Future.delayed(
|
||||
const Duration(seconds: 5),
|
||||
() {
|
||||
if (mounted) setState(() => showPermissionsError = false);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Rebuild the widget each time there's an update from choreo (i.e., an error).
|
||||
_choreoSub =
|
||||
widget.controller.choreographer.stateListener.stream.listen((_) {
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_choreoSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.controller.selectedEvents.isNotEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (widget.controller.showScrollDownButton) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 56.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: widget.controller.scrollDown,
|
||||
heroTag: null,
|
||||
mini: true,
|
||||
child: const Icon(Icons.arrow_downward_outlined),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.controller.choreographer.errorService.error != null) {
|
||||
return ChoreographerHasErrorButton(
|
||||
widget.controller.pangeaController,
|
||||
widget.controller.choreographer.errorService.error!,
|
||||
);
|
||||
}
|
||||
|
||||
return showPermissionsError
|
||||
? LanguagePermissionsButtons(
|
||||
choreographer: widget.controller.choreographer,
|
||||
roomID: widget.controller.roomId,
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
82
lib/pangea/widgets/chat/input_bar_wrapper.dart
Normal file
82
lib/pangea/widgets/chat/input_bar_wrapper.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:fluffychat/pages/chat/input_bar.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/pangea_text_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class InputBarWrapper extends StatefulWidget {
|
||||
final Room room;
|
||||
final int? minLines;
|
||||
final int? maxLines;
|
||||
final TextInputType? keyboardType;
|
||||
final TextInputAction? textInputAction;
|
||||
final ValueChanged<String>? onSubmitted;
|
||||
final ValueChanged<Uint8List?>? onSubmitImage;
|
||||
final FocusNode? focusNode;
|
||||
final PangeaTextController? controller;
|
||||
final InputDecoration? decoration;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final bool? autofocus;
|
||||
final bool readOnly;
|
||||
|
||||
const InputBarWrapper({
|
||||
required this.room,
|
||||
this.minLines,
|
||||
this.maxLines,
|
||||
this.keyboardType,
|
||||
this.onSubmitted,
|
||||
this.onSubmitImage,
|
||||
this.focusNode,
|
||||
this.controller,
|
||||
this.decoration,
|
||||
this.onChanged,
|
||||
this.autofocus,
|
||||
this.textInputAction,
|
||||
this.readOnly = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<InputBarWrapper> createState() => InputBarWrapperState();
|
||||
}
|
||||
|
||||
class InputBarWrapperState extends State<InputBarWrapper> {
|
||||
StreamSubscription? _choreoSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// Rebuild the widget each time there's an update from choreo
|
||||
_choreoSub =
|
||||
widget.controller?.choreographer.stateListener.stream.listen((_) {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_choreoSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InputBar(
|
||||
room: widget.room,
|
||||
minLines: widget.minLines,
|
||||
maxLines: widget.maxLines,
|
||||
keyboardType: widget.keyboardType,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
onSubmitImage: widget.onSubmitImage,
|
||||
focusNode: widget.focusNode,
|
||||
controller: widget.controller,
|
||||
decoration: widget.decoration,
|
||||
onChanged: widget.onChanged,
|
||||
autofocus: widget.autofocus,
|
||||
textInputAction: widget.textInputAction,
|
||||
readOnly: widget.readOnly,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/text_to_speech_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PangeaMessageActions extends StatelessWidget {
|
||||
final ChatController chatController;
|
||||
|
||||
const PangeaMessageActions({super.key, required this.chatController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return chatController.selectedEvents.length == 1
|
||||
? Row(
|
||||
children: <Widget>[
|
||||
// LanguageToggleSwitch(controller: chatController),
|
||||
TextToSpeechButton(
|
||||
controller: chatController,
|
||||
selectedEvent: chatController.selectedEvents.first,
|
||||
),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.mic),
|
||||
// onPressed: chatController.onMicTap,
|
||||
// ),
|
||||
// Add more IconButton widgets here
|
||||
],
|
||||
)
|
||||
: const SizedBox();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/audio_player.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class TextToSpeechButton extends StatefulWidget {
|
||||
final ChatController controller;
|
||||
final Event selectedEvent;
|
||||
|
||||
const TextToSpeechButton({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.selectedEvent,
|
||||
});
|
||||
|
||||
@override
|
||||
_TextToSpeechButtonState createState() => _TextToSpeechButtonState();
|
||||
}
|
||||
|
||||
class _TextToSpeechButtonState extends State<TextToSpeechButton> {
|
||||
final AudioPlayer _audioPlayer = AudioPlayer();
|
||||
late PangeaMessageEvent _pangeaMessageEvent;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_audioPlayer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pangeaMessageEvent = PangeaMessageEvent(
|
||||
event: widget.selectedEvent,
|
||||
timeline: widget.controller.timeline!,
|
||||
ownMessage:
|
||||
widget.selectedEvent.senderId == Matrix.of(context).client.userID,
|
||||
);
|
||||
}
|
||||
|
||||
Event? get localAudioEvent =>
|
||||
langCode != null && text != null && text!.isNotEmpty
|
||||
? _pangeaMessageEvent.getTextToSpeechLocal(langCode!, text!)
|
||||
: null;
|
||||
|
||||
String? get langCode =>
|
||||
widget.controller.choreographer.messageOptions.selectedDisplayLang
|
||||
?.langCode ??
|
||||
widget.controller.choreographer.l2LangCode;
|
||||
|
||||
String? get text => langCode != null
|
||||
? _pangeaMessageEvent.representationByLanguage(langCode!)?.text
|
||||
: null;
|
||||
|
||||
Future<void> _getAudio() async {
|
||||
try {
|
||||
if (!mounted) return;
|
||||
if (text == null || text!.isEmpty) return;
|
||||
if (langCode == null || langCode!.isEmpty) return;
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
await _pangeaMessageEvent.getTextToSpeechGlobal(langCode!);
|
||||
setState(() => _isLoading = false);
|
||||
} catch (e) {
|
||||
setState(() => _isLoading = false);
|
||||
debugger(when: kDebugMode);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.errorGettingAudio),
|
||||
),
|
||||
);
|
||||
ErrorHandler.logError(
|
||||
e: Exception(),
|
||||
s: StackTrace.current,
|
||||
m: 'text is null or empty in text_to_speech_button.dart',
|
||||
data: {'selectedEvent': widget.selectedEvent, 'langCode': langCode},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
final playButton = InkWell(
|
||||
borderRadius: BorderRadius.circular(64),
|
||||
onTap: text == null || text!.isEmpty ? null : _getAudio,
|
||||
child: Material(
|
||||
color: AppConfig.primaryColor.withAlpha(64),
|
||||
borderRadius: BorderRadius.circular(64),
|
||||
child: const Icon(
|
||||
// Change the icon based on some condition. If you have an audio player state, use it here.
|
||||
Icons.play_arrow_outlined,
|
||||
color: AppConfig.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return localAudioEvent == null
|
||||
? Opacity(
|
||||
opacity: text == null || text!.isEmpty ? 0.5 : 1,
|
||||
child: SizedBox(
|
||||
width: 44, // Match the size of the button in AudioPlayerState
|
||||
height: 36,
|
||||
child: Padding(
|
||||
//only left side of the button is padded to match the padding of the AudioPlayerState
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: playButton,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 250,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
AudioPlayerWidget(
|
||||
localAudioEvent!,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue