mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-03 19:54:12 +00:00 
			
		
		
		
	Can update a conversation message
This commit is contained in:
		@@ -292,6 +292,13 @@ class ConversationsHelper {
 | 
				
			|||||||
              lastMessageID: lastMessageID);
 | 
					              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
 | 
					  /// Send a new message to the server
 | 
				
			||||||
  Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
 | 
					  Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
 | 
				
			||||||
    final request = APIRequest(
 | 
					    final request = APIRequest(
 | 
				
			||||||
@@ -320,27 +327,34 @@ class ConversationsHelper {
 | 
				
			|||||||
    return SendMessageResult.SUCCESS;
 | 
					    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]
 | 
					  /// Delete permanently a message specified by its [id]
 | 
				
			||||||
  Future<bool> deleteMessage(int id) async {
 | 
					  Future<bool> deleteMessage(int id) async {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Delete the message online
 | 
					    // Delete the message online
 | 
				
			||||||
    final response = await APIRequest(
 | 
					    final response = await APIRequest(
 | 
				
			||||||
        uri: "conversations/deleteMessage",
 | 
					        uri: "conversations/deleteMessage",
 | 
				
			||||||
        needLogin: true,
 | 
					        needLogin: true,
 | 
				
			||||||
      args: {
 | 
					        args: {"messageID": id.toString()}).exec();
 | 
				
			||||||
        "messageID": id.toString()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    ).exec();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.code != 200) return false;
 | 
					    if (response.code != 200) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Delete the message locally
 | 
					    // Delete the message locally
 | 
				
			||||||
    return await _conversationMessagesDatabaseHelper.delete(id);
 | 
					    return await _conversationMessagesDatabaseHelper.delete(id);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Turn an API response into a ConversationMessage object
 | 
					  /// Turn an API response into a ConversationMessage object
 | 
				
			||||||
  ConversationMessage _apiToConversationMessage({
 | 
					  ConversationMessage _apiToConversationMessage({
 | 
				
			||||||
    @required int conversationID,
 | 
					    @required int conversationID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import 'package:comunic/helpers/database/database_contract.dart';
 | 
				
			|||||||
import 'package:comunic/helpers/database/model_database_helper.dart';
 | 
					import 'package:comunic/helpers/database/model_database_helper.dart';
 | 
				
			||||||
import 'package:comunic/lists/conversation_messages_list.dart';
 | 
					import 'package:comunic/lists/conversation_messages_list.dart';
 | 
				
			||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Conversation messages database helper
 | 
					/// Conversation messages database helper
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@@ -34,4 +35,26 @@ class ConversationMessagesDatabaseHelper
 | 
				
			|||||||
    finalList.addAll(list);
 | 
					    finalList.addAll(list);
 | 
				
			||||||
    return finalList;
 | 
					    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
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -289,6 +289,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
              userInfo: _usersInfo.getUser(_messages[i].userID),
 | 
					              userInfo: _usersInfo.getUser(_messages[i].userID),
 | 
				
			||||||
              isLastMessage: _isLastMessage(i),
 | 
					              isLastMessage: _isLastMessage(i),
 | 
				
			||||||
              isFirstMessage: _isFirstMessage(i),
 | 
					              isFirstMessage: _isFirstMessage(i),
 | 
				
			||||||
 | 
					              onRequestMessageUpdate: _updateMessage,
 | 
				
			||||||
              onRequestMessageDelete: _deleteMessage,
 | 
					              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
 | 
					  /// Request message deletion
 | 
				
			||||||
  Future<void> _deleteMessage(ConversationMessage message) async {
 | 
					  Future<void> _deleteMessage(ConversationMessage message) async {
 | 
				
			||||||
    final choice = await showDialog<bool>(
 | 
					    final choice = await showDialog<bool>(
 | 
				
			||||||
@@ -417,8 +445,7 @@ class _ConversationScreenState extends State<ConversationScreen> {
 | 
				
			|||||||
          ),
 | 
					          ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(choice == null || !choice)
 | 
					    if (choice == null || !choice) return;
 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Execute the request
 | 
					    // Execute the request
 | 
				
			||||||
    if (!await _conversationsHelper.deleteMessage(message.id))
 | 
					    if (!await _conversationsHelper.deleteMessage(message.id))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,9 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum _MenuChoices { DELETE }
 | 
					enum _MenuChoices { DELETE, REQUEST_UPDATE_CONTENT }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef OnRequestMessageUpdate = void Function(ConversationMessage);
 | 
				
			||||||
typedef OnRequestMessageDelete = void Function(ConversationMessage);
 | 
					typedef OnRequestMessageDelete = void Function(ConversationMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConversationMessageTile extends StatelessWidget {
 | 
					class ConversationMessageTile extends StatelessWidget {
 | 
				
			||||||
@@ -21,6 +22,7 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
  final User userInfo;
 | 
					  final User userInfo;
 | 
				
			||||||
  final bool isLastMessage;
 | 
					  final bool isLastMessage;
 | 
				
			||||||
  final bool isFirstMessage;
 | 
					  final bool isFirstMessage;
 | 
				
			||||||
 | 
					  final OnRequestMessageUpdate onRequestMessageUpdate;
 | 
				
			||||||
  final OnRequestMessageDelete onRequestMessageDelete;
 | 
					  final OnRequestMessageDelete onRequestMessageDelete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const ConversationMessageTile({
 | 
					  const ConversationMessageTile({
 | 
				
			||||||
@@ -29,11 +31,13 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
    @required this.userInfo,
 | 
					    @required this.userInfo,
 | 
				
			||||||
    @required this.isLastMessage,
 | 
					    @required this.isLastMessage,
 | 
				
			||||||
    @required this.isFirstMessage,
 | 
					    @required this.isFirstMessage,
 | 
				
			||||||
 | 
					    @required this.onRequestMessageUpdate,
 | 
				
			||||||
    @required this.onRequestMessageDelete,
 | 
					    @required this.onRequestMessageDelete,
 | 
				
			||||||
  })  : assert(message != null),
 | 
					  })  : assert(message != null),
 | 
				
			||||||
        assert(userInfo != null),
 | 
					        assert(userInfo != null),
 | 
				
			||||||
        assert(isLastMessage != null),
 | 
					        assert(isLastMessage != null),
 | 
				
			||||||
        assert(isFirstMessage != null),
 | 
					        assert(isFirstMessage != null),
 | 
				
			||||||
 | 
					        assert(onRequestMessageUpdate != null),
 | 
				
			||||||
        assert(onRequestMessageDelete != null),
 | 
					        assert(onRequestMessageDelete != null),
 | 
				
			||||||
        super(key: key);
 | 
					        super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,6 +51,14 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
          width: 35.0,
 | 
					          width: 35.0,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        itemBuilder: (c) => [
 | 
					        itemBuilder: (c) => [
 | 
				
			||||||
 | 
					              // Update message content
 | 
				
			||||||
 | 
					              PopupMenuItem(
 | 
				
			||||||
 | 
					                enabled: message.isOwner,
 | 
				
			||||||
 | 
					                value: _MenuChoices.REQUEST_UPDATE_CONTENT,
 | 
				
			||||||
 | 
					                child: Text(tr("Update")),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Delete the message
 | 
				
			||||||
              PopupMenuItem(
 | 
					              PopupMenuItem(
 | 
				
			||||||
                enabled: message.isOwner,
 | 
					                enabled: message.isOwner,
 | 
				
			||||||
                value: _MenuChoices.DELETE,
 | 
					                value: _MenuChoices.DELETE,
 | 
				
			||||||
@@ -256,12 +268,14 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Process menu choice
 | 
					  /// Process menu choice
 | 
				
			||||||
  void _menuOptionSelected(_MenuChoices value) {
 | 
					  void _menuOptionSelected(_MenuChoices value) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    switch (value) {
 | 
					    switch (value) {
 | 
				
			||||||
 | 
					      case _MenuChoices.REQUEST_UPDATE_CONTENT:
 | 
				
			||||||
 | 
					        onRequestMessageUpdate(message);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case _MenuChoices.DELETE:
 | 
					      case _MenuChoices.DELETE:
 | 
				
			||||||
        onRequestMessageDelete(message);
 | 
					        onRequestMessageDelete(message);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
import 'package:cached_network_image/cached_network_image.dart';
 | 
					import 'package:cached_network_image/cached_network_image.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// User interface utilities
 | 
					/// User interface utilities
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Build centered progress bar
 | 
					/// Build centered progress bar
 | 
				
			||||||
Widget buildCenteredProgressBar() {
 | 
					Widget buildCenteredProgressBar() {
 | 
				
			||||||
@@ -52,17 +55,66 @@ Widget buildErrorCard(String message, {List<Widget> actions}) {
 | 
				
			|||||||
/// Show an image with a given [url] in full screen
 | 
					/// Show an image with a given [url] in full screen
 | 
				
			||||||
void showImageFullScreen(BuildContext context, String url) {
 | 
					void showImageFullScreen(BuildContext context, String url) {
 | 
				
			||||||
  Navigator.of(context).push(MaterialPageRoute(builder: (c) {
 | 
					  Navigator.of(context).push(MaterialPageRoute(builder: (c) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO : add better support later
 | 
					    // TODO : add better support later
 | 
				
			||||||
    return CachedNetworkImage(
 | 
					    return CachedNetworkImage(
 | 
				
			||||||
      imageUrl: url,
 | 
					      imageUrl: url,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }));
 | 
					  }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Show simple snack
 | 
					/// Show simple snack
 | 
				
			||||||
void showSimpleSnack(BuildContext context, String message) {
 | 
					void showSimpleSnack(BuildContext context, String message) {
 | 
				
			||||||
  Scaffold.of(context).showSnackBar(SnackBar(content: Text(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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user