mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +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
 | 
			
		||||
  Future<int> createConversation(ConversationSettings settings) async {
 | 
			
		||||
 | 
			
		||||
    final response = await APIRequest(
 | 
			
		||||
      uri: "conversations/create",
 | 
			
		||||
      needLogin: true,
 | 
			
		||||
      args: {
 | 
			
		||||
    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();
 | 
			
		||||
    }).exec();
 | 
			
		||||
 | 
			
		||||
    if (response.code != 200) return -1;
 | 
			
		||||
 | 
			
		||||
    return response.getObject()["conversationID"];
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// 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
 | 
			
		||||
  ///
 | 
			
		||||
  /// 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()
 | 
			
		||||
        }).exec();
 | 
			
		||||
 | 
			
		||||
    if (response.code != 200) return null;
 | 
			
		||||
    return await _parseConversationMessageFromServer(conversationID, response);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Parse the response of the server
 | 
			
		||||
    ConversationMessagesList list = ConversationMessagesList();
 | 
			
		||||
    response.getArray().forEach((f) {
 | 
			
		||||
      list.add(ConversationMessage(
 | 
			
		||||
          id: f["ID"],
 | 
			
		||||
          conversationID: conversationID,
 | 
			
		||||
          userID: f["ID_user"],
 | 
			
		||||
          timeInsert: f["time_insert"],
 | 
			
		||||
          message: f["message"],
 | 
			
		||||
          imageURL: f["image_path"]));
 | 
			
		||||
    });
 | 
			
		||||
  /// Get older messages for a given conversation from an online source
 | 
			
		||||
  Future<ConversationMessagesList> getOlderMessages({
 | 
			
		||||
    @required int conversationID,
 | 
			
		||||
    @required int oldestMessagesID,
 | 
			
		||||
    int limit = 15,
 | 
			
		||||
  }) async {
 | 
			
		||||
    // Perform the request online
 | 
			
		||||
    final response = await APIRequest(
 | 
			
		||||
        uri: "conversations/get_older_messages",
 | 
			
		||||
        needLogin: true,
 | 
			
		||||
        args: {
 | 
			
		||||
          "conversationID": conversationID.toString(),
 | 
			
		||||
          "oldest_message_id": oldestMessagesID.toString(),
 | 
			
		||||
          "limit": limit.toString()
 | 
			
		||||
        }).exec();
 | 
			
		||||
 | 
			
		||||
    // Save messages in the cache
 | 
			
		||||
    _conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
 | 
			
		||||
 | 
			
		||||
    return list;
 | 
			
		||||
    return await _parseConversationMessageFromServer(conversationID, response);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Get new messages for a given conversation
 | 
			
		||||
@@ -237,4 +256,19 @@ class ConversationsHelper {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    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/models/new_conversation_message.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/intl_utils.dart';
 | 
			
		||||
import 'package:comunic/utils/list_utils.dart';
 | 
			
		||||
@@ -17,6 +18,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
enum ErrorLevel { NONE, MINOR, MAJOR }
 | 
			
		||||
enum _OlderMessagesLevel { NONE, LOADING, NO_MORE_AVAILABLE }
 | 
			
		||||
 | 
			
		||||
class ConversationScreen extends StatefulWidget {
 | 
			
		||||
  final int conversationID;
 | 
			
		||||
@@ -42,6 +44,14 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
  bool _isSendingMessage = false;
 | 
			
		||||
  TextEditingController _textEditingController = TextEditingController();
 | 
			
		||||
  Timer _refreshTime;
 | 
			
		||||
  ScrollWatcher _scrollController;
 | 
			
		||||
  _OlderMessagesLevel _loadingOlderMessages = _OlderMessagesLevel.NONE;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _scrollController = ScrollWatcher(onReachBottom: _loadOlderMessages);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void didChangeDependencies() {
 | 
			
		||||
@@ -59,6 +69,10 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
 | 
			
		||||
  void _setSending(bool sending) => setState(() => _isSendingMessage = sending);
 | 
			
		||||
 | 
			
		||||
  void _setLoadingOlderMessagesState(_OlderMessagesLevel state) => setState(() {
 | 
			
		||||
        _loadingOlderMessages = state;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  /// Method called when an error occurred while loading messages
 | 
			
		||||
  void _errorLoading() {
 | 
			
		||||
    _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR);
 | 
			
		||||
@@ -89,6 +103,47 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
    // anything (we wait for the online request)
 | 
			
		||||
    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
 | 
			
		||||
    final usersToGet =
 | 
			
		||||
        findMissingFromList(_usersInfo.usersID, messages.getUsersID());
 | 
			
		||||
@@ -204,10 +259,19 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
    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
 | 
			
		||||
  Widget _buildMessagesList() {
 | 
			
		||||
    return Expanded(
 | 
			
		||||
      child: ListView.builder(
 | 
			
		||||
          controller: _scrollController,
 | 
			
		||||
          reverse: true,
 | 
			
		||||
          itemCount: _messages.length,
 | 
			
		||||
          itemBuilder: (c, i) {
 | 
			
		||||
@@ -221,7 +285,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Send message from
 | 
			
		||||
  /// Send message form
 | 
			
		||||
  Widget _buildSendMessageForm() {
 | 
			
		||||
    return new Container(
 | 
			
		||||
      margin: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
			
		||||
@@ -244,6 +308,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
          // Message area
 | 
			
		||||
          new Flexible(
 | 
			
		||||
            child: new TextField(
 | 
			
		||||
              keyboardType: TextInputType.text,
 | 
			
		||||
              maxLines: null,
 | 
			
		||||
              maxLength: 200,
 | 
			
		||||
              maxLengthEnforced: true,
 | 
			
		||||
@@ -302,10 +367,17 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
			
		||||
        Container(
 | 
			
		||||
          child: _error == ErrorLevel.MINOR ? _buildError() : null,
 | 
			
		||||
        ),
 | 
			
		||||
        Container(
 | 
			
		||||
          child: _loadingOlderMessages == _OlderMessagesLevel.LOADING
 | 
			
		||||
              ? _buildLoadingOlderMessage()
 | 
			
		||||
              : null,
 | 
			
		||||
        ),
 | 
			
		||||
        _buildMessagesList(),
 | 
			
		||||
        Divider(),
 | 
			
		||||
        _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