diff --git a/packages/flutter_chat_core/lib/flutter_chat_core.dart b/packages/flutter_chat_core/lib/flutter_chat_core.dart index ad7050209..73de4043c 100644 --- a/packages/flutter_chat_core/lib/flutter_chat_core.dart +++ b/packages/flutter_chat_core/lib/flutter_chat_core.dart @@ -7,7 +7,7 @@ export 'src/chat_controller/in_memory_chat_controller.dart'; export 'src/chat_controller/scroll_to_message_mixin.dart'; export 'src/chat_controller/upload_progress_mixin.dart'; export 'src/models/builders.dart'; -export 'src/models/link_preview.dart'; +export 'src/models/link_preview_data.dart'; export 'src/models/message.dart'; export 'src/models/message_group_status.dart'; export 'src/models/user.dart'; diff --git a/packages/flutter_chat_core/lib/src/models/builders.dart b/packages/flutter_chat_core/lib/src/models/builders.dart index d111e613f..79f83b80a 100644 --- a/packages/flutter_chat_core/lib/src/models/builders.dart +++ b/packages/flutter_chat_core/lib/src/models/builders.dart @@ -75,6 +75,9 @@ typedef LoadMoreBuilder = Widget Function(BuildContext); /// Signature for building the empty chat list widget. typedef EmptyChatListBuilder = Widget Function(BuildContext); +/// Signature for building the link preview widget. +typedef LinkPreviewBuilder = Widget? Function(BuildContext, TextMessage); + /// A collection of builder functions used to customize the UI components /// of the chat interface. @Freezed(fromJson: false, toJson: false) @@ -128,6 +131,9 @@ abstract class Builders with _$Builders { /// Custom builder for the empty chat list. EmptyChatListBuilder? emptyChatListBuilder, + + /// Custom builder for the link preview widget. + LinkPreviewBuilder? linkPreviewBuilder, }) = _Builders; const Builders._(); diff --git a/packages/flutter_chat_core/lib/src/models/builders.freezed.dart b/packages/flutter_chat_core/lib/src/models/builders.freezed.dart index 1f108667a..78c1c7c36 100644 --- a/packages/flutter_chat_core/lib/src/models/builders.freezed.dart +++ b/packages/flutter_chat_core/lib/src/models/builders.freezed.dart @@ -30,7 +30,8 @@ mixin _$Builders { ChatAnimatedListBuilder? get chatAnimatedListBuilder;/// Custom builder for the "scroll to bottom" button. ScrollToBottomBuilder? get scrollToBottomBuilder;/// Custom builder for the load more indicator. LoadMoreBuilder? get loadMoreBuilder;/// Custom builder for the empty chat list. - EmptyChatListBuilder? get emptyChatListBuilder; + EmptyChatListBuilder? get emptyChatListBuilder;/// Custom builder for the link preview widget. + LinkPreviewBuilder? get linkPreviewBuilder; /// Create a copy of Builders /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -41,16 +42,16 @@ $BuildersCopyWith get copyWith => _$BuildersCopyWithImpl(thi @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is Builders&&(identical(other.textMessageBuilder, textMessageBuilder) || other.textMessageBuilder == textMessageBuilder)&&(identical(other.textStreamMessageBuilder, textStreamMessageBuilder) || other.textStreamMessageBuilder == textStreamMessageBuilder)&&(identical(other.imageMessageBuilder, imageMessageBuilder) || other.imageMessageBuilder == imageMessageBuilder)&&(identical(other.fileMessageBuilder, fileMessageBuilder) || other.fileMessageBuilder == fileMessageBuilder)&&(identical(other.videoMessageBuilder, videoMessageBuilder) || other.videoMessageBuilder == videoMessageBuilder)&&(identical(other.audioMessageBuilder, audioMessageBuilder) || other.audioMessageBuilder == audioMessageBuilder)&&(identical(other.systemMessageBuilder, systemMessageBuilder) || other.systemMessageBuilder == systemMessageBuilder)&&(identical(other.customMessageBuilder, customMessageBuilder) || other.customMessageBuilder == customMessageBuilder)&&(identical(other.unsupportedMessageBuilder, unsupportedMessageBuilder) || other.unsupportedMessageBuilder == unsupportedMessageBuilder)&&(identical(other.composerBuilder, composerBuilder) || other.composerBuilder == composerBuilder)&&(identical(other.chatMessageBuilder, chatMessageBuilder) || other.chatMessageBuilder == chatMessageBuilder)&&(identical(other.chatAnimatedListBuilder, chatAnimatedListBuilder) || other.chatAnimatedListBuilder == chatAnimatedListBuilder)&&(identical(other.scrollToBottomBuilder, scrollToBottomBuilder) || other.scrollToBottomBuilder == scrollToBottomBuilder)&&(identical(other.loadMoreBuilder, loadMoreBuilder) || other.loadMoreBuilder == loadMoreBuilder)&&(identical(other.emptyChatListBuilder, emptyChatListBuilder) || other.emptyChatListBuilder == emptyChatListBuilder)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Builders&&(identical(other.textMessageBuilder, textMessageBuilder) || other.textMessageBuilder == textMessageBuilder)&&(identical(other.textStreamMessageBuilder, textStreamMessageBuilder) || other.textStreamMessageBuilder == textStreamMessageBuilder)&&(identical(other.imageMessageBuilder, imageMessageBuilder) || other.imageMessageBuilder == imageMessageBuilder)&&(identical(other.fileMessageBuilder, fileMessageBuilder) || other.fileMessageBuilder == fileMessageBuilder)&&(identical(other.videoMessageBuilder, videoMessageBuilder) || other.videoMessageBuilder == videoMessageBuilder)&&(identical(other.audioMessageBuilder, audioMessageBuilder) || other.audioMessageBuilder == audioMessageBuilder)&&(identical(other.systemMessageBuilder, systemMessageBuilder) || other.systemMessageBuilder == systemMessageBuilder)&&(identical(other.customMessageBuilder, customMessageBuilder) || other.customMessageBuilder == customMessageBuilder)&&(identical(other.unsupportedMessageBuilder, unsupportedMessageBuilder) || other.unsupportedMessageBuilder == unsupportedMessageBuilder)&&(identical(other.composerBuilder, composerBuilder) || other.composerBuilder == composerBuilder)&&(identical(other.chatMessageBuilder, chatMessageBuilder) || other.chatMessageBuilder == chatMessageBuilder)&&(identical(other.chatAnimatedListBuilder, chatAnimatedListBuilder) || other.chatAnimatedListBuilder == chatAnimatedListBuilder)&&(identical(other.scrollToBottomBuilder, scrollToBottomBuilder) || other.scrollToBottomBuilder == scrollToBottomBuilder)&&(identical(other.loadMoreBuilder, loadMoreBuilder) || other.loadMoreBuilder == loadMoreBuilder)&&(identical(other.emptyChatListBuilder, emptyChatListBuilder) || other.emptyChatListBuilder == emptyChatListBuilder)&&(identical(other.linkPreviewBuilder, linkPreviewBuilder) || other.linkPreviewBuilder == linkPreviewBuilder)); } @override -int get hashCode => Object.hash(runtimeType,textMessageBuilder,textStreamMessageBuilder,imageMessageBuilder,fileMessageBuilder,videoMessageBuilder,audioMessageBuilder,systemMessageBuilder,customMessageBuilder,unsupportedMessageBuilder,composerBuilder,chatMessageBuilder,chatAnimatedListBuilder,scrollToBottomBuilder,loadMoreBuilder,emptyChatListBuilder); +int get hashCode => Object.hash(runtimeType,textMessageBuilder,textStreamMessageBuilder,imageMessageBuilder,fileMessageBuilder,videoMessageBuilder,audioMessageBuilder,systemMessageBuilder,customMessageBuilder,unsupportedMessageBuilder,composerBuilder,chatMessageBuilder,chatAnimatedListBuilder,scrollToBottomBuilder,loadMoreBuilder,emptyChatListBuilder,linkPreviewBuilder); @override String toString() { - return 'Builders(textMessageBuilder: $textMessageBuilder, textStreamMessageBuilder: $textStreamMessageBuilder, imageMessageBuilder: $imageMessageBuilder, fileMessageBuilder: $fileMessageBuilder, videoMessageBuilder: $videoMessageBuilder, audioMessageBuilder: $audioMessageBuilder, systemMessageBuilder: $systemMessageBuilder, customMessageBuilder: $customMessageBuilder, unsupportedMessageBuilder: $unsupportedMessageBuilder, composerBuilder: $composerBuilder, chatMessageBuilder: $chatMessageBuilder, chatAnimatedListBuilder: $chatAnimatedListBuilder, scrollToBottomBuilder: $scrollToBottomBuilder, loadMoreBuilder: $loadMoreBuilder, emptyChatListBuilder: $emptyChatListBuilder)'; + return 'Builders(textMessageBuilder: $textMessageBuilder, textStreamMessageBuilder: $textStreamMessageBuilder, imageMessageBuilder: $imageMessageBuilder, fileMessageBuilder: $fileMessageBuilder, videoMessageBuilder: $videoMessageBuilder, audioMessageBuilder: $audioMessageBuilder, systemMessageBuilder: $systemMessageBuilder, customMessageBuilder: $customMessageBuilder, unsupportedMessageBuilder: $unsupportedMessageBuilder, composerBuilder: $composerBuilder, chatMessageBuilder: $chatMessageBuilder, chatAnimatedListBuilder: $chatAnimatedListBuilder, scrollToBottomBuilder: $scrollToBottomBuilder, loadMoreBuilder: $loadMoreBuilder, emptyChatListBuilder: $emptyChatListBuilder, linkPreviewBuilder: $linkPreviewBuilder)'; } @@ -61,7 +62,7 @@ abstract mixin class $BuildersCopyWith<$Res> { factory $BuildersCopyWith(Builders value, $Res Function(Builders) _then) = _$BuildersCopyWithImpl; @useResult $Res call({ - TextMessageBuilder? textMessageBuilder, TextStreamMessageBuilder? textStreamMessageBuilder, ImageMessageBuilder? imageMessageBuilder, FileMessageBuilder? fileMessageBuilder, VideoMessageBuilder? videoMessageBuilder, AudioMessageBuilder? audioMessageBuilder, SystemMessageBuilder? systemMessageBuilder, CustomMessageBuilder? customMessageBuilder, UnsupportedMessageBuilder? unsupportedMessageBuilder, ComposerBuilder? composerBuilder, ChatMessageBuilder? chatMessageBuilder, ChatAnimatedListBuilder? chatAnimatedListBuilder, ScrollToBottomBuilder? scrollToBottomBuilder, LoadMoreBuilder? loadMoreBuilder, EmptyChatListBuilder? emptyChatListBuilder + TextMessageBuilder? textMessageBuilder, TextStreamMessageBuilder? textStreamMessageBuilder, ImageMessageBuilder? imageMessageBuilder, FileMessageBuilder? fileMessageBuilder, VideoMessageBuilder? videoMessageBuilder, AudioMessageBuilder? audioMessageBuilder, SystemMessageBuilder? systemMessageBuilder, CustomMessageBuilder? customMessageBuilder, UnsupportedMessageBuilder? unsupportedMessageBuilder, ComposerBuilder? composerBuilder, ChatMessageBuilder? chatMessageBuilder, ChatAnimatedListBuilder? chatAnimatedListBuilder, ScrollToBottomBuilder? scrollToBottomBuilder, LoadMoreBuilder? loadMoreBuilder, EmptyChatListBuilder? emptyChatListBuilder, LinkPreviewBuilder? linkPreviewBuilder }); @@ -78,7 +79,7 @@ class _$BuildersCopyWithImpl<$Res> /// Create a copy of Builders /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? textMessageBuilder = freezed,Object? textStreamMessageBuilder = freezed,Object? imageMessageBuilder = freezed,Object? fileMessageBuilder = freezed,Object? videoMessageBuilder = freezed,Object? audioMessageBuilder = freezed,Object? systemMessageBuilder = freezed,Object? customMessageBuilder = freezed,Object? unsupportedMessageBuilder = freezed,Object? composerBuilder = freezed,Object? chatMessageBuilder = freezed,Object? chatAnimatedListBuilder = freezed,Object? scrollToBottomBuilder = freezed,Object? loadMoreBuilder = freezed,Object? emptyChatListBuilder = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? textMessageBuilder = freezed,Object? textStreamMessageBuilder = freezed,Object? imageMessageBuilder = freezed,Object? fileMessageBuilder = freezed,Object? videoMessageBuilder = freezed,Object? audioMessageBuilder = freezed,Object? systemMessageBuilder = freezed,Object? customMessageBuilder = freezed,Object? unsupportedMessageBuilder = freezed,Object? composerBuilder = freezed,Object? chatMessageBuilder = freezed,Object? chatAnimatedListBuilder = freezed,Object? scrollToBottomBuilder = freezed,Object? loadMoreBuilder = freezed,Object? emptyChatListBuilder = freezed,Object? linkPreviewBuilder = freezed,}) { return _then(_self.copyWith( textMessageBuilder: freezed == textMessageBuilder ? _self.textMessageBuilder : textMessageBuilder // ignore: cast_nullable_to_non_nullable as TextMessageBuilder?,textStreamMessageBuilder: freezed == textStreamMessageBuilder ? _self.textStreamMessageBuilder : textStreamMessageBuilder // ignore: cast_nullable_to_non_nullable @@ -95,7 +96,8 @@ as ChatMessageBuilder?,chatAnimatedListBuilder: freezed == chatAnimatedListBuild as ChatAnimatedListBuilder?,scrollToBottomBuilder: freezed == scrollToBottomBuilder ? _self.scrollToBottomBuilder : scrollToBottomBuilder // ignore: cast_nullable_to_non_nullable as ScrollToBottomBuilder?,loadMoreBuilder: freezed == loadMoreBuilder ? _self.loadMoreBuilder : loadMoreBuilder // ignore: cast_nullable_to_non_nullable as LoadMoreBuilder?,emptyChatListBuilder: freezed == emptyChatListBuilder ? _self.emptyChatListBuilder : emptyChatListBuilder // ignore: cast_nullable_to_non_nullable -as EmptyChatListBuilder?, +as EmptyChatListBuilder?,linkPreviewBuilder: freezed == linkPreviewBuilder ? _self.linkPreviewBuilder : linkPreviewBuilder // ignore: cast_nullable_to_non_nullable +as LinkPreviewBuilder?, )); } @@ -106,7 +108,7 @@ as EmptyChatListBuilder?, class _Builders extends Builders { - const _Builders({this.textMessageBuilder, this.textStreamMessageBuilder, this.imageMessageBuilder, this.fileMessageBuilder, this.videoMessageBuilder, this.audioMessageBuilder, this.systemMessageBuilder, this.customMessageBuilder, this.unsupportedMessageBuilder, this.composerBuilder, this.chatMessageBuilder, this.chatAnimatedListBuilder, this.scrollToBottomBuilder, this.loadMoreBuilder, this.emptyChatListBuilder}): super._(); + const _Builders({this.textMessageBuilder, this.textStreamMessageBuilder, this.imageMessageBuilder, this.fileMessageBuilder, this.videoMessageBuilder, this.audioMessageBuilder, this.systemMessageBuilder, this.customMessageBuilder, this.unsupportedMessageBuilder, this.composerBuilder, this.chatMessageBuilder, this.chatAnimatedListBuilder, this.scrollToBottomBuilder, this.loadMoreBuilder, this.emptyChatListBuilder, this.linkPreviewBuilder}): super._(); /// Custom builder for text messages. @@ -139,6 +141,8 @@ class _Builders extends Builders { @override final LoadMoreBuilder? loadMoreBuilder; /// Custom builder for the empty chat list. @override final EmptyChatListBuilder? emptyChatListBuilder; +/// Custom builder for the link preview widget. +@override final LinkPreviewBuilder? linkPreviewBuilder; /// Create a copy of Builders /// with the given fields replaced by the non-null parameter values. @@ -150,16 +154,16 @@ _$BuildersCopyWith<_Builders> get copyWith => __$BuildersCopyWithImpl<_Builders> @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _Builders&&(identical(other.textMessageBuilder, textMessageBuilder) || other.textMessageBuilder == textMessageBuilder)&&(identical(other.textStreamMessageBuilder, textStreamMessageBuilder) || other.textStreamMessageBuilder == textStreamMessageBuilder)&&(identical(other.imageMessageBuilder, imageMessageBuilder) || other.imageMessageBuilder == imageMessageBuilder)&&(identical(other.fileMessageBuilder, fileMessageBuilder) || other.fileMessageBuilder == fileMessageBuilder)&&(identical(other.videoMessageBuilder, videoMessageBuilder) || other.videoMessageBuilder == videoMessageBuilder)&&(identical(other.audioMessageBuilder, audioMessageBuilder) || other.audioMessageBuilder == audioMessageBuilder)&&(identical(other.systemMessageBuilder, systemMessageBuilder) || other.systemMessageBuilder == systemMessageBuilder)&&(identical(other.customMessageBuilder, customMessageBuilder) || other.customMessageBuilder == customMessageBuilder)&&(identical(other.unsupportedMessageBuilder, unsupportedMessageBuilder) || other.unsupportedMessageBuilder == unsupportedMessageBuilder)&&(identical(other.composerBuilder, composerBuilder) || other.composerBuilder == composerBuilder)&&(identical(other.chatMessageBuilder, chatMessageBuilder) || other.chatMessageBuilder == chatMessageBuilder)&&(identical(other.chatAnimatedListBuilder, chatAnimatedListBuilder) || other.chatAnimatedListBuilder == chatAnimatedListBuilder)&&(identical(other.scrollToBottomBuilder, scrollToBottomBuilder) || other.scrollToBottomBuilder == scrollToBottomBuilder)&&(identical(other.loadMoreBuilder, loadMoreBuilder) || other.loadMoreBuilder == loadMoreBuilder)&&(identical(other.emptyChatListBuilder, emptyChatListBuilder) || other.emptyChatListBuilder == emptyChatListBuilder)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Builders&&(identical(other.textMessageBuilder, textMessageBuilder) || other.textMessageBuilder == textMessageBuilder)&&(identical(other.textStreamMessageBuilder, textStreamMessageBuilder) || other.textStreamMessageBuilder == textStreamMessageBuilder)&&(identical(other.imageMessageBuilder, imageMessageBuilder) || other.imageMessageBuilder == imageMessageBuilder)&&(identical(other.fileMessageBuilder, fileMessageBuilder) || other.fileMessageBuilder == fileMessageBuilder)&&(identical(other.videoMessageBuilder, videoMessageBuilder) || other.videoMessageBuilder == videoMessageBuilder)&&(identical(other.audioMessageBuilder, audioMessageBuilder) || other.audioMessageBuilder == audioMessageBuilder)&&(identical(other.systemMessageBuilder, systemMessageBuilder) || other.systemMessageBuilder == systemMessageBuilder)&&(identical(other.customMessageBuilder, customMessageBuilder) || other.customMessageBuilder == customMessageBuilder)&&(identical(other.unsupportedMessageBuilder, unsupportedMessageBuilder) || other.unsupportedMessageBuilder == unsupportedMessageBuilder)&&(identical(other.composerBuilder, composerBuilder) || other.composerBuilder == composerBuilder)&&(identical(other.chatMessageBuilder, chatMessageBuilder) || other.chatMessageBuilder == chatMessageBuilder)&&(identical(other.chatAnimatedListBuilder, chatAnimatedListBuilder) || other.chatAnimatedListBuilder == chatAnimatedListBuilder)&&(identical(other.scrollToBottomBuilder, scrollToBottomBuilder) || other.scrollToBottomBuilder == scrollToBottomBuilder)&&(identical(other.loadMoreBuilder, loadMoreBuilder) || other.loadMoreBuilder == loadMoreBuilder)&&(identical(other.emptyChatListBuilder, emptyChatListBuilder) || other.emptyChatListBuilder == emptyChatListBuilder)&&(identical(other.linkPreviewBuilder, linkPreviewBuilder) || other.linkPreviewBuilder == linkPreviewBuilder)); } @override -int get hashCode => Object.hash(runtimeType,textMessageBuilder,textStreamMessageBuilder,imageMessageBuilder,fileMessageBuilder,videoMessageBuilder,audioMessageBuilder,systemMessageBuilder,customMessageBuilder,unsupportedMessageBuilder,composerBuilder,chatMessageBuilder,chatAnimatedListBuilder,scrollToBottomBuilder,loadMoreBuilder,emptyChatListBuilder); +int get hashCode => Object.hash(runtimeType,textMessageBuilder,textStreamMessageBuilder,imageMessageBuilder,fileMessageBuilder,videoMessageBuilder,audioMessageBuilder,systemMessageBuilder,customMessageBuilder,unsupportedMessageBuilder,composerBuilder,chatMessageBuilder,chatAnimatedListBuilder,scrollToBottomBuilder,loadMoreBuilder,emptyChatListBuilder,linkPreviewBuilder); @override String toString() { - return 'Builders(textMessageBuilder: $textMessageBuilder, textStreamMessageBuilder: $textStreamMessageBuilder, imageMessageBuilder: $imageMessageBuilder, fileMessageBuilder: $fileMessageBuilder, videoMessageBuilder: $videoMessageBuilder, audioMessageBuilder: $audioMessageBuilder, systemMessageBuilder: $systemMessageBuilder, customMessageBuilder: $customMessageBuilder, unsupportedMessageBuilder: $unsupportedMessageBuilder, composerBuilder: $composerBuilder, chatMessageBuilder: $chatMessageBuilder, chatAnimatedListBuilder: $chatAnimatedListBuilder, scrollToBottomBuilder: $scrollToBottomBuilder, loadMoreBuilder: $loadMoreBuilder, emptyChatListBuilder: $emptyChatListBuilder)'; + return 'Builders(textMessageBuilder: $textMessageBuilder, textStreamMessageBuilder: $textStreamMessageBuilder, imageMessageBuilder: $imageMessageBuilder, fileMessageBuilder: $fileMessageBuilder, videoMessageBuilder: $videoMessageBuilder, audioMessageBuilder: $audioMessageBuilder, systemMessageBuilder: $systemMessageBuilder, customMessageBuilder: $customMessageBuilder, unsupportedMessageBuilder: $unsupportedMessageBuilder, composerBuilder: $composerBuilder, chatMessageBuilder: $chatMessageBuilder, chatAnimatedListBuilder: $chatAnimatedListBuilder, scrollToBottomBuilder: $scrollToBottomBuilder, loadMoreBuilder: $loadMoreBuilder, emptyChatListBuilder: $emptyChatListBuilder, linkPreviewBuilder: $linkPreviewBuilder)'; } @@ -170,7 +174,7 @@ abstract mixin class _$BuildersCopyWith<$Res> implements $BuildersCopyWith<$Res> factory _$BuildersCopyWith(_Builders value, $Res Function(_Builders) _then) = __$BuildersCopyWithImpl; @override @useResult $Res call({ - TextMessageBuilder? textMessageBuilder, TextStreamMessageBuilder? textStreamMessageBuilder, ImageMessageBuilder? imageMessageBuilder, FileMessageBuilder? fileMessageBuilder, VideoMessageBuilder? videoMessageBuilder, AudioMessageBuilder? audioMessageBuilder, SystemMessageBuilder? systemMessageBuilder, CustomMessageBuilder? customMessageBuilder, UnsupportedMessageBuilder? unsupportedMessageBuilder, ComposerBuilder? composerBuilder, ChatMessageBuilder? chatMessageBuilder, ChatAnimatedListBuilder? chatAnimatedListBuilder, ScrollToBottomBuilder? scrollToBottomBuilder, LoadMoreBuilder? loadMoreBuilder, EmptyChatListBuilder? emptyChatListBuilder + TextMessageBuilder? textMessageBuilder, TextStreamMessageBuilder? textStreamMessageBuilder, ImageMessageBuilder? imageMessageBuilder, FileMessageBuilder? fileMessageBuilder, VideoMessageBuilder? videoMessageBuilder, AudioMessageBuilder? audioMessageBuilder, SystemMessageBuilder? systemMessageBuilder, CustomMessageBuilder? customMessageBuilder, UnsupportedMessageBuilder? unsupportedMessageBuilder, ComposerBuilder? composerBuilder, ChatMessageBuilder? chatMessageBuilder, ChatAnimatedListBuilder? chatAnimatedListBuilder, ScrollToBottomBuilder? scrollToBottomBuilder, LoadMoreBuilder? loadMoreBuilder, EmptyChatListBuilder? emptyChatListBuilder, LinkPreviewBuilder? linkPreviewBuilder }); @@ -187,7 +191,7 @@ class __$BuildersCopyWithImpl<$Res> /// Create a copy of Builders /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? textMessageBuilder = freezed,Object? textStreamMessageBuilder = freezed,Object? imageMessageBuilder = freezed,Object? fileMessageBuilder = freezed,Object? videoMessageBuilder = freezed,Object? audioMessageBuilder = freezed,Object? systemMessageBuilder = freezed,Object? customMessageBuilder = freezed,Object? unsupportedMessageBuilder = freezed,Object? composerBuilder = freezed,Object? chatMessageBuilder = freezed,Object? chatAnimatedListBuilder = freezed,Object? scrollToBottomBuilder = freezed,Object? loadMoreBuilder = freezed,Object? emptyChatListBuilder = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? textMessageBuilder = freezed,Object? textStreamMessageBuilder = freezed,Object? imageMessageBuilder = freezed,Object? fileMessageBuilder = freezed,Object? videoMessageBuilder = freezed,Object? audioMessageBuilder = freezed,Object? systemMessageBuilder = freezed,Object? customMessageBuilder = freezed,Object? unsupportedMessageBuilder = freezed,Object? composerBuilder = freezed,Object? chatMessageBuilder = freezed,Object? chatAnimatedListBuilder = freezed,Object? scrollToBottomBuilder = freezed,Object? loadMoreBuilder = freezed,Object? emptyChatListBuilder = freezed,Object? linkPreviewBuilder = freezed,}) { return _then(_Builders( textMessageBuilder: freezed == textMessageBuilder ? _self.textMessageBuilder : textMessageBuilder // ignore: cast_nullable_to_non_nullable as TextMessageBuilder?,textStreamMessageBuilder: freezed == textStreamMessageBuilder ? _self.textStreamMessageBuilder : textStreamMessageBuilder // ignore: cast_nullable_to_non_nullable @@ -204,7 +208,8 @@ as ChatMessageBuilder?,chatAnimatedListBuilder: freezed == chatAnimatedListBuild as ChatAnimatedListBuilder?,scrollToBottomBuilder: freezed == scrollToBottomBuilder ? _self.scrollToBottomBuilder : scrollToBottomBuilder // ignore: cast_nullable_to_non_nullable as ScrollToBottomBuilder?,loadMoreBuilder: freezed == loadMoreBuilder ? _self.loadMoreBuilder : loadMoreBuilder // ignore: cast_nullable_to_non_nullable as LoadMoreBuilder?,emptyChatListBuilder: freezed == emptyChatListBuilder ? _self.emptyChatListBuilder : emptyChatListBuilder // ignore: cast_nullable_to_non_nullable -as EmptyChatListBuilder?, +as EmptyChatListBuilder?,linkPreviewBuilder: freezed == linkPreviewBuilder ? _self.linkPreviewBuilder : linkPreviewBuilder // ignore: cast_nullable_to_non_nullable +as LinkPreviewBuilder?, )); } diff --git a/packages/flutter_chat_core/lib/src/models/link_preview.dart b/packages/flutter_chat_core/lib/src/models/link_preview.dart deleted file mode 100644 index 9de312e2f..000000000 --- a/packages/flutter_chat_core/lib/src/models/link_preview.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'link_preview.freezed.dart'; -part 'link_preview.g.dart'; - -/// Represents the data extracted for a link preview. -@freezed -abstract class LinkPreview with _$LinkPreview { - /// Creates a [LinkPreview] instance. - const factory LinkPreview({ - /// The original URL link. - required String link, - - /// A description extracted from the link source. - String? description, - - /// The URL of an image associated with the link. - String? imageUrl, - - /// The title extracted from the link source. - String? title, - }) = _LinkPreview; - - const LinkPreview._(); - - /// Creates a [LinkPreview] instance from a JSON map. - factory LinkPreview.fromJson(Map json) => - _$LinkPreviewFromJson(json); -} diff --git a/packages/flutter_chat_core/lib/src/models/link_preview.freezed.dart b/packages/flutter_chat_core/lib/src/models/link_preview.freezed.dart deleted file mode 100644 index 75be51257..000000000 --- a/packages/flutter_chat_core/lib/src/models/link_preview.freezed.dart +++ /dev/null @@ -1,165 +0,0 @@ -// dart format width=80 -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'link_preview.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -// dart format off -T _$identity(T value) => value; - -/// @nodoc -mixin _$LinkPreview { - -/// The original URL link. - String get link;/// A description extracted from the link source. - String? get description;/// The URL of an image associated with the link. - String? get imageUrl;/// The title extracted from the link source. - String? get title; -/// Create a copy of LinkPreview -/// with the given fields replaced by the non-null parameter values. -@JsonKey(includeFromJson: false, includeToJson: false) -@pragma('vm:prefer-inline') -$LinkPreviewCopyWith get copyWith => _$LinkPreviewCopyWithImpl(this as LinkPreview, _$identity); - - /// Serializes this LinkPreview to a JSON map. - Map toJson(); - - -@override -bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkPreview&&(identical(other.link, link) || other.link == link)&&(identical(other.description, description) || other.description == description)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.title, title) || other.title == title)); -} - -@JsonKey(includeFromJson: false, includeToJson: false) -@override -int get hashCode => Object.hash(runtimeType,link,description,imageUrl,title); - -@override -String toString() { - return 'LinkPreview(link: $link, description: $description, imageUrl: $imageUrl, title: $title)'; -} - - -} - -/// @nodoc -abstract mixin class $LinkPreviewCopyWith<$Res> { - factory $LinkPreviewCopyWith(LinkPreview value, $Res Function(LinkPreview) _then) = _$LinkPreviewCopyWithImpl; -@useResult -$Res call({ - String link, String? description, String? imageUrl, String? title -}); - - - - -} -/// @nodoc -class _$LinkPreviewCopyWithImpl<$Res> - implements $LinkPreviewCopyWith<$Res> { - _$LinkPreviewCopyWithImpl(this._self, this._then); - - final LinkPreview _self; - final $Res Function(LinkPreview) _then; - -/// Create a copy of LinkPreview -/// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? link = null,Object? description = freezed,Object? imageUrl = freezed,Object? title = freezed,}) { - return _then(_self.copyWith( -link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable -as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable -as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable -as String?, - )); -} - -} - - -/// @nodoc -@JsonSerializable() - -class _LinkPreview extends LinkPreview { - const _LinkPreview({required this.link, this.description, this.imageUrl, this.title}): super._(); - factory _LinkPreview.fromJson(Map json) => _$LinkPreviewFromJson(json); - -/// The original URL link. -@override final String link; -/// A description extracted from the link source. -@override final String? description; -/// The URL of an image associated with the link. -@override final String? imageUrl; -/// The title extracted from the link source. -@override final String? title; - -/// Create a copy of LinkPreview -/// with the given fields replaced by the non-null parameter values. -@override @JsonKey(includeFromJson: false, includeToJson: false) -@pragma('vm:prefer-inline') -_$LinkPreviewCopyWith<_LinkPreview> get copyWith => __$LinkPreviewCopyWithImpl<_LinkPreview>(this, _$identity); - -@override -Map toJson() { - return _$LinkPreviewToJson(this, ); -} - -@override -bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkPreview&&(identical(other.link, link) || other.link == link)&&(identical(other.description, description) || other.description == description)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.title, title) || other.title == title)); -} - -@JsonKey(includeFromJson: false, includeToJson: false) -@override -int get hashCode => Object.hash(runtimeType,link,description,imageUrl,title); - -@override -String toString() { - return 'LinkPreview(link: $link, description: $description, imageUrl: $imageUrl, title: $title)'; -} - - -} - -/// @nodoc -abstract mixin class _$LinkPreviewCopyWith<$Res> implements $LinkPreviewCopyWith<$Res> { - factory _$LinkPreviewCopyWith(_LinkPreview value, $Res Function(_LinkPreview) _then) = __$LinkPreviewCopyWithImpl; -@override @useResult -$Res call({ - String link, String? description, String? imageUrl, String? title -}); - - - - -} -/// @nodoc -class __$LinkPreviewCopyWithImpl<$Res> - implements _$LinkPreviewCopyWith<$Res> { - __$LinkPreviewCopyWithImpl(this._self, this._then); - - final _LinkPreview _self; - final $Res Function(_LinkPreview) _then; - -/// Create a copy of LinkPreview -/// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? link = null,Object? description = freezed,Object? imageUrl = freezed,Object? title = freezed,}) { - return _then(_LinkPreview( -link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable -as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable -as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable -as String?, - )); -} - - -} - -// dart format on diff --git a/packages/flutter_chat_core/lib/src/models/link_preview.g.dart b/packages/flutter_chat_core/lib/src/models/link_preview.g.dart deleted file mode 100644 index f7fb17d1a..000000000 --- a/packages/flutter_chat_core/lib/src/models/link_preview.g.dart +++ /dev/null @@ -1,22 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'link_preview.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_LinkPreview _$LinkPreviewFromJson(Map json) => _LinkPreview( - link: json['link'] as String, - description: json['description'] as String?, - imageUrl: json['imageUrl'] as String?, - title: json['title'] as String?, -); - -Map _$LinkPreviewToJson(_LinkPreview instance) => - { - 'link': instance.link, - if (instance.description case final value?) 'description': value, - if (instance.imageUrl case final value?) 'imageUrl': value, - if (instance.title case final value?) 'title': value, - }; diff --git a/packages/flutter_chat_core/lib/src/models/link_preview_data.dart b/packages/flutter_chat_core/lib/src/models/link_preview_data.dart new file mode 100644 index 000000000..71a41bbf7 --- /dev/null +++ b/packages/flutter_chat_core/lib/src/models/link_preview_data.dart @@ -0,0 +1,54 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'link_preview_data.freezed.dart'; +part 'link_preview_data.g.dart'; + +/// Represents the data extracted for a link preview. +@freezed +abstract class LinkPreviewData with _$LinkPreviewData { + /// Creates a [LinkPreviewData] instance. + const factory LinkPreviewData({ + /// The original URL link. + required String link, + + /// A description extracted from the link source. + String? description, + + /// The preview data of an image extracted from the link + ImagePreviewData? image, + + /// The title extracted from the link source. + String? title, + + }) = _LinkPreviewData; + + const LinkPreviewData._(); + + /// Creates a [LinkPreviewData] instance from a JSON map. + factory LinkPreviewData.fromJson(Map json) => + _$LinkPreviewDataFromJson(json); +} + +/// Represents the data extracted for a link preview image. +@freezed +abstract class ImagePreviewData with _$ImagePreviewData { + /// Creates a [ImagePreviewData] instance. + const factory ImagePreviewData({ + + /// The URL of an image associated with the link. + required String url, + + /// The image width. + required double width, + + /// The image height. + required double height, + + }) = _ImagePreviewData; + + const ImagePreviewData._(); + + /// Creates a [ImagePreviewData] instance from a JSON map. + factory ImagePreviewData.fromJson(Map json) => + _$ImagePreviewDataFromJson(json); +} \ No newline at end of file diff --git a/packages/flutter_chat_core/lib/src/models/link_preview_data.freezed.dart b/packages/flutter_chat_core/lib/src/models/link_preview_data.freezed.dart new file mode 100644 index 000000000..05e47dadc --- /dev/null +++ b/packages/flutter_chat_core/lib/src/models/link_preview_data.freezed.dart @@ -0,0 +1,334 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'link_preview_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$LinkPreviewData { + +/// The original URL link. + String get link;/// A description extracted from the link source. + String? get description;/// The preview data of an image extracted from the link + ImagePreviewData? get image;/// The title extracted from the link source. + String? get title; +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$LinkPreviewDataCopyWith get copyWith => _$LinkPreviewDataCopyWithImpl(this as LinkPreviewData, _$identity); + + /// Serializes this LinkPreviewData to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkPreviewData&&(identical(other.link, link) || other.link == link)&&(identical(other.description, description) || other.description == description)&&(identical(other.image, image) || other.image == image)&&(identical(other.title, title) || other.title == title)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,link,description,image,title); + +@override +String toString() { + return 'LinkPreviewData(link: $link, description: $description, image: $image, title: $title)'; +} + + +} + +/// @nodoc +abstract mixin class $LinkPreviewDataCopyWith<$Res> { + factory $LinkPreviewDataCopyWith(LinkPreviewData value, $Res Function(LinkPreviewData) _then) = _$LinkPreviewDataCopyWithImpl; +@useResult +$Res call({ + String link, String? description, ImagePreviewData? image, String? title +}); + + +$ImagePreviewDataCopyWith<$Res>? get image; + +} +/// @nodoc +class _$LinkPreviewDataCopyWithImpl<$Res> + implements $LinkPreviewDataCopyWith<$Res> { + _$LinkPreviewDataCopyWithImpl(this._self, this._then); + + final LinkPreviewData _self; + final $Res Function(LinkPreviewData) _then; + +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? link = null,Object? description = freezed,Object? image = freezed,Object? title = freezed,}) { + return _then(_self.copyWith( +link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as ImagePreviewData?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?, + )); +} +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ImagePreviewDataCopyWith<$Res>? get image { + if (_self.image == null) { + return null; + } + + return $ImagePreviewDataCopyWith<$Res>(_self.image!, (value) { + return _then(_self.copyWith(image: value)); + }); +} +} + + +/// @nodoc +@JsonSerializable() + +class _LinkPreviewData extends LinkPreviewData { + const _LinkPreviewData({required this.link, this.description, this.image, this.title}): super._(); + factory _LinkPreviewData.fromJson(Map json) => _$LinkPreviewDataFromJson(json); + +/// The original URL link. +@override final String link; +/// A description extracted from the link source. +@override final String? description; +/// The preview data of an image extracted from the link +@override final ImagePreviewData? image; +/// The title extracted from the link source. +@override final String? title; + +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$LinkPreviewDataCopyWith<_LinkPreviewData> get copyWith => __$LinkPreviewDataCopyWithImpl<_LinkPreviewData>(this, _$identity); + +@override +Map toJson() { + return _$LinkPreviewDataToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkPreviewData&&(identical(other.link, link) || other.link == link)&&(identical(other.description, description) || other.description == description)&&(identical(other.image, image) || other.image == image)&&(identical(other.title, title) || other.title == title)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,link,description,image,title); + +@override +String toString() { + return 'LinkPreviewData(link: $link, description: $description, image: $image, title: $title)'; +} + + +} + +/// @nodoc +abstract mixin class _$LinkPreviewDataCopyWith<$Res> implements $LinkPreviewDataCopyWith<$Res> { + factory _$LinkPreviewDataCopyWith(_LinkPreviewData value, $Res Function(_LinkPreviewData) _then) = __$LinkPreviewDataCopyWithImpl; +@override @useResult +$Res call({ + String link, String? description, ImagePreviewData? image, String? title +}); + + +@override $ImagePreviewDataCopyWith<$Res>? get image; + +} +/// @nodoc +class __$LinkPreviewDataCopyWithImpl<$Res> + implements _$LinkPreviewDataCopyWith<$Res> { + __$LinkPreviewDataCopyWithImpl(this._self, this._then); + + final _LinkPreviewData _self; + final $Res Function(_LinkPreviewData) _then; + +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? link = null,Object? description = freezed,Object? image = freezed,Object? title = freezed,}) { + return _then(_LinkPreviewData( +link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as ImagePreviewData?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +/// Create a copy of LinkPreviewData +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ImagePreviewDataCopyWith<$Res>? get image { + if (_self.image == null) { + return null; + } + + return $ImagePreviewDataCopyWith<$Res>(_self.image!, (value) { + return _then(_self.copyWith(image: value)); + }); +} +} + + +/// @nodoc +mixin _$ImagePreviewData { + +/// The URL of an image associated with the link. + String get url;/// The image width. + double get width;/// The image height. + double get height; +/// Create a copy of ImagePreviewData +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ImagePreviewDataCopyWith get copyWith => _$ImagePreviewDataCopyWithImpl(this as ImagePreviewData, _$identity); + + /// Serializes this ImagePreviewData to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ImagePreviewData&&(identical(other.url, url) || other.url == url)&&(identical(other.width, width) || other.width == width)&&(identical(other.height, height) || other.height == height)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,url,width,height); + +@override +String toString() { + return 'ImagePreviewData(url: $url, width: $width, height: $height)'; +} + + +} + +/// @nodoc +abstract mixin class $ImagePreviewDataCopyWith<$Res> { + factory $ImagePreviewDataCopyWith(ImagePreviewData value, $Res Function(ImagePreviewData) _then) = _$ImagePreviewDataCopyWithImpl; +@useResult +$Res call({ + String url, double width, double height +}); + + + + +} +/// @nodoc +class _$ImagePreviewDataCopyWithImpl<$Res> + implements $ImagePreviewDataCopyWith<$Res> { + _$ImagePreviewDataCopyWithImpl(this._self, this._then); + + final ImagePreviewData _self; + final $Res Function(ImagePreviewData) _then; + +/// Create a copy of ImagePreviewData +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? url = null,Object? width = null,Object? height = null,}) { + return _then(_self.copyWith( +url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable +as String,width: null == width ? _self.width : width // ignore: cast_nullable_to_non_nullable +as double,height: null == height ? _self.height : height // ignore: cast_nullable_to_non_nullable +as double, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _ImagePreviewData extends ImagePreviewData { + const _ImagePreviewData({required this.url, required this.width, required this.height}): super._(); + factory _ImagePreviewData.fromJson(Map json) => _$ImagePreviewDataFromJson(json); + +/// The URL of an image associated with the link. +@override final String url; +/// The image width. +@override final double width; +/// The image height. +@override final double height; + +/// Create a copy of ImagePreviewData +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ImagePreviewDataCopyWith<_ImagePreviewData> get copyWith => __$ImagePreviewDataCopyWithImpl<_ImagePreviewData>(this, _$identity); + +@override +Map toJson() { + return _$ImagePreviewDataToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ImagePreviewData&&(identical(other.url, url) || other.url == url)&&(identical(other.width, width) || other.width == width)&&(identical(other.height, height) || other.height == height)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,url,width,height); + +@override +String toString() { + return 'ImagePreviewData(url: $url, width: $width, height: $height)'; +} + + +} + +/// @nodoc +abstract mixin class _$ImagePreviewDataCopyWith<$Res> implements $ImagePreviewDataCopyWith<$Res> { + factory _$ImagePreviewDataCopyWith(_ImagePreviewData value, $Res Function(_ImagePreviewData) _then) = __$ImagePreviewDataCopyWithImpl; +@override @useResult +$Res call({ + String url, double width, double height +}); + + + + +} +/// @nodoc +class __$ImagePreviewDataCopyWithImpl<$Res> + implements _$ImagePreviewDataCopyWith<$Res> { + __$ImagePreviewDataCopyWithImpl(this._self, this._then); + + final _ImagePreviewData _self; + final $Res Function(_ImagePreviewData) _then; + +/// Create a copy of ImagePreviewData +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? url = null,Object? width = null,Object? height = null,}) { + return _then(_ImagePreviewData( +url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable +as String,width: null == width ? _self.width : width // ignore: cast_nullable_to_non_nullable +as double,height: null == height ? _self.height : height // ignore: cast_nullable_to_non_nullable +as double, + )); +} + + +} + +// dart format on diff --git a/packages/flutter_chat_core/lib/src/models/link_preview_data.g.dart b/packages/flutter_chat_core/lib/src/models/link_preview_data.g.dart new file mode 100644 index 000000000..590f13b1e --- /dev/null +++ b/packages/flutter_chat_core/lib/src/models/link_preview_data.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'link_preview_data.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_LinkPreviewData _$LinkPreviewDataFromJson(Map json) => + _LinkPreviewData( + link: json['link'] as String, + description: json['description'] as String?, + image: + json['image'] == null + ? null + : ImagePreviewData.fromJson( + json['image'] as Map, + ), + title: json['title'] as String?, + ); + +Map _$LinkPreviewDataToJson(_LinkPreviewData instance) => + { + 'link': instance.link, + if (instance.description case final value?) 'description': value, + if (instance.image?.toJson() case final value?) 'image': value, + if (instance.title case final value?) 'title': value, + }; + +_ImagePreviewData _$ImagePreviewDataFromJson(Map json) => + _ImagePreviewData( + url: json['url'] as String, + width: (json['width'] as num).toDouble(), + height: (json['height'] as num).toDouble(), + ); + +Map _$ImagePreviewDataToJson(_ImagePreviewData instance) => + { + 'url': instance.url, + 'width': instance.width, + 'height': instance.height, + }; diff --git a/packages/flutter_chat_core/lib/src/models/message.dart b/packages/flutter_chat_core/lib/src/models/message.dart index 0c1597511..16785a607 100644 --- a/packages/flutter_chat_core/lib/src/models/message.dart +++ b/packages/flutter_chat_core/lib/src/models/message.dart @@ -3,7 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import '../utils/typedefs.dart'; import 'duration_converter.dart'; import 'epoch_date_time_converter.dart'; -import 'link_preview.dart'; +import 'link_preview_data.dart'; part 'message.freezed.dart'; part 'message.g.dart'; @@ -56,7 +56,7 @@ sealed class Message with _$Message { required String text, /// Optional preview data for a link found in the [text]. - LinkPreview? linkPreview, + LinkPreviewData? linkPreviewData, }) = TextMessage; /// Creates a streaming text message placeholder. diff --git a/packages/flutter_chat_core/lib/src/models/message.freezed.dart b/packages/flutter_chat_core/lib/src/models/message.freezed.dart index efd10da2a..251bf456a 100644 --- a/packages/flutter_chat_core/lib/src/models/message.freezed.dart +++ b/packages/flutter_chat_core/lib/src/models/message.freezed.dart @@ -147,7 +147,7 @@ as Map?, @JsonSerializable() class TextMessage extends Message { - const TextMessage({required this.id, required this.authorId, this.replyToMessageId, @EpochDateTimeConverter() this.createdAt, @EpochDateTimeConverter() this.deletedAt, @EpochDateTimeConverter() this.failedAt, @EpochDateTimeConverter() this.sentAt, @EpochDateTimeConverter() this.deliveredAt, @EpochDateTimeConverter() this.seenAt, @EpochDateTimeConverter() this.updatedAt, final Map>? reactions, final Map? metadata, required this.text, this.linkPreview, final String? $type}): _reactions = reactions,_metadata = metadata,$type = $type ?? 'text',super._(); + const TextMessage({required this.id, required this.authorId, this.replyToMessageId, @EpochDateTimeConverter() this.createdAt, @EpochDateTimeConverter() this.deletedAt, @EpochDateTimeConverter() this.failedAt, @EpochDateTimeConverter() this.sentAt, @EpochDateTimeConverter() this.deliveredAt, @EpochDateTimeConverter() this.seenAt, @EpochDateTimeConverter() this.updatedAt, final Map>? reactions, final Map? metadata, required this.text, this.linkPreviewData, final String? $type}): _reactions = reactions,_metadata = metadata,$type = $type ?? 'text',super._(); factory TextMessage.fromJson(Map json) => _$TextMessageFromJson(json); /// Unique identifier for the message. @@ -195,7 +195,7 @@ class TextMessage extends Message { /// The text content of the message. final String text; /// Optional preview data for a link found in the [text]. - final LinkPreview? linkPreview; + final LinkPreviewData? linkPreviewData; @JsonKey(name: 'type') final String $type; @@ -214,16 +214,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is TextMessage&&(identical(other.id, id) || other.id == id)&&(identical(other.authorId, authorId) || other.authorId == authorId)&&(identical(other.replyToMessageId, replyToMessageId) || other.replyToMessageId == replyToMessageId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.failedAt, failedAt) || other.failedAt == failedAt)&&(identical(other.sentAt, sentAt) || other.sentAt == sentAt)&&(identical(other.deliveredAt, deliveredAt) || other.deliveredAt == deliveredAt)&&(identical(other.seenAt, seenAt) || other.seenAt == seenAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._metadata, _metadata)&&(identical(other.text, text) || other.text == text)&&(identical(other.linkPreview, linkPreview) || other.linkPreview == linkPreview)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is TextMessage&&(identical(other.id, id) || other.id == id)&&(identical(other.authorId, authorId) || other.authorId == authorId)&&(identical(other.replyToMessageId, replyToMessageId) || other.replyToMessageId == replyToMessageId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.failedAt, failedAt) || other.failedAt == failedAt)&&(identical(other.sentAt, sentAt) || other.sentAt == sentAt)&&(identical(other.deliveredAt, deliveredAt) || other.deliveredAt == deliveredAt)&&(identical(other.seenAt, seenAt) || other.seenAt == seenAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._metadata, _metadata)&&(identical(other.text, text) || other.text == text)&&(identical(other.linkPreviewData, linkPreviewData) || other.linkPreviewData == linkPreviewData)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,authorId,replyToMessageId,createdAt,deletedAt,failedAt,sentAt,deliveredAt,seenAt,updatedAt,const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_metadata),text,linkPreview); +int get hashCode => Object.hash(runtimeType,id,authorId,replyToMessageId,createdAt,deletedAt,failedAt,sentAt,deliveredAt,seenAt,updatedAt,const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_metadata),text,linkPreviewData); @override String toString() { - return 'Message.text(id: $id, authorId: $authorId, replyToMessageId: $replyToMessageId, createdAt: $createdAt, deletedAt: $deletedAt, failedAt: $failedAt, sentAt: $sentAt, deliveredAt: $deliveredAt, seenAt: $seenAt, updatedAt: $updatedAt, reactions: $reactions, metadata: $metadata, text: $text, linkPreview: $linkPreview)'; + return 'Message.text(id: $id, authorId: $authorId, replyToMessageId: $replyToMessageId, createdAt: $createdAt, deletedAt: $deletedAt, failedAt: $failedAt, sentAt: $sentAt, deliveredAt: $deliveredAt, seenAt: $seenAt, updatedAt: $updatedAt, reactions: $reactions, metadata: $metadata, text: $text, linkPreviewData: $linkPreviewData)'; } @@ -234,11 +234,11 @@ abstract mixin class $TextMessageCopyWith<$Res> implements $MessageCopyWith<$Res factory $TextMessageCopyWith(TextMessage value, $Res Function(TextMessage) _then) = _$TextMessageCopyWithImpl; @override @useResult $Res call({ - MessageID id, UserID authorId, MessageID? replyToMessageId,@EpochDateTimeConverter() DateTime? createdAt,@EpochDateTimeConverter() DateTime? deletedAt,@EpochDateTimeConverter() DateTime? failedAt,@EpochDateTimeConverter() DateTime? sentAt,@EpochDateTimeConverter() DateTime? deliveredAt,@EpochDateTimeConverter() DateTime? seenAt,@EpochDateTimeConverter() DateTime? updatedAt, Map>? reactions, Map? metadata, String text, LinkPreview? linkPreview + MessageID id, UserID authorId, MessageID? replyToMessageId,@EpochDateTimeConverter() DateTime? createdAt,@EpochDateTimeConverter() DateTime? deletedAt,@EpochDateTimeConverter() DateTime? failedAt,@EpochDateTimeConverter() DateTime? sentAt,@EpochDateTimeConverter() DateTime? deliveredAt,@EpochDateTimeConverter() DateTime? seenAt,@EpochDateTimeConverter() DateTime? updatedAt, Map>? reactions, Map? metadata, String text, LinkPreviewData? linkPreviewData }); -$LinkPreviewCopyWith<$Res>? get linkPreview; +$LinkPreviewDataCopyWith<$Res>? get linkPreviewData; } /// @nodoc @@ -251,7 +251,7 @@ class _$TextMessageCopyWithImpl<$Res> /// Create a copy of Message /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? authorId = null,Object? replyToMessageId = freezed,Object? createdAt = freezed,Object? deletedAt = freezed,Object? failedAt = freezed,Object? sentAt = freezed,Object? deliveredAt = freezed,Object? seenAt = freezed,Object? updatedAt = freezed,Object? reactions = freezed,Object? metadata = freezed,Object? text = null,Object? linkPreview = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? authorId = null,Object? replyToMessageId = freezed,Object? createdAt = freezed,Object? deletedAt = freezed,Object? failedAt = freezed,Object? sentAt = freezed,Object? deliveredAt = freezed,Object? seenAt = freezed,Object? updatedAt = freezed,Object? reactions = freezed,Object? metadata = freezed,Object? text = null,Object? linkPreviewData = freezed,}) { return _then(TextMessage( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as MessageID,authorId: null == authorId ? _self.authorId : authorId // ignore: cast_nullable_to_non_nullable @@ -266,8 +266,8 @@ as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ig as DateTime?,reactions: freezed == reactions ? _self._reactions : reactions // ignore: cast_nullable_to_non_nullable as Map>?,metadata: freezed == metadata ? _self._metadata : metadata // ignore: cast_nullable_to_non_nullable as Map?,text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable -as String,linkPreview: freezed == linkPreview ? _self.linkPreview : linkPreview // ignore: cast_nullable_to_non_nullable -as LinkPreview?, +as String,linkPreviewData: freezed == linkPreviewData ? _self.linkPreviewData : linkPreviewData // ignore: cast_nullable_to_non_nullable +as LinkPreviewData?, )); } @@ -275,13 +275,13 @@ as LinkPreview?, /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$LinkPreviewCopyWith<$Res>? get linkPreview { - if (_self.linkPreview == null) { +$LinkPreviewDataCopyWith<$Res>? get linkPreviewData { + if (_self.linkPreviewData == null) { return null; } - return $LinkPreviewCopyWith<$Res>(_self.linkPreview!, (value) { - return _then(_self.copyWith(linkPreview: value)); + return $LinkPreviewDataCopyWith<$Res>(_self.linkPreviewData!, (value) { + return _then(_self.copyWith(linkPreviewData: value)); }); } } diff --git a/packages/flutter_chat_core/lib/src/models/message.g.dart b/packages/flutter_chat_core/lib/src/models/message.g.dart index 8ba964e67..eb4877e00 100644 --- a/packages/flutter_chat_core/lib/src/models/message.g.dart +++ b/packages/flutter_chat_core/lib/src/models/message.g.dart @@ -44,10 +44,12 @@ TextMessage _$TextMessageFromJson(Map json) => TextMessage( ), metadata: json['metadata'] as Map?, text: json['text'] as String, - linkPreview: - json['linkPreview'] == null + linkPreviewData: + json['linkPreviewData'] == null ? null - : LinkPreview.fromJson(json['linkPreview'] as Map), + : LinkPreviewData.fromJson( + json['linkPreviewData'] as Map, + ), $type: json['type'] as String?, ); @@ -102,7 +104,8 @@ Map _$TextMessageToJson( if (instance.reactions case final value?) 'reactions': value, if (instance.metadata case final value?) 'metadata': value, 'text': instance.text, - if (instance.linkPreview?.toJson() case final value?) 'linkPreview': value, + if (instance.linkPreviewData?.toJson() case final value?) + 'linkPreviewData': value, 'type': instance.$type, }; diff --git a/packages/flutter_chat_core/test/models/link_preview_test.dart b/packages/flutter_chat_core/test/models/link_preview_data_test.dart similarity index 63% rename from packages/flutter_chat_core/test/models/link_preview_test.dart rename to packages/flutter_chat_core/test/models/link_preview_data_test.dart index 8642a5428..0070e7f8c 100644 --- a/packages/flutter_chat_core/test/models/link_preview_test.dart +++ b/packages/flutter_chat_core/test/models/link_preview_data_test.dart @@ -2,8 +2,8 @@ import 'package:flutter_chat_core/flutter_chat_core.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - group('LinkPreview', () { - late LinkPreview linkPreview; + group('LinkPreviewData', () { + late LinkPreviewData linkPreviewData; setUp(() { final json = { @@ -13,7 +13,7 @@ void main() { 'title': 'Google', }; - linkPreview = LinkPreview.fromJson(json); + linkPreviewData = LinkPreviewData.fromJson(json); }); test('treats objects with the same properties as equal', () { @@ -24,16 +24,16 @@ void main() { 'title': 'Google', }; - final linkPreview2 = LinkPreview.fromJson(json); + final linkPreviewData2 = LinkPreviewData.fromJson(json); // Two objects with the same properties should be equal. - expect(linkPreview == linkPreview2, true); + expect(linkPreviewData == linkPreviewData2, true); - // Change one property of linkPreview2. - final copiedLinkPreview = linkPreview2.copyWith(title: null); + // Change one property of linkPreviewData2. + final copiedLinkPreview = linkPreviewData2.copyWith(title: null); // The original and the changed objects should not be equal. - expect(linkPreview == copiedLinkPreview, false); + expect(linkPreviewData == copiedLinkPreview, false); }); }); } diff --git a/packages/flutter_chat_core/test/models/message_test.dart b/packages/flutter_chat_core/test/models/message_test.dart index ff305f2d3..abebe2935 100644 --- a/packages/flutter_chat_core/test/models/message_test.dart +++ b/packages/flutter_chat_core/test/models/message_test.dart @@ -63,10 +63,14 @@ void main() { 'type': 'text', 'text': 'Hello, world!', 'metadata': {'key': 'value'}, - 'linkPreview': { + 'linkPreviewData': { 'title': 'Google', 'description': 'Google homepage', - 'imageUrl': 'https://google.com/logo.png', + 'image': { + 'url': 'https://google.com/logo.png', + 'width': 100.0, + 'height': 100.0, + }, 'link': 'https://google.com', }, }; @@ -83,10 +87,12 @@ void main() { ); expect(message.text, 'Hello, world!'); expect(message.metadata, {'key': 'value'}); - expect(message.linkPreview!.title, 'Google'); - expect(message.linkPreview!.description, 'Google homepage'); - expect(message.linkPreview!.imageUrl, 'https://google.com/logo.png'); - expect(message.linkPreview!.link, 'https://google.com'); + expect(message.linkPreviewData!.title, 'Google'); + expect(message.linkPreviewData!.description, 'Google homepage'); + expect(message.linkPreviewData!.image!.url, 'https://google.com/logo.png'); + expect(message.linkPreviewData!.image!.width, 100.0); + expect(message.linkPreviewData!.image!.height, 100.0); + expect(message.linkPreviewData!.link, 'https://google.com'); }); test('converts correctly to a JSON', () { @@ -97,10 +103,14 @@ void main() { 'type': 'text', 'text': 'Hello, world!', 'metadata': {'key': 'value'}, - 'linkPreview': { + 'linkPreviewData': { 'title': 'Google', 'description': 'Google homepage', - 'imageUrl': 'https://google.com/logo.png', + 'image': { + 'url': 'https://google.com/logo.png', + 'width': 100.0, + 'height': 100.0, + }, 'link': 'https://google.com', }, }); @@ -120,10 +130,14 @@ void main() { ), metadata: {'key': 'newValue'}, text: 'New text', - linkPreview: const LinkPreview( + linkPreviewData: const LinkPreviewData( title: 'New Title', description: 'New description', - imageUrl: 'https://newwebsite.com/logo.png', + image: ImagePreviewData( + url: 'https://newwebsite.com/logo.png', + width: 100.0, + height: 100.0, + ), link: 'https://newwebsite.com', ), ); @@ -137,13 +151,15 @@ void main() { ); expect(copiedMessage.text, 'New text'); expect(copiedMessage.metadata, {'key': 'newValue'}); - expect(copiedMessage.linkPreview!.title, 'New Title'); - expect(copiedMessage.linkPreview!.description, 'New description'); + expect(copiedMessage.linkPreviewData!.title, 'New Title'); + expect(copiedMessage.linkPreviewData!.description, 'New description'); expect( - copiedMessage.linkPreview!.imageUrl, + copiedMessage.linkPreviewData!.image!.url, 'https://newwebsite.com/logo.png', ); - expect(copiedMessage.linkPreview!.link, 'https://newwebsite.com'); + expect(copiedMessage.linkPreviewData!.image!.width, 100.0); + expect(copiedMessage.linkPreviewData!.image!.height, 100.0); + expect(copiedMessage.linkPreviewData!.link, 'https://newwebsite.com'); }); test( @@ -160,6 +176,7 @@ void main() { ); expect(copiedMessage.text, 'Hello, world!'); expect(copiedMessage.metadata, {'key': 'value'}); + }, ); @@ -171,10 +188,14 @@ void main() { 'type': 'text', 'text': 'Hello, world!', 'metadata': {'key': 'value'}, - 'linkPreview': { + 'linkPreviewData': { 'title': 'Google', 'description': 'Google homepage', - 'imageUrl': 'https://google.com/logo.png', + 'image': { + 'url': 'https://google.com/logo.png', + 'width': 100.0, + 'height': 100.0, + }, 'link': 'https://google.com', }, }; diff --git a/packages/flutter_chat_ui/lib/flutter_chat_ui.dart b/packages/flutter_chat_ui/lib/flutter_chat_ui.dart index 940df5fb9..5c5943a43 100644 --- a/packages/flutter_chat_ui/lib/flutter_chat_ui.dart +++ b/packages/flutter_chat_ui/lib/flutter_chat_ui.dart @@ -9,6 +9,7 @@ export 'src/chat_message/chat_message.dart'; export 'src/composer.dart'; export 'src/empty_chat_list.dart'; export 'src/is_typing.dart'; +export 'src/link_preview.dart'; export 'src/load_more.dart'; export 'src/scroll_to_bottom.dart'; export 'src/simple_text_message.dart'; diff --git a/packages/flutter_chat_ui/lib/src/link_preview.dart b/packages/flutter_chat_ui/lib/src/link_preview.dart new file mode 100644 index 000000000..e73af5575 --- /dev/null +++ b/packages/flutter_chat_ui/lib/src/link_preview.dart @@ -0,0 +1,2 @@ +/// Enum controlling the position of the link preview widget. +enum LinkPreviewPosition { top, bottom, none } diff --git a/packages/flutter_chat_ui/lib/src/simple_text_message.dart b/packages/flutter_chat_ui/lib/src/simple_text_message.dart index 05c736378..af087a662 100644 --- a/packages/flutter_chat_ui/lib/src/simple_text_message.dart +++ b/packages/flutter_chat_ui/lib/src/simple_text_message.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_chat_core/flutter_chat_core.dart'; import 'package:provider/provider.dart'; +import 'link_preview.dart'; + /// A widget that displays a simple text message. class SimpleTextMessage extends StatelessWidget { /// The text message data model. @@ -43,6 +45,11 @@ class SimpleTextMessage extends StatelessWidget { /// Position of the timestamp and status indicator relative to the text. final TimeAndStatusPosition timeAndStatusPosition; + /// The position of the link preview widget relative to the text. + /// If set to [LinkPreviewPosition.none], the link preview widget will not be displayed. + /// A [LinkPreviewBuilder] must be provided for the preview to be displayed. + final LinkPreviewPosition linkPreviewPosition; + /// Creates a widget to display a simple text message. const SimpleTextMessage({ super.key, @@ -59,6 +66,7 @@ class SimpleTextMessage extends StatelessWidget { this.showTime = true, this.showStatus = true, this.timeAndStatusPosition = TimeAndStatusPosition.end, + this.linkPreviewPosition = LinkPreviewPosition.bottom, }); bool get _isOnlyEmoji => message.metadata?['isOnlyEmoji'] == true; @@ -90,26 +98,51 @@ class SimpleTextMessage extends StatelessWidget { : textStyle, ); - return Container( - padding: - _isOnlyEmoji - ? EdgeInsets.symmetric( - horizontal: (padding?.horizontal ?? 0) / 2, - vertical: 0, - ) - : padding, - decoration: - _isOnlyEmoji - ? null - : BoxDecoration( - color: backgroundColor, - borderRadius: borderRadius ?? theme.shape, + final linkPreviewWidget = + linkPreviewPosition != LinkPreviewPosition.none + ? context.watch().linkPreviewBuilder?.call( + context, + message, + ) + : null; + + return ClipRRect( + borderRadius: borderRadius ?? theme.shape, + child: Container( + decoration: + _isOnlyEmoji + ? null + : BoxDecoration( + color: backgroundColor, + borderRadius: borderRadius ?? theme.shape, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (linkPreviewWidget != null && + linkPreviewPosition == LinkPreviewPosition.top) + linkPreviewWidget, + Container( + padding: + _isOnlyEmoji + ? EdgeInsets.symmetric( + horizontal: (padding?.horizontal ?? 0) / 2, + vertical: 0, + ) + : padding, + child: _buildContentBasedOnPosition( + context: context, + textContent: textContent, + timeAndStatus: timeAndStatus, + textStyle: textStyle, ), - child: _buildContentBasedOnPosition( - context: context, - textContent: textContent, - timeAndStatus: timeAndStatus, - textStyle: textStyle, + ), + if (linkPreviewWidget != null && + linkPreviewPosition == LinkPreviewPosition.bottom) + linkPreviewWidget, + ], + ), ), ); } diff --git a/packages/flyer_chat_text_message/lib/src/flyer_chat_text_message.dart b/packages/flyer_chat_text_message/lib/src/flyer_chat_text_message.dart index 2d8ac16c8..bce4fb1aa 100644 --- a/packages/flyer_chat_text_message/lib/src/flyer_chat_text_message.dart +++ b/packages/flyer_chat_text_message/lib/src/flyer_chat_text_message.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_chat_core/flutter_chat_core.dart'; +import 'package:flutter_chat_ui/flutter_chat_ui.dart' show LinkPreviewPosition; import 'package:gpt_markdown/gpt_markdown.dart'; import 'package:provider/provider.dart'; @@ -49,6 +50,11 @@ class FlyerChatTextMessage extends StatelessWidget { /// The callback function to handle link clicks. final void Function(String url, String title)? onLinkTab; + /// The position of the link preview widget relative to the text. + /// If set to [LinkPreviewPosition.none], the link preview widget will not be displayed. + /// A [LinkPreviewBuilder] must be provided for the preview to be displayed. + final LinkPreviewPosition linkPreviewPosition; + /// Creates a widget to display a text message. const FlyerChatTextMessage({ super.key, @@ -66,6 +72,7 @@ class FlyerChatTextMessage extends StatelessWidget { this.showStatus = true, this.timeAndStatusPosition = TimeAndStatusPosition.end, this.onLinkTab, + this.linkPreviewPosition = LinkPreviewPosition.bottom, }); bool get _isOnlyEmoji => message.metadata?['isOnlyEmoji'] == true; @@ -98,26 +105,51 @@ class FlyerChatTextMessage extends StatelessWidget { onLinkTab: onLinkTab, ); - return Container( - padding: - _isOnlyEmoji - ? EdgeInsets.symmetric( - horizontal: (padding?.horizontal ?? 0) / 2, - vertical: 0, - ) - : padding, - decoration: - _isOnlyEmoji - ? null - : BoxDecoration( - color: backgroundColor, - borderRadius: borderRadius ?? theme.shape, + final linkPreviewWidget = + linkPreviewPosition != LinkPreviewPosition.none + ? context.watch().linkPreviewBuilder?.call( + context, + message, + ) + : null; + + return ClipRRect( + borderRadius: borderRadius ?? theme.shape, + child: Container( + decoration: + _isOnlyEmoji + ? null + : BoxDecoration( + color: backgroundColor, + borderRadius: borderRadius ?? theme.shape, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (linkPreviewWidget != null && + linkPreviewPosition == LinkPreviewPosition.top) + linkPreviewWidget, + Container( + padding: + _isOnlyEmoji + ? EdgeInsets.symmetric( + horizontal: (padding?.horizontal ?? 0) / 2, + vertical: 0, + ) + : padding, + child: _buildContentBasedOnPosition( + context: context, + textContent: textContent, + timeAndStatus: timeAndStatus, + paragraphStyle: paragraphStyle, ), - child: _buildContentBasedOnPosition( - context: context, - textContent: textContent, - timeAndStatus: timeAndStatus, - paragraphStyle: paragraphStyle, + ), + if (linkPreviewWidget != null && + linkPreviewPosition == LinkPreviewPosition.bottom) + linkPreviewWidget, + ], + ), ), ); } diff --git a/packages/flyer_chat_text_message/pubspec.yaml b/packages/flyer_chat_text_message/pubspec.yaml index ba46bb798..d76284923 100644 --- a/packages/flyer_chat_text_message/pubspec.yaml +++ b/packages/flyer_chat_text_message/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: flutter: sdk: flutter flutter_chat_core: ^2.2.1 + flutter_chat_ui: ^2.2.1 gpt_markdown: ^1.0.16 provider: ^6.1.4