mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-09-05 06:33:25 +02:00
messagelistmodel: support message edition
Handle application/edited-message type to support message edition. Previous bodies are saved in the interaction to be able to get the original post to avoid unwanted editions. While loading a conversation, we store the editions in a temporary map that we link once the edited message is detected. This work because we can't edit a message before this message exists. PreviousBodies in interaction.h contains every previous body detected and the client can show previous version of the message in a popup. Deleting a message works the same way, just that any message with an empty body is not shown. https://git.jami.net/savoirfairelinux/jami-daemon/-/issues/316 Change-Id: Ib158abd16ad4b629532de11694e88d86a12d72a8
This commit is contained in:
parent
9457c7ccbb
commit
47cd60fbe4
21 changed files with 448 additions and 18 deletions
97
src/app/commoncomponents/EditedPopup.qml
Normal file
97
src/app/commoncomponents/EditedPopup.qml
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Savoir-faire Linux Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
|
||||||
|
BaseModalDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 488
|
||||||
|
height: 256
|
||||||
|
|
||||||
|
property var previousBodies: undefined
|
||||||
|
|
||||||
|
popupContent: Item {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
JamiListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
model: root.previousBodies
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: root.width - 2 * JamiTheme.preferredMarginSize
|
||||||
|
height: Math.max(JamiTheme.menuItemsPreferredHeight, rowBody.implicitHeight)
|
||||||
|
color: index % 2 === 0 ? JamiTheme.backgroundColor : JamiTheme.secondaryBackgroundColor
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: rowBody
|
||||||
|
spacing: JamiTheme.preferredMarginSize
|
||||||
|
width: parent.width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.preferredWidth: root.width / 4
|
||||||
|
Layout.leftMargin: JamiTheme.settingsMarginSize
|
||||||
|
|
||||||
|
text: MessagesAdapter.getFormattedDay(modelData.timestamp.toString())
|
||||||
|
+ " - " + MessagesAdapter.getFormattedTime(modelData.timestamp.toString())
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: 3 * rowBody.width / 4 - 2 * JamiTheme.preferredMarginSize
|
||||||
|
text: modelData.body
|
||||||
|
}
|
||||||
|
|
||||||
|
text: metrics.elidedText
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: btnCancel
|
||||||
|
imageColor: "grey"
|
||||||
|
normalColor: "transparent"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
source: JamiResources.round_close_24dp_svg
|
||||||
|
onClicked: close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ ContextMenuAutoLoader {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string location
|
property string location
|
||||||
|
property bool isOutgoing
|
||||||
property string msgId
|
property string msgId
|
||||||
property string transferName
|
property string transferName
|
||||||
property string transferId
|
property string transferId
|
||||||
|
@ -57,8 +58,29 @@ ContextMenuAutoLoader {
|
||||||
|
|
||||||
itemName: JamiStrings.reply
|
itemName: JamiStrings.reply
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
MessagesAdapter.editId = ""
|
||||||
MessagesAdapter.replyToId = root.msgId
|
MessagesAdapter.replyToId = root.msgId
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
GeneralMenuItem {
|
||||||
|
id: edit
|
||||||
|
|
||||||
|
canTrigger: transferId === "" && isOutgoing
|
||||||
|
itemName: JamiStrings.edit
|
||||||
|
onClicked: {
|
||||||
|
MessagesAdapter.replyToId = ""
|
||||||
|
MessagesAdapter.editId = root.msgId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GeneralMenuItem {
|
||||||
|
id: deleteMsg
|
||||||
|
dangerous: true
|
||||||
|
|
||||||
|
canTrigger: transferId === "" && isOutgoing
|
||||||
|
itemName: JamiStrings.optionDelete
|
||||||
|
onClicked: {
|
||||||
|
MessagesAdapter.editMessage(CurrentConversation.id, "", root.msgId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -153,8 +153,8 @@ Control {
|
||||||
var baseColor = isOutgoing ? JamiTheme.messageOutBgColor
|
var baseColor = isOutgoing ? JamiTheme.messageOutBgColor
|
||||||
: CurrentConversation.isCoreDialog ?
|
: CurrentConversation.isCoreDialog ?
|
||||||
JamiTheme.messageInBgColor : Qt.lighter(CurrentConversation.color, 1.5)
|
JamiTheme.messageInBgColor : Qt.lighter(CurrentConversation.color, 1.5)
|
||||||
if (Id === MessagesAdapter.replyToId) {
|
if (Id === MessagesAdapter.replyToId || Id === MessagesAdapter.editId) {
|
||||||
// If we are replying to
|
// If we are replying to or editing the message
|
||||||
return Qt.darker(baseColor, 1.5)
|
return Qt.darker(baseColor, 1.5)
|
||||||
}
|
}
|
||||||
return baseColor
|
return baseColor
|
||||||
|
@ -289,6 +289,7 @@ Control {
|
||||||
|
|
||||||
msgId: Id
|
msgId: Id
|
||||||
location: root.location
|
location: root.location
|
||||||
|
isOutgoing: root.isOutgoing
|
||||||
transferId: root.transferId
|
transferId: root.transferId
|
||||||
transferName: root.transferName
|
transferName: root.transferName
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,12 @@ SBSMessageBase {
|
||||||
formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
|
formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
|
||||||
extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage
|
extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage
|
||||||
|
|
||||||
|
EditedPopup {
|
||||||
|
id: editedPopup
|
||||||
|
|
||||||
|
previousBodies: PreviousBodies
|
||||||
|
}
|
||||||
|
|
||||||
innerContent.children: [
|
innerContent.children: [
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: textEditId
|
id: textEditId
|
||||||
|
@ -106,6 +112,48 @@ SBSMessageBase {
|
||||||
selectOnly: parent.readOnly
|
selectOnly: parent.readOnly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
RowLayout {
|
||||||
|
id: editedRow
|
||||||
|
anchors.right: isOutgoing ? parent.right : undefined
|
||||||
|
visible: PreviousBodies.length !== 0
|
||||||
|
|
||||||
|
ResponsiveImage {
|
||||||
|
id: editedImage
|
||||||
|
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
Layout.bottomMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
source: JamiResources.round_edit_24dp_svg
|
||||||
|
width: JamiTheme.editedFontSize
|
||||||
|
height: JamiTheme.editedFontSize
|
||||||
|
layer {
|
||||||
|
enabled: true
|
||||||
|
effect: ColorOverlay {
|
||||||
|
color: editedLabel.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: editedLabel
|
||||||
|
|
||||||
|
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||||
|
Layout.bottomMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
text: JamiStrings.edited
|
||||||
|
color: UtilsAdapter.luma(bubble.color) ?
|
||||||
|
JamiTheme.chatviewTextColorLight :
|
||||||
|
JamiTheme.chatviewTextColorDark
|
||||||
|
font.pointSize: JamiTheme.editedFontSize
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onTapped: {
|
||||||
|
editedPopup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Loader {
|
Loader {
|
||||||
id: extraContent
|
id: extraContent
|
||||||
anchors.right: isOutgoing ? parent.right : undefined
|
anchors.right: isOutgoing ? parent.right : undefined
|
||||||
|
|
|
@ -37,6 +37,7 @@ MenuItem {
|
||||||
property bool canTrigger: true
|
property bool canTrigger: true
|
||||||
property bool addMenuSeparatorAfter: false
|
property bool addMenuSeparatorAfter: false
|
||||||
property bool autoTextSizeAdjustment: true
|
property bool autoTextSizeAdjustment: true
|
||||||
|
property bool dangerous: false
|
||||||
property BaseContextMenu parentMenu
|
property BaseContextMenu parentMenu
|
||||||
|
|
||||||
property int itemPreferredWidth: JamiTheme.menuItemsPreferredWidth
|
property int itemPreferredWidth: JamiTheme.menuItemsPreferredWidth
|
||||||
|
@ -94,7 +95,7 @@ MenuItem {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: itemName
|
text: itemName
|
||||||
color: JamiTheme.textColor
|
color: dangerous ? JamiTheme.redColor : JamiTheme.textColor
|
||||||
font.pointSize: JamiTheme.textFontSize
|
font.pointSize: JamiTheme.textFontSize
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
|
@ -726,6 +726,8 @@ Item {
|
||||||
property string inReplyTo: qsTr("In reply to")
|
property string inReplyTo: qsTr("In reply to")
|
||||||
property string reply: qsTr("Reply")
|
property string reply: qsTr("Reply")
|
||||||
property string writeTo: qsTr("Write to %1")
|
property string writeTo: qsTr("Write to %1")
|
||||||
|
property string edit: qsTr("Edit")
|
||||||
|
property string edited: qsTr("Edited")
|
||||||
|
|
||||||
// Invitation View
|
// Invitation View
|
||||||
property string invitationViewSentRequest: qsTr("%1 has sent you a request for a conversation.")
|
property string invitationViewSentRequest: qsTr("%1 has sent you a request for a conversation.")
|
||||||
|
|
|
@ -267,6 +267,7 @@ Item {
|
||||||
property real smartlistItemInfoFontSize: calcSize(9 + fontSizeOffsetSmall)
|
property real smartlistItemInfoFontSize: calcSize(9 + fontSizeOffsetSmall)
|
||||||
property real filterItemFontSize: calcSize(smartlistItemFontSize)
|
property real filterItemFontSize: calcSize(smartlistItemFontSize)
|
||||||
property real filterBadgeFontSize: calcSize(8.25)
|
property real filterBadgeFontSize: calcSize(8.25)
|
||||||
|
property real editedFontSize: calcSize(8)
|
||||||
property real accountListItemHeight: 64
|
property real accountListItemHeight: 64
|
||||||
property real accountListAvatarSize: 40
|
property real accountListAvatarSize: 40
|
||||||
property real smartListItemHeight: 64
|
property real smartListItemHeight: 64
|
||||||
|
|
|
@ -81,6 +81,16 @@ Rectangle {
|
||||||
function onNewTextPasted() {
|
function onNewTextPasted() {
|
||||||
messageBar.textAreaObj.pasteText()
|
messageBar.textAreaObj.pasteText()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onEditIdChanged() {
|
||||||
|
if (MessagesAdapter.editId.length > 0)
|
||||||
|
messageBar.textAreaObj.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReplyToIdChanged() {
|
||||||
|
if (MessagesAdapter.replyToId.length > 0)
|
||||||
|
messageBar.forceActiveFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordBox {
|
RecordBox {
|
||||||
|
@ -131,6 +141,16 @@ Rectangle {
|
||||||
visible: MessagesAdapter.replyToId !== ""
|
visible: MessagesAdapter.replyToId !== ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditContainer {
|
||||||
|
id: editContainer
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: footerColumnLayout.width
|
||||||
|
Layout.maximumWidth: JamiTheme.chatViewMaximumWidth
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
visible: MessagesAdapter.editId !== ""
|
||||||
|
}
|
||||||
|
|
||||||
MessageBar {
|
MessageBar {
|
||||||
id: messageBar
|
id: messageBar
|
||||||
|
|
||||||
|
@ -162,8 +182,13 @@ Rectangle {
|
||||||
onSendFileButtonClicked: jamiFileDialog.open()
|
onSendFileButtonClicked: jamiFileDialog.open()
|
||||||
onSendMessageButtonClicked: {
|
onSendMessageButtonClicked: {
|
||||||
// Send text message
|
// Send text message
|
||||||
if (messageBar.text)
|
if (messageBar.text) {
|
||||||
MessagesAdapter.sendMessage(messageBar.text)
|
if (MessagesAdapter.editId !== "") {
|
||||||
|
MessagesAdapter.editMessage(CurrentConversation.id, messageBar.text)
|
||||||
|
} else {
|
||||||
|
MessagesAdapter.sendMessage(messageBar.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
messageBar.textAreaObj.clearText()
|
messageBar.textAreaObj.clearText()
|
||||||
|
|
||||||
// Send file messages
|
// Send file messages
|
||||||
|
|
99
src/app/mainview/components/EditContainer.qml
Normal file
99
src/app/mainview/components/EditContainer.qml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Savoir-faire Linux Inc.
|
||||||
|
* Author: Sébastien Blin <sebastien.blin@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
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
|
||||||
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
color: JamiTheme.messageOutBgColor
|
||||||
|
|
||||||
|
property var body: {
|
||||||
|
if (MessagesAdapter.editId === "")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return MessagesAdapter.dataForInteraction(MessagesAdapter.editId, MessageList.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: editLbl
|
||||||
|
|
||||||
|
text: JamiStrings.edit
|
||||||
|
|
||||||
|
color: UtilsAdapter.luma(root.color) ?
|
||||||
|
JamiTheme.chatviewTextColorLight :
|
||||||
|
JamiTheme.chatviewTextColorDark
|
||||||
|
font.pointSize: JamiTheme.textFontSize
|
||||||
|
font.kerning: true
|
||||||
|
font.bold: true
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: bodyLbl
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: root.width - 100
|
||||||
|
text: root.body
|
||||||
|
}
|
||||||
|
|
||||||
|
text: metrics.elidedText
|
||||||
|
color: UtilsAdapter.luma(root.color) ?
|
||||||
|
JamiTheme.chatviewTextColorLight :
|
||||||
|
JamiTheme.chatviewTextColorDark
|
||||||
|
font.pointSize: JamiTheme.textFontSize
|
||||||
|
font.kerning: true
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: closeReply
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
preferredSize: 24
|
||||||
|
|
||||||
|
source: JamiResources.round_close_24dp_svg
|
||||||
|
|
||||||
|
normalColor: JamiTheme.chatviewBgColor
|
||||||
|
imageColor: JamiTheme.chatviewButtonColor
|
||||||
|
|
||||||
|
onClicked: MessagesAdapter.editId = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -193,7 +193,8 @@ JamiListView {
|
||||||
onMessageListModelChanged: sourceModel = messageListModel
|
onMessageListModelChanged: sourceModel = messageListModel
|
||||||
filters: ExpressionFilter {
|
filters: ExpressionFilter {
|
||||||
readonly property int mergeType: Interaction.Type.MERGE
|
readonly property int mergeType: Interaction.Type.MERGE
|
||||||
expression: Body !== "" && Type !== mergeType
|
readonly property int editedType: Interaction.Type.EDITED
|
||||||
|
expression: Body !== "" && Type !== mergeType && Type !== editedType
|
||||||
}
|
}
|
||||||
sorters: ExpressionSorter {
|
sorters: ExpressionSorter {
|
||||||
expression: modelLeft.index > modelRight.index
|
expression: modelLeft.index > modelRight.index
|
||||||
|
|
|
@ -291,7 +291,7 @@ Item {
|
||||||
visible: (!root.isMe && !root.meModerator) ? root.participantIsMuted : root.isLocalMuted
|
visible: (!root.isMe && !root.meModerator) ? root.participantIsMuted : root.isLocalMuted
|
||||||
|
|
||||||
source: JamiResources.micro_off_black_24dp_svg
|
source: JamiResources.micro_off_black_24dp_svg
|
||||||
color: "red"
|
color: JamiTheme.redColor
|
||||||
|
|
||||||
HoverHandler { id: hoverMicrophone }
|
HoverHandler { id: hoverMicrophone }
|
||||||
MaterialToolTip {
|
MaterialToolTip {
|
||||||
|
|
|
@ -52,6 +52,7 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
|
||||||
{
|
{
|
||||||
connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() {
|
connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() {
|
||||||
set_replyToId("");
|
set_replyToId("");
|
||||||
|
set_editId("");
|
||||||
const QString& convId = lrcInstance_->get_selectedConvUid();
|
const QString& convId = lrcInstance_->get_selectedConvUid();
|
||||||
const auto& conversation = lrcInstance_->getConversationFromConvUid(convId);
|
const auto& conversation = lrcInstance_->getConversationFromConvUid(convId);
|
||||||
set_messageListModel(QVariant::fromValue(conversation.interactions.get()));
|
set_messageListModel(QVariant::fromValue(conversation.interactions.get()));
|
||||||
|
@ -155,6 +156,23 @@ MessagesAdapter::sendMessage(const QString& message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MessagesAdapter::editMessage(const QString& convId, const QString& newBody, const QString& messageId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const auto convUid = lrcInstance_->get_selectedConvUid();
|
||||||
|
auto editId = !messageId.isEmpty() ? messageId : editId_;
|
||||||
|
if (editId.isEmpty()) {
|
||||||
|
qWarning("No message to edit");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lrcInstance_->getCurrentConversationModel()->editMessage(convId, newBody, editId);
|
||||||
|
set_editId("");
|
||||||
|
} catch (...) {
|
||||||
|
qDebug() << "Exception during message edition:" << messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessagesAdapter::sendFile(const QString& message)
|
MessagesAdapter::sendFile(const QString& message)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@ class MessagesAdapter final : public QmlAdapterBase
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_RO_PROPERTY(QVariant, messageListModel)
|
QML_RO_PROPERTY(QVariant, messageListModel)
|
||||||
QML_PROPERTY(QString, replyToId)
|
QML_PROPERTY(QString, replyToId)
|
||||||
|
QML_PROPERTY(QString, editId)
|
||||||
QML_RO_PROPERTY(QList<QString>, currentConvComposingList)
|
QML_RO_PROPERTY(QList<QString>, currentConvComposingList)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -67,6 +68,9 @@ protected:
|
||||||
Q_INVOKABLE void unbanContact(int index);
|
Q_INVOKABLE void unbanContact(int index);
|
||||||
Q_INVOKABLE void unbanConversation(const QString& convUid);
|
Q_INVOKABLE void unbanConversation(const QString& convUid);
|
||||||
Q_INVOKABLE void sendMessage(const QString& message);
|
Q_INVOKABLE void sendMessage(const QString& message);
|
||||||
|
Q_INVOKABLE void editMessage(const QString& convId,
|
||||||
|
const QString& newBody,
|
||||||
|
const QString& messageId = "");
|
||||||
Q_INVOKABLE void sendFile(const QString& message);
|
Q_INVOKABLE void sendFile(const QString& message);
|
||||||
Q_INVOKABLE void acceptFile(const QString& arg);
|
Q_INVOKABLE void acceptFile(const QString& arg);
|
||||||
Q_INVOKABLE void cancelFile(const QString& arg);
|
Q_INVOKABLE void cancelFile(const QString& arg);
|
||||||
|
|
|
@ -142,6 +142,7 @@
|
||||||
<file>mainview/components/CallButtonDelegate.qml</file>
|
<file>mainview/components/CallButtonDelegate.qml</file>
|
||||||
<file>mainview/components/CallActionBar.qml</file>
|
<file>mainview/components/CallActionBar.qml</file>
|
||||||
<file>commoncomponents/HalfPill.qml</file>
|
<file>commoncomponents/HalfPill.qml</file>
|
||||||
|
<file>commoncomponents/EditedPopup.qml</file>
|
||||||
<file>commoncomponents/MaterialToolTip.qml</file>
|
<file>commoncomponents/MaterialToolTip.qml</file>
|
||||||
<file>mainview/components/ParticipantCallInStatusDelegate.qml</file>
|
<file>mainview/components/ParticipantCallInStatusDelegate.qml</file>
|
||||||
<file>mainview/components/ParticipantCallInStatusView.qml</file>
|
<file>mainview/components/ParticipantCallInStatusView.qml</file>
|
||||||
|
@ -163,6 +164,7 @@
|
||||||
<file>mainview/components/MessageBar.qml</file>
|
<file>mainview/components/MessageBar.qml</file>
|
||||||
<file>mainview/components/FilesToSendContainer.qml</file>
|
<file>mainview/components/FilesToSendContainer.qml</file>
|
||||||
<file>mainview/components/ReplyingContainer.qml</file>
|
<file>mainview/components/ReplyingContainer.qml</file>
|
||||||
|
<file>mainview/components/EditContainer.qml</file>
|
||||||
<file>commoncomponents/Avatar.qml</file>
|
<file>commoncomponents/Avatar.qml</file>
|
||||||
<file>mainview/components/ConversationAvatar.qml</file>
|
<file>mainview/components/ConversationAvatar.qml</file>
|
||||||
<file>mainview/components/InvitationView.qml</file>
|
<file>mainview/components/InvitationView.qml</file>
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct Info
|
||||||
QString callId;
|
QString callId;
|
||||||
QString confId;
|
QString confId;
|
||||||
std::unique_ptr<MessageListModel> interactions;
|
std::unique_ptr<MessageListModel> interactions;
|
||||||
QString lastMessageUid = 0;
|
QString lastMessageUid;
|
||||||
QHash<QString, QString> parentsId; // pair messageid/parentid for messages without parent loaded
|
QHash<QString, QString> parentsId; // pair messageid/parentid for messages without parent loaded
|
||||||
unsigned int unreadMessages = 0;
|
unsigned int unreadMessages = 0;
|
||||||
QVector<QPair<int, QString>> errors;
|
QVector<QPair<int, QString>> errors;
|
||||||
|
|
|
@ -217,6 +217,13 @@ public:
|
||||||
* @param parentId id of parent message. Default is "" - last message in conversation.
|
* @param parentId id of parent message. Default is "" - last message in conversation.
|
||||||
*/
|
*/
|
||||||
void sendMessage(const QString& uid, const QString& body, const QString& parentId = "");
|
void sendMessage(const QString& uid, const QString& body, const QString& parentId = "");
|
||||||
|
/**
|
||||||
|
* Edit a message (empty body = delete message)
|
||||||
|
* @param convId The conversation with the message to edit
|
||||||
|
* @param newBody The new body
|
||||||
|
* @param messageId The id of the message (MUST be by the same author & plain/text)
|
||||||
|
*/
|
||||||
|
void editMessage(const QString& convId, const QString& newBody, const QString& messageId);
|
||||||
/**
|
/**
|
||||||
* Modify the current filter (will change the result of getFilteredConversations)
|
* Modify the current filter (will change the result of getFilteredConversations)
|
||||||
* @param filter the new filter
|
* @param filter the new filter
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace interaction {
|
||||||
Q_NAMESPACE
|
Q_NAMESPACE
|
||||||
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
|
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
|
||||||
|
|
||||||
enum class Type { INVALID, INITIAL, TEXT, CALL, CONTACT, DATA_TRANSFER, MERGE, COUNT__ };
|
enum class Type { INVALID, INITIAL, TEXT, CALL, CONTACT, DATA_TRANSFER, MERGE, EDITED, COUNT__ };
|
||||||
Q_ENUM_NS(Type)
|
Q_ENUM_NS(Type)
|
||||||
|
|
||||||
static inline const QString
|
static inline const QString
|
||||||
|
@ -51,6 +51,8 @@ to_string(const Type& type)
|
||||||
return "DATA_TRANSFER";
|
return "DATA_TRANSFER";
|
||||||
case Type::MERGE:
|
case Type::MERGE:
|
||||||
return "MERGE";
|
return "MERGE";
|
||||||
|
case Type::EDITED:
|
||||||
|
return "EDITED";
|
||||||
case Type::INVALID:
|
case Type::INVALID:
|
||||||
case Type::COUNT__:
|
case Type::COUNT__:
|
||||||
default:
|
default:
|
||||||
|
@ -73,6 +75,8 @@ to_type(const QString& type)
|
||||||
return interaction::Type::DATA_TRANSFER;
|
return interaction::Type::DATA_TRANSFER;
|
||||||
else if (type == "merge")
|
else if (type == "merge")
|
||||||
return interaction::Type::MERGE;
|
return interaction::Type::MERGE;
|
||||||
|
else if (type == "application/edited-message")
|
||||||
|
return interaction::Type::EDITED;
|
||||||
else
|
else
|
||||||
return interaction::Type::INVALID;
|
return interaction::Type::INVALID;
|
||||||
}
|
}
|
||||||
|
@ -238,6 +242,19 @@ getContactInteractionString(const QString& authorUri, const ContactAction& actio
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Body
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
Q_PROPERTY(QString commitId MEMBER commitId)
|
||||||
|
Q_PROPERTY(QString body MEMBER body)
|
||||||
|
Q_PROPERTY(int timestamp MEMBER timestamp)
|
||||||
|
public:
|
||||||
|
QString commitId;
|
||||||
|
QString body;
|
||||||
|
std::time_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var authorUri
|
* @var authorUri
|
||||||
* @var body
|
* @var body
|
||||||
|
@ -263,6 +280,7 @@ struct Info
|
||||||
MapStringString commit;
|
MapStringString commit;
|
||||||
QVariantMap linkPreviewInfo = {};
|
QVariantMap linkPreviewInfo = {};
|
||||||
bool linkified = false;
|
bool linkified = false;
|
||||||
|
QVector<Body> previousBodies;
|
||||||
|
|
||||||
Info() {}
|
Info() {}
|
||||||
|
|
||||||
|
@ -286,7 +304,7 @@ struct Info
|
||||||
Info(const MapStringString& message, const QString& accountURI)
|
Info(const MapStringString& message, const QString& accountURI)
|
||||||
{
|
{
|
||||||
type = to_type(message["type"]);
|
type = to_type(message["type"]);
|
||||||
if (type == Type::TEXT) {
|
if (type == Type::TEXT || type == Type::EDITED) {
|
||||||
body = message["body"];
|
body = message["body"];
|
||||||
}
|
}
|
||||||
authorUri = accountURI == message["author"] ? "" : message["author"];
|
authorUri = accountURI == message["author"] ? "" : message["author"];
|
||||||
|
|
|
@ -220,7 +220,7 @@ public:
|
||||||
const VectorString peersForConversation(const conversation::Info& conversation) const;
|
const VectorString peersForConversation(const conversation::Info& conversation) const;
|
||||||
// insert swarm interactions. Return false if interaction already exists.
|
// insert swarm interactions. Return false if interaction already exists.
|
||||||
bool insertSwarmInteraction(const QString& interactionId,
|
bool insertSwarmInteraction(const QString& interactionId,
|
||||||
const interaction::Info& interaction,
|
interaction::Info& interaction,
|
||||||
conversation::Info& conversation,
|
conversation::Info& conversation,
|
||||||
bool insertAtBegin);
|
bool insertAtBegin);
|
||||||
void invalidateModel();
|
void invalidateModel();
|
||||||
|
@ -1154,7 +1154,7 @@ ConversationModel::sendMessage(const QString& uid, const QString& body, const QS
|
||||||
try {
|
try {
|
||||||
auto& conversation = pimpl_->getConversationForUid(uid, true).get();
|
auto& conversation = pimpl_->getConversationForUid(uid, true).get();
|
||||||
if (!conversation.isLegacy()) {
|
if (!conversation.isLegacy()) {
|
||||||
ConfigurationManager::instance().sendMessage(owner.id, uid, body, parentId);
|
ConfigurationManager::instance().sendMessage(owner.id, uid, body, parentId, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1181,7 +1181,8 @@ ConversationModel::sendMessage(const QString& uid, const QString& body, const QS
|
||||||
ConfigurationManager::instance().sendMessage(owner.id,
|
ConfigurationManager::instance().sendMessage(owner.id,
|
||||||
conversationId,
|
conversationId,
|
||||||
body,
|
body,
|
||||||
parentId);
|
parentId,
|
||||||
|
0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& peers = pimpl_->peersForConversation(newConv);
|
auto& peers = pimpl_->peersForConversation(newConv);
|
||||||
|
@ -1271,6 +1272,18 @@ ConversationModel::sendMessage(const QString& uid, const QString& body, const QS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConversationModel::editMessage(const QString& convId,
|
||||||
|
const QString& newBody,
|
||||||
|
const QString& messageId)
|
||||||
|
{
|
||||||
|
auto conversationOpt = getConversationForUid(convId);
|
||||||
|
if (!conversationOpt.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConfigurationManager::instance().sendMessage(owner.id, convId, newBody, messageId, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationModel::refreshFilter()
|
ConversationModel::refreshFilter()
|
||||||
{
|
{
|
||||||
|
@ -2323,6 +2336,7 @@ ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
|
||||||
}
|
}
|
||||||
auto msgId = message["id"];
|
auto msgId = message["id"];
|
||||||
auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
|
auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
|
||||||
|
conversation.interactions->editMessage(msgId, msg);
|
||||||
auto downloadFile = false;
|
auto downloadFile = false;
|
||||||
if (msg.type == interaction::Type::INITIAL) {
|
if (msg.type == interaction::Type::INITIAL) {
|
||||||
allLoaded = true;
|
allLoaded = true;
|
||||||
|
@ -2356,6 +2370,8 @@ ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
|
||||||
msg.body = interaction::getContactInteractionString(bestName,
|
msg.body = interaction::getContactInteractionString(bestName,
|
||||||
interaction::to_action(
|
interaction::to_action(
|
||||||
message["action"]));
|
message["action"]));
|
||||||
|
} else if (msg.type == interaction::Type::EDITED) {
|
||||||
|
conversation.interactions->addEdition(msgId, msg, false);
|
||||||
}
|
}
|
||||||
insertSwarmInteraction(msgId, msg, conversation, true);
|
insertSwarmInteraction(msgId, msg, conversation, true);
|
||||||
if (downloadFile) {
|
if (downloadFile) {
|
||||||
|
@ -2380,7 +2396,6 @@ ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this case, we only have loaded merge commits. Load more messages
|
// In this case, we only have loaded merge commits. Load more messages
|
||||||
ConfigurationManager::instance().loadConversationMessages(linked.owner.id,
|
ConfigurationManager::instance().loadConversationMessages(linked.owner.id,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
@ -2427,6 +2442,7 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
|
||||||
}
|
}
|
||||||
auto msgId = message["id"];
|
auto msgId = message["id"];
|
||||||
auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
|
auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
|
||||||
|
conversation.interactions->editMessage(msgId, msg);
|
||||||
api::datatransfer::Info info;
|
api::datatransfer::Info info;
|
||||||
QString fileId;
|
QString fileId;
|
||||||
|
|
||||||
|
@ -2464,6 +2480,8 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
|
||||||
} else if (msg.type == interaction::Type::TEXT
|
} else if (msg.type == interaction::Type::TEXT
|
||||||
&& msg.authorUri != linked.owner.profileInfo.uri) {
|
&& msg.authorUri != linked.owner.profileInfo.uri) {
|
||||||
conversation.unreadMessages++;
|
conversation.unreadMessages++;
|
||||||
|
} else if (msg.type == interaction::Type::EDITED) {
|
||||||
|
conversation.interactions->addEdition(msgId, msg, true);
|
||||||
}
|
}
|
||||||
if (!insertSwarmInteraction(msgId, msg, conversation, false)) {
|
if (!insertSwarmInteraction(msgId, msg, conversation, false)) {
|
||||||
// message already exists
|
// message already exists
|
||||||
|
@ -2510,7 +2528,7 @@ ConversationModelPimpl::slotConversationProfileUpdated(const QString& accountId,
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ConversationModelPimpl::insertSwarmInteraction(const QString& interactionId,
|
ConversationModelPimpl::insertSwarmInteraction(const QString& interactionId,
|
||||||
const interaction::Info& interaction,
|
interaction::Info& interaction,
|
||||||
conversation::Info& conversation,
|
conversation::Info& conversation,
|
||||||
bool insertAtBegin)
|
bool insertAtBegin)
|
||||||
{
|
{
|
||||||
|
@ -2518,6 +2536,11 @@ ConversationModelPimpl::insertSwarmInteraction(const QString& interactionId,
|
||||||
auto itExists = conversation.interactions->find(interactionId);
|
auto itExists = conversation.interactions->find(interactionId);
|
||||||
if (itExists != conversation.interactions->end()) {
|
if (itExists != conversation.interactions->end()) {
|
||||||
// Erase interaction if exists, as it will be updated via a re-insertion
|
// Erase interaction if exists, as it will be updated via a re-insertion
|
||||||
|
if (itExists->second.previousBodies.size() != 0) {
|
||||||
|
// If the message was edited, we should keep this state
|
||||||
|
interaction.body = itExists->second.body;
|
||||||
|
interaction.previousBodies = itExists->second.previousBodies;
|
||||||
|
}
|
||||||
itExists = conversation.interactions->erase(itExists);
|
itExists = conversation.interactions->erase(itExists);
|
||||||
if (itExists != conversation.interactions->end()) {
|
if (itExists != conversation.interactions->end()) {
|
||||||
// next interaction doesn't have parent anymore.
|
// next interaction doesn't have parent anymore.
|
||||||
|
|
|
@ -176,6 +176,7 @@ MessageListModel::clear()
|
||||||
Q_EMIT beginResetModel();
|
Q_EMIT beginResetModel();
|
||||||
interactions_.clear();
|
interactions_.clear();
|
||||||
replyTo_.clear();
|
replyTo_.clear();
|
||||||
|
editedBodies_.clear();
|
||||||
Q_EMIT endResetModel();
|
Q_EMIT endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +411,13 @@ MessageListModel::dataForItem(item_t item, int, int role) const
|
||||||
return QVariant(item.second.commit["uri"]);
|
return QVariant(item.second.commit["uri"]);
|
||||||
case Role::ContactAction:
|
case Role::ContactAction:
|
||||||
return QVariant(item.second.commit["action"]);
|
return QVariant(item.second.commit["action"]);
|
||||||
|
case Role::PreviousBodies: {
|
||||||
|
QVariantList variantList;
|
||||||
|
for (int i = 0; i < item.second.previousBodies.size(); i++) {
|
||||||
|
variantList.append(QVariant::fromValue(item.second.previousBodies[i]));
|
||||||
|
}
|
||||||
|
return variantList;
|
||||||
|
}
|
||||||
case Role::ReplyTo:
|
case Role::ReplyTo:
|
||||||
return QVariant(replyId);
|
return QVariant(replyId);
|
||||||
case Role::ReplyToAuthor:
|
case Role::ReplyToAuthor:
|
||||||
|
@ -541,12 +549,58 @@ MessageListModel::emitDataChanged(const QString& msgId, VectorInt roles)
|
||||||
Q_EMIT dataChanged(modelIndex, modelIndex, roles);
|
Q_EMIT dataChanged(modelIndex, modelIndex, roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MessageListModel::addEdition(const QString& msgId, const interaction::Info& info, bool end)
|
||||||
|
{
|
||||||
|
auto editedId = info.commit["edit"];
|
||||||
|
if (editedId.isEmpty())
|
||||||
|
return;
|
||||||
|
auto& edited = editedBodies_[editedId];
|
||||||
|
auto value = interaction::Body {msgId, info.body, info.timestamp};
|
||||||
|
if (end)
|
||||||
|
edited.push_back(value);
|
||||||
|
else
|
||||||
|
edited.push_front(value);
|
||||||
|
auto editedIt = find(editedId);
|
||||||
|
if (editedIt != interactions_.end()) {
|
||||||
|
// If already there, we can update the content
|
||||||
|
editMessage(editedId, editedIt->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MessageListModel::editMessage(const QString& msgId, interaction::Info& info)
|
||||||
|
{
|
||||||
|
auto it = editedBodies_.find(msgId);
|
||||||
|
if (it != editedBodies_.end()) {
|
||||||
|
if (info.previousBodies.isEmpty()) {
|
||||||
|
info.previousBodies.push_back(interaction::Body {msgId, info.body, info.timestamp});
|
||||||
|
}
|
||||||
|
// Find if already added (because MessageReceived can be triggered
|
||||||
|
// multiple times for same message)
|
||||||
|
for (const auto& editedBody : *it) {
|
||||||
|
auto itCommit = std::find_if(info.previousBodies.begin(),
|
||||||
|
info.previousBodies.end(),
|
||||||
|
[&](const auto& element) {
|
||||||
|
return element.commitId == editedBody.commitId;
|
||||||
|
});
|
||||||
|
if (itCommit == info.previousBodies.end()) {
|
||||||
|
info.previousBodies.push_back(editedBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.body = it->rbegin()->body;
|
||||||
|
editedBodies_.erase(it);
|
||||||
|
emitDataChanged(msgId, {MessageList::Role::Body, MessageList::Role::PreviousBodies});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
MessageListModel::lastMessageUid() const
|
MessageListModel::lastMessageUid() const
|
||||||
{
|
{
|
||||||
for (auto it = interactions_.rbegin(); it != interactions_.rend(); ++it) {
|
for (auto it = interactions_.rbegin(); it != interactions_.rend(); ++it) {
|
||||||
auto lastType = it->second.type;
|
auto lastType = it->second.type;
|
||||||
if (lastType != interaction::Type::MERGE and !it->second.body.isEmpty()) {
|
if (lastType != interaction::Type::MERGE and lastType != interaction::Type::EDITED
|
||||||
|
and !it->second.body.isEmpty()) {
|
||||||
return it->first;
|
return it->first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ struct Info;
|
||||||
X(ActionUri) \
|
X(ActionUri) \
|
||||||
X(LinkPreviewInfo) \
|
X(LinkPreviewInfo) \
|
||||||
X(Linkified) \
|
X(Linkified) \
|
||||||
|
X(PreviousBodies) \
|
||||||
X(ReplyTo) \
|
X(ReplyTo) \
|
||||||
X(ReplyToBody) \
|
X(ReplyToBody) \
|
||||||
X(ReplyToAuthor) \
|
X(ReplyToAuthor) \
|
||||||
|
@ -130,6 +131,8 @@ public:
|
||||||
|
|
||||||
Q_SIGNAL void timestampUpdate();
|
Q_SIGNAL void timestampUpdate();
|
||||||
|
|
||||||
|
void addEdition(const QString& msgId, const interaction::Info& info, bool end);
|
||||||
|
void editMessage(const QString& msgId, interaction::Info& info);
|
||||||
QString lastMessageUid() const;
|
QString lastMessageUid() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -145,6 +148,7 @@ private:
|
||||||
QMap<QString, QString> lastDisplayedMessageUid_;
|
QMap<QString, QString> lastDisplayedMessageUid_;
|
||||||
QMap<QString, QStringList> messageToReaders_;
|
QMap<QString, QStringList> messageToReaders_;
|
||||||
QMap<QString, QStringList> replyTo_;
|
QMap<QString, QStringList> replyTo_;
|
||||||
|
QMap<QString, QVector<interaction::Body>> editedBodies_;
|
||||||
|
|
||||||
void moveMessage(const QString& msgId, const QString& parentId);
|
void moveMessage(const QString& msgId, const QString& parentId);
|
||||||
void insertMessage(int index, item_t& message);
|
void insertMessage(int index, item_t& message);
|
||||||
|
|
|
@ -1047,13 +1047,16 @@ public Q_SLOTS: // METHODS
|
||||||
void sendMessage(const QString& accountId,
|
void sendMessage(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
const QString& message,
|
const QString& message,
|
||||||
const QString& parent)
|
const QString& parent,
|
||||||
|
int flags = 0)
|
||||||
{
|
{
|
||||||
DRing::sendMessage(accountId.toStdString(),
|
DRing::sendMessage(accountId.toStdString(),
|
||||||
conversationId.toStdString(),
|
conversationId.toStdString(),
|
||||||
message.toStdString(),
|
message.toStdString(),
|
||||||
parent.toStdString());
|
parent.toStdString(),
|
||||||
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t loadConversationMessages(const QString& accountId,
|
uint32_t loadConversationMessages(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
const QString& fromId,
|
const QString& fromId,
|
||||||
|
|
Loading…
Add table
Reference in a new issue