diff --git a/Ring/Ring/Database/DBHelpers/InteractionDataHelper.swift b/Ring/Ring/Database/DBHelpers/InteractionDataHelper.swift
index 6cd723a9a562acf26f481dcb72fb489c472408e2..1fad9f333906f4f0bac76f3a5cc947c91ffd2aba 100644
--- a/Ring/Ring/Database/DBHelpers/InteractionDataHelper.swift
+++ b/Ring/Ring/Database/DBHelpers/InteractionDataHelper.swift
@@ -2,6 +2,7 @@
  *  Copyright (C) 2017-2019 Savoir-faire Linux Inc.
  *
  *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -165,6 +166,19 @@ final class InteractionDataHelper {
         }
     }
 
+    func delete (interactionId: Int64, dataBase: Connection) -> Bool {
+        let query = table.filter(id == interactionId)
+        do {
+            let deletedRows = try dataBase.run(query.delete())
+            guard deletedRows == 1 else {
+                return false
+            }
+            return true
+        } catch _ {
+            return false
+        }
+    }
+
     func selectInteraction (interactionId: Int64, dataBase: Connection) throws -> Interaction? {
         let query = table.filter(id == interactionId)
         let items = try dataBase.prepare(query)
diff --git a/Ring/Ring/Database/DBManager.swift b/Ring/Ring/Database/DBManager.swift
index ad937d1007f21a1c7bde114ffbd47270c95d75e2..200473c77a0196bd9db76595214751132e6dbd55 100644
--- a/Ring/Ring/Database/DBManager.swift
+++ b/Ring/Ring/Database/DBManager.swift
@@ -2,6 +2,7 @@
  *  Copyright (C) 2017-2019 Savoir-faire Linux Inc.
  *
  *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -142,6 +143,7 @@ enum DBBridgingError: Error {
     case updateIntercationFailed
     case deleteConversationFailed
     case getProfileFailed
+    case deleteMessageFailed
 }
 
 enum InteractionType: String {
@@ -393,6 +395,21 @@ class DBManager {
         }
     }
 
+    func deleteMessage(messagesId: Int64, accountId: String) -> Completable {
+        return Completable.create { [unowned self] completable in
+            if let dataBase = self.dbConnections.forAccount(account: accountId) {
+                if self.interactionHepler.delete(interactionId: messagesId, dataBase: dataBase) {
+                    completable(.completed)
+                } else {
+                    completable(.error(DBBridgingError.deleteMessageFailed))
+                }
+            } else {
+                completable(.error(DBBridgingError.deleteMessageFailed))
+            }
+            return Disposables.create { }
+        }
+    }
+
     func clearAllHistoryFor(accountId: String) -> Completable {
         return Completable.create { [unowned self] completable in
             do {
diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
index 1ac05c7526afd900874e581c21b6a7f1b72b74b2..b464d3da8a85b57eb76842d2782a4f34e1fa886b 100644
--- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
@@ -25,12 +25,15 @@
 import UIKit
 import Reusable
 import RxSwift
+import RxCocoa
 import ActiveLabel
 import SwiftyBeaver
 
 // swiftlint:disable type_body_length
 class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
 
+    // MARK: Properties
+
     let log = SwiftyBeaver.self
 
     @IBOutlet weak var avatarView: UIView!
@@ -69,7 +72,19 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
 
     var playerHeight = Variable<CGFloat>(0)
 
+    private(set) var messageId: Int64?
+    private var isCopyable: Bool = false
+    private let _deleteMessage = BehaviorRelay<Bool>(value: false)
+    var deleteMessage: Observable<Bool> { _deleteMessage.asObservable() }
+
+    private var longGestureRecognizer: UILongPressGestureRecognizer?
+
+    // MARK: prepareForReuse
+
     override func prepareForReuse() {
+        self.prepareForReuseLongGesture()
+        self.setCellTimeLabelVisibility(hide: true)
+
         if self.sendingIndicator != nil {
             self.sendingIndicator.stopAnimating()
         }
@@ -78,11 +93,21 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.transferProgressView.removeFromSuperview()
         self.playerView?.removeFromSuperview()
         self.composingMsg.removeFromSuperview()
-        playerHeight.value = 0
+        self.playerHeight.value = 0
         self.disposeBag = DisposeBag()
         super.prepareForReuse()
     }
 
+    private func prepareForReuseLongGesture() {
+        self.messageId = nil
+        self.isCopyable = false
+        self._deleteMessage.accept(false)
+        if let longGestureRecognizer = longGestureRecognizer {
+            self.bubble.removeGestureRecognizer(longGestureRecognizer)
+            self.longGestureRecognizer = nil
+        }
+    }
+
     func startProgressMonitor(_ item: MessageViewModel,
                               _ conversationViewModel: ConversationViewModel) {
         if self.outgoingImageProgressUpdater != nil {
@@ -145,7 +170,17 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         }
     }
 
-    func showCopyMenu() {
+    private func configureLongGesture(_ messageId: Int64, _ bubblePosition: BubblePosition, _ isTransfer: Bool) {
+        self.messageId = messageId
+        self.isCopyable = bubblePosition != .generated && !isTransfer
+
+        self.bubble.isUserInteractionEnabled = true
+        longGestureRecognizer = UILongPressGestureRecognizer()
+        longGestureRecognizer!.rx.event.bind(onNext: { [weak self] _ in self?.showCopyMenu() }).disposed(by: self.disposeBag)
+        self.bubble.addGestureRecognizer(longGestureRecognizer!)
+    }
+
+    private func showCopyMenu() {
         becomeFirstResponder()
         let menu = UIMenuController.shared
         if !menu.isMenuVisible {
@@ -154,40 +189,35 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         }
     }
 
-    func setup() {
-        let longGestureRecognizer = UILongPressGestureRecognizer()
-        self.messageLabel.isUserInteractionEnabled = true
-        self.messageLabel.addGestureRecognizer(longGestureRecognizer)
-        longGestureRecognizer.rx.event.bind(onNext: { [weak self] _ in
-            self?.showCopyMenu()
-        }).disposed(by: self.disposeBag)
-    }
-
     override func copy(_ sender: Any?) {
         UIPasteboard.general.string = self.messageLabel.text
         UIMenuController.shared.setMenuVisible(false, animated: true)
     }
 
+    override func delete(_ sender: Any?) {
+        _deleteMessage.accept(true)
+    }
+
     override var canBecomeFirstResponder: Bool {
         return true
     }
 
     override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
-        if action == #selector(UIResponderStandardEditActions.copy) {
-            return true
-        }
-        return false
+        return action == #selector(UIResponderStandardEditActions.copy) && self.isCopyable ||
+            action == #selector(UIResponderStandardEditActions.delete)
+    }
+
+    private func setCellTimeLabelVisibility(hide: Bool) {
+        self.timeLabel.isHidden = hide
+        self.leftDivider.isHidden = hide
+        self.rightDivider.isHidden = hide
     }
 
-    func formatCellTimeLabel(_ item: MessageViewModel) {
+    private func formatCellTimeLabel(_ item: MessageViewModel) {
         // hide for potentially reused cell
-        self.timeLabel.isHidden = true
-        self.leftDivider.isHidden = true
-        self.rightDivider.isHidden = true
+        self.setCellTimeLabelVisibility(hide: true)
 
-        if item.timeStringShown == nil {
-            return
-        }
+        if item.timeStringShown == nil { return }
 
         // setup the label
         self.timeLabel.text = item.timeStringShown
@@ -195,32 +225,26 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.timeLabel.font = UIFont.systemFont(ofSize: 12.0, weight: UIFont.Weight.medium)
 
         // show the time
-        self.timeLabel.isHidden = false
-        self.leftDivider.isHidden = false
-        self.rightDivider.isHidden = false
+        self.setCellTimeLabelVisibility(hide: false)
     }
 
     // swiftlint:disable cyclomatic_complexity
     func applyBubbleStyleToCell(_ items: [MessageViewModel]?, cellForRowAt indexPath: IndexPath) {
-        guard let items = items else {
-            return
-        }
+
+        guard let items = items else { return }
         let item = items[indexPath.row]
-        let type = item.bubblePosition()
-        var bubbleColor: UIColor
-        if item.isTransfer {
-            if item.content.containsOnlyEmoji {
-                bubbleColor = UIColor.jamiMsgCellEmoji
-            } else {
-                bubbleColor = type == .received ? UIColor.jamiMsgCellReceived : UIColor(hex: 0xcfebf5, alpha: 1.0)
-            }
-        } else {
+
+        let bubbleColor: UIColor = { (bubblePosition: BubblePosition) -> UIColor in
             if item.content.containsOnlyEmoji {
-                bubbleColor = UIColor.jamiMsgCellEmoji
+                return UIColor.jamiMsgCellEmoji
+            } else if bubblePosition == .received {
+                return UIColor.jamiMsgCellReceived
+            } else if item.isTransfer {
+                return UIColor(hex: 0xcfebf5, alpha: 1.0)
             } else {
-                bubbleColor = type == .received ? UIColor.jamiMsgCellReceived : UIColor.jamiMsgCellSent
+                return UIColor.jamiMsgCellSent
             }
-        }
+        }(item.bubblePosition())
 
         if item.isTransfer {
             self.messageLabel.enabledTypes = []
@@ -233,7 +257,6 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
             }
         } else {
             self.messageLabel.enabledTypes = [.url]
-            self.setup()
             self.messageLabel.setTextWithLineSpacing(withText: item.content, withLineSpacing: 2)
             self.messageLabel.handleURLTap { url in
                 let urlString = url.absoluteString
@@ -303,18 +326,17 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.bubbleViewMask?.backgroundColor = UIColor.jamiMsgBackground
         self.transferImageView.backgroundColor = UIColor.jamiMsgBackground
         buttonsHeightConstraint?.priority = UILayoutPriority(rawValue: 999.0)
-        guard let item = items?[indexPath.row] else {
-            return
-        }
+        guard let item = items?[indexPath.row] else { return }
 
         self.transferImageView.removeFromSuperview()
         self.playerView?.removeFromSuperview()
         self.composingMsg.removeFromSuperview()
-        playerHeight.value = 0
+        self.playerHeight.value = 0
         self.bubbleViewMask?.isHidden = true
 
         // hide/show time label
         self.formatCellTimeLabel(item)
+        self.configureLongGesture(item.message.messageId, item.bubblePosition(), item.isTransfer)
 
         if item.bubblePosition() == .generated {
             self.bubble.backgroundColor = UIColor.jamiMsgCellReceived
@@ -412,19 +434,7 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
                     .bind(to: self.failedStatusLabel.rx.isHidden)
                     .disposed(by: self.disposeBag)
                 if self.messageReadIndicator != nil {
-                    Observable<(Data?, String, Bool)>.combineLatest(conversationViewModel.profileImageData.asObservable(),
-                                                                      conversationViewModel.bestName.asObservable(),
-                                                                      item.displayReadIndicator.asObservable()) { ($0, $1, $2) }
-                        .observeOn(MainScheduler.instance)
-                        .startWith((conversationViewModel.profileImageData.value, conversationViewModel.userName.value, item.displayReadIndicator.value))
-                        .subscribe({ [weak self] profileData -> Void in
-                            guard let bestName = profileData.element?.1 else { return }
-                            self?.messageReadIndicator?.subviews.forEach({ $0.removeFromSuperview() })
-                            if let displayReadIndicator = profileData.element?.2, displayReadIndicator {
-                                self?.messageReadIndicator?.addSubview(AvatarView(profileImageData: profileData.element?.0, username: bestName, size: 12))
-                            }
-                        })
-                        .disposed(by: self.disposeBag)
+                    configureMessageReadAvatar(item, conversationViewModel)
                 }
             }
         } else if item.bubblePosition() == .received {
@@ -443,33 +453,44 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
                     addComposingMsgView()
                 }
             }
-            // received message avatar
-            Observable<(Data?, String)>.combineLatest(conversationViewModel.profileImageData.asObservable(),
-                                                      conversationViewModel.userName.asObservable(),
-                                                      conversationViewModel.displayName.asObservable()) { profileImage, username, displayName in
-                                                        if let displayName = displayName, !displayName.isEmpty {
-                                                            return (profileImage, displayName)
-                                                        }
-                                                        return (profileImage, username)
-                }
-                .observeOn(MainScheduler.instance)
-                .startWith((conversationViewModel.profileImageData.value, conversationViewModel.userName.value))
-                .subscribe({ [weak self] profileData -> Void in
-                    guard let data = profileData.element?.1 else { return }
-                    self?.avatarView
-                        .subviews.forEach({ $0.removeFromSuperview() })
-                    self?.avatarView
-                        .addSubview(
-                            AvatarView(profileImageData: profileData.element?.0,
-                                               username: data,
-                                               size: 32))
-                    self?.avatarView.isHidden = !(item.sequencing == .lastOfSequence || item.sequencing == .singleMessage)
-                    return
-                })
-                .disposed(by: self.disposeBag)
+
+            configureReceivedMessageAvatar(item.sequencing, conversationViewModel)
         }
     }
 
+    private func configureReceivedMessageAvatar(_ itemSequencing: MessageSequencing, _ conversationViewModel: ConversationViewModel) {
+
+        Observable<(Data?, String)>.combineLatest(conversationViewModel.profileImageData.asObservable(),
+                                                  conversationViewModel.bestName.asObservable()) { ($0, $1) }
+            .observeOn(MainScheduler.instance)
+            .startWith((conversationViewModel.profileImageData.value, conversationViewModel.userName.value))
+            .subscribe({ [weak self] profileData in
+                guard let data = profileData.element?.1 else { return }
+                self?.avatarView.subviews.forEach({ $0.removeFromSuperview() })
+                if itemSequencing == .lastOfSequence || itemSequencing == .singleMessage {
+                    self?.avatarView.addSubview(AvatarView(profileImageData: profileData.element?.0, username: data, size: 32))
+                }
+            })
+            .disposed(by: self.disposeBag)
+     }
+
+    fileprivate func configureMessageReadAvatar(_ item: MessageViewModel, _ conversationViewModel: ConversationViewModel) {
+
+        Observable<(Data?, String, Bool)>.combineLatest(conversationViewModel.profileImageData.asObservable(),
+                                                        conversationViewModel.bestName.asObservable(),
+                                                        item.displayReadIndicator.asObservable()) { ($0, $1, $2) }
+            .observeOn(MainScheduler.instance)
+            .startWith((conversationViewModel.profileImageData.value, conversationViewModel.userName.value, item.displayReadIndicator.value))
+            .subscribe({ [weak self] profileData in
+                guard let bestName = profileData.element?.1 else { return }
+                self?.messageReadIndicator?.subviews.forEach({ $0.removeFromSuperview() })
+                if let displayReadIndicator = profileData.element?.2, displayReadIndicator {
+                    self?.messageReadIndicator?.addSubview(AvatarView(profileImageData: profileData.element?.0, username: bestName, size: 12))
+                }
+            })
+            .disposed(by: self.disposeBag)
+    }
+
     func addComposingMsgView() {
         self.composingMsg = UIView(frame: self.messageLabel.frame)
         let size: CGFloat = 10
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
index 3e3a1bf16487bb3580ff7a41d2b8ae54e4178f39..2d75154ea9898501df6887b9f33c9e4316c202d4 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
@@ -5,6 +5,7 @@
  *  Author: Quentin Muret <quentin.muret@savoirfairelinux.com>
  *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
  *  Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -47,6 +48,7 @@ class ConversationViewController: UIViewController,
     var bottomOffset: CGFloat = 0
     let scrollOffsetThreshold: CGFloat = 600
     var bottomHeight: CGFloat = 0.00
+    var isExecutingDeleteMessage: Bool = false
 
     @IBOutlet weak var currentCallButton: UIButton!
     @IBOutlet weak var currentCallLabel: UILabel!
@@ -104,7 +106,7 @@ class ConversationViewController: UIViewController,
         self.navigationController?.navigationBar.layer.shadowColor = UIColor.jamiNavigationBarShadow.cgColor
     }
 
-    func importDocument() {
+    private func importDocument() {
         let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .import)
         documentPicker.delegate = self
         documentPicker.modalPresentationStyle = .formSheet
@@ -128,7 +130,7 @@ class ConversationViewController: UIViewController,
         }
     }
 
-    func showNoPermissionsAlert(title: String) {
+    private func showNoPermissionsAlert(title: String) {
         let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
         let okAction = UIAlertAction(title: "OK", style: .default) { (_: UIAlertAction!) -> Void in }
         alert.addAction(okAction)
@@ -664,9 +666,12 @@ class ConversationViewController: UIViewController,
     }
 
     fileprivate func scrollToBottomIfNeed() {
-        if self.isBottomContentOffset {
+        if self.isBottomContentOffset && !self.isExecutingDeleteMessage {
             self.scrollToBottom(animated: false)
         }
+        if self.isExecutingDeleteMessage {
+            self.isExecutingDeleteMessage = false
+        }
     }
 
     fileprivate func scrollToBottom(animated: Bool) {
@@ -678,7 +683,7 @@ class ConversationViewController: UIViewController,
     }
 
     fileprivate var isBottomContentOffset: Bool {
-        updateBottomOffset()
+        self.updateBottomOffset()
         let offset = abs((self.tableView.contentOffset.y + self.tableView.contentInset.top) - bottomOffset)
         return offset <= scrollOffsetThreshold
     }
@@ -733,7 +738,7 @@ class ConversationViewController: UIViewController,
     }
 
     // MARK: - message formatting
-    func computeSequencing() {
+    private func computeSequencing() {
         var lastShownTime: Date?
         for (index, messageViewModel) in self.messageViewModels!.enumerated() {
             // time labels
@@ -759,7 +764,7 @@ class ConversationViewController: UIViewController,
         }
     }
 
-    func getMessageSequencing(forIndex index: Int) -> MessageSequencing {
+    private func getMessageSequencing(forIndex index: Int) -> MessageSequencing {
         if let models = self.messageViewModels {
             let messageItem = models[index]
             let msgOwner = messageItem.bubblePosition()
@@ -800,25 +805,22 @@ class ConversationViewController: UIViewController,
         return MessageSequencing.unknown
     }
 
-    func getTimeLabelString(forTime time: Date) -> String {
+    private func getTimeLabelString(forTime time: Date) -> String {
         // get the current time
         let currentDateTime = Date()
 
         // prepare formatter
         let dateFormatter = DateFormatter()
-        if Calendar.current.compare(currentDateTime, to: time, toGranularity: .year) == .orderedSame {
-            if Calendar.current.compare(currentDateTime, to: time, toGranularity: .weekOfYear) == .orderedSame {
-                if Calendar.current.compare(currentDateTime, to: time, toGranularity: .day) == .orderedSame {
-                    // age: [0, received the previous day[
-                    dateFormatter.dateFormat = "h:mma"
-                } else {
-                    // age: [received the previous day, received 7 days ago[
-                    dateFormatter.dateFormat = "E h:mma"
-                }
-            } else {
-                // age: [received 7 days ago, received the previous year[
-                dateFormatter.dateFormat = "MMM d, h:mma"
-            }
+
+        if Calendar.current.compare(currentDateTime, to: time, toGranularity: .day) == .orderedSame {
+            // age: [0, received the previous day[
+            dateFormatter.dateFormat = "h:mma"
+        } else if Calendar.current.compare(currentDateTime, to: time, toGranularity: .weekOfYear) == .orderedSame {
+            // age: [received the previous day, received 7 days ago[
+            dateFormatter.dateFormat = "E h:mma"
+        } else if Calendar.current.compare(currentDateTime, to: time, toGranularity: .year) == .orderedSame {
+            // age: [received 7 days ago, received the previous year[
+            dateFormatter.dateFormat = "MMM d, h:mma"
         } else {
             // age: [received the previous year, inf[
             dateFormatter.dateFormat = "MMM d, yyyy h:mma"
@@ -938,109 +940,128 @@ extension ConversationViewController: UITableViewDataSource {
         return self.messageViewModels?.count ?? 0
     }
 
-    // swiftlint:disable cyclomatic_complexity
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         if let item = self.messageViewModels?[indexPath.row] {
-            var type = MessageCell.self
-            if item.isTransfer {
-                type = item.bubblePosition() == .received ? MessageCellDataTransferReceived.self : MessageCellDataTransferSent.self
-            } else {
-                type =  item.bubblePosition() == .received ? MessageCellReceived.self :
-                    item.bubblePosition() == .sent ? MessageCellSent.self :
-                    item.bubblePosition() == .generated ? MessageCellGenerated.self :
-                    MessageCellGenerated.self
-            }
-            if item.message.incoming && item.message.status != .displayed && !item.message.isTransfer {
-                self.viewModel
-                    .setMessageAsRead(daemonId: item.message.daemonId,
-                                      messageId: item.message.messageId)
+
+            if item.message.incoming &&
+                item.message.status != .displayed &&
+                !item.message.isTransfer {
+                self.viewModel.setMessageAsRead(daemonId: item.message.daemonId,
+                                                messageId: item.message.messageId)
             }
-            let cell = tableView.dequeueReusableCell(for: indexPath, cellType: type)
+
+            let cellType = { (bubblePosition: BubblePosition, isTransfer: Bool) -> MessageCell.Type in
+                switch bubblePosition {
+                case .received: return isTransfer ? MessageCellDataTransferReceived.self : MessageCellReceived.self
+                case .sent: return isTransfer ? MessageCellDataTransferSent.self : MessageCellSent.self
+                case .generated: return MessageCellGenerated.self
+                }
+            }(item.bubblePosition(), item.isTransfer)
+
+            let cell = tableView.dequeueReusableCell(for: indexPath, cellType: cellType)
             cell.configureFromItem(viewModel, self.messageViewModels, cellForRowAt: indexPath)
 
-            if item.isTransfer {
-                cell.acceptButton?.setTitle(L10n.DataTransfer.readableStatusAccept, for: .normal)
-                item.lastTransferStatus = .unknown
-                changeTransferStatus(cell, nil, item.message.transferStatus, item, viewModel)
-                item.transferStatus.asObservable()
-                    .observeOn(MainScheduler.instance)
-                    .filter {
-                        return $0 != DataTransferStatus.unknown && $0 != item.lastTransferStatus && $0 != item.initialTransferStatus }
-                    .subscribe(onNext: { [weak self, weak tableView, weak cell] status in
-                        guard let cell = cell else {return}
-                        guard let currentIndexPath = tableView?.indexPath(for: cell) else { return }
-                        guard let transferId = item.daemonId else { return }
-                        guard let model = self?.viewModel else { return }
-                        self?.log.info("Transfer status change from: \(item.lastTransferStatus.description) to: \(status.description) for transferId: \(transferId) cell row: \(currentIndexPath.row)")
-                        if item.bubblePosition() == .sent && item.shouldDisplayTransferedImage {
-                            cell.displayTransferedImage(message: item, conversationID: model.conversation.value.conversationId, accountId: model.conversation.value.accountId)
-                        } else {
-                            self?.changeTransferStatus(cell, currentIndexPath, status, item, model)
-                            cell.stopProgressMonitor()
+            transferCellSetup(item, cell, tableView, indexPath)
+            deleteCellSetup(cell)
+
+            return cell
+        }
+        return tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellSent.self)
+    }
+
+    private func deleteCellSetup(_ cell: MessageCell) {
+        cell.deleteMessage
+            .observeOn(MainScheduler.instance)
+            .subscribe(onNext: { [weak self, weak cell] (shouldDelete) in
+                guard shouldDelete, let self = self, let cell = cell, let messageId = cell.messageId else { return }
+                self.isExecutingDeleteMessage = true
+                self.viewModel.deleteMessage(messageId: messageId)
+            })
+            .disposed(by: cell.disposeBag)
+    }
+
+    // swiftlint:disable cyclomatic_complexity
+    private func transferCellSetup(_ item: MessageViewModel, _ cell: MessageCell, _ tableView: UITableView, _ indexPath: IndexPath) {
+        if item.isTransfer {
+            cell.acceptButton?.setTitle(L10n.DataTransfer.readableStatusAccept, for: .normal)
+            item.lastTransferStatus = .unknown
+            changeTransferStatus(cell, nil, item.message.transferStatus, item, viewModel)
+            item.transferStatus.asObservable()
+                .observeOn(MainScheduler.instance)
+                .filter {
+                    return $0 != DataTransferStatus.unknown && $0 != item.lastTransferStatus && $0 != item.initialTransferStatus }
+                .subscribe(onNext: { [weak self, weak tableView, weak cell] status in
+                    guard let cell = cell else {return}
+                    guard let currentIndexPath = tableView?.indexPath(for: cell) else { return }
+                    guard let transferId = item.daemonId else { return }
+                    guard let model = self?.viewModel else { return }
+                    self?.log.info("Transfer status change from: \(item.lastTransferStatus.description) to: \(status.description) for transferId: \(transferId) cell row: \(currentIndexPath.row)")
+                    if item.bubblePosition() == .sent && item.shouldDisplayTransferedImage {
+                        cell.displayTransferedImage(message: item, conversationID: model.conversation.value.conversationId, accountId: model.conversation.value.accountId)
+                    } else {
+                        self?.changeTransferStatus(cell, currentIndexPath, status, item, model)
+                        cell.stopProgressMonitor()
+                    }
+                    item.lastTransferStatus = status
+                    item.initialTransferStatus = status
+                    tableView?.reloadData()
+                })
+                .disposed(by: cell.disposeBag)
+
+            cell.cancelButton.rx.tap
+                .subscribe(onNext: { [weak self, weak tableView, weak cell] _ in
+                    guard let cell = cell else {return}
+                    guard let transferId = item.daemonId else { return }
+                    self?.log.info("canceling transferId \(transferId)")
+                    _ = self?.viewModel.cancelTransfer(transferId: transferId)
+                    item.initialTransferStatus = .canceled
+                    item.message.transferStatus = .canceled
+                    cell.stopProgressMonitor()
+                    tableView?.reloadData()
+                })
+                .disposed(by: cell.disposeBag)
+            cell.playerHeight
+                .asObservable()
+                .share()
+                .observeOn(MainScheduler.instance)
+                .subscribe(onNext: {[weak tableView] height in
+                    if height > 0 {
+                        UIView.performWithoutAnimation {
+                            guard let sectionNumber = tableView?.numberOfSections,
+                                let rowNumber =  tableView?.numberOfRows(inSection: indexPath.section) else {return}
+                            if indexPath.section < sectionNumber && indexPath.section >= 0 {
+                                if indexPath.row < rowNumber &&
+                                    indexPath.row >= 0 &&
+                                    indexPath.row != tableView?.numberOfRows(inSection: indexPath.section) {
+                                    tableView?
+                                        .reloadItemsAtIndexPaths([indexPath],
+                                                                 animationStyle: .top)
+                                }
+                            }
                         }
-                        item.lastTransferStatus = status
-                        item.initialTransferStatus = status
-                        tableView?.reloadData()
-                    })
-                    .disposed(by: cell.disposeBag)
+                    }
+                }).disposed(by: cell.disposeBag)
 
-                cell.cancelButton.rx.tap
+            if item.bubblePosition() == .received {
+                cell.acceptButton?.rx.tap
                     .subscribe(onNext: { [weak self, weak tableView, weak cell] _ in
                         guard let cell = cell else {return}
                         guard let transferId = item.daemonId else { return }
-                        self?.log.info("canceling transferId \(transferId)")
-                        _ = self?.viewModel.cancelTransfer(transferId: transferId)
-                        item.initialTransferStatus = .canceled
-                        item.message.transferStatus = .canceled
-                        cell.stopProgressMonitor()
-                        tableView?.reloadData()
+                        self?.log.info("accepting transferId \(transferId)")
+                        if self?.viewModel.acceptTransfer(transferId: transferId, interactionID: item.messageId, messageContent: &item.message.content) != .success {
+                            _ = self?.viewModel.cancelTransfer(transferId: transferId)
+                            item.initialTransferStatus = .canceled
+                            item.message.transferStatus = .canceled
+                            cell.stopProgressMonitor()
+                            tableView?.reloadData()
+                        }
                     })
                     .disposed(by: cell.disposeBag)
-                cell.playerHeight
-                    .asObservable()
-                    .share()
-                    .observeOn(MainScheduler.instance)
-                    .subscribe(onNext: {[weak tableView] height in
-                        if height > 0 {
-                            UIView.performWithoutAnimation {
-                                guard let sectionNumber = tableView?.numberOfSections,
-                                let rowNumber =  tableView?.numberOfRows(inSection: indexPath.section) else {return}
-                                if indexPath.section < sectionNumber && indexPath.section >= 0 {
-                                    if indexPath.row < rowNumber &&
-                                        indexPath.row >= 0 &&
-                                        indexPath.row != tableView?.numberOfRows(inSection: indexPath.section) {
-                                        tableView?
-                                            .reloadItemsAtIndexPaths([indexPath],
-                                                                     animationStyle: .top)
-                                    }
-                                }
-                            }
-                        }
-                    }).disposed(by: cell.disposeBag)
-
-                if item.bubblePosition() == .received {
-                    cell.acceptButton?.rx.tap
-                        .subscribe(onNext: { [weak self, weak tableView, weak cell] _ in
-                            guard let cell = cell else {return}
-                            guard let transferId = item.daemonId else { return }
-                            self?.log.info("accepting transferId \(transferId)")
-                            if self?.viewModel.acceptTransfer(transferId: transferId, interactionID: item.messageId, messageContent: &item.message.content) != .success {
-                                _ = self?.viewModel.cancelTransfer(transferId: transferId)
-                                item.initialTransferStatus = .canceled
-                                item.message.transferStatus = .canceled
-                                cell.stopProgressMonitor()
-                                tableView?.reloadData()
-                            }
-                        })
-                        .disposed(by: cell.disposeBag)
-                }
-                if item.message.transferStatus == .success {
-                    self.addShareAction(cell: cell, item: item)
-                }
             }
-            return cell
+            if item.message.transferStatus == .success {
+                self.addShareAction(cell: cell, item: item)
+            }
         }
-        return tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellSent.self)
     }
 }
 // swiftlint:enable type_body_length
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
index e5a2a19b03a90718dd665757fea7f076a91b0c73..0181dbf4881a490b3b60290010d556733a1d7017 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
@@ -5,6 +5,7 @@
  *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
  *  Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *  Author: Quentin Muret <quentin.muret@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -277,11 +278,8 @@ class ConversationViewModel: Stateable, ViewModel {
         return Observable
             .combineLatest(userName.asObservable(),
                            displayName.asObservable()) {(userName, displayname) in
-                            guard let name = displayname,
-                                !name.isEmpty else {
-                                    return userName
-                            }
-                            return name
+                            guard let displayname = displayname, !displayname.isEmpty else { return userName }
+                            return displayname
         }
     }()
 
@@ -403,6 +401,17 @@ class ConversationViewModel: Stateable, ViewModel {
             }.first?.status = .displayed
     }
 
+    func deleteMessage(messageId: Int64) {
+        guard let account = self.accountService.currentAccount else { return }
+        self.conversationsService
+            .deleteMessage(messagesId: messageId, accountId: account.id)
+            .subscribe(onCompleted: { [weak self] in
+                self?.log.debug("Messages was deleted")
+            })
+            .disposed(by: disposeBag)
+        self.messages.value.removeAll(where: { $0.messageId == messageId })
+    }
+
     fileprivate var unreadMessagesCount: Int {
         let unreadMessages =  self.conversation.value.messages
             .filter({ message in
diff --git a/Ring/Ring/Services/ConversationsService.swift b/Ring/Ring/Services/ConversationsService.swift
index b7ae436a7077f6705d23b8b7201a52c8fe07e072..73a2e7c08f6da53e1b0db6873343b56a54de7807 100644
--- a/Ring/Ring/Services/ConversationsService.swift
+++ b/Ring/Ring/Services/ConversationsService.swift
@@ -3,6 +3,7 @@
  *
  *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
  *  Author: Quentin Muret <quentin.muret@savoirfairelinux.com>
+ *  Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -359,6 +360,17 @@ class ConversationsService {
         })
     }
 
+    func deleteMessage(messagesId: Int64, accountId: String) -> Completable {
+        return Completable.create(subscribe: { [unowned self] completable in
+            self.dbManager
+                .deleteMessage(messagesId: messagesId, accountId: accountId)
+                .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
+                .subscribe(onCompleted: { completable(.completed) }, onError: { error in completable(.error(error))})
+                .disposed(by: self.disposeBag)
+            return Disposables.create { }
+        })
+    }
+
     func getProfile(uri: String, accountId: String) -> Observable<Profile> {
         return self.dbManager.profileObservable(for: uri, createIfNotExists: false, accountId: accountId)
     }