From 8b6c732844c31ffa09e01ea91eeb4e02422fd607 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 21 Dec 2021 14:11:23 +0530 Subject: [PATCH] feat(ui, core): minor fixes and improvements Signed-off-by: xsahil03x --- .../lib/src/message_input/message_input.dart | 112 ++++++++++-------- .../lib/src/message_input_controller.dart | 9 +- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/message_input.dart index 7e02e4b3..be63b10b 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/message_input.dart @@ -895,7 +895,7 @@ class MessageInputState extends State _actionsShrunk = value.isNotEmpty && actionsLength > 1; }); - _checkContainsUrlDebounced.call([value, context]); + _checkContainsUrl(value, context); _checkCommands(value, context); _checkMentions(value, context); _checkEmoji(value, context); @@ -920,52 +920,65 @@ class MessageInputState extends State String? _lastSearchedContainsUrlText; CancelableOperation? _enrichUrlOperation; - - late final _checkContainsUrlDebounced = debounce( - (String value, BuildContext context) async { - // Cancel the previous operation if it's still running - _enrichUrlOperation?.cancel(); - - // If the text is same as the last time, don't do anything - if (_lastSearchedContainsUrlText == value) return; - _lastSearchedContainsUrlText = value; - - final matchedUrls = - RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+') - .allMatches(value); - - // Reset the og attachment if the text doesn't contain any url - if (matchedUrls.isEmpty) { - _effectiveController.clearOGAttachment(); - return; - } - - final firstMatchedUrl = matchedUrls.first.group(0)!; - - // If the parsed url matches the ogAttachment url, don't do anything - if (_effectiveController.ogAttachment?.titleLink == firstMatchedUrl) { - return; - } - - final client = StreamChat.of(context).client; - - _enrichUrlOperation = CancelableOperation.fromFuture( - client.enrichUrl(firstMatchedUrl), - ).then( - (ogAttachment) { - final attachment = Attachment.fromOGAttachment(ogAttachment); - _effectiveController.setOGAttachment(attachment); - }, - onError: (error, stackTrace) { - // Reset the ogAttachment if there was an error - _effectiveController.clearOGAttachment(); - widget.onError?.call(error, stackTrace); - }, - ); - }, - const Duration(milliseconds: 650), + final _urlRegex = RegExp( + r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+', ); + void _checkContainsUrl(String value, BuildContext context) async { + // Cancel the previous operation if it's still running + _enrichUrlOperation?.cancel(); + + // If the text is same as the last time, don't do anything + if (_lastSearchedContainsUrlText == value) return; + _lastSearchedContainsUrlText = value; + + final matchedUrls = _urlRegex.allMatches(value); + + // Reset the og attachment if the text doesn't contain any url + if (matchedUrls.isEmpty) { + _effectiveController.clearOGAttachment(); + return; + } + + final firstMatchedUrl = matchedUrls.first.group(0)!; + + // If the parsed url matches the ogAttachment url, don't do anything + if (_effectiveController.ogAttachment?.titleLink == firstMatchedUrl) { + return; + } + + final client = StreamChat.of(context).client; + + _enrichUrlOperation = CancelableOperation.fromFuture( + _enrichUrl(firstMatchedUrl, client), + ).then( + (ogAttachment) { + final attachment = Attachment.fromOGAttachment(ogAttachment); + _effectiveController.setOGAttachment(attachment); + }, + onError: (error, stackTrace) { + // Reset the ogAttachment if there was an error + _effectiveController.clearOGAttachment(); + widget.onError?.call(error, stackTrace); + }, + ); + } + + final _ogAttachmentCache = {}; + + Future _enrichUrl( + String url, + StreamChatClient client, + ) async { + var response = _ogAttachmentCache[url]; + if (response == null) { + final client = StreamChat.of(context).client; + response = await client.enrichUrl(url); + _ogAttachmentCache[url] = response; + } + return response; + } + void _checkEmoji(String value, BuildContext context) { if (value.isNotEmpty && _effectiveController.baseOffset > 0 && @@ -1169,11 +1182,14 @@ class MessageInputState extends State } Widget _buildAttachments() { - if (_effectiveController.attachments.isEmpty) return const Offstage(); - final fileAttachments = _effectiveController.attachments + final nonOGAttachments = _effectiveController.attachments.where( + (it) => it.titleLink == null, + ); + if (nonOGAttachments.isEmpty) return const Offstage(); + final fileAttachments = nonOGAttachments .where((it) => it.type == 'file') .toList(growable: false); - final remainingAttachments = _effectiveController.attachments + final remainingAttachments = nonOGAttachments .where((it) => it.type != 'file') .toList(growable: false); return Column( diff --git a/packages/stream_chat_flutter_core/lib/src/message_input_controller.dart b/packages/stream_chat_flutter_core/lib/src/message_input_controller.dart index f218b9ca..6af8db6e 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_input_controller.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_input_controller.dart @@ -66,10 +66,7 @@ class MessageInputController extends ValueNotifier { void _textEditingSyncer() { final cleanText = value.command == null ? value.text - : value.text?.replaceFirst( - '/${value.command} ', - '', - ); + : value.text?.replaceFirst('/${value.command} ', ''); if (cleanText != _textEditingController.text) { final previousOffset = _textEditingController.value.selection.start; @@ -207,7 +204,9 @@ class MessageInputController extends ValueNotifier { /// Removes the og attachment. void clearOGAttachment() { - attachments = [...attachments]..remove(_ogAttachment); + if (_ogAttachment != null) { + removeAttachment(_ogAttachment!); + } _ogAttachment = null; }