fix message input quoted message
This commit is contained in:
@@ -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');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user