mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-03 19:54:12 +00:00 
			
		
		
		
	Get older messages
This commit is contained in:
		@@ -29,21 +29,16 @@ class ConversationsHelper {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Return the ID of the newly created conversation or -1 in case of failure
 | 
					  /// Return the ID of the newly created conversation or -1 in case of failure
 | 
				
			||||||
  Future<int> createConversation(ConversationSettings settings) async {
 | 
					  Future<int> createConversation(ConversationSettings settings) async {
 | 
				
			||||||
 | 
					    final response =
 | 
				
			||||||
 | 
					        await APIRequest(uri: "conversations/create", needLogin: true, args: {
 | 
				
			||||||
 | 
					      "name": settings.hasName ? settings.name : "false",
 | 
				
			||||||
 | 
					      "follow": settings.following ? "true" : "false",
 | 
				
			||||||
 | 
					      "users": settings.members.join(",")
 | 
				
			||||||
 | 
					    }).exec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final response = await APIRequest(
 | 
					    if (response.code != 200) return -1;
 | 
				
			||||||
      uri: "conversations/create",
 | 
					 | 
				
			||||||
      needLogin: true,
 | 
					 | 
				
			||||||
      args: {
 | 
					 | 
				
			||||||
        "name" : settings.hasName ? settings.name : "false",
 | 
					 | 
				
			||||||
        "follow" : settings.following ? "true" : "false",
 | 
					 | 
				
			||||||
        "users": settings.members.join(",")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    ).exec();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(response.code != 200) return -1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return response.getObject()["conversationID"];
 | 
					    return response.getObject()["conversationID"];
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Download the list of conversations from the server
 | 
					  /// Download the list of conversations from the server
 | 
				
			||||||
@@ -156,6 +151,28 @@ class ConversationsHelper {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Parse a list of messages given by the server
 | 
				
			||||||
 | 
					  Future<ConversationMessagesList> _parseConversationMessageFromServer(
 | 
				
			||||||
 | 
					      int conversationID, APIResponse response) async {
 | 
				
			||||||
 | 
					    if (response.code != 200) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Parse the response of the server
 | 
				
			||||||
 | 
					    ConversationMessagesList list = ConversationMessagesList();
 | 
				
			||||||
 | 
					    response.getArray().forEach((f) {
 | 
				
			||||||
 | 
					      list.add(
 | 
				
			||||||
 | 
					        _apiToConversationMessage(
 | 
				
			||||||
 | 
					          conversationID: conversationID,
 | 
				
			||||||
 | 
					          map: f,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save messages in the cache
 | 
				
			||||||
 | 
					    _conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return list;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Refresh the list of messages of a conversation
 | 
					  /// Refresh the list of messages of a conversation
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Set [lastMessageID] to 0 to specify that we do not have any message of the
 | 
					  /// Set [lastMessageID] to 0 to specify that we do not have any message of the
 | 
				
			||||||
@@ -172,24 +189,26 @@ class ConversationsHelper {
 | 
				
			|||||||
          "last_message_id": lastMessageID.toString()
 | 
					          "last_message_id": lastMessageID.toString()
 | 
				
			||||||
        }).exec();
 | 
					        }).exec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.code != 200) return null;
 | 
					    return await _parseConversationMessageFromServer(conversationID, response);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Parse the response of the server
 | 
					  /// Get older messages for a given conversation from an online source
 | 
				
			||||||
    ConversationMessagesList list = ConversationMessagesList();
 | 
					  Future<ConversationMessagesList> getOlderMessages({
 | 
				
			||||||
    response.getArray().forEach((f) {
 | 
					    @required int conversationID,
 | 
				
			||||||
      list.add(ConversationMessage(
 | 
					    @required int oldestMessagesID,
 | 
				
			||||||
          id: f["ID"],
 | 
					    int limit = 15,
 | 
				
			||||||
          conversationID: conversationID,
 | 
					  }) async {
 | 
				
			||||||
          userID: f["ID_user"],
 | 
					    // Perform the request online
 | 
				
			||||||
          timeInsert: f["time_insert"],
 | 
					    final response = await APIRequest(
 | 
				
			||||||
          message: f["message"],
 | 
					        uri: "conversations/get_older_messages",
 | 
				
			||||||
          imageURL: f["image_path"]));
 | 
					        needLogin: true,
 | 
				
			||||||
    });
 | 
					        args: {
 | 
				
			||||||
 | 
					          "conversationID": conversationID.toString(),
 | 
				
			||||||
 | 
					          "oldest_message_id": oldestMessagesID.toString(),
 | 
				
			||||||
 | 
					          "limit": limit.toString()
 | 
				
			||||||
 | 
					        }).exec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Save messages in the cache
 | 
					    return await _parseConversationMessageFromServer(conversationID, response);
 | 
				
			||||||
    _conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return list;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get new messages for a given conversation
 | 
					  /// Get new messages for a given conversation
 | 
				
			||||||
@@ -237,4 +256,19 @@ class ConversationsHelper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return SendMessageResult.SUCCESS;
 | 
					    return SendMessageResult.SUCCESS;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Turn an API response into a ConversationMessage object
 | 
				
			||||||
 | 
					  ConversationMessage _apiToConversationMessage({
 | 
				
			||||||
 | 
					    @required int conversationID,
 | 
				
			||||||
 | 
					    @required Map<String, dynamic> map,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return ConversationMessage(
 | 
				
			||||||
 | 
					      id: map["ID"],
 | 
				
			||||||
 | 
					      conversationID: conversationID,
 | 
				
			||||||
 | 
					      userID: map["ID_user"],
 | 
				
			||||||
 | 
					      timeInsert: map["time_insert"],
 | 
				
			||||||
 | 
					      message: map["message"],
 | 
				
			||||||
 | 
					      imageURL: map["image_path"],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,4 +40,12 @@ class ConversationMessagesList extends ListBase<ConversationMessage> {
 | 
				
			|||||||
      if (message.id > lastMessageID) lastMessageID = message.id;
 | 
					      if (message.id > lastMessageID) lastMessageID = message.id;
 | 
				
			||||||
    return lastMessageID;
 | 
					    return lastMessageID;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get the ID of the first message present in this list
 | 
				
			||||||
 | 
					  int get firstMessageID {
 | 
				
			||||||
 | 
					    int firstMessageID = this[0].id;
 | 
				
			||||||
 | 
					    for (ConversationMessage message in this)
 | 
				
			||||||
 | 
					      if (message.id < firstMessageID) firstMessageID = message.id;
 | 
				
			||||||
 | 
					    return firstMessageID;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import 'package:comunic/lists/conversation_messages_list.dart';
 | 
				
			|||||||
import 'package:comunic/lists/users_list.dart';
 | 
					import 'package:comunic/lists/users_list.dart';
 | 
				
			||||||
import 'package:comunic/models/new_conversation_message.dart';
 | 
					import 'package:comunic/models/new_conversation_message.dart';
 | 
				
			||||||
import 'package:comunic/ui/tiles/conversation_message_tile.dart';
 | 
					import 'package:comunic/ui/tiles/conversation_message_tile.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/widgets/scroll_watcher.dart';
 | 
				
			||||||
import 'package:comunic/utils/files_utils.dart';
 | 
					import 'package:comunic/utils/files_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/list_utils.dart';
 | 
					import 'package:comunic/utils/list_utils.dart';
 | 
				
			||||||
@@ -17,6 +18,7 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ErrorLevel { NONE, MINOR, MAJOR }
 | 
					enum ErrorLevel { NONE, MINOR, MAJOR }
 | 
				
			||||||
 | 
					enum _OlderMessagesLevel { NONE, LOADING, NO_MORE_AVAILABLE }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConversationScreen extends StatefulWidget {
 | 
					class ConversationScreen extends StatefulWidget {
 | 
				
			||||||
  final int conversationID;
 | 
					  final int conversationID;
 | 
				
			||||||
@@ -42,6 +44,14 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
  bool _isSendingMessage = false;
 | 
					  bool _isSendingMessage = false;
 | 
				
			||||||
  TextEditingController _textEditingController = TextEditingController();
 | 
					  TextEditingController _textEditingController = TextEditingController();
 | 
				
			||||||
  Timer _refreshTime;
 | 
					  Timer _refreshTime;
 | 
				
			||||||
 | 
					  ScrollWatcher _scrollController;
 | 
				
			||||||
 | 
					  _OlderMessagesLevel _loadingOlderMessages = _OlderMessagesLevel.NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    _scrollController = ScrollWatcher(onReachBottom: _loadOlderMessages);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void didChangeDependencies() {
 | 
					  void didChangeDependencies() {
 | 
				
			||||||
@@ -59,6 +69,10 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  void _setSending(bool sending) => setState(() => _isSendingMessage = sending);
 | 
					  void _setSending(bool sending) => setState(() => _isSendingMessage = sending);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _setLoadingOlderMessagesState(_OlderMessagesLevel state) => setState(() {
 | 
				
			||||||
 | 
					        _loadingOlderMessages = state;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Method called when an error occurred while loading messages
 | 
					  /// Method called when an error occurred while loading messages
 | 
				
			||||||
  void _errorLoading() {
 | 
					  void _errorLoading() {
 | 
				
			||||||
    _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR);
 | 
					    _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR);
 | 
				
			||||||
@@ -89,6 +103,47 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
    // anything (we wait for the online request)
 | 
					    // anything (we wait for the online request)
 | 
				
			||||||
    if (messages.length == 0 && !online) return;
 | 
					    if (messages.length == 0 && !online) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await _applyNewMessages(messages);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get older messages
 | 
				
			||||||
 | 
					  Future<void> _loadOlderMessages() async {
 | 
				
			||||||
 | 
					    if (_loadingOlderMessages != _OlderMessagesLevel.NONE ||
 | 
				
			||||||
 | 
					        _messages == null ||
 | 
				
			||||||
 | 
					        _messages.length == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Let's start to load older messages
 | 
				
			||||||
 | 
					    _setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final messages = await _conversationsHelper.getOlderMessages(
 | 
				
			||||||
 | 
					        conversationID: widget.conversationID,
 | 
				
			||||||
 | 
					        oldestMessagesID: _messages.firstMessageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mark as not loading anymore
 | 
				
			||||||
 | 
					    _setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check for errors
 | 
				
			||||||
 | 
					    if (messages == null) {
 | 
				
			||||||
 | 
					      _errorLoading();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check if there is no more unread messages
 | 
				
			||||||
 | 
					    if (messages.length == 0) {
 | 
				
			||||||
 | 
					      _setLoadingOlderMessagesState(_OlderMessagesLevel.NO_MORE_AVAILABLE);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Apply the messages
 | 
				
			||||||
 | 
					    _applyNewMessages(messages);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Apply new messages [messages] must not be null
 | 
				
			||||||
 | 
					  Future<void> _applyNewMessages(ConversationMessagesList messages) async {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We ignore new messages once the area is no longer visible
 | 
				
			||||||
 | 
					    if(!this.mounted) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Then get information about users
 | 
					    //Then get information about users
 | 
				
			||||||
    final usersToGet =
 | 
					    final usersToGet =
 | 
				
			||||||
        findMissingFromList(_usersInfo.usersID, messages.getUsersID());
 | 
					        findMissingFromList(_usersInfo.usersID, messages.getUsersID());
 | 
				
			||||||
@@ -204,10 +259,19 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
    return buildErrorCard(tr("Could not load the list of messages!"));
 | 
					    return buildErrorCard(tr("Could not load the list of messages!"));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Widget shown when loading older messages
 | 
				
			||||||
 | 
					  Widget _buildLoadingOlderMessage() {
 | 
				
			||||||
 | 
					    return Container(
 | 
				
			||||||
 | 
					      padding: EdgeInsets.all(8.0),
 | 
				
			||||||
 | 
					      child: CircularProgressIndicator(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Messages list
 | 
					  /// Messages list
 | 
				
			||||||
  Widget _buildMessagesList() {
 | 
					  Widget _buildMessagesList() {
 | 
				
			||||||
    return Expanded(
 | 
					    return Expanded(
 | 
				
			||||||
      child: ListView.builder(
 | 
					      child: ListView.builder(
 | 
				
			||||||
 | 
					          controller: _scrollController,
 | 
				
			||||||
          reverse: true,
 | 
					          reverse: true,
 | 
				
			||||||
          itemCount: _messages.length,
 | 
					          itemCount: _messages.length,
 | 
				
			||||||
          itemBuilder: (c, i) {
 | 
					          itemBuilder: (c, i) {
 | 
				
			||||||
@@ -221,7 +285,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Send message from
 | 
					  /// Send message form
 | 
				
			||||||
  Widget _buildSendMessageForm() {
 | 
					  Widget _buildSendMessageForm() {
 | 
				
			||||||
    return new Container(
 | 
					    return new Container(
 | 
				
			||||||
      margin: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
					      margin: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
				
			||||||
@@ -244,6 +308,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
          // Message area
 | 
					          // Message area
 | 
				
			||||||
          new Flexible(
 | 
					          new Flexible(
 | 
				
			||||||
            child: new TextField(
 | 
					            child: new TextField(
 | 
				
			||||||
 | 
					              keyboardType: TextInputType.text,
 | 
				
			||||||
              maxLines: null,
 | 
					              maxLines: null,
 | 
				
			||||||
              maxLength: 200,
 | 
					              maxLength: 200,
 | 
				
			||||||
              maxLengthEnforced: true,
 | 
					              maxLengthEnforced: true,
 | 
				
			||||||
@@ -302,10 +367,17 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
        Container(
 | 
					        Container(
 | 
				
			||||||
          child: _error == ErrorLevel.MINOR ? _buildError() : null,
 | 
					          child: _error == ErrorLevel.MINOR ? _buildError() : null,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 | 
					        Container(
 | 
				
			||||||
 | 
					          child: _loadingOlderMessages == _OlderMessagesLevel.LOADING
 | 
				
			||||||
 | 
					              ? _buildLoadingOlderMessage()
 | 
				
			||||||
 | 
					              : null,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
        _buildMessagesList(),
 | 
					        _buildMessagesList(),
 | 
				
			||||||
        Divider(),
 | 
					        Divider(),
 | 
				
			||||||
        _buildSendMessageForm()
 | 
					        _buildSendMessageForm()
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								lib/ui/widgets/scroll_watcher.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/ui/widgets/scroll_watcher.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Scroll watcher
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef OnReachBottomCallback = void Function();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ScrollWatcher extends ScrollController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Callbacks
 | 
				
			||||||
 | 
					  OnReachBottomCallback onReachBottom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ScrollWatcher({this.onReachBottom}) {
 | 
				
			||||||
 | 
					    addListener(_updatePosition);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _updatePosition() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Refresh bottom position
 | 
				
			||||||
 | 
					    if(position.pixels.floor() == position.maxScrollExtent.floor())
 | 
				
			||||||
 | 
					      onReachBottom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user