fix message input quoted message

This commit is contained in:
Salvatore Giordano
2021-12-15 10:51:28 +01:00
parent ce20c247d7
commit ec28d00e81
5 changed files with 81 additions and 55 deletions
@@ -8,11 +8,11 @@ import 'package:uuid/uuid.dart';
part 'message.g.dart';
class _PinExpires {
const _PinExpires();
class _NullConst {
const _NullConst();
}
const _pinExpires = _PinExpires();
const _nullConst = _NullConst();
/// Enum defining the status of a sending message.
enum MessageSendingStatus {
@@ -273,8 +273,8 @@ class Message extends Equatable {
List<Reaction>? latestReactions,
List<Reaction>? ownReactions,
String? parentId,
Message? quotedMessage,
String? quotedMessageId,
Object? quotedMessage = _nullConst,
Object? quotedMessageId = _nullConst,
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
@@ -285,7 +285,7 @@ class Message extends Equatable {
User? user,
bool? pinned,
DateTime? pinnedAt,
Object? pinExpires = _pinExpires,
Object? pinExpires = _nullConst,
User? pinnedBy,
Map<String, Object?>? extraData,
MessageSendingStatus? status,
@@ -294,11 +294,32 @@ class Message extends Equatable {
assert(() {
if (pinExpires is! DateTime &&
pinExpires != null &&
pinExpires is! _PinExpires) {
pinExpires is! _NullConst) {
throw ArgumentError('`pinExpires` can only be set as DateTime or null');
}
return true;
}(), 'Validate type for pinExpires');
assert(() {
if (quotedMessage is! Message &&
quotedMessage != null &&
quotedMessage is! _NullConst) {
throw ArgumentError(
'`quotedMessage` can only be set as Message or null');
}
return true;
}(), 'Validate type for quotedMessage');
assert(() {
if (quotedMessageId is! String &&
quotedMessageId != null &&
quotedMessageId is! _NullConst) {
throw ArgumentError(
'`quotedMessage` can only be set as String or null');
}
return true;
}(), 'Validate type for quotedMessage');
return Message(
id: id ?? this.id,
text: text ?? this.text,
@@ -312,8 +333,12 @@ class Message extends Equatable {
latestReactions: latestReactions ?? this.latestReactions,
ownReactions: ownReactions ?? this.ownReactions,
parentId: parentId ?? this.parentId,
quotedMessage: quotedMessage ?? this.quotedMessage,
quotedMessageId: quotedMessageId ?? _quotedMessageId,
quotedMessage: quotedMessage == _nullConst
? this.quotedMessage
: quotedMessage as Message?,
quotedMessageId: quotedMessageId == _nullConst
? _quotedMessageId
: quotedMessageId as String?,
replyCount: replyCount ?? this.replyCount,
threadParticipants: threadParticipants ?? this.threadParticipants,
showInChannel: showInChannel ?? this.showInChannel,
@@ -325,7 +350,7 @@ class Message extends Equatable {
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
pinExpires == _nullConst ? this.pinExpires : pinExpires as DateTime?,
pinnedBy: pinnedBy ?? this.pinnedBy,
extraData: extraData ?? this.extraData,
status: status ?? this.status,
@@ -190,8 +190,6 @@ class MessageInput extends StatefulWidget {
this.actionsLocation = ActionsLocation.left,
this.attachmentThumbnailBuilders,
this.focusNode,
this.quotedMessage,
this.onQuotedMessageCleared,
this.sendButtonLocation = SendButtonLocation.outside,
this.autofocus = false,
this.hideSendAsDm = false,
@@ -270,12 +268,6 @@ class MessageInput extends StatefulWidget {
/// The focus node associated to the TextField.
final FocusNode? focusNode;
/// The message that is being quoted.
final Message? quotedMessage;
/// Callback invoked when the quoted message is cleared.
final VoidCallback? onQuotedMessageCleared;
/// The location of the send button
final SendButtonLocation sendButtonLocation;
@@ -385,12 +377,19 @@ class MessageInputState extends State<MessageInput>
void _createLocalController([Message? message]) {
assert(_controller == null, '');
_controller = RestorableMessageInputController(message: message);
_registerController();
}
void _registerController() {
assert(_controller != null, '');
registerForRestoration(_controller!, 'messageInputController');
registerForRestoration(
_controller!,
widget.restorationId ?? 'messageInputController',
);
_effectiveController.textEditingController
.removeListener(_onChangedDebounced);
_effectiveController.textEditingController.addListener(_onChangedDebounced);
if (!_isEditing && _timeOut <= 0) _startSlowMode();
}
@override
@@ -398,8 +397,13 @@ class MessageInputState extends State<MessageInput>
super.initState();
if (widget.messageInputController == null) {
_createLocalController();
} else {
_effectiveController.textEditingController
.removeListener(_onChangedDebounced);
_effectiveController.textEditingController
.addListener(_onChangedDebounced);
if (!_isEditing && _timeOut <= 0) _startSlowMode();
}
_effectiveController.textEditingController.addListener(_onChangedDebounced);
_focusNode.addListener(_focusNodeListener);
}
@@ -506,7 +510,10 @@ class MessageInputState extends State<MessageInput>
IconButton(
visualDensity: VisualDensity.compact,
icon: StreamSvgIcon.closeSmall(),
onPressed: widget.onQuotedMessageCleared,
onPressed: () {
_effectiveController.clearQuotedMessage();
_focusNode.unfocus();
},
),
],
),
@@ -646,9 +653,6 @@ class MessageInputState extends State<MessageInput>
return widget.sendButtonBuilder!(context, _effectiveController);
}
print(
'widget.validator(_effectiveController.message): ${widget.validator(_effectiveController.message)}');
return StreamMessageSendButton(
onSendMessage: sendMessage,
timeOut: _timeOut,
@@ -714,7 +718,6 @@ class MessageInputState extends State<MessageInput>
}
Expanded _buildTextInput(BuildContext context) {
print('build text input');
final margin = (widget.sendButtonLocation == SendButtonLocation.inside
? const EdgeInsets.only(right: 8)
: EdgeInsets.zero) +
@@ -873,7 +876,6 @@ class MessageInputState extends State<MessageInput>
late final _onChangedDebounced = debounce(
() {
print('onchangeddebounce');
var value = _effectiveController.text;
if (!mounted) return;
value = value.trim();
@@ -893,7 +895,6 @@ class MessageInputState extends State<MessageInput>
setState(() {
_actionsShrunk = value.isNotEmpty && actionsLength > 1;
});
print('CHECK COMMANDS 00');
_checkCommands(value, context);
_checkMentions(value, context);
@@ -965,7 +966,6 @@ class MessageInputState extends State<MessageInput>
void _checkCommands(String s, BuildContext context) {
if (s.startsWith('/')) {
print('CHECK COMMANDS');
final allCommands = StreamChannel.of(context).channel.config?.commands;
final command =
allCommands?.firstWhereOrNull((it) => it.name == s.substring(1));
@@ -1121,12 +1121,12 @@ class MessageInputState extends State<MessageInput>
Widget _buildReplyToMessage() {
if (!_hasQuotedMessage) return const Offstage();
final containsUrl = widget.quotedMessage!.attachments
final containsUrl = _effectiveController.value.quotedMessage!.attachments
.any((element) => element.titleLink != null);
return QuotedMessageWidget(
reverse: true,
showBorder: !containsUrl,
message: widget.quotedMessage!,
message: _effectiveController.value.quotedMessage!,
messageTheme: _streamChatTheme.otherMessageTheme,
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
);
@@ -1577,7 +1577,6 @@ class MessageInputState extends State<MessageInput>
shouldKeepFocus ??= !_commandEnabled;
_effectiveController.reset();
widget.onQuotedMessageCleared?.call();
if (widget.preMessageSending != null) {
message = await widget.preMessageSending!(message);
@@ -1699,7 +1698,6 @@ class MessageInputState extends State<MessageInput>
void didChangeDependencies() {
_streamChatTheme = StreamChatTheme.of(context);
_messageInputTheme = MessageInputTheme.of(context);
if (!_isEditing && _timeOut <= 0) _startSlowMode();
super.didChangeDependencies();
}
@@ -697,7 +697,7 @@ class _StreamMessageTextFieldState extends State<StreamMessageTextField>
void _registerController() {
assert(_controller != null, '');
registerForRestoration(_controller!, 'controller');
registerForRestoration(_controller!, restorationId ?? 'controller');
}
@override
@@ -40,7 +40,10 @@ class MessageInputController extends ValueNotifier<Message> {
MessageInputController._({
required Message initialMessage,
}) : _textEditingController =
TextEditingController(text: initialMessage.text),
TextEditingController.fromValue(TextEditingValue(
text: initialMessage.text ?? '',
composing: TextRange.collapsed(initialMessage.text?.length ?? 0),
)),
_initialMessage = initialMessage,
super(initialMessage) {
addListener(_textEditingSyncer);
@@ -83,6 +86,22 @@ class MessageInputController extends ValueNotifier<Message> {
value = message;
}
/// Sets the message that's being quoted.
set quotedMessage(Message message) {
value = value.copyWith(
quotedMessage: message,
quotedMessageId: message.id,
);
}
/// Clears the quoted message.
void clearQuotedMessage() {
value = value.copyWith(
quotedMessageId: null,
quotedMessage: null,
);
}
/// Sets a command for the message.
set command(Command command) {
value = value.copyWith(
@@ -3,28 +3,12 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
void main() {
testWidgets(
'should instantiate a new MessageInputController with default validator'
' and empty message',
'should instantiate a new MessageInputController with empty message',
(tester) async {
final controller = MessageInputController();
final controller = MessageInputController()..text = 'test';
expect(controller.isValid, false);
controller.text = 'test';
expect(controller.isValid, true);
},
);
testWidgets(
'should instantiate a new MessageInputController with default validator'
' and specified message',
(tester) async {
final message = Message(text: 'test');
final controller = MessageInputController(
message: message,
);
expect(controller.message, message);
expect(controller.isValid, true);
expect(controller.text, 'test');
expect(controller.message.text, 'test');
},
);
}