moxxy/lib/ui/widgets/messaging_textfield/messaging_textfield.dart

286 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/shared/helpers.dart';
import 'package:moxxyv2/shared/models/conversation.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/controller/conversation_controller.dart';
import 'package:moxxyv2/ui/pages/conversation/keyboard_dodging.dart';
import 'package:moxxyv2/ui/state/account.dart';
import 'package:moxxyv2/ui/theme.dart';
import 'package:moxxyv2/ui/widgets/chat/message.dart';
import 'package:moxxyv2/ui/widgets/messaging_textfield/constants.dart';
import 'package:moxxyv2/ui/widgets/messaging_textfield/record_icon.dart';
import 'package:moxxyv2/ui/widgets/messaging_textfield/send_button.dart';
import 'package:moxxyv2/ui/widgets/messaging_textfield/slider.dart';
import 'package:phosphor_flutter/phosphor_flutter.dart';
class EmojiStickerPickerIcon extends StatelessWidget {
const EmojiStickerPickerIcon({
required this.keyboardController,
required this.tabController,
required this.textFieldFocusNode,
super.key,
});
final KeyboardReplacerController keyboardController;
final TabController tabController;
final FocusNode textFieldFocusNode;
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Material(
color: Colors.transparent,
child: Padding(
padding: iconPadding,
child: StreamBuilder<KeyboardReplacerData>(
stream: keyboardController.stream,
initialData: keyboardController.currentData,
builder: (context, snapshot) {
return InkWell(
onTap: () {
keyboardController.toggleWidget(context, textFieldFocusNode);
},
child: Icon(
snapshot.data!.showWidget
? Icons.keyboard
: (tabController.index == 0
? Icons.emoji_emotions
: PhosphorIconsRegular.sticker),
size: iconSize,
color: primaryColor,
),
);
},
),
),
),
);
}
}
typedef ContentInsertionCallback = void Function(KeyboardInsertedContent);
class MobileMessagingTextField extends StatefulWidget {
const MobileMessagingTextField({
required this.conversationController,
required this.textFieldFocusNode,
required this.keyboardController,
required this.tabController,
required this.speedDialValueNotifier,
required this.isEncrypted,
required this.insertionCallback,
super.key,
});
final FocusNode textFieldFocusNode;
final BidirectionalConversationController conversationController;
final KeyboardReplacerController keyboardController;
final TabController tabController;
final ValueNotifier<bool> speedDialValueNotifier;
final bool isEncrypted;
/// Callback for when the system tells us that the soft keyboard inserted some
/// data into the [TextField].
final ContentInsertionCallback insertionCallback;
@override
MobileMessagingTextFieldState createState() =>
MobileMessagingTextFieldState();
}
class MobileMessagingTextFieldState extends State<MobileMessagingTextField>
with TickerProviderStateMixin {
late AnimationController _backgroundSliderAnimationController;
late Animation<int> _backgroundSliderAnimation;
@override
void initState() {
super.initState();
// Set up animations.
_backgroundSliderAnimationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_backgroundSliderAnimation =
IntTween(begin: 0).animate(_backgroundSliderAnimationController);
// Set up the controller.
widget.conversationController.messagingController.isRecordingNotifier
.addListener(_handleRecordingChange);
widget.conversationController.messagingController
.register(_backgroundSliderAnimationController);
}
@override
void dispose() {
_backgroundSliderAnimationController.dispose();
widget.conversationController.messagingController.isRecordingNotifier
.removeListener(_handleRecordingChange);
widget.conversationController.messagingController.dispose();
super.dispose();
}
void _handleRecordingChange() {
setState(() {
_backgroundSliderAnimation = IntTween(
begin: MediaQuery.of(context).size.width.toInt(),
end: 0,
).animate(_backgroundSliderAnimationController);
});
}
@override
Widget build(BuildContext context) {
return ColoredBox(
color: Colors.black26,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Padding(
padding: bottomBarPadding,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Stack(
children: [
ColoredBox(
color: Theme.of(context)
.extension<MoxxyThemeData>()!
.conversationTextFieldColor,
child: Padding(
padding: textFieldInnerPadding,
child: Column(
children: [
StreamBuilder<TextFieldData>(
stream: widget
.conversationController.textFieldDataStream,
initialData: const TextFieldData(
true,
null,
),
builder: (context, snapshot) {
if (snapshot.data!.quotedMessage == null) {
return const SizedBox();
}
return buildQuoteMessageWidget(
snapshot.data!.quotedMessage!,
isSent(
snapshot.data!.quotedMessage!,
GetIt.I
.get<AccountCubit>()
.state
.account
.jid,
),
widget.conversationController
.conversationType ==
ConversationType.groupchat,
textfieldQuotedMessageRadius,
textfieldQuotedMessageRadius,
resetQuote:
widget.conversationController.removeQuote,
);
},
),
StreamBuilder<TextFieldData>(
stream: widget
.conversationController.textFieldDataStream,
initialData: const TextFieldData(
true,
null,
),
builder: (context, snapshot) {
return Row(
children: [
EmojiStickerPickerIcon(
keyboardController:
widget.keyboardController,
tabController: widget.tabController,
textFieldFocusNode:
widget.textFieldFocusNode,
),
Expanded(
child: TextField(
controller: widget
.conversationController
.textController,
focusNode: widget.textFieldFocusNode,
style: TextStyle(
color: Theme.of(context)
.extension<MoxxyThemeData>()!
.conversationTextFieldTextColor,
),
contentInsertionConfiguration:
ContentInsertionConfiguration(
onContentInserted:
widget.insertionCallback,
),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
hintText:
t.pages.conversation.messageHint,
hintStyle: TextStyle(
color: Theme.of(context)
.extension<MoxxyThemeData>()!
.conversationTextFieldHintTextColor,
),
),
minLines: 1,
maxLines: 5,
),
),
RecordIcon(
widget.conversationController
.messagingController,
visible: snapshot.data!.isBodyEmpty &&
snapshot.data!.quotedMessage == null,
),
],
);
},
),
],
),
),
),
TextFieldSlider(
controller:
widget.conversationController.messagingController,
animation: _backgroundSliderAnimation,
),
],
),
),
),
),
Padding(
padding: sendButtonPadding,
child: SizedBox(
width: sendButtonSize,
height: sendButtonSize,
child: SendButton(
controller: widget.conversationController.messagingController,
conversationController: widget.conversationController,
speedDialValueNotifier: widget.speedDialValueNotifier,
isEncrypted: widget.isEncrypted,
),
),
),
],
),
);
}
}