Add send/received indicator

This commit is contained in:
Nicolas Werner 2019-09-18 22:58:25 +02:00
parent 5200db17e9
commit 240b3a566b
5 changed files with 133 additions and 7 deletions

View File

@ -0,0 +1,44 @@
import QtQuick 2.5
import QtQuick.Controls 2.5
import QtGraphicalEffects 1.0
import com.github.nheko 1.0
Rectangle {
id: indicator
property int state: 0
color: "transparent"
width: 16
height: 16
ToolTip.visible: ma.containsMouse
ToolTip.text: switch (state) {
case MtxEvent.Failed: return qsTr("Failed")
case MtxEvent.Sent: return qsTr("Sent")
case MtxEvent.Received: return qsTr("Received")
case MtxEvent.Read: return qsTr("Read")
default: return qsTr("Empty")
}
MouseArea{
id: ma
anchors.fill: parent
hoverEnabled: true
}
Image {
id: stateImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: switch (indicator.state) {
case MtxEvent.Failed: return "qrc:/icons/icons/ui/remove-symbol.png"
case MtxEvent.Sent: return "qrc:/icons/icons/ui/clock.png"
case MtxEvent.Received: return "qrc:/icons/icons/ui/checkmark.png"
case MtxEvent.Read: return "qrc:/icons/icons/ui/double-tick-indicator.png"
default: return ""
}
}
ColorOverlay {
anchors.fill: stateImg
source: stateImg
color: colors.buttonText
}
}

View File

@ -116,6 +116,11 @@ Rectangle {
property variant eventData: model
}
StatusIndicator {
state: model.state
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
}
Button {
Layout.alignment: Qt.AlignRight | Qt.AlignTop

View File

@ -117,6 +117,7 @@
<qresource prefix="/">
<file>qml/TimelineView.qml</file>
<file>qml/Avatar.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/delegates/TextMessage.qml</file>
<file>qml/delegates/NoticeMessage.qml</file>
<file>qml/delegates/ImageMessage.qml</file>

View File

@ -240,6 +240,35 @@ TimelineModel::TimelineModel(QString room_id, QObject *parent)
{
connect(
this, &TimelineModel::oldMessagesRetrieved, this, &TimelineModel::addBackwardsEvents);
connect(this, &TimelineModel::messageFailed, this, [this](QString txn_id) {
pending.remove(txn_id);
failed.insert(txn_id);
int idx = idToIndex(txn_id);
if (idx < 0) {
nhlog::ui()->warn("Failed index out of range");
return;
}
emit dataChanged(index(idx, 0), index(idx, 0));
});
connect(this, &TimelineModel::messageSent, this, [this](QString txn_id, QString event_id) {
int idx = idToIndex(txn_id);
if (idx < 0) {
nhlog::ui()->warn("Sent index out of range");
return;
}
eventOrder[idx] = event_id;
auto ev = events.value(txn_id);
ev = boost::apply_visitor(
[event_id](const auto &e) -> mtx::events::collections::TimelineEvents {
auto eventCopy = e;
eventCopy.event_id = event_id.toStdString();
return eventCopy;
},
ev);
events.remove(txn_id);
events.insert(event_id, ev);
emit dataChanged(index(idx, 0), index(idx, 0));
});
}
QHash<int, QByteArray>
@ -258,6 +287,7 @@ TimelineModel::roleNames() const
{Width, "width"},
{ProportionalHeight, "proportionalHeight"},
{Id, "id"},
{State, "state"},
};
}
int
@ -341,6 +371,13 @@ TimelineModel::data(const QModelIndex &index, int role) const
[](const auto &e) -> double { return eventPropHeight(e); }, event));
case Id:
return id;
case State:
if (failed.contains(id))
return qml_mtx_events::Failed;
else if (pending.contains(id))
return qml_mtx_events::Sent;
else
return qml_mtx_events::Received;
default:
return QVariant();
}
@ -378,8 +415,12 @@ TimelineModel::internalAddEvents(
QString id =
boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e);
if (this->events.contains(id))
if (this->events.contains(id)) {
this->events.insert(id, e);
int idx = idToIndex(id);
emit dataChanged(index(idx, 0), index(idx, 0));
continue;
}
if (auto redaction =
boost::get<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(&e)) {

View File

@ -6,6 +6,7 @@
#include <QColor>
#include <QDate>
#include <QHash>
#include <QSet>
#include "Logging.h"
#include "MatrixClient.h"
@ -68,6 +69,21 @@ enum EventType
UnknownMessage,
};
Q_ENUM_NS(EventType)
enum EventState
{
//! The plaintext message was received by the server.
Received,
//! At least one of the participants has read the message.
Read,
//! The client sent the message. Not yet received.
Sent,
//! When the message is loaded from cache or backfill.
Empty,
//! When the message failed to send
Failed,
};
Q_ENUM_NS(EventState)
}
struct DecryptionResult
@ -101,6 +117,7 @@ public:
Width,
ProportionalHeight,
Id,
State,
};
QHash<int, QByteArray> roleNames() const override;
@ -137,8 +154,8 @@ private slots:
signals:
void oldMessagesRetrieved(const mtx::responses::Messages &res);
void messageFailed(const std::string txn_id);
void messageSent(const std::string txn_id, std::string event_id);
void messageFailed(QString txn_id);
void messageSent(QString txn_id, QString event_id);
void currentIndexChanged(int index);
private:
@ -148,6 +165,7 @@ private:
const std::vector<mtx::events::collections::TimelineEvents> &timeline);
QHash<QString, mtx::events::collections::TimelineEvents> events;
QSet<QString> pending, failed;
std::vector<QString> eventOrder;
QString room_id_;
@ -164,20 +182,37 @@ template<class T>
void
TimelineModel::sendMessage(const T &msg)
{
auto txn_id = http::client()->generate_txn_id();
auto txn_id = http::client()->generate_txn_id();
mtx::events::RoomEvent<T> msgCopy = {};
msgCopy.content = msg;
msgCopy.type = mtx::events::EventType::RoomMessage;
msgCopy.event_id = txn_id;
msgCopy.sender = http::client()->user_id().to_string();
msgCopy.origin_server_ts = QDateTime::currentMSecsSinceEpoch();
internalAddEvents({msgCopy});
QString txn_id_qstr = QString::fromStdString(txn_id);
beginInsertRows(QModelIndex(),
static_cast<int>(this->eventOrder.size()),
static_cast<int>(this->eventOrder.size()));
pending.insert(txn_id_qstr);
this->eventOrder.insert(this->eventOrder.end(), txn_id_qstr);
endInsertRows();
http::client()->send_room_message<T, mtx::events::EventType::RoomMessage>(
room_id_.toStdString(),
txn_id,
msg,
[this, txn_id](const mtx::responses::EventId &res, mtx::http::RequestErr err) {
[this, txn_id, txn_id_qstr](const mtx::responses::EventId &res,
mtx::http::RequestErr err) {
if (err) {
const int status_code = static_cast<int>(err->status_code);
nhlog::net()->warn("[{}] failed to send message: {} {}",
txn_id,
err->matrix_error.error,
status_code);
emit messageFailed(txn_id);
emit messageFailed(txn_id_qstr);
}
emit messageSent(txn_id, res.event_id.to_string());
emit messageSent(txn_id_qstr, QString::fromStdString(res.event_id.to_string()));
});
}