LCOV - code coverage report
Current view: top level - src - message_list_core.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 50 58 86.2 %
Date: 2021-05-27 10:59:48 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:flutter/cupertino.dart';
       4             : import 'package:flutter/foundation.dart';
       5             : import 'package:flutter/material.dart';
       6             : import 'package:stream_chat/stream_chat.dart';
       7             : import 'package:stream_chat_flutter_core/src/stream_channel.dart';
       8             : import 'package:stream_chat_flutter_core/src/typedef.dart';
       9             : 
      10             : /// [MessageListCore] is a simplified class that allows fetching a list of
      11             : /// messages while exposing UI builders.
      12             : ///
      13             : /// A [MessageListController] is used to paginate data.
      14             : ///
      15             : /// ```dart
      16             : /// class ChannelPage extends StatelessWidget {
      17             : ///   const ChannelPage({
      18             : ///     Key key,
      19             : ///   }) : super(key: key);
      20             : ///
      21             : ///   @override
      22             : ///   Widget build(BuildContext context) {
      23             : ///     return Scaffold(
      24             : ///       body: Column(
      25             : ///         children: <Widget>[
      26             : ///           Expanded(
      27             : ///             child: MessageListCore(
      28             : ///         emptyBuilder: (context) {
      29             : ///           return Center(
      30             : ///             child: Text('Nothing here...'),
      31             : ///           );
      32             : ///         },
      33             : ///         loadingBuilder: (context) {
      34             : ///           return Center(
      35             : ///             child: CircularProgressIndicator(),
      36             : ///           );
      37             : ///         },
      38             : ///         messageListBuilder: (context, list) {
      39             : ///           return MessagesPage(list);
      40             : ///         },
      41             : ///         errorWidgetBuilder: (context, err) {
      42             : ///           return Center(
      43             : ///             child: Text('Error'),
      44             : ///           );
      45             : ///         },
      46             : ///             ),
      47             : ///           ),
      48             : ///         ],
      49             : ///       ),
      50             : ///     );
      51             : ///   }
      52             : /// }
      53             : /// ```
      54             : ///
      55             : ///
      56             : /// Make sure to have a [StreamChannel] ancestor in order to provide the
      57             : /// information about the channels.
      58             : ///
      59             : /// The widget uses a [ListView.custom] to render the list of channels.
      60             : ///
      61             : class MessageListCore extends StatefulWidget {
      62             :   /// Instantiate a new [MessageListView].
      63           1 :   const MessageListCore({
      64             :     Key? key,
      65             :     required this.loadingBuilder,
      66             :     required this.emptyBuilder,
      67             :     required this.messageListBuilder,
      68             :     required this.errorWidgetBuilder,
      69             :     this.showScrollToBottom = true,
      70             :     this.parentMessage,
      71             :     this.messageListController,
      72             :     this.messageFilter,
      73           1 :   }) : super(key: key);
      74             : 
      75             :   /// A [MessageListController] allows pagination.
      76             :   /// Use [ChannelListController.paginateData] pagination.
      77             :   final MessageListController? messageListController;
      78             : 
      79             :   /// Function called when messages are fetched
      80             :   final Widget Function(BuildContext, List<Message>) messageListBuilder;
      81             : 
      82             :   /// Function used to build a loading widget
      83             :   final WidgetBuilder loadingBuilder;
      84             : 
      85             :   /// Function used to build an empty widget
      86             :   final WidgetBuilder emptyBuilder;
      87             : 
      88             :   /// Callback triggered when an error occurs while performing the given
      89             :   /// request.
      90             :   ///
      91             :   /// This parameter can be used to display an error message to users in the
      92             :   /// event of a connection failure.
      93             :   final ErrorBuilder errorWidgetBuilder;
      94             : 
      95             :   /// If true will show a scroll to bottom message when there are new messages
      96             :   /// and the scroll offset is not zero.
      97             :   final bool showScrollToBottom;
      98             : 
      99             :   /// If the current message belongs to a `thread`, this property represents the
     100             :   /// first message or the parent of the conversation.
     101             :   final Message? parentMessage;
     102             : 
     103             :   /// Predicate used to filter messages
     104             :   final bool Function(Message)? messageFilter;
     105             : 
     106           1 :   @override
     107           1 :   MessageListCoreState createState() => MessageListCoreState();
     108             : }
     109             : 
     110             : /// The current state of the [MessageListCore].
     111             : class MessageListCoreState extends State<MessageListCore> {
     112             :   StreamChannelState? _streamChannel;
     113             : 
     114           5 :   bool get _upToDate => _streamChannel!.channel.state?.isUpToDate ?? true;
     115             : 
     116           3 :   bool get _isThreadConversation => widget.parentMessage != null;
     117             : 
     118           6 :   OwnUser? get _currentUser => _streamChannel!.channel.client.state.user;
     119             : 
     120             :   var _messages = <Message>[];
     121             : 
     122           1 :   @override
     123             :   Widget build(BuildContext context) {
     124           1 :     final messagesStream = _isThreadConversation
     125           4 :         ? _streamChannel!.channel.state?.threadsStream
     126           6 :             .where((threads) => threads.containsKey(widget.parentMessage!.id))
     127           6 :             .map((threads) => threads[widget.parentMessage!.id])
     128           4 :         : _streamChannel!.channel.state?.messagesStream;
     129             : 
     130           1 :     bool defaultFilter(Message m) {
     131           5 :       final isMyMessage = m.user?.id == _currentUser?.id;
     132           4 :       final isDeletedOrShadowed = m.isDeleted == true || m.shadowed == true;
     133             :       if (isDeletedOrShadowed && !isMyMessage) return false;
     134             :       return true;
     135             :     }
     136             : 
     137           1 :     return StreamBuilder<List<Message>?>(
     138           2 :       stream: messagesStream?.map((messages) =>
     139           4 :           messages?.where(widget.messageFilter ?? defaultFilter).toList(
     140             :                 growable: false,
     141             :               )),
     142           1 :       builder: (context, snapshot) {
     143           1 :         if (snapshot.hasError) {
     144           4 :           return widget.errorWidgetBuilder(context, snapshot.error!);
     145           1 :         } else if (!snapshot.hasData) {
     146           3 :           return widget.loadingBuilder(context);
     147             :         } else {
     148             :           final messageList =
     149           3 :               snapshot.data?.reversed.toList(growable: false) ?? [];
     150           2 :           if (messageList.isEmpty && !_isThreadConversation) {
     151           1 :             if (_upToDate) {
     152           3 :               return widget.emptyBuilder(context);
     153             :             }
     154             :           } else {
     155           1 :             _messages = messageList;
     156             :           }
     157           4 :           return widget.messageListBuilder(context, _messages);
     158             :         }
     159             :       },
     160             :     );
     161             :   }
     162             : 
     163             :   /// Fetches more messages with updated pagination and updates the widget.
     164             :   ///
     165             :   /// Optionally pass the fetch direction, defaults to [QueryDirection.top]
     166           1 :   Future<void> paginateData({
     167             :     QueryDirection direction = QueryDirection.top,
     168             :   }) {
     169           1 :     if (!_isThreadConversation) {
     170           2 :       return _streamChannel!.queryMessages(direction: direction);
     171             :     } else {
     172           0 :       return _streamChannel!.getReplies(widget.parentMessage!.id);
     173             :     }
     174             :   }
     175             : 
     176           1 :   @override
     177             :   void didChangeDependencies() {
     178           2 :     final newStreamChannel = StreamChannel.of(context);
     179             : 
     180           2 :     if (newStreamChannel != _streamChannel) {
     181           2 :       if (_streamChannel == null /*only first time*/ && _isThreadConversation) {
     182           4 :         newStreamChannel.getReplies(widget.parentMessage!.id);
     183             :       }
     184           1 :       _streamChannel = newStreamChannel;
     185             :     }
     186             : 
     187           1 :     super.didChangeDependencies();
     188             :   }
     189             : 
     190           0 :   @override
     191             :   void didUpdateWidget(covariant MessageListCore oldWidget) {
     192           0 :     super.didUpdateWidget(oldWidget);
     193             : 
     194           0 :     if (widget.messageListController != oldWidget.messageListController) {
     195           0 :       _setupController();
     196             :     }
     197             : 
     198           0 :     if (widget.parentMessage?.id != widget.parentMessage?.id) {
     199           0 :       if (_isThreadConversation) {
     200           0 :         _streamChannel!.getReplies(widget.parentMessage!.id);
     201             :       }
     202             :     }
     203             :   }
     204             : 
     205           1 :   @override
     206             :   void initState() {
     207           1 :     _setupController();
     208             : 
     209           1 :     super.initState();
     210             :   }
     211             : 
     212           1 :   void _setupController() {
     213           2 :     if (widget.messageListController != null) {
     214           4 :       widget.messageListController!.paginateData = paginateData;
     215             :     }
     216             :   }
     217             : 
     218           1 :   @override
     219             :   void dispose() {
     220           1 :     if (!_upToDate) {
     221           2 :       _streamChannel!.reloadChannel();
     222             :     }
     223           1 :     super.dispose();
     224             :   }
     225             : }
     226             : 
     227             : /// Controller used for paginating data in [ChannelListView]
     228             : class MessageListController {
     229             :   /// Call this function to load further data
     230             :   Future<void> Function({QueryDirection direction})? paginateData;
     231             : }

Generated by: LCOV version 1.14