diff --git a/resources/icons/Receive.svg b/resources/icons/Receive.svg
new file mode 100644
index 00000000..1be71fbd
--- /dev/null
+++ b/resources/icons/Receive.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/app/commoncomponents/SBSMessageBase.qml b/src/app/commoncomponents/SBSMessageBase.qml
index 0f61ff34..e458ec67 100644
--- a/src/app/commoncomponents/SBSMessageBase.qml
+++ b/src/app/commoncomponents/SBSMessageBase.qml
@@ -98,7 +98,6 @@ Control {
Layout.fillHeight: true
}
-
Label {
id: username
@@ -300,10 +299,7 @@ Control {
anchors.verticalCenter: parent.verticalCenter
anchors.right: isOutgoing ? optionButtonItem.right : undefined
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
- visible: CurrentAccount.type !== Profile.Type.SIP
- && root.type !== Interaction.Type.CALL
- && Body !== ""
- && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered)
+ visible: CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered)
source: JamiResources.more_vert_24dp_svg
width: optionButtonItem.width / 2
height: optionButtonItem.height
@@ -358,10 +354,7 @@ Control {
anchors.rightMargin: 5
anchors.right: isOutgoing ? more.left : undefined
anchors.left: !isOutgoing ? more.right : undefined
- visible: CurrentAccount.type !== Profile.Type.SIP
- && root.type !== Interaction.Type.CALL
- && Body !== ""
- && (bubbleArea.bubbleHovered || hovered || more.hovered || bgHandler.hovered)
+ visible: CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || more.hovered || bgHandler.hovered)
onClicked: {
MessagesAdapter.editId = "";
@@ -389,14 +382,14 @@ Control {
property bool bubbleHovered
property string imgSource
- width: (root.type === Interaction.Type.TEXT ? root.textContentWidth + ( IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth): innerContent.childrenRect.width)
+ width: (root.type === Interaction.Type.TEXT ? root.textContentWidth + (IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth) : innerContent.childrenRect.width)
height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0) + (root.bigMsg ? 15 : 0)
HoverHandler {
target: root
enabled: root.type === Interaction.Type.DATA_TRANSFER
onHoveredChanged: {
- root.hoveredLink = enabled && hovered ? bubble.imgSource : ""
+ root.hoveredLink = enabled && hovered ? bubble.imgSource : "";
}
}
@@ -406,12 +399,12 @@ Control {
showTime: IsEmojiOnly && !(root.seq === MsgSeq.last || root.seq === MsgSeq.single) ? false : true
formattedTime: root.formattedTime
- timeColor: IsEmojiOnly || root.timeUnderBubble? (JamiTheme.darkTheme ? "white" : "dark") : (UtilsAdapter.luma(bubble.color) ? "white" : "dark")
+ timeColor: IsEmojiOnly || root.timeUnderBubble ? (JamiTheme.darkTheme ? "white" : "dark") : (UtilsAdapter.luma(bubble.color) ? "white" : "dark")
timeLabel.opacity: 0.5
anchors.bottom: parent.bottom
anchors.right: IsEmojiOnly ? (isOutgoing ? parent.right : undefined) : parent.right
- anchors.left: ((IsEmojiOnly|| root.timeUnderBubble) && !isOutgoing) ? parent.left : undefined
+ anchors.left: ((IsEmojiOnly || root.timeUnderBubble) && !isOutgoing) ? parent.left : undefined
anchors.leftMargin: (IsEmojiOnly && !isOutgoing && emojiReactions.visible) ? bubble.timePosition : 0
anchors.rightMargin: IsEmojiOnly ? ((isOutgoing && emojiReactions.visible) ? bubble.timePosition : 0) : (root.timeUnderBubble ? 0 : 10)
timeLabel.Layout.bottomMargin: {
@@ -432,7 +425,7 @@ Control {
anchors.left: root.bigMsg ? bubble.left : timestampItem.left
anchors.bottom: parent.bottom
anchors.bottomMargin: root.bigMsg || bubble.isDeleted ? 6 : 10
- anchors.leftMargin: root.bigMsg ? 10 : - timestampItem.width - 16
+ anchors.leftMargin: root.bigMsg ? 10 : -timestampItem.width - 16
visible: bubble.isEdited
z: 1
ResponsiveImage {
@@ -489,7 +482,7 @@ Control {
borderColor: root.getBaseColor()
maxWidth: 2 / 3 * maxMsgWidth - JamiTheme.emojiMargins
- state: root.isOutgoing ? "anchorsRight" : (IsEmojiOnly ? "anchorsLeft" :(emojiReactions.width > bubble.width - JamiTheme.emojiMargins ? "anchorsLeft" : "anchorsRight"))
+ state: root.isOutgoing ? "anchorsRight" : (IsEmojiOnly ? "anchorsLeft" : (emojiReactions.width > bubble.width - JamiTheme.emojiMargins ? "anchorsLeft" : "anchorsRight"))
TapHandler {
onTapped: {
@@ -597,7 +590,7 @@ Control {
radius: width / 2
width: 12
height: 12
- border.color: JamiTheme.tintedBlue
+ border.color: JamiTheme.sending
border.width: 1
color: JamiTheme.transparentColor
visible: isOutgoing && Status === Interaction.Status.SENDING
@@ -605,6 +598,21 @@ Control {
anchors.bottom: parent.bottom
}
+ ResponsiveImage {
+ id: sent
+
+ containerHeight: 12
+ containerWidth: 12
+
+ width: 12
+ height: 12
+
+ visible: IsLastSent && !readsOne.visible
+ anchors.bottom: parent.bottom
+
+ source: JamiResources.receive_svg
+ }
+
ReadStatus {
id: readsOne
diff --git a/src/app/net/jami/Constants/JamiTheme.qml b/src/app/net/jami/Constants/JamiTheme.qml
index e976bb7d..0a228c10 100644
--- a/src/app/net/jami/Constants/JamiTheme.qml
+++ b/src/app/net/jami/Constants/JamiTheme.qml
@@ -588,6 +588,7 @@ Item {
property real cornerIconSize: 40
+ property color sending: darkTheme ? "#8c8c8c" : "#7f7f7f"
property color wizardIconColor: darkTheme ? "#8c8c8c" : "#7f7f7f"
// InfoBox
diff --git a/src/libclient/api/interaction.h b/src/libclient/api/interaction.h
index acfbead6..7d2f68da 100644
--- a/src/libclient/api/interaction.h
+++ b/src/libclient/api/interaction.h
@@ -413,6 +413,11 @@ struct Info
Info& operator=(const Info& other) = delete;
Info& operator=(Info&& other) = default;
+ bool sent() const
+ {
+ return status == Status::SUCCESS || status == Status::DISPLAYED || status == Status::TRANSFER_FINISHED;
+ }
+
void init(const MapStringString& message, const QString& accountURI)
{
type = to_type(message["type"]);
diff --git a/src/libclient/api/messagelistmodel.h b/src/libclient/api/messagelistmodel.h
index 582b1dde..f765702e 100644
--- a/src/libclient/api/messagelistmodel.h
+++ b/src/libclient/api/messagelistmodel.h
@@ -57,6 +57,7 @@ struct Info;
X(FileExtension) \
X(Readers) \
X(IsEmojiOnly) \
+ X(IsLastSent) \
X(Index)
namespace MessageList {
@@ -146,6 +147,9 @@ private:
QMap messageToReaders_; // {"messageId": ["peer1", "peer2"]}
QMap> replyTo_;
+ QString lastSent_;
+ int lastSentIdx_ {-1};
+
iterator find(const QString& msgId);
int move(iterator it, const QString& newParentId);
QVariant data(int idx, int role = Qt::DisplayRole) const;
diff --git a/src/libclient/messagelistmodel.cpp b/src/libclient/messagelistmodel.cpp
index e466448a..51f93090 100644
--- a/src/libclient/messagelistmodel.cpp
+++ b/src/libclient/messagelistmodel.cpp
@@ -146,6 +146,14 @@ MessageListModel::insert(const QString& id, const interaction::Info& interaction
}
beginInsertRows(QModelIndex(), index, index);
interactions_.emplace(interactions_.cbegin() + index, id, interaction);
+ // Update last sent if the message is outgoing and successful.
+ if (interaction.sent() && index > lastSentIdx_) {
+ auto oldIdx = indexOfMessage(lastSent_);
+ lastSentIdx_ = index;
+ lastSent_ = id;
+ auto modelIndex = QAbstractListModel::index(oldIdx, 0);
+ Q_EMIT dataChanged(modelIndex, modelIndex, {Role::IsLastSent});
+ }
endInsertRows();
return true;
}
@@ -160,6 +168,14 @@ MessageListModel::append(const QString& id, const interaction::Info& interaction
}
beginInsertRows(QModelIndex(), interactions_.size(), interactions_.size());
interactions_.emplace_back(id, interaction);
+ // Update last sent if the message is outgoing and successful.
+ if (interaction.sent()) {
+ auto oldIdx = indexOfMessage(lastSent_);
+ lastSentIdx_ = interactions_.size() - 1;
+ lastSent_ = id;
+ auto modelIndex = QAbstractListModel::index(oldIdx, 0);
+ Q_EMIT dataChanged(modelIndex, modelIndex, {Role::IsLastSent});
+ }
endInsertRows();
return true;
}
@@ -218,8 +234,18 @@ MessageListModel::updateStatus(const QString& id,
it->second.body = newBody;
roles.push_back(Role::Body);
}
- auto modelIndex = QAbstractListModel::index(indexOfMessage(id), 0);
+ // Update last sent if the message is outgoing and successful.
+ auto idx = indexOfMessage(id);
+ auto modelIndex = QAbstractListModel::index(idx, 0);
Q_EMIT dataChanged(modelIndex, modelIndex, roles);
+ if (it->second.sent() && idx > lastSentIdx_) {
+ auto oldIdx = indexOfMessage(lastSent_);
+ lastSentIdx_ = idx;
+ lastSent_ = id;
+ Q_EMIT dataChanged(modelIndex, modelIndex, {Role::IsLastSent});
+ modelIndex = QAbstractListModel::index(oldIdx, 0);
+ Q_EMIT dataChanged(modelIndex, modelIndex, {Role::IsLastSent});
+ }
return true;
}
@@ -486,6 +512,8 @@ MessageListModel::dataForItem(const item_t& item, int, int role) const
}
return QVariant(item.second.body);
}
+ case Role::IsLastSent:
+ return QVariant(item.first == lastSent_);
case Role::Timestamp:
return QVariant::fromValue(item.second.timestamp);
case Role::Duration: