1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 12:59:21 +00:00

Get older messages

This commit is contained in:
Pierre HUBERT 2019-04-27 18:29:30 +02:00
parent b02315cf09
commit d6e5b668cc
4 changed files with 170 additions and 29 deletions

View File

@ -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"],
);
}
} }

View File

@ -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;
}
} }

View File

@ -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()
], ],
); );
} }
} }

View 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();
}
}