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

Can update a conversation message

This commit is contained in:
Pierre HUBERT 2019-05-04 10:24:38 +02:00
parent c8b68e71aa
commit 62125d7c3d
5 changed files with 151 additions and 21 deletions

View File

@ -292,6 +292,13 @@ class ConversationsHelper {
lastMessageID: lastMessageID);
}
/// Get a single conversation message from the local database
///
/// Returns the message if found or null in case of failure
Future<ConversationMessage> getSingleMessageFromCache(int messageID) async {
return await _conversationMessagesDatabaseHelper.get(messageID);
}
/// Send a new message to the server
Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
final request = APIRequest(
@ -320,27 +327,34 @@ class ConversationsHelper {
return SendMessageResult.SUCCESS;
}
/// Update a message content
Future<bool> updateMessage(int id, String newContent) async {
final response = await APIRequest(
uri: "conversations/updateMessage",
needLogin: true,
args: {"messageID": id.toString(), "content": newContent}).exec();
if (response.code != 200) return false;
// Update the message content locally
return await _conversationMessagesDatabaseHelper.updateMessageContent(
id: id, newContent: newContent);
}
/// Delete permanently a message specified by its [id]
Future<bool> deleteMessage(int id) async {
// Delete the message online
final response = await APIRequest(
uri: "conversations/deleteMessage",
needLogin: true,
args: {
"messageID": id.toString()
}
).exec();
if(response.code != 200) return false;
args: {"messageID": id.toString()}).exec();
if (response.code != 200) return false;
// Delete the message locally
return await _conversationMessagesDatabaseHelper.delete(id);
}
/// Turn an API response into a ConversationMessage object
ConversationMessage _apiToConversationMessage({
@required int conversationID,

View File

@ -2,6 +2,7 @@ import 'package:comunic/helpers/database/database_contract.dart';
import 'package:comunic/helpers/database/model_database_helper.dart';
import 'package:comunic/lists/conversation_messages_list.dart';
import 'package:comunic/models/conversation_message.dart';
import 'package:meta/meta.dart';
/// Conversation messages database helper
///
@ -34,4 +35,26 @@ class ConversationMessagesDatabaseHelper
finalList.addAll(list);
return finalList;
}
/// Update the content of a message
Future<bool> updateMessageContent({
@required int id,
@required String newContent,
}) async {
assert(id != null);
assert(newContent != null);
final message = await get(id);
if(message == null)
return false;
// Update the conversation message using the map
final map = message.toMap();
map[ConversationsMessagesTableContract.C_MESSAGE] = newContent;
await insertOrUpdate(ConversationMessage.fromMap(map));
return true; // Success
}
}

View File

@ -289,6 +289,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
userInfo: _usersInfo.getUser(_messages[i].userID),
isLastMessage: _isLastMessage(i),
isFirstMessage: _isFirstMessage(i),
onRequestMessageUpdate: _updateMessage,
onRequestMessageDelete: _deleteMessage,
);
}),
@ -389,6 +390,33 @@ class _ConversationScreenState extends State<ConversationScreen> {
);
}
/// Request message content update
Future<void> _updateMessage(ConversationMessage message) async {
final newContent = await askUserString(
context: context,
title: tr("Update message"),
message: tr("Please enter new message content:"),
defaultValue: message.message,
hint: tr("New message"));
if (newContent == null) return;
if (!await _conversationsHelper.updateMessage(message.id, newContent)) {
showSimpleSnack(context, tr("Could not update message content!"));
return;
}
// Get the new version of the conversation message
final newMessage =
await _conversationsHelper.getSingleMessageFromCache(message.id);
setState(() {
final index = _messages.indexOf(message);
_messages.insert(index, newMessage);
_messages.removeAt(index + 1);
});
}
/// Request message deletion
Future<void> _deleteMessage(ConversationMessage message) async {
final choice = await showDialog<bool>(
@ -417,11 +445,10 @@ class _ConversationScreenState extends State<ConversationScreen> {
),
);
if(choice == null || !choice)
return;
if (choice == null || !choice) return;
// Execute the request
if(!await _conversationsHelper.deleteMessage(message.id))
if (!await _conversationsHelper.deleteMessage(message.id))
showSimpleSnack(context, tr("Could not delete conversation message!"));
// Remove the message from the list

View File

@ -12,8 +12,9 @@ import 'package:flutter/material.dart';
///
/// @author Pierre HUBERT
enum _MenuChoices { DELETE }
enum _MenuChoices { DELETE, REQUEST_UPDATE_CONTENT }
typedef OnRequestMessageUpdate = void Function(ConversationMessage);
typedef OnRequestMessageDelete = void Function(ConversationMessage);
class ConversationMessageTile extends StatelessWidget {
@ -21,6 +22,7 @@ class ConversationMessageTile extends StatelessWidget {
final User userInfo;
final bool isLastMessage;
final bool isFirstMessage;
final OnRequestMessageUpdate onRequestMessageUpdate;
final OnRequestMessageDelete onRequestMessageDelete;
const ConversationMessageTile({
@ -29,11 +31,13 @@ class ConversationMessageTile extends StatelessWidget {
@required this.userInfo,
@required this.isLastMessage,
@required this.isFirstMessage,
@required this.onRequestMessageUpdate,
@required this.onRequestMessageDelete,
}) : assert(message != null),
assert(userInfo != null),
assert(isLastMessage != null),
assert(isFirstMessage != null),
assert(onRequestMessageUpdate != null),
assert(onRequestMessageDelete != null),
super(key: key);
@ -47,6 +51,14 @@ class ConversationMessageTile extends StatelessWidget {
width: 35.0,
),
itemBuilder: (c) => [
// Update message content
PopupMenuItem(
enabled: message.isOwner,
value: _MenuChoices.REQUEST_UPDATE_CONTENT,
child: Text(tr("Update")),
),
// Delete the message
PopupMenuItem(
enabled: message.isOwner,
value: _MenuChoices.DELETE,
@ -256,12 +268,14 @@ class ConversationMessageTile extends StatelessWidget {
/// Process menu choice
void _menuOptionSelected(_MenuChoices value) {
switch (value) {
case _MenuChoices.REQUEST_UPDATE_CONTENT:
onRequestMessageUpdate(message);
break;
switch(value){
case _MenuChoices.DELETE:
onRequestMessageDelete(message);
break;
}
}
}

View File

@ -1,7 +1,10 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
/// User interface utilities
///
/// @author Pierre HUBERT
/// Build centered progress bar
Widget buildCenteredProgressBar() {
@ -52,17 +55,66 @@ Widget buildErrorCard(String message, {List<Widget> actions}) {
/// Show an image with a given [url] in full screen
void showImageFullScreen(BuildContext context, String url) {
Navigator.of(context).push(MaterialPageRoute(builder: (c) {
// TODO : add better support later
return CachedNetworkImage(
imageUrl: url,
);
}));
}
/// Show simple snack
void showSimpleSnack(BuildContext context, String message) {
Scaffold.of(context).showSnackBar(SnackBar(content: Text(message)));
}
/// Show an alert dialog to ask the user to enter a string
Future<String> askUserString({
@required BuildContext context,
@required String title,
@required String message,
@required String defaultValue,
@required String hint,
}) async {
assert(context != null);
assert(title != null);
assert(message != null);
assert(defaultValue != null);
assert(hint != null);
TextEditingController controller = TextEditingController(text: defaultValue);
final confirm = await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: Text(title),
content: Column(
children: <Widget>[
Text(message),
TextField(
controller: controller,
maxLines: null,
maxLength: 200,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: hint,
alignLabelWithHint: true,
),
)
],
),
actions: <Widget>[
FlatButton(
child: Text(tr("Cancel").toUpperCase()),
onPressed: () => Navigator.pop(c, false),
),
FlatButton(
child: Text(tr("OK")),
onPressed: () => Navigator.pop(c, true),
),
],
));
if (confirm == null || !confirm) return null;
return controller.text;
}