From 0a384f3c450736c342ca1271784fe1d6b624b84a Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 3 Sep 2021 16:34:47 +0530 Subject: [PATCH 001/148] changed over to the new overlays --- .../stream_chat_flutter/lib/overlays.dart | 140 +++++++++ .../lib/src/message_input.dart | 282 +++++++----------- 2 files changed, 255 insertions(+), 167 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/overlays.dart diff --git a/packages/stream_chat_flutter/lib/overlays.dart b/packages/stream_chat_flutter/lib/overlays.dart new file mode 100644 index 00000000..01210fec --- /dev/null +++ b/packages/stream_chat_flutter/lib/overlays.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; + +class AnchoredOverlay extends StatelessWidget { + final bool showOverlay; + final Widget Function(BuildContext, Offset anchor) overlayBuilder; + final Widget child; + + AnchoredOverlay({ + this.showOverlay = false, + required this.overlayBuilder, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return new Container( + child: new LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return new OverlayBuilder( + showOverlay: showOverlay, + overlayBuilder: (BuildContext overlayContext) { + RenderBox box = context.findRenderObject() as RenderBox; + final center = + box.size.center(box.localToGlobal(const Offset(0.0, 0.0))); + return overlayBuilder(overlayContext, center); + }, + child: child, + ); + }), + ); + } +} + +class OverlayBuilder extends StatefulWidget { + final bool showOverlay; + final Widget Function(BuildContext) overlayBuilder; + final Widget child; + + OverlayBuilder({ + this.showOverlay = false, + required this.overlayBuilder, + required this.child, + }); + + @override + _OverlayBuilderState createState() => new _OverlayBuilderState(); +} + +class _OverlayBuilderState extends State { + OverlayEntry? overlayEntry; + + @override + void initState() { + super.initState(); + + if (widget.showOverlay) { + WidgetsBinding.instance?.addPostFrameCallback((_) => showOverlay()); + } + } + + @override + void didUpdateWidget(OverlayBuilder oldWidget) { + super.didUpdateWidget(oldWidget); + WidgetsBinding.instance + ?.addPostFrameCallback((_) => syncWidgetAndOverlay()); + } + + @override + void reassemble() { + super.reassemble(); + WidgetsBinding.instance + ?.addPostFrameCallback((_) => syncWidgetAndOverlay()); + } + + @override + void dispose() { + if (isShowingOverlay()) { + hideOverlay(); + } + + super.dispose(); + } + + bool isShowingOverlay() => overlayEntry != null; + + void showOverlay() { + overlayEntry = new OverlayEntry( + builder: widget.overlayBuilder, + ); + if (overlayEntry != null) { + addToOverlay(overlayEntry!); + } + } + + void addToOverlay(OverlayEntry entry) async { + print('addToOverlay'); + Overlay.of(context)?.insert(entry); + } + + void hideOverlay() { + print('hideOverlay'); + overlayEntry?.remove(); + overlayEntry = null; + } + + void syncWidgetAndOverlay() { + if (isShowingOverlay() && !widget.showOverlay) { + hideOverlay(); + } else if (!isShowingOverlay() && widget.showOverlay) { + showOverlay(); + } + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} + +class CenterAbout extends StatelessWidget { + final Offset position; + final Widget child; + + CenterAbout({ + required this.position, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return new Positioned( + top: position.dy, + left: position.dx, + child: new FractionalTranslation( + translation: const Offset(-0.5, -0.5), + child: child, + ), + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 651a204c..c51cab9c 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -11,6 +11,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:stream_chat_flutter/overlays.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; @@ -318,7 +319,7 @@ class MessageInputState extends State { late final FocusNode _focusNode; bool _inputEnabled = true; bool _commandEnabled = false; - OverlayEntry? _commandsOverlay, _mentionsOverlay, _emojiOverlay; + Widget? _commandsOverlay, _mentionsOverlay, _emojiOverlay; late Iterable _emojiNames; Command? _chosenCommand; @@ -469,7 +470,21 @@ class MessageInputState extends State { child: child, ); } - return child; + return AnchoredOverlay( + showOverlay: _commandsOverlay != null || + _emojiOverlay != null || + _mentionsOverlay != null, + overlayBuilder: (context, offset) { + if (_commandsOverlay != null) { + return _buildCommandsOverlayEntry(); + } else if (_emojiOverlay != null) { + return _buildEmojiOverlay(); + } else { + return _buildMentionsOverlayEntry(); + } + }, + child: child, + ); } Flex _buildTextField(BuildContext context) => Flex( @@ -811,11 +826,8 @@ class MessageInputState extends State { 1); }); - _commandsOverlay?.remove(); _commandsOverlay = null; - _mentionsOverlay?.remove(); _mentionsOverlay = null; - _emojiOverlay?.remove(); _emojiOverlay = null; _checkCommands(s.trim(), context); @@ -861,9 +873,9 @@ class MessageInputState extends State { } else { _emojiOverlay = _buildEmojiOverlay(); - if (_emojiOverlay != null) { - Overlay.of(context)!.insert(_emojiOverlay!); - } + // if (_emojiOverlay != null) { + // Overlay.of(context)!.insert(_emojiOverlay!); + // } } } } @@ -877,9 +889,6 @@ class MessageInputState extends State { .last .contains('@')) { _mentionsOverlay = _buildMentionsOverlayEntry(); - if (_mentionsOverlay != null) { - Overlay.of(context)!.insert(_mentionsOverlay!); - } } } @@ -899,18 +908,14 @@ class MessageInputState extends State { setState(() { _commandEnabled = true; }); - _commandsOverlay?.remove(); _commandsOverlay = null; } else { _commandsOverlay = _buildCommandsOverlayEntry(); - if (_commandsOverlay != null) { - Overlay.of(context)!.insert(_commandsOverlay!); - } } } } - OverlayEntry? _buildCommandsOverlayEntry() { + Widget _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); final commands = StreamChannel.of(context) .channel @@ -920,15 +925,7 @@ class MessageInputState extends State { .toList() ?? []; - if (commands.isEmpty) { - return null; - } - - // ignore: cast_nullable_to_non_nullable - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; - - final child = Padding( + return Padding( padding: const EdgeInsets.all(8), child: Card( elevation: 2, @@ -1020,22 +1017,6 @@ class MessageInputState extends State { ), ), ); - return OverlayEntry( - builder: (context) => Positioned( - bottom: size.height + MediaQuery.of(context).viewInsets.bottom, - left: 0, - right: 0, - child: TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutExpo, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), - child: child, - ), - )); } Widget _buildFilePickerSection() { @@ -1323,7 +1304,7 @@ class MessageInputState extends State { } } - OverlayEntry? _buildMentionsOverlayEntry() { + Widget _buildMentionsOverlayEntry() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split('@'); @@ -1343,10 +1324,6 @@ class MessageInputState extends State { .toList() ?? []; - if (members.isEmpty) { - return null; - } - // ignore: cast_nullable_to_non_nullable final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; @@ -1396,7 +1373,6 @@ class MessageInputState extends State { ), ); _debounce!.cancel(); - _mentionsOverlay?.remove(); _mentionsOverlay = null; }, child: widget.mentionsTileBuilder != null @@ -1414,133 +1390,119 @@ class MessageInputState extends State { ), ), ); - return OverlayEntry( - builder: (context) => Positioned( - bottom: size.height + MediaQuery.of(context).viewInsets.bottom, - left: 0, - right: 0, - child: TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutExpo, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), + return Positioned( + bottom: size.height + MediaQuery.of(context).viewInsets.bottom, + left: 0, + right: 0, + child: TweenAnimationBuilder( + tween: Tween(begin: 0, end: 1), + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOutExpo, + builder: (context, val, child) => Transform.scale( + scale: val, child: child, ), + child: child, ), ); } - OverlayEntry? _buildEmojiOverlay() { + Widget _buildEmojiOverlay() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split(':'); final query = splits.last.toLowerCase(); - if (query.isEmpty) { - return null; - } - final emojis = _emojiNames .where((e) => e.contains(query)) .map(Emoji.byName) .where((e) => e != null); - if (emojis.isEmpty) { - return null; - } - // ignore: cast_nullable_to_non_nullable final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; - return OverlayEntry( - builder: (context) => Positioned( - bottom: size.height + MediaQuery.of(context).viewInsets.bottom, - left: 0, - right: 0, - child: Card( - margin: const EdgeInsets.all(8), - elevation: 2, - color: _streamChatTheme.colorTheme.barsBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(const Size.fromHeight(200)), - decoration: BoxDecoration( - boxShadow: const [ - BoxShadow( - spreadRadius: -8, - blurRadius: 5, - offset: Offset(0, -4), - ), - ], - color: _streamChatTheme.colorTheme.barsBg, - ), - child: ListView.builder( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - itemCount: emojis.length + 1, - itemBuilder: (context, i) { - if (i == 0) { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 8), - child: Row( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8), - child: StreamSvgIcon.smile( - color: _streamChatTheme - .colorTheme.accentPrimary, - ), - ), - Flexible( - child: Text( - context.translations.emojiMatchingQueryText( - query, - ), - style: TextStyle( - color: _streamChatTheme - .colorTheme.textHighEmphasis - .withOpacity(.5), - ), - ), - ) - ], - ), - ); - } - - final emoji = emojis.elementAt(i - 1)!; - final themeData = Theme.of(context); - return ListTile( - title: SubstringHighlight( - text: - // ignore: lines_longer_than_80_chars - "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", - term: query, - textStyleHighlight: - themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - fontWeight: FontWeight.bold, - ), - textStyle: themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - ), - ), - onTap: () { - _chooseEmoji(splits, emoji); - }, - ); - }), - ), + return Positioned( + bottom: size.height + MediaQuery.of(context).viewInsets.bottom, + left: 0, + right: 0, + child: Card( + margin: const EdgeInsets.all(8), + elevation: 2, + color: _streamChatTheme.colorTheme.barsBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(const Size.fromHeight(200)), + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + spreadRadius: -8, + blurRadius: 5, + offset: Offset(0, -4), ), - )); + ], + color: _streamChatTheme.colorTheme.barsBg, + ), + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + itemCount: emojis.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + return Padding( + padding: const EdgeInsets.only(left: 8, top: 8), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: StreamSvgIcon.smile( + color: _streamChatTheme.colorTheme.accentPrimary, + ), + ), + Flexible( + child: Text( + context.translations.emojiMatchingQueryText( + query, + ), + style: TextStyle( + color: _streamChatTheme.colorTheme.textHighEmphasis + .withOpacity(.5), + ), + ), + ) + ], + ), + ); + } + + final emoji = emojis.elementAt(i - 1)!; + final themeData = Theme.of(context); + return ListTile( + title: SubstringHighlight( + text: + // ignore: lines_longer_than_80_chars + "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", + term: query, + textStyleHighlight: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + textStyle: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + ), + ), + onTap: () { + _chooseEmoji(splits, emoji); + }, + ); + }, + ), + ), + ), + ); } void _chooseEmoji(List splits, Emoji emoji) { @@ -1555,7 +1517,6 @@ class MessageInputState extends State { ), ); - _emojiOverlay?.remove(); _emojiOverlay = null; } @@ -1565,7 +1526,6 @@ class MessageInputState extends State { _chosenCommand = c; _commandEnabled = true; }); - _commandsOverlay?.remove(); _commandsOverlay = null; } @@ -1778,13 +1738,9 @@ class MessageInputState extends State { if (_commandsOverlay == null) { setState(() { _commandsOverlay = _buildCommandsOverlayEntry(); - if (_commandsOverlay != null) { - Overlay.of(context)!.insert(_commandsOverlay!); - } }); } else { setState(() { - _commandsOverlay?.remove(); _commandsOverlay = null; }); } @@ -1809,11 +1765,8 @@ class MessageInputState extends State { ), splashRadius: 24, onPressed: () async { - _emojiOverlay?.remove(); _emojiOverlay = null; - _commandsOverlay?.remove(); _commandsOverlay = null; - _mentionsOverlay?.remove(); _mentionsOverlay = null; if (_openFilePickerSection) { @@ -2100,9 +2053,7 @@ class MessageInputState extends State { _commandEnabled = false; }); - _commandsOverlay?.remove(); _commandsOverlay = null; - _mentionsOverlay?.remove(); _mentionsOverlay = null; Message message; @@ -2246,9 +2197,6 @@ class MessageInputState extends State { @override void dispose() { - _commandsOverlay?.remove(); - _emojiOverlay?.remove(); - _mentionsOverlay?.remove(); _keyboardListener?.cancel(); textEditingController.dispose(); _stopSlowMode(); From ae2e8b740fe76b7ea5615ad60212d84420e4b9ba Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 6 Sep 2021 16:46:15 +0530 Subject: [PATCH 002/148] corrected overlay positions --- .../lib/src/message_input.dart | 182 +++++++++--------- .../lib/{ => src}/overlays.dart | 39 ++-- 2 files changed, 112 insertions(+), 109 deletions(-) rename packages/stream_chat_flutter/lib/{ => src}/overlays.dart (79%) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index c51cab9c..cf118b7f 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -11,7 +11,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/overlays.dart'; +import 'package:stream_chat_flutter/src/overlays.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; @@ -475,13 +475,20 @@ class MessageInputState extends State { _emojiOverlay != null || _mentionsOverlay != null, overlayBuilder: (context, offset) { + late Widget child; + if (_commandsOverlay != null) { - return _buildCommandsOverlayEntry(); + child = _buildCommandsOverlayEntry(); } else if (_emojiOverlay != null) { - return _buildEmojiOverlay(); + child = _buildEmojiOverlay(); } else { - return _buildMentionsOverlayEntry(); + child = _buildMentionsOverlayEntry(); } + + return CenterAbout( + position: Offset(offset.dx, offset.dy), + child: child, + ); }, child: child, ); @@ -924,9 +931,10 @@ class MessageInputState extends State { .where((c) => c.name.contains(text.replaceFirst('/', ''))) .toList() ?? []; + final size = MediaQuery.of(context).size; return Padding( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.all(4), child: Card( elevation: 2, shape: RoundedRectangleBorder( @@ -935,7 +943,7 @@ class MessageInputState extends State { color: _streamChatTheme.colorTheme.barsBg, clipBehavior: Clip.hardEdge, child: Container( - constraints: BoxConstraints.loose(const Size.fromHeight(400)), + constraints: BoxConstraints.loose(Size(size.width - 16, 400)), decoration: BoxDecoration( color: _streamChatTheme.colorTheme.barsBg, borderRadius: BorderRadius.circular(8)), @@ -1336,7 +1344,7 @@ class MessageInputState extends State { ), clipBehavior: Clip.hardEdge, child: Container( - constraints: BoxConstraints.loose(const Size.fromHeight(240)), + constraints: BoxConstraints.loose(Size(size.width - 16, 200)), decoration: BoxDecoration( color: _streamChatTheme.colorTheme.barsBg, ), @@ -1390,20 +1398,15 @@ class MessageInputState extends State { ), ), ); - return Positioned( - bottom: size.height + MediaQuery.of(context).viewInsets.bottom, - left: 0, - right: 0, - child: TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutExpo, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), + return TweenAnimationBuilder( + tween: Tween(begin: 0, end: 1), + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOutExpo, + builder: (context, val, child) => Transform.scale( + scale: val, child: child, ), + child: child, ); } @@ -1422,84 +1425,79 @@ class MessageInputState extends State { final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; - return Positioned( - bottom: size.height + MediaQuery.of(context).viewInsets.bottom, - left: 0, - right: 0, - child: Card( - margin: const EdgeInsets.all(8), - elevation: 2, - color: _streamChatTheme.colorTheme.barsBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), + return Card( + margin: const EdgeInsets.all(8), + elevation: 2, + color: _streamChatTheme.colorTheme.barsBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(Size(size.width - 16, 200)), + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + spreadRadius: -8, + blurRadius: 5, + offset: Offset(0, -4), + ), + ], + color: _streamChatTheme.colorTheme.barsBg, ), - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(const Size.fromHeight(200)), - decoration: BoxDecoration( - boxShadow: const [ - BoxShadow( - spreadRadius: -8, - blurRadius: 5, - offset: Offset(0, -4), - ), - ], - color: _streamChatTheme.colorTheme.barsBg, - ), - child: ListView.builder( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - itemCount: emojis.length + 1, - itemBuilder: (context, i) { - if (i == 0) { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 8), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: StreamSvgIcon.smile( - color: _streamChatTheme.colorTheme.accentPrimary, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + itemCount: emojis.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + return Padding( + padding: const EdgeInsets.only(left: 8, top: 8), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: StreamSvgIcon.smile( + color: _streamChatTheme.colorTheme.accentPrimary, + ), + ), + Flexible( + child: Text( + context.translations.emojiMatchingQueryText( + query, + ), + style: TextStyle( + color: _streamChatTheme.colorTheme.textHighEmphasis + .withOpacity(.5), ), ), - Flexible( - child: Text( - context.translations.emojiMatchingQueryText( - query, - ), - style: TextStyle( - color: _streamChatTheme.colorTheme.textHighEmphasis - .withOpacity(.5), - ), - ), - ) - ], - ), - ); - } - - final emoji = emojis.elementAt(i - 1)!; - final themeData = Theme.of(context); - return ListTile( - title: SubstringHighlight( - text: - // ignore: lines_longer_than_80_chars - "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", - term: query, - textStyleHighlight: themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - fontWeight: FontWeight.bold, - ), - textStyle: themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - ), + ) + ], ), - onTap: () { - _chooseEmoji(splits, emoji); - }, ); - }, - ), + } + + final emoji = emojis.elementAt(i - 1)!; + final themeData = Theme.of(context); + return ListTile( + title: SubstringHighlight( + text: + // ignore: lines_longer_than_80_chars + "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", + term: query, + textStyleHighlight: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + textStyle: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + ), + ), + onTap: () { + _chooseEmoji(splits, emoji); + }, + ); + }, ), ), ); diff --git a/packages/stream_chat_flutter/lib/overlays.dart b/packages/stream_chat_flutter/lib/src/overlays.dart similarity index 79% rename from packages/stream_chat_flutter/lib/overlays.dart rename to packages/stream_chat_flutter/lib/src/overlays.dart index 01210fec..65956cb9 100644 --- a/packages/stream_chat_flutter/lib/overlays.dart +++ b/packages/stream_chat_flutter/lib/src/overlays.dart @@ -16,17 +16,17 @@ class AnchoredOverlay extends StatelessWidget { return new Container( child: new LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - return new OverlayBuilder( - showOverlay: showOverlay, - overlayBuilder: (BuildContext overlayContext) { - RenderBox box = context.findRenderObject() as RenderBox; - final center = + return new OverlayBuilder( + showOverlay: showOverlay, + overlayBuilder: (BuildContext overlayContext) { + RenderBox box = context.findRenderObject() as RenderBox; + final center = box.size.center(box.localToGlobal(const Offset(0.0, 0.0))); - return overlayBuilder(overlayContext, center); - }, - child: child, - ); - }), + return overlayBuilder(overlayContext, center); + }, + child: child, + ); + }), ); } } @@ -84,7 +84,7 @@ class _OverlayBuilderState extends State { bool isShowingOverlay() => overlayEntry != null; void showOverlay() { - overlayEntry = new OverlayEntry( + overlayEntry = OverlayEntry( builder: widget.overlayBuilder, ); if (overlayEntry != null) { @@ -128,13 +128,18 @@ class CenterAbout extends StatelessWidget { @override Widget build(BuildContext context) { - return new Positioned( - top: position.dy, + var size = MediaQuery.of(context).size; + + return Positioned( + bottom: size.height - position.dy, left: position.dx, - child: new FractionalTranslation( - translation: const Offset(-0.5, -0.5), - child: child, + child: FractionalTranslation( + translation: const Offset(-0.5, 0.0), + child: Padding( + padding: const EdgeInsets.only(bottom: 32.0), + child: child, + ), ), ); } -} \ No newline at end of file +} From 4ddb0703b30cfc3b255d815304b95fb090281ed1 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 6 Sep 2021 19:35:04 +0530 Subject: [PATCH 003/148] fix(llc, ui): message search pagination Signed-off-by: xsahil03x --- .../lib/src/core/api/general_api.dart | 4 ++ .../lib/src/core/api/requests.dart | 16 +++++-- .../lib/src/core/api/requests.g.dart | 2 + .../lib/src/core/api/responses.dart | 6 +++ .../lib/src/core/api/responses.g.dart | 4 +- .../test/src/core/api/general_api_test.dart | 18 +++++++ .../test/src/core/api/requests_test.dart | 22 +++++++-- .../lib/src/message_search_bloc.dart | 27 +++++++++-- .../lib/src/message_search_list_core.dart | 47 ++++++++++++++----- .../lib/src/dao/channel_query_dao.dart | 5 +- .../lib/src/dao/message_dao.dart | 5 +- 11 files changed, 127 insertions(+), 29 deletions(-) diff --git a/packages/stream_chat/lib/src/core/api/general_api.dart b/packages/stream_chat/lib/src/core/api/general_api.dart index c6243148..fa08a475 100644 --- a/packages/stream_chat/lib/src/core/api/general_api.dart +++ b/packages/stream_chat/lib/src/core/api/general_api.dart @@ -36,6 +36,10 @@ class GeneralApi { PaginationParams? pagination, Filter? messageFilters, }) async { + assert( + pagination?.offset == null || pagination?.offset == 0 || sort == null, + 'Cannot specify `offset` with `sort` parameter', + ); assert(() { if (query == null && messageFilters == null) { throw ArgumentError('Provide at least `query` or `messageFilters`'); diff --git a/packages/stream_chat/lib/src/core/api/requests.dart b/packages/stream_chat/lib/src/core/api/requests.dart index 29112d72..0bc49fc7 100644 --- a/packages/stream_chat/lib/src/core/api/requests.dart +++ b/packages/stream_chat/lib/src/core/api/requests.dart @@ -60,12 +60,16 @@ class PaginationParams extends Equatable { /// ``` const PaginationParams({ this.limit = 10, - this.offset = 0, + this.offset, + this.next, this.greaterThan, this.greaterThanOrEqual, this.lessThan, this.lessThanOrEqual, - }); + }) : assert( + offset == null || offset == 0 || next == null, + 'Cannot specify non-zero `offset` with `next` parameter', + ); /// Create a new instance from a json factory PaginationParams.fromJson(Map json) => @@ -75,7 +79,10 @@ class PaginationParams extends Equatable { final int limit; /// The offset of requesting items. - final int offset; + final int? offset; + + /// A key used to paginate. + final String? next; /// Filter on ids greater than the given value. @JsonKey(name: 'id_gt') @@ -100,6 +107,7 @@ class PaginationParams extends Equatable { PaginationParams copyWith({ int? limit, int? offset, + String? next, String? greaterThan, String? greaterThanOrEqual, String? lessThan, @@ -108,6 +116,7 @@ class PaginationParams extends Equatable { PaginationParams( limit: limit ?? this.limit, offset: offset ?? this.offset, + next: next ?? this.next, greaterThan: greaterThan ?? this.greaterThan, greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual, lessThan: lessThan ?? this.lessThan, @@ -118,6 +127,7 @@ class PaginationParams extends Equatable { List get props => [ limit, offset, + next, greaterThan, greaterThanOrEqual, lessThan, diff --git a/packages/stream_chat/lib/src/core/api/requests.g.dart b/packages/stream_chat/lib/src/core/api/requests.g.dart index 6cd935b8..fd0ac624 100644 --- a/packages/stream_chat/lib/src/core/api/requests.g.dart +++ b/packages/stream_chat/lib/src/core/api/requests.g.dart @@ -23,6 +23,7 @@ PaginationParams _$PaginationParamsFromJson(Map json) { return PaginationParams( limit: json['limit'] as int, offset: json['offset'] as int, + next: json['next'] as String?, greaterThan: json['id_gt'] as String?, greaterThanOrEqual: json['id_gte'] as String?, lessThan: json['id_lt'] as String?, @@ -42,6 +43,7 @@ Map _$PaginationParamsToJson(PaginationParams instance) { } } + writeNotNull('next', instance.next); writeNotNull('id_gt', instance.greaterThan); writeNotNull('id_gte', instance.greaterThanOrEqual); writeNotNull('id_lt', instance.lessThan); diff --git a/packages/stream_chat/lib/src/core/api/responses.dart b/packages/stream_chat/lib/src/core/api/responses.dart index 033e91b2..5b52b65d 100644 --- a/packages/stream_chat/lib/src/core/api/responses.dart +++ b/packages/stream_chat/lib/src/core/api/responses.dart @@ -253,6 +253,12 @@ class SearchMessagesResponse extends _BaseResponse { @JsonKey(defaultValue: []) late List results; + /// Message id of where to start searching from for next [results] + late String? next; + + /// Message id of where to start searching from for previous [results] + late String? previous; + /// Create a new instance from a json static SearchMessagesResponse fromJson(Map json) => _$SearchMessagesResponseFromJson(json); diff --git a/packages/stream_chat/lib/src/core/api/responses.g.dart b/packages/stream_chat/lib/src/core/api/responses.g.dart index 15b64a05..10d96682 100644 --- a/packages/stream_chat/lib/src/core/api/responses.g.dart +++ b/packages/stream_chat/lib/src/core/api/responses.g.dart @@ -161,7 +161,9 @@ SearchMessagesResponse _$SearchMessagesResponseFromJson( ..results = (json['results'] as List?) ?.map((e) => GetMessageResponse.fromJson(e as Map)) .toList() ?? - []; + [] + ..next = json['next'] as String? + ..previous = json['previous'] as String?; } GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson( diff --git a/packages/stream_chat/test/src/core/api/general_api_test.dart b/packages/stream_chat/test/src/core/api/general_api_test.dart index afb3f6aa..570d7c77 100644 --- a/packages/stream_chat/test/src/core/api/general_api_test.dart +++ b/packages/stream_chat/test/src/core/api/general_api_test.dart @@ -86,6 +86,24 @@ void main() { }, ); + test( + 'should throw if `pagination.offset` and `sort` both are provided', + () async { + final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']); + const sort = [SortOption('test-field')]; + const pagination = PaginationParams(offset: 10); + try { + await generalApi.searchMessages( + filter, + sort: sort, + pagination: pagination, + ); + } catch (e) { + expect(e, isA()); + } + }, + ); + test('should run successfully with `query`', () async { final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']); const query = 'test-query'; diff --git a/packages/stream_chat/test/src/core/api/requests_test.dart b/packages/stream_chat/test/src/core/api/requests_test.dart index 76253403..876a3f61 100644 --- a/packages/stream_chat/test/src/core/api/requests_test.dart +++ b/packages/stream_chat/test/src/core/api/requests_test.dart @@ -9,11 +9,23 @@ void main() { expect(j, {'field': 'name', 'direction': -1}); }); - test('PaginationParams', () { - const option = PaginationParams(); - final j = option.toJson(); - expect(j, containsPair('limit', 10)); - expect(j, containsPair('offset', 0)); + group('PaginationParams', () { + test('default', () { + const option = PaginationParams(); + final j = option.toJson(); + expect(j, containsPair('limit', 10)); + }); + + test( + 'should throw if non-zero `offset` and `next` both are provided', + () { + try { + PaginationParams(offset: 10, next: 'next-message-id'); + } catch (e) { + expect(e, isA()); + } + }, + ); }); }); } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index 44ac9b3f..d9d5933d 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -43,6 +43,9 @@ class MessageSearchBlocState extends State with AutomaticKeepAliveClientMixin { late StreamChatCoreState _streamChatCoreState; + String? nextId; + String? previousId; + /// The current messages list List? get messageResponses => _messageResponses.valueOrNull; @@ -76,11 +79,17 @@ class MessageSearchBlocState extends State _queryMessagesLoadingController.add(true); } try { - final clear = pagination == null || pagination.offset == 0; + var clear = pagination == null; + if (sort != null) { + clear |= pagination?.next == null; + } else { + final offset = pagination?.offset; + clear |= offset == null || offset == 0; + } final oldMessages = List.from(messageResponses ?? []); - final messages = await client.search( + final response = await client.search( filter, sort: sort, query: query, @@ -88,10 +97,20 @@ class MessageSearchBlocState extends State messageFilters: messageFilter, ); + final next = response.next; + final previous = response.previous; + + nextId = next != null && next.isNotEmpty + ? next + : /*reset nextId if we get nothing*/ null; + previousId = previous != null && previous.isNotEmpty + ? previous + : /*reset previousId if we get nothing*/ null; + if (clear) { - _messageResponses.add(messages.results); + _messageResponses.add(response.results); } else { - final temp = oldMessages + messages.results; + final temp = oldMessages + response.results; _messageResponses.add(temp); } if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 4901c332..2e9f6c61 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -37,7 +37,7 @@ class MessageSearchListCore extends StatefulWidget { /// * [errorBuilder] /// * [loadingBuilder] /// * [childBuilder] - const MessageSearchListCore({ + MessageSearchListCore({ Key? key, required this.emptyBuilder, required this.errorBuilder, @@ -49,7 +49,21 @@ class MessageSearchListCore extends StatefulWidget { this.paginationParams, this.messageFilters, this.messageSearchListController, - }) : super(key: key); + }) : assert( + messageQuery != null || messageFilters != null, + 'Provide at least `query` or `messageFilters`', + ), + assert( + messageQuery == null || messageFilters == null, + "Can't provide both `query` and `messageFilters` at the same time", + ), + assert( + paginationParams?.offset == null || + paginationParams?.offset == 0 || + sortOptions == null, + 'Cannot specify `offset` with `sortOptions` parameter', + ), + super(key: key); /// A [MessageSearchListController] allows reloading and pagination. /// Use [MessageSearchListController.loadData] and @@ -73,9 +87,8 @@ class MessageSearchListCore extends StatefulWidget { final List? sortOptions; /// Pagination parameters - /// limit: the number of users to return (max is 30) + /// limit: the number of messages to return (max is 30) /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel final PaginationParams? paginationParams; /// The message query filters to use. @@ -159,15 +172,25 @@ class MessageSearchListCoreState extends State { ); /// Fetches more messages with updated pagination and updates the widget - Future paginateData() => _messageSearchBloc!.search( - filter: widget.filters, - sort: widget.sortOptions, - pagination: widget.paginationParams!.copyWith( - offset: _messageSearchBloc!.messageResponses?.length ?? 0, - ), - query: widget.messageQuery, - messageFilter: widget.messageFilters, + Future paginateData() { + PaginationParams? pagination; + if (widget.sortOptions != null) { + pagination = widget.paginationParams?.copyWith( + next: _messageSearchBloc?.nextId, ); + } else { + pagination = widget.paginationParams?.copyWith( + offset: _messageSearchBloc?.messageResponses?.length, + ); + } + return _messageSearchBloc!.search( + filter: widget.filters, + sort: widget.sortOptions, + pagination: pagination, + query: widget.messageQuery, + messageFilter: widget.messageFilters, + ); + } @override void didUpdateWidget(MessageSearchListCore oldWidget) { diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart index 9100e76d..c1781732 100644 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart @@ -113,8 +113,9 @@ class ChannelQueryDao extends DatabaseAccessor cachedChannels.sort(chainedComparator); - if (paginationParams?.offset != null && cachedChannels.isNotEmpty) { - cachedChannels.removeRange(0, paginationParams!.offset); + final offset = paginationParams?.offset; + if (offset != null && offset > 0 && cachedChannels.isNotEmpty) { + cachedChannels.removeRange(0, offset); } if (paginationParams?.limit != null) { diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart index c2e1cde2..fcedc157 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -117,8 +117,9 @@ class MessageDao extends DatabaseAccessor msgList.removeRange(0, greaterThanIndex); } } - if (options?.limit != null) { - return msgList.take(options!.limit).toList(); + final limit = options?.limit; + if (limit != null && limit > 0) { + return msgList.take(limit).toList(); } } return msgList; From 0af7b1a0e61be679bb63875858ce1dd27990c097 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 7 Sep 2021 14:31:48 +0530 Subject: [PATCH 004/148] added new overlays and docs --- .../lib/src/message_input.dart | 23 +++- .../stream_chat_flutter/lib/src/overlays.dart | 103 +++++++++++------- 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index cf118b7f..60fe49a1 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -475,7 +475,7 @@ class MessageInputState extends State { _emojiOverlay != null || _mentionsOverlay != null, overlayBuilder: (context, offset) { - late Widget child; + late Widget? child; if (_commandsOverlay != null) { child = _buildCommandsOverlayEntry(); @@ -487,7 +487,7 @@ class MessageInputState extends State { return CenterAbout( position: Offset(offset.dx, offset.dy), - child: child, + child: child ?? const SizedBox(), ); }, child: child, @@ -922,7 +922,7 @@ class MessageInputState extends State { } } - Widget _buildCommandsOverlayEntry() { + Widget? _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); final commands = StreamChannel.of(context) .channel @@ -931,6 +931,11 @@ class MessageInputState extends State { .where((c) => c.name.contains(text.replaceFirst('/', ''))) .toList() ?? []; + + if (commands.isEmpty) { + return null; + } + final size = MediaQuery.of(context).size; return Padding( @@ -1312,7 +1317,7 @@ class MessageInputState extends State { } } - Widget _buildMentionsOverlayEntry() { + Widget? _buildMentionsOverlayEntry() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split('@'); @@ -1332,6 +1337,10 @@ class MessageInputState extends State { .toList() ?? []; + if (members.isEmpty) { + return null; + } + // ignore: cast_nullable_to_non_nullable final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; @@ -1410,7 +1419,7 @@ class MessageInputState extends State { ); } - Widget _buildEmojiOverlay() { + Widget? _buildEmojiOverlay() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split(':'); @@ -1421,6 +1430,10 @@ class MessageInputState extends State { .map(Emoji.byName) .where((e) => e != null); + if (emojis.isEmpty) { + return null; + } + // ignore: cast_nullable_to_non_nullable final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; diff --git a/packages/stream_chat_flutter/lib/src/overlays.dart b/packages/stream_chat_flutter/lib/src/overlays.dart index 65956cb9..ce6a9a4e 100644 --- a/packages/stream_chat_flutter/lib/src/overlays.dart +++ b/packages/stream_chat_flutter/lib/src/overlays.dart @@ -1,49 +1,66 @@ import 'package:flutter/material.dart'; +/// Class to create an overlay anchored to a child widget class AnchoredOverlay extends StatelessWidget { - final bool showOverlay; - final Widget Function(BuildContext, Offset anchor) overlayBuilder; - final Widget child; - - AnchoredOverlay({ + /// Constructor for creating an [AnchoredOverlay] + const AnchoredOverlay({ + Key? key, this.showOverlay = false, required this.overlayBuilder, required this.child, - }); + }) : super(key: key); + + /// Show or hide overlay + final bool showOverlay; + + /// Builder for creating overlay widget + final Widget Function(BuildContext, Offset anchor) overlayBuilder; + + /// Child to which overlay is anchored + final Widget child; @override - Widget build(BuildContext context) { - return new Container( - child: new LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return new OverlayBuilder( - showOverlay: showOverlay, - overlayBuilder: (BuildContext overlayContext) { - RenderBox box = context.findRenderObject() as RenderBox; - final center = - box.size.center(box.localToGlobal(const Offset(0.0, 0.0))); - return overlayBuilder(overlayContext, center); - }, - child: child, - ); - }), - ); - } + Widget build(BuildContext context) => SizedBox( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => + OverlayBuilder( + showOverlay: showOverlay, + overlayBuilder: (BuildContext overlayContext) { + final box = context.findRenderObject() as RenderBox?; + if (box != null) { + final center = box.size + .center(box.localToGlobal(const Offset(0, 0))); + return overlayBuilder(overlayContext, center); + } else { + return const SizedBox(); + } + }, + child: child, + )), + ); } +/// Class for creating a declarative-ish overlay class OverlayBuilder extends StatefulWidget { - final bool showOverlay; - final Widget Function(BuildContext) overlayBuilder; - final Widget child; - - OverlayBuilder({ + /// Constructor for creating an [OverlayBuilder] + const OverlayBuilder({ + Key? key, this.showOverlay = false, required this.overlayBuilder, required this.child, - }); + }) : super(key: key); + + /// Show or hide overlay + final bool showOverlay; + + /// Builder for creating overlay widget + final Widget Function(BuildContext) overlayBuilder; + + /// Child to which overlay is anchored + final Widget child; @override - _OverlayBuilderState createState() => new _OverlayBuilderState(); + _OverlayBuilderState createState() => _OverlayBuilderState(); } class _OverlayBuilderState extends State { @@ -112,31 +129,35 @@ class _OverlayBuilderState extends State { } @override - Widget build(BuildContext context) { - return widget.child; - } + Widget build(BuildContext context) => widget.child; } +/// Class to position Overlay class CenterAbout extends StatelessWidget { - final Offset position; - final Widget child; - - CenterAbout({ + /// Constructor for creating an [CenterAbout] + const CenterAbout({ + Key? key, required this.position, required this.child, - }); + }) : super(key: key); + + /// Position of child + final Offset position; + + /// Child widget to which overlay is attached + final Widget child; @override Widget build(BuildContext context) { - var size = MediaQuery.of(context).size; + final size = MediaQuery.of(context).size; return Positioned( bottom: size.height - position.dy, left: position.dx, child: FractionalTranslation( - translation: const Offset(-0.5, 0.0), + translation: const Offset(-0.5, 0), child: Padding( - padding: const EdgeInsets.only(bottom: 32.0), + padding: const EdgeInsets.only(bottom: 32), child: child, ), ), From 14a3e12afcb3af02f70470ce2f21ce6bd1284f0b Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 7 Sep 2021 14:33:06 +0530 Subject: [PATCH 005/148] added changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 02e2b214..0ee6f5d5 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -58,6 +58,7 @@ breakdown: - Fixed date dividers position/alignment in non reversed `MessageListView`. - Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set. - Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when sending a message with no text. +- MessageInput can now be placed in any position on the screen. ## 2.1.2 From 1170a3cb484fe9c3f5d103abe0d208ce66051f97 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 7 Sep 2021 14:57:55 +0530 Subject: [PATCH 006/148] removed comment --- packages/stream_chat_flutter/lib/src/message_input.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 059075ff..48bcf19c 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -882,10 +882,6 @@ class MessageInputState extends State { _chooseEmoji(splits.sublist(0, splits.length - 1), emoji); } else { _emojiOverlay = _buildEmojiOverlay(); - - // if (_emojiOverlay != null) { - // Overlay.of(context)!.insert(_emojiOverlay!); - // } } } } From 21647040e13423bd5b4b8b9089039632d5280ae2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 7 Sep 2021 17:59:56 +0530 Subject: [PATCH 007/148] feat(core, ui): Improve pagination invocation by using paginationEnded flag. Signed-off-by: xsahil03x --- .../lib/src/message_search_list_view.dart | 4 +- .../lib/src/user_list_view.dart | 4 +- .../lib/src/channels_bloc.dart | 3 +- .../lib/src/message_search_bloc.dart | 37 +++-- .../lib/src/message_search_list_core.dart | 18 +-- .../lib/src/user_list_core.dart | 10 +- .../lib/src/users_bloc.dart | 21 ++- .../test/message_search_bloc_test.dart | 140 ++++++++++++++---- .../test/users_bloc_test.dart | 135 ++++++++++++----- 9 files changed, 272 insertions(+), 100 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index 0fd45ea1..f80c688f 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -58,7 +58,7 @@ class MessageSearchListView extends StatefulWidget { required this.filters, this.messageQuery, this.sortOptions, - this.paginationParams, + this.paginationParams = const PaginationParams(limit: 30), this.messageFilters, this.separatorBuilder, this.itemBuilder, @@ -93,7 +93,7 @@ class MessageSearchListView extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams? paginationParams; + final PaginationParams paginationParams; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index eb45f918..4d650192 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -51,7 +51,7 @@ class UserListView extends StatefulWidget { this.filter, this.sort, this.presence, - this.pagination, + this.pagination = const PaginationParams(limit: 30), this.onUserTap, this.onUserLongPress, this.userWidget, @@ -93,7 +93,7 @@ class UserListView extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams? pagination; + final PaginationParams pagination; /// Function called when tapping on a channel /// By default it calls [Navigator.push] building a [MaterialPageRoute] diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 6687eef5..25e31ed7 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -105,7 +105,8 @@ class ChannelsBlocState extends State }) async { final client = _streamChatCoreState!.client; - final clear = paginationParams.offset == 0; + final clear = + paginationParams.offset == null || paginationParams.offset == 0; if (clear && _paginationEnded) { _paginationEnded = false; } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index d9d5933d..c99036a7 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -62,6 +62,8 @@ class MessageSearchBlocState extends State Stream get queryMessagesLoading => _queryMessagesLoadingController.stream; + bool _paginationEnded = false; + /// Calls [StreamChatClient.search] updating /// [messagesStream] and [queryMessagesLoading] stream Future search({ @@ -69,24 +71,31 @@ class MessageSearchBlocState extends State Filter? messageFilter, List? sort, String? query, - PaginationParams? pagination, + PaginationParams pagination = const PaginationParams(limit: 30), }) async { final client = _streamChatCoreState.client; - if (_queryMessagesLoadingController.value == true) return; + var clear = false; + if (sort != null) { + clear |= pagination.next == null; + } else { + final offset = pagination.offset; + clear |= offset == null || offset == 0; + } + + if (clear && _paginationEnded) { + _paginationEnded = false; + } + + if ((!clear && _paginationEnded) || + _queryMessagesLoadingController.value == true) { + return; + } if (_messageResponses.hasValue) { _queryMessagesLoadingController.add(true); } try { - var clear = pagination == null; - if (sort != null) { - clear |= pagination?.next == null; - } else { - final offset = pagination?.offset; - clear |= offset == null || offset == 0; - } - final oldMessages = List.from(messageResponses ?? []); final response = await client.search( @@ -107,15 +116,19 @@ class MessageSearchBlocState extends State ? previous : /*reset previousId if we get nothing*/ null; + final newMessages = response.results; if (clear) { - _messageResponses.add(response.results); + _messageResponses.add(newMessages); } else { - final temp = oldMessages + response.results; + final temp = oldMessages + newMessages; _messageResponses.add(temp); } if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { _queryMessagesLoadingController.add(false); } + if (newMessages.isEmpty || newMessages.length < pagination.limit) { + _paginationEnded = true; + } } catch (e, stk) { // reset loading controller _queryMessagesLoadingController.add(false); diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 47ffe28b..6e3d04fb 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -47,7 +47,7 @@ class MessageSearchListCore extends StatefulWidget { required this.filters, this.messageQuery, this.sortOptions, - this.paginationParams, + this.paginationParams = const PaginationParams(limit: 30), this.messageFilters, this.messageSearchListController, }) : assert( @@ -59,8 +59,8 @@ class MessageSearchListCore extends StatefulWidget { "Can't provide both `query` and `messageFilters` at the same time", ), assert( - paginationParams?.offset == null || - paginationParams?.offset == 0 || + paginationParams.offset == null || + paginationParams.offset == 0 || sortOptions == null, 'Cannot specify `offset` with `sortOptions` parameter', ), @@ -90,7 +90,7 @@ class MessageSearchListCore extends StatefulWidget { /// Pagination parameters /// limit: the number of messages to return (max is 30) /// offset: the offset (max is 1000) - final PaginationParams? paginationParams; + final PaginationParams paginationParams; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. @@ -169,13 +169,13 @@ class MessageSearchListCoreState extends State { /// Fetches more messages with updated pagination and updates the widget Future paginateData() { - PaginationParams? pagination; + PaginationParams pagination; if (widget.sortOptions != null) { - pagination = widget.paginationParams?.copyWith( + pagination = widget.paginationParams.copyWith( next: _messageSearchBloc?.nextId, ); } else { - pagination = widget.paginationParams?.copyWith( + pagination = widget.paginationParams.copyWith( offset: _messageSearchBloc?.messageResponses?.length, ); } @@ -196,8 +196,8 @@ class MessageSearchListCoreState extends State { widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || widget.messageFilters?.toString() != oldWidget.messageFilters?.toString() || - widget.paginationParams?.toJson().toString() != - oldWidget.paginationParams?.toJson().toString()) { + widget.paginationParams.toJson().toString() != + oldWidget.paginationParams.toJson().toString()) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index 57d61fe6..e403be4e 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -66,7 +66,7 @@ class UserListCore extends StatefulWidget { this.filter, this.sort, this.presence, - this.pagination, + this.pagination = const PaginationParams(limit: 30), this.groupAlphabetically = false, this.userListController, }) : super(key: key); @@ -106,7 +106,7 @@ class UserListCore extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams? pagination; + final PaginationParams pagination; /// Set it to true to group users by their first character /// @@ -201,7 +201,7 @@ class UserListCoreState extends State filter: widget.filter, sort: widget.sort, presence: widget.presence, - pagination: widget.pagination!.copyWith( + pagination: widget.pagination.copyWith( offset: _usersBloc!.users?.length ?? 0, ), ); @@ -212,8 +212,8 @@ class UserListCoreState extends State if (widget.filter?.toString() != oldWidget.filter?.toString() || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.presence != oldWidget.presence || - widget.pagination?.toJson().toString() != - oldWidget.pagination?.toJson().toString()) { + widget.pagination.toJson().toString() != + oldWidget.pagination.toJson().toString()) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart index e7916478..c119badc 100644 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart @@ -57,6 +57,8 @@ class UsersBlocState extends State late StreamChatCoreState _streamChatCore; + bool _paginationEnded = false; + /// The Query Users method allows you to search for users and see if they are /// online/offline. /// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart) @@ -64,19 +66,26 @@ class UsersBlocState extends State Filter? filter, List? sort, bool? presence, - PaginationParams? pagination, + PaginationParams pagination = const PaginationParams(limit: 30), }) async { final client = _streamChatCore.client; - if (_queryUsersLoadingController.value == true) return; + final clear = pagination.offset == null || pagination.offset == 0; + + if (clear && _paginationEnded) { + _paginationEnded = false; + } + + if ((!clear && _paginationEnded) || + _queryUsersLoadingController.value == true) { + return; + } if (_usersController.hasValue) { _queryUsersLoadingController.add(true); } try { - final clear = pagination == null || pagination.offset == 0; - final oldUsers = List.from(users ?? []); final usersResponse = await client.queryUsers( @@ -86,6 +95,7 @@ class UsersBlocState extends State pagination: pagination, ); + final newUsers = usersResponse.users; if (clear) { _usersController.add(usersResponse.users); } else { @@ -95,6 +105,9 @@ class UsersBlocState extends State if (_usersController.hasValue && _queryUsersLoadingController.value) { _queryUsersLoadingController.add(false); } + if (newUsers.isEmpty || newUsers.length < pagination.limit) { + _paginationEnded = true; + } } catch (e, stk) { // reset loading controller _queryUsersLoadingController.add(false); diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart index 979ca5b2..c689d90c 100644 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart @@ -31,8 +31,7 @@ void main() { ); testWidgets( - 'messageSearchBlocState.search() should throw if used where ' - 'StreamChat is not present in the widget tree', + '''messageSearchBlocState.search() should throw if used where StreamChat is not present in the widget tree''', (tester) async { const messageSearchBloc = MessageSearchBloc( child: Offstage(), @@ -74,7 +73,10 @@ void main() { messageFilters: any(named: 'messageFilters'), paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(filter: testFilter); @@ -95,8 +97,7 @@ void main() { ); testWidgets( - 'messageSearchBlocState.messagesStream should emit error ' - 'if client.search() throws', + '''messageSearchBlocState.messagesStream should emit error if client.search() throws''', (tester) async { const messageSearchBlocKey = Key('messageSearchBloc'); const childKey = Key('child'); @@ -144,9 +145,7 @@ void main() { ); testWidgets( - 'calling messageSearchBlocState.search() again with an offset ' - 'should emit new data through messagesStream and also emit loading state ' - 'through queryMessagesLoading', + '''calling messageSearchBlocState.search() again with an offset should emit new data through messagesStream and also emit loading state through queryMessagesLoading''', (tester) async { const messageSearchBlocKey = Key('messageSearchBloc'); const childKey = Key('child'); @@ -168,19 +167,23 @@ void main() { find.byKey(messageSearchBlocKey), ); - final messageResponseList = _generateMessages(); + const pagination = PaginationParams(limit: 25); + final messageResponseList = _generateMessages(count: 25); when(() => mockClient.search( testFilter, query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), + paginationParams: pagination, )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); - messageSearchBlocState.search(filter: testFilter); + messageSearchBlocState.search(pagination: pagination, filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, @@ -192,22 +195,24 @@ void main() { query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), + paginationParams: pagination, )).called(1); final offset = messageResponseList.length; final paginatedMessageResponseList = _generateMessages(offset: offset); - final pagination = PaginationParams(offset: offset); + final newPagination = pagination.copyWith(offset: offset); when(() => mockClient.search( testFilter, query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, + paginationParams: newPagination, )).thenAnswer( - (_) async => - SearchMessagesResponse()..results = paginatedMessageResponseList, + (_) async => SearchMessagesResponse() + ..results = paginatedMessageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(pagination: pagination, filter: testFilter); @@ -236,9 +241,7 @@ void main() { ); testWidgets( - 'calling messageSearchBlocState.search() again with an offset ' - 'should emit error through queryUsersLoading if ' - 'client.search() throws', + '''calling messageSearchBlocState.search() again with an offset should emit error through queryUsersLoading if client.search() throws''', (tester) async { const messageSearchBlocKey = Key('messageSearchBloc'); const childKey = Key('child'); @@ -260,19 +263,23 @@ void main() { find.byKey(messageSearchBlocKey), ); - final messageResponseList = _generateMessages(); + const pagination = PaginationParams(limit: 25); + final messageResponseList = _generateMessages(count: 25); when(() => mockClient.search( testFilter, query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), + paginationParams: pagination, )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); - messageSearchBlocState.search(filter: testFilter); + messageSearchBlocState.search(pagination: pagination, filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, @@ -284,11 +291,11 @@ void main() { query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: any(named: 'paginationParams'), + paginationParams: pagination, )).called(1); final offset = messageResponseList.length; - final pagination = PaginationParams(offset: offset); + final newPagination = pagination.copyWith(offset: offset); const error = 'Error! Error! Error!'; when(() => mockClient.search( @@ -296,10 +303,13 @@ void main() { query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, + paginationParams: newPagination, )).thenThrow(error); - messageSearchBlocState.search(pagination: pagination, filter: testFilter); + messageSearchBlocState.search( + pagination: newPagination, + filter: testFilter, + ); await expectLater( messageSearchBlocState.queryMessagesLoading, @@ -311,8 +321,80 @@ void main() { query: any(named: 'query'), sort: any(named: 'sort'), messageFilters: any(named: 'messageFilters'), - paginationParams: pagination, + paginationParams: newPagination, )).called(1); }, ); + + testWidgets( + '''calling messageSearchBlocState.search() again with an offset should do nothing and return if pagination is completed''', + (tester) async { + const messageSearchBlocKey = Key('messageSearchBloc'); + const childKey = Key('child'); + const messageSearchBloc = MessageSearchBloc( + key: messageSearchBlocKey, + child: Offstage(key: childKey), + ); + + final mockClient = MockClient(); + + await tester.pumpWidget( + StreamChatCore( + client: mockClient, + child: messageSearchBloc, + ), + ); + + final messageSearchBlocState = tester.state( + find.byKey(messageSearchBlocKey), + ); + + const pagination = PaginationParams(limit: 25); + + final messageResponseList = _generateMessages(count: 20); + + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, + ); + + messageSearchBlocState.search(pagination: pagination, filter: testFilter); + + await expectLater( + messageSearchBlocState.messagesStream, + emits(isSameMessageResponseListAs(messageResponseList)), + ); + + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); + + final offset = messageResponseList.length; + final newPagination = pagination.copyWith(offset: offset); + + messageSearchBlocState.search( + filter: testFilter, + pagination: newPagination, + ); + + // should emit nothing. + await expectLater( + // skipping the initial data (behaviorSubject). + messageSearchBlocState.messagesStream.skip(1), + emitsInOrder([]), + ); + }, + ); } diff --git a/packages/stream_chat_flutter_core/test/users_bloc_test.dart b/packages/stream_chat_flutter_core/test/users_bloc_test.dart index af088a76..549f9291 100644 --- a/packages/stream_chat_flutter_core/test/users_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/users_bloc_test.dart @@ -164,16 +164,17 @@ void main() { find.byKey(usersBlocKey), ); - final users = _generateUsers(); + const pagination = PaginationParams(limit: 25); + final users = _generateUsers(count: 25); when(() => mockClient.queryUsers( filter: any(named: 'filter'), sort: any(named: 'sort'), presence: any(named: 'presence'), - pagination: any(named: 'pagination'), + pagination: pagination, )).thenAnswer((_) async => QueryUsersResponse()..users = users); - usersBlocState.queryUsers(); + usersBlocState.queryUsers(pagination: pagination); await expectLater( usersBlocState.usersStream, @@ -184,23 +185,23 @@ void main() { filter: any(named: 'filter'), sort: any(named: 'sort'), presence: any(named: 'presence'), - pagination: any(named: 'pagination'), + pagination: pagination, )).called(1); final offset = users.length; final paginatedUsers = _generateUsers(offset: offset); - final pagination = PaginationParams(offset: offset); + final newPagination = pagination.copyWith(offset: offset); when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )) - .thenAnswer( - (_) async => QueryUsersResponse()..users = paginatedUsers); + filter: any(named: 'filter'), + sort: any(named: 'sort'), + presence: any(named: 'presence'), + pagination: newPagination, + )).thenAnswer( + (_) async => QueryUsersResponse()..users = paginatedUsers, + ); - usersBlocState.queryUsers(pagination: pagination); + usersBlocState.queryUsers(pagination: newPagination); await Future.wait([ expectLater( @@ -217,7 +218,7 @@ void main() { filter: any(named: 'filter'), sort: any(named: 'sort'), presence: any(named: 'presence'), - pagination: pagination, + pagination: newPagination, )).called(1); }, ); @@ -247,13 +248,89 @@ void main() { find.byKey(usersBlocKey), ); - final users = _generateUsers(); + const pagination = PaginationParams(limit: 25); + final users = _generateUsers(count: 25); when(() => mockClient.queryUsers( filter: any(named: 'filter'), sort: any(named: 'sort'), presence: any(named: 'presence'), - pagination: any(named: 'pagination'), + pagination: pagination, + )).thenAnswer((_) async => QueryUsersResponse()..users = users); + + usersBlocState.queryUsers(pagination: pagination); + + await expectLater( + usersBlocState.usersStream, + emits(isSameUserListAs(users)), + ); + + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + presence: any(named: 'presence'), + pagination: pagination, + )).called(1); + + final offset = users.length; + final newPagination = pagination.copyWith(offset: offset); + + const error = 'Error! Error! Error!'; + + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + presence: any(named: 'presence'), + pagination: newPagination, + )).thenThrow(error); + + usersBlocState.queryUsers(pagination: newPagination); + + await expectLater( + usersBlocState.queryUsersLoading, + emitsError(error), + ); + + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + presence: any(named: 'presence'), + pagination: newPagination, + )).called(1); + }, + ); + + testWidgets( + '''calling usersBlocState.queryUsers() again with an offset should do nothing and return if pagination is completed''', + (tester) async { + const usersBlocKey = Key('usersBloc'); + const childKey = Key('child'); + const usersBloc = UsersBloc( + key: usersBlocKey, + child: Offstage(key: childKey), + ); + + final mockClient = MockClient(); + + await tester.pumpWidget( + StreamChatCore( + client: mockClient, + child: usersBloc, + ), + ); + + final usersBlocState = tester.state( + find.byKey(usersBlocKey), + ); + + const pagination = PaginationParams(limit: 30); + final users = _generateUsers(count: 25); + + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + presence: any(named: 'presence'), + pagination: pagination, )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -271,30 +348,16 @@ void main() { )).called(1); final offset = users.length; - final pagination = PaginationParams(offset: offset); + final newPagination = pagination.copyWith(offset: offset); - const error = 'Error! Error! Error!'; - - when(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).thenThrow(error); - - usersBlocState.queryUsers(pagination: pagination); + usersBlocState.queryUsers(pagination: newPagination); + // should emit nothing. await expectLater( - usersBlocState.queryUsersLoading, - emitsError(error), + // skipping the initial data (behaviorSubject). + usersBlocState.usersStream, + emitsInOrder([]), ); - - verify(() => mockClient.queryUsers( - filter: any(named: 'filter'), - sort: any(named: 'sort'), - presence: any(named: 'presence'), - pagination: pagination, - )).called(1); }, ); } From d9de5e15ada2bd299bb2b2edd0c1035b63ab6e39 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 7 Sep 2021 19:45:31 +0530 Subject: [PATCH 008/148] test(core): fix message search list core tests Signed-off-by: xsahil03x --- .../lib/src/message_search_list_core.dart | 8 +- .../test/message_search_list_core_test.dart | 103 ++++++++++++++---- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 47ffe28b..4cc6c46b 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -38,7 +38,7 @@ class MessageSearchListCore extends StatefulWidget { /// * [errorBuilder] /// * [loadingBuilder] /// * [childBuilder] - MessageSearchListCore({ + const MessageSearchListCore({ Key? key, required this.emptyBuilder, required this.errorBuilder, @@ -58,12 +58,6 @@ class MessageSearchListCore extends StatefulWidget { messageQuery == null || messageFilters == null, "Can't provide both `query` and `messageFilters` at the same time", ), - assert( - paginationParams?.offset == null || - paginationParams?.offset == 0 || - sortOptions == null, - 'Cannot specify `offset` with `sortOptions` parameter', - ), super(key: key); /// A [MessageSearchListController] allows reloading and pagination. diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index 6b700c76..2c398013 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -7,6 +7,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; const testFilter = Filter.custom(operator: '\$test', value: 'testValue'); +const testMessageFilter = Filter.custom(operator: '\$test', value: 'testValue'); void main() { List _generateMessages({ @@ -28,6 +29,40 @@ void main() { }, ); + testWidgets( + 'should throw if both `messageQuery` and `messageFilters` are provided', + (tester) async { + expect( + () => MessageSearchListCore( + childBuilder: (_) => const Offstage(), + loadingBuilder: (_) => const Offstage(), + emptyBuilder: (_) => const Offstage(), + errorBuilder: (_, __) => const Offstage(), + filters: testFilter, + messageFilters: testMessageFilter, + messageQuery: 'test', + ), + throwsAssertionError, + ); + }, + ); + + testWidgets( + 'should throw if both `messageQuery` and `messageFilters` are not provided', + (tester) async { + expect( + () => MessageSearchListCore( + childBuilder: (_) => const Offstage(), + loadingBuilder: (_) => const Offstage(), + emptyBuilder: (_) => const Offstage(), + errorBuilder: (_, __) => const Offstage(), + filters: testFilter, + ), + throwsAssertionError, + ); + }, + ); + testWidgets( 'should throw if MessageSearchListCore is used where MessageSearchBloc ' 'is not present in the widget tree', @@ -40,6 +75,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object? error) => const Offstage(), filters: testFilter, + messageFilters: testMessageFilter, ); await tester.pumpWidget(messageSearchListCore); @@ -61,6 +97,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object? error) => const Offstage(), filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -92,6 +129,7 @@ void main() { errorBuilder: (BuildContext context, Object error) => const Offstage(), messageSearchListController: controller, filters: testFilter, + messageFilters: testMessageFilter, ); expect(controller.loadData, isNull); @@ -129,6 +167,7 @@ void main() { key: errorWidgetKey, ), filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -138,7 +177,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).thenThrow(error); @@ -159,7 +198,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).called(1); }, @@ -179,6 +218,7 @@ void main() { const Offstage(key: emptyWidgetKey), errorBuilder: (BuildContext context, Object error) => const Offstage(), filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -188,10 +228,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); await tester.pumpWidget( @@ -211,7 +254,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).called(1); }, @@ -231,6 +274,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -240,10 +284,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); await tester.pumpWidget( @@ -263,7 +310,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: any(named: 'paginationParams'), )).called(1); }, @@ -289,6 +336,7 @@ void main() { errorBuilder: (BuildContext context, Object error) => const Offstage(), paginationParams: pagination, filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -298,10 +346,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: pagination, )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); await tester.pumpWidget( @@ -332,7 +383,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: pagination, )).called(1); @@ -348,11 +399,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: updatedPagination, )).thenAnswer( - (_) async => - SearchMessagesResponse()..results = paginatedMessageResponseList, + (_) async => SearchMessagesResponse() + ..results = paginatedMessageResponseList + ..next = null + ..previous = null, ); await messageSearchListCoreState.paginateData(); @@ -372,7 +425,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: updatedPagination, )).called(1); }, @@ -406,6 +459,7 @@ void main() { const Offstage(), paginationParams: pagination.copyWith(limit: limit), filters: testFilter, + messageFilters: testMessageFilter, ); final mockClient = MockClient(); @@ -415,10 +469,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: pagination, )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); await tester.pumpWidget( @@ -453,7 +510,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: pagination, )).called(1); @@ -466,11 +523,13 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: updatedPagination, )).thenAnswer( - (_) async => - SearchMessagesResponse()..results = updatedMessageResponseList, + (_) async => SearchMessagesResponse() + ..results = updatedMessageResponseList + ..next = null + ..previous = null, ); await tester.pumpAndSettle(); @@ -487,7 +546,7 @@ void main() { testFilter, query: any(named: 'query'), sort: any(named: 'sort'), - messageFilters: any(named: 'messageFilters'), + messageFilters: testMessageFilter, paginationParams: updatedPagination, )).called(1); }, From 3069f8762935a1f972c3298deed678ed054bc735 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 7 Sep 2021 20:20:49 +0530 Subject: [PATCH 009/148] test(core): fix tests Signed-off-by: xsahil03x --- .../lib/src/channels_bloc.dart | 3 ++- .../lib/src/users_bloc.dart | 4 +++- .../test/message_search_bloc_test.dart | 21 ++++++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 6687eef5..1aed7198 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -105,7 +105,8 @@ class ChannelsBlocState extends State }) async { final client = _streamChatCoreState!.client; - final clear = paginationParams.offset == 0; + final offset = paginationParams.offset; + final clear = offset == null || offset == 0; if (clear && _paginationEnded) { _paginationEnded = false; } diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart index e7916478..f2ec10d9 100644 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart @@ -75,7 +75,9 @@ class UsersBlocState extends State } try { - final clear = pagination == null || pagination.offset == 0; + final clear = pagination == null || + pagination.offset == null || + pagination.offset == 0; final oldUsers = List.from(users ?? []); diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart index 979ca5b2..3e56ca76 100644 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart @@ -74,7 +74,10 @@ void main() { messageFilters: any(named: 'messageFilters'), paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(filter: testFilter); @@ -177,7 +180,10 @@ void main() { messageFilters: any(named: 'messageFilters'), paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(filter: testFilter); @@ -206,8 +212,10 @@ void main() { messageFilters: any(named: 'messageFilters'), paginationParams: pagination, )).thenAnswer( - (_) async => - SearchMessagesResponse()..results = paginatedMessageResponseList, + (_) async => SearchMessagesResponse() + ..results = paginatedMessageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(pagination: pagination, filter: testFilter); @@ -269,7 +277,10 @@ void main() { messageFilters: any(named: 'messageFilters'), paginationParams: any(named: 'paginationParams'), )).thenAnswer( - (_) async => SearchMessagesResponse()..results = messageResponseList, + (_) async => SearchMessagesResponse() + ..results = messageResponseList + ..next = null + ..previous = null, ); messageSearchBlocState.search(filter: testFilter); From da475285fcd4d7459c98e8a96b4c327a3858bb91 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 7 Sep 2021 21:08:34 +0530 Subject: [PATCH 010/148] test(core): fix tests Signed-off-by: xsahil03x --- .../test/message_search_list_core_test.dart | 4 ++-- .../stream_chat_flutter_core/test/user_list_core_test.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index 2c398013..03396513 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -322,7 +322,7 @@ void main() { (tester) async { const messageSearchListCoreKey = Key('messageSearchListCore'); const childWidgetKey = Key('childWidget'); - const pagination = PaginationParams(); + const pagination = PaginationParams(limit: 25); final messageSearchListCore = MessageSearchListCore( key: messageSearchListCoreKey, childBuilder: (List messages) => Container( @@ -341,7 +341,7 @@ void main() { final mockClient = MockClient(); - final messageResponseList = _generateMessages(); + final messageResponseList = _generateMessages(count: 25); when(() => mockClient.search( testFilter, query: any(named: 'query'), diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index eac8dc71..77977bb8 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -321,7 +321,7 @@ void main() { (tester) async { const userListCoreKey = Key('userListCore'); const listWidgetKey = Key('listWidget'); - const pagination = PaginationParams(); + const pagination = PaginationParams(limit: 15); final userListCore = UserListCore( key: userListCoreKey, listBuilder: (_, items) => Container( @@ -347,7 +347,7 @@ void main() { final mockClient = MockClient(); - final users = _generateUsers(); + final users = _generateUsers(count: 15); when(() => mockClient.queryUsers( filter: any(named: 'filter'), sort: any(named: 'sort'), From 01e259a6ae141139e354d1b27ac04edec756074c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 7 Sep 2021 21:12:33 +0530 Subject: [PATCH 011/148] test(llc): fix tests Signed-off-by: xsahil03x --- .../test/src/theme/message_search_list_view_theme_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/test/src/theme/message_search_list_view_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_search_list_view_theme_test.dart index 4da1c7dc..69c63abe 100644 --- a/packages/stream_chat_flutter/test/src/theme/message_search_list_view_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/message_search_list_view_theme_test.dart @@ -69,6 +69,7 @@ void main() { body: MessageSearchBloc( child: MessageSearchListView( filters: Filter.in_('members', const ['test_id']), + messageQuery: 'test query', ), ), ); @@ -100,6 +101,7 @@ void main() { body: MessageSearchBloc( child: MessageSearchListView( filters: Filter.in_('members', const ['test_id']), + messageQuery: 'test query', ), ), ); From b5f40dd417cc7eac42b0bb73142df3b20e73f134 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 15:23:55 +0530 Subject: [PATCH 012/148] chore(ui, core): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 100 +++++++++--------- .../stream_chat_flutter_core/CHANGELOG.md | 30 +++++- 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 02e2b214..ed8f5e06 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,5 +1,22 @@ ## 2.2.1 +🛑️ Breaking Changes from `2.2.1` + +- `MessageSearchListView` paginationParams property is now non-nullable with a default value. + ```dart + paginationParams = const PaginationParams(limit: 30) + ``` +- `UserListView` pagination property is now non-nullable with a default value. + ```dart + pagination = const PaginationParams(limit: 30) + ``` + +🐞 Fixed + +- Fixed `MessageSearchListView` pagination. + +## 2.2.1 + - Updated `stream_chat_flutter_core` dependency to 2.2.1 ## 2.2.0 @@ -7,14 +24,13 @@ ✅ Added - [#516](https://github.com/GetStream/stream-chat-flutter/issues/516): - Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image - is loading + Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image is loading - Added a `backgroundColor` property to the following widgets: - - `ChannelHeader` - - `ChannelListHeader` - - `GalleryHeader` - - `GalleryFooter` - - `ThreadHeader` + - `ChannelHeader` + - `ChannelListHeader` + - `GalleryHeader` + - `GalleryFooter` + - `ThreadHeader` - Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message. - Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded. This will override the default error alert behaviour. @@ -34,9 +50,8 @@ You can call `.copyWith` to customize just a subset of properties. 🔄 Changed -Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with -them, and have been upgraded with some goodies like `lerp` functions. Here's the full naming -breakdown: +Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with them, and have been +upgraded with some goodies like `lerp` functions. Here's the full naming breakdown: * `AvatarTheme` is now `AvatarThemeData` * `ChannelHeaderTheme` is now `ChannelHeaderThemeData` @@ -53,18 +68,18 @@ breakdown: 🐞 Fixed -- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the - camera is null. +- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the camera is null. - Fixed date dividers position/alignment in non reversed `MessageListView`. - Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set. -- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when sending a message with no text. +- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when + sending a message with no text. ## 2.1.2 🐞 Fixed -- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no - members when sending message +- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no members when sending + message ## 2.1.1 @@ -82,8 +97,7 @@ breakdown: 🔄 Changed - `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`. -- `StreamChat.of(context).userStream` is now deprecated in favor - of `StreamChat.of(context).currentUserStream`. +- `StreamChat.of(context).userStream` is now deprecated in favor of `StreamChat.of(context).currentUserStream`. 🐞 Fixed @@ -136,8 +150,7 @@ You can call `.copyWith` to customize just a subset of properties - Added video compress options (frame and quality) to `MessageInput` - TypingIndicator now has a property called `parentId` to show typing indicator specific to threads -- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView - header/footer +- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer - `MessageWidget` accepts a `userAvatarBuilder` - Added pinMessage ui support - Added `MessageListView.threadSeparatorBuilder` property @@ -146,12 +159,10 @@ You can call `.copyWith` to customize just a subset of properties 🐞 Fixed -- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text - box when editing message -- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator - use case -- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without - a reload +- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing + message +- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case +- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload - `MessageListView` not rendering if the user is not a member of the channel - Fix `MessageInput` overflow when there are no actions - Minor fixes and improvements @@ -204,18 +215,15 @@ You can call `.copyWith` to customize just a subset of properties. ✅ Added - TypingIndicator now has a property called `parentId` to show typing indicator specific to threads -- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView - header/footer +- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer - `MessageWidget` accepts a `userAvatarBuilder` 🐞 Fixed -- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text - box when editing message -- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator - use case -- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without - a reload +- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing + message +- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case +- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload - `MessageListView` not rendering if the user is not a member of the channel ## 2.0.0-nullsafety.7 @@ -285,8 +293,7 @@ You can call `.copyWith` to customize just a subset of properties. - Show error messages as system and keep them in the message input - Remove notification badge logic - Use shimmer while loading images -- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated - to `MessageInput` +- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated to `MessageInput` - Add possibility to specify custom message actions using `MessageWidget.customActions` - Added `MessageListView.onAttachmentTap` callback - Fixed message newline issue @@ -343,8 +350,7 @@ You can call `.copyWith` to customize just a subset of properties. - Improved api documentation - Updated `stream_chat` dependency to `^1.0.0-beta` - Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples) -- Reimplemented existing widgets - using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core) +- Reimplemented existing widgets using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core) ## 0.2.21 @@ -361,8 +367,8 @@ You can call `.copyWith` to customize just a subset of properties. ## 0.2.20+2 -- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the - list when a new message arrives +- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message + arrives ## 0.2.20+1 @@ -396,8 +402,7 @@ You can call `.copyWith` to customize just a subset of properties. ## 0.2.16 -- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress - implementation +- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress implementation - Make public autofocus field of the TextField of message_input ## 0.2.15 @@ -582,11 +587,10 @@ You can call `.copyWith` to customize just a subset of properties. ## 0.2.1-alpha+1 -- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have - the `StreamChat` widget as ancestor in every route. Now the recommended way to add `StreamChat` to - your app is using the `builder` property of your `MaterialApp` widget. Otherwise you can use it in - the usual way, but you need to add a `StreamChat` widget to every route of your app. - Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more +- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have the `StreamChat` widget + as ancestor in every route. Now the recommended way to add `StreamChat` to your app is using the `builder` property of + your `MaterialApp` widget. Otherwise you can use it in the usual way, but you need to add a `StreamChat` widget to + every route of your app. Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more information. ```dart @@ -688,8 +692,8 @@ Widget build(BuildContext context) { - Add gesture (vertical drag down) to close the keyboard -- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will - even close the keyboard) +- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will even close the + keyboard) The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261 diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 1378ced0..d0d89a6b 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,20 @@ +## Upcoming + +🛑️ Breaking Changes from `2.2.1` + +- `MessageSearchListViewCore` paginationParams property is now non-nullable with a default value. + ```dart + paginationParams = const PaginationParams(limit: 30) + ``` +- `UserListViewCore` pagination property is now non-nullable with a default value. + ```dart + pagination = const PaginationParams(limit: 30) + ``` + +🐞 Fixed + +- Fixed `MessageSearchBloc` pagination. + ## 2.2.1 - Updated `stream_chat` dependency to 2.2.1 @@ -5,13 +22,17 @@ ## 2.2.0 🛑️ Breaking Changes from `2.1.1` + - Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder` 🔄 Changed + - `BetterStreamBuilder.initialData` is now nullable/not-required. 🐞 Fixed -- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after refresh + +- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after + refresh ## 2.1.1 @@ -20,12 +41,15 @@ ## 2.1.0 🛑️ Breaking Changes from `2.0.0` + - Changed default message filter of `MessageListCore` ✅ Added + - Added `MessageListCore.paginationLimit` 🔄 Changed + - `StreamChatCore.of(context).user` is now deprecated in favor of `StreamChatCore.of(context).currentUser`. - `StreamChatCore.of(context).userStream` is now deprecated in favor of `StreamChatCore.of(context).currentUserStream`. @@ -34,7 +58,8 @@ 🛑️ Breaking Changes from `1.5.3` - migrate this package to null safety -- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of individual params/properties +- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of individual + params/properties - `options.state` -> bool state - `options.watch` -> bool watch - `options.presence` -> bool presence @@ -51,6 +76,7 @@ - Performance improvements ## 2.0.0-nullsafety.9 + - Update llc dependency ## 2.0.0-nullsafety.8 From e27ec9edb71913211678e46e180dae6b917ac00e Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 15:25:21 +0530 Subject: [PATCH 013/148] chore(core): add doc comments Signed-off-by: xsahil03x --- .../stream_chat_flutter_core/lib/src/message_search_bloc.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index d9d5933d..cd4fa9f7 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -43,7 +43,10 @@ class MessageSearchBlocState extends State with AutomaticKeepAliveClientMixin { late StreamChatCoreState _streamChatCoreState; + /// The key used to paginate next items. String? nextId; + + /// The key used to paginate previous items. String? previousId; /// The current messages list From f37412059957a5b96169ee67a97fa27d60304804 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 15:47:52 +0530 Subject: [PATCH 014/148] chore(llc): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 67a80e17..f54ab8d3 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +✅ Added + +- Add support for `next`, `previous` value pagination in `client.search`. + ## 2.2.1 🐞 Fixed @@ -13,11 +19,16 @@ ✅ Added -- `User` and `OwnUser` classes now have an `image` property. Setting an image will also set the 'image' key on `extraData`, so `user.image` and `user.extraData['image']` is the same. -- `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name' key on `extraData`, so `user.name` and `user.extraData['name']` is the same. -- `Channel` class now has extra `image` getter and setter. As well as an `updateImage` to do a partial update after a channel has been initialized. -- `Channel` class now has extra `name` getter and setter. As well as an `updateName` to do a partial update after a channel has been initialized. +- `User` and `OwnUser` classes now have an `image` property. Setting an image will also set the 'image' key + on `extraData`, so `user.image` and `user.extraData['image']` is the same. +- `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name' key on `extraData`, + so `user.name` and `user.extraData['name']` is the same. +- `Channel` class now has extra `image` getter and setter. As well as an `updateImage` to do a partial update after a + channel has been initialized. +- `Channel` class now has extra `name` getter and setter. As well as an `updateName` to do a partial update after a + channel has been initialized. - Added slow mode which allows a cooldown period after a user sends a message. + ## 2.1.1 🐞 Fixed @@ -42,7 +53,7 @@ 🐞 Fixed -- [#563](https://github.com/GetStream/stream-chat-flutter/issues/563): `Channel.stopWatching()` not working +- [#563](https://github.com/GetStream/stream-chat-flutter/issues/563): `Channel.stopWatching()` not working - [#575](https://github.com/GetStream/stream-chat-flutter/issues/575): Wrong `OwnUser.*` ## 2.0.0 @@ -70,11 +81,11 @@ 🐞 Fixed -- [#369](https://github.com/GetStream/stream-chat-flutter/issues/369): Client does not return without internet connection +- [#369](https://github.com/GetStream/stream-chat-flutter/issues/369): Client does not return without internet + connection - several minor fixes - performance improvements - ✅ Added - New `Location` enum is introduced for easily changing the client location/baseUrl. @@ -89,6 +100,7 @@ ## 2.0.0-nullsafety.8 🐞 Fixed + - Export `PushProvider` enum ## 2.0.0-nullsafety.7 @@ -124,6 +136,7 @@ - Fix thread reply not working with attachments - Minor fixes + ## 2.0.0-nullsafety.5 - Minor fixes From 977add6ab9fbb57c4cafaf75c347f55613ecaf45 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 15:49:47 +0530 Subject: [PATCH 015/148] chore(llc): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index f54ab8d3..1f7d7bce 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -2,7 +2,7 @@ ✅ Added -- Add support for `next`, `previous` value pagination in `client.search`. +- Add support for `next`, `previous` value pagination in `client.search`, [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination) ## 2.2.1 From b3fa3aea454d2c03f4e85ebc72f3331d14eee8d6 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 8 Sep 2021 16:04:27 +0530 Subject: [PATCH 016/148] added new overlays and replaced impls in message_input.dart --- .../lib/src/commands_overlay.dart | 204 +++++++++ .../lib/src/emoji_overlay.dart | 115 +++++ .../lib/src/mentions_overlay.dart | 104 +++++ .../lib/src/message_input.dart | 403 ++---------------- 4 files changed, 457 insertions(+), 369 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/src/commands_overlay.dart create mode 100644 packages/stream_chat_flutter/lib/src/emoji_overlay.dart create mode 100644 packages/stream_chat_flutter/lib/src/mentions_overlay.dart diff --git a/packages/stream_chat_flutter/lib/src/commands_overlay.dart b/packages/stream_chat_flutter/lib/src/commands_overlay.dart new file mode 100644 index 00000000..8e236c35 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/commands_overlay.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; + +class CommandsOverlay extends StatelessWidget { + final BuildContext context; + final String text; + final ValueChanged onCommandResult; + + const CommandsOverlay( + this.context, { + required this.text, + required this.onCommandResult, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext otherContext) { + var _streamChatTheme = StreamChatTheme.of(context); + final commands = StreamChannel.of(context) + .channel + .config + ?.commands + .where((c) => c.name.contains(text.replaceFirst('/', ''))) + .toList() ?? + []; + + if (commands.isEmpty) { + return const SizedBox(); + } + + final size = MediaQuery.of(context).size; + + return Padding( + padding: const EdgeInsets.all(4), + child: Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + color: _streamChatTheme.colorTheme.barsBg, + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(Size(size.width - 16, 400)), + decoration: BoxDecoration( + color: _streamChatTheme.colorTheme.barsBg, + borderRadius: BorderRadius.circular(8)), + child: ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + children: [ + if (commands.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, + ), + child: StreamSvgIcon.lightning( + color: _streamChatTheme.colorTheme.accentPrimary, + ), + ), + Text( + context.translations.instantCommandsLabel, + style: TextStyle( + color: _streamChatTheme.colorTheme.textHighEmphasis + .withOpacity(.5), + ), + ) + ], + ), + ), + const SizedBox( + height: 10, + ), + ...commands + .map( + (c) => InkWell( + onTap: () { + onCommandResult(c); + }, + child: SizedBox( + height: 40, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + _buildCommandIcon(_streamChatTheme, c.name), + const SizedBox( + width: 8, + ), + Text.rich( + TextSpan( + text: c.name.capitalize(), + style: const TextStyle( + fontWeight: FontWeight.bold), + children: [ + TextSpan( + text: ' /${c.name} ${c.args}', + style: _streamChatTheme.textTheme.body + .copyWith( + // ignore: lines_longer_than_80_chars + color: _streamChatTheme + // ignore: lines_longer_than_80_chars + .colorTheme + .textLowEmphasis, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ) + .toList(), + ], + ), + ), + ), + ); + } + + Widget _buildCommandIcon( + StreamChatThemeData _streamChatTheme, String iconType) { + switch (iconType) { + case 'giphy': + return CircleAvatar( + radius: 12, + child: StreamSvgIcon.giphyIcon( + size: 24, + ), + ); + case 'ban': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.iconUserDelete( + size: 16, + color: Colors.white, + ), + ); + case 'flag': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.flag( + size: 14, + color: Colors.white, + ), + ); + case 'imgur': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: ClipOval( + child: StreamSvgIcon.imgur( + size: 24, + ), + ), + ); + case 'mute': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.mute( + size: 16, + color: Colors.white, + ), + ); + case 'unban': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.userAdd( + size: 16, + color: Colors.white, + ), + ); + case 'unmute': + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.volumeUp( + size: 16, + color: Colors.white, + ), + ); + default: + return CircleAvatar( + backgroundColor: _streamChatTheme.colorTheme.accentPrimary, + radius: 12, + child: StreamSvgIcon.lightning( + size: 16, + color: Colors.white, + ), + ); + } + } +} diff --git a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart new file mode 100644 index 00000000..292e8230 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter/src/emoji/emoji.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:substring_highlight/substring_highlight.dart'; + +class EmojiOverlay extends StatelessWidget { + final BuildContext context; + final String query; + final ValueChanged onEmojiResult; + + const EmojiOverlay( + this.context, { + required this.query, + required this.onEmojiResult, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext otherContext) { + var _streamChatTheme = StreamChatTheme.of(context); + var _emojiNames = + Emoji.all().where((it) => it.name != null).map((e) => e.name!); + + final emojis = _emojiNames + .where((e) => e.contains(query)) + .map(Emoji.byName) + .where((e) => e != null); + + if (emojis.isEmpty) { + return const SizedBox(); + } + + // ignore: cast_nullable_to_non_nullable + final renderBox = context.findRenderObject() as RenderBox; + final size = renderBox.size; + + return Card( + margin: const EdgeInsets.all(8), + elevation: 2, + color: _streamChatTheme.colorTheme.barsBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(Size(size.width - 16, 200)), + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + spreadRadius: -8, + blurRadius: 5, + offset: Offset(0, -4), + ), + ], + color: _streamChatTheme.colorTheme.barsBg, + ), + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + itemCount: emojis.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + return Padding( + padding: const EdgeInsets.only(left: 8, top: 8), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: StreamSvgIcon.smile( + color: _streamChatTheme.colorTheme.accentPrimary, + ), + ), + Flexible( + child: Text( + context.translations.emojiMatchingQueryText( + query, + ), + style: TextStyle( + color: _streamChatTheme.colorTheme.textHighEmphasis + .withOpacity(.5), + ), + ), + ) + ], + ), + ); + } + + final emoji = emojis.elementAt(i - 1)!; + final themeData = Theme.of(context); + return ListTile( + title: SubstringHighlight( + text: + // ignore: lines_longer_than_80_chars + "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", + term: query, + textStyleHighlight: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + textStyle: themeData.textTheme.headline6!.copyWith( + fontSize: 14.5, + ), + ), + onTap: () { + onEmojiResult(emoji); + }, + ); + }, + ), + ), + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart new file mode 100644 index 00000000..83dd6ab2 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +class MentionsOverlay extends StatelessWidget { + final BuildContext context; + final String query; + final ValueChanged onMentionResult; + + /// Customize the tile for the mentions overlay + final MentionTileBuilder? mentionsTileBuilder; + + const MentionsOverlay( + this.context, { + required this.query, + required this.onMentionResult, + this.mentionsTileBuilder, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext otherContext) { + var _streamChatTheme = StreamChatTheme.of(context); + + Future>? queryMembers; + + final channelState = StreamChannel.of(context); + if (query.isNotEmpty) { + queryMembers = channelState.channel + .queryMembers(filter: Filter.autoComplete('name', query)) + .then((res) => res.members); + } + + final members = channelState.channel.state?.members + .where((m) => m.user?.name.toLowerCase().contains(query) == true) + .toList() ?? + []; + + if (members.isEmpty) { + return const SizedBox(); + } + + // ignore: cast_nullable_to_non_nullable + final renderBox = context.findRenderObject() as RenderBox; + final size = renderBox.size; + final child = Card( + margin: const EdgeInsets.all(8), + elevation: 2, + color: _streamChatTheme.colorTheme.barsBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(Size(size.width - 16, 200)), + decoration: BoxDecoration( + color: _streamChatTheme.colorTheme.barsBg, + ), + child: FutureBuilder>( + future: queryMembers ?? Future.value(members), + initialData: members, + builder: (context, snapshot) => ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + children: [ + const SizedBox( + height: 8, + ), + ...snapshot.data! + .where((it) => it.user != null) + .map( + (m) => Material( + color: _streamChatTheme.colorTheme.barsBg, + child: InkWell( + onTap: () { + onMentionResult(m.user); + }, + child: mentionsTileBuilder != null + ? mentionsTileBuilder!(context, m) + : MentionTile(m), + ), + ), + ) + .toList(), + const SizedBox( + height: 8, + ), + ], + ), + ), + ), + ); + return TweenAnimationBuilder( + tween: Tween(begin: 0, end: 1), + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOutExpo, + builder: (context, val, child) => Transform.scale( + scale: val, + child: child, + ), + child: child, + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 48bcf19c..f150d09d 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -11,6 +11,9 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:stream_chat_flutter/src/commands_overlay.dart'; +import 'package:stream_chat_flutter/src/emoji_overlay.dart'; +import 'package:stream_chat_flutter/src/mentions_overlay.dart'; import 'package:stream_chat_flutter/src/overlays.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; @@ -23,7 +26,6 @@ import 'package:stream_chat_flutter/src/video_service.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -import 'package:substring_highlight/substring_highlight.dart'; import 'package:video_compress/video_compress.dart'; export 'package:video_compress/video_compress.dart' show VideoQuality; @@ -323,7 +325,6 @@ class MessageInputState extends State { bool _inputEnabled = true; bool _commandEnabled = false; Widget? _commandsOverlay, _mentionsOverlay, _emojiOverlay; - late Iterable _emojiNames; Command? _chosenCommand; bool _actionsShrunk = false; @@ -347,8 +348,6 @@ class MessageInputState extends State { void initState() { super.initState(); _focusNode = widget.focusNode ?? FocusNode(); - _emojiNames = - Emoji.all().where((it) => it.name != null).map((e) => e.name!); if (!kIsWeb) { _keyboardListener = @@ -478,7 +477,7 @@ class MessageInputState extends State { _emojiOverlay != null || _mentionsOverlay != null, overlayBuilder: (context, offset) { - late Widget? child; + late Widget child; if (_commandsOverlay != null) { child = _buildCommandsOverlayEntry(); @@ -490,7 +489,7 @@ class MessageInputState extends State { return CenterAbout( position: Offset(offset.dx, offset.dy), - child: child ?? const SizedBox(), + child: child, ); }, child: child, @@ -921,114 +920,10 @@ class MessageInputState extends State { } } - Widget? _buildCommandsOverlayEntry() { + Widget _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); - final commands = StreamChannel.of(context) - .channel - .config - ?.commands - .where((c) => c.name.contains(text.replaceFirst('/', ''))) - .toList() ?? - []; - if (commands.isEmpty) { - return null; - } - - final size = MediaQuery.of(context).size; - - return Padding( - padding: const EdgeInsets.all(4), - child: Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - color: _streamChatTheme.colorTheme.barsBg, - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 400)), - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - borderRadius: BorderRadius.circular(8)), - child: ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: [ - if (commands.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: 8), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - ), - child: StreamSvgIcon.lightning( - color: _streamChatTheme.colorTheme.accentPrimary, - ), - ), - Text( - context.translations.instantCommandsLabel, - style: TextStyle( - color: _streamChatTheme.colorTheme.textHighEmphasis - .withOpacity(.5), - ), - ) - ], - ), - ), - const SizedBox( - height: 10, - ), - ...commands - .map( - (c) => InkWell( - onTap: () { - _setCommand(c); - }, - child: SizedBox( - height: 40, - child: Row( - children: [ - const SizedBox( - width: 16, - ), - _buildCommandIcon(c.name), - const SizedBox( - width: 8, - ), - Text.rich( - TextSpan( - text: c.name.capitalize(), - style: const TextStyle( - fontWeight: FontWeight.bold), - children: [ - TextSpan( - text: ' /${c.name} ${c.args}', - style: _streamChatTheme.textTheme.body - .copyWith( - // ignore: lines_longer_than_80_chars - color: _streamChatTheme - // ignore: lines_longer_than_80_chars - .colorTheme - .textLowEmphasis, - ), - ), - ], - ), - ), - ], - ), - ), - ), - ) - .toList(), - ], - ), - ), - ), - ); + return CommandsOverlay(context, text: text, onCommandResult: _setCommand); } Widget _buildFilePickerSection() { @@ -1240,278 +1135,48 @@ class MessageInputState extends State { }); } - Widget _buildCommandIcon(String iconType) { - switch (iconType) { - case 'giphy': - return CircleAvatar( - radius: 12, - child: StreamSvgIcon.giphyIcon( - size: 24, - ), - ); - case 'ban': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.iconUserDelete( - size: 16, - color: Colors.white, - ), - ); - case 'flag': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.flag( - size: 14, - color: Colors.white, - ), - ); - case 'imgur': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: ClipOval( - child: StreamSvgIcon.imgur( - size: 24, - ), - ), - ); - case 'mute': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.mute( - size: 16, - color: Colors.white, - ), - ); - case 'unban': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.userAdd( - size: 16, - color: Colors.white, - ), - ); - case 'unmute': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.volumeUp( - size: 16, - color: Colors.white, - ), - ); - default: - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: StreamSvgIcon.lightning( - size: 16, - color: Colors.white, - ), - ); - } - } - - Widget? _buildMentionsOverlayEntry() { + Widget _buildMentionsOverlayEntry() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split('@'); final query = splits.last.toLowerCase(); - Future>? queryMembers; + return MentionsOverlay(context, query: query, onMentionResult: (user) { + if (user != null) { + _mentionedUsers.add(user); + } - final channelState = StreamChannel.of(context); - if (query.isNotEmpty) { - queryMembers = channelState.channel - .queryMembers(filter: Filter.autoComplete('name', query)) - .then((res) => res.members); - } + splits[splits.length - 1] = user!.name; + final rejoin = splits.join('@'); - final members = channelState.channel.state?.members - .where((m) => m.user?.name.toLowerCase().contains(query) == true) - .toList() ?? - []; - - if (members.isEmpty) { - return null; - } - - // ignore: cast_nullable_to_non_nullable - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; - final child = Card( - margin: const EdgeInsets.all(8), - elevation: 2, - color: _streamChatTheme.colorTheme.barsBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 200)), - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, + textEditingController.value = TextEditingValue( + text: rejoin + + textEditingController.text + .substring(textEditingController.selection.start), + selection: TextSelection.collapsed( + offset: rejoin.length, ), - child: FutureBuilder>( - future: queryMembers ?? Future.value(members), - initialData: members, - builder: (context, snapshot) => ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: [ - const SizedBox( - height: 8, - ), - ...snapshot.data! - .where((it) => it.user != null) - .map( - (m) => Material( - color: _streamChatTheme.colorTheme.barsBg, - child: InkWell( - onTap: () { - if (m.user != null) { - _mentionedUsers.add(m.user!); - } - - splits[splits.length - 1] = m.user!.name; - final rejoin = splits.join('@'); - - textEditingController.value = TextEditingValue( - text: rejoin + - textEditingController.text.substring( - textEditingController.selection.start), - selection: TextSelection.collapsed( - offset: rejoin.length, - ), - ); - _debounce!.cancel(); - _mentionsOverlay = null; - }, - child: widget.mentionsTileBuilder != null - ? widget.mentionsTileBuilder!(context, m) - : MentionTile(m), - ), - ), - ) - .toList(), - const SizedBox( - height: 8, - ), - ], - ), - ), - ), - ); - return TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutExpo, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), - child: child, - ); + ); + _debounce!.cancel(); + setState(() { + _mentionsOverlay = null; + }); + }); } - Widget? _buildEmojiOverlay() { + Widget _buildEmojiOverlay() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split(':'); + final query = splits.last.toLowerCase(); - final emojis = _emojiNames - .where((e) => e.contains(query)) - .map(Emoji.byName) - .where((e) => e != null); - - if (emojis.isEmpty) { - return null; - } - - // ignore: cast_nullable_to_non_nullable - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; - - return Card( - margin: const EdgeInsets.all(8), - elevation: 2, - color: _streamChatTheme.colorTheme.barsBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 200)), - decoration: BoxDecoration( - boxShadow: const [ - BoxShadow( - spreadRadius: -8, - blurRadius: 5, - offset: Offset(0, -4), - ), - ], - color: _streamChatTheme.colorTheme.barsBg, - ), - child: ListView.builder( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - itemCount: emojis.length + 1, - itemBuilder: (context, i) { - if (i == 0) { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 8), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: StreamSvgIcon.smile( - color: _streamChatTheme.colorTheme.accentPrimary, - ), - ), - Flexible( - child: Text( - context.translations.emojiMatchingQueryText( - query, - ), - style: TextStyle( - color: _streamChatTheme.colorTheme.textHighEmphasis - .withOpacity(.5), - ), - ), - ) - ], - ), - ); - } - - final emoji = emojis.elementAt(i - 1)!; - final themeData = Theme.of(context); - return ListTile( - title: SubstringHighlight( - text: - // ignore: lines_longer_than_80_chars - "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", - term: query, - textStyleHighlight: themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - fontWeight: FontWeight.bold, - ), - textStyle: themeData.textTheme.headline6!.copyWith( - fontSize: 14.5, - ), - ), - onTap: () { - _chooseEmoji(splits, emoji); - }, - ); - }, - ), - ), + return EmojiOverlay( + context, + query: query, + onEmojiResult: (emoji) { + _chooseEmoji(splits, emoji); + }, ); } From 2f65a0d480387d1cc27536fcd9d3bb0d24cd6c0c Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 8 Sep 2021 16:12:29 +0530 Subject: [PATCH 017/148] added docs --- .../lib/src/commands_overlay.dart | 17 +++++++++---- .../lib/src/emoji_overlay.dart | 19 ++++++++++----- .../lib/src/mentions_overlay.dart | 24 ++++++++++++------- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/commands_overlay.dart b/packages/stream_chat_flutter/lib/src/commands_overlay.dart index 8e236c35..aec35da9 100644 --- a/packages/stream_chat_flutter/lib/src/commands_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/commands_overlay.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +/// Overlay for displaying commands that can be used class CommandsOverlay extends StatelessWidget { - final BuildContext context; - final String text; - final ValueChanged onCommandResult; - + /// Constructor for creating a [CommandsOverlay] const CommandsOverlay( this.context, { required this.text, @@ -14,9 +12,18 @@ class CommandsOverlay extends StatelessWidget { Key? key, }) : super(key: key); + /// Context of the upper tree + final BuildContext context; + + /// Query for searching commands + final String text; + + /// Callback called when a command is selected + final ValueChanged onCommandResult; + @override Widget build(BuildContext otherContext) { - var _streamChatTheme = StreamChatTheme.of(context); + final _streamChatTheme = StreamChatTheme.of(context); final commands = StreamChannel.of(context) .channel .config diff --git a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart index 292e8230..c3768851 100644 --- a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart @@ -4,11 +4,9 @@ import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:substring_highlight/substring_highlight.dart'; +/// Overlay for displaying emoji that can be used class EmojiOverlay extends StatelessWidget { - final BuildContext context; - final String query; - final ValueChanged onEmojiResult; - + /// Constructor for creating a [EmojiOverlay] const EmojiOverlay( this.context, { required this.query, @@ -16,10 +14,19 @@ class EmojiOverlay extends StatelessWidget { Key? key, }) : super(key: key); + /// Context of the upper tree + final BuildContext context; + + /// Query for searching emoji + final String query; + + /// Callback called when an emoji is selected + final ValueChanged onEmojiResult; + @override Widget build(BuildContext otherContext) { - var _streamChatTheme = StreamChatTheme.of(context); - var _emojiNames = + final _streamChatTheme = StreamChatTheme.of(context); + final _emojiNames = Emoji.all().where((it) => it.name != null).map((e) => e.name!); final emojis = _emojiNames diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart index 83dd6ab2..823e920d 100644 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -1,15 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Overlay for displaying users that can be mentioned class MentionsOverlay extends StatelessWidget { - final BuildContext context; - final String query; - final ValueChanged onMentionResult; - - /// Customize the tile for the mentions overlay - final MentionTileBuilder? mentionsTileBuilder; - + /// Constructor for creating a [MentionsOverlay] const MentionsOverlay( this.context, { required this.query, @@ -18,9 +12,21 @@ class MentionsOverlay extends StatelessWidget { Key? key, }) : super(key: key); + /// Context of the upper tree + final BuildContext context; + + /// Query for searching users + final String query; + + /// Callback called when a user is selected + final ValueChanged onMentionResult; + + /// Customize the tile for the mentions overlay + final MentionTileBuilder? mentionsTileBuilder; + @override Widget build(BuildContext otherContext) { - var _streamChatTheme = StreamChatTheme.of(context); + final _streamChatTheme = StreamChatTheme.of(context); Future>? queryMembers; From f4e37e23d6dbe0b8dfea0727f80eb3a6375a6949 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 16:37:34 +0530 Subject: [PATCH 018/148] fix(llc): Fix unread count not updating when the current user is set. Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 4 ++++ .../stream_chat/lib/src/client/client.dart | 24 +++++++++++-------- .../lib/src/core/models/own_user.dart | 6 ++--- .../lib/src/core/models/own_user.g.dart | 2 +- .../test/src/client/client_test.dart | 22 +++++++++++++++++ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 61f00005..76b65833 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -14,6 +14,10 @@ - Added `Filter.contains` and `Filter.empty` +🐞 Fixed + +- [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not updating correctly. + ## 2.2.1 🐞 Fixed diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index f9d10a92..9253b623 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -1366,16 +1366,7 @@ class ClientState { .where((event) => event.me != null && event.type != EventType.healthCheck) .map((e) => e.me!) - .listen((user) { - currentUser = currentUser?.merge(user) ?? user; - final totalUnreadCount = user.totalUnreadCount; - _totalUnreadCountController.add(totalUnreadCount); - - final unreadChannels = user.unreadChannels; - if (unreadChannels != null) { - _unreadChannelsController.add(unreadChannels); - } - }), + .listen((user) => currentUser = currentUser?.merge(user) ?? user), _client .on() .map((event) => event.unreadChannels) @@ -1441,6 +1432,7 @@ class ClientState { /// Sets the user currently interacting with the client /// note: this fully overrides the [currentUser] set currentUser(OwnUser? user) { + _computeUnreadCounts(user); _currentUserController.add(user); } @@ -1506,6 +1498,18 @@ class ClientState { _channelsController.add(newChannels); } + void _computeUnreadCounts(OwnUser? user) { + final totalUnreadCount = user?.totalUnreadCount; + if (totalUnreadCount != null) { + _totalUnreadCountController.add(totalUnreadCount); + } + + final unreadChannels = user?.unreadChannels; + if (unreadChannels != null) { + _unreadChannelsController.add(unreadChannels); + } + } + final _channelsController = BehaviorSubject>.seeded({}); final _currentUserController = BehaviorSubject(); final _usersController = BehaviorSubject>.seeded({}); diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index a65052f4..07c6cf30 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -17,7 +17,7 @@ class OwnUser extends User { this.devices = const [], this.mutes = const [], this.totalUnreadCount = 0, - this.unreadChannels, + this.unreadChannels = 0, this.channelMutes = const [], required String id, String? role, @@ -151,8 +151,8 @@ class OwnUser extends User { final int totalUnreadCount; /// Total unread channels by the user. - @JsonKey(includeIfNull: false) - final int? unreadChannels; + @JsonKey(includeIfNull: false, defaultValue: 0) + final int unreadChannels; /// Known top level fields. /// diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index ca4acdea..185b6dd0 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -17,7 +17,7 @@ OwnUser _$OwnUserFromJson(Map json) { .toList() ?? [], totalUnreadCount: json['total_unread_count'] as int? ?? 0, - unreadChannels: json['unread_channels'] as int?, + unreadChannels: json['unread_channels'] as int? ?? 0, channelMutes: (json['channel_mutes'] as List?) ?.map((e) => Mute.fromJson(e as Map)) .toList() ?? diff --git a/packages/stream_chat/test/src/client/client_test.dart b/packages/stream_chat/test/src/client/client_test.dart index a1a3fc96..b59faa38 100644 --- a/packages/stream_chat/test/src/client/client_test.dart +++ b/packages/stream_chat/test/src/client/client_test.dart @@ -2313,5 +2313,27 @@ void main() { )).called(1); verifyNoMoreInteractions(api.message); }); + + test( + '''setting the `currentUser` should also compute and update the unreadCounts''', + () { + final state = client.state; + final initialUser = OwnUser.fromUser(user); + + expect(state.currentUser, initialUser); + expect(state.totalUnreadCount, 0); + expect(state.unreadChannels, 0); + + final updateUser = initialUser.copyWith( + totalUnreadCount: 33, + unreadChannels: 33, + ); + state.currentUser = updateUser; + + expect(state.currentUser, updateUser); + expect(state.totalUnreadCount, 33); + expect(state.unreadChannels, 33); + }, + ); }); } From 94636f2115f50ce2dc93cd6f7ca696daa8984cde Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 8 Sep 2021 18:43:32 +0530 Subject: [PATCH 019/148] chore(llc): Update current user in `event.unreadChannels`,`.totalUnreadCount` Signed-off-by: xsahil03x --- packages/stream_chat/lib/src/client/client.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 9253b623..9487e6e7 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -1371,12 +1371,16 @@ class ClientState { .on() .map((event) => event.unreadChannels) .whereType() - .listen(_unreadChannelsController.add), + .listen((count) { + currentUser = currentUser?.copyWith(unreadChannels: count); + }), _client .on() .map((event) => event.totalUnreadCount) .whereType() - .listen(_totalUnreadCountController.add), + .listen((count) { + currentUser = currentUser?.copyWith(totalUnreadCount: count); + }), ]); _listenChannelDeleted(); From 720c0a80edc51f8998f23b39f710b2312d6c9ee1 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 9 Sep 2021 10:25:08 +0200 Subject: [PATCH 020/148] chore(repo): update flutter version in workflow --- .github/workflows/stream_flutter_workflow.yml | 2 +- .../lib/src/core/http/interceptor/auth_interceptor.dart | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 0baaaff7..2e81d732 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -2,7 +2,7 @@ name: stream_flutter_workflow env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - flutter_version: "2.2.2" + flutter_version: "2.5.0" on: pull_request: diff --git a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart index 9947eefa..37f618f5 100644 --- a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart +++ b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart @@ -38,7 +38,9 @@ class AuthInterceptor extends Interceptor { 'Authorization': token.rawValue, 'stream-auth-type': token.authType.raw, }; - options..queryParameters.addAll(params)..headers.addAll(headers); + options + ..queryParameters.addAll(params) + ..headers.addAll(headers); return handler.next(options); } From 19472027cc20e3f4a6421aca60da704321c5765a Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 9 Sep 2021 17:01:36 +0200 Subject: [PATCH 021/148] refactor(ui): remove context and add size property to EmojiOverlay --- .../core/http/interceptor/auth_interceptor.dart | 4 +++- .../example/ios/Flutter/AppFrameworkInfo.plist | 2 +- .../lib/src/emoji_overlay.dart | 16 ++++++---------- .../lib/src/message_input.dart | 4 +++- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart index 9947eefa..37f618f5 100644 --- a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart +++ b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart @@ -38,7 +38,9 @@ class AuthInterceptor extends Interceptor { 'Authorization': token.rawValue, 'stream-auth-type': token.authType.raw, }; - options..queryParameters.addAll(params)..headers.addAll(headers); + options + ..queryParameters.addAll(params) + ..headers.addAll(headers); return handler.next(options); } diff --git a/packages/stream_chat_flutter/example/ios/Flutter/AppFrameworkInfo.plist b/packages/stream_chat_flutter/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78..f2872cf4 100644 --- a/packages/stream_chat_flutter/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/stream_chat_flutter/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart index c3768851..211b18b1 100644 --- a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart @@ -7,15 +7,15 @@ import 'package:substring_highlight/substring_highlight.dart'; /// Overlay for displaying emoji that can be used class EmojiOverlay extends StatelessWidget { /// Constructor for creating a [EmojiOverlay] - const EmojiOverlay( - this.context, { + const EmojiOverlay({ required this.query, required this.onEmojiResult, + required this.size, Key? key, }) : super(key: key); - /// Context of the upper tree - final BuildContext context; + /// The size of the overlay + final Size size; /// Query for searching emoji final String query; @@ -24,7 +24,7 @@ class EmojiOverlay extends StatelessWidget { final ValueChanged onEmojiResult; @override - Widget build(BuildContext otherContext) { + Widget build(BuildContext context) { final _streamChatTheme = StreamChatTheme.of(context); final _emojiNames = Emoji.all().where((it) => it.name != null).map((e) => e.name!); @@ -38,10 +38,6 @@ class EmojiOverlay extends StatelessWidget { return const SizedBox(); } - // ignore: cast_nullable_to_non_nullable - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; - return Card( margin: const EdgeInsets.all(8), elevation: 2, @@ -51,7 +47,7 @@ class EmojiOverlay extends StatelessWidget { ), clipBehavior: Clip.hardEdge, child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 200)), + constraints: BoxConstraints.loose(size), decoration: BoxDecoration( boxShadow: const [ BoxShadow( diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index f150d09d..54cc7bae 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -1170,9 +1170,11 @@ class MessageInputState extends State { .split(':'); final query = splits.last.toLowerCase(); + // ignore: cast_nullable_to_non_nullable + final renderObject = context.findRenderObject() as RenderBox; return EmojiOverlay( - context, + size: Size(renderObject.size.width - 16, 200), query: query, onEmojiResult: (emoji) { _chooseEmoji(splits, emoji); From 377789c4210f918d508c468cbcb771f6235522cc Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 10 Sep 2021 10:52:59 +0200 Subject: [PATCH 022/148] refactor(ui): remove context from Commands and Mentions overlays --- .../{overlays.dart => anchored_overlay.dart} | 2 - .../lib/src/commands_overlay.dart | 25 ++++---- .../lib/src/emoji_overlay.dart | 2 +- .../lib/src/mentions_overlay.dart | 24 ++++---- .../lib/src/message_input.dart | 57 ++++++++++++------- 5 files changed, 61 insertions(+), 49 deletions(-) rename packages/stream_chat_flutter/lib/src/{overlays.dart => anchored_overlay.dart} (98%) diff --git a/packages/stream_chat_flutter/lib/src/overlays.dart b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart similarity index 98% rename from packages/stream_chat_flutter/lib/src/overlays.dart rename to packages/stream_chat_flutter/lib/src/anchored_overlay.dart index ce6a9a4e..d79e19df 100644 --- a/packages/stream_chat_flutter/lib/src/overlays.dart +++ b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart @@ -110,12 +110,10 @@ class _OverlayBuilderState extends State { } void addToOverlay(OverlayEntry entry) async { - print('addToOverlay'); Overlay.of(context)?.insert(entry); } void hideOverlay() { - print('hideOverlay'); overlayEntry?.remove(); overlayEntry = null; } diff --git a/packages/stream_chat_flutter/lib/src/commands_overlay.dart b/packages/stream_chat_flutter/lib/src/commands_overlay.dart index aec35da9..ffaec928 100644 --- a/packages/stream_chat_flutter/lib/src/commands_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/commands_overlay.dart @@ -1,33 +1,34 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Overlay for displaying commands that can be used class CommandsOverlay extends StatelessWidget { /// Constructor for creating a [CommandsOverlay] - const CommandsOverlay( - this.context, { + const CommandsOverlay({ required this.text, required this.onCommandResult, + required this.size, + required this.channel, Key? key, }) : super(key: key); - /// Context of the upper tree - final BuildContext context; + /// The size of the overlay + final Size size; /// Query for searching commands final String text; + /// The channel to search for users + final Channel channel; + /// Callback called when a command is selected final ValueChanged onCommandResult; @override - Widget build(BuildContext otherContext) { + Widget build(BuildContext context) { final _streamChatTheme = StreamChatTheme.of(context); - final commands = StreamChannel.of(context) - .channel - .config - ?.commands + final commands = channel.config?.commands .where((c) => c.name.contains(text.replaceFirst('/', ''))) .toList() ?? []; @@ -36,8 +37,6 @@ class CommandsOverlay extends StatelessWidget { return const SizedBox(); } - final size = MediaQuery.of(context).size; - return Padding( padding: const EdgeInsets.all(4), child: Card( @@ -48,7 +47,7 @@ class CommandsOverlay extends StatelessWidget { color: _streamChatTheme.colorTheme.barsBg, clipBehavior: Clip.hardEdge, child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 400)), + constraints: BoxConstraints.loose(size), decoration: BoxDecoration( color: _streamChatTheme.colorTheme.barsBg, borderRadius: BorderRadius.circular(8)), diff --git a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart index 211b18b1..c37dd687 100644 --- a/packages/stream_chat_flutter/lib/src/emoji_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/emoji_overlay.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:substring_highlight/substring_highlight.dart'; /// Overlay for displaying emoji that can be used diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart index 823e920d..57d286d9 100644 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -4,20 +4,24 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Overlay for displaying users that can be mentioned class MentionsOverlay extends StatelessWidget { /// Constructor for creating a [MentionsOverlay] - const MentionsOverlay( - this.context, { + const MentionsOverlay({ required this.query, required this.onMentionResult, + required this.size, + required this.channel, this.mentionsTileBuilder, Key? key, }) : super(key: key); - /// Context of the upper tree - final BuildContext context; + /// The size of the overlay + final Size size; /// Query for searching users final String query; + /// The channel to search for users + final Channel channel; + /// Callback called when a user is selected final ValueChanged onMentionResult; @@ -25,19 +29,18 @@ class MentionsOverlay extends StatelessWidget { final MentionTileBuilder? mentionsTileBuilder; @override - Widget build(BuildContext otherContext) { + Widget build(BuildContext context) { final _streamChatTheme = StreamChatTheme.of(context); Future>? queryMembers; - final channelState = StreamChannel.of(context); if (query.isNotEmpty) { - queryMembers = channelState.channel + queryMembers = channel .queryMembers(filter: Filter.autoComplete('name', query)) .then((res) => res.members); } - final members = channelState.channel.state?.members + final members = channel.state?.members .where((m) => m.user?.name.toLowerCase().contains(query) == true) .toList() ?? []; @@ -46,9 +49,6 @@ class MentionsOverlay extends StatelessWidget { return const SizedBox(); } - // ignore: cast_nullable_to_non_nullable - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; final child = Card( margin: const EdgeInsets.all(8), elevation: 2, @@ -58,7 +58,7 @@ class MentionsOverlay extends StatelessWidget { ), clipBehavior: Clip.hardEdge, child: Container( - constraints: BoxConstraints.loose(Size(size.width - 16, 200)), + constraints: BoxConstraints.loose(size), decoration: BoxDecoration( color: _streamChatTheme.colorTheme.barsBg, ), diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 54cc7bae..a6795a9e 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -14,7 +14,7 @@ import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/src/commands_overlay.dart'; import 'package:stream_chat_flutter/src/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/mentions_overlay.dart'; -import 'package:stream_chat_flutter/src/overlays.dart'; +import 'package:stream_chat_flutter/src/anchored_overlay.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; @@ -923,7 +923,14 @@ class MessageInputState extends State { Widget _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); - return CommandsOverlay(context, text: text, onCommandResult: _setCommand); +// ignore: cast_nullable_to_non_nullable + final renderObject = context.findRenderObject() as RenderBox; + return CommandsOverlay( + channel: StreamChannel.of(context).channel, + size: Size(renderObject.size.width - 16, 400), + text: text, + onCommandResult: _setCommand, + ); } Widget _buildFilePickerSection() { @@ -1141,27 +1148,35 @@ class MessageInputState extends State { .split('@'); final query = splits.last.toLowerCase(); - return MentionsOverlay(context, query: query, onMentionResult: (user) { - if (user != null) { - _mentionedUsers.add(user); - } + // ignore: cast_nullable_to_non_nullable + final renderObject = context.findRenderObject() as RenderBox; - splits[splits.length - 1] = user!.name; - final rejoin = splits.join('@'); + return MentionsOverlay( + channel: StreamChannel.of(context).channel, + size: Size(renderObject.size.width - 16, 400), + query: query, + onMentionResult: (user) { + if (user != null) { + _mentionedUsers.add(user); + } - textEditingController.value = TextEditingValue( - text: rejoin + - textEditingController.text - .substring(textEditingController.selection.start), - selection: TextSelection.collapsed( - offset: rejoin.length, - ), - ); - _debounce!.cancel(); - setState(() { - _mentionsOverlay = null; - }); - }); + splits[splits.length - 1] = user!.name; + final rejoin = splits.join('@'); + + textEditingController.value = TextEditingValue( + text: rejoin + + textEditingController.text + .substring(textEditingController.selection.start), + selection: TextSelection.collapsed( + offset: rejoin.length, + ), + ); + _debounce!.cancel(); + setState(() { + _mentionsOverlay = null; + }); + }, + ); } Widget _buildEmojiOverlay() { From 88d1210a8d24bc21592fcf97908bb038f3d80de7 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 10 Sep 2021 18:04:45 +0530 Subject: [PATCH 023/148] fix gradient border radius --- .../stream_chat_flutter/lib/src/user_avatar.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index f9a1fd02..a923011c 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -66,6 +66,12 @@ class UserAvatar extends StatelessWidget { final placeholder = this.placeholder ?? streamChatTheme.placeholderUserImage; + final backupGradientAvatar = ClipRRect( + borderRadius: borderRadius ?? + streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius, + child: streamChatTheme.defaultUserImage(context, user), + ); + Widget avatar = FittedBox( fit: BoxFit.cover, child: Container( @@ -76,8 +82,7 @@ class UserAvatar extends StatelessWidget { fit: BoxFit.cover, filterQuality: FilterQuality.high, imageUrl: user.image!, - errorWidget: (context, __, ___) => - streamChatTheme.defaultUserImage(context, user), + errorWidget: (context, __, ___) => backupGradientAvatar, placeholder: placeholder != null ? (context, __) => placeholder(context, user) : null, @@ -91,11 +96,7 @@ class UserAvatar extends StatelessWidget { ), ), ) - : ClipRRect( - borderRadius: borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius, - child: streamChatTheme.defaultUserImage(context, user), - ), + : backupGradientAvatar, ), ); From 874002e8e74cd66f58ed6d30392e9b051f2895f5 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 10 Sep 2021 18:19:23 +0530 Subject: [PATCH 024/148] fix(ui): Remove `FittedBox` from actions widget. Signed-off-by: xsahil03x --- .../lib/src/message_input.dart | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 9646608c..516f6f40 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -604,20 +604,18 @@ class MessageInputState extends State { !widget.showCommandsButton && widget.actions?.isNotEmpty != true ? const Offstage() - : FittedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (!widget.disableAttachments) - _buildAttachmentButton(context), - if (widget.showCommandsButton && - widget.editMessage == null && - channel.state != null && - channel.config?.commands.isNotEmpty == true) - _buildCommandButton(context), - ...widget.actions ?? [], - ].insertBetween(const SizedBox(width: 8)), - ), + : Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (!widget.disableAttachments) + _buildAttachmentButton(context), + if (widget.showCommandsButton && + widget.editMessage == null && + channel.state != null && + channel.config?.commands.isNotEmpty == true) + _buildCommandButton(context), + ...widget.actions ?? [], + ].insertBetween(const SizedBox(width: 8)), ), duration: const Duration(milliseconds: 300), alignment: Alignment.center, From 9926dc6907ee54e712e0c74ce10e07957a87f264 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 10 Sep 2021 18:23:12 +0530 Subject: [PATCH 025/148] chore(ui): Update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index ed8f5e06..7c316bf6 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,10 @@ +## Upcoming + +🐞 Fixed + +- [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case + there are no actions available to show. + ## 2.2.1 🛑️ Breaking Changes from `2.2.1` From eb3d356407cd483456d5ae72a10b36f25d26556f Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 13 Sep 2021 15:16:04 +0530 Subject: [PATCH 026/148] general fixes --- .../lib/src/message_input.dart | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index a6795a9e..296fb889 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -477,19 +477,19 @@ class MessageInputState extends State { _emojiOverlay != null || _mentionsOverlay != null, overlayBuilder: (context, offset) { - late Widget child; + late Widget? child; if (_commandsOverlay != null) { - child = _buildCommandsOverlayEntry(); + child = _commandsOverlay; } else if (_emojiOverlay != null) { - child = _buildEmojiOverlay(); + child = _emojiOverlay; } else { - child = _buildMentionsOverlayEntry(); + child = _mentionsOverlay; } return CenterAbout( position: Offset(offset.dx, offset.dy), - child: child, + child: child!, ); }, child: child, @@ -827,14 +827,6 @@ class MessageInputState extends State { .keyStroke(widget.parentMessage?.id) .catchError((e) {}); - setState(() { - _actionsShrunk = s.trim().isNotEmpty && - ((widget.actions?.length ?? 0) + - (widget.showCommandsButton ? 1 : 0) + - (widget.disableAttachments ? 0 : 1) > - 1); - }); - _commandsOverlay = null; _mentionsOverlay = null; _emojiOverlay = null; @@ -844,6 +836,14 @@ class MessageInputState extends State { _checkMentions(s, context); _checkEmoji(s, context); + + setState(() { + _actionsShrunk = s.trim().isNotEmpty && + ((widget.actions?.length ?? 0) + + (widget.showCommandsButton ? 1 : 0) + + (widget.disableAttachments ? 0 : 1) > + 1); + }); }, ); } From 8779341bbd266d8718a56483186dc1ae343d42eb Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 14 Sep 2021 20:28:00 +0530 Subject: [PATCH 027/148] fix(core): match oldWidget objects using jsonEncode Signed-off-by: xsahil03x --- .../lib/src/channel_list_core.dart | 5 ++--- .../lib/src/message_search_list_core.dart | 12 ++++++------ .../lib/src/user_list_core.dart | 5 ++--- .../test/message_search_bloc_test.dart | 2 +- .../test/message_search_list_core_test.dart | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index c5d3f69a..e79a7f1f 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -214,15 +214,14 @@ class ChannelListCoreState extends State { void didUpdateWidget(ChannelListCore oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.filter?.toString() != oldWidget.filter?.toString() || + if (jsonEncode(widget.filter) != jsonEncode(oldWidget.filter) || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.state != oldWidget.state || widget.watch != oldWidget.watch || widget.presence != oldWidget.presence || widget.messageLimit != oldWidget.messageLimit || widget.memberLimit != oldWidget.memberLimit || - widget.pagination.toJson().toString() != - oldWidget.pagination.toJson().toString()) { + jsonEncode(widget.pagination) != jsonEncode(oldWidget.pagination)) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 28e092a7..e5b91b6e 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -185,13 +185,13 @@ class MessageSearchListCoreState extends State { @override void didUpdateWidget(MessageSearchListCore oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.filters.toString() != oldWidget.filters.toString() || + if (jsonEncode(widget.filters) != jsonEncode(oldWidget.filters) || jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || - widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || - widget.messageFilters?.toString() != - oldWidget.messageFilters?.toString() || - widget.paginationParams.toJson().toString() != - oldWidget.paginationParams.toJson().toString()) { + widget.messageQuery != oldWidget.messageQuery || + jsonEncode(widget.messageFilters) != + jsonEncode(oldWidget.messageFilters) || + jsonEncode(widget.paginationParams) != + jsonEncode(oldWidget.paginationParams)) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index e403be4e..06ad7d80 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -209,11 +209,10 @@ class UserListCoreState extends State @override void didUpdateWidget(UserListCore oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.filter?.toString() != oldWidget.filter?.toString() || + if (jsonEncode(widget.filter) != jsonEncode(oldWidget.filter) || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.presence != oldWidget.presence || - widget.pagination.toJson().toString() != - oldWidget.pagination.toJson().toString()) { + jsonEncode(widget.pagination) != jsonEncode(oldWidget.pagination)) { loadData(); } diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart index c689d90c..332289ea 100644 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart @@ -8,7 +8,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'matchers/get_message_response_matcher.dart'; import 'mocks.dart'; -const testFilter = Filter.custom(operator: '\$test', value: 'testValue'); +const testFilter = Filter.custom(key: 'test', value: 'testValue'); void main() { List _generateMessages({ diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index 03396513..02a78412 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -6,8 +6,8 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; -const testFilter = Filter.custom(operator: '\$test', value: 'testValue'); -const testMessageFilter = Filter.custom(operator: '\$test', value: 'testValue'); +const testFilter = Filter.custom(key: 'test', value: 'testValue'); +const testMessageFilter = Filter.custom(key: 'test', value: 'testValue'); void main() { List _generateMessages({ From d888f1568c8f7677a48cd329a9564048fa1b2927 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 14 Sep 2021 20:31:06 +0530 Subject: [PATCH 028/148] chore(core): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter_core/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index d0d89a6b..f5da89c3 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -14,6 +14,7 @@ 🐞 Fixed - Fixed `MessageSearchBloc` pagination. +- [[#673]](https://github.com/GetStream/stream-chat-flutter/issues/673): Fix `Core Widgets` not getting rebuild with new data on configuration change. ## 2.2.1 From fb7ede84a3fe8102e087d0bc503f7c9dbbb5aa97 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 14 Sep 2021 22:13:37 +0530 Subject: [PATCH 029/148] attachment tap fixes --- .../stream_chat_flutter/lib/src/message_widget.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 7619f21b..537c088f 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -214,6 +214,11 @@ class MessageWidget extends StatefulWidget { ), onShowMessage: onShowMessage, onReturnAction: onReturnAction, + onAttachmentTap: onAttachmentTap != null + ? () { + onAttachmentTap(message, attachment); + } + : null, ); }).toList(), ), @@ -243,6 +248,11 @@ class MessageWidget extends StatefulWidget { mediaQueryData.size.width * 0.8, mediaQueryData.size.height * 0.3, ), + onAttachmentTap: onAttachmentTap != null + ? () { + onAttachmentTap(message, attachment); + } + : null, ), border, reverse, From 380e34450f18994d73a1684a961dde4d242f9bef Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 14 Sep 2021 22:14:38 +0530 Subject: [PATCH 030/148] added changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index ed8f5e06..090a0c55 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -14,6 +14,7 @@ 🐞 Fixed - Fixed `MessageSearchListView` pagination. +- Fixed `MessageWidget` attachment tap callbacks. ## 2.2.1 From 33b9152d4f5dba5a50d5f7b5100d8244e1a22823 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 15 Sep 2021 12:08:34 +0200 Subject: [PATCH 031/148] feat(ui): add `onAttachmentTap` to `ImageGroup` --- packages/stream_chat_flutter/lib/src/image_group.dart | 8 ++++++++ packages/stream_chat_flutter/lib/src/message_widget.dart | 1 + 2 files changed, 9 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/image_group.dart b/packages/stream_chat_flutter/lib/src/image_group.dart index 627406d0..88b309fe 100644 --- a/packages/stream_chat_flutter/lib/src/image_group.dart +++ b/packages/stream_chat_flutter/lib/src/image_group.dart @@ -15,6 +15,7 @@ class ImageGroup extends StatelessWidget { required this.size, this.onReturnAction, this.onShowMessage, + this.onAttachmentTap, }) : super(key: key); /// List of attachments to show @@ -23,6 +24,9 @@ class ImageGroup extends StatelessWidget { /// Callback when attachment is returned to from other screens final ValueChanged? onReturnAction; + /// Callback when attachment is tapped + final void Function(Message message, Attachment attachment)? onAttachmentTap; + /// Message which images are attached to final Message message; @@ -117,6 +121,10 @@ class ImageGroup extends StatelessWidget { BuildContext context, int index, ) async { + if (onAttachmentTap != null) { + return onAttachmentTap!(message, images[index]); + } + final channel = StreamChannel.of(context).channel; final res = await Navigator.push( diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 537c088f..7ceca3ad 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -133,6 +133,7 @@ class MessageWidget extends StatefulWidget { messageTheme: messageTheme, onShowMessage: onShowMessage, onReturnAction: onReturnAction, + onAttachmentTap: onAttachmentTap, ), ), border, From 59aad47789eac90bfa3e2c5e0859d44a4648f381 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 16:01:00 +0530 Subject: [PATCH 032/148] refactor(llc): make `Filter.empty` constructor const. Signed-off-by: xsahil03x --- packages/stream_chat/lib/src/core/models/filter.dart | 9 ++++++--- .../stream_chat/test/src/core/models/filter_test.dart | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index d4536eab..d5a2c08f 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -102,6 +102,12 @@ class Filter extends Equatable { this.key, }) : operator = operator.rawValue; + /// An empty filter + const Filter.empty() + : value = const {}, + operator = null, + key = null; + /// Combines the provided filters and matches the values /// matched by all filters. factory Filter.and(List filters) => @@ -172,9 +178,6 @@ class Filter extends Equatable { String? key, }) = Filter.__; - /// An empty filter - factory Filter.empty() => const Filter.raw(value: {}); - /// Creates a custom [Filter] from a raw map value /// /// ```dart diff --git a/packages/stream_chat/test/src/core/models/filter_test.dart b/packages/stream_chat/test/src/core/models/filter_test.dart index 6e3dbf64..a62c162e 100644 --- a/packages/stream_chat/test/src/core/models/filter_test.dart +++ b/packages/stream_chat/test/src/core/models/filter_test.dart @@ -139,7 +139,7 @@ void main() { }); test('empty', () { - final filter = Filter.empty(); + const filter = Filter.empty(); expect(filter.value, {}); }); From ea3d10646e578991eb58ac3ddcde85d3d3bcf8ae Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 16:02:05 +0530 Subject: [PATCH 033/148] feat(ui, core): Add default empty filter in `UserListView`. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/src/user_list_view.dart | 3 ++- packages/stream_chat_flutter_core/lib/src/user_list_core.dart | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index 4d650192..12dbb3ec 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -48,7 +48,7 @@ class UserListView extends StatefulWidget { /// Instantiate a new UserListView const UserListView({ Key? key, - this.filter, + this.filter = const Filter.empty(), this.sort, this.presence, this.pagination = const PaginationParams(limit: 30), @@ -76,6 +76,7 @@ class UserListView extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. + // TODO: Make it non-nullable in a future breaking release final Filter? filter; /// The sorting used for the channels matching the filters. diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index 06ad7d80..75fc7411 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -63,7 +63,7 @@ class UserListCore extends StatefulWidget { required this.loadingBuilder, required this.listBuilder, Key? key, - this.filter, + this.filter = const Filter.empty(), this.sort, this.presence, this.pagination = const PaginationParams(limit: 30), @@ -91,6 +91,7 @@ class UserListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. + // TODO: Make it non-nullable in a future breaking release final Filter? filter; /// The sorting used for the channels matching the filters. From 1d142d7697690eb4a0e0a1b905e1ef1737e1b909 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 16:25:32 +0530 Subject: [PATCH 034/148] chore(core, ui): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 7 +++++++ packages/stream_chat_flutter_core/CHANGELOG.md | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index ed8f5e06..b2ef50cb 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -11,6 +11,13 @@ pagination = const PaginationParams(limit: 30) ``` +🔄 Changed + +- `UserListViewCore` filter property now has a default value. + ```dart + filter = const Filter.empty() + ``` + 🐞 Fixed - Fixed `MessageSearchListView` pagination. diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index f5da89c3..5ce1eb8b 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -11,10 +11,18 @@ pagination = const PaginationParams(limit: 30) ``` +🔄 Changed + +- `UserListViewCore` filter property now has a default value. + ```dart + filter = const Filter.empty() + ``` + 🐞 Fixed - Fixed `MessageSearchBloc` pagination. -- [[#673]](https://github.com/GetStream/stream-chat-flutter/issues/673): Fix `Core Widgets` not getting rebuild with new data on configuration change. +- [[#673]](https://github.com/GetStream/stream-chat-flutter/issues/673): Fix `Core Widgets` not getting rebuild with new + data on configuration change. ## 2.2.1 From abc22f0eb91f14f10d786ac2f4918604976b6aa9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 17:45:32 +0530 Subject: [PATCH 035/148] fix(llc): Fix `Filter.empty` encoding Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 1 + packages/stream_chat/lib/src/core/models/filter.dart | 2 +- packages/stream_chat/test/src/core/models/filter_test.dart | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 90211d07..98aa9782 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -19,6 +19,7 @@ 🐞 Fixed - [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not updating correctly. +- Fix `Filter.empty()` json encoding. ## 2.2.1 diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index d5a2c08f..ca494bdb 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -104,7 +104,7 @@ class Filter extends Equatable { /// An empty filter const Filter.empty() - : value = const {}, + : value = const {}, operator = null, key = null; diff --git a/packages/stream_chat/test/src/core/models/filter_test.dart b/packages/stream_chat/test/src/core/models/filter_test.dart index a62c162e..e396c2b3 100644 --- a/packages/stream_chat/test/src/core/models/filter_test.dart +++ b/packages/stream_chat/test/src/core/models/filter_test.dart @@ -226,6 +226,12 @@ void main() { json.encode(value), ); }); + + test('empty', () { + const filter = Filter.empty(); + final encoded = json.encode(filter); + expect(encoded, '{}'); + }); }); test('groupedFilter', () { From 6427e34119f8a26952a814e841191a498d997cf6 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 20:50:44 +0530 Subject: [PATCH 036/148] feat(ui, core): deprecate `pagination` in favor of `limit` Signed-off-by: xsahil03x --- .../lib/src/channel_list_view.dart | 25 +++++++++---- .../lib/src/message_search_list_view.dart | 23 +++++++++--- .../lib/src/user_list_view.dart | 21 ++++++++--- .../theme/channel_list_view_theme_test.dart | 2 +- .../src/theme/user_list_view_theme_test.dart | 4 +-- .../lib/src/channel_list_core.dart | 30 +++++++++++----- .../lib/src/message_search_list_core.dart | 36 ++++++++++++++----- .../lib/src/user_list_core.dart | 28 +++++++++++---- .../test/channel_list_core_test.dart | 14 ++++++++ .../test/message_search_list_core_test.dart | 15 ++++++++ .../test/user_list_core_test.dart | 13 +++++++ 11 files changed, 167 insertions(+), 44 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 35fc6d73..0f06e269 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -59,7 +59,7 @@ typedef ViewInfoCallback = void Function(Channel); /// Modify it to change the widget appearance. class ChannelListView extends StatefulWidget { /// Instantiate a new ChannelListView - const ChannelListView({ + ChannelListView({ Key? key, this.filter, this.sort, @@ -68,9 +68,12 @@ class ChannelListView extends StatefulWidget { this.presence = false, this.memberLimit, this.messageLimit, - this.pagination = const PaginationParams( - limit: 25, - ), + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.pagination, + int? limit, this.onChannelTap, this.onChannelLongPress, this.channelWidget, @@ -92,7 +95,8 @@ class ChannelListView extends StatefulWidget { this.onDeletePressed, this.swipeActions, this.channelListController, - }) : super(key: key); + }) : limit = limit ?? pagination?.limit ?? 25, + super(key: key); /// If true a default swipe to action behaviour will be added to this widget final bool swipeToAction; @@ -129,7 +133,14 @@ class ChannelListView extends StatefulWidget { /// limit: the number of channels to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? pagination; + + /// The amount of channels requested per API call. + final int limit; /// Function called when tapping on a channel /// By default it calls [Navigator.push] building a [MaterialPageRoute] @@ -218,7 +229,7 @@ class _ChannelListViewState extends State { presence: widget.presence, memberLimit: widget.memberLimit, messageLimit: widget.messageLimit, - pagination: widget.pagination, + limit: widget.limit, channelListController: _channelListController, listBuilder: widget.listBuilder ?? _buildListView, emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget, diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index f80c688f..ab1a9178 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -53,12 +53,17 @@ typedef EmptyMessageSearchBuilder = Widget Function( /// Modify it to change the widget appearance. class MessageSearchListView extends StatefulWidget { /// Instantiate a new MessageSearchListView - const MessageSearchListView({ + MessageSearchListView({ Key? key, required this.filters, this.messageQuery, this.sortOptions, - this.paginationParams = const PaginationParams(limit: 30), + @Deprecated( + "'paginationParams' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.paginationParams, + int? limit, this.messageFilters, this.separatorBuilder, this.itemBuilder, @@ -71,7 +76,8 @@ class MessageSearchListView extends StatefulWidget { this.loadingBuilder, this.childBuilder, this.messageSearchListController, - }) : super(key: key); + }) : limit = limit ?? paginationParams?.limit ?? 30, + super(key: key); /// Message String to search on final String? messageQuery; @@ -93,7 +99,14 @@ class MessageSearchListView extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + @Deprecated( + "'paginationParams' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? paginationParams; + + /// The amount of messages requested per API call. + final int limit; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. @@ -152,7 +165,7 @@ class _MessageSearchListViewState extends State { filters: widget.filters, sortOptions: widget.sortOptions, messageQuery: widget.messageQuery, - paginationParams: widget.paginationParams, + limit: widget.limit, messageFilters: widget.messageFilters, messageSearchListController: _messageSearchListController, emptyBuilder: widget.emptyBuilder ?? diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index 12dbb3ec..b946f800 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -46,12 +46,17 @@ typedef UserItemBuilder = Widget Function(BuildContext, User, bool); /// Modify it to change the widget appearance. class UserListView extends StatefulWidget { /// Instantiate a new UserListView - const UserListView({ + UserListView({ Key? key, this.filter = const Filter.empty(), this.sort, this.presence, - this.pagination = const PaginationParams(limit: 30), + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.pagination, + int? limit, this.onUserTap, this.onUserLongPress, this.userWidget, @@ -71,6 +76,7 @@ class UserListView extends StatefulWidget { crossAxisCount == 1 || groupAlphabetically == false, 'Cannot group alphabetically when crossAxisCount > 1', ), + limit = limit ?? pagination?.limit ?? 30, super(key: key); /// The query filters to use. @@ -94,7 +100,14 @@ class UserListView extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? pagination; + + /// The amount of users requested per API call. + final int limit; /// Function called when tapping on a channel /// By default it calls [Navigator.push] building a [MaterialPageRoute] @@ -185,7 +198,7 @@ class _UserListViewState extends State ), listBuilder: widget.listBuilder ?? (context, list) => _buildListView(list), - pagination: widget.pagination, + limit: widget.limit, sort: widget.sort, filter: widget.filter, presence: widget.presence, diff --git a/packages/stream_chat_flutter/test/src/theme/channel_list_view_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/channel_list_view_theme_test.dart index 38b1c524..079c01e3 100644 --- a/packages/stream_chat_flutter/test/src/theme/channel_list_view_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/channel_list_view_theme_test.dart @@ -62,7 +62,7 @@ void main() { return Scaffold( body: StreamChannel( channel: MockChannel(), - child: const ChannelListView(), + child: ChannelListView(), ), ); }, diff --git a/packages/stream_chat_flutter/test/src/theme/user_list_view_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/user_list_view_theme_test.dart index a22eebd0..3aa20744 100644 --- a/packages/stream_chat_flutter/test/src/theme/user_list_view_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/user_list_view_theme_test.dart @@ -56,7 +56,7 @@ void main() { home: Builder( builder: (BuildContext context) { _context = context; - return const Scaffold( + return Scaffold( body: UsersBloc( child: UserListView(), ), @@ -85,7 +85,7 @@ void main() { home: Builder( builder: (BuildContext context) { _context = context; - return const Scaffold( + return Scaffold( body: UsersBloc( child: UserListView(), ), diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index e79a7f1f..d3d6c312 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -56,7 +56,7 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; /// information about the channels. class ChannelListCore extends StatefulWidget { /// Instantiate a new ChannelListView - const ChannelListCore({ + ChannelListCore({ Key? key, required this.errorBuilder, required this.emptyBuilder, @@ -69,11 +69,15 @@ class ChannelListCore extends StatefulWidget { this.memberLimit, this.messageLimit, this.sort, - this.pagination = const PaginationParams( - limit: 25, - ), + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.pagination, this.channelListController, - }) : super(key: key); + int? limit, + }) : limit = limit ?? pagination?.limit ?? 25, + super(key: key); /// A [ChannelListController] allows reloading and pagination. /// Use [ChannelListController.loadData] and @@ -124,7 +128,14 @@ class ChannelListCore extends StatefulWidget { /// limit: the number of channels to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? pagination; + + /// The amount of channels requested per API call. + final int limit; @override ChannelListCoreState createState() => ChannelListCoreState(); @@ -162,7 +173,7 @@ class ChannelListCoreState extends State { presence: widget.presence, memberLimit: widget.memberLimit, messageLimit: widget.messageLimit, - paginationParams: widget.pagination, + paginationParams: PaginationParams(limit: widget.limit), ); /// Fetches more channels with updated pagination and updates the widget @@ -174,7 +185,8 @@ class ChannelListCoreState extends State { presence: widget.presence, memberLimit: widget.memberLimit, messageLimit: widget.messageLimit, - paginationParams: widget.pagination.copyWith( + paginationParams: PaginationParams( + limit: widget.limit, offset: _channelsBloc.channels?.length ?? 0, ), ); @@ -221,7 +233,7 @@ class ChannelListCoreState extends State { widget.presence != oldWidget.presence || widget.messageLimit != oldWidget.messageLimit || widget.memberLimit != oldWidget.memberLimit || - jsonEncode(widget.pagination) != jsonEncode(oldWidget.pagination)) { + widget.limit != oldWidget.limit) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index e5b91b6e..f4d18a0b 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -38,7 +38,7 @@ class MessageSearchListCore extends StatefulWidget { /// * [errorBuilder] /// * [loadingBuilder] /// * [childBuilder] - const MessageSearchListCore({ + MessageSearchListCore({ Key? key, required this.emptyBuilder, required this.errorBuilder, @@ -47,9 +47,14 @@ class MessageSearchListCore extends StatefulWidget { required this.filters, this.messageQuery, this.sortOptions, - this.paginationParams = const PaginationParams(limit: 30), + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.paginationParams, this.messageFilters, this.messageSearchListController, + int? limit, }) : assert( messageQuery != null || messageFilters != null, 'Provide at least `query` or `messageFilters`', @@ -58,6 +63,13 @@ class MessageSearchListCore extends StatefulWidget { messageQuery == null || messageFilters == null, "Can't provide both `query` and `messageFilters` at the same time", ), + assert( + paginationParams?.offset == null || + paginationParams?.offset == 0 || + sortOptions == null, + 'Cannot specify `offset` with `sortOptions` parameter', + ), + limit = limit ?? paginationParams?.limit ?? 30, super(key: key); /// A [MessageSearchListController] allows reloading and pagination. @@ -84,7 +96,14 @@ class MessageSearchListCore extends StatefulWidget { /// Pagination parameters /// limit: the number of messages to return (max is 30) /// offset: the offset (max is 1000) - final PaginationParams paginationParams; + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? paginationParams; + + /// The amount of messages requested per API call. + final int limit; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. @@ -157,19 +176,19 @@ class MessageSearchListCoreState extends State { filter: widget.filters, sort: widget.sortOptions, query: widget.messageQuery, - pagination: widget.paginationParams, messageFilter: widget.messageFilters, + pagination: PaginationParams(limit: widget.limit), ); /// Fetches more messages with updated pagination and updates the widget Future paginateData() { - PaginationParams pagination; + var pagination = PaginationParams(limit: widget.limit); if (widget.sortOptions != null) { - pagination = widget.paginationParams.copyWith( + pagination = pagination.copyWith( next: _messageSearchBloc?.nextId, ); } else { - pagination = widget.paginationParams.copyWith( + pagination = pagination.copyWith( offset: _messageSearchBloc?.messageResponses?.length, ); } @@ -190,8 +209,7 @@ class MessageSearchListCoreState extends State { widget.messageQuery != oldWidget.messageQuery || jsonEncode(widget.messageFilters) != jsonEncode(oldWidget.messageFilters) || - jsonEncode(widget.paginationParams) != - jsonEncode(oldWidget.paginationParams)) { + widget.limit != oldWidget.limit) { loadData(); } diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index 75fc7411..839067bf 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -57,7 +57,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// [errorBuilder] must all be supplied and not null. class UserListCore extends StatefulWidget { /// Instantiate a new [UserListCore] - const UserListCore({ + UserListCore({ required this.errorBuilder, required this.emptyBuilder, required this.loadingBuilder, @@ -66,10 +66,16 @@ class UserListCore extends StatefulWidget { this.filter = const Filter.empty(), this.sort, this.presence, - this.pagination = const PaginationParams(limit: 30), + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + this.pagination, this.groupAlphabetically = false, this.userListController, - }) : super(key: key); + int? limit, + }) : limit = limit ?? pagination?.limit ?? 30, + super(key: key); /// A [UserListController] allows reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData] @@ -107,7 +113,14 @@ class UserListCore extends StatefulWidget { /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + @Deprecated( + "'pagination' is deprecated and shouldn't be used. " + "This property is no longer used, Please use 'limit' instead", + ) + final PaginationParams? pagination; + + /// The amount of users requested per API call. + final int limit; /// Set it to true to group users by their first character /// @@ -194,7 +207,7 @@ class UserListCoreState extends State filter: widget.filter, sort: widget.sort, presence: widget.presence, - pagination: widget.pagination, + pagination: PaginationParams(limit: widget.limit), ); /// Fetches more users with updated pagination and updates the widget @@ -202,7 +215,8 @@ class UserListCoreState extends State filter: widget.filter, sort: widget.sort, presence: widget.presence, - pagination: widget.pagination.copyWith( + pagination: PaginationParams( + limit: widget.limit, offset: _usersBloc!.users?.length ?? 0, ), ); @@ -213,7 +227,7 @@ class UserListCoreState extends State if (jsonEncode(widget.filter) != jsonEncode(oldWidget.filter) || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.presence != oldWidget.presence || - jsonEncode(widget.pagination) != jsonEncode(oldWidget.pagination)) { + widget.limit != oldWidget.limit) { loadData(); } diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart index 47aa2bf9..8e8755aa 100644 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart @@ -508,4 +508,18 @@ void main() { )).called(1); }, ); + + test('`widget.limit` should match `widget.pagination.limit`', () { + const pagination = PaginationParams(limit: 30); + final channelListCore = ChannelListCore( + listBuilder: (_, __) => const Offstage(), + loadingBuilder: (BuildContext context) => const Offstage(), + emptyBuilder: (BuildContext context) => const Offstage(), + errorBuilder: (BuildContext context, Object error) => const Offstage(), + pagination: pagination, + ); + + expect(channelListCore.limit, pagination.limit); + }); + } diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index 02a78412..ca416595 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -551,4 +551,19 @@ void main() { )).called(1); }, ); + + test('`widget.limit` should match `widget.pagination.limit`', () { + const pagination = PaginationParams(limit: 30); + final messageSearchListCore = MessageSearchListCore( + childBuilder: (List messages) => const Offstage(), + loadingBuilder: (BuildContext context) => const Offstage(), + emptyBuilder: (BuildContext context) => const Offstage(), + errorBuilder: (BuildContext context, Object? error) => const Offstage(), + filters: testFilter, + messageFilters: testMessageFilter, + paginationParams: pagination, + ); + + expect(messageSearchListCore.limit, pagination.limit); + }); } diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index 77977bb8..ade58e0f 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -520,4 +520,17 @@ void main() { )).called(1); }, ); + + test('`widget.limit` should match `widget.pagination.limit`', () { + const pagination = PaginationParams(limit: 30); + final userListCore = UserListCore( + listBuilder: (_, __) => const Offstage(), + loadingBuilder: (BuildContext context) => const Offstage(), + emptyBuilder: (BuildContext context) => const Offstage(), + errorBuilder: (BuildContext context, Object error) => const Offstage(), + pagination: pagination, + ); + + expect(userListCore.limit, pagination.limit); + }); } From d9ced67f80f6dfcc1a981ff4c878942cc05a4df7 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 15 Sep 2021 21:02:29 +0530 Subject: [PATCH 037/148] chore(ui, core): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 22 ++++++++++++++++--- .../stream_chat_flutter_core/CHANGELOG.md | 22 ++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index b2ef50cb..16db1398 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,14 +1,30 @@ ## 2.2.1 -🛑️ Breaking Changes from `2.2.1` +⚠️ Deprecated -- `MessageSearchListView` paginationParams property is now non-nullable with a default value. +- `MessageSearchListView` `paginationParams` property is now deprecated in favor of `limit`. ```dart + // previous paginationParams = const PaginationParams(limit: 30) + + // new + limit = 30 ``` -- `UserListView` pagination property is now non-nullable with a default value. +- `UserListView` `pagination` property is now deprecated in favor of `limit`. ```dart + // previous pagination = const PaginationParams(limit: 30) + + // new + limit = 30 + ``` +- `ChannelListView` `pagination` property is now deprecated in favor of `limit`. + ```dart + // previous + pagination = const PaginationParams(limit: 30) + + // new + limit = 30 ``` 🔄 Changed diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 5ce1eb8b..c4287a09 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,14 +1,30 @@ ## Upcoming -🛑️ Breaking Changes from `2.2.1` +⚠️ Deprecated -- `MessageSearchListViewCore` paginationParams property is now non-nullable with a default value. +- `MessageSearchListViewCore` `paginationParams` property is now deprecated in favor of `limit`. ```dart + // previous paginationParams = const PaginationParams(limit: 30) + + // new + limit = 30 ``` -- `UserListViewCore` pagination property is now non-nullable with a default value. +- `UserListViewCore` `pagination` property is now deprecated in favor of `limit`. ```dart + // previous pagination = const PaginationParams(limit: 30) + + // new + limit = 30 + ``` +- `ChannelListViewCore` `pagination` property is now deprecated in favor of `limit`. + ```dart + // previous + pagination = const PaginationParams(limit: 30) + + // new + limit = 30 ``` 🔄 Changed From 9a70beb33dae74c523248f3cbe737e6a925e6de2 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 16 Sep 2021 11:43:04 +0200 Subject: [PATCH 038/148] chore(core): fix format --- .../stream_chat_flutter_core/test/channel_list_core_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart index 8e8755aa..dd87d2cf 100644 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart @@ -521,5 +521,4 @@ void main() { expect(channelListCore.limit, pagination.limit); }); - } From b23d86fc5561515d504fac551dbafe786889d95d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 16 Sep 2021 14:47:25 +0530 Subject: [PATCH 039/148] ref(llc): add `fileSize`, `mimeType` in attachment.dart Signed-off-by: xsahil03x --- .../lib/src/core/models/attachment.dart | 23 +++++++- .../lib/src/core/models/attachment_file.dart | 26 ++++++--- .../test/src/core/models/attachment_test.dart | 55 +++++++++++++++++++ 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/attachment.dart b/packages/stream_chat/lib/src/core/models/attachment.dart index 8973aee2..0ef9db7a 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.dart @@ -33,13 +33,20 @@ class Attachment extends Equatable { this.authorIcon, this.assetUrl, List? actions, - this.extraData = const {}, + Map extraData = const {}, this.file, UploadState? uploadState, }) : id = id ?? const Uuid().v4(), title = title ?? file?.name, localUri = file?.path != null ? Uri.parse(file!.path!) : null, - actions = actions ?? [] { + actions = actions ?? [], + // For backwards compatibility, + // set 'file_size', 'mime_type' in [extraData]. + extraData = { + ...extraData, + if (file?.size != null) 'file_size': file?.size, + if (file?.mimeType != null) 'mime_type': file?.mimeType?.mimeType, + } { this.uploadState = uploadState ?? ((assetUrl != null || imageUrl != null) ? const UploadState.success() @@ -121,6 +128,18 @@ class Attachment extends Equatable { /// This is created locally for uniquely identifying a attachment. final String id; + /// Shortcut for file size. + /// + /// {@macro fileSize} + @JsonKey(ignore: true) + int? get fileSize => extraData['file_size'] as int?; + + /// Shortcut for file mimeType. + /// + /// {@macro mimeType} + @JsonKey(ignore: true) + String? get mimeType => extraData['mime_type'] as String?; + /// Known top level fields. /// Useful for [Serializer] methods. static const topLevelFields = [ diff --git a/packages/stream_chat/lib/src/core/models/attachment_file.dart b/packages/stream_chat/lib/src/core/models/attachment_file.dart index 7bff5806..c6983b74 100644 --- a/packages/stream_chat/lib/src/core/models/attachment_file.dart +++ b/packages/stream_chat/lib/src/core/models/attachment_file.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'package:dio/dio.dart' show MultipartFile; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:meta/meta.dart'; import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; import 'package:stream_chat/src/core/util/extension.dart'; @@ -65,7 +66,7 @@ class AttachmentFile { AttachmentFile({ required this.size, this.path, - this.name, + String? name, this.bytes, }) : assert( path != null || bytes != null, @@ -74,7 +75,12 @@ class AttachmentFile { assert( !CurrentPlatform.isWeb || bytes != null, 'File by path is not supported in web, Please provide bytes', - ); + ), + assert( + name?.contains('.') ?? true, + 'Invalid file name, should also contain file extension', + ), + _name = name; /// Create a new instance from a json factory AttachmentFile.fromJson(Map json) => @@ -87,8 +93,10 @@ class AttachmentFile { /// ``` final String? path; + final String? _name; + /// File name including its extension. - final String? name; + String? get name => _name ?? path?.split('/').last; /// Byte data for this file. Particularly useful if you want to manipulate /// its data or easily upload to somewhere else. @@ -101,26 +109,26 @@ class AttachmentFile { /// File extension for this file. String? get extension => name?.split('.').last; + /// The mime type of this file. + MediaType? get mimeType => name?.mimeType; + /// Serialize to json Map toJson() => _$AttachmentFileToJson(this); /// Converts this into a [MultipartFile] Future toMultipartFile() async { - final filename = path?.split('/').last ?? name; - final mimeType = filename?.mimeType; - - late MultipartFile multiPartFile; + MultipartFile multiPartFile; if (CurrentPlatform.isWeb) { multiPartFile = MultipartFile.fromBytes( bytes!, - filename: filename, + filename: name, contentType: mimeType, ); } else { multiPartFile = await MultipartFile.fromFile( path!, - filename: filename, + filename: name, contentType: mimeType, ); } diff --git a/packages/stream_chat/test/src/core/models/attachment_test.dart b/packages/stream_chat/test/src/core/models/attachment_test.dart index 37716656..eb6b72a1 100644 --- a/packages/stream_chat/test/src/core/models/attachment_test.dart +++ b/packages/stream_chat/test/src/core/models/attachment_test.dart @@ -1,5 +1,6 @@ import 'package:stream_chat/src/core/models/action.dart'; import 'package:stream_chat/src/core/models/attachment.dart'; +import 'package:stream_chat/src/core/models/attachment_file.dart'; import 'package:test/test.dart'; import '../../utils.dart'; @@ -41,5 +42,59 @@ void main() { }, ); }); + + test('name property and extraData manipulation', () { + final file = AttachmentFile(size: 3, path: 'myfolder/myfile.txt'); + final attachment = Attachment(file: file); + + expect(attachment.fileSize, 3); + expect(attachment.mimeType, 'text/plain'); + expect(attachment.toJson(), { + 'title': 'myfile.txt', + 'actions': [], + 'file_size': 3, + 'mime_type': 'text/plain' + }); + expect(Attachment.fromJson(attachment.toJson()).toJson(), { + 'title': 'myfile.txt', + 'actions': [], + 'file_size': 3, + 'mime_type': 'text/plain' + }); + + // Setting the size and mimeType using extraData should work fine + var newAttachment = Attachment( + extraData: const { + 'file_size': 6, + 'mime_type': 'application/pdf', + }, + ); + + expect(newAttachment.extraData['file_size'], 6); + expect(newAttachment.extraData['mime_type'], 'application/pdf'); + expect(newAttachment.fileSize, 6); + expect(newAttachment.mimeType, 'application/pdf'); + + // switching a new file should update size and mimeType + final fileTwo = AttachmentFile(size: 12, path: 'myfolder/fileTwo.pdf'); + newAttachment = attachment.copyWith(file: fileTwo); + + expect(newAttachment.extraData['file_size'], 12); + expect(newAttachment.extraData['mime_type'], 'application/pdf'); + expect(newAttachment.fileSize, 12); + expect(newAttachment.mimeType, 'application/pdf'); + + // if file is available, should override size and mimeType. + final fileThree = AttachmentFile(size: 9, path: 'myfolder/fileThree.png'); + newAttachment = attachment.copyWith(file: fileThree, extraData: { + 'file_size': 88, + 'mime_type': 'application/pdf', + }); + + expect(newAttachment.extraData['file_size'], 9); + expect(newAttachment.extraData['mime_type'], 'image/png'); + expect(newAttachment.fileSize, 9); + expect(newAttachment.mimeType, 'image/png'); + }); }); } From e7f531c857f8af6c62a2288055053fa32f93b78a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 16 Sep 2021 14:56:02 +0530 Subject: [PATCH 040/148] chore(llc): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 98aa9782..6b480d88 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -15,6 +15,9 @@ - Added `Filter.contains` and `Filter.empty` - Added support for `next`, `previous` value pagination in `client.search` , [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination) +- `Attachment` class now have a `fileSize` and `mimeType` property. Setting an `file` will also set the `file_size` + , `mime_type` key on `extraData`, so `user.fileSize`, `user.mimetype` and `user.extraData['file_size']` + , `user.extraData['mime_type]` is same respectively. 🐞 Fixed From dc3f864d348160c1cc36b1b365c2c2bb5b1cd93a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 16 Sep 2021 15:42:40 +0530 Subject: [PATCH 041/148] Update packages/stream_chat/test/src/core/models/attachment_test.dart --- packages/stream_chat/test/src/core/models/attachment_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/test/src/core/models/attachment_test.dart b/packages/stream_chat/test/src/core/models/attachment_test.dart index eb6b72a1..4e08f7f2 100644 --- a/packages/stream_chat/test/src/core/models/attachment_test.dart +++ b/packages/stream_chat/test/src/core/models/attachment_test.dart @@ -43,7 +43,7 @@ void main() { ); }); - test('name property and extraData manipulation', () { + test('fileName, mimeType property and extraData manipulation', () { final file = AttachmentFile(size: 3, path: 'myfolder/myfile.txt'); final attachment = Attachment(file: file); From 1179c6a109832339fc45a33beed3e41c36b0e213 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 16 Sep 2021 15:51:51 +0530 Subject: [PATCH 042/148] corrected attachment builders --- packages/stream_chat_flutter/lib/src/message_widget.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 7ceca3ad..4025337c 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -96,7 +96,7 @@ class MessageWidget extends StatefulWidget { this.bottomRowBuilder, this.deletedBottomRowBuilder, this.onReturnAction, - Map? customAttachmentBuilders, + this.customAttachmentBuilders, this.readList, this.padding, this.textPadding = const EdgeInsets.symmetric( @@ -406,6 +406,9 @@ class MessageWidget extends StatefulWidget { /// Builder for respective attachment types final Map attachmentBuilders; + /// Builder for respective attachment types (user facing builder) + final Map? customAttachmentBuilders; + /// Center user avatar with bottom of the message final bool translateUserAvatar; @@ -530,7 +533,7 @@ class MessageWidget extends StatefulWidget { showPinButton: showPinButton ?? this.showPinButton, showPinHighlight: showPinHighlight ?? this.showPinHighlight, customAttachmentBuilders: - customAttachmentBuilders ?? attachmentBuilders, + customAttachmentBuilders ?? this.customAttachmentBuilders, translateUserAvatar: translateUserAvatar ?? this.translateUserAvatar, onQuotedMessageTap: onQuotedMessageTap ?? this.onQuotedMessageTap, onMessageTap: onMessageTap ?? this.onMessageTap, From 99e4d724d6979282d9f92f931570cc50ab94a2ce Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 16 Sep 2021 12:20:30 +0200 Subject: [PATCH 043/148] fix(ui): fix giphy attachment tap callback --- .../lib/src/attachment/giphy_attachment.dart | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index 7eb2606e..2210ef39 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -253,22 +253,7 @@ class GiphyAttachment extends AttachmentWidget { Widget _buildSentAttachment(BuildContext context, String imageUrl) => SizedBox( child: GestureDetector( - onTap: () async { - final res = - await Navigator.push(context, MaterialPageRoute(builder: (_) { - final channel = StreamChannel.of(context).channel; - return StreamChannel( - channel: channel, - child: FullScreenMedia( - mediaAttachments: [attachment], - userName: message.user?.name, - message: message, - onShowMessage: onShowMessage, - ), - ); - })); - if (res != null) onReturnAction!(res); - }, + onTap: () => onAttachmentTap ?? _onImageTap(context), child: Stack( children: [ CachedNetworkImage( From eac4085090addc6b87b2ce608dba29d262b16f1f Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 16 Sep 2021 12:24:42 +0200 Subject: [PATCH 044/148] fix(ui): fix giphy attachment tap callback --- .../lib/src/attachment/giphy_attachment.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index 2210ef39..e165fc6d 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -98,7 +98,13 @@ class GiphyAttachment extends AttachmentWidget { Padding( padding: const EdgeInsets.all(2), child: GestureDetector( - onTap: () => onAttachmentTap ?? _onImageTap(context), + onTap: () { + if (onAttachmentTap != null) { + onAttachmentTap?.call(); + } else { + _onImageTap(context); + } + }, child: CachedNetworkImage( height: size?.height, width: size?.width, @@ -253,7 +259,13 @@ class GiphyAttachment extends AttachmentWidget { Widget _buildSentAttachment(BuildContext context, String imageUrl) => SizedBox( child: GestureDetector( - onTap: () => onAttachmentTap ?? _onImageTap(context), + onTap: () { + if (onAttachmentTap != null) { + onAttachmentTap?.call(); + } else { + _onImageTap(context); + } + }, child: Stack( children: [ CachedNetworkImage( From b30dcb8342507e3d1ef2dcbc283e4800de798775 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 16 Sep 2021 16:06:33 +0530 Subject: [PATCH 045/148] Update packages/stream_chat/CHANGELOG.md Co-authored-by: Salvatore Giordano --- packages/stream_chat/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 6b480d88..9c925f88 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -15,9 +15,9 @@ - Added `Filter.contains` and `Filter.empty` - Added support for `next`, `previous` value pagination in `client.search` , [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination) -- `Attachment` class now have a `fileSize` and `mimeType` property. Setting an `file` will also set the `file_size` - , `mime_type` key on `extraData`, so `user.fileSize`, `user.mimetype` and `user.extraData['file_size']` - , `user.extraData['mime_type]` is same respectively. +- `Attachment` class now has a `fileSize` and `mimeType` property. Setting a `file` will also set the `file_size` + , `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype` and `attachment.extraData['file_size']` + , `attachment.extraData['mime_type]` is same respectively. 🐞 Fixed From 6af4e0778795709e49ce68f5bd3441a145089dd2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 16 Sep 2021 16:41:17 +0530 Subject: [PATCH 046/148] ref(ui): Use `titleLink` instead of `ogScrapeUrl` for handling link attachment. Signed-off-by: xsahil03x --- .../lib/src/attachment/attachment_title.dart | 66 +++++++++---------- .../lib/src/attachment/file_attachment.dart | 3 +- .../lib/src/message_input.dart | 2 +- .../lib/src/message_list_view.dart | 2 +- .../lib/src/message_widget.dart | 13 ++-- .../lib/src/quoted_message_widget.dart | 10 +-- .../lib/src/url_attachment.dart | 6 +- .../stream_chat_flutter/lib/src/utils.dart | 4 +- 8 files changed, 49 insertions(+), 57 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart index a7d4d2e3..df134250 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart @@ -19,42 +19,36 @@ class AttachmentTitle extends StatelessWidget { final Attachment attachment; @override - Widget build(BuildContext context) => GestureDetector( - onTap: () { - if (attachment.titleLink != null) { - launchURL(context, attachment.titleLink); - } - }, - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (attachment.title != null) - Text( - attachment.title!, - overflow: TextOverflow.ellipsis, - style: messageTheme.messageTextStyle?.copyWith( - color: StreamChatTheme.of(context).colorTheme.accentPrimary, - fontWeight: FontWeight.bold, - ), + Widget build(BuildContext context) { + final normalizedTitleLink = attachment.titleLink?.replaceFirst( + RegExp(r'https?://(www\.)?'), + '', + ); + return GestureDetector( + onTap: () { + final titleLink = attachment.titleLink; + if (titleLink != null) launchURL(context, titleLink); + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (attachment.title != null) + Text( + attachment.title!, + overflow: TextOverflow.ellipsis, + style: messageTheme.messageTextStyle?.copyWith( + color: StreamChatTheme.of(context).colorTheme.accentPrimary, + fontWeight: FontWeight.bold, ), - if (attachment.titleLink != null || - attachment.ogScrapeUrl != null) - Text( - Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!) - .authority - .split('.') - .reversed - .take(2) - .toList() - .reversed - .join('.'), - style: messageTheme.messageTextStyle, - ), - ], - ), + ), + if (normalizedTitleLink != null) + Text(normalizedTitleLink, style: messageTheme.messageTextStyle), + ], ), - ); + ), + ); + } } diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index f57e1ee6..6045539f 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -258,7 +258,8 @@ class FileAttachment extends AttachmentWidget { visualDensity: VisualDensity.compact, splashRadius: 16, onPressed: () { - launchURL(context, attachment.assetUrl); + final assetUrl = attachment.assetUrl; + if (assetUrl != null) launchURL(context, assetUrl); }, ); } diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 9646608c..f5f51b46 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -1575,7 +1575,7 @@ class MessageInputState extends State { Widget _buildReplyToMessage() { if (!_hasQuotedMessage) return const Offstage(); final containsUrl = widget.quotedMessage!.attachments - .any((element) => element.ogScrapeUrl != null) == + .any((element) => element.titleLink != null) == true; return QuotedMessageWidget( reverse: true, 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 ab4ae04f..e325598d 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -991,7 +991,7 @@ class _MessageListViewState extends State { final isOnlyEmoji = message.text?.isOnlyEmoji ?? false; final hasUrlAttachment = - message.attachments.any((it) => it.ogScrapeUrl != null) == true; + message.attachments.any((it) => it.titleLink != null) == true; final borderSide = isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment) diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 7619f21b..e60353ad 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -566,12 +566,11 @@ class _MessageWidgetState extends State bool get isOnlyEmoji => widget.message.text?.isOnlyEmoji == true; - bool get hasNonUrlAttachments => widget.message.attachments - .where((it) => it.ogScrapeUrl == null) - .isNotEmpty; + bool get hasNonUrlAttachments => + widget.message.attachments.where((it) => it.titleLink == null).isNotEmpty; bool get hasUrlAttachments => - widget.message.attachments.any((it) => it.ogScrapeUrl != null) == true; + widget.message.attachments.any((it) => it.titleLink != null) == true; bool get showBottomRow => showThreadReplyIndicator || @@ -975,9 +974,9 @@ class _MessageWidgetState extends State Widget _buildUrlAttachment() { final urlAttachment = widget.message.attachments - .firstWhere((element) => element.ogScrapeUrl != null); + .firstWhere((element) => element.titleLink != null); - final host = Uri.parse(urlAttachment.ogScrapeUrl!).host; + final host = Uri.parse(urlAttachment.titleLink!).host; final splitList = host.split('.'); final hostName = splitList.length == 3 ? splitList[1] : splitList[0]; final hostDisplayName = urlAttachment.authorName?.capitalize() ?? @@ -1143,7 +1142,7 @@ class _MessageWidgetState extends State final attachmentGroups = >{}; widget.message.attachments - .where((element) => element.ogScrapeUrl == null && element.type != null) + .where((element) => element.titleLink == null && element.type != null) .forEach((e) { if (attachmentGroups[e.type] == null) { attachmentGroups[e.type!] = []; diff --git a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart index ad3d936a..01068abd 100644 --- a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart @@ -97,8 +97,8 @@ class QuotedMessageWidget extends StatelessWidget { bool get _hasAttachments => message.attachments.isNotEmpty == true; - bool get _containsScrapeUrl => - message.attachments.any((element) => element.ogScrapeUrl != null) == true; + bool get _containsLinkAttachment => + message.attachments.any((element) => element.titleLink != null) == true; bool get _containsText => message.text?.isNotEmpty == true; @@ -198,9 +198,9 @@ class QuotedMessageWidget extends StatelessWidget { Widget _parseAttachments(BuildContext context) { Widget child; Attachment attachment; - if (_containsScrapeUrl) { + if (_containsLinkAttachment) { attachment = message.attachments.firstWhere( - (element) => element.ogScrapeUrl != null, + (element) => element.titleLink != null, ); child = _buildUrlAttachment(attachment); } else { @@ -280,7 +280,7 @@ class QuotedMessageWidget extends StatelessWidget { }; Color? _getBackgroundColor(BuildContext context) { - if (_containsScrapeUrl) { + if (_containsLinkAttachment) { return StreamChatTheme.of(context).colorTheme.linkBg; } return messageTheme.messageBackgroundColor; diff --git a/packages/stream_chat_flutter/lib/src/url_attachment.dart b/packages/stream_chat_flutter/lib/src/url_attachment.dart index 5d5ca489..b940f227 100644 --- a/packages/stream_chat_flutter/lib/src/url_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/url_attachment.dart @@ -30,10 +30,8 @@ class UrlAttachment extends StatelessWidget { final chatThemeData = StreamChatTheme.of(context); return GestureDetector( onTap: () { - launchURL( - context, - urlAttachment.ogScrapeUrl, - ); + final titleLink = urlAttachment.titleLink; + if (titleLink != null) launchURL(context, titleLink); }, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/packages/stream_chat_flutter/lib/src/utils.dart b/packages/stream_chat_flutter/lib/src/utils.dart index d97ea6a1..4dc9fee7 100644 --- a/packages/stream_chat_flutter/lib/src/utils.dart +++ b/packages/stream_chat_flutter/lib/src/utils.dart @@ -7,8 +7,8 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:stream_chat_flutter/src/extension.dart'; /// Launch URL -Future launchURL(BuildContext context, String? url) async { - if (url != null && await canLaunch(url)) { +Future launchURL(BuildContext context, String url) async { + if (await canLaunch(url)) { await launch(url); } else { ScaffoldMessenger.of(context).showSnackBar( From 28b8d3bac059af80ae52633cd05d514e02ca2425 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Thu, 16 Sep 2021 14:01:44 +0200 Subject: [PATCH 047/148] fix: attachment buttons render overflow --- packages/stream_chat_flutter/lib/src/message_input.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 516f6f40..fd431701 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -604,8 +604,7 @@ class MessageInputState extends State { !widget.showCommandsButton && widget.actions?.isNotEmpty != true ? const Offstage() - : Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + : Wrap( children: [ if (!widget.disableAttachments) _buildAttachmentButton(context), From 58d8b0843333b421392704f1dbd744d37ac65cb0 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Thu, 16 Sep 2021 14:02:40 +0200 Subject: [PATCH 048/148] feat: update attachment buttons animation curve --- packages/stream_chat_flutter/lib/src/message_input.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index fd431701..f3032cf4 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -578,6 +578,8 @@ class MessageInputState extends State { crossFadeState: _actionsShrunk ? CrossFadeState.showFirst : CrossFadeState.showSecond, + firstCurve: Curves.easeOut, + secondCurve: Curves.easeIn, firstChild: IconButton( onPressed: () { if (_actionsShrunk) { @@ -1086,6 +1088,7 @@ class MessageInputState extends State { return AnimatedContainer( duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, height: _openFilePickerSection ? _kMinMediaPickerSize : 0, child: Material( color: _streamChatTheme.colorTheme.inputBg, From 2b0588c9d8ce7c55ae198b82984a4ab07890e8d1 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Thu, 16 Sep 2021 14:04:07 +0200 Subject: [PATCH 049/148] fix(ui): flickering loading indicator --- packages/stream_chat_flutter/lib/src/message_input.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index f3032cf4..cbfe6e5d 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -2315,7 +2315,7 @@ class _PickerWidgetState extends State<_PickerWidget> { future: requestPermission, builder: (context, snapshot) { if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); + return const Offstage(); } if (snapshot.data!) { From ec6c6632899d59a84c996891314e5616d2820a86 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Thu, 16 Sep 2021 14:05:27 +0200 Subject: [PATCH 050/148] fix(ui): render overflow for image attachements --- .../lib/src/message_input.dart | 201 +++++++++--------- 1 file changed, 106 insertions(+), 95 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index cbfe6e5d..ead14671 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -1090,107 +1090,118 @@ class MessageInputState extends State { duration: const Duration(milliseconds: 300), curve: Curves.easeOut, height: _openFilePickerSection ? _kMinMediaPickerSize : 0, - child: Material( - color: _streamChatTheme.colorTheme.inputBg, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( + child: SingleChildScrollView( + child: SizedBox( + height: _kMinMediaPickerSize, + child: Material( + color: _streamChatTheme.colorTheme.inputBg, + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - IconButton( - icon: StreamSvgIcon.pictures( - color: _getIconColor(0), + Row( + children: [ + IconButton( + icon: StreamSvgIcon.pictures( + color: _getIconColor(0), + ), + onPressed: + _attachmentContainsFile && _attachments.isNotEmpty + ? null + : () { + setState(() { + _filePickerIndex = 0; + }); + }, + ), + IconButton( + iconSize: 32, + icon: StreamSvgIcon.files( + color: _getIconColor(1), + ), + onPressed: + !_attachmentContainsFile && _attachments.isNotEmpty + ? null + : () { + pickFile(DefaultAttachmentTypes.file); + }, + ), + IconButton( + icon: StreamSvgIcon.camera( + color: _getIconColor(2), + ), + onPressed: attachmentLimitCrossed || + (_attachmentContainsFile && + _attachments.isNotEmpty) + ? null + : () { + pickFile(DefaultAttachmentTypes.image, + camera: true); + }, + ), + IconButton( + padding: const EdgeInsets.all(0), + icon: StreamSvgIcon.record( + color: _getIconColor(3), + ), + onPressed: attachmentLimitCrossed || + (_attachmentContainsFile && + _attachments.isNotEmpty) + ? null + : () { + pickFile(DefaultAttachmentTypes.video, + camera: true); + }, + ), + ], + ), + DecoratedBox( + decoration: BoxDecoration( + color: _streamChatTheme.colorTheme.barsBg, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), ), - onPressed: _attachmentContainsFile && _attachments.isNotEmpty - ? null - : () { - setState(() { - _filePickerIndex = 0; - }); - }, - ), - IconButton( - iconSize: 32, - icon: StreamSvgIcon.files( - color: _getIconColor(1), - ), - onPressed: !_attachmentContainsFile && _attachments.isNotEmpty - ? null - : () { - pickFile(DefaultAttachmentTypes.file); - }, - ), - IconButton( - icon: StreamSvgIcon.camera( - color: _getIconColor(2), - ), - onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && _attachments.isNotEmpty) - ? null - : () { - pickFile(DefaultAttachmentTypes.image, camera: true); - }, - ), - IconButton( - padding: const EdgeInsets.all(0), - icon: StreamSvgIcon.record( - color: _getIconColor(3), - ), - onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && _attachments.isNotEmpty) - ? null - : () { - pickFile(DefaultAttachmentTypes.video, camera: true); - }, - ), - ], - ), - DecoratedBox( - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: Container( - width: 40, - height: 4, - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.inputBg, - borderRadius: BorderRadius.circular(4), + child: Center( + child: Padding( + padding: const EdgeInsets.all(8), + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: _streamChatTheme.colorTheme.inputBg, + borderRadius: BorderRadius.circular(4), + ), + ), ), ), ), - ), + if (_openFilePickerSection) + Expanded( + child: DecoratedBox( + decoration: BoxDecoration( + color: _streamChatTheme.colorTheme.barsBg, + borderRadius: BorderRadius.circular(8), + ), + child: _PickerWidget( + filePickerIndex: _filePickerIndex, + streamChatTheme: _streamChatTheme, + containsFile: _attachmentContainsFile, + selectedMedias: _attachments.keys.toList(), + onAddMoreFilesClick: pickFile, + onMediaSelected: (media) { + if (_attachments.containsKey(media.id)) { + setState(() => _attachments.remove(media.id)); + } else { + _addAssetAttachment(media); + } + }, + ), + ), + ), + ], ), - if (_openFilePickerSection) - Expanded( - child: DecoratedBox( - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - borderRadius: BorderRadius.circular(8), - ), - child: _PickerWidget( - filePickerIndex: _filePickerIndex, - streamChatTheme: _streamChatTheme, - containsFile: _attachmentContainsFile, - selectedMedias: _attachments.keys.toList(), - onAddMoreFilesClick: pickFile, - onMediaSelected: (media) { - if (_attachments.containsKey(media.id)) { - setState(() => _attachments.remove(media.id)); - } else { - _addAssetAttachment(media); - } - }, - ), - ), - ), - ], + ), ), ), ); From aa68a257984dd1b9369b1781b5a464bf5f1de26c Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Thu, 16 Sep 2021 14:10:16 +0200 Subject: [PATCH 051/148] chore(ui): update CHANGELOG.md --- packages/stream_chat_flutter/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 7c316bf6..5dbe7d48 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -4,6 +4,11 @@ - [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case there are no actions available to show. +- [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error. + +🔄 Changed + +- Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. ## 2.2.1 From 145685be4b7a3704de8b3d7195f9f83acd5fa939 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 16 Sep 2021 16:15:41 +0200 Subject: [PATCH 052/148] wip --- .../stream_chat_flutter/example/lib/main.dart | 2 +- .../lib/src/message_input.dart | 120 ++++++++++-------- 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 3d292b09..e980b0cf 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -11,7 +11,7 @@ void main() async { /// from your project dashboard. final client = StreamChatClient( 's2dxdhpxd94g', - logLevel: Level.INFO, + logLevel: Level.OFF, ); /// Set the current user and connect the websocket. In a production diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 296fb889..4cd6d7ee 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -324,7 +324,9 @@ class MessageInputState extends State { late final FocusNode _focusNode; bool _inputEnabled = true; bool _commandEnabled = false; - Widget? _commandsOverlay, _mentionsOverlay, _emojiOverlay; + bool _showCommandsOverlay = false; + bool _showEmojiOverlay = false; + bool _showMentionsOverlay = false; Command? _chosenCommand; bool _actionsShrunk = false; @@ -473,26 +475,33 @@ class MessageInputState extends State { ); } return AnchoredOverlay( - showOverlay: _commandsOverlay != null || - _emojiOverlay != null || - _mentionsOverlay != null, - overlayBuilder: (context, offset) { - late Widget? child; - - if (_commandsOverlay != null) { - child = _commandsOverlay; - } else if (_emojiOverlay != null) { - child = _emojiOverlay; - } else { - child = _mentionsOverlay; - } - - return CenterAbout( + showOverlay: _showMentionsOverlay, + overlayBuilder: (context, offset) => CenterAbout( + position: Offset(offset.dx, offset.dy), + child: _buildMentionsOverlayEntry(), + ), + child: AnchoredOverlay( + showOverlay: _showCommandsOverlay, + overlayBuilder: (context, offset) => CenterAbout( position: Offset(offset.dx, offset.dy), - child: child!, - ); - }, - child: child, + child: _buildCommandsOverlayEntry(), + ), + child: AnchoredOverlay( + showOverlay: textEditingController.text.isNotEmpty && + textEditingController.selection.baseOffset > 0 && + textEditingController.text + .substring( + 0, + textEditingController.selection.baseOffset, + ) + .contains(':'), + overlayBuilder: (context, offset) => CenterAbout( + position: Offset(offset.dx, offset.dy), + child: _buildEmojiOverlay(), + ), + child: child, + ), + ), ); } @@ -827,10 +836,6 @@ class MessageInputState extends State { .keyStroke(widget.parentMessage?.id) .catchError((e) {}); - _commandsOverlay = null; - _mentionsOverlay = null; - _emojiOverlay = null; - _checkCommands(s.trim(), context); _checkMentions(s, context); @@ -840,8 +845,8 @@ class MessageInputState extends State { setState(() { _actionsShrunk = s.trim().isNotEmpty && ((widget.actions?.length ?? 0) + - (widget.showCommandsButton ? 1 : 0) + - (widget.disableAttachments ? 0 : 1) > + (widget.showCommandsButton ? 1 : 0) + + (widget.disableAttachments ? 0 : 1) > 1); }); }, @@ -879,8 +884,6 @@ class MessageInputState extends State { if (textToSelection.endsWith(':') && emoji != null) { _chooseEmoji(splits.sublist(0, splits.length - 1), emoji); - } else { - _emojiOverlay = _buildEmojiOverlay(); } } } @@ -893,7 +896,15 @@ class MessageInputState extends State { .split(' ') .last .contains('@')) { - _mentionsOverlay = _buildMentionsOverlayEntry(); + if (!_showMentionsOverlay) { + setState(() { + _showMentionsOverlay = true; + }); + } + } else if (_showMentionsOverlay) { + setState(() { + _showMentionsOverlay = false; + }); } } @@ -910,20 +921,29 @@ class MessageInputState extends State { if (matchedCommandsList.length == 1) { _chosenCommand = matchedCommandsList[0]; textEditingController.clear(); + + if (_showCommandsOverlay) { + setState(() { + _commandEnabled = true; + _showCommandsOverlay = false; + }); + } + } else if (!_showCommandsOverlay) { setState(() { - _commandEnabled = true; + _showCommandsOverlay = true; }); - _commandsOverlay = null; - } else { - _commandsOverlay = _buildCommandsOverlayEntry(); } + } else if (_showCommandsOverlay) { + setState(() { + _showCommandsOverlay = false; + }); } } Widget _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); -// ignore: cast_nullable_to_non_nullable + // ignore: cast_nullable_to_non_nullable final renderObject = context.findRenderObject() as RenderBox; return CommandsOverlay( channel: StreamChannel.of(context).channel, @@ -1143,6 +1163,10 @@ class MessageInputState extends State { } Widget _buildMentionsOverlayEntry() { + if (textEditingController.value.selection.start < 0) { + return const Offstage(); + } + final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split('@'); @@ -1173,7 +1197,7 @@ class MessageInputState extends State { ); _debounce!.cancel(); setState(() { - _mentionsOverlay = null; + _showMentionsOverlay = false; }); }, ); @@ -1208,8 +1232,6 @@ class MessageInputState extends State { offset: rejoin.length, ), ); - - _emojiOverlay = null; } void _setCommand(Command c) { @@ -1217,8 +1239,8 @@ class MessageInputState extends State { setState(() { _chosenCommand = c; _commandEnabled = true; + _showCommandsOverlay = false; }); - _commandsOverlay = null; } Widget _buildReplyToMessage() { @@ -1411,7 +1433,7 @@ class MessageInputState extends State { icon: StreamSvgIcon.lightning( color: s.isNotEmpty ? _streamChatTheme.colorTheme.disabled - : (_commandsOverlay != null + : (_showCommandsOverlay ? _messageInputTheme.actionButtonColor : _messageInputTheme.actionButtonIdleColor), ), @@ -1427,15 +1449,9 @@ class MessageInputState extends State { await Future.delayed(const Duration(milliseconds: 300)); } - if (_commandsOverlay == null) { - setState(() { - _commandsOverlay = _buildCommandsOverlayEntry(); - }); - } else { - setState(() { - _commandsOverlay = null; - }); - } + setState(() { + _showCommandsOverlay = !_showCommandsOverlay; + }); }, ); @@ -1457,9 +1473,8 @@ class MessageInputState extends State { ), splashRadius: 24, onPressed: () async { - _emojiOverlay = null; - _commandsOverlay = null; - _mentionsOverlay = null; + _showCommandsOverlay = false; + _showMentionsOverlay = false; if (_openFilePickerSection) { setState(() => _openFilePickerSection = false); @@ -1745,9 +1760,6 @@ class MessageInputState extends State { _commandEnabled = false; }); - _commandsOverlay = null; - _mentionsOverlay = null; - Message message; if (widget.editMessage != null) { message = widget.editMessage!.copyWith( From f156c2b91b8a516193e18104355f0ae9ae84be7e Mon Sep 17 00:00:00 2001 From: Salih Guler Date: Fri, 17 Sep 2021 09:51:33 +0300 Subject: [PATCH 053/148] fix: Fix the wrong call on byShortName feat: Add hashCode and equals override to Emoji class. --- .../lib/src/emoji/emoji.dart | 53 ++++++++++--- .../test/src/emoji_test.dart | 79 +++++++++++++++++++ 2 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 packages/stream_chat_flutter/test/src/emoji_test.dart diff --git a/packages/stream_chat_flutter/lib/src/emoji/emoji.dart b/packages/stream_chat_flutter/lib/src/emoji/emoji.dart index 38af85af..36717000 100644 --- a/packages/stream_chat_flutter/lib/src/emoji/emoji.dart +++ b/packages/stream_chat_flutter/lib/src/emoji/emoji.dart @@ -26,7 +26,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import 'package:collection/collection.dart' show IterableExtension; +import 'package:collection/collection.dart' + show IterableExtension, ListEquality; /// All Groups enum EmojiGroup { @@ -114240,7 +114241,6 @@ final emojiRegex = RegExp( class Emoji { static const variationSelector16 = 65039; static const ZWJ = 8205; - final String? name; final String? char; final String? shortName; @@ -114252,14 +114252,39 @@ class Emoji { /// Emoji class. /// [name] of emoji. [char] and character of emoji. [shortName] and a digest name of emoji, [emojiGroup] is emoji's group and [emojiSubgroup] is emoji's subgroup. [keywords] list of keywords for emoji. [modifiable] `true` if emoji has skin. - Emoji( - {this.name, - this.char, - this.shortName, - this.emojiGroup, - this.emojiSubgroup, - this.keywords = const [], - this.modifiable = false}); + Emoji({ + this.name, + this.char, + this.shortName, + this.emojiGroup, + this.emojiSubgroup, + this.keywords = const [], + this.modifiable = false, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Emoji && + runtimeType == other.runtimeType && + name == other.name && + char == other.char && + shortName == other.shortName && + emojiGroup == other.emojiGroup && + emojiSubgroup == other.emojiSubgroup && + const ListEquality().equals(keywords, other.keywords) && + modifiable == other.modifiable; + + @override + int get hashCode => Object.hash( + name.hashCode, + char.hashCode, + shortName.hashCode, + emojiGroup.hashCode, + emojiSubgroup.hashCode, + keywords.hashCode, + modifiable.hashCode, + ); /// Runes of Emoji Character List get charRunes { @@ -114339,9 +114364,11 @@ class Emoji { return _emojis.firstWhereOrNull((Emoji emoji) => emoji.name == name); } - /// Returns Emoji by [name] as short name. - static Emoji? byShortName(String name) { - return _emojis.firstWhereOrNull((Emoji emoji) => emoji.char == name); + /// Returns Emoji by [shortName] as short name. + static Emoji? byShortName(String shortName) { + return _emojis.firstWhereOrNull( + (Emoji emoji) => emoji.shortName == shortName, + ); } /// Returns list of Emojis in a same [group] diff --git a/packages/stream_chat_flutter/test/src/emoji_test.dart b/packages/stream_chat_flutter/test/src/emoji_test.dart new file mode 100644 index 00000000..61a0ba58 --- /dev/null +++ b/packages/stream_chat_flutter/test/src/emoji_test.dart @@ -0,0 +1,79 @@ +import 'package:stream_chat_flutter/src/emoji/emoji.dart'; +import 'package:test/test.dart'; + +void main() { + group('src/emoji', () { + test('${Emoji.byShortName} should bring the correct emoji', () { + final resultEmoji = Emoji.byShortName('smiley'); + expect(resultEmoji, _mockEmoji); + }); + }); +} + +final _mockEmoji = Emoji( + name: 'grinning face with big eyes', + char: '\u{1F603}', + shortName: 'smiley', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'mouth', + 'open', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'good', + 'smile', + 'teeth', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'smiles', + 'dentist', + ':-D', + '=D' + ]); From 7d362db390d00bde1b0f2a1af4e998bda218a2f8 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 17 Sep 2021 18:34:00 +0530 Subject: [PATCH 054/148] fix(ui): check scrollController is attached before calling jump. --- .../lib/src/message_list_view.dart | 10 ++-- .../test/src/message_list_view_test.dart | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 packages/stream_chat_flutter/test/src/message_list_view_test.dart 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 e325598d..c6786a4b 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -1199,10 +1199,12 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); + if (_scrollController?.isAttached ?? false) { + _scrollController?.jumpTo( + index: initialIndex, + alignment: initialAlignment, + ); + } }); _messageNewListener = diff --git a/packages/stream_chat_flutter/test/src/message_list_view_test.dart b/packages/stream_chat_flutter/test/src/message_list_view_test.dart new file mode 100644 index 00000000..8217f6de --- /dev/null +++ b/packages/stream_chat_flutter/test/src/message_list_view_test.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +import 'mocks.dart'; + +void main() { + late StreamChatClient client; + late Channel channel; + + setUp(() { + client = MockClient(); + channel = MockChannel(); + when(() => channel.on(any(), any(), any(), any())) + .thenAnswer((_) => const Stream.empty()); + final channelClientState = MockChannelState(); + when(() => channel.state).thenReturn(channelClientState); + when(() => channelClientState.threadsStream) + .thenAnswer((_) => const Stream.empty()); + when(() => channelClientState.messagesStream) + .thenAnswer((_) => const Stream.empty()); + when(() => channelClientState.messages).thenReturn([]); + when(() => channelClientState.isUpToDate).thenReturn(true); + }); + + // https://github.com/GetStream/stream-chat-flutter/issues/674 + testWidgets('renders empty message list view', (tester) async { + const emptyWidgetKey = Key('empty_widget'); + + await tester.pumpWidget( + MaterialApp( + home: StreamChat( + client: client, + child: StreamChannel( + channel: channel, + child: MessageListView( + emptyBuilder: (_) => Container(key: emptyWidgetKey), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(MessageListView), findsOneWidget); + expect(find.byKey(emptyWidgetKey), findsOneWidget); + }); +} From e7b4681c331f335cdaa9e1cd3a380798c5e1468e Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 17 Sep 2021 18:41:23 +0530 Subject: [PATCH 055/148] update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 59463470..7f9af105 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -5,6 +5,7 @@ - [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case there are no actions available to show. - [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error. +- [[#674]](https://github.com/GetStream/stream-chat-flutter/issues/674): Check scrollController is attached before calling jump in MessageListView. 🔄 Changed From 3f068090208b0adf71f64787613845ddcd05d55d Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 17 Sep 2021 19:46:25 +0530 Subject: [PATCH 056/148] Update packages/stream_chat_flutter/lib/src/message_list_view.dart Co-authored-by: Salvatore Giordano --- 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 c6786a4b..61053106 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -1199,7 +1199,7 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - if (_scrollController?.isAttached ?? false) { + if (_scrollController?.isAttached == true) { _scrollController?.jumpTo( index: initialIndex, alignment: initialAlignment, From 92f5153b6b7632dbbacdeba9b3adb687a5f385a6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 11:33:42 +0200 Subject: [PATCH 057/148] refactor(ui): refactor overlays --- .../stream_chat/lib/src/client/channel.dart | 11 ++- .../lib/src/anchored_overlay.dart | 87 +++++++++++++------ .../lib/src/attachment/giphy_attachment.dart | 3 +- .../lib/src/mentions_overlay.dart | 13 +-- .../lib/src/message_input.dart | 81 ++++++++--------- .../lib/src/message_widget.dart | 19 ++-- packages/stream_chat_flutter/pubspec.yaml | 1 - 7 files changed, 125 insertions(+), 90 deletions(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index c2c40018..decc1c7d 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1081,10 +1081,10 @@ class Channel { // remove regular message if present if (oldIndex != -1) { final oldMessage = state!.messages[oldIndex]; - state!.updateChannelState(state!._channelState.copyWith( - messages: state?.messages?..remove(oldMessage), + state!._channelState = state!._channelState.copyWith( + messages: [...(state!.messages..remove(oldMessage))], channel: state?._channelState.channel, - )); + ); } else { // remove thread message if present // also reduces total reply count @@ -1773,9 +1773,8 @@ class ChannelClientState { List get messages => _channelState.messages; /// Channel message list as a stream. - Stream> get messagesStream => channelStateStream - .map((cs) => cs.messages) - .distinct(const ListEquality().equals); + Stream> get messagesStream => + channelStateStream.map((cs) => cs.messages); /// Channel pinned message list. List get pinnedMessages => _channelState.pinnedMessages.toList(); diff --git a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart index d79e19df..540fe2ec 100644 --- a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart @@ -1,43 +1,62 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; -/// Class to create an overlay anchored to a child widget -class AnchoredOverlay extends StatelessWidget { - /// Constructor for creating an [AnchoredOverlay] - const AnchoredOverlay({ - Key? key, - this.showOverlay = false, +/// Options for building an [Overlay] +class OverlayOption { + /// Constructor for creating an [OverlayOption]. + OverlayOption({ + required this.showOverlay, required this.overlayBuilder, - required this.child, - }) : super(key: key); + }); /// Show or hide overlay final bool showOverlay; /// Builder for creating overlay widget final Widget Function(BuildContext, Offset anchor) overlayBuilder; +} + +/// Class to create an overlay anchored to a child widget +class AnchoredOverlay extends StatelessWidget { + /// Constructor for creating an [AnchoredOverlay] + const AnchoredOverlay({ + Key? key, + required this.overlayOptions, + required this.child, + }) : super(key: key); + + /// Builder for creating overlay widget + final List overlayOptions; /// Child to which overlay is anchored final Widget child; @override - Widget build(BuildContext context) => SizedBox( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) => - OverlayBuilder( - showOverlay: showOverlay, - overlayBuilder: (BuildContext overlayContext) { - final box = context.findRenderObject() as RenderBox?; - if (box != null) { - final center = box.size - .center(box.localToGlobal(const Offset(0, 0))); - return overlayBuilder(overlayContext, center); - } else { - return const SizedBox(); - } - }, - child: child, - )), - ); + Widget build(BuildContext context) { + final overlay = + overlayOptions.firstWhereOrNull((option) => option.showOverlay); + return SizedBox( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => + OverlayBuilder( + showOverlay: overlay != null, + overlayBuilder: (BuildContext overlayContext) { + final box = context.findRenderObject() as RenderBox?; + if (box != null) { + final center = + box.size.center(box.localToGlobal(const Offset(0, 0))); + return overlay?.overlayBuilder(overlayContext, center) ?? + const Offstage(); + } else { + return const Offstage(); + } + }, + child: child, + ), + ), + ); + } } /// Class for creating a declarative-ish overlay @@ -63,18 +82,31 @@ class OverlayBuilder extends StatefulWidget { _OverlayBuilderState createState() => _OverlayBuilderState(); } -class _OverlayBuilderState extends State { +class _OverlayBuilderState extends State + with WidgetsBindingObserver { OverlayEntry? overlayEntry; @override void initState() { super.initState(); + WidgetsBinding.instance!.addObserver(this); + if (widget.showOverlay) { WidgetsBinding.instance?.addPostFrameCallback((_) => showOverlay()); } } + @override + void didChangeMetrics() { + super.didChangeMetrics(); + + SchedulerBinding.instance!.addPostFrameCallback((_) { + hideOverlay(); + syncWidgetAndOverlay(); + }); + } + @override void didUpdateWidget(OverlayBuilder oldWidget) { super.didUpdateWidget(oldWidget); @@ -91,6 +123,7 @@ class _OverlayBuilderState extends State { @override void dispose() { + WidgetsBinding.instance!.removeObserver(this); if (isShowingOverlay()) { hideOverlay(); } diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index e165fc6d..dacf7fbe 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -2,10 +2,10 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/visible_footnote.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/extension.dart'; /// Widget for showing a GIF attachment class GiphyAttachment extends AttachmentWidget { @@ -48,6 +48,7 @@ class GiphyAttachment extends AttachmentWidget { } Widget _buildSendingAttachment(BuildContext context, String imageUrl) { + print('sending attachment GIPHY $imageUrl'); final streamChannel = StreamChannel.of(context); return Column( mainAxisSize: MainAxisSize.min, diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart index 57d286d9..b51f8261 100644 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -49,7 +49,7 @@ class MentionsOverlay extends StatelessWidget { return const SizedBox(); } - final child = Card( + return Card( margin: const EdgeInsets.all(8), elevation: 2, color: _streamChatTheme.colorTheme.barsBg, @@ -96,15 +96,6 @@ class MentionsOverlay extends StatelessWidget { ), ), ); - return TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutExpo, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), - child: child, - ); + ; } } diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 65c23804..2eca6ee7 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -6,18 +6,17 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/commands_overlay.dart'; -import 'package:stream_chat_flutter/src/emoji_overlay.dart'; -import 'package:stream_chat_flutter/src/mentions_overlay.dart'; import 'package:stream_chat_flutter/src/anchored_overlay.dart'; +import 'package:stream_chat_flutter/src/commands_overlay.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; +import 'package:stream_chat_flutter/src/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; +import 'package:stream_chat_flutter/src/mentions_overlay.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; @@ -334,8 +333,6 @@ class MessageInputState extends State { bool _openFilePickerSection = false; int _filePickerIndex = 0; - final _keyboardVisibilityController = KeyboardVisibilityController(); - /// The editing controller passed to the input TextField late final TextEditingController textEditingController; @@ -351,15 +348,6 @@ class MessageInputState extends State { super.initState(); _focusNode = widget.focusNode ?? FocusNode(); - if (!kIsWeb) { - _keyboardListener = - _keyboardVisibilityController.onChange.listen((visible) { - if (_focusNode.hasFocus) { - _onChanged(context, textEditingController.text); - } - }); - } - textEditingController = widget.textEditingController ?? TextEditingController(); if (widget.editMessage != null || widget.initialMessage != null) { @@ -474,20 +462,26 @@ class MessageInputState extends State { child: child, ); } + return AnchoredOverlay( - showOverlay: _showMentionsOverlay, - overlayBuilder: (context, offset) => CenterAbout( - position: Offset(offset.dx, offset.dy), - child: _buildMentionsOverlayEntry(), - ), - child: AnchoredOverlay( - showOverlay: _showCommandsOverlay, - overlayBuilder: (context, offset) => CenterAbout( - position: Offset(offset.dx, offset.dy), - child: _buildCommandsOverlayEntry(), + overlayOptions: [ + OverlayOption( + overlayBuilder: (context, offset) => CenterAbout( + position: Offset(offset.dx, offset.dy), + child: _buildMentionsOverlayEntry(), + ), + showOverlay: _showMentionsOverlay, ), - child: AnchoredOverlay( - showOverlay: textEditingController.text.isNotEmpty && + OverlayOption( + showOverlay: _showCommandsOverlay, + overlayBuilder: (context, offset) => CenterAbout( + position: Offset(offset.dx, offset.dy), + child: _buildCommandsOverlayEntry(), + ), + ), + OverlayOption( + showOverlay: _focusNode.hasFocus && + textEditingController.text.isNotEmpty && textEditingController.selection.baseOffset > 0 && textEditingController.text .substring( @@ -499,9 +493,9 @@ class MessageInputState extends State { position: Offset(offset.dx, offset.dy), child: _buildEmojiOverlay(), ), - child: child, ), - ), + ], + child: child, ); } @@ -817,7 +811,10 @@ class MessageInputState extends State { String? _previousValue; - void _onChanged(BuildContext context, String s) { + void _onChanged( + BuildContext context, + String s, + ) { if (s == _previousValue) { return; } @@ -830,10 +827,13 @@ class MessageInputState extends State { if (!mounted) { return; } - StreamChannel.of(context) - .channel - .keyStroke(widget.parentMessage?.id) - .catchError((e) {}); + + if (s.isNotEmpty) { + StreamChannel.of(context) + .channel + .keyStroke(widget.parentMessage?.id) + .catchError((e) {}); + } _checkCommands(s.trim(), context); @@ -999,7 +999,9 @@ class MessageInputState extends State { } return AnimatedContainer( - duration: const Duration(milliseconds: 300), + duration: _openFilePickerSection + ? const Duration(milliseconds: 300) + : const Duration(), curve: Curves.easeOut, height: _openFilePickerSection ? _kMinMediaPickerSize : 0, child: SingleChildScrollView( @@ -1215,8 +1217,12 @@ class MessageInputState extends State { } Widget _buildEmojiOverlay() { + if (textEditingController.value.selection.baseOffset < 0) { + return const Offstage(); + } + final splits = textEditingController.text - .substring(0, textEditingController.value.selection.start) + .substring(0, textEditingController.value.selection.baseOffset) .split(':'); final query = splits.last.toLowerCase(); @@ -1837,8 +1843,6 @@ class MessageInputState extends State { } } - StreamSubscription? _keyboardListener; - void _showErrorAlert(String description) { showModalBottomSheet( backgroundColor: _streamChatTheme.colorTheme.barsBg, @@ -1912,7 +1916,6 @@ class MessageInputState extends State { @override void dispose() { - _keyboardListener?.cancel(); textEditingController.dispose(); _stopSlowMode(); super.dispose(); diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 01177603..fc56303f 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -580,11 +580,12 @@ class _MessageWidgetState extends State bool get isOnlyEmoji => widget.message.text?.isOnlyEmoji == true; - bool get hasNonUrlAttachments => - widget.message.attachments.where((it) => it.titleLink == null).isNotEmpty; + bool get hasNonUrlAttachments => widget.message.attachments + .where((it) => it.titleLink == null || it.type == 'giphy') + .isNotEmpty; - bool get hasUrlAttachments => - widget.message.attachments.any((it) => it.titleLink != null) == true; + bool get hasUrlAttachments => widget.message.attachments + .any((it) => it.titleLink != null && it.type != 'giphy'); bool get showBottomRow => showThreadReplyIndicator || @@ -603,6 +604,9 @@ class _MessageWidgetState extends State @override Widget build(BuildContext context) { super.build(context); + if (widget.message.isEphemeral) { + print('EPHEMERAL'); + } final avatarWidth = widget.messageTheme.avatarTheme?.constraints.maxWidth ?? 40; final bottomRowPadding = @@ -1153,10 +1157,15 @@ class _MessageWidgetState extends State } Widget _parseAttachments() { + if (widget.message.isEphemeral) { + print('EPHEMERAL'); + } final attachmentGroups = >{}; widget.message.attachments - .where((element) => element.titleLink == null && element.type != null) + .where((element) => + (element.titleLink == null && element.type != null) || + element.type == 'giphy') .forEach((e) { if (attachmentGroups[e.type] == null) { attachmentGroups[e.type!] = []; diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 907e9a7f..54888873 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: file_picker: ^3.0.1 flutter: sdk: flutter - flutter_keyboard_visibility: ^5.0.1 flutter_markdown: ^0.6.1 flutter_portal: ^0.4.0 flutter_slidable: ^0.6.0 From 76671197f72e4e88754427c3e6bc8a32c69a44ad Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 11:35:50 +0200 Subject: [PATCH 058/148] refactor(ui): fix sendAction --- packages/stream_chat/lib/src/client/channel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index decc1c7d..96bd1838 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1082,7 +1082,7 @@ class Channel { if (oldIndex != -1) { final oldMessage = state!.messages[oldIndex]; state!._channelState = state!._channelState.copyWith( - messages: [...(state!.messages..remove(oldMessage))], + messages: state!.messages..remove(oldMessage), channel: state?._channelState.channel, ); } else { From 8730c1b05514603c44ca9c2d9fc6efa9462d9f50 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 11:36:28 +0200 Subject: [PATCH 059/148] fix(ui): fix example logs --- packages/stream_chat_flutter/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index e980b0cf..3d292b09 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -11,7 +11,7 @@ void main() async { /// from your project dashboard. final client = StreamChatClient( 's2dxdhpxd94g', - logLevel: Level.OFF, + logLevel: Level.INFO, ); /// Set the current user and connect the websocket. In a production From d07b7c6cfdf3d2a265406739d373effeeea6bae8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 11:47:08 +0200 Subject: [PATCH 060/148] docs(ui): add code docs --- packages/stream_chat_flutter/lib/src/anchored_overlay.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart index 540fe2ec..ef06cda0 100644 --- a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart @@ -26,7 +26,8 @@ class AnchoredOverlay extends StatelessWidget { required this.child, }) : super(key: key); - /// Builder for creating overlay widget + /// List of overlay options + /// The first one which showOverlay is true will be used final List overlayOptions; /// Child to which overlay is anchored From bba1e817d398ff4f50bfad1bcdeebc8edd59b50b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 11:59:21 +0200 Subject: [PATCH 061/148] fix(ui): remove dead code --- .../lib/src/attachment/giphy_attachment.dart | 1 - packages/stream_chat_flutter/lib/src/message_widget.dart | 6 ------ 2 files changed, 7 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index dacf7fbe..8272a962 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -48,7 +48,6 @@ class GiphyAttachment extends AttachmentWidget { } Widget _buildSendingAttachment(BuildContext context, String imageUrl) { - print('sending attachment GIPHY $imageUrl'); final streamChannel = StreamChannel.of(context); return Column( mainAxisSize: MainAxisSize.min, diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index fc56303f..6731e15a 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -604,9 +604,6 @@ class _MessageWidgetState extends State @override Widget build(BuildContext context) { super.build(context); - if (widget.message.isEphemeral) { - print('EPHEMERAL'); - } final avatarWidth = widget.messageTheme.avatarTheme?.constraints.maxWidth ?? 40; final bottomRowPadding = @@ -1157,9 +1154,6 @@ class _MessageWidgetState extends State } Widget _parseAttachments() { - if (widget.message.isEphemeral) { - print('EPHEMERAL'); - } final attachmentGroups = >{}; widget.message.attachments From a4efbbaa9f74a82e1b88fc1d569e068502bc4e71 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 12:07:08 +0200 Subject: [PATCH 062/148] feat(ui): add customOverlays to `MessageInput` --- packages/stream_chat_flutter/CHANGELOG.md | 6 +++++- packages/stream_chat_flutter/lib/src/message_input.dart | 5 ++++- packages/stream_chat_flutter/lib/stream_chat_flutter.dart | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 4fa15914..581101e6 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -5,11 +5,16 @@ - [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case there are no actions available to show. - [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error. +- `MessageInput` overlays now follow the `MessageInput` focus. 🔄 Changed - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. +✅ Added + +- Added `MessageInput.customOverlays` property to add custom overlays to the `MessageInput`. + ## 2.2.1 ⚠️ Deprecated @@ -109,7 +114,6 @@ upgraded with some goodies like `lerp` functions. Here's the full naming breakdo - Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set. - Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when sending a message with no text. -- MessageInput can now be placed in any position on the screen. ## 2.1.2 diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 2eca6ee7..6257a2c8 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -192,6 +192,7 @@ class MessageInput extends StatefulWidget { this.onAttachmentLimitExceed, this.attachmentButtonBuilder, this.commandButtonBuilder, + this.customOverlays = const [], }) : assert( initialMessage == null || editMessage == null, "Can't provide both `initialMessage` and `editMessage`", @@ -222,6 +223,9 @@ class MessageInput extends StatefulWidget { /// Use this to transform the message final FutureOr Function(Message)? preMessageSending; + /// List of custom overlays + final List customOverlays; + /// Parent message in case of a thread final Message? parentMessage; @@ -324,7 +328,6 @@ class MessageInputState extends State { bool _inputEnabled = true; bool _commandEnabled = false; bool _showCommandsOverlay = false; - bool _showEmojiOverlay = false; bool _showMentionsOverlay = false; Command? _chosenCommand; diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 42188fa0..694293a1 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -1,6 +1,7 @@ export 'package:jiffy/jiffy.dart'; export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; +export 'src/anchored_overlay.dart'; export 'src/attachment/attachment.dart'; export 'src/back_button.dart'; export 'src/channel_avatar.dart'; From 829c7042cceab550a65966c294c4884c7350bf52 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 13:35:49 +0200 Subject: [PATCH 063/148] fix(ui): fix ios --- .../lib/src/anchored_overlay.dart | 16 ++++++++++++---- .../lib/src/message_input.dart | 7 ++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart index ef06cda0..9a7f45ee 100644 --- a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart @@ -98,14 +98,22 @@ class _OverlayBuilderState extends State } } + EdgeInsets? _previousInsets; + @override void didChangeMetrics() { super.didChangeMetrics(); - SchedulerBinding.instance!.addPostFrameCallback((_) { - hideOverlay(); - syncWidgetAndOverlay(); - }); + final newInsets = MediaQuery.of(context).viewPadding; + + if (newInsets.bottom != _previousInsets?.bottom) { + WidgetsBinding.instance!.addPostFrameCallback((_) { + hideOverlay(); + syncWidgetAndOverlay(); + }); + + _previousInsets = newInsets; + } } @override diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 6257a2c8..837e88c4 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -470,7 +470,7 @@ class MessageInputState extends State { overlayOptions: [ OverlayOption( overlayBuilder: (context, offset) => CenterAbout( - position: Offset(offset.dx, offset.dy), + position: offset, child: _buildMentionsOverlayEntry(), ), showOverlay: _showMentionsOverlay, @@ -478,7 +478,7 @@ class MessageInputState extends State { OverlayOption( showOverlay: _showCommandsOverlay, overlayBuilder: (context, offset) => CenterAbout( - position: Offset(offset.dx, offset.dy), + position: offset, child: _buildCommandsOverlayEntry(), ), ), @@ -493,10 +493,11 @@ class MessageInputState extends State { ) .contains(':'), overlayBuilder: (context, offset) => CenterAbout( - position: Offset(offset.dx, offset.dy), + position: offset, child: _buildEmojiOverlay(), ), ), + ...widget.customOverlays, ], child: child, ); From e10da9b245866fa0299ed34138eb8d6564298f2a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 20 Sep 2021 17:33:12 +0530 Subject: [PATCH 064/148] ref(llc): update `lastSyncedAt` only after calling sync. Signed-off-by: xsahil03x --- packages/stream_chat/lib/src/client/client.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 9487e6e7..15b88ce2 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -406,10 +406,6 @@ class StreamChatClient { if (event.type == EventType.healthCheck) { return _handleHealthCheckEvent(event); } - if (!event.isLocal && _synced) { - _lastSyncedAt = event.createdAt; - _chatPersistenceClient?.updateLastSyncAt(event.createdAt); - } state.updateUser(event.user); return _eventController.add(event); } @@ -1325,6 +1321,7 @@ class StreamChatClient { // resetting state state.dispose(); state = ClientState(this); + _lastSyncedAt = null; // resetting credentials _tokenManager.reset(); From 59ded0e033ba315239466b4a9a1ff8544aa46e10 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 15:51:47 +0200 Subject: [PATCH 065/148] fix(ui): fix `MessageListView` header and footer while reverse: false --- .../lib/src/message_list_view.dart | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) 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 61053106..77938032 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -494,14 +494,18 @@ class _MessageListViewState extends State { return _buildThreadSeparator(); } if (i == itemCount - 3) { - if (widget.headerBuilder == null) { + if (widget.reverse + ? widget.headerBuilder == null + : widget.footerBuilder == null) { if (_isThreadConversation) return const Offstage(); return const SizedBox(height: 52); } return const SizedBox(height: 8); } if (i == 0) { - if (widget.footerBuilder == null) { + if (widget.reverse + ? widget.footerBuilder == null + : widget.headerBuilder == null) { return const SizedBox(height: 30); } return const SizedBox(height: 8); @@ -560,8 +564,13 @@ class _MessageListViewState extends State { } if (i == itemCount - 2) { - return widget.headerBuilder?.call(context) ?? - const Offstage(); + if (widget.reverse) { + return widget.headerBuilder?.call(context) ?? + const Offstage(); + } else { + return widget.footerBuilder?.call(context) ?? + const Offstage(); + } } if (i == itemCount - 3) { @@ -579,8 +588,13 @@ class _MessageListViewState extends State { } if (i == 0) { - return widget.footerBuilder?.call(context) ?? - const Offstage(); + if (widget.reverse) { + return widget.footerBuilder?.call(context) ?? + const Offstage(); + } else { + return widget.headerBuilder?.call(context) ?? + const Offstage(); + } } const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer @@ -1199,12 +1213,10 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - if (_scrollController?.isAttached == true) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); - } + _scrollController?.jumpTo( + index: initialIndex, + alignment: initialAlignment, + ); }); _messageNewListener = From 9774ee336b820a72c7c363b8dd92a7f89a81d560 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 15:54:04 +0200 Subject: [PATCH 066/148] fix(ui): fix `MessageListView` header and footer while reverse: false --- .../lib/src/message_list_view.dart | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) 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 61053106..77938032 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -494,14 +494,18 @@ class _MessageListViewState extends State { return _buildThreadSeparator(); } if (i == itemCount - 3) { - if (widget.headerBuilder == null) { + if (widget.reverse + ? widget.headerBuilder == null + : widget.footerBuilder == null) { if (_isThreadConversation) return const Offstage(); return const SizedBox(height: 52); } return const SizedBox(height: 8); } if (i == 0) { - if (widget.footerBuilder == null) { + if (widget.reverse + ? widget.footerBuilder == null + : widget.headerBuilder == null) { return const SizedBox(height: 30); } return const SizedBox(height: 8); @@ -560,8 +564,13 @@ class _MessageListViewState extends State { } if (i == itemCount - 2) { - return widget.headerBuilder?.call(context) ?? - const Offstage(); + if (widget.reverse) { + return widget.headerBuilder?.call(context) ?? + const Offstage(); + } else { + return widget.footerBuilder?.call(context) ?? + const Offstage(); + } } if (i == itemCount - 3) { @@ -579,8 +588,13 @@ class _MessageListViewState extends State { } if (i == 0) { - return widget.footerBuilder?.call(context) ?? - const Offstage(); + if (widget.reverse) { + return widget.footerBuilder?.call(context) ?? + const Offstage(); + } else { + return widget.headerBuilder?.call(context) ?? + const Offstage(); + } } const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer @@ -1199,12 +1213,10 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - if (_scrollController?.isAttached == true) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); - } + _scrollController?.jumpTo( + index: initialIndex, + alignment: initialAlignment, + ); }); _messageNewListener = From 0f0344ddf8c3d7941660b4c4756b70dbe3509c2c Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 15:54:15 +0200 Subject: [PATCH 067/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 7f9af105..1c39ec47 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -6,6 +6,7 @@ there are no actions available to show. - [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error. - [[#674]](https://github.com/GetStream/stream-chat-flutter/issues/674): Check scrollController is attached before calling jump in MessageListView. +- Fixed `MessageListView` header and footer when `reverse: false`. 🔄 Changed From 8cb95cfd7ccafa96320ddb88200e08066a1782eb Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 15:54:50 +0200 Subject: [PATCH 068/148] Revert "fix(ui): fix `MessageListView` header and footer while reverse: false" This reverts commit 59ded0e033ba315239466b4a9a1ff8544aa46e10. --- .../lib/src/message_list_view.dart | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) 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 77938032..61053106 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -494,18 +494,14 @@ class _MessageListViewState extends State { return _buildThreadSeparator(); } if (i == itemCount - 3) { - if (widget.reverse - ? widget.headerBuilder == null - : widget.footerBuilder == null) { + if (widget.headerBuilder == null) { if (_isThreadConversation) return const Offstage(); return const SizedBox(height: 52); } return const SizedBox(height: 8); } if (i == 0) { - if (widget.reverse - ? widget.footerBuilder == null - : widget.headerBuilder == null) { + if (widget.footerBuilder == null) { return const SizedBox(height: 30); } return const SizedBox(height: 8); @@ -564,13 +560,8 @@ class _MessageListViewState extends State { } if (i == itemCount - 2) { - if (widget.reverse) { - return widget.headerBuilder?.call(context) ?? - const Offstage(); - } else { - return widget.footerBuilder?.call(context) ?? - const Offstage(); - } + return widget.headerBuilder?.call(context) ?? + const Offstage(); } if (i == itemCount - 3) { @@ -588,13 +579,8 @@ class _MessageListViewState extends State { } if (i == 0) { - if (widget.reverse) { - return widget.footerBuilder?.call(context) ?? - const Offstage(); - } else { - return widget.headerBuilder?.call(context) ?? - const Offstage(); - } + return widget.footerBuilder?.call(context) ?? + const Offstage(); } const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer @@ -1213,10 +1199,12 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); + if (_scrollController?.isAttached == true) { + _scrollController?.jumpTo( + index: initialIndex, + alignment: initialAlignment, + ); + } }); _messageNewListener = From 6e8c8624b1f1ae4fb502367561e359df3087cb28 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 16:00:32 +0200 Subject: [PATCH 069/148] fix(ui): remove default padding in date divider --- .../lib/src/message_list_view.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 61053106..cf39a97a 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -525,13 +525,13 @@ class _MessageListViewState extends State { ? widget.dateDividerBuilder!( nextMessage.createdAt.toLocal(), ) - : DateDivider( - dateTime: nextMessage.createdAt.toLocal(), + : Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: DateDivider( + dateTime: nextMessage.createdAt.toLocal(), + ), ); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: divider, - ); + return divider; } final timeDiff = Jiffy(nextMessage.createdAt.toLocal()).diff( From 98ae2dd2ce0158c4c98eafb3f4e5d6e29ca63040 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 20 Sep 2021 16:01:13 +0200 Subject: [PATCH 070/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 7f9af105..614b47f1 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -10,6 +10,7 @@ 🔄 Changed - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. +- Removed default padding in `DateDivider` in `MessageListView` ## 2.2.1 From 3da495a87230a237a74df0631bdb1cb33cb1cf7f Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 21 Sep 2021 09:46:46 +0200 Subject: [PATCH 071/148] refactor(ui): use portal --- packages/stream_chat_flutter/CHANGELOG.md | 4 - .../lib/src/anchored_overlay.dart | 206 ------------------ .../lib/src/mentions_overlay.dart | 2 +- .../lib/src/message_input.dart | 70 +++--- .../lib/stream_chat_flutter.dart | 1 - 5 files changed, 31 insertions(+), 252 deletions(-) delete mode 100644 packages/stream_chat_flutter/lib/src/anchored_overlay.dart diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index d4a755a5..c500cb5e 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -12,10 +12,6 @@ - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. -✅ Added - -- Added `MessageInput.customOverlays` property to add custom overlays to the `MessageInput`. - ## 2.2.1 ⚠️ Deprecated diff --git a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart b/packages/stream_chat_flutter/lib/src/anchored_overlay.dart deleted file mode 100644 index 9a7f45ee..00000000 --- a/packages/stream_chat_flutter/lib/src/anchored_overlay.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; - -/// Options for building an [Overlay] -class OverlayOption { - /// Constructor for creating an [OverlayOption]. - OverlayOption({ - required this.showOverlay, - required this.overlayBuilder, - }); - - /// Show or hide overlay - final bool showOverlay; - - /// Builder for creating overlay widget - final Widget Function(BuildContext, Offset anchor) overlayBuilder; -} - -/// Class to create an overlay anchored to a child widget -class AnchoredOverlay extends StatelessWidget { - /// Constructor for creating an [AnchoredOverlay] - const AnchoredOverlay({ - Key? key, - required this.overlayOptions, - required this.child, - }) : super(key: key); - - /// List of overlay options - /// The first one which showOverlay is true will be used - final List overlayOptions; - - /// Child to which overlay is anchored - final Widget child; - - @override - Widget build(BuildContext context) { - final overlay = - overlayOptions.firstWhereOrNull((option) => option.showOverlay); - return SizedBox( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) => - OverlayBuilder( - showOverlay: overlay != null, - overlayBuilder: (BuildContext overlayContext) { - final box = context.findRenderObject() as RenderBox?; - if (box != null) { - final center = - box.size.center(box.localToGlobal(const Offset(0, 0))); - return overlay?.overlayBuilder(overlayContext, center) ?? - const Offstage(); - } else { - return const Offstage(); - } - }, - child: child, - ), - ), - ); - } -} - -/// Class for creating a declarative-ish overlay -class OverlayBuilder extends StatefulWidget { - /// Constructor for creating an [OverlayBuilder] - const OverlayBuilder({ - Key? key, - this.showOverlay = false, - required this.overlayBuilder, - required this.child, - }) : super(key: key); - - /// Show or hide overlay - final bool showOverlay; - - /// Builder for creating overlay widget - final Widget Function(BuildContext) overlayBuilder; - - /// Child to which overlay is anchored - final Widget child; - - @override - _OverlayBuilderState createState() => _OverlayBuilderState(); -} - -class _OverlayBuilderState extends State - with WidgetsBindingObserver { - OverlayEntry? overlayEntry; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance!.addObserver(this); - - if (widget.showOverlay) { - WidgetsBinding.instance?.addPostFrameCallback((_) => showOverlay()); - } - } - - EdgeInsets? _previousInsets; - - @override - void didChangeMetrics() { - super.didChangeMetrics(); - - final newInsets = MediaQuery.of(context).viewPadding; - - if (newInsets.bottom != _previousInsets?.bottom) { - WidgetsBinding.instance!.addPostFrameCallback((_) { - hideOverlay(); - syncWidgetAndOverlay(); - }); - - _previousInsets = newInsets; - } - } - - @override - void didUpdateWidget(OverlayBuilder oldWidget) { - super.didUpdateWidget(oldWidget); - WidgetsBinding.instance - ?.addPostFrameCallback((_) => syncWidgetAndOverlay()); - } - - @override - void reassemble() { - super.reassemble(); - WidgetsBinding.instance - ?.addPostFrameCallback((_) => syncWidgetAndOverlay()); - } - - @override - void dispose() { - WidgetsBinding.instance!.removeObserver(this); - if (isShowingOverlay()) { - hideOverlay(); - } - - super.dispose(); - } - - bool isShowingOverlay() => overlayEntry != null; - - void showOverlay() { - overlayEntry = OverlayEntry( - builder: widget.overlayBuilder, - ); - if (overlayEntry != null) { - addToOverlay(overlayEntry!); - } - } - - void addToOverlay(OverlayEntry entry) async { - Overlay.of(context)?.insert(entry); - } - - void hideOverlay() { - overlayEntry?.remove(); - overlayEntry = null; - } - - void syncWidgetAndOverlay() { - if (isShowingOverlay() && !widget.showOverlay) { - hideOverlay(); - } else if (!isShowingOverlay() && widget.showOverlay) { - showOverlay(); - } - } - - @override - Widget build(BuildContext context) => widget.child; -} - -/// Class to position Overlay -class CenterAbout extends StatelessWidget { - /// Constructor for creating an [CenterAbout] - const CenterAbout({ - Key? key, - required this.position, - required this.child, - }) : super(key: key); - - /// Position of child - final Offset position; - - /// Child widget to which overlay is attached - final Widget child; - - @override - Widget build(BuildContext context) { - final size = MediaQuery.of(context).size; - - return Positioned( - bottom: size.height - position.dy, - left: position.dx, - child: FractionalTranslation( - translation: const Offset(-0.5, 0), - child: Padding( - padding: const EdgeInsets.only(bottom: 32), - child: child, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart index b51f8261..41b25d83 100644 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -83,7 +83,7 @@ class MentionsOverlay extends StatelessWidget { }, child: mentionsTileBuilder != null ? mentionsTileBuilder!(context, m) - : MentionTile(m), + : MentionTile(m, key: ValueKey(m.user?.id)), ), ), ) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 837e88c4..f8c83d02 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -6,11 +6,11 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/anchored_overlay.dart'; import 'package:stream_chat_flutter/src/commands_overlay.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji_overlay.dart'; @@ -192,7 +192,6 @@ class MessageInput extends StatefulWidget { this.onAttachmentLimitExceed, this.attachmentButtonBuilder, this.commandButtonBuilder, - this.customOverlays = const [], }) : assert( initialMessage == null || editMessage == null, "Can't provide both `initialMessage` and `editMessage`", @@ -223,9 +222,6 @@ class MessageInput extends StatefulWidget { /// Use this to transform the message final FutureOr Function(Message)? preMessageSending; - /// List of custom overlays - final List customOverlays; - /// Parent message in case of a thread final Message? parentMessage; @@ -466,40 +462,32 @@ class MessageInputState extends State { ); } - return AnchoredOverlay( - overlayOptions: [ - OverlayOption( - overlayBuilder: (context, offset) => CenterAbout( - position: offset, - child: _buildMentionsOverlayEntry(), - ), - showOverlay: _showMentionsOverlay, + return PortalEntry( + portal: _buildCommandsOverlayEntry(), + visible: _showCommandsOverlay, + childAnchor: Alignment.topCenter, + portalAnchor: Alignment.bottomCenter, + child: PortalEntry( + portal: _buildEmojiOverlay(), + visible: _focusNode.hasFocus && + textEditingController.text.isNotEmpty && + textEditingController.selection.baseOffset > 0 && + textEditingController.text + .substring( + 0, + textEditingController.selection.baseOffset, + ) + .contains(':'), + childAnchor: Alignment.topCenter, + portalAnchor: Alignment.bottomCenter, + child: PortalEntry( + portal: _buildMentionsOverlayEntry(), + visible: _showMentionsOverlay, + childAnchor: Alignment.topCenter, + portalAnchor: Alignment.bottomCenter, + child: child, ), - OverlayOption( - showOverlay: _showCommandsOverlay, - overlayBuilder: (context, offset) => CenterAbout( - position: offset, - child: _buildCommandsOverlayEntry(), - ), - ), - OverlayOption( - showOverlay: _focusNode.hasFocus && - textEditingController.text.isNotEmpty && - textEditingController.selection.baseOffset > 0 && - textEditingController.text - .substring( - 0, - textEditingController.selection.baseOffset, - ) - .contains(':'), - overlayBuilder: (context, offset) => CenterAbout( - position: offset, - child: _buildEmojiOverlay(), - ), - ), - ...widget.customOverlays, - ], - child: child, + ), ); } @@ -946,8 +934,10 @@ class MessageInputState extends State { Widget _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); - // ignore: cast_nullable_to_non_nullable - final renderObject = context.findRenderObject() as RenderBox; + final renderObject = context.findRenderObject() as RenderBox?; + if (renderObject == null) { + return const Offstage(); + } return CommandsOverlay( channel: StreamChannel.of(context).channel, size: Size(renderObject.size.width - 16, 400), diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 694293a1..42188fa0 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -1,7 +1,6 @@ export 'package:jiffy/jiffy.dart'; export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -export 'src/anchored_overlay.dart'; export 'src/attachment/attachment.dart'; export 'src/back_button.dart'; export 'src/channel_avatar.dart'; From e0b23653b798ed017487a2988044308653d67814 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 21 Sep 2021 14:40:25 +0200 Subject: [PATCH 072/148] fix(ui): fix review --- packages/stream_chat_flutter/lib/src/mentions_overlay.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart index 41b25d83..63c43585 100644 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart @@ -96,6 +96,5 @@ class MentionsOverlay extends StatelessWidget { ), ), ); - ; } } From 403d0e62a090d7b916d8fa705b0c54661ce26375 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 21 Sep 2021 18:25:21 +0530 Subject: [PATCH 073/148] fix(llc): Connecting user without providing `name` uses `id` instead for setting `user.name`. Signed-off-by: xsahil03x --- packages/stream_chat/CHANGELOG.md | 5 ++++- .../lib/src/core/models/own_user.dart | 4 ---- .../test/src/core/models/own_user_test.dart | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 9c925f88..e0c0f693 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -16,13 +16,16 @@ - Added support for `next`, `previous` value pagination in `client.search` , [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination) - `Attachment` class now has a `fileSize` and `mimeType` property. Setting a `file` will also set the `file_size` - , `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype` and `attachment.extraData['file_size']` + , `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype` + and `attachment.extraData['file_size']` , `attachment.extraData['mime_type]` is same respectively. 🐞 Fixed - [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not updating correctly. - Fix `Filter.empty()` json encoding. +- [[#700]](https://github.com/GetStream/stream-chat-flutter/issues/700) Connecting user without providing `name` + uses `id` instead for setting `user.name`. ## 2.2.1 diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index 07c6cf30..eb64eed3 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -54,8 +54,6 @@ class OwnUser extends User { factory OwnUser.fromUser(User user) => OwnUser( id: user.id, role: user.role, - name: user.name, - image: user.image, createdAt: user.createdAt, updatedAt: user.updatedAt, lastActive: user.lastActive, @@ -116,8 +114,6 @@ class OwnUser extends User { return copyWith( id: other.id, role: other.role, - name: other.name, - image: other.image, banned: other.banned, channelMutes: other.channelMutes, createdAt: other.createdAt, diff --git a/packages/stream_chat/test/src/core/models/own_user_test.dart b/packages/stream_chat/test/src/core/models/own_user_test.dart index e1d12ada..ea84185f 100644 --- a/packages/stream_chat/test/src/core/models/own_user_test.dart +++ b/packages/stream_chat/test/src/core/models/own_user_test.dart @@ -175,5 +175,26 @@ void main() { expect(newUser.teams, ['team1', 'team2']); expect(newUser.language, 'fr'); }); + + test( + 'fromUser should not override name with id if not available in extraData', + () { + final user = User(id: 'test-id'); + expect(user.id, 'test-id'); + expect(user.name, 'test-id'); + + final encodedUser = user.toJson(); + expect(encodedUser['id'], 'test-id'); + expect(encodedUser['name'], null); + + final ownUser = OwnUser.fromUser(user); + expect(user.id, 'test-id'); + expect(user.name, 'test-id'); + + final encodedOwnUser = ownUser.toJson(); + expect(encodedOwnUser['id'], 'test-id'); + expect(encodedOwnUser['name'], null); + }, + ); }); } From e04a62dc5465a847e17618e9c8f7a2f37016fc66 Mon Sep 17 00:00:00 2001 From: Gordon Date: Tue, 21 Sep 2021 15:08:04 +0200 Subject: [PATCH 074/148] docs(doc): add versioning policy to README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 62358d57..93f6d42c 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,21 @@ Every package folder includes a fully functional example with setup instructions We also provide a set of sample apps created using the Stream Flutter SDK at [this location](https://github.com/GetStream/flutter-samples). +## Versioning Policy + +All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). + +That means that with a version number x.y.z (major.minor.patch): + +- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). +- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). +- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). + +See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. + +This versioning policy does not apply to prerelease packages (below major version of 1). See this [StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) for more information on Dart package versioning. + +Whenever possible, we will add deprecation warnings in preparation for future breaking changes. ## We are hiring From 371fff385122d87ab3d26e00bf1e5a9e0142b04e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 21 Sep 2021 15:48:57 +0200 Subject: [PATCH 075/148] fix(ui): add default loading builder in photoview --- packages/stream_chat_flutter/lib/src/full_screen_media.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 bef2a531..a959229c 100644 --- a/packages/stream_chat_flutter/lib/src/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/full_screen_media.dart @@ -5,11 +5,11 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/gallery_footer.dart'; import 'package:stream_chat_flutter/src/gallery_header.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; /// Return action for coming back from pages enum ReturnActionType { @@ -112,6 +112,8 @@ class _FullScreenMediaState extends State attachment.assetUrl ?? attachment.thumbUrl; return PhotoView( + loadingBuilder: (context, image) => + const Offstage(), imageProvider: (imageUrl == null && attachment.localUri != null && attachment.file?.bytes != null) From 152adfc8b0cbfa0346700ccadc221cea172af929 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 21 Sep 2021 16:44:03 +0200 Subject: [PATCH 076/148] refactor(ui): added `MultiPortal` and `customPortalOptions` --- packages/stream_chat_flutter/CHANGELOG.md | 4 ++ .../lib/src/message_input.dart | 53 +++++++++------- .../stream_chat_flutter/lib/src/overlays.dart | 63 +++++++++++++++++++ 3 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/src/overlays.dart diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index c500cb5e..e7bbf104 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -12,6 +12,10 @@ - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. +✅ Added + +- Added `MessageInput.customPortalOptions` property to add custom overlays to the `MessageInput`. + ## 2.2.1 ⚠️ Deprecated diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index f8c83d02..1c8f8f37 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -6,7 +6,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -18,6 +17,7 @@ import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/mentions_overlay.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; +import 'package:stream_chat_flutter/src/overlays.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; @@ -192,12 +192,16 @@ class MessageInput extends StatefulWidget { this.onAttachmentLimitExceed, this.attachmentButtonBuilder, this.commandButtonBuilder, + this.customPortalOptions = const [], }) : assert( initialMessage == null || editMessage == null, "Can't provide both `initialMessage` and `editMessage`", ), super(key: key); + /// List of portal options for showing overlays + final List customPortalOptions; + /// Message to edit final Message? editMessage; @@ -462,32 +466,33 @@ class MessageInputState extends State { ); } - return PortalEntry( - portal: _buildCommandsOverlayEntry(), - visible: _showCommandsOverlay, + return MultiPortal( childAnchor: Alignment.topCenter, portalAnchor: Alignment.bottomCenter, - child: PortalEntry( - portal: _buildEmojiOverlay(), - visible: _focusNode.hasFocus && - textEditingController.text.isNotEmpty && - textEditingController.selection.baseOffset > 0 && - textEditingController.text - .substring( - 0, - textEditingController.selection.baseOffset, - ) - .contains(':'), - childAnchor: Alignment.topCenter, - portalAnchor: Alignment.bottomCenter, - child: PortalEntry( - portal: _buildMentionsOverlayEntry(), - visible: _showMentionsOverlay, - childAnchor: Alignment.topCenter, - portalAnchor: Alignment.bottomCenter, - child: child, + portalOptions: [ + PortalOptions( + visible: _showCommandsOverlay, + widget: _buildCommandsOverlayEntry(), ), - ), + PortalOptions( + visible: _focusNode.hasFocus && + textEditingController.text.isNotEmpty && + textEditingController.selection.baseOffset > 0 && + textEditingController.text + .substring( + 0, + textEditingController.selection.baseOffset, + ) + .contains(':'), + widget: _buildEmojiOverlay(), + ), + PortalOptions( + visible: _showMentionsOverlay, + widget: _buildMentionsOverlayEntry(), + ), + ...widget.customPortalOptions, + ], + child: child, ); } diff --git a/packages/stream_chat_flutter/lib/src/overlays.dart b/packages/stream_chat_flutter/lib/src/overlays.dart new file mode 100644 index 00000000..59328429 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/overlays.dart @@ -0,0 +1,63 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_portal/flutter_portal.dart'; + +/// Class that contains the parameters for building a portal entry +class PortalOptions { + /// Constructs a new portal options object + /// [visible] - the visibility of the portal + /// [widget] - the widget to be displayed + PortalOptions({ + required this.visible, + required this.widget, + }); + + /// the visibility of the portal + final bool visible; + + /// the widget to be displayed + final Widget widget; +} + +/// Widget that renders a single portal widget from a list of [portalOptions] +/// It shows the first one that is visible +class MultiPortal extends StatelessWidget { + /// Constructs a new MultiPortal widget + /// [portalOptions] - the list of portal options + /// [portalAnchor] - the anchor relative to the portal + /// [childAnchor] - the anchor relative to the child + /// [child] - the child widget + const MultiPortal({ + Key? key, + required this.portalOptions, + required this.child, + required this.portalAnchor, + required this.childAnchor, + }) : super(key: key); + + /// The list of portal options + final List portalOptions; + + /// The child widget + final Widget child; + + /// The anchor relative to the portal + final Alignment? portalAnchor; + + /// The anchor relative to the child + final Alignment? childAnchor; + + @override + Widget build(BuildContext context) { + final visibleOverlay = + portalOptions.firstWhereOrNull((element) => element.visible); + + return PortalEntry( + childAnchor: childAnchor, + portalAnchor: portalAnchor, + visible: visibleOverlay != null, + portal: visibleOverlay?.widget, + child: child, + ); + } +} From 9a1cb38e2a1a215a321d70fd2aa4fdf2329c7c6f Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 09:24:36 +0200 Subject: [PATCH 077/148] refactor(ui): rename portal to overlays --- .../lib/src/message_input.dart | 20 +++++----- .../stream_chat_flutter/lib/src/overlays.dart | 40 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 1c8f8f37..9cd84aa2 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -192,15 +192,15 @@ class MessageInput extends StatefulWidget { this.onAttachmentLimitExceed, this.attachmentButtonBuilder, this.commandButtonBuilder, - this.customPortalOptions = const [], + this.customOverlays = const [], }) : assert( initialMessage == null || editMessage == null, "Can't provide both `initialMessage` and `editMessage`", ), super(key: key); - /// List of portal options for showing overlays - final List customPortalOptions; + /// List of options for showing overlays + final List customOverlays; /// Message to edit final Message? editMessage; @@ -466,15 +466,15 @@ class MessageInputState extends State { ); } - return MultiPortal( + return MultiOverlay( childAnchor: Alignment.topCenter, - portalAnchor: Alignment.bottomCenter, - portalOptions: [ - PortalOptions( + overlayAnchor: Alignment.bottomCenter, + overlayOptions: [ + OverlayOptions( visible: _showCommandsOverlay, widget: _buildCommandsOverlayEntry(), ), - PortalOptions( + OverlayOptions( visible: _focusNode.hasFocus && textEditingController.text.isNotEmpty && textEditingController.selection.baseOffset > 0 && @@ -486,11 +486,11 @@ class MessageInputState extends State { .contains(':'), widget: _buildEmojiOverlay(), ), - PortalOptions( + OverlayOptions( visible: _showMentionsOverlay, widget: _buildMentionsOverlayEntry(), ), - ...widget.customPortalOptions, + ...widget.customOverlays, ], child: child, ); diff --git a/packages/stream_chat_flutter/lib/src/overlays.dart b/packages/stream_chat_flutter/lib/src/overlays.dart index 59328429..8d36be50 100644 --- a/packages/stream_chat_flutter/lib/src/overlays.dart +++ b/packages/stream_chat_flutter/lib/src/overlays.dart @@ -2,47 +2,47 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_portal/flutter_portal.dart'; -/// Class that contains the parameters for building a portal entry -class PortalOptions { - /// Constructs a new portal options object - /// [visible] - the visibility of the portal +/// Class that contains the parameters for building an overlay entry +class OverlayOptions { + /// Constructs a new overlay options object + /// [visible] - the visibility of the overlay /// [widget] - the widget to be displayed - PortalOptions({ + OverlayOptions({ required this.visible, required this.widget, }); - /// the visibility of the portal + /// the visibility of the overlay final bool visible; /// the widget to be displayed final Widget widget; } -/// Widget that renders a single portal widget from a list of [portalOptions] +/// Widget that renders a single overlay widget from a list of [overlayOptions] /// It shows the first one that is visible -class MultiPortal extends StatelessWidget { - /// Constructs a new MultiPortal widget - /// [portalOptions] - the list of portal options - /// [portalAnchor] - the anchor relative to the portal +class MultiOverlay extends StatelessWidget { + /// Constructs a new MultiOverlay widget + /// [overlayOptions] - the list of overlay options + /// [overlayAnchor] - the anchor relative to the overlay /// [childAnchor] - the anchor relative to the child /// [child] - the child widget - const MultiPortal({ + const MultiOverlay({ Key? key, - required this.portalOptions, + required this.overlayOptions, required this.child, - required this.portalAnchor, + required this.overlayAnchor, required this.childAnchor, }) : super(key: key); - /// The list of portal options - final List portalOptions; + /// The list of overlay options + final List overlayOptions; /// The child widget final Widget child; - /// The anchor relative to the portal - final Alignment? portalAnchor; + /// The anchor relative to the overlay + final Alignment? overlayAnchor; /// The anchor relative to the child final Alignment? childAnchor; @@ -50,11 +50,11 @@ class MultiPortal extends StatelessWidget { @override Widget build(BuildContext context) { final visibleOverlay = - portalOptions.firstWhereOrNull((element) => element.visible); + overlayOptions.firstWhereOrNull((element) => element.visible); return PortalEntry( childAnchor: childAnchor, - portalAnchor: portalAnchor, + portalAnchor: overlayAnchor, visible: visibleOverlay != null, portal: visibleOverlay?.widget, child: child, From 403bec53659ce80e1f259c05538d7376adb9d473 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 09:46:47 +0200 Subject: [PATCH 078/148] fix(ui): add missing code --- .../stream_chat_flutter/lib/src/message_list_view.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 77938032..44063487 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -1213,10 +1213,12 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); + if (_scrollController?.isAttached == true) { + _scrollController?.jumpTo( + index: initialIndex, + alignment: initialAlignment, + ); + } }); _messageNewListener = From f032a72d00649ca78f0c92776019976dfda2f27a Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 10:30:23 +0200 Subject: [PATCH 079/148] chore(ui,core,llc,persistance,localization): remove analysis warnings due to flutter 2.5 --- packages/stream_chat/lib/src/client/client.dart | 9 --------- packages/stream_chat/lib/src/client/retry_queue.dart | 2 +- .../lib/src/core/models/channel_model.dart | 2 +- .../stream_chat/lib/src/core/models/reaction.dart | 2 +- packages/stream_chat/lib/src/ws/websocket.dart | 4 ++-- .../test/src/core/api/responses_test.dart | 2 +- .../test/src/core/models/filter_test.dart | 2 +- .../test/src/core/models/serialization_test.dart | 2 +- .../test/src/core/util/extension_test.dart | 2 +- packages/stream_chat/test/src/fakes.dart | 2 +- packages/stream_chat/test/src/ws/websocket_test.dart | 4 ++-- .../stream_chat_flutter/example/lib/split_view.dart | 4 +--- .../example/lib/tutorial_part_2.dart | 4 +--- .../example/lib/tutorial_part_3.dart | 4 +--- .../example/lib/tutorial_part_4.dart | 4 +--- .../example/lib/tutorial_part_5.dart | 4 +--- .../example/lib/tutorial_part_6.dart | 4 +--- .../attachment/attachment_upload_state_builder.dart | 2 +- .../lib/src/attachment/file_attachment.dart | 5 ++--- .../lib/src/attachment/giphy_attachment.dart | 2 +- .../lib/src/channel_bottom_sheet.dart | 2 +- .../stream_chat_flutter/lib/src/channel_header.dart | 9 ++++++--- .../stream_chat_flutter/lib/src/channel_info.dart | 2 +- .../lib/src/channel_list_header.dart | 7 +++++-- .../lib/src/channel_list_view.dart | 2 +- .../stream_chat_flutter/lib/src/channel_name.dart | 2 +- .../stream_chat_flutter/lib/src/date_divider.dart | 2 +- .../stream_chat_flutter/lib/src/gallery_footer.dart | 2 +- .../stream_chat_flutter/lib/src/gallery_header.dart | 7 +++++-- .../lib/src/message_reactions_modal.dart | 2 +- .../lib/src/message_search_item.dart | 2 +- .../lib/src/message_search_list_view.dart | 2 +- .../stream_chat_flutter/lib/src/stream_chat.dart | 4 +++- .../lib/src/stream_chat_theme.dart | 2 +- .../stream_chat_flutter/lib/src/thread_header.dart | 9 ++++++--- .../lib/src/typing_indicator.dart | 2 +- packages/stream_chat_flutter/lib/src/user_item.dart | 2 +- .../stream_chat_flutter/lib/src/user_list_view.dart | 2 +- packages/stream_chat_flutter/lib/src/utils.dart | 2 +- .../lib/src/visible_footnote.dart | 2 +- .../test/src/theme/gallery_footer_theme_test.dart | 2 +- .../test/src/theme/gallery_header_theme_test.dart | 2 +- .../test/channel_list_core_test.dart | 12 ++++++------ .../test/message_search_list_core_test.dart | 6 +++--- .../test/user_list_core_test.dart | 10 +++++----- .../test/override_test.dart | 2 +- .../lib/src/db/shared/native_db.dart | 3 +-- .../lib/src/db/shared/web_db.dart | 3 +-- .../test/src/converter/list_coverter_test.dart | 2 +- .../test/src/converter/map_converter_test.dart | 2 +- .../message_sending_status_converter_test.dart | 2 +- .../test/src/dao/user_dao_test.dart | 2 +- .../test/src/db/moor_chat_database_test.dart | 2 +- .../test/src/mapper/channel_mapper_test.dart | 3 ++- .../test/src/mapper/member_mapper_test.dart | 2 +- .../test/src/mapper/message_mapper_test.dart | 2 +- .../test/src/mapper/pinned_message_mapper_test.dart | 2 +- .../mapper/pinned_message_reaction_mapper_test.dart | 2 +- .../test/src/mapper/reaction_mapper_test.dart | 2 +- .../test/src/mapper/read_mapper_test.dart | 2 +- .../test/src/mapper/user_mapper_test.dart | 3 ++- 61 files changed, 96 insertions(+), 104 deletions(-) diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 15b88ce2..ae6716fd 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -143,9 +143,6 @@ class StreamChatClient { late final RetryPolicy _retryPolicy; - /// sync state of the channels present inside state, defaults to false - bool _synced = false; - /// the last dateTime at the which all the channels were synced DateTime? _lastSyncedAt; @@ -434,8 +431,6 @@ class StreamChatClient { type: EventType.connectionRecovered, online: true, )); - } else { - _synced = false; } } @@ -460,13 +455,11 @@ class StreamChatClient { Future sync({List? cids, DateTime? lastSyncAt}) async { cids ??= await _chatPersistenceClient?.getChannelCids(); if (cids == null || cids.isEmpty) { - _synced = true; return; } lastSyncAt ??= await _chatPersistenceClient?.getLastSyncAt(); if (lastSyncAt == null) { - _synced = true; return; } @@ -484,12 +477,10 @@ class StreamChatClient { handleEvent(event); } - _synced = true; final now = DateTime.now(); _lastSyncedAt = now; _chatPersistenceClient?.updateLastSyncAt(now); } catch (e, stk) { - _synced = false; logger.severe('Error during sync', e, stk); } } diff --git a/packages/stream_chat/lib/src/client/retry_queue.dart b/packages/stream_chat/lib/src/client/retry_queue.dart index 4c44433f..c441b5fb 100644 --- a/packages/stream_chat/lib/src/client/retry_queue.dart +++ b/packages/stream_chat/lib/src/client/retry_queue.dart @@ -6,8 +6,8 @@ import 'package:rxdart/rxdart.dart'; import 'package:stream_chat/src/client/channel.dart'; import 'package:stream_chat/src/client/retry_policy.dart'; import 'package:stream_chat/src/core/error/error.dart'; -import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/core/models/message.dart'; +import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/stream_chat.dart'; /// The retry queue associated to a channel diff --git a/packages/stream_chat/lib/src/core/models/channel_model.dart b/packages/stream_chat/lib/src/core/models/channel_model.dart index a4e6db79..31ac4923 100644 --- a/packages/stream_chat/lib/src/core/models/channel_model.dart +++ b/packages/stream_chat/lib/src/core/models/channel_model.dart @@ -1,7 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/channel_config.dart'; -import 'package:stream_chat/src/core/util/serializer.dart'; import 'package:stream_chat/src/core/models/user.dart'; +import 'package:stream_chat/src/core/util/serializer.dart'; part 'channel_model.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/reaction.dart b/packages/stream_chat/lib/src/core/models/reaction.dart index 57d0b48d..2b0ffbf6 100644 --- a/packages/stream_chat/lib/src/core/models/reaction.dart +++ b/packages/stream_chat/lib/src/core/models/reaction.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:stream_chat/src/core/util/serializer.dart'; import 'package:stream_chat/src/core/models/user.dart'; +import 'package:stream_chat/src/core/util/serializer.dart'; part 'reaction.g.dart'; diff --git a/packages/stream_chat/lib/src/ws/websocket.dart b/packages/stream_chat/lib/src/ws/websocket.dart index b26d811e..05382fb7 100644 --- a/packages/stream_chat/lib/src/ws/websocket.dart +++ b/packages/stream_chat/lib/src/ws/websocket.dart @@ -6,15 +6,15 @@ import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:rxdart/rxdart.dart'; import 'package:stream_chat/src/core/error/error.dart'; -import 'package:stream_chat/src/ws/connection_status.dart'; import 'package:stream_chat/src/core/http/token.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; import 'package:stream_chat/src/core/models/event.dart'; import 'package:stream_chat/src/core/models/user.dart'; import 'package:stream_chat/src/event_type.dart'; +import 'package:stream_chat/src/ws/connection_status.dart'; import 'package:stream_chat/src/ws/timer_helper.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/status.dart' as status; +import 'package:web_socket_channel/web_socket_channel.dart'; /// Typedef which exposes an [Event] as the only parameter. typedef EventHandler = void Function(Event); diff --git a/packages/stream_chat/test/src/core/api/responses_test.dart b/packages/stream_chat/test/src/core/api/responses_test.dart index 77e7abc3..a61d825b 100644 --- a/packages/stream_chat/test/src/core/api/responses_test.dart +++ b/packages/stream_chat/test/src/core/api/responses_test.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/core/api/responses.dart'; import 'package:stream_chat/src/core/models/device.dart'; import 'package:stream_chat/src/core/models/member.dart'; @@ -8,6 +7,7 @@ import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/src/core/models/reaction.dart'; import 'package:stream_chat/src/core/models/read.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/api/responses', () { diff --git a/packages/stream_chat/test/src/core/models/filter_test.dart b/packages/stream_chat/test/src/core/models/filter_test.dart index e396c2b3..c0a8ce8f 100644 --- a/packages/stream_chat/test/src/core/models/filter_test.dart +++ b/packages/stream_chat/test/src/core/models/filter_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/core/models/filter.dart'; +import 'package:test/test.dart'; void main() { group('operators', () { diff --git a/packages/stream_chat/test/src/core/models/serialization_test.dart b/packages/stream_chat/test/src/core/models/serialization_test.dart index 605b5b58..63b1428f 100644 --- a/packages/stream_chat/test/src/core/models/serialization_test.dart +++ b/packages/stream_chat/test/src/core/models/serialization_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:stream_chat/src/core/util/serializer.dart'; +import 'package:test/test.dart'; void main() { group('src/models/serialization', () { diff --git a/packages/stream_chat/test/src/core/util/extension_test.dart b/packages/stream_chat/test/src/core/util/extension_test.dart index 98f9f784..ae3c08d8 100644 --- a/packages/stream_chat/test/src/core/util/extension_test.dart +++ b/packages/stream_chat/test/src/core/util/extension_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:stream_chat/src/core/util/extension.dart'; +import 'package:test/test.dart'; void main() { test('`.withNullifyer` converts the type into non-nullable', () { diff --git a/packages/stream_chat/test/src/fakes.dart b/packages/stream_chat/test/src/fakes.dart index 8d3f6d37..3dcd8ebd 100644 --- a/packages/stream_chat/test/src/fakes.dart +++ b/packages/stream_chat/test/src/fakes.dart @@ -6,11 +6,11 @@ import 'package:rxdart/rxdart.dart'; import 'package:stream_chat/src/core/api/channel_api.dart'; import 'package:stream_chat/src/core/api/device_api.dart'; import 'package:stream_chat/src/core/api/general_api.dart'; +import 'package:stream_chat/src/core/api/guest_api.dart'; import 'package:stream_chat/src/core/api/message_api.dart'; import 'package:stream_chat/src/core/api/moderation_api.dart'; import 'package:stream_chat/src/core/api/stream_chat_api.dart'; import 'package:stream_chat/src/core/api/user_api.dart'; -import 'package:stream_chat/src/core/api/guest_api.dart'; import 'package:stream_chat/src/core/http/token.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; import 'package:stream_chat/src/ws/websocket.dart'; diff --git a/packages/stream_chat/test/src/ws/websocket_test.dart b/packages/stream_chat/test/src/ws/websocket_test.dart index 94033bf0..5519b2b3 100644 --- a/packages/stream_chat/test/src/ws/websocket_test.dart +++ b/packages/stream_chat/test/src/ws/websocket_test.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'dart:convert'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; +import 'package:stream_chat/src/ws/websocket.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:test/test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat/src/ws/websocket.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import '../fakes.dart'; diff --git a/packages/stream_chat_flutter/example/lib/split_view.dart b/packages/stream_chat_flutter/example/lib/split_view.dart index 52c0e885..1fd3ccb6 100644 --- a/packages/stream_chat_flutter/example/lib/split_view.dart +++ b/packages/stream_chat_flutter/example/lib/split_view.dart @@ -106,9 +106,7 @@ class ChannelListPage extends StatelessWidget { [StreamChat.of(context).currentUser!.id], ), sort: const [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, ), ), ); diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart index 5fa9cfdb..6abc260e 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart @@ -83,9 +83,7 @@ class ChannelListPage extends StatelessWidget { [StreamChat.of(context).currentUser!.id], ), sort: const [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, channelWidget: const ChannelPage(), ), ), diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart index 824c0efa..538dbd68 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart @@ -85,9 +85,7 @@ class ChannelListPage extends StatelessWidget { ), channelPreviewBuilder: _channelPreviewBuilder, // sort: [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, channelWidget: const ChannelPage(), ), ), diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart index df94cf06..5a05e4f9 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart @@ -69,9 +69,7 @@ class ChannelListPage extends StatelessWidget { [StreamChat.of(context).currentUser!.id], ), sort: const [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, channelWidget: const ChannelPage(), ), ), diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart index b9089d6a..86ac2e1f 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart @@ -75,9 +75,7 @@ class ChannelListPage extends StatelessWidget { [StreamChat.of(context).currentUser!.id], ), sort: const [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, channelWidget: const ChannelPage(), ), ), diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart index b64281cb..2fbdcbee 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart @@ -102,9 +102,7 @@ class ChannelListPage extends StatelessWidget { [StreamChat.of(context).currentUser!.id], ), sort: const [SortOption('last_message_at')], - pagination: const PaginationParams( - limit: 20, - ), + limit: 20, channelWidget: const ChannelPage(), ), ), diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart index af46515e..1a0ba989 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/upload_progress_indicator.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; /// Widget to build in progress typedef InProgressBuilder = Widget Function(BuildContext, int, int); diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index 6045539f..4d2e8825 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -1,15 +1,14 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/upload_progress_indicator.dart'; import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; - -import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; /// Widget for displaying file attachments class FileAttachment extends AttachmentWidget { diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index e165fc6d..8272a962 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -2,10 +2,10 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/visible_footnote.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/extension.dart'; /// Widget for showing a GIF attachment class GiphyAttachment extends AttachmentWidget { diff --git a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart index 5723234d..f4be36e1 100644 --- a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart +++ b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/channel_info.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Bottom Sheet with options class ChannelBottomSheet extends StatefulWidget { diff --git a/packages/stream_chat_flutter/lib/src/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel_header.dart index 1c12b132..576c0da3 100644 --- a/packages/stream_chat_flutter/lib/src/channel_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_header.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:stream_chat_flutter/src/back_button.dart'; import 'package:stream_chat_flutter/src/channel_info.dart'; import 'package:stream_chat_flutter/src/channel_name.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.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/extension.dart'; /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header_paint.png) @@ -141,8 +142,10 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { showMessage: showConnectionStateTile && showStatus, message: statusString, child: AppBar( - textTheme: Theme.of(context).textTheme, - brightness: Theme.of(context).brightness, + titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light, elevation: 1, leading: leadingWidget, backgroundColor: backgroundColor ?? channelHeaderTheme.color, diff --git a/packages/stream_chat_flutter/lib/src/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel_info.dart index de9d74b9..6bd61dad 100644 --- a/packages/stream_chat_flutter/lib/src/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel_info.dart @@ -1,8 +1,8 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Widget which shows channel info class ChannelInfo extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel_list_header.dart index 4ce8b0d2..d95e439d 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_header.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart'; @@ -125,8 +126,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { showMessage: showConnectionStateTile && showStatus, message: statusString, child: AppBar( - textTheme: Theme.of(context).textTheme, - brightness: Theme.of(context).brightness, + titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light, elevation: 1, backgroundColor: backgroundColor ?? channelListHeaderThemeData.color, diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 0f06e269..96b27823 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/theme/themes.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/extension.dart'; /// Callback called when tapping on a channel typedef ChannelTapCallback = void Function(Channel, Widget?); diff --git a/packages/stream_chat_flutter/lib/src/channel_name.dart b/packages/stream_chat_flutter/lib/src/channel_name.dart index 918aa076..258893c8 100644 --- a/packages/stream_chat_flutter/lib/src/channel_name.dart +++ b/packages/stream_chat_flutter/lib/src/channel_name.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:stream_chat_flutter/src/extension.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/extension.dart'; /// It shows the current [Channel] name using a [Text] widget. /// diff --git a/packages/stream_chat_flutter/lib/src/date_divider.dart b/packages/stream_chat_flutter/lib/src/date_divider.dart index 3d85299c..7c977f60 100644 --- a/packages/stream_chat_flutter/lib/src/date_divider.dart +++ b/packages/stream_chat_flutter/lib/src/date_divider.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; /// It shows a date divider depending on the date difference class DateDivider extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery_footer.dart index 2b2858f8..eaa03fb7 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_footer.dart @@ -7,12 +7,12 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/theme/themes.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.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/extension.dart'; /// Footer widget for media display class GalleryFooter extends StatefulWidget implements PreferredSizeWidget { diff --git a/packages/stream_chat_flutter/lib/src/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery_header.dart index 74eeb4ee..09f79016 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_header.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_header.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:stream_chat_flutter/src/attachment_actions_modal.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; @@ -58,8 +59,10 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { final galleryHeaderThemeData = GalleryHeaderTheme.of(context); return AppBar( - textTheme: Theme.of(context).textTheme, - brightness: Theme.of(context).brightness, + titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light, elevation: 1, leading: showBackButton ? IconButton( 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 d2c69e4b..e9f2e55e 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/src/extension.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/stream_chat.dart'; @@ -8,7 +9,6 @@ import 'package:stream_chat_flutter/src/theme/themes.dart'; import 'package:stream_chat_flutter/src/user_avatar.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/extension.dart'; /// Modal widget for displaying message reactions class MessageReactionsModal extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/message_search_item.dart b/packages/stream_chat_flutter/lib/src/message_search_item.dart index ab41532c..89d43531 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_item.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_item.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:stream_chat_flutter/src/extension.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/extension.dart'; /// It shows the current [Message] preview. /// diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index ab1a9178..5c8c2bc6 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/message_search_item.dart'; import 'package:stream_chat_flutter/src/theme/themes.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/extension.dart'; /// Callback called when tapping on a user typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index f185d288..af617d34 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -100,7 +100,9 @@ class StreamChatState extends State { return Theme( data: materialTheme.copyWith( primaryIconTheme: streamTheme.primaryIconTheme, - accentColor: streamTheme.colorTheme.accentPrimary, + colorScheme: materialTheme.colorScheme.copyWith( + secondary: streamTheme.colorTheme.accentPrimary, + ), ), child: StreamChatCore( client: client, diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index f7a05d7e..0e50f132 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -131,7 +131,7 @@ class StreamChatThemeData { final defaultTheme = StreamChatThemeData(brightness: theme.brightness); final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( defaultTheme.colorTheme.copyWith( - accentPrimary: theme.accentColor, + accentPrimary: theme.colorScheme.secondary, ), defaultTheme.textTheme, ); diff --git a/packages/stream_chat_flutter/lib/src/thread_header.dart b/packages/stream_chat_flutter/lib/src/thread_header.dart index 55a270fc..60bfed89 100644 --- a/packages/stream_chat_flutter/lib/src/thread_header.dart +++ b/packages/stream_chat_flutter/lib/src/thread_header.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.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/extension.dart'; /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header_paint.png) @@ -129,8 +130,10 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget { return AppBar( automaticallyImplyLeading: false, - textTheme: Theme.of(context).textTheme, - brightness: Theme.of(context).brightness, + titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light, elevation: 1, leading: leading ?? (showBackButton diff --git a/packages/stream_chat_flutter/lib/src/typing_indicator.dart b/packages/stream_chat_flutter/lib/src/typing_indicator.dart index 48752f07..5d425e58 100644 --- a/packages/stream_chat_flutter/lib/src/typing_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/typing_indicator.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// Widget to show the current list of typing users class TypingIndicator extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/user_item.dart b/packages/stream_chat_flutter/lib/src/user_item.dart index a55693aa..a2622385 100644 --- a/packages/stream_chat_flutter/lib/src/user_item.dart +++ b/packages/stream_chat_flutter/lib/src/user_item.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/user_list_view.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/extension.dart'; /// /// It shows the current [User] preview. diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index b946f800..98cf6621 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/theme/themes.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/extension.dart'; /// Callback called when tapping on a user typedef UserTapCallback = void Function(User, Widget?); diff --git a/packages/stream_chat_flutter/lib/src/utils.dart b/packages/stream_chat_flutter/lib/src/utils.dart index 4dc9fee7..252bd87b 100644 --- a/packages/stream_chat_flutter/lib/src/utils.dart +++ b/packages/stream_chat_flutter/lib/src/utils.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; /// Launch URL Future launchURL(BuildContext context, String url) async { diff --git a/packages/stream_chat_flutter/lib/src/visible_footnote.dart b/packages/stream_chat_flutter/lib/src/visible_footnote.dart index 8d16ce41..18bb976d 100644 --- a/packages/stream_chat_flutter/lib/src/visible_footnote.dart +++ b/packages/stream_chat_flutter/lib/src/visible_footnote.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; /// Widget for displaying a footnote class VisibleFootnote extends StatelessWidget { diff --git a/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart index fb8d7035..6a73e34e 100644 --- a/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart' hide TextTheme; import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class MockStreamChatClient extends Mock implements StreamChatClient {} diff --git a/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart index aefbb319..534008ba 100644 --- a/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class MockStreamChatClient extends Mock implements StreamChatClient {} diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart index dd87d2cf..8407be67 100644 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart @@ -129,7 +129,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => Container(key: errorWidgetKey), - pagination: pagination, + limit: pagination.limit, ); final mockClient = MockClient(); @@ -186,7 +186,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: pagination.limit, ); final mockClient = MockClient(); @@ -243,7 +243,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: pagination.limit, ); final mockClient = MockClient(); @@ -306,7 +306,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: pagination.limit, ); final mockClient = MockClient(); @@ -420,7 +420,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination.copyWith(limit: limit), + limit: limit, ); final mockClient = MockClient(); @@ -516,7 +516,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: pagination.limit, ); expect(channelListCore.limit, pagination.limit); diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index ca416595..475c6c23 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -334,7 +334,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - paginationParams: pagination, + limit: pagination.limit, filters: testFilter, messageFilters: testMessageFilter, ); @@ -457,7 +457,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - paginationParams: pagination.copyWith(limit: limit), + limit: limit, filters: testFilter, messageFilters: testMessageFilter, ); @@ -561,7 +561,7 @@ void main() { errorBuilder: (BuildContext context, Object? error) => const Offstage(), filters: testFilter, messageFilters: testMessageFilter, - paginationParams: pagination, + limit: pagination.limit, ); expect(messageSearchListCore.limit, pagination.limit); diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index ade58e0f..b077868b 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -341,7 +341,7 @@ void main() { loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: pagination.limit, groupAlphabetically: true, ); @@ -447,7 +447,7 @@ void main() { emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination.copyWith(limit: limit), + limit: limit, groupAlphabetically: true, ); @@ -522,15 +522,15 @@ void main() { ); test('`widget.limit` should match `widget.pagination.limit`', () { - const pagination = PaginationParams(limit: 30); + const limit = 30; final userListCore = UserListCore( listBuilder: (_, __) => const Offstage(), loadingBuilder: (BuildContext context) => const Offstage(), emptyBuilder: (BuildContext context) => const Offstage(), errorBuilder: (BuildContext context, Object error) => const Offstage(), - pagination: pagination, + limit: limit, ); - expect(userListCore.limit, pagination.limit); + expect(userListCore.limit, limit); }); } diff --git a/packages/stream_chat_localizations/test/override_test.dart b/packages/stream_chat_localizations/test/override_test.dart index 3e134b6f..513dd336 100644 --- a/packages/stream_chat_localizations/test/override_test.dart +++ b/packages/stream_chat_localizations/test/override_test.dart @@ -2,9 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_localizations/src/stream_chat_localizations.dart'; -import 'package:flutter_test/flutter_test.dart'; class FooStreamChatLocalizations extends StreamChatLocalizationsEn { FooStreamChatLocalizations( diff --git a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart index 27aa23b5..d70426e1 100644 --- a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart +++ b/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart @@ -7,11 +7,10 @@ import 'package:moor/isolate.dart'; import 'package:moor/moor.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; import 'package:stream_chat_persistence/stream_chat_persistence.dart'; -import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; - /// A Helper class to construct new instances of [MoorChatDatabase] specifically /// for native platform applications class SharedDB { diff --git a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart index bb664443..5b1d8311 100644 --- a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart +++ b/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart @@ -1,8 +1,7 @@ // coverage:ignore-file import 'package:moor/moor_web.dart'; -import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; - import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; +import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; /// A Helper class to construct new instances of [MoorChatDatabase] specifically /// for Web applications diff --git a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart index 73ce2452..a5fe7a49 100644 --- a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat_persistence/src/converter/list_converter.dart'; +import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart index 4727cb0d..6e14140b 100644 --- a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat_persistence/src/converter/map_converter.dart'; +import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart index 1035a759..c53d8fb4 100644 --- a/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart @@ -1,6 +1,6 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/converter/message_sending_status_converter.dart'; +import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart index 5b5d04d4..1fd4eafd 100644 --- a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:test/test.dart'; -import 'package:stream_chat/stream_chat.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart b/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart index 2ec4c76b..101b44ed 100644 --- a/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart +++ b/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart @@ -1,8 +1,8 @@ -import 'package:test/test.dart'; import 'package:moor/ffi.dart'; import 'package:moor/isolate.dart'; import 'package:moor/moor.dart' hide isNotNull; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; +import 'package:test/test.dart'; DatabaseConnection _backgroundConnection() => DatabaseConnection.fromExecutor(VmDatabase.memory()); diff --git a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart index 93789cc7..b45f46b4 100644 --- a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart @@ -1,9 +1,10 @@ import 'dart:math' as math; -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/channel_mapper.dart'; +import 'package:test/test.dart'; + import '../utils/date_matcher.dart'; void main() { diff --git a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart index feca753b..b3f834ef 100644 --- a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/member_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart index 0f9e8cf6..52a58a1a 100644 --- a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:math' as math; -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/message_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart index 5150df62..40c4ff6c 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:math' as math; -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/pinned_message_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart index bd8d0ba8..b814b7dc 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart @@ -1,7 +1,7 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/pinned_message_reaction_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart index cb5d231e..e211f685 100644 --- a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart @@ -1,7 +1,7 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/reaction_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart index 73c87da3..eb296a75 100644 --- a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart @@ -1,7 +1,7 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/read_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart index eca2a32a..55dbbff4 100644 --- a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart @@ -1,8 +1,9 @@ import 'dart:math' as math; -import 'package:test/test.dart'; + import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; +import 'package:test/test.dart'; import '../utils/date_matcher.dart'; From 2ac6afb9812d3191ccc4270dff7760440bb23306 Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:37:42 +0200 Subject: [PATCH 080/148] docs(doc): add versioning policy --- .../docs/Flutter/basics/versioning_policy.mdx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docusaurus/docs/Flutter/basics/versioning_policy.mdx diff --git a/docusaurus/docs/Flutter/basics/versioning_policy.mdx b/docusaurus/docs/Flutter/basics/versioning_policy.mdx new file mode 100644 index 00000000..26ab9541 --- /dev/null +++ b/docusaurus/docs/Flutter/basics/versioning_policy.mdx @@ -0,0 +1,21 @@ +--- +id: versioning_policy +sidebar_position: 3 +title: Versioning Policy +--- + +All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). + +That means that with a version number x.y.z (major.minor.patch): + +- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). +- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). +- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). + +See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. + +This versioning policy does not apply to prerelease packages (below major version of 1). See this +[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) +for more information on Dart package versioning. + +Whenever possible, we will add deprecation warnings in preparation for future breaking changes. From a071ee645aa1b0b587face7fdd14b971bf973e37 Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:39:53 +0200 Subject: [PATCH 081/148] docs(doc): update CONTRIBUTING.md --- CONTRIBUTING.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3bb5a923..249cdf5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -177,6 +177,24 @@ Develop is merged into master after the team performs various automated and QA t --- +# Versioning Policy + +All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). + +That means that with a version number x.y.z (major.minor.patch): + +- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). +- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). +- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). + +See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. + +This versioning policy does not apply to prerelease packages (below major version of 1). See this [StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) for more information on Dart package versioning. + +Whenever possible, we will add deprecation warnings in preparation for future breaking changes. + +--- + # Styleguides 💅 ![image](https://user-images.githubusercontent.com/20601437/124241186-d17a8680-db1b-11eb-9a21-3df305674ca9.png) From a7813642de44855ce79797f74001ecdaa4234a00 Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:41:49 +0200 Subject: [PATCH 082/148] docs(doc): update CONTRIBUTING.md --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 249cdf5e..59c37838 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -189,7 +189,9 @@ That means that with a version number x.y.z (major.minor.patch): See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. -This versioning policy does not apply to prerelease packages (below major version of 1). See this [StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) for more information on Dart package versioning. +This versioning policy does not apply to prerelease packages (below major version of 1). See this +[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) +for more information on Dart package versioning. Whenever possible, we will add deprecation warnings in preparation for future breaking changes. From 516fd9160ae8354d3a55cdbf80500dc17ef1d14d Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:42:21 +0200 Subject: [PATCH 083/148] docs(doc): update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 93f6d42c..d76e5bd9 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,9 @@ That means that with a version number x.y.z (major.minor.patch): See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. -This versioning policy does not apply to prerelease packages (below major version of 1). See this [StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) for more information on Dart package versioning. +This versioning policy does not apply to prerelease packages (below major version of 1). See this +[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) +for more information on Dart package versioning. Whenever possible, we will add deprecation warnings in preparation for future breaking changes. From 7db270f5f245819e2467f2648cafc492a8c6939d Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:51:54 +0200 Subject: [PATCH 084/148] docs(doc): update CONTRIBUTING.md --- CONTRIBUTING.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59c37838..b99a2b3c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -181,19 +181,7 @@ Develop is merged into master after the team performs various automated and QA t All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). -That means that with a version number x.y.z (major.minor.patch): - -- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). -- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). -- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). - -See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. - -This versioning policy does not apply to prerelease packages (below major version of 1). See this -[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) -for more information on Dart package versioning. - -Whenever possible, we will add deprecation warnings in preparation for future breaking changes. +See our [versioning policy documentation](https://getstream.io/chat/docs/sdk/flutter/basics/versioning_policy/) for more information. --- From 9f32e4671dde4470cbe122e9d9789a4e8025d603 Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 10:52:39 +0200 Subject: [PATCH 085/148] docs(doc): update README.md --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index d76e5bd9..061a193d 100644 --- a/README.md +++ b/README.md @@ -66,19 +66,7 @@ We also provide a set of sample apps created using the Stream Flutter SDK at [th All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). -That means that with a version number x.y.z (major.minor.patch): - -- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). -- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). -- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). - -See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information. - -This versioning policy does not apply to prerelease packages (below major version of 1). See this -[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins) -for more information on Dart package versioning. - -Whenever possible, we will add deprecation warnings in preparation for future breaking changes. +See our [versioning policy documentation](https://getstream.io/chat/docs/sdk/flutter/basics/versioning_policy/) for more information. ## We are hiring From 5e9bd3fb789af266513d8f95d5dd609ebdbcda0f Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 11:01:16 +0200 Subject: [PATCH 086/148] chore(ui,core,llc,persistance,localization): change AppBar props --- .../stream_chat_flutter/lib/src/channel_header.dart | 11 +++++++---- .../lib/src/channel_list_header.dart | 10 ++++++---- .../stream_chat_flutter/lib/src/gallery_header.dart | 10 ++++++---- .../stream_chat_flutter/lib/src/thread_header.dart | 10 ++++++---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel_header.dart index 576c0da3..62f994dc 100644 --- a/packages/stream_chat_flutter/lib/src/channel_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_header.dart @@ -138,14 +138,17 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { break; } + final theme = Theme.of(context); + return InfoTile( showMessage: showConnectionStateTile && showStatus, message: statusString, child: AppBar( - titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light, + toolbarTextStyle: theme.textTheme.bodyText2, + titleTextStyle: theme.textTheme.headline6, + systemOverlayStyle: theme.brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, elevation: 1, leading: leadingWidget, backgroundColor: backgroundColor ?? channelHeaderTheme.color, diff --git a/packages/stream_chat_flutter/lib/src/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel_list_header.dart index d95e439d..2dfc1ffb 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_header.dart @@ -122,14 +122,16 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { final chatThemeData = StreamChatTheme.of(context); final channelListHeaderThemeData = ChannelListHeaderTheme.of(context); + final theme = Theme.of(context); return InfoTile( showMessage: showConnectionStateTile && showStatus, message: statusString, child: AppBar( - titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light, + toolbarTextStyle: theme.textTheme.bodyText2, + titleTextStyle: theme.textTheme.headline6, + systemOverlayStyle: theme.brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, elevation: 1, backgroundColor: backgroundColor ?? channelListHeaderThemeData.color, diff --git a/packages/stream_chat_flutter/lib/src/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery_header.dart index 09f79016..fc5f67b4 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_header.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_header.dart @@ -58,11 +58,13 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { final galleryHeaderThemeData = GalleryHeaderTheme.of(context); + final theme = Theme.of(context); return AppBar( - titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light, + toolbarTextStyle: theme.textTheme.bodyText2, + titleTextStyle: theme.textTheme.headline6, + systemOverlayStyle: theme.brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, elevation: 1, leading: showBackButton ? IconButton( diff --git a/packages/stream_chat_flutter/lib/src/thread_header.dart b/packages/stream_chat_flutter/lib/src/thread_header.dart index 60bfed89..dfe13b41 100644 --- a/packages/stream_chat_flutter/lib/src/thread_header.dart +++ b/packages/stream_chat_flutter/lib/src/thread_header.dart @@ -128,12 +128,14 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget { ], ); + final theme = Theme.of(context); return AppBar( automaticallyImplyLeading: false, - titleTextStyle: Theme.of(context).appBarTheme.titleTextStyle, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light, + toolbarTextStyle: theme.textTheme.bodyText2, + titleTextStyle: theme.textTheme.headline6, + systemOverlayStyle: theme.brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, elevation: 1, leading: leading ?? (showBackButton From dc923769d08c799388a8f75e5d2970f72453ebbd Mon Sep 17 00:00:00 2001 From: Gordon Date: Wed, 22 Sep 2021 11:10:38 +0200 Subject: [PATCH 087/148] docs(doc): update versioning policy --- docusaurus/docs/Flutter/basics/versioning_policy.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docusaurus/docs/Flutter/basics/versioning_policy.mdx b/docusaurus/docs/Flutter/basics/versioning_policy.mdx index 26ab9541..48f25d66 100644 --- a/docusaurus/docs/Flutter/basics/versioning_policy.mdx +++ b/docusaurus/docs/Flutter/basics/versioning_policy.mdx @@ -7,8 +7,7 @@ title: Versioning Policy All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/). That means that with a version number x.y.z (major.minor.patch): - -- When releasing critical bug fixes, we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). +- When releasing bug fixes (backwards compatible), we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). A bug fix is defined as an internal change that fixes incorrect behavior. - When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0). - When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0). From b7fe7287a582820a7dd3c1d953a654d0648db3f5 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Wed, 22 Sep 2021 13:00:11 +0200 Subject: [PATCH 088/148] docs: change paginationParams to limit --- .../message_search_list_view.mdx | 2 +- .../message_search_list_core.mdx | 6 +++--- .../lib/src/message_search_list_view.dart | 14 +++++++------- .../lib/src/message_search_list_core.dart | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docusaurus/docs/Flutter/stream_chat_flutter/message_search_list_view.mdx b/docusaurus/docs/Flutter/stream_chat_flutter/message_search_list_view.mdx index 864b5e49..39bf2d9e 100644 --- a/docusaurus/docs/Flutter/stream_chat_flutter/message_search_list_view.mdx +++ b/docusaurus/docs/Flutter/stream_chat_flutter/message_search_list_view.mdx @@ -29,7 +29,7 @@ class MessageSearchPage extends StatelessWidget { child: MessageSearchListView( filters: Filter.in_('members', [StreamChat.of(context).user!.id],), messageQuery: 'your query here', - paginationParams: PaginationParams(limit: 20), + limit: 20, ), ), ); diff --git a/docusaurus/docs/Flutter/stream_chat_flutter_core/message_search_list_core.mdx b/docusaurus/docs/Flutter/stream_chat_flutter_core/message_search_list_core.mdx index 70cc026f..f3be5e85 100644 --- a/docusaurus/docs/Flutter/stream_chat_flutter_core/message_search_list_core.mdx +++ b/docusaurus/docs/Flutter/stream_chat_flutter_core/message_search_list_core.mdx @@ -28,9 +28,9 @@ class MessageSearchPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: MessageSearchListCore( - messageQuery: _messageFilter, - filters: _channelsFilter, - paginationParams: PaginationParams(limit: 20), + messageQuery: _messageFilter, + filters: _channelsFilter, + limit: 20, ), ); } diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index 5c8c2bc6..a1611412 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -30,13 +30,13 @@ typedef EmptyMessageSearchBuilder = Widget Function( /// Widget build(BuildContext context) { /// return Scaffold( /// body: MessageSearchListView( -/// messageQuery: _channelQuery, -/// filters: { -/// 'members': { -/// r'$in': [user.id] -/// } -/// }, -/// paginationParams: PaginationParams(limit: 20), +/// messageQuery: _channelQuery, +/// filters: { +/// 'members': { +/// r'$in': [user.id] +/// } +/// }, +/// limit: 20, /// ), /// ); /// } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index f4d18a0b..565743f2 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -18,9 +18,9 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; /// Widget build(BuildContext context) { /// return Scaffold( /// body: MessageSearchListCore( -/// messageQuery: _messageFilter, -/// filters: _channelsFilter, -/// paginationParams: PaginationParams(limit: 20), +/// messageQuery: _messageFilter, +/// filters: _channelsFilter, +/// limit: 20, /// ), /// ); /// } From 669abf59c942e9db0663269bd64c29dd15582674 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:17:57 +0200 Subject: [PATCH 089/148] chore(llc): bump version --- packages/stream_chat/CHANGELOG.md | 2 +- packages/stream_chat/lib/version.dart | 2 +- packages/stream_chat/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index e0c0f693..3c7dd84f 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 3.0.0 🛑️ Breaking Changes from `2.2.1` diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index b208e15c..4eda59c0 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '2.2.1'; +const PACKAGE_VERSION = '3.0.0'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index a07606ba..dd394c1e 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 2.2.1 +version: 3.0.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues From 0e02ccf9e205f4b66f8fc207df40540ca78c7004 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:19:28 +0200 Subject: [PATCH 090/148] chore(core): bump version --- packages/stream_chat_flutter_core/CHANGELOG.md | 2 +- packages/stream_chat_flutter_core/pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index c4287a09..e1ec1fc5 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 3.0.0 ⚠️ Deprecated diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 85b57124..27010d48 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 2.2.1 +version: 3.0.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -16,7 +16,7 @@ dependencies: sdk: flutter meta: ^1.3.0 rxdart: ^0.27.0 - stream_chat: ^2.2.1 + stream_chat: 3.0.0 dev_dependencies: fake_async: ^1.2.0 From ad3c3572ddf588b5a5fb9457588c1cb423b5d4af Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:25:45 +0200 Subject: [PATCH 091/148] feat!(core): commit breaking changes --- .../stream_chat_flutter_core/CHANGELOG.md | 8 +++--- .../lib/src/channel_list_core.dart | 22 +++------------- .../lib/src/user_list_core.dart | 25 +++---------------- 3 files changed, 12 insertions(+), 43 deletions(-) diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index e1ec1fc5..b14c7cbc 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,6 +1,6 @@ ## 3.0.0 -⚠️ Deprecated +🛑️ Breaking Changes from `2.2.1` - `MessageSearchListViewCore` `paginationParams` property is now deprecated in favor of `limit`. ```dart @@ -10,7 +10,7 @@ // new limit = 30 ``` -- `UserListViewCore` `pagination` property is now deprecated in favor of `limit`. +- `UserListCore` `pagination` property is now deprecated in favor of `limit`. ```dart // previous pagination = const PaginationParams(limit: 30) @@ -18,7 +18,7 @@ // new limit = 30 ``` -- `ChannelListViewCore` `pagination` property is now deprecated in favor of `limit`. +- `ChannelListCore` `pagination` property is now deprecated in favor of `limit`. ```dart // previous pagination = const PaginationParams(limit: 30) @@ -27,6 +27,8 @@ limit = 30 ``` +- `UserListViewCore` `filter` property now is non-nullable. + 🔄 Changed - `UserListViewCore` filter property now has a default value. diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index d3d6c312..8ac28e76 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -56,7 +56,7 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; /// information about the channels. class ChannelListCore extends StatefulWidget { /// Instantiate a new ChannelListView - ChannelListCore({ + const ChannelListCore({ Key? key, required this.errorBuilder, required this.emptyBuilder, @@ -69,15 +69,9 @@ class ChannelListCore extends StatefulWidget { this.memberLimit, this.messageLimit, this.sort, - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.pagination, this.channelListController, - int? limit, - }) : limit = limit ?? pagination?.limit ?? 25, - super(key: key); + this.limit = 25, + }) : super(key: key); /// A [ChannelListController] allows reloading and pagination. /// Use [ChannelListController.loadData] and @@ -124,16 +118,6 @@ class ChannelListCore extends StatefulWidget { /// Number of messages to fetch in each channel final int? messageLimit; - /// Pagination parameters - /// limit: the number of channels to return (max is 30) - /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? pagination; - /// The amount of channels requested per API call. final int limit; diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index 839067bf..05e9730b 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -57,7 +57,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// [errorBuilder] must all be supplied and not null. class UserListCore extends StatefulWidget { /// Instantiate a new [UserListCore] - UserListCore({ + const UserListCore({ required this.errorBuilder, required this.emptyBuilder, required this.loadingBuilder, @@ -66,16 +66,10 @@ class UserListCore extends StatefulWidget { this.filter = const Filter.empty(), this.sort, this.presence, - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.pagination, this.groupAlphabetically = false, this.userListController, - int? limit, - }) : limit = limit ?? pagination?.limit ?? 30, - super(key: key); + this.limit = 30, + }) : super(key: key); /// A [UserListController] allows reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData] @@ -97,8 +91,7 @@ class UserListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - // TODO: Make it non-nullable in a future breaking release - final Filter? filter; + final Filter filter; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be @@ -109,16 +102,6 @@ class UserListCore extends StatefulWidget { /// If true you’ll receive user presence updates via the websocket events final bool? presence; - /// Pagination parameters - /// limit: the number of users to return (max is 30) - /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel - @Deprecated( - "'pagination' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? pagination; - /// The amount of users requested per API call. final int limit; From 0810065f10a4562d77644aadc24d6ba775eb46a6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:28:25 +0200 Subject: [PATCH 092/148] chore(ui): bump version --- packages/stream_chat_flutter/CHANGELOG.md | 5 ++++- .../lib/src/message_search_list_view.dart | 22 +++---------------- .../lib/src/user_list_view.dart | 3 +-- packages/stream_chat_flutter/pubspec.yaml | 4 ++-- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index c6081ec6..50b0376c 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,4 +1,7 @@ -## Upcoming +## 3.0.0 +🛑️ Breaking Changes from `2.2.1` + +- `UserListView` `filter` property now is non-nullable. 🐞 Fixed diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index a1611412..daf34ff4 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -53,17 +53,12 @@ typedef EmptyMessageSearchBuilder = Widget Function( /// Modify it to change the widget appearance. class MessageSearchListView extends StatefulWidget { /// Instantiate a new MessageSearchListView - MessageSearchListView({ + const MessageSearchListView({ Key? key, required this.filters, this.messageQuery, this.sortOptions, - @Deprecated( - "'paginationParams' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - this.paginationParams, - int? limit, + this.limit = 30, this.messageFilters, this.separatorBuilder, this.itemBuilder, @@ -76,8 +71,7 @@ class MessageSearchListView extends StatefulWidget { this.loadingBuilder, this.childBuilder, this.messageSearchListController, - }) : limit = limit ?? paginationParams?.limit ?? 30, - super(key: key); + }) : super(key: key); /// Message String to search on final String? messageQuery; @@ -95,16 +89,6 @@ class MessageSearchListView extends StatefulWidget { /// Direction can be ascending or descending. final List? sortOptions; - /// Pagination parameters - /// limit: the number of users to return (max is 30) - /// offset: the offset (max is 1000) - /// message_limit: how many messages should be included to each channel - @Deprecated( - "'paginationParams' is deprecated and shouldn't be used. " - "This property is no longer used, Please use 'limit' instead", - ) - final PaginationParams? paginationParams; - /// The amount of messages requested per API call. final int limit; diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index 98cf6621..f7c9fa72 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -82,8 +82,7 @@ class UserListView extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - // TODO: Make it non-nullable in a future breaking release - final Filter? filter; + final Filter filter; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 907e9a7f..cf2c332a 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 2.2.1 +version: 3.0.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -37,7 +37,7 @@ dependencies: scrollable_positioned_list: ^0.2.0-nullsafety.0 share_plus: ^2.0.3 shimmer: ^2.0.0 - stream_chat_flutter_core: ^2.2.1 + stream_chat_flutter_core: 3.0.0 substring_highlight: ^1.0.26 synchronized: ^3.0.0 url_launcher: ^6.0.3 From bc27b5e70c6aadfb54d47b264b1a779b558f40ce Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:32:56 +0200 Subject: [PATCH 093/148] chore(persistence): bump version --- packages/stream_chat_persistence/CHANGELOG.md | 3 +-- packages/stream_chat_persistence/pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index 139056bd..de24856e 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,9 +1,8 @@ -## Upcoming +## 3.0.0 - [[#604]](https://github.com/GetStream/stream-chat-flutter/issues/604) Fix cascade deletion by enabling `pragma foreign_keys`. - Added a new table `PinnedMessageReactions` and dao `PinnedMessageReactionDao` specifically for pinned messages. -- Updated `stream_chat`: `^2.2.0` -> `TODO` ## 2.2.0 diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index a8e5f991..b510ac9d 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_persistence homepage: https://github.com/GetStream/stream-chat-flutter description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 2.2.0 +version: 3.0.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -18,7 +18,7 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.1 sqlite3_flutter_libs: ^0.5.0 - stream_chat: ^2.2.0 + stream_chat: 3.0.0 dev_dependencies: build_runner: ^2.0.1 From f09246ec1397619d52b76e230c6f636f49f12b40 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:33:36 +0200 Subject: [PATCH 094/148] chore(localization): bump version --- packages/stream_chat_localizations/CHANGELOG.md | 2 +- packages/stream_chat_localizations/pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index d8328475..deb564a9 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 2.0.0 🐞 Fixed diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index 0ed72acd..c89013c6 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -1,6 +1,6 @@ name: stream_chat_localizations description: The Official localizations for Stream Chat Flutter, a service for building chat applications -version: 1.1.0 +version: 2.0.0 homepage: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -14,7 +14,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - stream_chat_flutter: ^2.2.0 + stream_chat_flutter: 3.0.0 dev_dependencies: flutter_test: From 1fe446c6e65df3e753e8aad55efa37916ec2a70d Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 22 Sep 2021 15:47:52 +0200 Subject: [PATCH 095/148] fix(core): fix analysis --- packages/stream_chat_flutter_core/test/user_list_core_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index b077868b..c916ce17 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -522,7 +522,7 @@ void main() { ); test('`widget.limit` should match `widget.pagination.limit`', () { - const limit = 30; + const limit = 20; final userListCore = UserListCore( listBuilder: (_, __) => const Offstage(), loadingBuilder: (BuildContext context) => const Offstage(), From 853a3fe2af9ee6c60861ae4f13e9ccfd5d21dcc4 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 09:20:33 +0200 Subject: [PATCH 096/148] Apply suggestions from code review --- packages/stream_chat_flutter/CHANGELOG.md | 1 + packages/stream_chat_flutter_core/CHANGELOG.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 50b0376c..d1c04d24 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,4 +1,5 @@ ## 3.0.0 + 🛑️ Breaking Changes from `2.2.1` - `UserListView` `filter` property now is non-nullable. diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index b14c7cbc..9cc64f18 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -27,11 +27,11 @@ limit = 30 ``` -- `UserListViewCore` `filter` property now is non-nullable. +- `UserListCore` `filter` property now is non-nullable. 🔄 Changed -- `UserListViewCore` filter property now has a default value. +- `UserListCore` filter property now has a default value. ```dart filter = const Filter.empty() ``` From 175d4d38609ccfb9bfc31f434dc0c02b75e22134 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 09:30:59 +0200 Subject: [PATCH 097/148] chore(ui,core,persistance,localization): added dependency changelogs --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ packages/stream_chat_flutter_core/CHANGELOG.md | 2 ++ packages/stream_chat_localizations/CHANGELOG.md | 2 ++ packages/stream_chat_persistence/CHANGELOG.md | 1 + 4 files changed, 7 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index d1c04d24..0117a7ce 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ ## 3.0.0 +- Updated `stream_chat_flutter_core` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). + 🛑️ Breaking Changes from `2.2.1` - `UserListView` `filter` property now is non-nullable. diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 9cc64f18..cc1394a1 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,5 +1,7 @@ ## 3.0.0 +- Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). + 🛑️ Breaking Changes from `2.2.1` - `MessageSearchListViewCore` `paginationParams` property is now deprecated in favor of `limit`. diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index deb564a9..c1c469bf 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,5 +1,7 @@ ## 2.0.0 +- Updated `stream_chat_flutter` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog). + 🐞 Fixed * Fixed typos in `Italian` translations. diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index de24856e..f8a2434f 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,5 +1,6 @@ ## 3.0.0 +- Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). - [[#604]](https://github.com/GetStream/stream-chat-flutter/issues/604) Fix cascade deletion by enabling `pragma foreign_keys`. - Added a new table `PinnedMessageReactions` and dao `PinnedMessageReactionDao` specifically for pinned messages. From f345fa1d36181724a62ba3c4404b194350cc22b9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 27 Sep 2021 14:05:56 +0530 Subject: [PATCH 098/148] ref(ui): minor improvements Signed-off-by: xsahil03x --- .../lib/src/message_input.dart | 92 +++++++------------ 1 file changed, 32 insertions(+), 60 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 9cd84aa2..95322c10 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -171,7 +171,7 @@ class MessageInput extends StatefulWidget { this.disableAttachments = false, this.initialMessage, this.textEditingController, - this.actions, + this.actions = const [], this.actionsLocation = ActionsLocation.left, this.attachmentThumbnailBuilders, this.focusNode, @@ -248,7 +248,7 @@ class MessageInput extends StatefulWidget { final TextEditingController? textEditingController; /// List of action widgets - final List? actions; + final List actions; /// The location of the custom actions final ActionsLocation actionsLocation; @@ -324,7 +324,7 @@ class MessageInputState extends State { final List _mentionedUsers = []; final _imagePicker = ImagePicker(); - late final FocusNode _focusNode; + late final _focusNode = widget.focusNode ?? FocusNode(); bool _inputEnabled = true; bool _commandEnabled = false; bool _showCommandsOverlay = false; @@ -337,7 +337,8 @@ class MessageInputState extends State { int _filePickerIndex = 0; /// The editing controller passed to the input TextField - late final TextEditingController textEditingController; + late final TextEditingController textEditingController = + widget.textEditingController ?? TextEditingController(); late StreamChatThemeData _streamChatTheme; late MessageInputThemeData _messageInputTheme; @@ -349,18 +350,10 @@ class MessageInputState extends State { @override void initState() { super.initState(); - _focusNode = widget.focusNode ?? FocusNode(); - - textEditingController = - widget.textEditingController ?? TextEditingController(); if (widget.editMessage != null || widget.initialMessage != null) { _parseExistingMessage(widget.editMessage ?? widget.initialMessage!); } - - textEditingController.addListener(() { - _onChanged(context, textEditingController.text); - }); - + textEditingController.addListener(_onChangedDebounced); _focusNode.addListener(() { if (_focusNode.hasFocus) { _openFilePickerSection = false; @@ -625,7 +618,7 @@ class MessageInputState extends State { ), secondChild: widget.disableAttachments && !widget.showCommandsButton && - widget.actions?.isNotEmpty != true + widget.actions.isNotEmpty != true ? const Offstage() : Wrap( children: [ @@ -636,7 +629,7 @@ class MessageInputState extends State { channel.state != null && channel.config?.commands.isNotEmpty == true) _buildCommandButton(context), - ...widget.actions ?? [], + ...widget.actions, ].insertBetween(const SizedBox(width: 8)), ), duration: const Duration(milliseconds: 300), @@ -804,50 +797,32 @@ class MessageInputState extends State { ).merge(passedDecoration); } - Timer? _debounce; + late final _onChangedDebounced = debounce( + () { + var value = textEditingController.text; + if (!mounted) return; + value = value.trim(); - String? _previousValue; + final channel = StreamChannel.of(context).channel; + if (value.isNotEmpty) { + channel.keyStroke(widget.parentMessage?.id).catchError((e) {}); + } - void _onChanged( - BuildContext context, - String s, - ) { - if (s == _previousValue) { - return; - } - _previousValue = s; + var actionsLength = widget.actions.length; + if (widget.showCommandsButton) actionsLength += 1; + if (!widget.disableAttachments) actionsLength += 1; - if (_debounce?.isActive == true) _debounce!.cancel(); - _debounce = Timer( - const Duration(milliseconds: 350), - () { - if (!mounted) { - return; - } + setState(() { + _actionsShrunk = value.isNotEmpty && actionsLength > 1; + }); - if (s.isNotEmpty) { - StreamChannel.of(context) - .channel - .keyStroke(widget.parentMessage?.id) - .catchError((e) {}); - } - - _checkCommands(s.trim(), context); - - _checkMentions(s, context); - - _checkEmoji(s, context); - - setState(() { - _actionsShrunk = s.trim().isNotEmpty && - ((widget.actions?.length ?? 0) + - (widget.showCommandsButton ? 1 : 0) + - (widget.disableAttachments ? 0 : 1) > - 1); - }); - }, - ); - } + _checkCommands(value, context); + _checkMentions(value, context); + _checkEmoji(value, context); + }, + const Duration(milliseconds: 350), + leading: true, + ); String _getHint(BuildContext context) { if (_commandEnabled && _chosenCommand!.name == 'giphy') { @@ -867,10 +842,7 @@ class MessageInputState extends State { if (s.isNotEmpty && textEditingController.selection.baseOffset > 0 && textEditingController.text - .substring( - 0, - textEditingController.selection.baseOffset, - ) + .substring(0, textEditingController.selection.baseOffset) .contains(':')) { final textToSelection = textEditingController.text .substring(0, textEditingController.value.selection.start); @@ -1207,7 +1179,7 @@ class MessageInputState extends State { offset: rejoin.length, ), ); - _debounce!.cancel(); + _onChangedDebounced.cancel(); setState(() { _showMentionsOverlay = false; }); From 2b5e827e13527fd56ef26606d14eeada2aff9dcb Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 10:48:46 +0200 Subject: [PATCH 099/148] chore(ui,core,persistance,localization): loose dependency constraints --- packages/stream_chat_flutter/pubspec.yaml | 2 +- packages/stream_chat_flutter_core/pubspec.yaml | 2 +- packages/stream_chat_localizations/pubspec.yaml | 2 +- packages/stream_chat_persistence/pubspec.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index cf2c332a..fb7d3af7 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: scrollable_positioned_list: ^0.2.0-nullsafety.0 share_plus: ^2.0.3 shimmer: ^2.0.0 - stream_chat_flutter_core: 3.0.0 + stream_chat_flutter_core: ^3.0.0 substring_highlight: ^1.0.26 synchronized: ^3.0.0 url_launcher: ^6.0.3 diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 27010d48..e5520921 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: sdk: flutter meta: ^1.3.0 rxdart: ^0.27.0 - stream_chat: 3.0.0 + stream_chat: ^3.0.0 dev_dependencies: fake_async: ^1.2.0 diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index c89013c6..552ee5ec 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - stream_chat_flutter: 3.0.0 + stream_chat_flutter: ^3.0.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index b510ac9d..4f3ab8e9 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.1 sqlite3_flutter_libs: ^0.5.0 - stream_chat: 3.0.0 + stream_chat: ^3.0.0 dev_dependencies: build_runner: ^2.0.1 From 1e5a042d3b2beb1df8d495e048cfc7294d4cc193 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 11:08:49 +0200 Subject: [PATCH 100/148] chore(ui): fix dependencies --- packages/stream_chat_flutter/test/src/emoji_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/test/src/emoji_test.dart b/packages/stream_chat_flutter/test/src/emoji_test.dart index 61a0ba58..ce63faa9 100644 --- a/packages/stream_chat_flutter/test/src/emoji_test.dart +++ b/packages/stream_chat_flutter/test/src/emoji_test.dart @@ -1,5 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; -import 'package:test/test.dart'; void main() { group('src/emoji', () { From 6686cc1af63c308e9e56c99b3473b82fc497aa5b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 27 Sep 2021 16:27:17 +0530 Subject: [PATCH 101/148] feat(ui): improve add support for `mentionAllAppUsers` in `UserMentionsOverlay`. Signed-off-by: xsahil03x --- .../lib/src/extension.dart | 53 +++++ .../lib/src/mention_tile.dart | 4 + .../lib/src/mentions_overlay.dart | 100 --------- .../lib/src/message_input.dart | 27 +-- .../lib/src/user_mention_tile.dart | 91 +++++++++ .../lib/src/user_mentions_overlay.dart | 191 ++++++++++++++++++ .../stream_chat_flutter/lib/src/utils.dart | 39 +++- .../lib/stream_chat_flutter.dart | 1 + packages/stream_chat_flutter/pubspec.yaml | 1 + .../test/src/extension_test.dart | 71 +++++++ .../lib/src/stream_channel.dart | 22 +- 11 files changed, 481 insertions(+), 119 deletions(-) delete mode 100644 packages/stream_chat_flutter/lib/src/mentions_overlay.dart create mode 100644 packages/stream_chat_flutter/lib/src/user_mention_tile.dart create mode 100644 packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart create mode 100644 packages/stream_chat_flutter/test/src/extension_test.dart diff --git a/packages/stream_chat_flutter/lib/src/extension.dart b/packages/stream_chat_flutter/lib/src/extension.dart index 19bf4523..9551a40d 100644 --- a/packages/stream_chat_flutter/lib/src/extension.dart +++ b/packages/stream_chat_flutter/lib/src/extension.dart @@ -1,4 +1,5 @@ import 'package:characters/characters.dart'; +import 'package:diacritic/diacritic.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; @@ -24,6 +25,12 @@ extension StringExtension on String { final characters = trim().characters; return characters.every(_emojiChars.contains); } + + /// Removes accents and diacritics from the given String. + String get diacriticsInsensitive => removeDiacritics(this); + + /// Levenshtein distance between this and [t]. + int levenshteinDistance(String t) => levenshtein(this, t); } /// List extension @@ -170,3 +177,49 @@ extension IconButtonX on IconButton { icon: icon ?? this.icon, ); } + +/// Extensions on List +extension UserListX on List { + /// It does an search on a list of [User] and returns users with + /// `id` or `name` containing the [query]. + /// + /// Results are returned sorted by their edit distance from the + /// searched string, distance is calculated using the [levenshtein] algorithm. + List search(String query) { + String normalize(String input) => input.toLowerCase().diacriticsInsensitive; + + final normalizedQuery = normalize(query); + + final matchingUsers = {}; // User:lDistance + + for (final user in this) { + final normalizedId = normalize(user.id); + final normalizedUserName = normalize(user.name); + final lDistance = normalizedUserName.levenshteinDistance(normalizedQuery); + final containsId = normalizedId.contains(normalizedQuery); + final containsName = normalizedUserName.contains(normalizedQuery); + if (lDistance < 3 || containsId || containsName) { + matchingUsers[user] = lDistance; + } + } + + final entries = matchingUsers.entries.toList(growable: false) + ..sort((prev, curr) { + bool containsQuery(User user) => + normalize(user.id).contains(normalizedQuery) || + normalize(user.name).contains(normalizedQuery); + + final containsInPrev = containsQuery(prev.key); + final containsInCurr = containsQuery(curr.key); + + if (containsInPrev && !containsInCurr) { + return -1; + } else if (!containsInPrev && containsInCurr) { + return 1; + } + return prev.value.compareTo(curr.value); + }); + + return entries.map((e) => e.key).toList(growable: false); + } +} diff --git a/packages/stream_chat_flutter/lib/src/mention_tile.dart b/packages/stream_chat_flutter/lib/src/mention_tile.dart index d9469215..b900be65 100644 --- a/packages/stream_chat_flutter/lib/src/mention_tile.dart +++ b/packages/stream_chat_flutter/lib/src/mention_tile.dart @@ -4,6 +4,10 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// This widget is used for showing user tiles for mentions /// Use [title], [subtitle], [leading], [trailing] for /// substituting widgets in respective positions +@Deprecated( + "'MentionTile' widget is deprecated and shouldn't be used. " + "Use 'UserMentionTile' instead", +) class MentionTile extends StatelessWidget { /// Constructor for creating a [MentionTile] widget const MentionTile( diff --git a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/mentions_overlay.dart deleted file mode 100644 index 63c43585..00000000 --- a/packages/stream_chat_flutter/lib/src/mentions_overlay.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Overlay for displaying users that can be mentioned -class MentionsOverlay extends StatelessWidget { - /// Constructor for creating a [MentionsOverlay] - const MentionsOverlay({ - required this.query, - required this.onMentionResult, - required this.size, - required this.channel, - this.mentionsTileBuilder, - Key? key, - }) : super(key: key); - - /// The size of the overlay - final Size size; - - /// Query for searching users - final String query; - - /// The channel to search for users - final Channel channel; - - /// Callback called when a user is selected - final ValueChanged onMentionResult; - - /// Customize the tile for the mentions overlay - final MentionTileBuilder? mentionsTileBuilder; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - - Future>? queryMembers; - - if (query.isNotEmpty) { - queryMembers = channel - .queryMembers(filter: Filter.autoComplete('name', query)) - .then((res) => res.members); - } - - final members = channel.state?.members - .where((m) => m.user?.name.toLowerCase().contains(query) == true) - .toList() ?? - []; - - if (members.isEmpty) { - return const SizedBox(); - } - - return Card( - margin: const EdgeInsets.all(8), - elevation: 2, - color: _streamChatTheme.colorTheme.barsBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - clipBehavior: Clip.hardEdge, - child: Container( - constraints: BoxConstraints.loose(size), - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.barsBg, - ), - child: FutureBuilder>( - future: queryMembers ?? Future.value(members), - initialData: members, - builder: (context, snapshot) => ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: [ - const SizedBox( - height: 8, - ), - ...snapshot.data! - .where((it) => it.user != null) - .map( - (m) => Material( - color: _streamChatTheme.colorTheme.barsBg, - child: InkWell( - onTap: () { - onMentionResult(m.user); - }, - child: mentionsTileBuilder != null - ? mentionsTileBuilder!(context, m) - : MentionTile(m, key: ValueKey(m.user?.id)), - ), - ), - ) - .toList(), - const SizedBox( - height: 8, - ), - ], - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 95322c10..c8197e59 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -15,7 +15,7 @@ import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; -import 'package:stream_chat_flutter/src/mentions_overlay.dart'; +import 'package:stream_chat_flutter/src/user_mentions_overlay.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/overlays.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; @@ -193,6 +193,7 @@ class MessageInput extends StatefulWidget { this.attachmentButtonBuilder, this.commandButtonBuilder, this.customOverlays = const [], + this.mentionAllAppUsers = false, }) : assert( initialMessage == null || editMessage == null, "Can't provide both `initialMessage` and `editMessage`", @@ -303,6 +304,11 @@ class MessageInput extends StatefulWidget { /// calling `.copyWith`. final ActionButtonBuilder? commandButtonBuilder; + /// When enabled mentions search users across the entire app. + /// + /// Defaults to false. + final bool mentionAllAppUsers; + @override MessageInputState createState() => MessageInputState(); @@ -1159,16 +1165,15 @@ class MessageInputState extends State { // ignore: cast_nullable_to_non_nullable final renderObject = context.findRenderObject() as RenderBox; - return MentionsOverlay( + return UserMentionsOverlay( + query: query, + mentionAllAppUsers: widget.mentionAllAppUsers, + client: StreamChat.of(context).client, channel: StreamChannel.of(context).channel, size: Size(renderObject.size.width - 16, 400), - query: query, - onMentionResult: (user) { - if (user != null) { - _mentionedUsers.add(user); - } - - splits[splits.length - 1] = user!.name; + onMentionUserTap: (user) { + _mentionedUsers.add(user); + splits[splits.length - 1] = user.name; final rejoin = splits.join('@'); textEditingController.value = TextEditingValue( @@ -1180,9 +1185,7 @@ class MessageInputState extends State { ), ); _onChangedDebounced.cancel(); - setState(() { - _showMentionsOverlay = false; - }); + setState(() => _showMentionsOverlay = false); }, ); } diff --git a/packages/stream_chat_flutter/lib/src/user_mention_tile.dart b/packages/stream_chat_flutter/lib/src/user_mention_tile.dart new file mode 100644 index 00000000..ff9e145b --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/user_mention_tile.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +/// This widget is used for showing user tiles for mentions +/// Use [title], [subtitle], [leading], [trailing] for +/// substituting widgets in respective positions +class UserMentionTile extends StatelessWidget { + /// Constructor for creating a [UserMentionTile] widget + const UserMentionTile( + this.user, { + Key? key, + this.title, + this.subtitle, + this.leading, + this.trailing, + }) : super(key: key); + + /// User to display in the tile + final User user; + + /// Widget to display as title + final Widget? title; + + /// Widget to display below [title] + final Widget? subtitle; + + /// Widget at the start of the tile + final Widget? leading; + + /// Widget at the end of tile + final Widget? trailing; + + @override + Widget build(BuildContext context) { + final chatThemeData = StreamChatTheme.of(context); + return SizedBox( + height: 56, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + leading ?? + UserAvatar( + user: user, + constraints: BoxConstraints.tight(const Size(40, 40)), + ), + const SizedBox(width: 8), + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title ?? + Text( + user.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: chatThemeData.textTheme.bodyBold, + ), + const SizedBox(height: 2), + subtitle ?? + Text( + '@${user.id}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: chatThemeData.textTheme.footnoteBold.copyWith( + color: chatThemeData.colorTheme.textLowEmphasis, + ), + ), + ], + ), + ), + ), + trailing ?? + Padding( + padding: const EdgeInsets.only( + right: 18, + left: 8, + ), + child: StreamSvgIcon.mentions( + color: chatThemeData.colorTheme.accentPrimary, + ), + ), + ], + ), + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart new file mode 100644 index 00000000..d155ce4c --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; +import 'package:stream_chat_flutter/src/user_mention_tile.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; + +/// Builder function for building a mention tile. +/// +/// Use [UserMentionTile] for the default implementation. +typedef MentionTileBuilder = Widget Function( + BuildContext context, + User user, +); + +/// Overlay for displaying users that can be mentioned. +class UserMentionsOverlay extends StatefulWidget { + /// Constructor for creating a [UserMentionsOverlay]. + UserMentionsOverlay({ + Key? key, + required this.query, + required this.channel, + required this.size, + this.client, + this.limit = 10, + this.mentionAllAppUsers = false, + this.mentionsTileBuilder, + this.onMentionUserTap, + }) : assert( + channel.state != null, + 'Channel ${channel.cid} is not yet initialized', + ), + assert( + mentionAllAppUsers && client != null, + 'StreamChatClient is required in order to use mentionAllAppUsers', + ), + super(key: key); + + /// Query for searching users. + final String query; + + /// Limit applied on user search results. + final int limit; + + /// The size of the overlay. + final Size size; + + /// The channel to search for users. + final Channel channel; + + /// The client to search for users in case [mentionAllAppUsers] is True. + final StreamChatClient? client; + + /// When enabled mentions search users across the entire app. + /// + /// Defaults to false. + final bool mentionAllAppUsers; + + /// Customize the tile for the mentions overlay. + final MentionTileBuilder? mentionsTileBuilder; + + /// Callback called when a user is selected. + final void Function(User user)? onMentionUserTap; + + @override + _UserMentionsOverlayState createState() => _UserMentionsOverlayState(); +} + +class _UserMentionsOverlayState extends State { + late Future> userMentionsFuture; + late List initialMentions = membersAndWatchers; + + @override + void initState() { + super.initState(); + userMentionsFuture = queryMentions(widget.query); + } + + @override + void didUpdateWidget(covariant UserMentionsOverlay oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.channel != oldWidget.channel || + widget.query != oldWidget.query || + widget.mentionAllAppUsers != oldWidget.mentionAllAppUsers || + widget.limit != oldWidget.limit) { + userMentionsFuture = queryMentions(widget.query); + } + } + + @override + Widget build(BuildContext context) { + final theme = StreamChatTheme.of(context); + return Card( + margin: const EdgeInsets.all(8), + elevation: 2, + color: theme.colorTheme.barsBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + child: Container( + constraints: BoxConstraints.loose(widget.size), + decoration: BoxDecoration(color: theme.colorTheme.barsBg), + child: FutureBuilder>( + future: userMentionsFuture, + initialData: initialMentions, + builder: (context, snapshot) { + final users = snapshot.data!; + return ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + itemCount: users.length, + itemBuilder: (context, index) { + final user = users[index]; + return Material( + color: theme.colorTheme.barsBg, + child: InkWell( + onTap: () => widget.onMentionUserTap?.call(user), + child: widget.mentionsTileBuilder?.call(context, user) ?? + UserMentionTile(user), + ), + ); + }, + ); + }, + ), + ), + ); + } + + List get membersAndWatchers { + final state = widget.channel.state!; + return { + ...state.watchers, + ...state.members.map((it) => it.user), + }.whereType().toList(growable: false); + } + + Future> queryMentions(String query) async { + if (widget.mentionAllAppUsers) { + return _queryUsers(query); + } + + var channelState = widget.channel.state; + + channelState = channelState!; + final members = channelState.members; + + // By default, we return maximum 100 members via queryChannels api call. + // Thus it is safe to assume, that if number of members in channel.state + // is < 100, then all the members are already available on client side + // and we don't need to make any api call to queryMembers endpoint. + if (members.length < 100) { + final matchingUsers = membersAndWatchers.search(query); + return matchingUsers.toList(growable: false); + } + + final result = await _queryMembers(query); + return result + .map((it) => it.user) + .whereType() + .toList(growable: false); + } + + Future> _queryMembers(String query) async { + final response = await widget.channel.queryMembers( + pagination: PaginationParams(limit: widget.limit), + filter: query.isEmpty + ? const Filter.empty() + : Filter.autoComplete('name', query), + ); + return response.members; + } + + Future> _queryUsers(String query) async { + assert( + widget.client != null, + 'StreamChatClient is required in order to query all app users', + ); + final response = await widget.client!.queryUsers( + pagination: PaginationParams(limit: widget.limit), + filter: query.isEmpty + ? const Filter.empty() + : Filter.or([ + Filter.autoComplete('id', query), + Filter.autoComplete('name', query), + ]), + sort: [const SortOption('id', direction: SortOption.ASC)], + ); + return response.users; + } +} diff --git a/packages/stream_chat_flutter/lib/src/utils.dart b/packages/stream_chat_flutter/lib/src/utils.dart index 4dc9fee7..9b85e8c2 100644 --- a/packages/stream_chat_flutter/lib/src/utils.dart +++ b/packages/stream_chat_flutter/lib/src/utils.dart @@ -1,10 +1,11 @@ import 'dart:async'; +import 'dart:math' as math; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; /// Launch URL Future launchURL(BuildContext context, String url) async { @@ -388,3 +389,39 @@ class Tuple2 { @override int get hashCode => item1.hashCode ^ item2.hashCode; } + +/// Levenshtein algorithm implementation based on: +/// http://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows +int levenshtein(String s, String t, {bool caseSensitive = true}) { + if (!caseSensitive) { + // ignore: parameter_assignments + s = s.toLowerCase(); + // ignore: parameter_assignments + t = t.toLowerCase(); + } + if (s == t) return 0; + if (s.isEmpty) return t.length; + if (t.isEmpty) return s.length; + + final v0 = List.filled(t.length + 1, 0); + final v1 = List.filled(t.length + 1, 0); + + for (var i = 0; i < t.length + 1; i < i++) { + v0[i] = i; + } + + for (var i = 0; i < s.length; i++) { + v1[0] = i + 1; + + for (var j = 0; j < t.length; j++) { + final cost = (s[i] == t[j]) ? 0 : 1; + v1[j + 1] = math.min(v1[j] + 1, math.min(v0[j + 1] + 1, v0[j] + cost)); + } + + for (var j = 0; j < t.length + 1; j++) { + v0[j] = v1[j]; + } + } + + return v1[t.length]; +} diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 42188fa0..0447fc68 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -44,5 +44,6 @@ export 'src/unread_indicator.dart'; export 'src/user_avatar.dart'; export 'src/user_item.dart'; export 'src/user_list_view.dart'; +export 'src/user_mention_tile.dart'; export 'src/utils.dart'; export 'src/visible_footnote.dart'; diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 54888873..a792d67b 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: characters: ^1.1.0 chewie: ^1.2.0 collection: ^1.15.0 + diacritic: ^0.1.3 dio: ^4.0.0 ezanimation: ^0.5.0 file_picker: ^3.0.1 diff --git a/packages/stream_chat_flutter/test/src/extension_test.dart b/packages/stream_chat_flutter/test/src/extension_test.dart new file mode 100644 index 00000000..9d8d92e4 --- /dev/null +++ b/packages/stream_chat_flutter/test/src/extension_test.dart @@ -0,0 +1,71 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +void main() { + group('List.search', () { + test('should work fine', () { + final tommaso = User(id: 'tommaso', name: 'Tommaso'); + final thierry = User(id: 'thierry', name: 'Thierry'); + final users = [tommaso, thierry]; + + final a = users.search('Tom'); + expect(users.search('Tom'), [tommaso]); + expect(users.search('Thier'), [thierry]); + }); + + test('should search using UpperCased', () { + final tommaso = User(id: 'tommaso', name: 'Tommaso'); + final thierry = User(id: 'thierry', name: 'Thierry'); + final users = [tommaso, thierry]; + + expect(users.search('tom'), [tommaso]); + expect(users.search('thier'), [thierry]); + }); + + test('should search by .id or .name', () { + final user1 = User(id: 'searchingThis'); + final user2 = User(id: 'x', name: 'searchingThis'); + + expect([user1].search('sear'), [user1]); + expect([user2].search('sear'), [user2]); + }); + + test('should search transliterated', () { + final tommaso = User(id: 'tommaso', name: 'Tommaso'); + final thierry = User(id: 'thierry', name: 'Thierry'); + final users = [tommaso, thierry]; + + expect(users.search('tóm'), [tommaso]); + expect(users.search('thíer'), [thierry]); + }); + + test('search and sorted by distance', () { + final tommaso = User(id: 'tommaso', name: 'Tommaso'); + final tomas = User(id: 'tomas', name: 'Tomas'); + final users = [tommaso, tomas]; + + expect(users.search('tom'), [tomas, tommaso]); + }); + + test('should work fine with cyrillic diacritics', () { + final petyo = User(id: '42', name: 'Петьо'); + final anastasia = User(id: '13', name: 'Анастасiя'); + final dmitriy = User(id: '99', name: 'Дмитрий'); + final users = [petyo, anastasia, dmitriy]; + + expect(users.search('petyo'), []); + expect(users.search('Пе'), [petyo]); + expect(users.search('Ана'), [anastasia]); + expect(users.search('Дмитри'), [dmitriy]); + expect(users.search('Дмитрии'), [dmitriy]); + }); + + test('should work fine with french diacritics', () { + final user = User(id: 'fra', name: 'françois'); + + expect([user].search('françois'), [user]); + expect([user].search('franc'), [user]); + }); + }); +} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index fb7b79f2..443529ac 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -152,13 +152,9 @@ class StreamChannelState extends State { int limit = 20, }) { if (direction == QueryDirection.top) { - return _queryTopMessages( - limit: limit, - ); + return _queryTopMessages(limit: limit); } - return _queryBottomMessages( - limit: limit, - ); + return _queryBottomMessages(limit: limit); } /// Calls [channel.getReplies] updating [queryMessage] stream @@ -311,6 +307,20 @@ class StreamChannelState extends State { return message; } + /// Query channel members. + Future> queryMembers({ + Filter? filter, + List? sort, + PaginationParams? pagination, + }) async { + final response = await channel.queryMembers( + filter: filter, + sort: sort, + pagination: pagination, + ); + return response.members; + } + /// Reloads the channel with latest message Future reloadChannel() => _queryAtMessage(before: 30); From a931682b4c3551580eb5d08b356afb235dbad37d Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 27 Sep 2021 17:46:57 +0530 Subject: [PATCH 102/148] added not exists filter --- .../stream_chat/lib/src/core/models/filter.dart | 15 +++++++++++---- .../test/src/core/models/filter_test.dart | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index ca494bdb..ba7d044e 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -40,9 +40,12 @@ enum FilterOperator { /// Matches values with the specified prefix. autoComplete, - /// Matches values that exist/don't exist based on the specified boolean value. + /// Matches values that exist. exists, + /// Matches values that don't exist. + notExists, + /// Matches all the values specified in an array. and, @@ -163,9 +166,13 @@ class Filter extends Equatable { factory Filter.autoComplete(String key, String text) => Filter._(operator: FilterOperator.autoComplete, key: key, value: text); - /// Matches values that exist/don't exist based on the specified boolean value. - factory Filter.exists(String key, {bool exists = true}) => - Filter._(operator: FilterOperator.exists, key: key, value: exists); + /// Matches values that exist. + factory Filter.exists(String key) => + Filter._(operator: FilterOperator.exists, key: key, value: true); + + /// Matches values that don't exist. + factory Filter.notExists(String key) => + Filter._(operator: FilterOperator.exists, key: key, value: false); /// Matches any list that contains the specified values factory Filter.contains(String key, Object value) => diff --git a/packages/stream_chat/test/src/core/models/filter_test.dart b/packages/stream_chat/test/src/core/models/filter_test.dart index c0a8ce8f..f836d845 100644 --- a/packages/stream_chat/test/src/core/models/filter_test.dart +++ b/packages/stream_chat/test/src/core/models/filter_test.dart @@ -114,7 +114,7 @@ void main() { test('notExists', () { const key = 'testKey'; - final filter = Filter.exists(key, exists: false); + final filter = Filter.notExists(key); expect(filter.key, key); expect(filter.value, isFalse); expect(filter.operator, FilterOperator.exists.rawValue); From d996eddb568e0b421a25e688a79325253cbb4fde Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 27 Sep 2021 17:59:59 +0530 Subject: [PATCH 103/148] added changelog --- packages/stream_chat/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 3c7dd84f..06370e64 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +🛑️ Breaking Changes from `3.0.0` + +- `Filter.notExists` now replaces `Filter.exists(key, exists: false)`. + ## 3.0.0 🛑️ Breaking Changes from `2.2.1` From 98493b77d8bc4c3a30aea80cefb5ee8e2fa345b0 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 27 Sep 2021 18:37:26 +0530 Subject: [PATCH 104/148] removed breaking change --- packages/stream_chat/CHANGELOG.md | 2 +- packages/stream_chat/lib/src/core/models/filter.dart | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 06370e64..c12377b1 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,6 +1,6 @@ ## Upcoming -🛑️ Breaking Changes from `3.0.0` +✅ Added - `Filter.notExists` now replaces `Filter.exists(key, exists: false)`. diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index ba7d044e..fb4cfb42 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -167,12 +167,11 @@ class Filter extends Equatable { Filter._(operator: FilterOperator.autoComplete, key: key, value: text); /// Matches values that exist. - factory Filter.exists(String key) => - Filter._(operator: FilterOperator.exists, key: key, value: true); + factory Filter.exists(String key, {bool exists = true}) => + Filter._(operator: FilterOperator.exists, key: key, value: exists); /// Matches values that don't exist. - factory Filter.notExists(String key) => - Filter._(operator: FilterOperator.exists, key: key, value: false); + factory Filter.notExists(String key) => Filter.exists(key, exists: false); /// Matches any list that contains the specified values factory Filter.contains(String key, Object value) => From 9ac85a31a7023d380dee7470d937684e9725e5c6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 16:57:14 +0200 Subject: [PATCH 105/148] fix(ui): thread reply scrolling main list --- packages/stream_chat_flutter/lib/src/message_list_view.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 4f76a3bf..e67f00dd 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -1227,8 +1227,9 @@ class _MessageListViewState extends State { _bottomPaginationActive = false; _topPaginationActive = false; } - if (event.message!.user!.id == - streamChannel!.channel.client.state.currentUser!.id) { + if (event.message?.parentId == widget.parentMessage?.id && + event.message!.user!.id == + streamChannel!.channel.client.state.currentUser!.id) { WidgetsBinding.instance!.addPostFrameCallback((_) { _scrollController?.jumpTo( index: 0, From 5a9af36d8acc7264397f0c0154647130e5b4a8f8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 27 Sep 2021 16:58:57 +0200 Subject: [PATCH 106/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 0117a7ce..ec368723 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +🐞 Fixed + +-[[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. + ## 3.0.0 - Updated `stream_chat_flutter_core` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). From 4592491b153ff5d8ea6c8a63bf9a6ddfc52676d9 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 28 Sep 2021 14:09:47 +0530 Subject: [PATCH 107/148] Update packages/stream_chat/CHANGELOG.md Co-authored-by: Salvatore Giordano --- packages/stream_chat/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index c12377b1..cd732218 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -2,7 +2,7 @@ ✅ Added -- `Filter.notExists` now replaces `Filter.exists(key, exists: false)`. +- Added `Filter.notExists`. ## 3.0.0 From 8fd25028d3811e7be1aad5a3f18f2e15695be458 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 28 Sep 2021 14:10:14 +0530 Subject: [PATCH 108/148] Update packages/stream_chat/lib/src/core/models/filter.dart Co-authored-by: Salvatore Giordano --- packages/stream_chat/lib/src/core/models/filter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index fb4cfb42..cd53c9e8 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -166,7 +166,7 @@ class Filter extends Equatable { factory Filter.autoComplete(String key, String text) => Filter._(operator: FilterOperator.autoComplete, key: key, value: text); - /// Matches values that exist. + /// Matches values that exist/don't exist based on the specified boolean value. factory Filter.exists(String key, {bool exists = true}) => Filter._(operator: FilterOperator.exists, key: key, value: exists); From 0f9a6c21ac16f88d4e0314974e6436932db986c2 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 28 Sep 2021 14:10:34 +0530 Subject: [PATCH 109/148] Update packages/stream_chat/lib/src/core/models/filter.dart Co-authored-by: Salvatore Giordano --- packages/stream_chat/lib/src/core/models/filter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index cd53c9e8..e668c417 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -40,7 +40,7 @@ enum FilterOperator { /// Matches values with the specified prefix. autoComplete, - /// Matches values that exist. + /// Matches values that exist/don't exist based on the specified boolean value. exists, /// Matches values that don't exist. From 517c2cf00b0957409eee160e6d9e3efeaa3ea35e Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 28 Sep 2021 17:21:52 +0530 Subject: [PATCH 110/148] added docs for Filter.notExists --- .../docs/Flutter/guides/understanding_filters.mdx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docusaurus/docs/Flutter/guides/understanding_filters.mdx b/docusaurus/docs/Flutter/guides/understanding_filters.mdx index e9d72ecf..292afd07 100644 --- a/docusaurus/docs/Flutter/guides/understanding_filters.mdx +++ b/docusaurus/docs/Flutter/guides/understanding_filters.mdx @@ -104,7 +104,16 @@ Filter.autoComplete('name', 'demo') The 'exists' filter matches values that exist, or don't exist, based on the specified boolean value. ```dart -Filter.exists('name', true) +Filter.exists('name') +``` + +#### Filter.notExists + +The 'notExists' filter checks if the specified key doesn't exist. This is a simplified call to `Filter.exists` +with the value set to false. + +```dart +Filter.notExists('name') ``` #### Filter.contains From b5060b26f60eb403e6885676e6a68546063b304c Mon Sep 17 00:00:00 2001 From: Omkar Todkar Date: Wed, 29 Sep 2021 02:50:38 +0530 Subject: [PATCH 111/148] support for int userId in tokens --- packages/stream_chat/lib/src/core/http/token.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/lib/src/core/http/token.dart b/packages/stream_chat/lib/src/core/http/token.dart index 7d746286..0830aa68 100644 --- a/packages/stream_chat/lib/src/core/http/token.dart +++ b/packages/stream_chat/lib/src/core/http/token.dart @@ -46,12 +46,12 @@ class Token extends Equatable { /// Creates a [Token] instance from the provided [rawValue] if it's valid. factory Token.fromRawValue(String rawValue) { final jwtBody = JsonWebToken.unverified(rawValue); - final userId = jwtBody.claims.getTyped('user_id'); + final userId = jwtBody.claims.getTyped('user_id'); assert( userId != null, 'Invalid `token`, It should contain `user_id`', ); - return Token._(rawValue: rawValue, userId: userId!, authType: AuthType.jwt); + return Token._(rawValue: rawValue, userId: userId!.toString(), authType: AuthType.jwt); } /// The token which can be used during the development. From 2ce209bed0d68cb66545682ca2333d3c744df8e2 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 29 Sep 2021 09:55:56 +0200 Subject: [PATCH 112/148] fix(llc): format and analyze --- packages/stream_chat/CHANGELOG.md | 6 ++++++ packages/stream_chat/lib/src/core/http/token.dart | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 3c7dd84f..fbac9f87 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +🐞 Fixed + +- [[#710]](https://github.com/GetStream/stream-chat-flutter/issues/710) Fixed JWT requiring using `String` as id. + ## 3.0.0 🛑️ Breaking Changes from `2.2.1` diff --git a/packages/stream_chat/lib/src/core/http/token.dart b/packages/stream_chat/lib/src/core/http/token.dart index 0830aa68..eba49b82 100644 --- a/packages/stream_chat/lib/src/core/http/token.dart +++ b/packages/stream_chat/lib/src/core/http/token.dart @@ -51,7 +51,11 @@ class Token extends Equatable { userId != null, 'Invalid `token`, It should contain `user_id`', ); - return Token._(rawValue: rawValue, userId: userId!.toString(), authType: AuthType.jwt); + return Token._( + rawValue: rawValue, + userId: userId!.toString(), + authType: AuthType.jwt, + ); } /// The token which can be used during the development. From 35673f140c64921f98e14357b042ff277345d3e6 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 16:21:40 +0530 Subject: [PATCH 113/148] ref(llc): fixes and improvements Signed-off-by: xsahil03x --- .../stream_chat/lib/src/client/channel.dart | 98 ++++++++++--------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 96bd1838..0c6b0ed1 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -351,7 +351,7 @@ class Channel { Future get initialized => _initializedCompleter.future; final _cancelableAttachmentUploadRequest = {}; - final _messageAttachmentsUploadCompleter = {}; + final _messageAttachmentsUploadCompleter = >{}; /// Cancels [attachmentId] upload request. Throws exception if the request /// hasn't even started yet, Already completed or Already cancelled. @@ -382,12 +382,10 @@ class Channel { String messageId, Iterable attachmentIds, ) { - final message = [ + var message = [ ...state!.messages, ...state!.threads.values.expand((messages) => messages), - ].firstWhereOrNull( - (it) => it.id == messageId, - ); + ].firstWhereOrNull((it) => it.id == messageId); if (message == null) { throw const StreamChatError('Error, Message not found'); @@ -409,11 +407,15 @@ class Channel { client.logger.info('Found ${attachments.length} attachments'); void updateAttachment(Attachment attachment) { - final index = - message.attachments.indexWhere((it) => it.id == attachment.id); + final index = message!.attachments.indexWhere( + (it) => it.id == attachment.id, + ); if (index != -1) { - message.attachments[index] = attachment; - state?.addMessage(message); + final newAttachments = [...message!.attachments]..[index] = attachment; + final updatedMessage = message!.copyWith(attachments: newAttachments); + state?.addMessage(updatedMessage); + // updating original message for next iteration + message = message!.merge(updatedMessage); } } @@ -476,7 +478,7 @@ class Channel { _cancelableAttachmentUploadRequest.remove(it.id); }); })).whenComplete(() { - if (message.attachments.every((it) => it.uploadState.isSuccess)) { + if (message!.attachments.every((it) => it.uploadState.isSuccess)) { _messageAttachmentsUploadCompleter.remove(messageId)?.complete(message); } }); @@ -1076,33 +1078,7 @@ class Channel { } else { // remove the passed message if response does // not contain message - final oldIndex = state!.messages.indexWhere((m) => m.id == messageId); - - // remove regular message if present - if (oldIndex != -1) { - final oldMessage = state!.messages[oldIndex]; - state!._channelState = state!._channelState.copyWith( - messages: state!.messages..remove(oldMessage), - channel: state?._channelState.channel, - ); - } else { - // remove thread message if present - // also reduces total reply count - final oldMessage = state!.threads.values - .expand((messages) => messages) - .firstWhereOrNull((m) => m.id == messageId); - if (oldMessage?.parentId != null) { - final parentMessage = state!.messages.firstWhereOrNull( - (element) => element.id == oldMessage!.parentId, - ); - if (parentMessage != null) { - state!.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount! - 1)); - } - state!.updateThreadInfo(oldMessage!.parentId!, - state!.threads[oldMessage.parentId!]!..remove(oldMessage)); - } - } + state!.removeMessage(message); await _client.chatPersistenceClient?.deleteMessageById(messageId); } return res; @@ -1704,7 +1680,7 @@ class ChannelClientState { })); } - /// Add a message to this channel. + /// Add a [message] to this [channelState]. void addMessage(Message message) { if (message.parentId == null || message.showInChannel == true) { final newMessages = List.from(_channelState.messages); @@ -1735,6 +1711,35 @@ class ChannelClientState { } } + /// Remove a [message] from this [channelState]. + void removeMessage(Message message) { + final parentId = message.parentId; + // i.e. it's a thread message + // 1. Remove the thread message + // 2. Reduce total reply count of parent message + if (parentId != null) { + final allMessages = [...messages]; + final parentMessage = allMessages.firstWhereOrNull( + (it) => it.id == parentId, + ); + + // return if message not available in the memory + if (parentMessage == null) return; + final replyCount = parentMessage.replyCount; + // return if reply count is null or zero + if (replyCount == null || replyCount == 0) return; + + addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); + updateThreadInfo(parentId, threads[parentId]!..remove(message)); + } else { + // Remove regular message + final allMessages = [...messages]; + if (allMessages.remove(message)) { + _channelState = _channelState.copyWith(messages: allMessages); + } + } + } + void _listenReadEvents() { if (_channelState.channel?.config.readEvents == false) { return; @@ -1773,8 +1778,9 @@ class ChannelClientState { List get messages => _channelState.messages; /// Channel message list as a stream. - Stream> get messagesStream => - channelStateStream.map((cs) => cs.messages); + Stream> get messagesStream => channelStateStream + .map((cs) => cs.messages) + .distinct(const ListEquality().equals); /// Channel pinned message list. List get pinnedMessages => _channelState.pinnedMessages.toList(); @@ -1861,15 +1867,11 @@ class ChannelClientState { if (newThreads.containsKey(parentId)) { newThreads[parentId] = [ - ...newThreads[parentId] - ?.where( - (newMessage) => !messages.any((m) => m.id == newMessage.id)) - .toList() ?? - [], ...messages, - ]; - - newThreads[parentId]!.sort(_sortByCreatedAt); + ...newThreads[parentId]!.where( + (newMessage) => !messages.any((m) => m.id == newMessage.id), + ), + ]..sort(_sortByCreatedAt); } else { newThreads[parentId] = messages; } From 599c824fc08deb6a5f80258adf2bd3af84c30d0d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 17:06:11 +0530 Subject: [PATCH 114/148] fix(ui): fix early command check Signed-off-by: xsahil03x --- .../lib/src/message_input.dart | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 95322c10..73a211ff 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:collection/collection.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; @@ -878,24 +879,11 @@ class MessageInputState extends State { void _checkCommands(String s, BuildContext context) { if (s.startsWith('/')) { - final matchedCommandsList = StreamChannel.of(context) - .channel - .config - ?.commands - .where((element) => element.name == s.substring(1)) - .toList() ?? - []; - - if (matchedCommandsList.length == 1) { - _chosenCommand = matchedCommandsList[0]; - textEditingController.clear(); - - if (_showCommandsOverlay) { - setState(() { - _commandEnabled = true; - _showCommandsOverlay = false; - }); - } + final allCommands = StreamChannel.of(context).channel.config?.commands; + final command = + allCommands?.firstWhereOrNull((it) => it.name == s.substring(1)); + if (command != null) { + return _setCommand(command); } else if (!_showCommandsOverlay) { setState(() { _showCommandsOverlay = true; From c19b72c04229066fb2c53034bdbfa8b7bb79be59 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 17:13:56 +0530 Subject: [PATCH 115/148] Update packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart Co-authored-by: Salvatore Giordano --- packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart index d155ce4c..6068369a 100644 --- a/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart @@ -30,7 +30,7 @@ class UserMentionsOverlay extends StatefulWidget { 'Channel ${channel.cid} is not yet initialized', ), assert( - mentionAllAppUsers && client != null, + !mentionAllAppUsers || (mentionAllAppUsers && client != null), 'StreamChatClient is required in order to use mentionAllAppUsers', ), super(key: key); From a0c98aa851ac621388d75f15c626bcd0fc698797 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 17:16:56 +0530 Subject: [PATCH 116/148] ref(ui): remove initial data to fix flickering in mentions overlay. Signed-off-by: xsahil03x --- .../stream_chat_flutter/lib/src/user_mentions_overlay.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart index 6068369a..e3830231 100644 --- a/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart +++ b/packages/stream_chat_flutter/lib/src/user_mentions_overlay.dart @@ -67,7 +67,6 @@ class UserMentionsOverlay extends StatefulWidget { class _UserMentionsOverlayState extends State { late Future> userMentionsFuture; - late List initialMentions = membersAndWatchers; @override void initState() { @@ -102,8 +101,9 @@ class _UserMentionsOverlayState extends State { decoration: BoxDecoration(color: theme.colorTheme.barsBg), child: FutureBuilder>( future: userMentionsFuture, - initialData: initialMentions, builder: (context, snapshot) { + if (snapshot.hasError) return const Offstage(); + if (!snapshot.hasData) return const Offstage(); final users = snapshot.data!; return ListView.builder( padding: const EdgeInsets.all(0), From 7ad621f93db07d177ed2b3f7471082d7b031156f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 17:21:15 +0530 Subject: [PATCH 117/148] chore(ui): improve `MentionTile` deprecation message. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/src/mention_tile.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/mention_tile.dart b/packages/stream_chat_flutter/lib/src/mention_tile.dart index b900be65..c302ccb8 100644 --- a/packages/stream_chat_flutter/lib/src/mention_tile.dart +++ b/packages/stream_chat_flutter/lib/src/mention_tile.dart @@ -4,10 +4,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// This widget is used for showing user tiles for mentions /// Use [title], [subtitle], [leading], [trailing] for /// substituting widgets in respective positions -@Deprecated( - "'MentionTile' widget is deprecated and shouldn't be used. " - "Use 'UserMentionTile' instead", -) +@Deprecated('Use `UserMentionTile` instead. Will be removed in future release') class MentionTile extends StatelessWidget { /// Constructor for creating a [MentionTile] widget const MentionTile( From 0233c243f18687bf7bc52f678d216dcf933f64bf Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 29 Sep 2021 15:11:10 +0200 Subject: [PATCH 118/148] fix(llc): fixed expired cdn attachment links --- packages/stream_chat/CHANGELOG.md | 1 + packages/stream_chat/lib/src/client/channel.dart | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index fbac9f87..5a4b4d84 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -3,6 +3,7 @@ 🐞 Fixed - [[#710]](https://github.com/GetStream/stream-chat-flutter/issues/710) Fixed JWT requiring using `String` as id. +- Fixed expired CDN attachment links not updating correctly. ## 3.0.0 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index c2c40018..baac7da5 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1521,7 +1521,7 @@ class ChannelClientState { } } - void _checkExpiredAttachmentMessages(ChannelState channelState) { + void _checkExpiredAttachmentMessages(ChannelState channelState) async { final expiredAttachmentMessagesId = channelState.messages .where((m) => !_updatedMessagesIds.contains(m.id) && @@ -1532,20 +1532,24 @@ class ChannelClientState { return false; } final uri = Uri.parse(url); - if (uri.host != 'stream-io-cdn.com' || + if (!uri.host.endsWith('stream-io-cdn.com') || uri.queryParameters['Expires'] == null) { return false; } - final expiration = - DateTime.parse(uri.queryParameters['Expires']!); + final secondsFromEpoch = + int.parse(uri.queryParameters['Expires']!); + final expiration = DateTime.fromMillisecondsSinceEpoch( + secondsFromEpoch * 1000); return expiration.isBefore(DateTime.now()); }) == true) .map((e) => e.id) .toList(); + if (expiredAttachmentMessagesId.isNotEmpty == true) { - _channel.getMessagesById(expiredAttachmentMessagesId); + await _channel._initializedCompleter.future; _updatedMessagesIds.addAll(expiredAttachmentMessagesId); + _channel.getMessagesById(expiredAttachmentMessagesId); } } From bcf181bf213479b71fd24f43cc1b2e22c2b80ed3 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 18:54:44 +0530 Subject: [PATCH 119/148] fix(llc): fix tests Signed-off-by: xsahil03x --- .../test/src/client/channel_test.dart | 182 +++++++++++++++--- packages/stream_chat/test/src/matchers.dart | 59 ++++++ 2 files changed, 217 insertions(+), 24 deletions(-) diff --git a/packages/stream_chat/test/src/client/channel_test.dart b/packages/stream_chat/test/src/client/channel_test.dart index 08ebd091..6ec26d27 100644 --- a/packages/stream_chat/test/src/client/channel_test.dart +++ b/packages/stream_chat/test/src/client/channel_test.dart @@ -334,20 +334,87 @@ void main() { expectLater( // skipping first seed message list -> [] messages channel.state?.messagesStream.skip(1), - emitsInOrder([ + emitsInOrder( [ - isSameMessageAs( - message.copyWith(status: MessageSendingStatus.sending), - matchSendingStatus: true, - ), + // preparing attachments to upload + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sending, + attachments: [ + ...attachments.map((it) => it.copyWith( + uploadState: const UploadState.preparing())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // 0th attachment is successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sending, + attachments: [...attachments]..[0] = + attachments[0].copyWith( + uploadState: const UploadState.success(), + ), + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // 0th and 1st attachment is successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sending, + attachments: [...attachments] + ..[0] = attachments[0].copyWith( + uploadState: const UploadState.success(), + ) + ..[1] = attachments[1].copyWith( + uploadState: const UploadState.success(), + ), + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // all the attachments are successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sending, + attachments: [ + ...attachments.map((it) => + it.copyWith(uploadState: const UploadState.success())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sent, + attachments: [ + ...attachments.map((it) => + it.copyWith(uploadState: const UploadState.success())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], ], - [ - isSameMessageAs( - message.copyWith(status: MessageSendingStatus.sent), - matchSendingStatus: true, - ), - ], - ]), + ), ); final res = await channel.sendMessage(message); @@ -472,20 +539,87 @@ void main() { expectLater( // skipping first seed message list -> [] messages channel.state?.messagesStream.skip(1), - emitsInOrder([ + emitsInOrder( [ - isSameMessageAs( - message.copyWith(status: MessageSendingStatus.updating), - matchSendingStatus: true, - ), + // preparing attachments to upload + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.updating, + attachments: [ + ...attachments.map((it) => it.copyWith( + uploadState: const UploadState.preparing())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // 0th attachment is successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.updating, + attachments: [...attachments]..[0] = + attachments[0].copyWith( + uploadState: const UploadState.success(), + ), + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // 0th and 1st attachment is successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.updating, + attachments: [...attachments] + ..[0] = attachments[0].copyWith( + uploadState: const UploadState.success(), + ) + ..[1] = attachments[1].copyWith( + uploadState: const UploadState.success(), + ), + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + // all the attachments are successfully uploaded + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.updating, + attachments: [ + ...attachments.map((it) => + it.copyWith(uploadState: const UploadState.success())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], + [ + isSameMessageAs( + message.copyWith( + status: MessageSendingStatus.sent, + attachments: [ + ...attachments.map((it) => + it.copyWith(uploadState: const UploadState.success())) + ], + ), + matchSendingStatus: true, + matchAttachments: true, + matchAttachmentsUploadState: true, + ), + ], ], - [ - isSameMessageAs( - message.copyWith(status: MessageSendingStatus.sent), - matchSendingStatus: true, - ), - ], - ]), + ), ); final res = await channel.updateMessage(message); diff --git a/packages/stream_chat/test/src/matchers.dart b/packages/stream_chat/test/src/matchers.dart index 27cfa43d..e54b113d 100644 --- a/packages/stream_chat/test/src/matchers.dart +++ b/packages/stream_chat/test/src/matchers.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart' show MultipartFile; import 'package:stream_chat/src/client/channel.dart'; +import 'package:stream_chat/src/core/models/attachment.dart'; import 'package:stream_chat/src/core/models/channel_state.dart'; import 'package:stream_chat/src/core/models/event.dart'; import 'package:stream_chat/src/core/models/message.dart'; @@ -46,12 +47,16 @@ Matcher isSameMessageAs( bool matchText = false, bool matchReactions = false, bool matchSendingStatus = false, + bool matchAttachments = false, + bool matchAttachmentsUploadState = false, }) => _IsSameMessageAs( targetMessage: targetMessage, matchText: matchText, matchReactions: matchReactions, matchSendingStatus: matchSendingStatus, + matchAttachments: matchAttachments, + matchAttachmentsUploadState: matchAttachmentsUploadState, ); class _IsSameMessageAs extends Matcher { @@ -60,12 +65,16 @@ class _IsSameMessageAs extends Matcher { this.matchText = false, this.matchReactions = false, this.matchSendingStatus = false, + this.matchAttachments = false, + this.matchAttachmentsUploadState = false, }); final Message targetMessage; final bool matchText; final bool matchReactions; final bool matchSendingStatus; + final bool matchAttachments; + final bool matchAttachmentsUploadState; @override Description describe(Description description) => @@ -96,6 +105,56 @@ class _IsSameMessageAs extends Matcher { ?.map((it) => '${it.type}-${it.messageId}') .toList()); } + if (matchAttachments) { + bool matchAttachments() { + final attachments = message.attachments; + final targetAttachments = targetMessage.attachments; + if (identical(attachments, targetAttachments)) return true; + final length = attachments.length; + if (length != targetAttachments.length) return false; + for (var i = 0; i < length; i++) { + if (!isSameAttachmentAs( + attachments[i], + matchUploadState: matchAttachmentsUploadState, + ).matches(targetAttachments[i], matchState)) return false; + } + return true; + } + + matches &= matchAttachments(); + } + return matches; + } +} + +Matcher isSameAttachmentAs( + Attachment targetAttachment, { + bool matchUploadState = false, +}) => + _IsSameAttachmentAs( + targetAttachment: targetAttachment, + matchUploadState: matchUploadState, + ); + +class _IsSameAttachmentAs extends Matcher { + const _IsSameAttachmentAs({ + required this.targetAttachment, + this.matchUploadState = false, + }); + + final Attachment targetAttachment; + final bool matchUploadState; + + @override + Description describe(Description description) => + description.add('is same attachment as $targetAttachment'); + + @override + bool matches(covariant Attachment attachment, Map matchState) { + var matches = attachment.id == targetAttachment.id; + if (matchUploadState) { + matches &= attachment.uploadState == targetAttachment.uploadState; + } return matches; } } From 2bf88bfe0db452b93342c41938ff7059245e849f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 19:59:46 +0530 Subject: [PATCH 120/148] fix(ui): fix tests Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/src/message_input.dart | 3 ++- .../test/src/message_action_modal_test.dart | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 97d8b2ac..c49158cd 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -16,12 +16,12 @@ import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; -import 'package:stream_chat_flutter/src/user_mentions_overlay.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/overlays.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; +import 'package:stream_chat_flutter/src/user_mentions_overlay.dart'; import 'package:stream_chat_flutter/src/video_service.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -1880,6 +1880,7 @@ class MessageInputState extends State { void dispose() { textEditingController.dispose(); _stopSlowMode(); + _onChangedDebounced.cancel(); super.dispose(); } diff --git a/packages/stream_chat_flutter/test/src/message_action_modal_test.dart b/packages/stream_chat_flutter/test/src/message_action_modal_test.dart index 38518b19..9210ac49 100644 --- a/packages/stream_chat_flutter/test/src/message_action_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_action_modal_test.dart @@ -263,9 +263,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); final channel = MockChannel(); + final channelState = MockChannelState(); when(() => client.state).thenReturn(clientState); when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.state).thenReturn(channelState); final themeData = ThemeData(); final streamTheme = StreamChatThemeData.fromTheme(themeData); From aa6fc06402e24821981320d90b2bdd942d64bf26 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 29 Sep 2021 16:53:29 +0200 Subject: [PATCH 121/148] fix analysis --- packages/stream_chat_flutter/lib/src/message_input.dart | 2 +- packages/stream_chat_flutter/test/src/extension_test.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index c49158cd..d8b22193 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -57,7 +57,7 @@ typedef AttachmentThumbnailBuilder = Widget Function( ); /// Builder function for building a mention tile -/// Use [MentionTile] for the default implementation +/// Use [UserMentionTile] for the default implementation typedef MentionTileBuilder = Widget Function( BuildContext context, Member member, diff --git a/packages/stream_chat_flutter/test/src/extension_test.dart b/packages/stream_chat_flutter/test/src/extension_test.dart index 9d8d92e4..696a19fc 100644 --- a/packages/stream_chat_flutter/test/src/extension_test.dart +++ b/packages/stream_chat_flutter/test/src/extension_test.dart @@ -9,7 +9,6 @@ void main() { final thierry = User(id: 'thierry', name: 'Thierry'); final users = [tommaso, thierry]; - final a = users.search('Tom'); expect(users.search('Tom'), [tommaso]); expect(users.search('Thier'), [thierry]); }); From b831ba497333662a088bb8750e6beacfc91a06f1 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 21:15:55 +0530 Subject: [PATCH 122/148] refactor(ui): deprecate `MentionTileBuilder`. Signed-off-by: xsahil03x --- .../lib/src/message_input.dart | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index c49158cd..1aaa6d34 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -56,13 +56,25 @@ typedef AttachmentThumbnailBuilder = Widget Function( Attachment, ); -/// Builder function for building a mention tile -/// Use [MentionTile] for the default implementation +/// Builder function for building a mention tile. +/// +/// Use [MentionTile] for the default implementation. +@Deprecated( + 'Use `UserMentionTileBuilder` instead. Will be removed in future release', +) typedef MentionTileBuilder = Widget Function( BuildContext context, Member member, ); +/// Builder function for building a user mention tile. +/// +/// Use [UserMentionTile] for the default implementation. +typedef UserMentionTileBuilder = Widget Function( + BuildContext context, + User user, +); + /// Widget builder for action button. /// /// [defaultActionButton] is the default [IconButton] configuration, @@ -184,7 +196,9 @@ class MessageInput extends StatefulWidget { this.idleSendButton, this.activeSendButton, this.showCommandsButton = true, - this.mentionsTileBuilder, + @Deprecated('''Use `userMentionsTileBuilder` instead. Will be removed in future release''') + this.mentionsTileBuilder, + this.userMentionsTileBuilder, this.maxAttachmentSize = _kDefaultMaxAttachmentSize, this.compressedVideoQuality = VideoQuality.DefaultQuality, this.compressedVideoFrameRate = 30, @@ -279,9 +293,12 @@ class MessageInput extends StatefulWidget { /// Send button widget in an active state final Widget? activeSendButton; - /// Customize the tile for the mentions overlay + /// Customize the tile for the mentions overlay. final MentionTileBuilder? mentionsTileBuilder; + /// Customize the tile for the mentions overlay. + final UserMentionTileBuilder? userMentionsTileBuilder; + /// A callback for error reporting final ErrorListener? onError; @@ -1153,12 +1170,26 @@ class MessageInputState extends State { // ignore: cast_nullable_to_non_nullable final renderObject = context.findRenderObject() as RenderBox; + var tileBuilder = widget.userMentionsTileBuilder; + if (tileBuilder == null && widget.mentionsTileBuilder != null) { + tileBuilder = (context, user) { + final member = Member( + user: user, + userId: user.id, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + ); + return widget.mentionsTileBuilder!.call(context, member); + }; + } + return UserMentionsOverlay( query: query, mentionAllAppUsers: widget.mentionAllAppUsers, client: StreamChat.of(context).client, channel: StreamChannel.of(context).channel, size: Size(renderObject.size.width - 16, 400), + mentionsTileBuilder: tileBuilder, onMentionUserTap: (user) { _mentionedUsers.add(user); splits[splits.length - 1] = user.name; From ade9bc381060a1bf798289f2b365815f6bca5673 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 29 Sep 2021 21:25:59 +0530 Subject: [PATCH 123/148] chore(ui): fix analyzer warning. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/src/message_input.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 4397738f..e95d3778 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -57,11 +57,6 @@ typedef AttachmentThumbnailBuilder = Widget Function( ); /// Builder function for building a mention tile. -/// -/// Use [UserMentionTile] for the default implementation. -@Deprecated( - 'Use `UserMentionTileBuilder` instead. Will be removed in future release', -) typedef MentionTileBuilder = Widget Function( BuildContext context, Member member, From 09f6a9cd42c12b9aa79f796eedfb938ab18ec0d0 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 30 Sep 2021 10:14:16 +0200 Subject: [PATCH 124/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 32f1b059..35ce4094 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,16 @@ +## Upcoming + +✅ Added + +- Added `MessageInput.customOverlays` property to add custom overlays to the message input. +- Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input. +- The `MessageInput` now supports local search for channels with less than 100 members. + +⚠️ Deprecated + +- `MessageInput.mentionsTileBuilder` is now deprecated in favor of `MessageInput.userMentionsTileBuilder`. +- `MentionTile` is now deprecated in favor of `UserMentionsTile`. + ## 3.0.0 - Updated `stream_chat_flutter_core` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). From a7175b0b2c23f9e64f1ab461a77dd2c3a37bfbf8 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 30 Sep 2021 16:12:06 +0530 Subject: [PATCH 125/148] Update packages/stream_chat_flutter/lib/src/message_input.dart Co-authored-by: Deven Joshi --- packages/stream_chat_flutter/lib/src/message_input.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index e95d3778..82087fab 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -1174,7 +1174,7 @@ class MessageInputState extends State { createdAt: user.createdAt, updatedAt: user.updatedAt, ); - return widget.mentionsTileBuilder!.call(context, member); + return widget.mentionsTileBuilder!(context, member); }; } From 4199bc7b6db9345e2512b6689ac150414add1752 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 30 Sep 2021 16:38:56 +0530 Subject: [PATCH 126/148] removed filteroperator --- packages/stream_chat/lib/src/core/models/filter.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/filter.dart b/packages/stream_chat/lib/src/core/models/filter.dart index e668c417..108efcb2 100644 --- a/packages/stream_chat/lib/src/core/models/filter.dart +++ b/packages/stream_chat/lib/src/core/models/filter.dart @@ -43,9 +43,6 @@ enum FilterOperator { /// Matches values that exist/don't exist based on the specified boolean value. exists, - /// Matches values that don't exist. - notExists, - /// Matches all the values specified in an array. and, From 007e6783de3ec6bbf5df4e3da97346a1bbe274e2 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 1 Oct 2021 10:20:08 +0200 Subject: [PATCH 127/148] chore(repo): update flutter version in workflow --- .github/workflows/stream_flutter_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 2e81d732..28063c18 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -2,7 +2,7 @@ name: stream_flutter_workflow env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - flutter_version: "2.5.0" + flutter_version: "2.5.1" on: pull_request: From 0983a25aca58da01c2550422a9b71708e2a4afdc Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 1 Oct 2021 15:45:01 +0530 Subject: [PATCH 128/148] chore(llc): fix test failing on latest version --- packages/stream_chat/pubspec.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index dd394c1e..81657659 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -30,4 +30,6 @@ dev_dependencies: freezed: ^0.14.1+3 json_serializable: ^4.1.0 mocktail: ^0.1.1 - test: ^1.17.7 \ No newline at end of file + # Remove fixed version once the below issue is fixed + # https://github.com/flutter/flutter/issues/91059 + test: 1.17.12 From dd6c8386991f03282c9f4caa7c4512a2179dc464 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 1 Oct 2021 16:18:49 +0530 Subject: [PATCH 129/148] chore(persistence): switch `test` to `flutter_test` Signed-off-by: xsahil03x --- packages/stream_chat_persistence/pubspec.yaml | 3 ++- .../test/src/converter/list_coverter_test.dart | 2 +- .../test/src/converter/map_converter_test.dart | 2 +- .../src/converter/message_sending_status_converter_test.dart | 2 +- .../stream_chat_persistence/test/src/dao/channel_dao_test.dart | 2 +- .../test/src/dao/channel_query_dao_test.dart | 2 +- .../test/src/dao/connection_event_dao_test.dart | 2 +- .../stream_chat_persistence/test/src/dao/member_dao_test.dart | 2 +- .../stream_chat_persistence/test/src/dao/message_dao_test.dart | 2 +- .../test/src/dao/pinned_message_dao_test.dart | 2 +- .../test/src/dao/pinned_message_reaction_dao_test.dart | 2 +- .../test/src/dao/reaction_dao_test.dart | 2 +- .../stream_chat_persistence/test/src/dao/read_dao_test.dart | 2 +- .../stream_chat_persistence/test/src/dao/user_dao_test.dart | 2 +- .../test/src/db/moor_chat_database_test.dart | 2 +- .../test/src/mapper/channel_mapper_test.dart | 2 +- .../test/src/mapper/event_mapper_test.dart | 2 +- .../test/src/mapper/member_mapper_test.dart | 2 +- .../test/src/mapper/message_mapper_test.dart | 2 +- .../test/src/mapper/pinned_message_mapper_test.dart | 2 +- .../test/src/mapper/pinned_message_reaction_mapper_test.dart | 2 +- .../test/src/mapper/reaction_mapper_test.dart | 2 +- .../test/src/mapper/read_mapper_test.dart | 2 +- .../test/src/mapper/user_mapper_test.dart | 2 +- .../stream_chat_persistence/test/src/utils/date_matcher.dart | 2 +- .../test/stream_chat_persistence_client_test.dart | 3 ++- 26 files changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index 4f3ab8e9..f6409fa2 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -22,7 +22,8 @@ dependencies: dev_dependencies: build_runner: ^2.0.1 + flutter_test: + sdk: flutter mocktail: ^0.1.1 moor_generator: ^4.2.1 pedantic: ^1.11.0 - test: ^1.17.7 diff --git a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart index a5fe7a49..9cee72b0 100644 --- a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_persistence/src/converter/list_converter.dart'; -import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart index 6e14140b..c7754892 100644 --- a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_persistence/src/converter/map_converter.dart'; -import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart index c53d8fb4..840f0469 100644 --- a/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/message_sending_status_converter_test.dart @@ -1,6 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/converter/message_sending_status_converter.dart'; -import 'package:test/test.dart'; void main() { group('mapToDart', () { diff --git a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart index c0a00100..ff75c13c 100644 --- a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/channel_dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart index 81e629b6..2ea1e73a 100644 --- a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/channel_query_dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart index 40c385ba..6b576169 100644 --- a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/connection_event_dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart index 4ca3880b..33531ca9 100644 --- a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart index d069f9f9..31546ecf 100644 --- a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart index f8fae456..45f2ed0a 100644 --- a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart index aa92c5cc..5c451ec1 100644 --- a/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/pinned_message_reaction_dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart index 21570d4e..8109e75e 100644 --- a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/reaction_dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart index ca556632..1cebd1eb 100644 --- a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart index 1fd4eafd..ab88881a 100644 --- a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; import '../../stream_chat_persistence_client_test.dart'; diff --git a/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart b/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart index 101b44ed..eb1b6933 100644 --- a/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart +++ b/packages/stream_chat_persistence/test/src/db/moor_chat_database_test.dart @@ -1,8 +1,8 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:moor/ffi.dart'; import 'package:moor/isolate.dart'; import 'package:moor/moor.dart' hide isNotNull; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; -import 'package:test/test.dart'; DatabaseConnection _backgroundConnection() => DatabaseConnection.fromExecutor(VmDatabase.memory()); diff --git a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart index b45f46b4..2c87097b 100644 --- a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/channel_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart index 3182162b..a7c72d0d 100644 --- a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/event_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart index b3f834ef..19f299b3 100644 --- a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/member_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart index 52a58a1a..d26d905b 100644 --- a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/message_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart index 40c4ff6c..9f258b76 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/pinned_message_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart index b814b7dc..f031d941 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/pinned_message_reaction_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart index e211f685..831d6068 100644 --- a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/reaction_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart index eb296a75..f152e476 100644 --- a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart @@ -1,7 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/read_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart index 55dbbff4..7b6ef2e8 100644 --- a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart @@ -1,9 +1,9 @@ import 'dart:math' as math; +import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; -import 'package:test/test.dart'; import '../utils/date_matcher.dart'; diff --git a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart b/packages/stream_chat_persistence/test/src/utils/date_matcher.dart index dec68db0..5f55f46a 100644 --- a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart +++ b/packages/stream_chat_persistence/test/src/utils/date_matcher.dart @@ -1,4 +1,4 @@ -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; Matcher isSameDateAs(DateTime targetDate) => _IsSameDateAs(targetDate: targetDate); diff --git a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart index 25e926b1..5c2b6bff 100644 --- a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart +++ b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart @@ -1,9 +1,10 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:moor/ffi.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; -import 'package:test/test.dart'; + import 'mock_chat_database.dart'; import 'src/utils/date_matcher.dart'; From 9898e88081b3aa4ba5af71fd10d8e8b53f9a7603 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 1 Oct 2021 13:10:29 +0200 Subject: [PATCH 130/148] fix(ui): fix floating date stream subscription --- packages/stream_chat_flutter/lib/src/message_list_view.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 e67f00dd..42285475 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -293,7 +293,6 @@ class _MessageListViewState extends State { void Function(Message)? _onThreadTap; bool _showScrollToBottom = false; late final ItemPositionsListener _itemPositionListener; - late final Stream> _itemPositionStream; int? _messageListLength; StreamChannelState? streamChannel; late StreamChatThemeData _streamTheme; @@ -665,7 +664,8 @@ class _MessageListViewState extends State { right: 0, child: BetterStreamBuilder>( initialData: _itemPositionListener.itemPositions.value, - stream: _itemPositionStream, + stream: _valueListenableToStreamAdapter( + _itemPositionListener.itemPositions), comparator: (a, b) { if (a == null || b == null) { return false; @@ -1194,8 +1194,6 @@ class _MessageListViewState extends State { _scrollController = widget.scrollController ?? ItemScrollController(); _itemPositionListener = widget.itemPositionListener ?? ItemPositionsListener.create(); - _itemPositionStream = - _valueListenableToStreamAdapter(_itemPositionListener.itemPositions); _getOnThreadTap(); super.initState(); From 3139b9d51e6946818c3105f28b6717df1ef8fd96 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 1 Oct 2021 13:11:22 +0200 Subject: [PATCH 131/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index e68c7b9b..9e4f2744 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -3,6 +3,7 @@ 🐞 Fixed -[[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. +- Fixed floating date stream subscription causing "Bad state: stream has already been listened.” error. ✅ Added From c4c651bc1ae96cbb5dea0a418df6dc6927464a4f Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Mon, 4 Oct 2021 16:59:56 +0530 Subject: [PATCH 132/148] capitalize extension fix --- packages/stream_chat_flutter/lib/src/extension.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/extension.dart b/packages/stream_chat_flutter/lib/src/extension.dart index 9551a40d..0072a811 100644 --- a/packages/stream_chat_flutter/lib/src/extension.dart +++ b/packages/stream_chat_flutter/lib/src/extension.dart @@ -12,7 +12,7 @@ final _emojiChars = Emoji.chars(); extension StringExtension on String { /// Returns the capitalized string String capitalize() => - '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; + isNotEmpty ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : ''; /// Returns whether the string contains only emoji's or not. /// From 195b95e16e8603b730e331ffc1526c82841907e3 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 4 Oct 2021 13:36:42 +0200 Subject: [PATCH 133/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 9e4f2744..d5e21c54 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -4,6 +4,7 @@ -[[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. - Fixed floating date stream subscription causing "Bad state: stream has already been listened.” error. +- Fixed `String` capitalize extension not working on empty strings. ✅ Added From 801a488c425347d1e2d438b23016f9c3e3342ce5 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 4 Oct 2021 19:38:58 +0530 Subject: [PATCH 134/148] feat: copy linkBg theme property in message theme. Signed-off-by: xsahil03x --- .../src/{ => attachment}/url_attachment.dart | 6 +++++- .../lib/src/message_widget.dart | 5 +++-- .../lib/src/quoted_message_widget.dart | 2 +- .../lib/src/stream_chat_theme.dart | 2 ++ .../lib/src/theme/message_theme.dart | 18 +++++++++++++++--- .../test/src/theme/message_theme_test.dart | 2 ++ 6 files changed, 28 insertions(+), 7 deletions(-) rename packages/stream_chat_flutter/lib/src/{ => attachment}/url_attachment.dart (94%) diff --git a/packages/stream_chat_flutter/lib/src/url_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart similarity index 94% rename from packages/stream_chat_flutter/lib/src/url_attachment.dart rename to packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart index b940f227..d56bcd6a 100644 --- a/packages/stream_chat_flutter/lib/src/url_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart @@ -10,6 +10,7 @@ class UrlAttachment extends StatelessWidget { Key? key, required this.urlAttachment, required this.hostDisplayName, + required this.messageTheme, this.textPadding = const EdgeInsets.symmetric( horizontal: 16, vertical: 8, @@ -25,6 +26,9 @@ class UrlAttachment extends StatelessWidget { /// Padding for text final EdgeInsets textPadding; + /// [MessageThemeData] for showing image title + final MessageThemeData messageTheme; + @override Widget build(BuildContext context) { final chatThemeData = StreamChatTheme.of(context); @@ -58,7 +62,7 @@ class UrlAttachment extends StatelessWidget { borderRadius: const BorderRadius.only( topRight: Radius.circular(16), ), - color: chatThemeData.colorTheme.linkBg, + color: messageTheme.linkBackgroundColor, ), child: Padding( padding: const EdgeInsets.only( diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 6731e15a..4422cdf9 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:stream_chat_flutter/src/attachment/url_attachment.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/image_group.dart'; import 'package:stream_chat_flutter/src/message_action.dart'; @@ -15,7 +16,6 @@ import 'package:stream_chat_flutter/src/message_reactions_modal.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/theme/themes.dart'; -import 'package:stream_chat_flutter/src/url_attachment.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Widget builder for building attachments @@ -1002,6 +1002,7 @@ class _MessageWidgetState extends State urlAttachment: urlAttachment, hostDisplayName: hostDisplayName, textPadding: widget.textPadding, + messageTheme: widget.messageTheme, ); } @@ -1339,7 +1340,7 @@ class _MessageWidgetState extends State } if (hasUrlAttachments) { - return _streamChatTheme.colorTheme.linkBg; + return widget.messageTheme.linkBackgroundColor; } if (isOnlyEmoji) { diff --git a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart index 01068abd..f1dc9711 100644 --- a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart @@ -281,7 +281,7 @@ class QuotedMessageWidget extends StatelessWidget { Color? _getBackgroundColor(BuildContext context) { if (_containsLinkAttachment) { - return StreamChatTheme.of(context).colorTheme.linkBg; + return messageTheme.linkBackgroundColor; } return messageTheme.messageBackgroundColor; } diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 0e50f132..3fb13238 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -223,6 +223,7 @@ class StreamChatThemeData { messageLinksStyle: TextStyle( color: accentColor, ), + linkBackgroundColor: colorTheme.linkBg, ), otherMessageTheme: MessageThemeData( reactionsBackgroundColor: colorTheme.disabled, @@ -246,6 +247,7 @@ class StreamChatThemeData { width: 32, ), ), + linkBackgroundColor: colorTheme.linkBg, ), messageInputTheme: MessageInputThemeData( borderRadius: BorderRadius.circular(20), diff --git a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart index 023a2917..7bed2ceb 100644 --- a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart @@ -17,6 +17,7 @@ class MessageThemeData with Diagnosticable { this.reactionsMaskColor, this.avatarTheme, this.createdAtStyle, + this.linkBackgroundColor, }); /// Text style for message text @@ -52,6 +53,9 @@ class MessageThemeData with Diagnosticable { /// Theme of the avatar final AvatarThemeData? avatarTheme; + /// Background color for messages with url attachments. + final Color? linkBackgroundColor; + /// Copy with a theme MessageThemeData copyWith({ TextStyle? messageTextStyle, @@ -65,6 +69,7 @@ class MessageThemeData with Diagnosticable { Color? reactionsBackgroundColor, Color? reactionsBorderColor, Color? reactionsMaskColor, + Color? linkBackgroundColor, }) => MessageThemeData( messageTextStyle: messageTextStyle ?? this.messageTextStyle, @@ -80,6 +85,7 @@ class MessageThemeData with Diagnosticable { reactionsBackgroundColor ?? this.reactionsBackgroundColor, reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, + linkBackgroundColor: linkBackgroundColor ?? this.linkBackgroundColor, ); /// Linearly interpolate from one [MessageThemeData] to another. @@ -105,6 +111,8 @@ class MessageThemeData with Diagnosticable { reactionsMaskColor: Color.lerp(a.reactionsMaskColor, b.reactionsMaskColor, t), repliesStyle: TextStyle.lerp(a.repliesStyle, b.repliesStyle, t), + linkBackgroundColor: + Color.lerp(a.linkBackgroundColor, b.linkBackgroundColor, t), ); /// Merge with a theme @@ -127,6 +135,7 @@ class MessageThemeData with Diagnosticable { reactionsBackgroundColor: other.reactionsBackgroundColor, reactionsBorderColor: other.reactionsBorderColor, reactionsMaskColor: other.reactionsMaskColor, + linkBackgroundColor: other.linkBackgroundColor, ); } @@ -145,7 +154,8 @@ class MessageThemeData with Diagnosticable { reactionsBackgroundColor == other.reactionsBackgroundColor && reactionsBorderColor == other.reactionsBorderColor && reactionsMaskColor == other.reactionsMaskColor && - avatarTheme == other.avatarTheme; + avatarTheme == other.avatarTheme && + linkBackgroundColor == other.linkBackgroundColor; @override int get hashCode => @@ -159,7 +169,8 @@ class MessageThemeData with Diagnosticable { reactionsBackgroundColor.hashCode ^ reactionsBorderColor.hashCode ^ reactionsMaskColor.hashCode ^ - avatarTheme.hashCode; + avatarTheme.hashCode ^ + linkBackgroundColor.hashCode; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -175,6 +186,7 @@ class MessageThemeData with Diagnosticable { ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) ..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor)) ..add(ColorProperty('reactionsBorderColor', reactionsBorderColor)) - ..add(ColorProperty('reactionsMaskColor', reactionsMaskColor)); + ..add(ColorProperty('reactionsMaskColor', reactionsMaskColor)) + ..add(ColorProperty('linkBackgroundColor', linkBackgroundColor)); } } diff --git a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart index 5908d864..502be84a 100644 --- a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart +++ b/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart @@ -59,6 +59,7 @@ final _messageThemeControl = MessageThemeData( messageLinksStyle: TextStyle( color: ColorTheme.light().accentPrimary, ), + linkBackgroundColor: ColorTheme.light().linkBg, ); final _messageThemeControlDark = MessageThemeData( @@ -87,4 +88,5 @@ final _messageThemeControlDark = MessageThemeData( messageLinksStyle: TextStyle( color: ColorTheme.dark().accentPrimary, ), + linkBackgroundColor: ColorTheme.dark().linkBg, ); From fdca20e1abc797bf173b5a627e1e5fc3c3a75f53 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 4 Oct 2021 19:41:16 +0530 Subject: [PATCH 135/148] chore(ui): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index d5e21c54..01cb56a9 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -11,6 +11,7 @@ - Added `MessageInput.customOverlays` property to add custom overlays to the message input. - Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input. - The `MessageInput` now supports local search for channels with less than 100 members. +- Added new `linkBackgroundColor` in `MessageTheme` for setting background colors of link attachments. ⚠️ Deprecated From 628a7d946891ad654af7b671e777234a4cb0c5d8 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 4 Oct 2021 20:16:50 +0530 Subject: [PATCH 136/148] feat(ui): add `paginationLoadingIndicatorBuilder` in `MessageListView`. Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 1 + .../lib/src/message_list_view.dart | 36 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index d5e21c54..3461f3ed 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -11,6 +11,7 @@ - Added `MessageInput.customOverlays` property to add custom overlays to the message input. - Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input. - The `MessageInput` now supports local search for channels with less than 100 members. +- Added `MessageListView.paginationLoadingIndicatorBuilder` to override the default loading indicator shown while paginating the message list. ⚠️ Deprecated 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 42285475..56c17270 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -169,6 +169,7 @@ class MessageListView extends StatefulWidget { this.messageListController, this.reverse = true, this.paginationLimit = 20, + this.paginationLoadingIndicatorBuilder, }) : super(key: key); /// Function used to build a custom message widget @@ -284,6 +285,9 @@ class MessageListView extends StatefulWidget { /// Use [ChannelListController.paginateData] pagination. final MessageListController? messageListController; + /// Builder used to build the loading indicator shown while paginating. + final WidgetBuilder? paginationLoadingIndicatorBuilder; + @override _MessageListViewState createState() => _MessageListViewState(); } @@ -572,17 +576,22 @@ class _MessageListViewState extends State { } } + final indicatorBuilder = + widget.paginationLoadingIndicatorBuilder; + if (i == itemCount - 3) { - return _buildLoadingIndicator( + return _loadingIndicator( streamChannel!, QueryDirection.top, + indicatorBuilder: indicatorBuilder, ); } if (i == 1) { - return _buildLoadingIndicator( + return _loadingIndicator( streamChannel!, QueryDirection.bottom, + indicatorBuilder: indicatorBuilder, ); } @@ -816,15 +825,17 @@ class _MessageListViewState extends State { }, ); - Widget _buildLoadingIndicator( + Widget _loadingIndicator( StreamChannelState streamChannel, - QueryDirection direction, - ) => + QueryDirection direction, { + WidgetBuilder? indicatorBuilder, + }) => _LoadingIndicator( direction: direction, streamTheme: _streamTheme, streamChannel: streamChannel, isThreadConversation: _isThreadConversation, + indicatorBuilder: indicatorBuilder, ); Widget _buildBottomMessage( @@ -1290,12 +1301,14 @@ class _LoadingIndicator extends StatelessWidget { required this.isThreadConversation, required this.direction, required this.streamChannel, + this.indicatorBuilder, }) : super(key: key); final StreamChatThemeData streamTheme; final bool isThreadConversation; final QueryDirection direction; final StreamChannelState streamChannel; + final WidgetBuilder? indicatorBuilder; @override Widget build(BuildContext context) { @@ -1314,12 +1327,13 @@ class _LoadingIndicator extends StatelessWidget { ), builder: (context, data) { if (!data) return const Offstage(); - return const Center( - child: Padding( - padding: EdgeInsets.all(8), - child: CircularProgressIndicator(), - ), - ); + return indicatorBuilder?.call(context) ?? + const Center( + child: Padding( + padding: EdgeInsets.all(8), + child: CircularProgressIndicator(), + ), + ); }, ); } From ef3cbe3833064a772aa49962db2cc1f8ba84134b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 09:51:05 +0200 Subject: [PATCH 137/148] chore(ui): update dependencies --- packages/stream_chat_flutter/lib/src/message_input.dart | 3 +++ packages/stream_chat_flutter/pubspec.yaml | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 82087fab..fed1cc4e 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -384,6 +384,9 @@ class MessageInputState extends State { Timer? _slowModeTimer; void _startSlowMode() { + if (!mounted) { + return; + } final channel = StreamChannel.of(context).channel; final cooldownStartedAt = channel.cooldownStartedAt; if (cooldownStartedAt != null) { diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 69cee1e9..f7236c74 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: diacritic: ^0.1.3 dio: ^4.0.0 ezanimation: ^0.5.0 - file_picker: ^3.0.1 + file_picker: ^4.1.3 flutter: sdk: flutter flutter_markdown: ^0.6.1 @@ -25,7 +25,7 @@ dependencies: flutter_slidable: ^0.6.0 flutter_svg: ^0.22.0 http_parser: ^4.0.0 - image_gallery_saver: ^1.6.9 + image_gallery_saver: ^1.7.0 image_picker: ^0.8.2 jiffy: ^4.1.0 lottie: ^1.0.1 @@ -43,7 +43,7 @@ dependencies: url_launcher: ^6.0.3 video_compress: ^3.0.0 video_player: ^2.1.0 - video_thumbnail: ^0.3.3 + video_thumbnail: ^0.4.3 visibility_detector: ^0.2.0 flutter: @@ -57,7 +57,7 @@ flutter: dev_dependencies: flutter_test: sdk: flutter - golden_toolkit: ^0.9.0 + golden_toolkit: ^0.10.0 mocktail: ^0.1.2 path: ^1.8.0 pedantic: ^1.11.0 From ba28d9dbec7b4a6d21bb7ecb5b854305e0dfff71 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 10:47:28 +0200 Subject: [PATCH 138/148] chore(llc): update dependencies --- packages/stream_chat/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 81657659..8141577b 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -28,8 +28,8 @@ dependencies: dev_dependencies: build_runner: ^2.0.1 freezed: ^0.14.1+3 - json_serializable: ^4.1.0 + json_serializable: ^5.0.2 mocktail: ^0.1.1 # Remove fixed version once the below issue is fixed # https://github.com/flutter/flutter/issues/91059 - test: 1.17.12 + test: ^1.18.2 From d808276fe35133761a5d639c58942f5976d30b1e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 10:53:00 +0200 Subject: [PATCH 139/148] chore(llc,core,ui): update changelog and pubspecs --- packages/stream_chat/CHANGELOG.md | 2 +- packages/stream_chat/lib/version.dart | 2 +- packages/stream_chat/pubspec.yaml | 2 +- packages/stream_chat_flutter/CHANGELOG.md | 4 +++- packages/stream_chat_flutter/pubspec.yaml | 4 ++-- packages/stream_chat_flutter_core/CHANGELOG.md | 4 ++++ packages/stream_chat_flutter_core/pubspec.yaml | 4 ++-- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index dae7b360..a9a8ed10 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 3.1.0 ✅ Added diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 4eda59c0..a82ba271 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '3.0.0'; +const PACKAGE_VERSION = '3.1.0'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 8141577b..e09e8271 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 3.0.0 +version: 3.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 29684cad..4c857168 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,4 +1,6 @@ -## Upcoming +## 3.1.0 + +- Updated `stream_chat_flutter_core` dependency to [`3.1.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). 🐞 Fixed diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index f7236c74..0b190ee9 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 3.0.0 +version: 3.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -37,7 +37,7 @@ dependencies: scrollable_positioned_list: ^0.2.0-nullsafety.0 share_plus: ^2.0.3 shimmer: ^2.0.0 - stream_chat_flutter_core: ^3.0.0 + stream_chat_flutter_core: ^3.1.0 substring_highlight: ^1.0.26 synchronized: ^3.0.0 url_launcher: ^6.0.3 diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index cc1394a1..897f8984 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.0 + +- Updated `stream_chat` dependency to [`3.1.0`](https://pub.dev/packages/stream_chat/changelog). + ## 3.0.0 - Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index e5520921..08db72e4 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 3.0.0 +version: 3.1.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -16,7 +16,7 @@ dependencies: sdk: flutter meta: ^1.3.0 rxdart: ^0.27.0 - stream_chat: ^3.0.0 + stream_chat: ^3.1.0 dev_dependencies: fake_async: ^1.2.0 From 3adc75c265d38cb7a0d8e49b144f90c1071c19a9 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 13:50:42 +0200 Subject: [PATCH 140/148] fix(llc): regenerate code and remove default values where possible --- .../lib/src/core/api/requests.dart | 2 +- .../lib/src/core/api/requests.g.dart | 34 +- .../lib/src/core/api/responses.g.dart | 461 +++++++++--------- .../lib/src/core/models/action.dart | 1 - .../lib/src/core/models/action.g.dart | 16 +- .../lib/src/core/models/attachment.dart | 5 +- .../lib/src/core/models/attachment.g.dart | 64 ++- .../core/models/attachment_file.freezed.dart | 128 ++++- .../src/core/models/attachment_file.g.dart | 49 +- .../lib/src/core/models/channel_config.dart | 13 - .../lib/src/core/models/channel_config.g.dart | 51 +- .../lib/src/core/models/channel_model.dart | 11 +- .../lib/src/core/models/channel_model.g.dart | 58 ++- .../lib/src/core/models/channel_state.dart | 5 - .../lib/src/core/models/channel_state.g.dart | 54 +- .../lib/src/core/models/command.g.dart | 12 +- .../lib/src/core/models/device.g.dart | 10 +- .../lib/src/core/models/event.dart | 1 - .../lib/src/core/models/event.g.dart | 122 +++-- .../lib/src/core/models/member.dart | 4 - .../lib/src/core/models/member.g.dart | 48 +- .../lib/src/core/models/message.dart | 19 +- .../lib/src/core/models/message.g.dart | 130 +++-- .../lib/src/core/models/mute.g.dart | 14 +- .../lib/src/core/models/own_user.dart | 10 +- .../lib/src/core/models/own_user.g.dart | 70 ++- .../lib/src/core/models/reaction.dart | 6 +- .../lib/src/core/models/reaction.g.dart | 28 +- .../stream_chat/lib/src/core/models/read.dart | 1 - .../lib/src/core/models/read.g.dart | 12 +- .../stream_chat/lib/src/core/models/user.dart | 14 +- .../lib/src/core/models/user.g.dart | 42 +- 32 files changed, 744 insertions(+), 751 deletions(-) diff --git a/packages/stream_chat/lib/src/core/api/requests.dart b/packages/stream_chat/lib/src/core/api/requests.dart index 0bc49fc7..14995b10 100644 --- a/packages/stream_chat/lib/src/core/api/requests.dart +++ b/packages/stream_chat/lib/src/core/api/requests.dart @@ -15,7 +15,7 @@ class SortOption { /// ``` const SortOption( this.field, { - this.direction = DESC, + this.direction = SortOption.DESC, this.comparator, }); diff --git a/packages/stream_chat/lib/src/core/api/requests.g.dart b/packages/stream_chat/lib/src/core/api/requests.g.dart index fd0ac624..ef995997 100644 --- a/packages/stream_chat/lib/src/core/api/requests.g.dart +++ b/packages/stream_chat/lib/src/core/api/requests.g.dart @@ -6,12 +6,11 @@ part of 'requests.dart'; // JsonSerializableGenerator // ************************************************************************** -SortOption _$SortOptionFromJson(Map json) { - return SortOption( - json['field'] as String, - direction: json['direction'] as int, - ); -} +SortOption _$SortOptionFromJson(Map json) => + SortOption( + json['field'] as String, + direction: json['direction'] as int? ?? SortOption.DESC, + ); Map _$SortOptionToJson(SortOption instance) => { @@ -19,22 +18,20 @@ Map _$SortOptionToJson(SortOption instance) => 'direction': instance.direction, }; -PaginationParams _$PaginationParamsFromJson(Map json) { - return PaginationParams( - limit: json['limit'] as int, - offset: json['offset'] as int, - next: json['next'] as String?, - greaterThan: json['id_gt'] as String?, - greaterThanOrEqual: json['id_gte'] as String?, - lessThan: json['id_lt'] as String?, - lessThanOrEqual: json['id_lte'] as String?, - ); -} +PaginationParams _$PaginationParamsFromJson(Map json) => + PaginationParams( + limit: json['limit'] as int? ?? 10, + offset: json['offset'] as int?, + next: json['next'] as String?, + greaterThan: json['id_gt'] as String?, + greaterThanOrEqual: json['id_gte'] as String?, + lessThan: json['id_lt'] as String?, + lessThanOrEqual: json['id_lte'] as String?, + ); Map _$PaginationParamsToJson(PaginationParams instance) { final val = { 'limit': instance.limit, - 'offset': instance.offset, }; void writeNotNull(String key, dynamic value) { @@ -43,6 +40,7 @@ Map _$PaginationParamsToJson(PaginationParams instance) { } } + writeNotNull('offset', instance.offset); writeNotNull('next', instance.next); writeNotNull('id_gt', instance.greaterThan); writeNotNull('id_gte', instance.greaterThanOrEqual); diff --git a/packages/stream_chat/lib/src/core/api/responses.g.dart b/packages/stream_chat/lib/src/core/api/responses.g.dart index 10d96682..13d7d77b 100644 --- a/packages/stream_chat/lib/src/core/api/responses.g.dart +++ b/packages/stream_chat/lib/src/core/api/responses.g.dart @@ -6,14 +6,13 @@ part of 'responses.dart'; // JsonSerializableGenerator // ************************************************************************** -ErrorResponse _$ErrorResponseFromJson(Map json) { - return ErrorResponse() - ..duration = json['duration'] as String? - ..code = json['code'] as int? - ..message = json['message'] as String? - ..statusCode = json['StatusCode'] as int? - ..moreInfo = json['more_info'] as String?; -} +ErrorResponse _$ErrorResponseFromJson(Map json) => + ErrorResponse() + ..duration = json['duration'] as String? + ..code = json['code'] as int? + ..message = json['message'] as String? + ..statusCode = json['StatusCode'] as int? + ..moreInfo = json['more_info'] as String?; Map _$ErrorResponseToJson(ErrorResponse instance) => { @@ -24,275 +23,253 @@ Map _$ErrorResponseToJson(ErrorResponse instance) => 'more_info': instance.moreInfo, }; -SyncResponse _$SyncResponseFromJson(Map json) { - return SyncResponse() - ..duration = json['duration'] as String? - ..events = (json['events'] as List?) - ?.map((e) => Event.fromJson(e as Map)) - .toList() ?? - []; -} +SyncResponse _$SyncResponseFromJson(Map json) => SyncResponse() + ..duration = json['duration'] as String? + ..events = (json['events'] as List?) + ?.map((e) => Event.fromJson(e as Map)) + .toList() ?? + []; QueryChannelsResponse _$QueryChannelsResponseFromJson( - Map json) { - return QueryChannelsResponse() - ..duration = json['duration'] as String? - ..channels = (json['channels'] as List?) - ?.map((e) => ChannelState.fromJson(e as Map)) - .toList() ?? - []; -} + Map json) => + QueryChannelsResponse() + ..duration = json['duration'] as String? + ..channels = (json['channels'] as List?) + ?.map((e) => ChannelState.fromJson(e as Map)) + .toList() ?? + []; TranslateMessageResponse _$TranslateMessageResponseFromJson( - Map json) { - return TranslateMessageResponse() - ..duration = json['duration'] as String? - ..message = Message.fromJson(json['message'] as Map); -} + Map json) => + TranslateMessageResponse() + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); -QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { - return QueryMembersResponse() - ..duration = json['duration'] as String? - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - []; -} +QueryMembersResponse _$QueryMembersResponseFromJson( + Map json) => + QueryMembersResponse() + ..duration = json['duration'] as String? + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + []; -QueryUsersResponse _$QueryUsersResponseFromJson(Map json) { - return QueryUsersResponse() - ..duration = json['duration'] as String? - ..users = (json['users'] as List?) - ?.map((e) => User.fromJson(e as Map)) - .toList() ?? - []; -} +QueryUsersResponse _$QueryUsersResponseFromJson(Map json) => + QueryUsersResponse() + ..duration = json['duration'] as String? + ..users = (json['users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + []; QueryReactionsResponse _$QueryReactionsResponseFromJson( - Map json) { - return QueryReactionsResponse() - ..duration = json['duration'] as String? - ..reactions = (json['reactions'] as List?) - ?.map((e) => Reaction.fromJson(e as Map)) - .toList() ?? - []; -} + Map json) => + QueryReactionsResponse() + ..duration = json['duration'] as String? + ..reactions = (json['reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList() ?? + []; -QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) { - return QueryRepliesResponse() - ..duration = json['duration'] as String? - ..messages = (json['messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - []; -} +QueryRepliesResponse _$QueryRepliesResponseFromJson( + Map json) => + QueryRepliesResponse() + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; -ListDevicesResponse _$ListDevicesResponseFromJson(Map json) { - return ListDevicesResponse() - ..duration = json['duration'] as String? - ..devices = (json['devices'] as List?) - ?.map((e) => Device.fromJson(e as Map)) - .toList() ?? - []; -} +ListDevicesResponse _$ListDevicesResponseFromJson(Map json) => + ListDevicesResponse() + ..duration = json['duration'] as String? + ..devices = (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + []; -SendFileResponse _$SendFileResponseFromJson(Map json) { - return SendFileResponse() - ..duration = json['duration'] as String? - ..file = json['file'] as String; -} +SendFileResponse _$SendFileResponseFromJson(Map json) => + SendFileResponse() + ..duration = json['duration'] as String? + ..file = json['file'] as String; -SendImageResponse _$SendImageResponseFromJson(Map json) { - return SendImageResponse() - ..duration = json['duration'] as String? - ..file = json['file'] as String; -} +SendImageResponse _$SendImageResponseFromJson(Map json) => + SendImageResponse() + ..duration = json['duration'] as String? + ..file = json['file'] as String; -SendReactionResponse _$SendReactionResponseFromJson(Map json) { - return SendReactionResponse() - ..duration = json['duration'] as String? - ..message = Message.fromJson(json['message'] as Map) - ..reaction = Reaction.fromJson(json['reaction'] as Map); -} +SendReactionResponse _$SendReactionResponseFromJson( + Map json) => + SendReactionResponse() + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) + ..reaction = Reaction.fromJson(json['reaction'] as Map); ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson( - Map json) { - return ConnectGuestUserResponse() - ..duration = json['duration'] as String? - ..accessToken = json['access_token'] as String - ..user = User.fromJson(json['user'] as Map); -} + Map json) => + ConnectGuestUserResponse() + ..duration = json['duration'] as String? + ..accessToken = json['access_token'] as String + ..user = User.fromJson(json['user'] as Map); -UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) { - return UpdateUsersResponse() - ..duration = json['duration'] as String? - ..users = (json['users'] as Map?)?.map( - (k, e) => MapEntry(k, User.fromJson(e as Map)), - ) ?? - {}; -} +UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) => + UpdateUsersResponse() + ..duration = json['duration'] as String? + ..users = (json['users'] as Map?)?.map( + (k, e) => MapEntry(k, User.fromJson(e as Map)), + ) ?? + {}; UpdateMessageResponse _$UpdateMessageResponseFromJson( - Map json) { - return UpdateMessageResponse() - ..duration = json['duration'] as String? - ..message = Message.fromJson(json['message'] as Map); -} + Map json) => + UpdateMessageResponse() + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); -SendMessageResponse _$SendMessageResponseFromJson(Map json) { - return SendMessageResponse() - ..duration = json['duration'] as String? - ..message = Message.fromJson(json['message'] as Map); -} +SendMessageResponse _$SendMessageResponseFromJson(Map json) => + SendMessageResponse() + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); -GetMessageResponse _$GetMessageResponseFromJson(Map json) { - return GetMessageResponse() - ..duration = json['duration'] as String? - ..message = Message.fromJson(json['message'] as Map) - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson(json['channel'] as Map); -} +GetMessageResponse _$GetMessageResponseFromJson(Map json) => + GetMessageResponse() + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) + ..channel = json['channel'] == null + ? null + : ChannelModel.fromJson(json['channel'] as Map); SearchMessagesResponse _$SearchMessagesResponseFromJson( - Map json) { - return SearchMessagesResponse() - ..duration = json['duration'] as String? - ..results = (json['results'] as List?) - ?.map((e) => GetMessageResponse.fromJson(e as Map)) - .toList() ?? - [] - ..next = json['next'] as String? - ..previous = json['previous'] as String?; -} + Map json) => + SearchMessagesResponse() + ..duration = json['duration'] as String? + ..results = (json['results'] as List?) + ?.map( + (e) => GetMessageResponse.fromJson(e as Map)) + .toList() ?? + [] + ..next = json['next'] as String? + ..previous = json['previous'] as String?; GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson( - Map json) { - return GetMessagesByIdResponse() - ..duration = json['duration'] as String? - ..messages = (json['messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - []; -} + Map json) => + GetMessagesByIdResponse() + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; UpdateChannelResponse _$UpdateChannelResponseFromJson( - Map json) { - return UpdateChannelResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} + Map json) => + UpdateChannelResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson( - Map json) { - return PartialUpdateChannelResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList(); -} + Map json) => + PartialUpdateChannelResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(); InviteMembersResponse _$InviteMembersResponseFromJson( - Map json) { - return InviteMembersResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} + Map json) => + InviteMembersResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); RemoveMembersResponse _$RemoveMembersResponseFromJson( - Map json) { - return RemoveMembersResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} + Map json) => + RemoveMembersResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); -SendActionResponse _$SendActionResponseFromJson(Map json) { - return SendActionResponse() - ..duration = json['duration'] as String? - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} +SendActionResponse _$SendActionResponseFromJson(Map json) => + SendActionResponse() + ..duration = json['duration'] as String? + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); -AddMembersResponse _$AddMembersResponseFromJson(Map json) { - return AddMembersResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} +AddMembersResponse _$AddMembersResponseFromJson(Map json) => + AddMembersResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); -AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) { - return AcceptInviteResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} +AcceptInviteResponse _$AcceptInviteResponseFromJson( + Map json) => + AcceptInviteResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); -RejectInviteResponse _$RejectInviteResponseFromJson(Map json) { - return RejectInviteResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..message = json['message'] == null - ? null - : Message.fromJson(json['message'] as Map); -} +RejectInviteResponse _$RejectInviteResponseFromJson( + Map json) => + RejectInviteResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..message = json['message'] == null + ? null + : Message.fromJson(json['message'] as Map); -EmptyResponse _$EmptyResponseFromJson(Map json) { - return EmptyResponse()..duration = json['duration'] as String?; -} +EmptyResponse _$EmptyResponseFromJson(Map json) => + EmptyResponse()..duration = json['duration'] as String?; -ChannelStateResponse _$ChannelStateResponseFromJson(Map json) { - return ChannelStateResponse() - ..duration = json['duration'] as String? - ..channel = ChannelModel.fromJson(json['channel'] as Map) - ..messages = (json['messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - [] - ..members = (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [] - ..watcherCount = json['watcher_count'] as int? ?? 0 - ..read = (json['read'] as List?) - ?.map((e) => Read.fromJson(e as Map)) - .toList() ?? - []; -} +ChannelStateResponse _$ChannelStateResponseFromJson( + Map json) => + ChannelStateResponse() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [] + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..watcherCount = json['watcher_count'] as int? ?? 0 + ..read = (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + []; diff --git a/packages/stream_chat/lib/src/core/models/action.dart b/packages/stream_chat/lib/src/core/models/action.dart index 16a307e7..709fbfe0 100644 --- a/packages/stream_chat/lib/src/core/models/action.dart +++ b/packages/stream_chat/lib/src/core/models/action.dart @@ -21,7 +21,6 @@ class Action { final String name; /// The style of the action - @JsonKey(defaultValue: 'default') final String style; /// The test of the action diff --git a/packages/stream_chat/lib/src/core/models/action.g.dart b/packages/stream_chat/lib/src/core/models/action.g.dart index 9ae999ed..2c9148d9 100644 --- a/packages/stream_chat/lib/src/core/models/action.g.dart +++ b/packages/stream_chat/lib/src/core/models/action.g.dart @@ -6,15 +6,13 @@ part of 'action.dart'; // JsonSerializableGenerator // ************************************************************************** -Action _$ActionFromJson(Map json) { - return Action( - name: json['name'] as String, - style: json['style'] as String? ?? 'default', - text: json['text'] as String, - type: json['type'] as String, - value: json['value'] as String?, - ); -} +Action _$ActionFromJson(Map json) => Action( + name: json['name'] as String, + style: json['style'] as String? ?? 'default', + text: json['text'] as String, + type: json['type'] as String, + value: json['value'] as String?, + ); Map _$ActionToJson(Action instance) => { 'name': instance.name, diff --git a/packages/stream_chat/lib/src/core/models/attachment.dart b/packages/stream_chat/lib/src/core/models/attachment.dart index 0ef9db7a..ca7e0afc 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.dart @@ -117,10 +117,7 @@ class Attachment extends Equatable { late final UploadState uploadState; /// Map of custom channel extraData - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) + @JsonKey(includeIfNull: false) final Map extraData; /// The attachment ID. diff --git a/packages/stream_chat/lib/src/core/models/attachment.g.dart b/packages/stream_chat/lib/src/core/models/attachment.g.dart index de129ab6..381eb99c 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.g.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.g.dart @@ -6,39 +6,37 @@ part of 'attachment.dart'; // JsonSerializableGenerator // ************************************************************************** -Attachment _$AttachmentFromJson(Map json) { - return Attachment( - id: json['id'] as String?, - type: json['type'] as String?, - titleLink: json['title_link'] as String?, - title: json['title'] as String?, - thumbUrl: json['thumb_url'] as String?, - text: json['text'] as String?, - pretext: json['pretext'] as String?, - ogScrapeUrl: json['og_scrape_url'] as String?, - imageUrl: json['image_url'] as String?, - footerIcon: json['footer_icon'] as String?, - footer: json['footer'] as String?, - fields: json['fields'], - fallback: json['fallback'] as String?, - color: json['color'] as String?, - authorName: json['author_name'] as String?, - authorLink: json['author_link'] as String?, - authorIcon: json['author_icon'] as String?, - assetUrl: json['asset_url'] as String?, - actions: (json['actions'] as List?) - ?.map((e) => Action.fromJson(e as Map)) - .toList() ?? - [], - extraData: json['extra_data'] as Map? ?? {}, - file: json['file'] == null - ? null - : AttachmentFile.fromJson(json['file'] as Map), - uploadState: json['upload_state'] == null - ? null - : UploadState.fromJson(json['upload_state'] as Map), - ); -} +Attachment _$AttachmentFromJson(Map json) => Attachment( + id: json['id'] as String?, + type: json['type'] as String?, + titleLink: json['title_link'] as String?, + title: json['title'] as String?, + thumbUrl: json['thumb_url'] as String?, + text: json['text'] as String?, + pretext: json['pretext'] as String?, + ogScrapeUrl: json['og_scrape_url'] as String?, + imageUrl: json['image_url'] as String?, + footerIcon: json['footer_icon'] as String?, + footer: json['footer'] as String?, + fields: json['fields'], + fallback: json['fallback'] as String?, + color: json['color'] as String?, + authorName: json['author_name'] as String?, + authorLink: json['author_link'] as String?, + authorIcon: json['author_icon'] as String?, + assetUrl: json['asset_url'] as String?, + actions: (json['actions'] as List?) + ?.map((e) => Action.fromJson(e as Map)) + .toList() ?? + [], + extraData: json['extra_data'] as Map? ?? const {}, + file: json['file'] == null + ? null + : AttachmentFile.fromJson(json['file'] as Map), + uploadState: json['upload_state'] == null + ? null + : UploadState.fromJson(json['upload_state'] as Map), + ); Map _$AttachmentToJson(Attachment instance) { final val = {}; diff --git a/packages/stream_chat/lib/src/core/models/attachment_file.freezed.dart b/packages/stream_chat/lib/src/core/models/attachment_file.freezed.dart index 5d7075c3..4956274a 100644 --- a/packages/stream_chat/lib/src/core/models/attachment_file.freezed.dart +++ b/packages/stream_chat/lib/src/core/models/attachment_file.freezed.dart @@ -1,5 +1,6 @@ +// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND -// 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 +// 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 part of 'attachment_file.dart'; @@ -13,7 +14,7 @@ final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); UploadState _$UploadStateFromJson(Map json) { - switch (json['runtimeType'] as String) { + switch (json['runtimeType'] as String?) { case 'preparing': return Preparing.fromJson(json); case 'inProgress': @@ -24,7 +25,8 @@ UploadState _$UploadStateFromJson(Map json) { return Failed.fromJson(json); default: - throw FallThroughError(); + throw CheckedFromJsonException(json, 'runtimeType', 'UploadState', + 'Invalid union type "${json['runtimeType']}"!'); } } @@ -72,6 +74,14 @@ mixin _$UploadState { }) => throw _privateConstructorUsedError; @optionalTypeArgs + TResult? whenOrNull({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs TResult maybeWhen({ TResult Function()? preparing, TResult Function(int uploaded, int total)? inProgress, @@ -89,6 +99,14 @@ mixin _$UploadState { }) => throw _privateConstructorUsedError; @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs TResult maybeMap({ TResult Function(Preparing value)? preparing, TResult Function(InProgress value)? inProgress, @@ -138,7 +156,7 @@ class _$Preparing implements Preparing { const _$Preparing(); factory _$Preparing.fromJson(Map json) => - _$_$PreparingFromJson(json); + _$$PreparingFromJson(json); @override String toString() { @@ -164,6 +182,17 @@ class _$Preparing implements Preparing { return preparing(); } + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + }) { + return preparing?.call(); + } + @override @optionalTypeArgs TResult maybeWhen({ @@ -190,6 +219,17 @@ class _$Preparing implements Preparing { return preparing(this); } + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + }) { + return preparing?.call(this); + } + @override @optionalTypeArgs TResult maybeMap({ @@ -207,7 +247,7 @@ class _$Preparing implements Preparing { @override Map toJson() { - return _$_$PreparingToJson(this)..['runtimeType'] = 'preparing'; + return _$$PreparingToJson(this)..['runtimeType'] = 'preparing'; } } @@ -258,7 +298,7 @@ class _$InProgress implements InProgress { const _$InProgress({required this.uploaded, required this.total}); factory _$InProgress.fromJson(Map json) => - _$_$InProgressFromJson(json); + _$$InProgressFromJson(json); @override final int uploaded; @@ -303,6 +343,17 @@ class _$InProgress implements InProgress { return inProgress(uploaded, total); } + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + }) { + return inProgress?.call(uploaded, total); + } + @override @optionalTypeArgs TResult maybeWhen({ @@ -329,6 +380,17 @@ class _$InProgress implements InProgress { return inProgress(this); } + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + }) { + return inProgress?.call(this); + } + @override @optionalTypeArgs TResult maybeMap({ @@ -346,7 +408,7 @@ class _$InProgress implements InProgress { @override Map toJson() { - return _$_$InProgressToJson(this)..['runtimeType'] = 'inProgress'; + return _$$InProgressToJson(this)..['runtimeType'] = 'inProgress'; } } @@ -386,7 +448,7 @@ class _$Success implements Success { const _$Success(); factory _$Success.fromJson(Map json) => - _$_$SuccessFromJson(json); + _$$SuccessFromJson(json); @override String toString() { @@ -412,6 +474,17 @@ class _$Success implements Success { return success(); } + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + }) { + return success?.call(); + } + @override @optionalTypeArgs TResult maybeWhen({ @@ -438,6 +511,17 @@ class _$Success implements Success { return success(this); } + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + }) { + return success?.call(this); + } + @override @optionalTypeArgs TResult maybeMap({ @@ -455,7 +539,7 @@ class _$Success implements Success { @override Map toJson() { - return _$_$SuccessToJson(this)..['runtimeType'] = 'success'; + return _$$SuccessToJson(this)..['runtimeType'] = 'success'; } } @@ -500,7 +584,7 @@ class _$Failed implements Failed { const _$Failed({required this.error}); factory _$Failed.fromJson(Map json) => - _$_$FailedFromJson(json); + _$$FailedFromJson(json); @override final String error; @@ -538,6 +622,17 @@ class _$Failed implements Failed { return failed(error); } + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + }) { + return failed?.call(error); + } + @override @optionalTypeArgs TResult maybeWhen({ @@ -564,6 +659,17 @@ class _$Failed implements Failed { return failed(this); } + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + }) { + return failed?.call(this); + } + @override @optionalTypeArgs TResult maybeMap({ @@ -581,7 +687,7 @@ class _$Failed implements Failed { @override Map toJson() { - return _$_$FailedToJson(this)..['runtimeType'] = 'failed'; + return _$$FailedToJson(this)..['runtimeType'] = 'failed'; } } diff --git a/packages/stream_chat/lib/src/core/models/attachment_file.g.dart b/packages/stream_chat/lib/src/core/models/attachment_file.g.dart index 5844ae16..f36c269d 100644 --- a/packages/stream_chat/lib/src/core/models/attachment_file.g.dart +++ b/packages/stream_chat/lib/src/core/models/attachment_file.g.dart @@ -6,14 +6,13 @@ part of 'attachment_file.dart'; // JsonSerializableGenerator // ************************************************************************** -AttachmentFile _$AttachmentFileFromJson(Map json) { - return AttachmentFile( - size: json['size'] as int?, - path: json['path'] as String?, - name: json['name'] as String?, - bytes: _fromString(json['bytes'] as String?), - ); -} +AttachmentFile _$AttachmentFileFromJson(Map json) => + AttachmentFile( + size: json['size'] as int?, + path: json['path'] as String?, + name: json['name'] as String?, + bytes: _fromString(json['bytes'] as String?), + ); Map _$AttachmentFileToJson(AttachmentFile instance) => { @@ -23,39 +22,31 @@ Map _$AttachmentFileToJson(AttachmentFile instance) => 'size': instance.size, }; -_$Preparing _$_$PreparingFromJson(Map json) { - return _$Preparing(); -} +_$Preparing _$$PreparingFromJson(Map json) => _$Preparing(); -Map _$_$PreparingToJson(_$Preparing instance) => +Map _$$PreparingToJson(_$Preparing instance) => {}; -_$InProgress _$_$InProgressFromJson(Map json) { - return _$InProgress( - uploaded: json['uploaded'] as int, - total: json['total'] as int, - ); -} +_$InProgress _$$InProgressFromJson(Map json) => _$InProgress( + uploaded: json['uploaded'] as int, + total: json['total'] as int, + ); -Map _$_$InProgressToJson(_$InProgress instance) => +Map _$$InProgressToJson(_$InProgress instance) => { 'uploaded': instance.uploaded, 'total': instance.total, }; -_$Success _$_$SuccessFromJson(Map json) { - return _$Success(); -} +_$Success _$$SuccessFromJson(Map json) => _$Success(); -Map _$_$SuccessToJson(_$Success instance) => +Map _$$SuccessToJson(_$Success instance) => {}; -_$Failed _$_$FailedFromJson(Map json) { - return _$Failed( - error: json['error'] as String, - ); -} +_$Failed _$$FailedFromJson(Map json) => _$Failed( + error: json['error'] as String, + ); -Map _$_$FailedToJson(_$Failed instance) => { +Map _$$FailedToJson(_$Failed instance) => { 'error': instance.error, }; diff --git a/packages/stream_chat/lib/src/core/models/channel_config.dart b/packages/stream_chat/lib/src/core/models/channel_config.dart index 2d5182b3..158738e6 100644 --- a/packages/stream_chat/lib/src/core/models/channel_config.dart +++ b/packages/stream_chat/lib/src/core/models/channel_config.dart @@ -31,15 +31,12 @@ class ChannelConfig { _$ChannelConfigFromJson(json); /// Moderation configuration - @JsonKey(defaultValue: 'flag') final String automod; /// List of available commands - @JsonKey(defaultValue: []) final List commands; /// True if the channel should send connect events - @JsonKey(defaultValue: false) final bool connectEvents; /// Date of channel creation @@ -49,43 +46,33 @@ class ChannelConfig { final DateTime updatedAt; /// Max channel message length - @JsonKey(defaultValue: 0) final int maxMessageLength; /// Duration of message retention - @JsonKey(defaultValue: '') final String messageRetention; /// True if users can be muted - @JsonKey(defaultValue: false) final bool mutes; /// True if reaction are active for this channel - @JsonKey(defaultValue: false) final bool reactions; /// True if readEvents are active for this channel - @JsonKey(defaultValue: false) final bool readEvents; /// True if reply message are active for this channel - @JsonKey(defaultValue: false) final bool replies; /// True if it's possible to perform a search in this channel - @JsonKey(defaultValue: false) final bool search; /// True if typing events should be sent for this channel - @JsonKey(defaultValue: false) final bool typingEvents; /// True if it's possible to upload files to this channel - @JsonKey(defaultValue: false) final bool uploads; /// True if urls appears as attachments - @JsonKey(defaultValue: false) final bool urlEnrichment; /// Serialize to json diff --git a/packages/stream_chat/lib/src/core/models/channel_config.g.dart b/packages/stream_chat/lib/src/core/models/channel_config.g.dart index 723281c0..48b2a739 100644 --- a/packages/stream_chat/lib/src/core/models/channel_config.g.dart +++ b/packages/stream_chat/lib/src/core/models/channel_config.g.dart @@ -6,32 +6,31 @@ part of 'channel_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelConfig _$ChannelConfigFromJson(Map json) { - return ChannelConfig( - automod: json['automod'] as String? ?? 'flag', - commands: (json['commands'] as List?) - ?.map((e) => Command.fromJson(e as Map)) - .toList() ?? - [], - connectEvents: json['connect_events'] as bool? ?? false, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - maxMessageLength: json['max_message_length'] as int? ?? 0, - messageRetention: json['message_retention'] as String? ?? '', - mutes: json['mutes'] as bool? ?? false, - reactions: json['reactions'] as bool? ?? false, - readEvents: json['read_events'] as bool? ?? false, - replies: json['replies'] as bool? ?? false, - search: json['search'] as bool? ?? false, - typingEvents: json['typing_events'] as bool? ?? false, - uploads: json['uploads'] as bool? ?? false, - urlEnrichment: json['url_enrichment'] as bool? ?? false, - ); -} +ChannelConfig _$ChannelConfigFromJson(Map json) => + ChannelConfig( + automod: json['automod'] as String? ?? 'flag', + commands: (json['commands'] as List?) + ?.map((e) => Command.fromJson(e as Map)) + .toList() ?? + const [], + connectEvents: json['connect_events'] as bool? ?? false, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + maxMessageLength: json['max_message_length'] as int? ?? 0, + messageRetention: json['message_retention'] as String? ?? '', + mutes: json['mutes'] as bool? ?? false, + reactions: json['reactions'] as bool? ?? false, + readEvents: json['read_events'] as bool? ?? false, + replies: json['replies'] as bool? ?? false, + search: json['search'] as bool? ?? false, + typingEvents: json['typing_events'] as bool? ?? false, + uploads: json['uploads'] as bool? ?? false, + urlEnrichment: json['url_enrichment'] as bool? ?? false, + ); Map _$ChannelConfigToJson(ChannelConfig instance) => { diff --git a/packages/stream_chat/lib/src/core/models/channel_model.dart b/packages/stream_chat/lib/src/core/models/channel_model.dart index 31ac4923..7892cc10 100644 --- a/packages/stream_chat/lib/src/core/models/channel_model.dart +++ b/packages/stream_chat/lib/src/core/models/channel_model.dart @@ -59,7 +59,7 @@ class ChannelModel { final User? createdBy; /// True if this channel is frozen - @JsonKey(includeIfNull: false, defaultValue: false) + @JsonKey(includeIfNull: false) final bool frozen; /// The date of the last message @@ -79,18 +79,15 @@ class ChannelModel { final DateTime? deletedAt; /// The count of this channel members - @JsonKey(includeIfNull: false, toJson: Serializer.readOnly, defaultValue: 0) + @JsonKey(includeIfNull: false, toJson: Serializer.readOnly) final int memberCount; /// The number of seconds in a cooldown - @JsonKey(includeIfNull: false, defaultValue: 0) + @JsonKey(includeIfNull: false) final int cooldown; /// Map of custom channel extraData - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) + @JsonKey(includeIfNull: false) final Map extraData; /// The team the channel belongs to diff --git a/packages/stream_chat/lib/src/core/models/channel_model.g.dart b/packages/stream_chat/lib/src/core/models/channel_model.g.dart index d9adf94b..94d9a81a 100644 --- a/packages/stream_chat/lib/src/core/models/channel_model.g.dart +++ b/packages/stream_chat/lib/src/core/models/channel_model.g.dart @@ -6,36 +6,34 @@ part of 'channel_model.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelModel _$ChannelModelFromJson(Map json) { - return ChannelModel( - id: json['id'] as String?, - type: json['type'] as String?, - cid: json['cid'] as String?, - config: json['config'] == null - ? null - : ChannelConfig.fromJson(json['config'] as Map), - createdBy: json['created_by'] == null - ? null - : User.fromJson(json['created_by'] as Map), - frozen: json['frozen'] as bool? ?? false, - lastMessageAt: json['last_message_at'] == null - ? null - : DateTime.parse(json['last_message_at'] as String), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - deletedAt: json['deleted_at'] == null - ? null - : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int? ?? 0, - extraData: json['extra_data'] as Map? ?? {}, - team: json['team'] as String?, - cooldown: json['cooldown'] as int? ?? 0, - ); -} +ChannelModel _$ChannelModelFromJson(Map json) => ChannelModel( + id: json['id'] as String?, + type: json['type'] as String?, + cid: json['cid'] as String?, + config: json['config'] == null + ? null + : ChannelConfig.fromJson(json['config'] as Map), + createdBy: json['created_by'] == null + ? null + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, + lastMessageAt: json['last_message_at'] == null + ? null + : DateTime.parse(json['last_message_at'] as String), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + memberCount: json['member_count'] as int? ?? 0, + extraData: json['extra_data'] as Map? ?? const {}, + team: json['team'] as String?, + cooldown: json['cooldown'] as int? ?? 0, + ); Map _$ChannelModelToJson(ChannelModel instance) { final val = { diff --git a/packages/stream_chat/lib/src/core/models/channel_state.dart b/packages/stream_chat/lib/src/core/models/channel_state.dart index 998efa98..fb5fc64f 100644 --- a/packages/stream_chat/lib/src/core/models/channel_state.dart +++ b/packages/stream_chat/lib/src/core/models/channel_state.dart @@ -25,26 +25,21 @@ class ChannelState { final ChannelModel? channel; /// A paginated list of channel messages - @JsonKey(defaultValue: []) final List messages; /// A paginated list of channel members - @JsonKey(defaultValue: []) final List members; /// A paginated list of pinned messages - @JsonKey(defaultValue: []) final List pinnedMessages; /// The count of users watching the channel final int? watcherCount; /// A paginated list of users watching the channel - @JsonKey(defaultValue: []) final List watchers; /// The list of channel reads - @JsonKey(defaultValue: []) final List read; /// Create a new instance from a json diff --git a/packages/stream_chat/lib/src/core/models/channel_state.g.dart b/packages/stream_chat/lib/src/core/models/channel_state.g.dart index 39c6373f..0da51768 100644 --- a/packages/stream_chat/lib/src/core/models/channel_state.g.dart +++ b/packages/stream_chat/lib/src/core/models/channel_state.g.dart @@ -6,34 +6,32 @@ part of 'channel_state.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelState _$ChannelStateFromJson(Map json) { - return ChannelState( - channel: json['channel'] == null - ? null - : ChannelModel.fromJson(json['channel'] as Map), - messages: (json['messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - [], - members: (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - [], - pinnedMessages: (json['pinned_messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - [], - watcherCount: json['watcher_count'] as int?, - watchers: (json['watchers'] as List?) - ?.map((e) => User.fromJson(e as Map)) - .toList() ?? - [], - read: (json['read'] as List?) - ?.map((e) => Read.fromJson(e as Map)) - .toList() ?? - [], - ); -} +ChannelState _$ChannelStateFromJson(Map json) => ChannelState( + channel: json['channel'] == null + ? null + : ChannelModel.fromJson(json['channel'] as Map), + messages: (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + const [], + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + const [], + pinnedMessages: (json['pinned_messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + const [], + watcherCount: json['watcher_count'] as int?, + watchers: (json['watchers'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + const [], + read: (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + const [], + ); Map _$ChannelStateToJson(ChannelState instance) => { diff --git a/packages/stream_chat/lib/src/core/models/command.g.dart b/packages/stream_chat/lib/src/core/models/command.g.dart index cf8be971..0fe9d54c 100644 --- a/packages/stream_chat/lib/src/core/models/command.g.dart +++ b/packages/stream_chat/lib/src/core/models/command.g.dart @@ -6,13 +6,11 @@ part of 'command.dart'; // JsonSerializableGenerator // ************************************************************************** -Command _$CommandFromJson(Map json) { - return Command( - name: json['name'] as String, - description: json['description'] as String, - args: json['args'] as String, - ); -} +Command _$CommandFromJson(Map json) => Command( + name: json['name'] as String, + description: json['description'] as String, + args: json['args'] as String, + ); Map _$CommandToJson(Command instance) => { 'name': instance.name, diff --git a/packages/stream_chat/lib/src/core/models/device.g.dart b/packages/stream_chat/lib/src/core/models/device.g.dart index 5fcd9435..4de1f064 100644 --- a/packages/stream_chat/lib/src/core/models/device.g.dart +++ b/packages/stream_chat/lib/src/core/models/device.g.dart @@ -6,12 +6,10 @@ part of 'device.dart'; // JsonSerializableGenerator // ************************************************************************** -Device _$DeviceFromJson(Map json) { - return Device( - id: json['id'] as String, - pushProvider: json['push_provider'] as String, - ); -} +Device _$DeviceFromJson(Map json) => Device( + id: json['id'] as String, + pushProvider: json['push_provider'] as String, + ); Map _$DeviceToJson(Device instance) => { 'id': instance.id, diff --git a/packages/stream_chat/lib/src/core/models/event.dart b/packages/stream_chat/lib/src/core/models/event.dart index 9250176d..2aa1b33b 100644 --- a/packages/stream_chat/lib/src/core/models/event.dart +++ b/packages/stream_chat/lib/src/core/models/event.dart @@ -92,7 +92,6 @@ class Event { final bool isLocal; /// Map of custom channel extraData - @JsonKey(defaultValue: {}) final Map extraData; /// Known top level fields. diff --git a/packages/stream_chat/lib/src/core/models/event.g.dart b/packages/stream_chat/lib/src/core/models/event.g.dart index 93d2e75b..af48500c 100644 --- a/packages/stream_chat/lib/src/core/models/event.g.dart +++ b/packages/stream_chat/lib/src/core/models/event.g.dart @@ -6,42 +6,40 @@ part of 'event.dart'; // JsonSerializableGenerator // ************************************************************************** -Event _$EventFromJson(Map json) { - return Event( - type: json['type'] as String, - cid: json['cid'] as String?, - connectionId: json['connection_id'] as String?, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - me: json['me'] == null - ? null - : OwnUser.fromJson(json['me'] as Map), - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - message: json['message'] == null - ? null - : Message.fromJson(json['message'] as Map), - totalUnreadCount: json['total_unread_count'] as int?, - unreadChannels: json['unread_channels'] as int?, - reaction: json['reaction'] == null - ? null - : Reaction.fromJson(json['reaction'] as Map), - online: json['online'] as bool?, - channel: json['channel'] == null - ? null - : EventChannel.fromJson(json['channel'] as Map), - member: json['member'] == null - ? null - : Member.fromJson(json['member'] as Map), - channelId: json['channel_id'] as String?, - channelType: json['channel_type'] as String?, - parentId: json['parent_id'] as String?, - extraData: json['extra_data'] as Map? ?? {}, - isLocal: json['is_local'] as bool? ?? false, - ); -} +Event _$EventFromJson(Map json) => Event( + type: json['type'] as String? ?? 'local.event', + cid: json['cid'] as String?, + connectionId: json['connection_id'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + me: json['me'] == null + ? null + : OwnUser.fromJson(json['me'] as Map), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + message: json['message'] == null + ? null + : Message.fromJson(json['message'] as Map), + totalUnreadCount: json['total_unread_count'] as int?, + unreadChannels: json['unread_channels'] as int?, + reaction: json['reaction'] == null + ? null + : Reaction.fromJson(json['reaction'] as Map), + online: json['online'] as bool?, + channel: json['channel'] == null + ? null + : EventChannel.fromJson(json['channel'] as Map), + member: json['member'] == null + ? null + : Member.fromJson(json['member'] as Map), + channelId: json['channel_id'] as String?, + channelType: json['channel_type'] as String?, + parentId: json['parent_id'] as String?, + extraData: json['extra_data'] as Map? ?? const {}, + isLocal: json['is_local'] as bool? ?? false, + ); Map _$EventToJson(Event instance) => { 'type': instance.type, @@ -64,30 +62,28 @@ Map _$EventToJson(Event instance) => { 'extra_data': instance.extraData, }; -EventChannel _$EventChannelFromJson(Map json) { - return EventChannel( - members: (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList(), - id: json['id'] as String?, - type: json['type'] as String?, - cid: json['cid'] as String, - config: ChannelConfig.fromJson(json['config'] as Map), - createdBy: json['created_by'] == null - ? null - : User.fromJson(json['created_by'] as Map), - frozen: json['frozen'] as bool? ?? false, - lastMessageAt: json['last_message_at'] == null - ? null - : DateTime.parse(json['last_message_at'] as String), - createdAt: DateTime.parse(json['created_at'] as String), - updatedAt: DateTime.parse(json['updated_at'] as String), - deletedAt: json['deleted_at'] == null - ? null - : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int? ?? 0, - extraData: json['extra_data'] as Map? ?? {}, - cooldown: json['cooldown'] as int? ?? 0, - team: json['team'] as String?, - ); -} +EventChannel _$EventChannelFromJson(Map json) => EventChannel( + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(), + id: json['id'] as String?, + type: json['type'] as String?, + cid: json['cid'] as String, + config: ChannelConfig.fromJson(json['config'] as Map), + createdBy: json['created_by'] == null + ? null + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, + lastMessageAt: json['last_message_at'] == null + ? null + : DateTime.parse(json['last_message_at'] as String), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + memberCount: json['member_count'] as int, + extraData: json['extra_data'] as Map?, + cooldown: json['cooldown'] as int, + team: json['team'] as String?, + ); diff --git a/packages/stream_chat/lib/src/core/models/member.dart b/packages/stream_chat/lib/src/core/models/member.dart index 5c9bc95c..0dae7c04 100644 --- a/packages/stream_chat/lib/src/core/models/member.dart +++ b/packages/stream_chat/lib/src/core/models/member.dart @@ -42,7 +42,6 @@ class Member extends Equatable { final DateTime? inviteRejectedAt; /// True if the user has been invited to the channel - @JsonKey(defaultValue: false) final bool invited; /// The role of the user in the channel @@ -52,15 +51,12 @@ class Member extends Equatable { final String? userId; /// True if the user is a moderator of the channel - @JsonKey(defaultValue: false) final bool isModerator; /// True if the member is banned from the channel - @JsonKey(defaultValue: false) final bool banned; /// True if the member is shadow banned from the channel - @JsonKey(defaultValue: false) final bool shadowBanned; /// The date of creation diff --git a/packages/stream_chat/lib/src/core/models/member.g.dart b/packages/stream_chat/lib/src/core/models/member.g.dart index a75b458d..0e711b1a 100644 --- a/packages/stream_chat/lib/src/core/models/member.g.dart +++ b/packages/stream_chat/lib/src/core/models/member.g.dart @@ -6,31 +6,29 @@ part of 'member.dart'; // JsonSerializableGenerator // ************************************************************************** -Member _$MemberFromJson(Map json) { - return Member( - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - inviteAcceptedAt: json['invite_accepted_at'] == null - ? null - : DateTime.parse(json['invite_accepted_at'] as String), - inviteRejectedAt: json['invite_rejected_at'] == null - ? null - : DateTime.parse(json['invite_rejected_at'] as String), - invited: json['invited'] as bool? ?? false, - role: json['role'] as String?, - userId: json['user_id'] as String?, - isModerator: json['is_moderator'] as bool? ?? false, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - banned: json['banned'] as bool? ?? false, - shadowBanned: json['shadow_banned'] as bool? ?? false, - ); -} +Member _$MemberFromJson(Map json) => Member( + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + inviteAcceptedAt: json['invite_accepted_at'] == null + ? null + : DateTime.parse(json['invite_accepted_at'] as String), + inviteRejectedAt: json['invite_rejected_at'] == null + ? null + : DateTime.parse(json['invite_rejected_at'] as String), + invited: json['invited'] as bool? ?? false, + role: json['role'] as String?, + userId: json['user_id'] as String?, + isModerator: json['is_moderator'] as bool? ?? false, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + banned: json['banned'] as bool? ?? false, + shadowBanned: json['shadow_banned'] as bool? ?? false, + ); Map _$MemberToJson(Member instance) => { 'user': instance.user?.toJson(), diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index 6ad8721c..490bd981 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -98,23 +98,16 @@ class Message extends Equatable { @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, - defaultValue: 'regular', ) final String type; /// The list of attachments, either provided by the user or generated from a /// command or as a result of URL scraping. - @JsonKey( - includeIfNull: false, - defaultValue: [], - ) + @JsonKey(includeIfNull: false) final List attachments; /// The list of user mentioned in the message - @JsonKey( - toJson: User.toIds, - defaultValue: [], - ) + @JsonKey(toJson: User.toIds) final List mentionedUsers; /// A map describing the count of number of every reaction @@ -155,14 +148,12 @@ class Message extends Equatable { final bool? showInChannel; /// If true the message is silent - @JsonKey(defaultValue: false) final bool silent; /// If true the message is shadowed @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, - defaultValue: false, ) final bool shadowed; @@ -183,7 +174,6 @@ class Message extends Equatable { final User? user; /// If true the message is pinned - @JsonKey(defaultValue: false) final bool pinned; /// Reserved field indicating when the message was pinned @@ -200,10 +190,7 @@ class Message extends Equatable { final User? pinnedBy; /// Message custom extraData - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) + @JsonKey(includeIfNull: false) final Map extraData; /// True if the message is a system info diff --git a/packages/stream_chat/lib/src/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index 25254e2c..b3b58c58 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -6,72 +6,70 @@ part of 'message.dart'; // JsonSerializableGenerator // ************************************************************************** -Message _$MessageFromJson(Map json) { - return Message( - id: json['id'] as String?, - text: json['text'] as String?, - type: json['type'] as String? ?? 'regular', - attachments: (json['attachments'] as List?) - ?.map((e) => Attachment.fromJson(e as Map)) - .toList() ?? - [], - mentionedUsers: (json['mentioned_users'] as List?) - ?.map((e) => User.fromJson(e as Map)) - .toList() ?? - [], - silent: json['silent'] as bool? ?? false, - shadowed: json['shadowed'] as bool? ?? false, - reactionCounts: (json['reaction_counts'] as Map?)?.map( - (k, e) => MapEntry(k, e as int), - ), - reactionScores: (json['reaction_scores'] as Map?)?.map( - (k, e) => MapEntry(k, e as int), - ), - latestReactions: (json['latest_reactions'] as List?) - ?.map((e) => Reaction.fromJson(e as Map)) - .toList(), - ownReactions: (json['own_reactions'] as List?) - ?.map((e) => Reaction.fromJson(e as Map)) - .toList(), - parentId: json['parent_id'] as String?, - quotedMessage: json['quoted_message'] == null - ? null - : Message.fromJson(json['quoted_message'] as Map), - quotedMessageId: json['quoted_message_id'] as String?, - replyCount: json['reply_count'] as int?, - threadParticipants: (json['thread_participants'] as List?) - ?.map((e) => User.fromJson(e as Map)) - .toList(), - showInChannel: json['show_in_channel'] as bool?, - command: json['command'] as String?, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - pinned: json['pinned'] as bool? ?? false, - pinnedAt: json['pinned_at'] == null - ? null - : DateTime.parse(json['pinned_at'] as String), - pinExpires: json['pin_expires'] == null - ? null - : DateTime.parse(json['pin_expires'] as String), - pinnedBy: json['pinned_by'] == null - ? null - : User.fromJson(json['pinned_by'] as Map), - extraData: json['extra_data'] as Map? ?? {}, - deletedAt: json['deleted_at'] == null - ? null - : DateTime.parse(json['deleted_at'] as String), - i18n: (json['i18n'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - ); -} +Message _$MessageFromJson(Map json) => Message( + id: json['id'] as String?, + text: json['text'] as String?, + type: json['type'] as String? ?? 'regular', + attachments: (json['attachments'] as List?) + ?.map((e) => Attachment.fromJson(e as Map)) + .toList() ?? + const [], + mentionedUsers: (json['mentioned_users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + const [], + silent: json['silent'] as bool? ?? false, + shadowed: json['shadowed'] as bool? ?? false, + reactionCounts: (json['reaction_counts'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), + ), + reactionScores: (json['reaction_scores'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), + ), + latestReactions: (json['latest_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + ownReactions: (json['own_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + parentId: json['parent_id'] as String?, + quotedMessage: json['quoted_message'] == null + ? null + : Message.fromJson(json['quoted_message'] as Map), + quotedMessageId: json['quoted_message_id'] as String?, + replyCount: json['reply_count'] as int? ?? 0, + threadParticipants: (json['thread_participants'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + showInChannel: json['show_in_channel'] as bool?, + command: json['command'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + pinned: json['pinned'] as bool? ?? false, + pinnedAt: json['pinned_at'] == null + ? null + : DateTime.parse(json['pinned_at'] as String), + pinExpires: json['pin_expires'] == null + ? null + : DateTime.parse(json['pin_expires'] as String), + pinnedBy: json['pinned_by'] == null + ? null + : User.fromJson(json['pinned_by'] as Map), + extraData: json['extra_data'] as Map? ?? const {}, + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + i18n: (json['i18n'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + ); Map _$MessageToJson(Message instance) { final val = { diff --git a/packages/stream_chat/lib/src/core/models/mute.g.dart b/packages/stream_chat/lib/src/core/models/mute.g.dart index b847a109..f57fd312 100644 --- a/packages/stream_chat/lib/src/core/models/mute.g.dart +++ b/packages/stream_chat/lib/src/core/models/mute.g.dart @@ -6,11 +6,9 @@ part of 'mute.dart'; // JsonSerializableGenerator // ************************************************************************** -Mute _$MuteFromJson(Map json) { - return Mute( - user: User.fromJson(json['user'] as Map), - channel: ChannelModel.fromJson(json['channel'] as Map), - createdAt: DateTime.parse(json['created_at'] as String), - updatedAt: DateTime.parse(json['updated_at'] as String), - ); -} +Mute _$MuteFromJson(Map json) => Mute( + user: User.fromJson(json['user'] as Map), + channel: ChannelModel.fromJson(json['channel'] as Map), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + ); diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index eb64eed3..f65735ab 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -131,23 +131,23 @@ class OwnUser extends User { } /// List of user devices. - @JsonKey(includeIfNull: false, defaultValue: []) + @JsonKey(includeIfNull: false) final List devices; /// List of users muted by the user. - @JsonKey(includeIfNull: false, defaultValue: []) + @JsonKey(includeIfNull: false) final List mutes; /// List of users muted by the user. - @JsonKey(includeIfNull: false, defaultValue: []) + @JsonKey(includeIfNull: false) final List channelMutes; /// Total unread messages by the user. - @JsonKey(includeIfNull: false, defaultValue: 0) + @JsonKey(includeIfNull: false) final int totalUnreadCount; /// Total unread channels by the user. - @JsonKey(includeIfNull: false, defaultValue: 0) + @JsonKey(includeIfNull: false) final int unreadChannels; /// Known top level fields. diff --git a/packages/stream_chat/lib/src/core/models/own_user.g.dart b/packages/stream_chat/lib/src/core/models/own_user.g.dart index 185b6dd0..41b6e2f5 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.g.dart @@ -6,39 +6,37 @@ part of 'own_user.dart'; // JsonSerializableGenerator // ************************************************************************** -OwnUser _$OwnUserFromJson(Map json) { - return OwnUser( - devices: (json['devices'] as List?) - ?.map((e) => Device.fromJson(e as Map)) - .toList() ?? - [], - mutes: (json['mutes'] as List?) - ?.map((e) => Mute.fromJson(e as Map)) - .toList() ?? - [], - totalUnreadCount: json['total_unread_count'] as int? ?? 0, - unreadChannels: json['unread_channels'] as int? ?? 0, - channelMutes: (json['channel_mutes'] as List?) - ?.map((e) => Mute.fromJson(e as Map)) - .toList() ?? - [], - id: json['id'] as String, - role: json['role'] as String?, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - lastActive: json['last_active'] == null - ? null - : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool? ?? false, - extraData: json['extra_data'] as Map? ?? {}, - banned: json['banned'] as bool? ?? false, - teams: - (json['teams'] as List?)?.map((e) => e as String).toList() ?? - [], - language: json['language'] as String?, - ); -} +OwnUser _$OwnUserFromJson(Map json) => OwnUser( + devices: (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + const [], + mutes: (json['mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + const [], + totalUnreadCount: json['total_unread_count'] as int? ?? 0, + unreadChannels: json['unread_channels'] as int? ?? 0, + channelMutes: (json['channel_mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + const [], + id: json['id'] as String, + role: json['role'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + lastActive: json['last_active'] == null + ? null + : DateTime.parse(json['last_active'] as String), + online: json['online'] as bool? ?? false, + extraData: json['extra_data'] as Map? ?? const {}, + banned: json['banned'] as bool? ?? false, + teams: + (json['teams'] as List?)?.map((e) => e as String).toList() ?? + const [], + language: json['language'] as String?, + ); diff --git a/packages/stream_chat/lib/src/core/models/reaction.dart b/packages/stream_chat/lib/src/core/models/reaction.dart index 2b0ffbf6..f16fb09f 100644 --- a/packages/stream_chat/lib/src/core/models/reaction.dart +++ b/packages/stream_chat/lib/src/core/models/reaction.dart @@ -41,7 +41,6 @@ class Reaction { final User? user; /// The score of the reaction (ie. number of reactions sent) - @JsonKey(defaultValue: 0) final int score; /// The userId that sent the reaction @@ -49,10 +48,7 @@ class Reaction { final String? userId; /// Reaction custom extraData - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) + @JsonKey(includeIfNull: false) final Map extraData; /// Map of custom user extraData diff --git a/packages/stream_chat/lib/src/core/models/reaction.g.dart b/packages/stream_chat/lib/src/core/models/reaction.g.dart index 3e92150d..3554106d 100644 --- a/packages/stream_chat/lib/src/core/models/reaction.g.dart +++ b/packages/stream_chat/lib/src/core/models/reaction.g.dart @@ -6,21 +6,19 @@ part of 'reaction.dart'; // JsonSerializableGenerator // ************************************************************************** -Reaction _$ReactionFromJson(Map json) { - return Reaction( - messageId: json['message_id'] as String?, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - type: json['type'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - userId: json['user_id'] as String?, - score: json['score'] as int? ?? 0, - extraData: json['extra_data'] as Map? ?? {}, - ); -} +Reaction _$ReactionFromJson(Map json) => Reaction( + messageId: json['message_id'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + type: json['type'] as String, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + userId: json['user_id'] as String?, + score: json['score'] as int? ?? 0, + extraData: json['extra_data'] as Map? ?? const {}, + ); Map _$ReactionToJson(Reaction instance) { final val = { diff --git a/packages/stream_chat/lib/src/core/models/read.dart b/packages/stream_chat/lib/src/core/models/read.dart index 55912762..812fc6f7 100644 --- a/packages/stream_chat/lib/src/core/models/read.dart +++ b/packages/stream_chat/lib/src/core/models/read.dart @@ -23,7 +23,6 @@ class Read { final User user; /// Number of unread messages - @JsonKey(defaultValue: 0) final int unreadMessages; /// Serialize to json diff --git a/packages/stream_chat/lib/src/core/models/read.g.dart b/packages/stream_chat/lib/src/core/models/read.g.dart index 93c832d8..f44a21d5 100644 --- a/packages/stream_chat/lib/src/core/models/read.g.dart +++ b/packages/stream_chat/lib/src/core/models/read.g.dart @@ -6,13 +6,11 @@ part of 'read.dart'; // JsonSerializableGenerator // ************************************************************************** -Read _$ReadFromJson(Map json) { - return Read( - lastRead: DateTime.parse(json['last_read'] as String), - user: User.fromJson(json['user'] as Map), - unreadMessages: json['unread_messages'] as int? ?? 0, - ); -} +Read _$ReadFromJson(Map json) => Read( + lastRead: DateTime.parse(json['last_read'] as String), + user: User.fromJson(json['user'] as Map), + unreadMessages: json['unread_messages'] as int? ?? 0, + ); Map _$ReadToJson(Read instance) => { 'last_read': instance.lastRead.toIso8601String(), diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index 5a5ef8e5..44df4399 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -100,7 +100,6 @@ class User extends Equatable { @JsonKey( includeIfNull: false, toJson: Serializer.readOnly, - defaultValue: [], ) final List teams; @@ -118,19 +117,20 @@ class User extends Equatable { /// True if user is online. @JsonKey( - includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) + includeIfNull: false, + toJson: Serializer.readOnly, + ) final bool online; /// True if user is banned from the chat. @JsonKey( - includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false) + includeIfNull: false, + toJson: Serializer.readOnly, + ) final bool banned; /// Map of custom user extraData. - @JsonKey( - includeIfNull: false, - defaultValue: {}, - ) + @JsonKey(includeIfNull: false) final Map extraData; /// The language this user prefers. diff --git a/packages/stream_chat/lib/src/core/models/user.g.dart b/packages/stream_chat/lib/src/core/models/user.g.dart index ab2f04d5..82c799a0 100644 --- a/packages/stream_chat/lib/src/core/models/user.g.dart +++ b/packages/stream_chat/lib/src/core/models/user.g.dart @@ -6,28 +6,26 @@ part of 'user.dart'; // JsonSerializableGenerator // ************************************************************************** -User _$UserFromJson(Map json) { - return User( - id: json['id'] as String, - role: json['role'] as String?, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - lastActive: json['last_active'] == null - ? null - : DateTime.parse(json['last_active'] as String), - extraData: json['extra_data'] as Map? ?? {}, - online: json['online'] as bool? ?? false, - banned: json['banned'] as bool? ?? false, - teams: - (json['teams'] as List?)?.map((e) => e as String).toList() ?? - [], - language: json['language'] as String?, - ); -} +User _$UserFromJson(Map json) => User( + id: json['id'] as String, + role: json['role'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + lastActive: json['last_active'] == null + ? null + : DateTime.parse(json['last_active'] as String), + extraData: json['extra_data'] as Map? ?? const {}, + online: json['online'] as bool? ?? false, + banned: json['banned'] as bool? ?? false, + teams: + (json['teams'] as List?)?.map((e) => e as String).toList() ?? + const [], + language: json['language'] as String?, + ); Map _$UserToJson(User instance) { final val = { From 964d77d18e0ce79b43587114e01ec8ce1dad2195 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 14:13:51 +0200 Subject: [PATCH 141/148] Update packages/stream_chat/pubspec.yaml Co-authored-by: Sahil Kumar --- packages/stream_chat/pubspec.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index e09e8271..2ad238d6 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -30,6 +30,4 @@ dev_dependencies: freezed: ^0.14.1+3 json_serializable: ^5.0.2 mocktail: ^0.1.1 - # Remove fixed version once the below issue is fixed - # https://github.com/flutter/flutter/issues/91059 test: ^1.18.2 From 859c42305c67dfccb998d06234fc57c5d36e7bf6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 14:23:49 +0200 Subject: [PATCH 142/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 4c857168..f42b8889 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,6 +1,7 @@ ## 3.1.0 - Updated `stream_chat_flutter_core` dependency to [`3.1.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). +- Updated `file_picker`, `image_gallery_saver`, and `video_thumbnail` to the latest versions. 🐞 Fixed From 27c684e766f1702f411642587e3c7685869f6438 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 14:35:37 +0200 Subject: [PATCH 143/148] fix(llc): fix event model --- packages/stream_chat/lib/src/core/models/event.dart | 2 +- packages/stream_chat/lib/src/core/models/event.g.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/lib/src/core/models/event.dart b/packages/stream_chat/lib/src/core/models/event.dart index 2aa1b33b..aed35696 100644 --- a/packages/stream_chat/lib/src/core/models/event.dart +++ b/packages/stream_chat/lib/src/core/models/event.dart @@ -183,7 +183,7 @@ class EventChannel extends ChannelModel { DateTime? deletedAt, required int memberCount, Map? extraData, - required int cooldown, + int cooldown = 0, String? team, }) : super( id: id, diff --git a/packages/stream_chat/lib/src/core/models/event.g.dart b/packages/stream_chat/lib/src/core/models/event.g.dart index af48500c..29a4482b 100644 --- a/packages/stream_chat/lib/src/core/models/event.g.dart +++ b/packages/stream_chat/lib/src/core/models/event.g.dart @@ -84,6 +84,6 @@ EventChannel _$EventChannelFromJson(Map json) => EventChannel( : DateTime.parse(json['deleted_at'] as String), memberCount: json['member_count'] as int, extraData: json['extra_data'] as Map?, - cooldown: json['cooldown'] as int, + cooldown: json['cooldown'] as int? ?? 0, team: json['team'] as String?, ); From 9766b38244524e338d3a710983cb54c053ea6507 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 14:36:47 +0200 Subject: [PATCH 144/148] chore(llc,core,ui): update changelog and pubspecs --- packages/stream_chat/CHANGELOG.md | 2 +- packages/stream_chat/lib/version.dart | 2 +- packages/stream_chat/pubspec.yaml | 2 +- packages/stream_chat_flutter/CHANGELOG.md | 4 ++-- packages/stream_chat_flutter/pubspec.yaml | 4 ++-- packages/stream_chat_flutter_core/CHANGELOG.md | 4 ++-- packages/stream_chat_flutter_core/pubspec.yaml | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index a9a8ed10..667365d0 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.1.0 +## 3.1.1 ✅ Added diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index a82ba271..680b2d9f 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '3.1.0'; +const PACKAGE_VERSION = '3.1.1'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 2ad238d6..0638bc51 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 3.1.0 +version: 3.1.1 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index f42b8889..5ac322df 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,6 +1,6 @@ -## 3.1.0 +## 3.1.1 -- Updated `stream_chat_flutter_core` dependency to [`3.1.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). +- Updated `stream_chat_flutter_core` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `file_picker`, `image_gallery_saver`, and `video_thumbnail` to the latest versions. 🐞 Fixed diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 0b190ee9..ef714445 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 3.1.0 +version: 3.1.1 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -37,7 +37,7 @@ dependencies: scrollable_positioned_list: ^0.2.0-nullsafety.0 share_plus: ^2.0.3 shimmer: ^2.0.0 - stream_chat_flutter_core: ^3.1.0 + stream_chat_flutter_core: ^3.1.1 substring_highlight: ^1.0.26 synchronized: ^3.0.0 url_launcher: ^6.0.3 diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 897f8984..96bae0c7 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,6 +1,6 @@ -## 3.1.0 +## 3.1.1 -- Updated `stream_chat` dependency to [`3.1.0`](https://pub.dev/packages/stream_chat/changelog). +- Updated `stream_chat` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat/changelog). ## 3.0.0 diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 08db72e4..128c58fb 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 3.1.0 +version: 3.1.1 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -16,7 +16,7 @@ dependencies: sdk: flutter meta: ^1.3.0 rxdart: ^0.27.0 - stream_chat: ^3.1.0 + stream_chat: ^3.1.1 dev_dependencies: fake_async: ^1.2.0 From 1ea8f38293b3bc278768f9bf4487655f1e49124e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 5 Oct 2021 14:44:49 +0200 Subject: [PATCH 145/148] fix(core): fix analysis --- packages/stream_chat_flutter_core/test/channels_bloc_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart index 2aee9eba..a237a23c 100644 --- a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart @@ -558,7 +558,6 @@ void main() { config: ChannelConfig(), createdAt: DateTime.now(), memberCount: 1, - cooldown: 0, ), ); From 96c037565b7b9052f48538964b522401f5295b95 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 6 Oct 2021 09:56:02 +0200 Subject: [PATCH 146/148] fix(ui): update sdk contraints to reflect the code --- packages/stream_chat_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index ef714445..6d067ed0 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -6,7 +6,7 @@ repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.14.0 <3.0.0' flutter: ">=1.17.0" dependencies: From cdcaf0a284ef03750bb7c4f9397c3e815fac6aba Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 6 Oct 2021 09:58:10 +0200 Subject: [PATCH 147/148] chore(ui): update changelog --- packages/stream_chat_flutter/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 5ac322df..ffc30572 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## Upcoming + +- Updated Dart SDK constraints to `>=2.14.0 <3.0.0` + ## 3.1.1 - Updated `stream_chat_flutter_core` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). @@ -5,7 +9,7 @@ 🐞 Fixed --[[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. +- [[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. - Fixed floating date stream subscription causing "Bad state: stream has already been listened.” error. - Fixed `String` capitalize extension not working on empty strings. From 9371004843327a069bdc815c3e0e6048da26eb26 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 6 Oct 2021 17:47:29 +0530 Subject: [PATCH 148/148] fix for example --- packages/stream_chat_flutter/example/lib/tutorial_part_6.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart index 2fbdcbee..af59d300 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart @@ -34,7 +34,7 @@ Future main() async { await client.connectUser( User(id: 'super-band-9'), - '''eyJ0eXAiO«iJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', ); runApp(