From a1045acfc916e670beb6f1bf3e8468c3ab5cee80 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 1 Feb 2021 21:35:12 +0530 Subject: [PATCH 01/17] Merge branch 'hotfix/video' of /Users/deven9852/Stream/stream-chat-flutter with conflicts. --- .../flutter_widgets/example/ios/Podfile.lock | 299 ++++++++++++++++++ .../lib/src/full_screen_media.dart | 18 +- .../lib/src/message_list_view.dart | 4 + .../lib/src/message_widget.dart | 38 +++ .../lib/src/video_attachment.dart | 74 +---- 5 files changed, 368 insertions(+), 65 deletions(-) create mode 100644 packages/flutter_widgets/example/ios/Podfile.lock diff --git a/packages/flutter_widgets/example/ios/Podfile.lock b/packages/flutter_widgets/example/ios/Podfile.lock new file mode 100644 index 00000000..a4f9baa6 --- /dev/null +++ b/packages/flutter_widgets/example/ios/Podfile.lock @@ -0,0 +1,299 @@ +PODS: + - DKImagePickerController/Core (4.3.2): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.2) + - DKImagePickerController/PhotoGallery (4.3.2): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.2) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - esys_flutter_share (0.0.1): + - Flutter + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Firebase/CoreOnly (6.33.0): + - FirebaseCore (= 6.10.3) + - Firebase/Messaging (6.33.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 4.7.0) + - firebase_core (0.5.3): + - Firebase/CoreOnly (~> 6.33.0) + - Flutter + - firebase_messaging (7.0.3): + - Firebase/CoreOnly (~> 6.33.0) + - Firebase/Messaging (~> 6.33.0) + - firebase_core + - Flutter + - FirebaseCore (6.10.3): + - FirebaseCoreDiagnostics (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - FirebaseCoreDiagnostics (1.7.0): + - GoogleDataTransport (~> 7.4) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - nanopb (~> 1.30906.0) + - FirebaseInstallations (1.7.0): + - FirebaseCore (~> 6.10) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - PromisesObjC (~> 1.2) + - FirebaseInstanceID (4.8.0): + - FirebaseCore (~> 6.10) + - FirebaseInstallations (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - FirebaseMessaging (4.7.1): + - FirebaseCore (~> 6.10) + - FirebaseInstanceID (~> 4.7) + - GoogleUtilities/AppDelegateSwizzler (~> 6.7) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Reachability (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - Protobuf (>= 3.9.2, ~> 3.9) + - Flutter (1.0.0) + - flutter_apns (0.0.1): + - Flutter + - flutter_app_badger (0.0.1): + - Flutter + - flutter_keyboard_visibility (0.0.1): + - Flutter + - flutter_local_notifications (0.0.1): + - Flutter + - flutter_secure_storage (3.3.1): + - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - GoogleDataTransport (7.5.1): + - nanopb (~> 1.30906.0) + - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (6.7.2): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (6.7.2)" + - GoogleUtilities/Reachability (6.7.2): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (6.7.2): + - GoogleUtilities/Logger + - image_gallery_saver (1.5.0): + - Flutter + - image_picker (0.0.1): + - Flutter + - nanopb (1.30906.0): + - nanopb/decode (= 1.30906.0) + - nanopb/encode (= 1.30906.0) + - nanopb/decode (1.30906.0) + - nanopb/encode (1.30906.0) + - path_provider (0.0.1): + - Flutter + - photo_manager (0.0.1): + - Flutter + - PromisesObjC (1.2.11) + - Protobuf (3.13.0) + - SDWebImage (5.10.0): + - SDWebImage/Core (= 5.10.0) + - SDWebImage/Core (5.10.0) + - shared_preferences (0.0.1): + - Flutter + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - sqlite3 (3.32.3): + - sqlite3/common (= 3.32.3) + - sqlite3/common (3.32.3) + - sqlite3/fts5 (3.32.3): + - sqlite3/common + - sqlite3/json1 (3.32.3): + - sqlite3/common + - sqlite3/perf-threadsafe (3.32.3): + - sqlite3/common + - sqlite3/rtree (3.32.3): + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): + - Flutter + - sqlite3 (~> 3.32.3) + - sqlite3/fts5 + - sqlite3/json1 + - sqlite3/perf-threadsafe + - sqlite3/rtree + - Starscream (4.0.4) + - StreamChatClient (2.4.2): + - Starscream (~> 4.0) + - SwiftyGif (5.3.0) + - url_launcher (0.0.1): + - Flutter + - video_compress (0.3.0): + - Flutter + - video_player (0.0.1): + - Flutter + - wakelock (0.0.1): + - Flutter + +DEPENDENCIES: + - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) + - Flutter (from `Flutter`) + - flutter_apns (from `.symlinks/plugins/flutter_apns/ios`) + - flutter_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`) + - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) + - image_picker (from `.symlinks/plugins/image_picker/ios`) + - path_provider (from `.symlinks/plugins/path_provider/ios`) + - photo_manager (from `.symlinks/plugins/photo_manager/ios`) + - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) + - StreamChatClient + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - video_compress (from `.symlinks/plugins/video_compress/ios`) + - video_player (from `.symlinks/plugins/video_player/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) + +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - Firebase + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseInstallations + - FirebaseInstanceID + - FirebaseMessaging + - FMDB + - GoogleDataTransport + - GoogleUtilities + - nanopb + - PromisesObjC + - Protobuf + - SDWebImage + - sqlite3 + - Starscream + - StreamChatClient + - SwiftyGif + +EXTERNAL SOURCES: + esys_flutter_share: + :path: ".symlinks/plugins/esys_flutter_share/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_messaging: + :path: ".symlinks/plugins/firebase_messaging/ios" + Flutter: + :path: Flutter + flutter_apns: + :path: ".symlinks/plugins/flutter_apns/ios" + flutter_app_badger: + :path: ".symlinks/plugins/flutter_app_badger/ios" + flutter_keyboard_visibility: + :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + image_gallery_saver: + :path: ".symlinks/plugins/image_gallery_saver/ios" + image_picker: + :path: ".symlinks/plugins/image_picker/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + photo_manager: + :path: ".symlinks/plugins/photo_manager/ios" + shared_preferences: + :path: ".symlinks/plugins/shared_preferences/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + sqlite3_flutter_libs: + :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" + video_compress: + :path: ".symlinks/plugins/video_compress/ios" + video_player: + :path: ".symlinks/plugins/video_player/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" + +SPEC CHECKSUMS: + DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 + file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 + Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 + firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 + firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75 + FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd + FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 + FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 + FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1 + FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + flutter_apns: ddc629f26016140bf52165040b0a8e8869f9ce32 + flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68 + flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 + flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 + flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 + image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 + nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + photo_manager: f7c619c2cc8c2adb8d85c63363babac477de9c67 + PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f + Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 + SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + sqlite3: 8f7d2078ae27778699a622a94b853285793422a2 + sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f + Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9 + StreamChatClient: fc6419fa7ceda9c048188bb7c8e9f07755ced05f + SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe + video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e + wakelock: bfc7955c418d0db797614075aabbc58a39ab5107 + +PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184 + +COCOAPODS: 1.10.1 diff --git a/packages/stream_chat_flutter/lib/src/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/full_screen_media.dart index 98ed3384..98093f6d 100644 --- a/packages/stream_chat_flutter/lib/src/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/full_screen_media.dart @@ -61,9 +61,14 @@ class _FullScreenMediaState extends State .where((element) => element.type == 'video') .toList() .forEach((element) { - videoPackages.add(VideoPackage(context, element, () { - setState(() {}); - })); + videoPackages.add(VideoPackage( + context, + element, + () { + setState(() {}); + }, + showControls: true, + )); }); } @@ -233,15 +238,18 @@ class VideoPackage { bool initialised = false; VoidCallback onInit; BuildContext context; + bool showControls; /// - VideoPackage(this.context, Attachment attachment, this.onInit) { + VideoPackage(this.context, Attachment attachment, this.onInit, + {this.showControls = false}) { _videoPlayerController = VideoPlayerController.network(attachment.assetUrl); _videoPlayerController.initialize().whenComplete(() { initialised = true; _chewieController = ChewieController( videoPlayerController: _videoPlayerController, - autoInitialize: false, + autoInitialize: true, + showControls: showControls, aspectRatio: _videoPlayerController.value.aspectRatio, ); onInit(); diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 86c1445c..11a0a784 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -239,6 +239,8 @@ class _MessageListViewState extends State { MessageListController _messageListController = MessageListController(); + Map videoPackages = {}; + @override Widget build(BuildContext context) { return MessageListCore( @@ -776,6 +778,7 @@ class _MessageListViewState extends State { break; } }, + videoPackages: videoPackages, ); } @@ -936,6 +939,7 @@ class _MessageListViewState extends State { break; } }, + videoPackages: videoPackages, ); if (!message.isDeleted && !message.isSystem && !message.isEphemeral) { diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index c2cbdc35..3a17c1c4 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -142,6 +142,8 @@ class MessageWidget extends StatefulWidget { /// Function called when quotedMessage is tapped final OnQuotedMessageTap onQuotedMessageTap; + Map videoPackages = {}; + /// MessageWidget({ Key key, @@ -190,6 +192,7 @@ class MessageWidget extends StatefulWidget { this.attachmentPadding = EdgeInsets.zero, this.allRead = false, this.onQuotedMessageTap, + this.videoPackages, }) : attachmentBuilders = { 'image': (context, message, attachment) { return ImageAttachment( @@ -832,6 +835,41 @@ class _MessageWidgetState extends State { children: widget.message.attachments ?.where((element) => element.ogScrapeUrl == null) ?.map((attachment) { + if (attachment.type == 'video') { + VideoPackage package; + + if (widget.videoPackages == null) { + package = VideoPackage(context, attachment, () {}); + } else { + package = widget?.videoPackages[ + '${widget.message.id}${widget.message.attachments.indexOf(attachment)}'] ?? + VideoPackage(context, attachment, () {}); + } + + if (widget.videoPackages != null) { + widget.videoPackages[ + '${widget.message.id}${widget.message.attachments.indexOf(attachment)}'] = + package; + } + + return Transform( + transform: Matrix4.rotationY(widget.reverse ? pi : 0), + alignment: Alignment.center, + child: VideoAttachment( + attachment: attachment, + messageTheme: widget.messageTheme, + size: Size( + MediaQuery.of(context).size.width * 0.8, + MediaQuery.of(context).size.height * 0.3, + ), + message: widget.message, + onShowMessage: widget.onShowMessage, + onReturnAction: widget.onReturnAction, + videoPackage: package, + ), + ); + } + final attachmentBuilder = widget.attachmentBuilders[attachment.type]; diff --git a/packages/stream_chat_flutter/lib/src/video_attachment.dart b/packages/stream_chat_flutter/lib/src/video_attachment.dart index b775109f..97cca98d 100644 --- a/packages/stream_chat_flutter/lib/src/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/video_attachment.dart @@ -16,11 +16,13 @@ class VideoAttachment extends StatefulWidget { final Message message; final ShowMessageCallback onShowMessage; final ValueChanged onReturnAction; + final VideoPackage videoPackage; VideoAttachment({ Key key, @required this.attachment, @required this.messageTheme, + this.videoPackage, this.message, this.size, this.onShowMessage, @@ -32,13 +34,21 @@ class VideoAttachment extends StatefulWidget { } class _VideoAttachmentState extends State { - ChewieController _chewieController; - VideoPlayerController _videoPlayerController; bool initialized = false; + @override + void initState() { + super.initState(); + widget.videoPackage.onInit = () { + setState(() { + initialized = true; + }); + }; + } + @override Widget build(BuildContext context) { - if (!initialized) { + if (!widget.videoPackage.initialised) { return Container( height: widget.size?.height ?? 100, width: widget.size?.width ?? 100, @@ -47,43 +57,6 @@ class _VideoAttachmentState extends State { ), ); } - _chewieController = ChewieController( - videoPlayerController: _videoPlayerController, - autoInitialize: true, - showControls: false, - aspectRatio: _videoPlayerController.value.aspectRatio, - errorBuilder: (_, e) { - if (widget.attachment.thumbUrl != null) { - return Stack( - children: [ - Container( - height: widget.size?.height, - width: widget.size?.width, - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: CachedNetworkImageProvider( - widget.attachment.thumbUrl, - ), - ), - ), - ), - if (widget.attachment.titleLink != null) - Material( - color: Colors.transparent, - child: InkWell( - onTap: () => - launchURL(context, widget.attachment.titleLink), - ), - ), - ], - ); - } - return AttachmentError( - attachment: widget.attachment, - size: widget.size, - ); - }); return GestureDetector( onTap: () async { @@ -123,7 +96,7 @@ class _VideoAttachmentState extends State { child: Stack( children: [ Chewie( - controller: _chewieController, + controller: widget.videoPackage.chewieController, ), Positioned.fill( child: Center( @@ -153,23 +126,4 @@ class _VideoAttachmentState extends State { ), ); } - - @override - void initState() { - super.initState(); - _videoPlayerController = - VideoPlayerController.network(widget.attachment.assetUrl); - _videoPlayerController.initialize().whenComplete(() { - setState(() { - initialized = true; - }); - }); - } - - @override - void dispose() { - _videoPlayerController?.dispose(); - _chewieController?.dispose(); - super.dispose(); - } } From c5d770c7831bfd09b4b65fc1cda28c8ebcdd0c1b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 2 Feb 2021 16:18:00 +0530 Subject: [PATCH 02/17] [LLC] Add optimistic reaction update Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 103 ++++++++++++++++-- .../stream_chat/lib/src/models/reaction.dart | 40 ++++++- 2 files changed, 129 insertions(+), 14 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 6117b5d7..e5bf528c 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -274,28 +274,107 @@ class Channel { bool enforceUnique = false, }) async { final messageId = message.id; + final now = DateTime.now(); + final user = _client.state.user; + + final latestReactions = [...message.latestReactions]; + final ownReactions = [...message.ownReactions]; + if (enforceUnique) { + latestReactions.removeWhere((it) => it.userId == user.id); + ownReactions.removeWhere((it) => it.userId == user.id); + } + + final newReaction = Reaction( + messageId: messageId, + createdAt: now, + type: type, + user: user, + score: 1, + extraData: extraData, + ); + + // Inserting at the 0th index as it's the latest reaction + latestReactions.insert(0, newReaction); + ownReactions.add(newReaction); + + final newMessage = message.copyWith( + reactionCounts: {...message.reactionCounts}..update(type, (value) { + if (enforceUnique) return value; + return value + 1; + }, ifAbsent: () => 1), + reactionScores: {...message.reactionScores}..update(type, (value) { + if (enforceUnique) return value; + return value + 1; + }, ifAbsent: () => 1), + latestReactions: latestReactions, + ownReactions: ownReactions, + ); + + state?.addMessage(newMessage); + final data = Map.from(extraData) ..addAll({ 'type': type, }); - final res = await _client.post( - '/messages/$messageId/reaction', - data: { - 'reaction': data, - 'enforce_unique': enforceUnique, - }, - ); - return _client.decode(res.data, SendReactionResponse.fromJson); + try { + final res = await _client.post( + '/messages/$messageId/reaction', + data: { + 'reaction': data, + 'enforce_unique': enforceUnique, + }, + ); + final reactionResp = + _client.decode(res.data, SendReactionResponse.fromJson); + state?.addMessage(reactionResp.message); + return reactionResp; + } catch (error) { + if (error is DioError && error.type != DioErrorType.RESPONSE) { + // Reset the message if the update fails + state?.addMessage(message); + } + rethrow; + } } /// Delete a reaction from this channel - Future deleteReaction(Message message, Reaction reaction) { + Future deleteReaction( + Message message, Reaction reaction) async { _checkInitialized(); - return client - .delete('/messages/${message.id}/reaction/${reaction.type}') - .then((res) => _client.decode(res.data, EmptyResponse.fromJson)); + final type = reaction.type; + + final reactionCounts = {...message.reactionCounts} + ..update(type, (value) => value - 1); + final reactionScores = {...message.reactionScores} + ..update(type, (value) => value - 1); + + final removeWhere = (Reaction r) => + r.userId == reaction.userId && + r.type == reaction.type && + r.messageId == reaction.messageId; + + final newMessage = message.copyWith( + reactionCounts: reactionCounts..removeWhere((_, value) => value == 0), + reactionScores: reactionScores..removeWhere((_, value) => value == 0), + latestReactions: [...message.latestReactions]..removeWhere(removeWhere), + ownReactions: [...message.ownReactions]..removeWhere(removeWhere), + ); + + state?.addMessage(newMessage); + + try { + final res = await client + .delete('/messages/${message.id}/reaction/${reaction.type}'); + return _client.decode(res.data, EmptyResponse.fromJson); + } catch (error) { + if (error is DioError && error.type != DioErrorType.RESPONSE) { + // Reset the message if the update fails + state?.addMessage(message); + } + rethrow; + } } /// Edit the channel custom data diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index 9a4869f7..f0957761 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -49,10 +49,10 @@ class Reaction { this.createdAt, this.type, this.user, - this.userId, + String userId, this.score, this.extraData, - }); + }) : userId = userId ?? user?.id; /// Create a new instance from a json factory Reaction.fromJson(Map json) { @@ -65,4 +65,40 @@ class Reaction { return Serialization.moveFromExtraDataToRoot( _$ReactionToJson(this), topLevelFields); } + + /// Creates a copy of [Reaction] with specified attributes overridden. + Reaction copyWith({ + String messageId, + DateTime createdAt, + String type, + User user, + String userId, + int score, + Map extraData, + }) { + return Reaction( + messageId: messageId ?? this.messageId, + createdAt: createdAt ?? this.createdAt, + type: type ?? this.type, + user: user ?? this.user, + userId: userId ?? this.userId, + score: score ?? this.score, + extraData: extraData ?? this.extraData, + ); + } + + /// Returns a new [Reaction] that is a combination of this reaction and the given + /// [other] reaction. + Reaction merge(Reaction other) { + if (other == null) return this; + return copyWith( + messageId: other.messageId, + createdAt: other.createdAt, + type: other.type, + user: other.user, + userId: other.userId, + score: other.score, + extraData: other.extraData, + ); + } } From 94882c73c5bf675b92cfc0843b36197661dbce8d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 2 Feb 2021 16:20:22 +0530 Subject: [PATCH 03/17] [UI-Kit -> MessageReactionsModal] Fix username overflow Signed-off-by: Sahil Kumar --- .../stream_chat_flutter/lib/src/message_reactions_modal.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart index e3d4f201..430602cb 100644 --- a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart @@ -257,7 +257,7 @@ class MessageReactionsModal extends StatelessWidget { ), const SizedBox(height: 8), Text( - reaction.user.name, + reaction.user.name.split(' ')[0], style: StreamChatTheme.of(context).textTheme.footnoteBold, textAlign: TextAlign.center, ), From 7d02187751ed9583140eca38a7e705aa4a568600 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 2 Feb 2021 16:30:52 +0530 Subject: [PATCH 04/17] [UI-Kit] Fix test Signed-off-by: Sahil Kumar --- .../test/src/message_reaction_modal_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/test/src/message_reaction_modal_test.dart b/packages/stream_chat_flutter/test/src/message_reaction_modal_test.dart index c023b20d..4e515a09 100644 --- a/packages/stream_chat_flutter/test/src/message_reaction_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_reaction_modal_test.dart @@ -96,7 +96,7 @@ void main() { findsNWidgets(2)); expect(find.byKey(Key('StreamSvgIcon-Icon_love_reaction.svg')), findsNWidgets(2)); - expect(find.text(testUserId), findsNWidgets(2)); + expect(find.text(testUserId.split(' ')[0]), findsNWidgets(2)); }, ); } From 91e173bbfd21a1c71a6f47d0adc651b8cd283bb7 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 2 Feb 2021 17:19:46 +0530 Subject: [PATCH 05/17] feat: Added cache for photos and videos screen --- .../lib/src/channel_media_display_screen.dart | 21 +++++++++++++++++-- .../lib/src/message_list_view.dart | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart b/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart index 85b571bc..ba7291d6 100644 --- a/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart +++ b/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart @@ -33,6 +33,8 @@ class ChannelMediaDisplayScreen extends StatefulWidget { } class _ChannelMediaDisplayScreenState extends State { + Map _controllerCache = {}; + @override void initState() { super.initState(); @@ -147,8 +149,15 @@ class _ChannelMediaDisplayScreenState extends State { .forEach((e) { VideoPlayerController controller; if (e.type == 'video') { - controller = VideoPlayerController.network(e.assetUrl); - controller.initialize(); + var cachedController = _controllerCache[e.assetUrl]; + + if (cachedController == null) { + controller = VideoPlayerController.network(e.assetUrl); + controller.initialize(); + _controllerCache[e.assetUrl] = controller; + } else { + controller = cachedController; + } } media.add(_AssetPackage(e, item.message, controller)); }); @@ -221,6 +230,14 @@ class _ChannelMediaDisplayScreenState extends State { stream: messageSearchBloc.messagesStream, ); } + + @override + void dispose() { + super.dispose(); + for (var c in _controllerCache.values) { + c.dispose(); + } + } } class _AssetPackage { diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 11a0a784..f3659838 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -1056,6 +1056,7 @@ class _MessageListViewState extends State { streamChannel.reloadChannel(); } _messageNewListener?.cancel(); + videoPackages.values.forEach((e) => e.dispose()); super.dispose(); } } From 0030de3dd6b0dad03f891cc6c8b56f606e807c50 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 2 Feb 2021 17:23:18 +0530 Subject: [PATCH 06/17] [LLC] Fix reactions tests Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 30 +++++++++---------- .../test/src/api/channel_test.dart | 21 ++++++------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index e5bf528c..a7034dfd 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -329,11 +329,9 @@ class Channel { _client.decode(res.data, SendReactionResponse.fromJson); state?.addMessage(reactionResp.message); return reactionResp; - } catch (error) { - if (error is DioError && error.type != DioErrorType.RESPONSE) { - // Reset the message if the update fails - state?.addMessage(message); - } + } catch (_) { + // Reset the message if the update fails + state?.addMessage(message); rethrow; } } @@ -341,14 +339,16 @@ class Channel { /// Delete a reaction from this channel Future deleteReaction( Message message, Reaction reaction) async { - _checkInitialized(); - final type = reaction.type; - final reactionCounts = {...message.reactionCounts} - ..update(type, (value) => value - 1); - final reactionScores = {...message.reactionScores} - ..update(type, (value) => value - 1); + final reactionCounts = {...message.reactionCounts}; + if (reactionCounts.containsKey(type)) { + reactionCounts.update(type, (value) => value - 1); + } + final reactionScores = {...message.reactionScores}; + if (reactionScores.containsKey(type)) { + reactionScores.update(type, (value) => value - 1); + } final removeWhere = (Reaction r) => r.userId == reaction.userId && @@ -368,11 +368,9 @@ class Channel { final res = await client .delete('/messages/${message.id}/reaction/${reaction.type}'); return _client.decode(res.data, EmptyResponse.fromJson); - } catch (error) { - if (error is DioError && error.type != DioErrorType.RESPONSE) { - // Reset the message if the update fails - state?.addMessage(message); - } + } catch (_) { + // Reset the message if the update fails + state?.addMessage(message); rethrow; } } diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index 4b274a94..9baf4235 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -396,6 +396,10 @@ void main() { await channelClient.sendReaction( Message( id: 'messageid', + reactionCounts: const {}, + reactionScores: const {}, + latestReactions: const [], + ownReactions: const [], ), reactionType, ); @@ -421,20 +425,17 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); - await channelClient.watch(); - when(mockDio.delete('/messages/messageid/reaction/test')) .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); await channelClient.deleteReaction( - Message(id: 'messageid'), + Message( + id: 'messageid', + reactionCounts: const {}, + reactionScores: const {}, + latestReactions: const [], + ownReactions: const [], + ), Reaction(type: 'test'), ); From fa5ab3c575e60f20121600e6dfa6932e5d45dbec Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 2 Feb 2021 17:37:16 +0530 Subject: [PATCH 07/17] feat: Fixed video refresh bug --- packages/stream_chat_flutter/lib/src/message_widget.dart | 4 ++-- packages/stream_chat_flutter/lib/src/video_attachment.dart | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 3a17c1c4..9a75a455 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -142,7 +142,7 @@ class MessageWidget extends StatefulWidget { /// Function called when quotedMessage is tapped final OnQuotedMessageTap onQuotedMessageTap; - Map videoPackages = {}; + final Map videoPackages; /// MessageWidget({ @@ -192,7 +192,7 @@ class MessageWidget extends StatefulWidget { this.attachmentPadding = EdgeInsets.zero, this.allRead = false, this.onQuotedMessageTap, - this.videoPackages, + this.videoPackages = const {}, }) : attachmentBuilders = { 'image': (context, message, attachment) { return ImageAttachment( diff --git a/packages/stream_chat_flutter/lib/src/video_attachment.dart b/packages/stream_chat_flutter/lib/src/video_attachment.dart index 97cca98d..107776bf 100644 --- a/packages/stream_chat_flutter/lib/src/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/video_attachment.dart @@ -1,12 +1,8 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/full_screen_media.dart'; -import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:video_player/video_player.dart'; -import 'attachment_error.dart'; import 'attachment_title.dart'; class VideoAttachment extends StatefulWidget { From 2cd02bea4dc454404177f629156b80d6e7f0ef1d Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 2 Feb 2021 18:09:29 +0530 Subject: [PATCH 08/17] feat: Fixed modals controller --- .../stream_chat_flutter/lib/src/message_actions_modal.dart | 4 ++++ .../stream_chat_flutter/lib/src/message_reactions_modal.dart | 4 ++++ packages/stream_chat_flutter/lib/src/message_widget.dart | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index 6ca7c688..d46d348a 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; @@ -33,6 +34,7 @@ class MessageActionsModal extends StatefulWidget { final ShapeBorder messageShape; final ShapeBorder attachmentShape; final DisplayWidget showUserAvatar; + final Map videoPackages; const MessageActionsModal({ Key key, @@ -53,6 +55,7 @@ class MessageActionsModal extends StatefulWidget { this.messageShape, this.attachmentShape, this.reverse = false, + this.videoPackages, }) : super(key: key); @override @@ -180,6 +183,7 @@ class _MessageActionsModalState extends State { showSendingIndicator: false, shape: widget.messageShape, attachmentShape: widget.attachmentShape, + videoPackages: widget.videoPackages, ), ), SizedBox(height: 8), diff --git a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart index e3d4f201..c970e92c 100644 --- a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; @@ -22,6 +23,7 @@ class MessageReactionsModal extends StatelessWidget { final ShapeBorder messageShape; final ShapeBorder attachmentShape; final void Function(User) onUserAvatarTap; + final Map videoPackages; const MessageReactionsModal({ Key key, @@ -35,6 +37,7 @@ class MessageReactionsModal extends StatelessWidget { this.reverse = false, this.showUserAvatar = DisplayWidget.show, this.onUserAvatarTap, + this.videoPackages, }) : super(key: key); @override @@ -143,6 +146,7 @@ class MessageReactionsModal extends StatelessWidget { (message.status == MessageSendingStatus.sent || message.status == null), + videoPackages: videoPackages, ), ), if (message.latestReactions?.isNotEmpty == true) ...[ diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 9a75a455..75a04c28 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -192,7 +192,7 @@ class MessageWidget extends StatefulWidget { this.attachmentPadding = EdgeInsets.zero, this.allRead = false, this.onQuotedMessageTap, - this.videoPackages = const {}, + this.videoPackages, }) : attachmentBuilders = { 'image': (context, message, attachment) { return ImageAttachment( @@ -743,6 +743,7 @@ class _MessageWidgetState extends State { !isFailedState && widget.onThreadTap != null, showFlagButton: widget.showFlagButton, + videoPackages: widget.videoPackages, ), ); }); @@ -771,6 +772,7 @@ class _MessageWidgetState extends State { editMessageInputBuilder: widget.editMessageInputBuilder, onThreadTap: widget.onThreadTap, showReactions: widget.showReactions, + videoPackages: widget.videoPackages, ), ); }); From bc7c52a7f77ff86d7ef82bfbebc0992c2ca998fb Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 2 Feb 2021 18:47:00 +0530 Subject: [PATCH 09/17] Delete Podfile.lock --- .../flutter_widgets/example/ios/Podfile.lock | 299 ------------------ 1 file changed, 299 deletions(-) delete mode 100644 packages/flutter_widgets/example/ios/Podfile.lock diff --git a/packages/flutter_widgets/example/ios/Podfile.lock b/packages/flutter_widgets/example/ios/Podfile.lock deleted file mode 100644 index a4f9baa6..00000000 --- a/packages/flutter_widgets/example/ios/Podfile.lock +++ /dev/null @@ -1,299 +0,0 @@ -PODS: - - DKImagePickerController/Core (4.3.2): - - DKImagePickerController/ImageDataManager - - DKImagePickerController/Resource - - DKImagePickerController/ImageDataManager (4.3.2) - - DKImagePickerController/PhotoGallery (4.3.2): - - DKImagePickerController/Core - - DKPhotoGallery - - DKImagePickerController/Resource (4.3.2) - - DKPhotoGallery (0.0.17): - - DKPhotoGallery/Core (= 0.0.17) - - DKPhotoGallery/Model (= 0.0.17) - - DKPhotoGallery/Preview (= 0.0.17) - - DKPhotoGallery/Resource (= 0.0.17) - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Core (0.0.17): - - DKPhotoGallery/Model - - DKPhotoGallery/Preview - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Model (0.0.17): - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Preview (0.0.17): - - DKPhotoGallery/Model - - DKPhotoGallery/Resource - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Resource (0.0.17): - - SDWebImage - - SwiftyGif - - esys_flutter_share (0.0.1): - - Flutter - - file_picker (0.0.1): - - DKImagePickerController/PhotoGallery - - Flutter - - Firebase/CoreOnly (6.33.0): - - FirebaseCore (= 6.10.3) - - Firebase/Messaging (6.33.0): - - Firebase/CoreOnly - - FirebaseMessaging (~> 4.7.0) - - firebase_core (0.5.3): - - Firebase/CoreOnly (~> 6.33.0) - - Flutter - - firebase_messaging (7.0.3): - - Firebase/CoreOnly (~> 6.33.0) - - Firebase/Messaging (~> 6.33.0) - - firebase_core - - Flutter - - FirebaseCore (6.10.3): - - FirebaseCoreDiagnostics (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - FirebaseCoreDiagnostics (1.7.0): - - GoogleDataTransport (~> 7.4) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - nanopb (~> 1.30906.0) - - FirebaseInstallations (1.7.0): - - FirebaseCore (~> 6.10) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - PromisesObjC (~> 1.2) - - FirebaseInstanceID (4.8.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - FirebaseMessaging (4.7.1): - - FirebaseCore (~> 6.10) - - FirebaseInstanceID (~> 4.7) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Reachability (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - Protobuf (>= 3.9.2, ~> 3.9) - - Flutter (1.0.0) - - flutter_apns (0.0.1): - - Flutter - - flutter_app_badger (0.0.1): - - Flutter - - flutter_keyboard_visibility (0.0.1): - - Flutter - - flutter_local_notifications (0.0.1): - - Flutter - - flutter_secure_storage (3.3.1): - - Flutter - - FMDB (2.7.5): - - FMDB/standard (= 2.7.5) - - FMDB/standard (2.7.5) - - GoogleDataTransport (7.5.1): - - nanopb (~> 1.30906.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/Network (6.7.2): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): - - GoogleUtilities/Logger - - image_gallery_saver (1.5.0): - - Flutter - - image_picker (0.0.1): - - Flutter - - nanopb (1.30906.0): - - nanopb/decode (= 1.30906.0) - - nanopb/encode (= 1.30906.0) - - nanopb/decode (1.30906.0) - - nanopb/encode (1.30906.0) - - path_provider (0.0.1): - - Flutter - - photo_manager (0.0.1): - - Flutter - - PromisesObjC (1.2.11) - - Protobuf (3.13.0) - - SDWebImage (5.10.0): - - SDWebImage/Core (= 5.10.0) - - SDWebImage/Core (5.10.0) - - shared_preferences (0.0.1): - - Flutter - - sqflite (0.0.2): - - Flutter - - FMDB (>= 2.7.5) - - sqlite3 (3.32.3): - - sqlite3/common (= 3.32.3) - - sqlite3/common (3.32.3) - - sqlite3/fts5 (3.32.3): - - sqlite3/common - - sqlite3/json1 (3.32.3): - - sqlite3/common - - sqlite3/perf-threadsafe (3.32.3): - - sqlite3/common - - sqlite3/rtree (3.32.3): - - sqlite3/common - - sqlite3_flutter_libs (0.0.1): - - Flutter - - sqlite3 (~> 3.32.3) - - sqlite3/fts5 - - sqlite3/json1 - - sqlite3/perf-threadsafe - - sqlite3/rtree - - Starscream (4.0.4) - - StreamChatClient (2.4.2): - - Starscream (~> 4.0) - - SwiftyGif (5.3.0) - - url_launcher (0.0.1): - - Flutter - - video_compress (0.3.0): - - Flutter - - video_player (0.0.1): - - Flutter - - wakelock (0.0.1): - - Flutter - -DEPENDENCIES: - - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`) - - file_picker (from `.symlinks/plugins/file_picker/ios`) - - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - - Flutter (from `Flutter`) - - flutter_apns (from `.symlinks/plugins/flutter_apns/ios`) - - flutter_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`) - - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) - - image_picker (from `.symlinks/plugins/image_picker/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) - - photo_manager (from `.symlinks/plugins/photo_manager/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - - sqflite (from `.symlinks/plugins/sqflite/ios`) - - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - - StreamChatClient - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - - video_compress (from `.symlinks/plugins/video_compress/ios`) - - video_player (from `.symlinks/plugins/video_player/ios`) - - wakelock (from `.symlinks/plugins/wakelock/ios`) - -SPEC REPOS: - trunk: - - DKImagePickerController - - DKPhotoGallery - - Firebase - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseInstallations - - FirebaseInstanceID - - FirebaseMessaging - - FMDB - - GoogleDataTransport - - GoogleUtilities - - nanopb - - PromisesObjC - - Protobuf - - SDWebImage - - sqlite3 - - Starscream - - StreamChatClient - - SwiftyGif - -EXTERNAL SOURCES: - esys_flutter_share: - :path: ".symlinks/plugins/esys_flutter_share/ios" - file_picker: - :path: ".symlinks/plugins/file_picker/ios" - firebase_core: - :path: ".symlinks/plugins/firebase_core/ios" - firebase_messaging: - :path: ".symlinks/plugins/firebase_messaging/ios" - Flutter: - :path: Flutter - flutter_apns: - :path: ".symlinks/plugins/flutter_apns/ios" - flutter_app_badger: - :path: ".symlinks/plugins/flutter_app_badger/ios" - flutter_keyboard_visibility: - :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" - flutter_local_notifications: - :path: ".symlinks/plugins/flutter_local_notifications/ios" - flutter_secure_storage: - :path: ".symlinks/plugins/flutter_secure_storage/ios" - image_gallery_saver: - :path: ".symlinks/plugins/image_gallery_saver/ios" - image_picker: - :path: ".symlinks/plugins/image_picker/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" - photo_manager: - :path: ".symlinks/plugins/photo_manager/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" - sqflite: - :path: ".symlinks/plugins/sqflite/ios" - sqlite3_flutter_libs: - :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" - video_compress: - :path: ".symlinks/plugins/video_compress/ios" - video_player: - :path: ".symlinks/plugins/video_player/ios" - wakelock: - :path: ".symlinks/plugins/wakelock/ios" - -SPEC CHECKSUMS: - DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d - DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 - file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 - Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 - firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 - firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75 - FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd - FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 - FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 - FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1 - FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a - Flutter: 0e3d915762c693b495b44d77113d4970485de6ec - flutter_apns: ddc629f26016140bf52165040b0a8e8869f9ce32 - flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68 - flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 - flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec - FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 - image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 - nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc - path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - photo_manager: f7c619c2cc8c2adb8d85c63363babac477de9c67 - PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f - Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 - SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee - shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 - sqlite3: 8f7d2078ae27778699a622a94b853285793422a2 - sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f - Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9 - StreamChatClient: fc6419fa7ceda9c048188bb7c8e9f07755ced05f - SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 - url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef - video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe - video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e - wakelock: bfc7955c418d0db797614075aabbc58a39ab5107 - -PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184 - -COCOAPODS: 1.10.1 From c81ff80b4dd2e58be255ae0dfcbedb40842fd170 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 3 Feb 2021 13:18:08 +0530 Subject: [PATCH 10/17] [LLC] Fix reaction events listener Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 183 ++++-------------- 1 file changed, 40 insertions(+), 143 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index a7034dfd..39ed445e 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -277,11 +277,9 @@ class Channel { final now = DateTime.now(); final user = _client.state.user; - final latestReactions = [...message.latestReactions]; - final ownReactions = [...message.ownReactions]; + final latestReactions = [...message.latestReactions ?? []]; if (enforceUnique) { latestReactions.removeWhere((it) => it.userId == user.id); - ownReactions.removeWhere((it) => it.userId == user.id); } final newReaction = Reaction( @@ -295,14 +293,17 @@ class Channel { // Inserting at the 0th index as it's the latest reaction latestReactions.insert(0, newReaction); - ownReactions.add(newReaction); + final ownReactions = [...latestReactions] + ..removeWhere((it) => it.userId != user.id); final newMessage = message.copyWith( - reactionCounts: {...message.reactionCounts}..update(type, (value) { + reactionCounts: {...message?.reactionCounts ?? {}} + ..update(type, (value) { if (enforceUnique) return value; return value + 1; }, ifAbsent: () => 1), - reactionScores: {...message.reactionScores}..update(type, (value) { + reactionScores: {...message.reactionScores ?? {}} + ..update(type, (value) { if (enforceUnique) return value; return value + 1; }, ifAbsent: () => 1), @@ -340,26 +341,32 @@ class Channel { Future deleteReaction( Message message, Reaction reaction) async { final type = reaction.type; + final user = _client.state.user; - final reactionCounts = {...message.reactionCounts}; + final reactionCounts = {...message.reactionCounts ?? {}}; if (reactionCounts.containsKey(type)) { reactionCounts.update(type, (value) => value - 1); } - final reactionScores = {...message.reactionScores}; + final reactionScores = {...message.reactionScores ?? {}}; if (reactionScores.containsKey(type)) { reactionScores.update(type, (value) => value - 1); } - final removeWhere = (Reaction r) => - r.userId == reaction.userId && - r.type == reaction.type && - r.messageId == reaction.messageId; + final latestReactions = [...message.latestReactions ?? []] + ..removeWhere((r) { + return r.userId == reaction.userId && + r.type == reaction.type && + r.messageId == reaction.messageId; + }); + + final ownReactions = [...latestReactions ?? []] + ..removeWhere((it) => it.userId != user.id); final newMessage = message.copyWith( reactionCounts: reactionCounts..removeWhere((_, value) => value == 0), reactionScores: reactionScores..removeWhere((_, value) => value == 0), - latestReactions: [...message.latestReactions]..removeWhere(removeWhere), - ownReactions: [...message.ownReactions]..removeWhere(removeWhere), + latestReactions: latestReactions, + ownReactions: ownReactions, ); state?.addMessage(newMessage); @@ -1075,73 +1082,24 @@ class ChannelClientState { void _listenReactionDeleted() { _subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) { - final reaction = event.reaction; - final message = event.message; - _removeMessageReaction(message, reaction); - })); - } - - void _removeMessageReaction(Message message, Reaction reaction) { - if (message.parentId == null || message.showInChannel == true) { - _channelState = _channelState.copyWith( - messages: _channelState?.messages?.map((m) { - if (m.id == message.id) { - return _removeReactionFromMessage(m, reaction); - } - return m; - })?.toList(), + final userId = _channel.client.state.user.id; + final message = event.message.copyWith( + ownReactions: [...event.message.latestReactions] + ..removeWhere((it) => it.userId != userId), ); - } - - if (message.parentId != null) { - final newThreads = threads; - if (newThreads.containsKey(message.parentId)) { - newThreads[message.parentId] = newThreads[message.parentId].map((m) { - if (m.id == message.id) { - return _removeReactionFromMessage(m, reaction); - } - return m; - }).toList(); - _threads = newThreads; - } - } + addMessage(message); + })); } void _listenReactions() { - _subscriptions.add(_channel - .on( - EventType.reactionNew, - ) - .listen((event) { - final message = event.message; - _addMessageReaction(message, event.reaction); - })); - } - - void _addMessageReaction(Message message, Reaction reaction) { - if (message.parentId == null || message.showInChannel == true) { - _channelState = _channelState.copyWith( - messages: _channelState.messages.map((m) { - if (message.id == m.id) { - return _addReactionToMessage(m, reaction); - } - return m; - }).toList(), + _subscriptions.add(_channel.on(EventType.reactionNew).listen((event) { + final userId = _channel.client.state.user.id; + final message = event.message.copyWith( + ownReactions: [...event.message.latestReactions] + ..removeWhere((it) => it.userId != userId), ); - } - - if (message.parentId != null) { - final newThreads = threads; - if (newThreads.containsKey(message.parentId)) { - newThreads[message.parentId] = newThreads[message.parentId].map((m) { - if (message.id == m.id) { - return _addReactionToMessage(m, reaction); - } - return m; - }).toList(); - _threads = newThreads; - } - } + addMessage(message); + })); } void _listenMessageUpdated() { @@ -1151,13 +1109,12 @@ class ChannelClientState { EventType.reactionUpdated, ) .listen((event) { - final message = event.message; - addMessage(message.copyWith( - ownReactions: message.latestReactions - .where( - (element) => element.user?.id == _channel._client.state.user.id) - .toList(), - )); + final userId = _channel.client.state.user.id; + final message = event.message.copyWith( + ownReactions: [...event.message.latestReactions] + ..removeWhere((it) => it.userId != userId), + ); + addMessage(message); })); } @@ -1240,66 +1197,6 @@ class ChannelClientState { })); } - Message _addReactionToMessage(Message message, Reaction reaction) { - final newMessage = message.copyWith( - latestReactions: message.latestReactions..add(reaction), - reactionCounts: { - ...message.reactionCounts ?? {}, - reaction.type: (message.reactionCounts == null - ? 0 - : message.reactionCounts[reaction.type] ?? 0) + - 1, - }, - reactionScores: { - ...message.reactionScores ?? {}, - reaction.type: (message.reactionScores == null - ? 0 - : message.reactionScores[reaction.type] ?? 0) + - reaction.score, - }, - ); - - if (reaction.user.id == _channel.client.state.user.id) { - return newMessage.copyWith( - ownReactions: message.ownReactions..add(reaction), - ); - } - - return newMessage; - } - - Message _removeReactionFromMessage(Message message, Reaction reaction) { - final newMessage = message.copyWith( - latestReactions: message.latestReactions - ..removeWhere( - (r) => r.type == reaction.type && r.userId == reaction.userId), - reactionCounts: { - ...message.reactionCounts, - reaction.type: (message.reactionCounts[reaction.type] ?? 0) - 1, - }, - reactionScores: { - ...message.reactionScores ?? {}, - reaction.type: max( - (message.reactionScores == null - ? 0 - : message.reactionScores[reaction.type] ?? 0) - - reaction.score, - 0), - }, - ); - - newMessage.reactionCounts.removeWhere((_, v) => v <= 0); - - if (reaction.user.id == _channel.client.state.user.id) { - return newMessage.copyWith( - ownReactions: message.ownReactions - ..removeWhere((r) => r.type == reaction.type), - ); - } - - return newMessage; - } - /// Channel message list List get messages => _channelState.messages; From 5347ba78d0854f22c9e1c50348eee729ee41d479 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 3 Feb 2021 13:33:33 +0530 Subject: [PATCH 11/17] [LLC] Fix reaction tests Signed-off-by: Sahil Kumar --- packages/stream_chat/test/src/api/channel_test.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index 9baf4235..32ac65c2 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -7,6 +7,7 @@ import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/reaction.dart'; +import 'package:stream_chat/src/models/own_user.dart'; import 'package:test/test.dart'; class MockDio extends Mock implements DioForNative {} @@ -379,7 +380,8 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - ); + )..state.user = OwnUser(id: 'test-id'); + final channelClient = client.channel('messaging', id: 'testid'); final reactionType = 'test'; @@ -422,7 +424,8 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - ); + )..state.user = OwnUser(id: 'test-id'); + final channelClient = client.channel('messaging', id: 'testid'); when(mockDio.delete('/messages/messageid/reaction/test')) From d0ff54cbf382179db0ac6365dca304201db2822c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 3 Feb 2021 13:48:13 +0530 Subject: [PATCH 12/17] [LLC] save one extra rebuild Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 39ed445e..7359c343 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -328,7 +328,6 @@ class Channel { ); final reactionResp = _client.decode(res.data, SendReactionResponse.fromJson); - state?.addMessage(reactionResp.message); return reactionResp; } catch (_) { // Reset the message if the update fails From 63e11225efb518d5e4125e13c5892648c2c49dc6 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 3 Feb 2021 14:10:45 +0530 Subject: [PATCH 13/17] fix: import --- packages/stream_chat_flutter/lib/src/message_actions_modal.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index d46d348a..303c40c8 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/utils.dart'; From 2cd5eed2453dc4fcb69e4d47918b5f4c0a3a6303 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 3 Feb 2021 14:11:39 +0530 Subject: [PATCH 14/17] fix: import --- packages/stream_chat_flutter/lib/src/message_actions_modal.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index e0951cff..9214befb 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -8,7 +8,6 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'extension.dart'; import 'message_input.dart'; From 4fa6581f190061862ac3ee3cbfe9ea46d3590054 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 3 Feb 2021 14:31:25 +0530 Subject: [PATCH 15/17] fix: tests --- .../lib/src/channel_media_display_screen.dart | 8 ++++---- .../test/stream_chat_core_test.dart | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart b/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart index ba7291d6..8e9f566c 100644 --- a/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart +++ b/packages/stream_chat_flutter/lib/src/channel_media_display_screen.dart @@ -33,7 +33,7 @@ class ChannelMediaDisplayScreen extends StatefulWidget { } class _ChannelMediaDisplayScreenState extends State { - Map _controllerCache = {}; + Map controllerCache = {}; @override void initState() { @@ -149,12 +149,12 @@ class _ChannelMediaDisplayScreenState extends State { .forEach((e) { VideoPlayerController controller; if (e.type == 'video') { - var cachedController = _controllerCache[e.assetUrl]; + var cachedController = controllerCache[e.assetUrl]; if (cachedController == null) { controller = VideoPlayerController.network(e.assetUrl); controller.initialize(); - _controllerCache[e.assetUrl] = controller; + controllerCache[e.assetUrl] = controller; } else { controller = cachedController; } @@ -234,7 +234,7 @@ class _ChannelMediaDisplayScreenState extends State { @override void dispose() { super.dispose(); - for (var c in _controllerCache.values) { + for (var c in controllerCache.values) { c.dispose(); } } diff --git a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart index a8d7e227..40880dfc 100644 --- a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart @@ -86,7 +86,7 @@ void main() { testWidgets( 'StreamChatCore should disconnect on background', (WidgetTester tester) async { - await fakeAsync((_async) { + fakeAsync((_async) { final client = MockClient(); final clientState = MockClientState(); final channel = MockChannel(); @@ -123,6 +123,7 @@ void main() { _async.elapse(Duration(seconds: 5)); verify(client.disconnect()).called(1); + eventStreamController.close(); }); }, ); @@ -174,6 +175,7 @@ void main() { await untilCalled(showLocalNotificationMock(event)); verify(showLocalNotificationMock(event)).called(1); + eventStreamController.close(); }, ); } From 22fee997e808ad732419222b46111e237523ca09 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 3 Feb 2021 16:49:10 +0530 Subject: [PATCH 16/17] fix: added comment --- packages/stream_chat_flutter/lib/src/message_widget.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 75a04c28..d4a6702a 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -142,6 +142,7 @@ class MessageWidget extends StatefulWidget { /// Function called when quotedMessage is tapped final OnQuotedMessageTap onQuotedMessageTap; + /// The cache for the video controllers of attachments IDed as message ID + attachment index final Map videoPackages; /// From 35657cfbe99cc923ea38ca31593e4a728cf88e5c Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 3 Feb 2021 17:03:04 +0530 Subject: [PATCH 17/17] fix: added final --- packages/stream_chat_flutter/lib/src/message_list_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 5cd945e4..2929c2d7 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -239,7 +239,7 @@ class _MessageListViewState extends State { final MessageListController _messageListController = MessageListController(); - Map videoPackages = {}; + final Map videoPackages = {}; @override Widget build(BuildContext context) {