mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 21:09:21 +00:00
Get older messages
This commit is contained in:
parent
b02315cf09
commit
d6e5b668cc
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user