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