Chat message edition

This commit is contained in:
Christophe Deschamps 2025-12-11 08:51:52 +01:00
parent d40045d5bb
commit 1bae93aab5
17 changed files with 232 additions and 11 deletions

View file

@ -120,8 +120,12 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
mHasTextContent = mChatMessageModel->getHasTextContent(); mHasTextContent = mChatMessageModel->getHasTextContent();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
mIsOutgoing = chatmessage->isOutgoing(); mIsOutgoing = chatmessage->isOutgoing();
mIsRetractable = chatmessage->isRetractable(); mIsRetractable =
chatmessage->isOutgoing() && chatmessage->isRetractable() && !chatmessage->getChatRoom()->isReadOnly();
mIsRetracted = chatmessage->isRetracted(); mIsRetracted = chatmessage->isRetracted();
mIsEditable =
chatmessage->isOutgoing() && chatmessage->isEditable() && !chatmessage->getChatRoom()->isReadOnly();
mIsEdited = chatmessage->isEdited();
mIsRemoteMessage = !chatmessage->isOutgoing(); mIsRemoteMessage = !chatmessage->isOutgoing();
mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly());
mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()); mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress());
@ -364,6 +368,13 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
setRetracted(); setRetracted();
}); });
}); });
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::contentEdited,
[this](const std::shared_ptr<linphone::ChatMessage> &message) {
mChatMessageModelConnection->invokeToCore([this] {
mIsEdited = true;
emit edited();
});
});
} }
QList<ImdnStatus> ChatMessageCore::computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message) { QList<ImdnStatus> ChatMessageCore::computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message) {
@ -498,6 +509,10 @@ bool ChatMessageCore::isRetracted() const {
return mIsRetracted; return mIsRetracted;
} }
bool ChatMessageCore::isEdited() const {
return mIsEdited;
}
QString ChatMessageCore::getOwnReaction() const { QString ChatMessageCore::getOwnReaction() const {
return mOwnReaction; return mOwnReaction;
} }

View file

@ -114,6 +114,8 @@ class ChatMessageCore : public QObject, public AbstractObject {
Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT) Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT)
Q_PROPERTY(bool isRetractable MEMBER mIsRetractable CONSTANT) Q_PROPERTY(bool isRetractable MEMBER mIsRetractable CONSTANT)
Q_PROPERTY(bool isRetracted READ isRetracted NOTIFY isRetractedChanged) Q_PROPERTY(bool isRetracted READ isRetracted NOTIFY isRetractedChanged)
Q_PROPERTY(bool isEditable MEMBER mIsEditable CONSTANT)
Q_PROPERTY(bool isEdited READ isEdited NOTIFY edited)
public: public:
static QSharedPointer<ChatMessageCore> create(const std::shared_ptr<linphone::ChatMessage> &chatmessage); static QSharedPointer<ChatMessageCore> create(const std::shared_ptr<linphone::ChatMessage> &chatmessage);
@ -148,6 +150,7 @@ public:
bool isRetracted() const; bool isRetracted() const;
void setRetracted(); void setRetracted();
bool isEdited() const;
QString getOwnReaction() const; QString getOwnReaction() const;
void setOwnReaction(const QString &reaction); void setOwnReaction(const QString &reaction);
@ -183,6 +186,7 @@ signals:
void singletonReactionMapChanged(); void singletonReactionMapChanged();
void ephemeralDurationChanged(int duration); void ephemeralDurationChanged(int duration);
void isRetractedChanged(); void isRetractedChanged();
void edited();
void lDelete(); void lDelete();
void deleted(); void deleted();
@ -224,6 +228,8 @@ private:
int mEphemeralDuration = 0; int mEphemeralDuration = 0;
bool mIsRetractable = false; bool mIsRetractable = false;
bool mIsRetracted = false; bool mIsRetracted = false;
bool mIsEditable = false;
bool mIsEdited = false;
bool mIsOutgoing = false; bool mIsOutgoing = false;
QString mTotalReactionsLabel; QString mTotalReactionsLabel;

View file

@ -64,6 +64,7 @@ void EventLogList::disconnectItem(const QSharedPointer<EventLogCore> &item) {
if (message) { if (message) {
disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr); disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr);
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr); disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
disconnect(message.get(), &ChatMessageCore::edited, this, nullptr);
} }
} }
@ -77,6 +78,20 @@ void EventLogList::connectItem(const QSharedPointer<EventLogCore> &item) {
if (mChatCore) emit mChatCore->lUpdateLastMessage(); if (mChatCore) emit mChatCore->lUpdateLastMessage();
remove(item); remove(item);
}); });
connect(message.get(), &ChatMessageCore::edited, this, [this, item] {
auto eventLogModel = item->getModel();
mCoreModelConnection->invokeToModel([this, eventLogModel, item]() {
auto chatRoom = mChatCore->getModel()->getMonitor();
auto newEventLog = EventLogCore::create(eventLogModel->getEventLog(), chatRoom);
bool wasLastMessage =
mChatCore->getModel()->getLastChatMessage() == eventLogModel->getEventLog()->getChatMessage();
mCoreModelConnection->invokeToCore([this, newEventLog, wasLastMessage, item] {
connectItem(newEventLog);
replace(item, newEventLog);
if (wasLastMessage) mChatCore->setLastMessage(newEventLog->getChatMessageCore());
});
});
});
} }
} }

View file

@ -2225,6 +2225,12 @@
<extracomment>&quot;Reception info&quot;</extracomment> <extracomment>&quot;Reception info&quot;</extracomment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="407"/>
<source>menu_edit_chat_message</source>
<extracomment>&quot;Edit&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/> <location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/>
<source>chat_message_reply</source> <source>chat_message_reply</source>

View file

@ -2218,6 +2218,12 @@
<extracomment>&quot;Reception info&quot;</extracomment> <extracomment>&quot;Reception info&quot;</extracomment>
<translation>Reception info</translation> <translation>Reception info</translation>
</message> </message>
<message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="407"/>
<source>menu_edit_chat_message</source>
<extracomment>&quot;Edit&quot;</extracomment>
<translation>Edit</translation>
</message>
<message> <message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/> <location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/>
<source>chat_message_reply</source> <source>chat_message_reply</source>
@ -2236,6 +2242,12 @@
<extracomment>&quot;Delete&quot;</extracomment> <extracomment>&quot;Delete&quot;</extracomment>
<translation>Delete</translation> <translation>Delete</translation>
</message> </message>
<message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="469"/>
<source>conversation_message_edited_label</source>
<extracomment>&quot;Edited&quot;</extracomment>
<translation>Edited</translation>
</message>
</context> </context>
<context> <context>
<name>ChatMessageContentCore</name> <name>ChatMessageContentCore</name>
@ -5795,6 +5807,12 @@ To enable them in a commercial project, please contact us.</translation>
<extracomment>Reply to %1</extracomment> <extracomment>Reply to %1</extracomment>
<translation>Reply to %1</translation> <translation>Reply to %1</translation>
</message> </message>
<message>
<location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="417"/>
<source>conversation_editing_message_title</source>
<extracomment>Message beeing edited</extracomment>
<translation>Message beeing edited</translation>
</message>
<message> <message>
<location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="617"/> <location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="617"/>
<source>shared_medias_title</source> <source>shared_medias_title</source>
@ -6038,18 +6056,36 @@ To enable them in a commercial project, please contact us.</translation>
<extracomment>Cannot reply to invalid message</extracomment> <extracomment>Cannot reply to invalid message</extracomment>
<translation>Cannot reply to invalid message</translation> <translation>Cannot reply to invalid message</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2123"/>
<source>chat_message_edit_error</source>
<extracomment>Cannot modify invalid message</extracomment>
<translation>Cannot modify invalid message</translation>
</message>
<message> <message>
<location filename="../../tool/Utils.cpp" line="2129"/> <location filename="../../tool/Utils.cpp" line="2129"/>
<source>info_popup_reply_message_error</source> <source>info_popup_reply_message_error</source>
<extracomment>Could not send reply message : %1</extracomment> <extracomment>Could not send reply message : %1</extracomment>
<translation>Could not send reply message : %1</translation> <translation>Could not send reply message : %1</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2129"/>
<source>info_popup_edited_message_error</source>
<extracomment>Could not send edited message : %1</extracomment>
<translation>Could not send edited message : %1</translation>
</message>
<message> <message>
<location filename="../../tool/Utils.cpp" line="2156"/> <location filename="../../tool/Utils.cpp" line="2156"/>
<source>info_popup_send_reply_message_error_message</source> <source>info_popup_send_reply_message_error_message</source>
<extracomment>Failed to create reply message</extracomment> <extracomment>Failed to create reply message</extracomment>
<translation>Failed to create reply message</translation> <translation>Failed to create reply message</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2156"/>
<source>info_popup_send_edited_message_error_message</source>
<extracomment>Failed to create edited message</extracomment>
<translation>Failed to create edited message</translation>
</message>
<message numerus="yes"> <message numerus="yes">
<location filename="../../tool/Utils.cpp" line="2278"/> <location filename="../../tool/Utils.cpp" line="2278"/>
<source>nHour</source> <source>nHour</source>

View file

@ -2218,6 +2218,12 @@
<extracomment>&quot;Reception info&quot;</extracomment> <extracomment>&quot;Reception info&quot;</extracomment>
<translation>Info de réception</translation> <translation>Info de réception</translation>
</message> </message>
<message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="407"/>
<source>menu_edit_chat_message</source>
<extracomment>&quot;Edit&quot;</extracomment>
<translation>Modifier</translation>
</message>
<message> <message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/> <location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="419"/>
<source>chat_message_reply</source> <source>chat_message_reply</source>
@ -2236,6 +2242,12 @@
<extracomment>&quot;Delete&quot;</extracomment> <extracomment>&quot;Delete&quot;</extracomment>
<translation>Supprimer</translation> <translation>Supprimer</translation>
</message> </message>
<message>
<location filename="../../view/Control/Display/Chat/ChatMessage.qml" line="469"/>
<source>conversation_message_edited_label</source>
<extracomment>&quot;Edited&quot;</extracomment>
<translation>Modifié</translation>
</message>
</context> </context>
<context> <context>
<name>ChatMessageContentCore</name> <name>ChatMessageContentCore</name>
@ -5795,6 +5807,12 @@ Pour les activer dans un projet commercial, merci de nous contacter.</translatio
<extracomment>Reply to %1</extracomment> <extracomment>Reply to %1</extracomment>
<translation>Réponse à %1</translation> <translation>Réponse à %1</translation>
</message> </message>
<message>
<location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="417"/>
<source>conversation_editing_message_title</source>
<extracomment>Message beeing edited</extracomment>
<translation>Modification du message</translation>
</message>
<message> <message>
<location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="617"/> <location filename="../../view/Page/Form/Chat/SelectedChatView.qml" line="617"/>
<source>shared_medias_title</source> <source>shared_medias_title</source>
@ -6210,18 +6228,36 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>Cannot reply to invalid message</extracomment> <extracomment>Cannot reply to invalid message</extracomment>
<translation>Impossible de répondre : message invalide</translation> <translation>Impossible de répondre : message invalide</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2123"/>
<source>chat_message_edit_error</source>
<extracomment>Cannot modify invalid message</extracomment>
<translation>Impossible de modifier le message : message invalide</translation>
</message>
<message> <message>
<location filename="../../tool/Utils.cpp" line="2129"/> <location filename="../../tool/Utils.cpp" line="2129"/>
<source>info_popup_reply_message_error</source> <source>info_popup_reply_message_error</source>
<extracomment>Could not send reply message : %1</extracomment> <extracomment>Could not send reply message : %1</extracomment>
<translation>Impossible d&apos;envoyer la réponse : %1</translation> <translation>Impossible d&apos;envoyer la réponse : %1</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2129"/>
<source>info_popup_edited_message_error</source>
<extracomment>Could not send edited message : %1</extracomment>
<translation>Impossible d&apos;envoyer le message modifié : %1</translation>
</message>
<message> <message>
<location filename="../../tool/Utils.cpp" line="2156"/> <location filename="../../tool/Utils.cpp" line="2156"/>
<source>info_popup_send_reply_message_error_message</source> <source>info_popup_send_reply_message_error_message</source>
<extracomment>Failed to create reply message</extracomment> <extracomment>Failed to create reply message</extracomment>
<translation>Impossible de créer le message</translation> <translation>Impossible de créer le message</translation>
</message> </message>
<message>
<location filename="../../tool/Utils.cpp" line="2156"/>
<source>info_popup_send_edited_message_error_message</source>
<extracomment>Failed to create edited message</extracomment>
<translation>Impossible de créer le message modifié</translation>
</message>
<message> <message>
<location filename="../../tool/Utils.cpp" line="2194"/> <location filename="../../tool/Utils.cpp" line="2194"/>
<source>info_popup_send_voice_message_error_message</source> <source>info_popup_send_voice_message_error_message</source>

View file

@ -185,6 +185,11 @@ ChatModel::createReplyMessage(const std::shared_ptr<linphone::ChatMessage> &mess
return mMonitor->createReplyMessage(message); return mMonitor->createReplyMessage(message);
} }
std::shared_ptr<linphone::ChatMessage>
ChatModel::createReplacesMessage(const std::shared_ptr<linphone::ChatMessage> &message) {
return mMonitor->createReplacesMessage(message);
}
std::shared_ptr<linphone::ChatMessage> std::shared_ptr<linphone::ChatMessage>
ChatModel::createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message) { ChatModel::createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message) {
return mMonitor->createForwardMessage(message); return mMonitor->createForwardMessage(message);

View file

@ -68,6 +68,7 @@ public:
std::shared_ptr<linphone::ChatMessage> createReplyMessage(const std::shared_ptr<linphone::ChatMessage> &message); std::shared_ptr<linphone::ChatMessage> createReplyMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message); std::shared_ptr<linphone::ChatMessage> createForwardMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createReplacesMessage(const std::shared_ptr<linphone::ChatMessage> &message);
std::shared_ptr<linphone::ChatMessage> createTextMessageFromText(QString text); std::shared_ptr<linphone::ChatMessage> createTextMessageFromText(QString text);
std::shared_ptr<linphone::ChatMessage> createMessage(QString text, std::shared_ptr<linphone::ChatMessage> createMessage(QString text,

View file

@ -199,3 +199,7 @@ void ChatMessageModel::onEphemeralMessageDeleted(const std::shared_ptr<linphone:
void ChatMessageModel::onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) { void ChatMessageModel::onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) {
emit retracted(message); emit retracted(message);
} }
void ChatMessageModel::onContentEdited(const std::shared_ptr<linphone::ChatMessage> &message) {
emit contentEdited(message);
}

View file

@ -97,6 +97,7 @@ signals:
void ephemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message); void ephemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message);
void ephemeralMessageTimeUpdated(const std::shared_ptr<linphone::ChatMessage> &message, int expireTime); void ephemeralMessageTimeUpdated(const std::shared_ptr<linphone::ChatMessage> &message, int expireTime);
void retracted(const std::shared_ptr<linphone::ChatMessage> &message); void retracted(const std::shared_ptr<linphone::ChatMessage> &message);
void contentEdited(const std::shared_ptr<linphone::ChatMessage> &message);
private: private:
linphone::ChatMessage::State mMessageState; linphone::ChatMessage::State mMessageState;
@ -133,6 +134,7 @@ private:
void onEphemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatMessage> &message) override; void onEphemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message) override; void onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) override; void onRetracted(const std::shared_ptr<linphone::ChatMessage> &message) override;
void onContentEdited(const std::shared_ptr<linphone::ChatMessage> &message) override;
}; };
#endif #endif

View file

@ -2159,6 +2159,50 @@ void Utils::sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString
}); });
} }
void Utils::sendReplaceMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files) {
auto chatModel = chatGui && chatGui->mCore ? chatGui->mCore->getModel() : nullptr;
auto chatMessageModel = message && message->mCore ? message->mCore->getModel() : nullptr;
if (!chatModel || !chatMessageModel) {
//: Cannot edit to invalid message
QString error = !chatMessageModel ? tr("chat_message_edit_error")
//: Error in the chat
: tr("chat_error");
//: Error
showInformationPopup(tr("info_popup_error_title"),
//: Could not send edited message : %1
tr("info_popup_edited_message_error").arg(error));
return;
}
QList<std::shared_ptr<ChatMessageContentModel>> filesContent;
for (auto &file : files) {
auto contentGui = qvariant_cast<ChatMessageContentGui *>(file);
if (contentGui) {
auto contentCore = contentGui->mCore;
filesContent.append(contentCore->getContentModel());
}
}
App::postModelAsync([chatModel, chatMessageModel, text, filesContent] {
mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO));
auto chat = chatModel->getMonitor();
auto messageToEdit = chatMessageModel->getMonitor();
auto linMessage = chatModel->createReplacesMessage(messageToEdit);
if (linMessage) {
linMessage->addUtf8TextContent(Utils::appStringToCoreString(text));
for (auto &content : filesContent) {
linMessage->addFileContent(content->getContent());
}
linMessage->send();
} else {
App::postCoreAsync([] {
//: Error
showInformationPopup(tr("info_popup_error_title"),
//: Failed to create edited message
tr("info_popup_send_edited_message_error_message"));
});
}
});
}
VariantObject *Utils::createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) { VariantObject *Utils::createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) {
VariantObject *data = new VariantObject("createVoiceRecordingMessage"); VariantObject *data = new VariantObject("createVoiceRecordingMessage");
if (!data) return nullptr; if (!data) return nullptr;

View file

@ -183,6 +183,8 @@ public:
Q_INVOKABLE static void Q_INVOKABLE static void
sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files); sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files);
Q_INVOKABLE static void forwardMessageTo(ChatMessageGui *message, ChatGui *chatGui); Q_INVOKABLE static void forwardMessageTo(ChatMessageGui *message, ChatGui *chatGui);
Q_INVOKABLE static void
sendReplaceMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files);
Q_INVOKABLE static void sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui); Q_INVOKABLE static void sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui);
Q_INVOKABLE static QString getEphemeralFormatedTime(int selectedTime); Q_INVOKABLE static QString getEphemeralFormatedTime(int selectedTime);

View file

@ -32,6 +32,7 @@ Control.Control {
leftPadding: isRemoteMessage ? Utils.getSizeWithScreenRatio(5) : 0 leftPadding: isRemoteMessage ? Utils.getSizeWithScreenRatio(5) : 0
signal messageDeletionRequested() signal messageDeletionRequested()
signal messageEditionRequested()
signal isFileHoveringChanged(bool isFileHovering) signal isFileHoveringChanged(bool isFileHovering)
signal showReactionsForMessageRequested() signal showReactionsForMessageRequested()
signal showImdnStatusForMessageRequested() signal showImdnStatusForMessageRequested()
@ -297,16 +298,26 @@ Control.Control {
} }
} }
RowLayout { RowLayout {
spacing: mainItem.isRemoteMessage ? 0 : Utils.getSizeWithScreenRatio(5) spacing: mainItem.isRemoteMessage && !mainItem.chatMessage.core.isEdited ? 0 : Utils.getSizeWithScreenRatio(5)
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: childrenRect.height Layout.preferredHeight: childrenRect.height
Text {
Layout.alignment: Qt.AlignVCenter
text: qsTr("conversation_message_edited_label")
visible: mainItem.chatMessage.core.isEdited
color: DefaultStyle.main2_500_main
font {
pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight
}
}
Text { Text {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
text: UtilsCpp.formatDate(mainItem.chatMessage.core.timestamp, true, false, "dd/MM") text: UtilsCpp.formatDate(mainItem.chatMessage.core.timestamp, true, false, "dd/MM")
color: DefaultStyle.main2_500_main color: DefaultStyle.main2_500_main
font { font {
pixelSize: Typography.p3.pixelSize pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight weight: Typography.p3.weight
} }
} }
EffectImage { EffectImage {
@ -423,6 +434,19 @@ Control.Control {
optionsMenu.close() optionsMenu.close()
} }
} }
IconLabelButton {
inverseLayout: true
//: "Edit"
text: qsTr("menu_edit_chat_message")
visible: mainItem.chatMessage.core.isEditable
icon.source: AppIcons.pencil
Layout.fillWidth: true
Layout.preferredHeight: Utils.getSizeWithScreenRatio(45)
onClicked: {
mainItem.messageEditionRequested()
optionsMenu.close()
}
}
IconLabelButton { IconLabelButton {
inverseLayout: true inverseLayout: true
visible: !mainItem.chatMessage.core.isRetracted visible: !mainItem.chatMessage.core.isRetracted

View file

@ -25,6 +25,7 @@ ListView {
signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage)
signal replyToMessageRequested(ChatMessageGui chatMessage) signal replyToMessageRequested(ChatMessageGui chatMessage)
signal forwardMessageRequested(ChatMessageGui chatMessage) signal forwardMessageRequested(ChatMessageGui chatMessage)
signal editMessageRequested(ChatMessageGui chatMessage)
signal requestHighlight(int indexToHighlight) signal requestHighlight(int indexToHighlight)
signal requestAutoPlayVoiceRecording(int indexToPlay) signal requestAutoPlayVoiceRecording(int indexToPlay)
currentIndex: -1 currentIndex: -1
@ -315,6 +316,7 @@ ListView {
chatMessage.core.lDelete() chatMessage.core.lDelete()
} }
} }
onMessageEditionRequested: mainItem.editMessageRequested(chatMessage)
onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(chatMessage) onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(chatMessage)
onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(chatMessage) onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(chatMessage)
onReplyToMessageRequested: mainItem.replyToMessageRequested(chatMessage) onReplyToMessageRequested: mainItem.replyToMessageRequested(chatMessage)

View file

@ -23,6 +23,7 @@ Control.Control {
// disable record button if call ongoing // disable record button if call ongoing
property bool callOngoing: false property bool callOngoing: false
property bool isEditing: false
property ChatGui chat property ChatGui chat
@ -78,6 +79,7 @@ Control.Control {
spacing: Utils.getSizeWithScreenRatio(16) spacing: Utils.getSizeWithScreenRatio(16)
PopupButton { PopupButton {
id: emojiPickerButton id: emojiPickerButton
visible: !mainItem.isEditing
style: ButtonStyle.noBackground style: ButtonStyle.noBackground
icon.source: checked ? AppIcons.closeX : AppIcons.smiley icon.source: checked ? AppIcons.closeX : AppIcons.smiley
popup.width: Utils.getSizeWithScreenRatio(393) popup.width: Utils.getSizeWithScreenRatio(393)
@ -189,7 +191,7 @@ Control.Control {
//: Cannot record a message while a call is ongoing //: Cannot record a message while a call is ongoing
ToolTip.text: qsTr("cannot_record_while_in_call_tooltip") ToolTip.text: qsTr("cannot_record_while_in_call_tooltip")
enabled: !mainItem.callOngoing enabled: !mainItem.callOngoing
visible: !mainItem.callOngoing && sendingTextArea.text.length === 0 && mainItem.selectedFilesCount === 0 visible: !mainItem.callOngoing && sendingTextArea.text.length === 0 && mainItem.selectedFilesCount === 0 && !mainItem.isEditing
style: ButtonStyle.noBackground style: ButtonStyle.noBackground
hoverEnabled: true hoverEnabled: true
icon.source: AppIcons.microphone icon.source: AppIcons.microphone
@ -202,7 +204,7 @@ Control.Control {
Layout.preferredHeight: height Layout.preferredHeight: height
visible: sendingTextArea.text.length !== 0 || mainItem.selectedFilesCount > 0 visible: sendingTextArea.text.length !== 0 || mainItem.selectedFilesCount > 0
style: ButtonStyle.noBackgroundOrange style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.paperPlaneRight icon.source: mainItem.isEditing ? AppIcons.pencil : AppIcons.paperPlaneRight
onClicked: { onClicked: {
mainItem.sendMessage() mainItem.sendMessage()
} }

View file

@ -21,6 +21,7 @@ FocusScope {
property CallGui call property CallGui call
property alias callHeaderContent: splitPanel.header.contentItem property alias callHeaderContent: splitPanel.header.contentItem
property bool replyingToMessage: false property bool replyingToMessage: false
property bool editingMessage: false
enum PanelType { MessageReactions, SharedFiles, Medias, ImdnStatus, ForwardToList, ManageParticipants, EphemeralSettings, None} enum PanelType { MessageReactions, SharedFiles, Medias, ImdnStatus, ForwardToList, ManageParticipants, EphemeralSettings, None}
signal oneOneCall(bool video) signal oneOneCall(bool video)
@ -299,11 +300,19 @@ FocusScope {
onReplyToMessageRequested: (chatMessage) => { onReplyToMessageRequested: (chatMessage) => {
mainItem.chatMessage = chatMessage mainItem.chatMessage = chatMessage
mainItem.replyingToMessage = true mainItem.replyingToMessage = true
if (mainItem.editingMessage) mainItem.editingMessage = false
} }
onForwardMessageRequested: (chatMessage) => { onForwardMessageRequested: (chatMessage) => {
mainItem.chatMessage = chatMessage mainItem.chatMessage = chatMessage
contentLoader.panelType = SelectedChatView.PanelType.ForwardToList contentLoader.panelType = SelectedChatView.PanelType.ForwardToList
detailsPanel.visible = true detailsPanel.visible = true
if (mainItem.editingMessage) mainItem.editingMessage = false
}
onEditMessageRequested: (chatMessage) => {
mainItem.chatMessage = chatMessage
mainItem.editingMessage = true
if (mainItem.replyingToMessage) mainItem.replyingToMessage = false
messageSender.text = chatMessage.core.text
} }
} }
ScrollBar { ScrollBar {
@ -367,7 +376,7 @@ FocusScope {
} }
Control.Control { Control.Control {
id: selectedFilesArea id: selectedFilesArea
visible: selectedFiles.count > 0 || mainItem.replyingToMessage visible: selectedFiles.count > 0 || mainItem.replyingToMessage || mainItem.editingMessage
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: implicitHeight Layout.preferredHeight: implicitHeight
topPadding: Utils.getSizeWithScreenRatio(12) topPadding: Utils.getSizeWithScreenRatio(12)
@ -384,7 +393,12 @@ FocusScope {
style: ButtonStyle.noBackground style: ButtonStyle.noBackground
onClicked: { onClicked: {
contents.clear() contents.clear()
mainItem.replyingToMessage = false if (mainItem.replyingToMessage)
mainItem.replyingToMessage = false
else if (mainItem.editingMessage) {
mainItem.editingMessage = false
messageSender.text = ""
}
} }
} }
background: Item{ background: Item{
@ -410,11 +424,13 @@ FocusScope {
ColumnLayout { ColumnLayout {
id: replyLayout id: replyLayout
spacing: 0 spacing: 0
visible: mainItem.chatMessage && mainItem.replyingToMessage visible: mainItem.chatMessage && (mainItem.replyingToMessage || mainItem.editingMessage)
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
//: Reply to %1 //: Reply to %1
text: mainItem.chatMessage ? qsTr("reply_to_label").arg(UtilsCpp.boldTextPart(mainItem.chatMessage.core.fromName, mainItem.chatMessage.core.fromName)) : "" text: mainItem.replyingToMessage ?
(mainItem.chatMessage ? qsTr("reply_to_label").arg(UtilsCpp.boldTextPart(mainItem.chatMessage.core.fromName, mainItem.chatMessage.core.fromName)) : "")
: qsTr("conversation_editing_message_title")
color: DefaultStyle.main2_500_main color: DefaultStyle.main2_500_main
font { font {
pixelSize: Typography.p3.pixelSize pixelSize: Typography.p3.pixelSize
@ -489,6 +505,7 @@ FocusScope {
chat: mainItem.chat chat: mainItem.chat
selectedFilesCount: contents.count selectedFilesCount: contents.count
callOngoing: mainItem.call != null callOngoing: mainItem.call != null
isEditing: mainItem.editingMessage
onChatChanged: { onChatChanged: {
if (chat) messageSender.text = mainItem.chat.core.sendingText if (chat) messageSender.text = mainItem.chat.core.sendingText
} }
@ -507,6 +524,10 @@ FocusScope {
mainItem.replyingToMessage = false mainItem.replyingToMessage = false
UtilsCpp.sendReplyMessage(mainItem.chatMessage, mainItem.chat, text, filesContents) UtilsCpp.sendReplyMessage(mainItem.chatMessage, mainItem.chat, text, filesContents)
} }
else if (mainItem.editingMessage) {
UtilsCpp.sendReplaceMessage(mainItem.chatMessage, mainItem.chat, text, filesContents)
mainItem.editingMessage = false
}
else if (filesContents.length === 0) else if (filesContents.length === 0)
mainItem.chat.core.lSendTextMessage(text) mainItem.chat.core.lSendTextMessage(text)
else mainItem.chat.core.lSendMessage(text, filesContents) else mainItem.chat.core.lSendMessage(text, filesContents)