Add send/received indicator
This commit is contained in:
parent
5200db17e9
commit
240b3a566b
44
resources/qml/StatusIndicator.qml
Normal file
44
resources/qml/StatusIndicator.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -116,6 +116,11 @@ Rectangle {
|
|||||||
property variant eventData: model
|
property variant eventData: model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusIndicator {
|
||||||
|
state: model.state
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>qml/TimelineView.qml</file>
|
<file>qml/TimelineView.qml</file>
|
||||||
<file>qml/Avatar.qml</file>
|
<file>qml/Avatar.qml</file>
|
||||||
|
<file>qml/StatusIndicator.qml</file>
|
||||||
<file>qml/delegates/TextMessage.qml</file>
|
<file>qml/delegates/TextMessage.qml</file>
|
||||||
<file>qml/delegates/NoticeMessage.qml</file>
|
<file>qml/delegates/NoticeMessage.qml</file>
|
||||||
<file>qml/delegates/ImageMessage.qml</file>
|
<file>qml/delegates/ImageMessage.qml</file>
|
||||||
|
@ -240,6 +240,35 @@ TimelineModel::TimelineModel(QString room_id, QObject *parent)
|
|||||||
{
|
{
|
||||||
connect(
|
connect(
|
||||||
this, &TimelineModel::oldMessagesRetrieved, this, &TimelineModel::addBackwardsEvents);
|
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>
|
QHash<int, QByteArray>
|
||||||
@ -258,6 +287,7 @@ TimelineModel::roleNames() const
|
|||||||
{Width, "width"},
|
{Width, "width"},
|
||||||
{ProportionalHeight, "proportionalHeight"},
|
{ProportionalHeight, "proportionalHeight"},
|
||||||
{Id, "id"},
|
{Id, "id"},
|
||||||
|
{State, "state"},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
int
|
int
|
||||||
@ -341,6 +371,13 @@ TimelineModel::data(const QModelIndex &index, int role) const
|
|||||||
[](const auto &e) -> double { return eventPropHeight(e); }, event));
|
[](const auto &e) -> double { return eventPropHeight(e); }, event));
|
||||||
case Id:
|
case Id:
|
||||||
return 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:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -378,8 +415,12 @@ TimelineModel::internalAddEvents(
|
|||||||
QString id =
|
QString id =
|
||||||
boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e);
|
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;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto redaction =
|
if (auto redaction =
|
||||||
boost::get<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(&e)) {
|
boost::get<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(&e)) {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
@ -68,6 +69,21 @@ enum EventType
|
|||||||
UnknownMessage,
|
UnknownMessage,
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(EventType)
|
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
|
struct DecryptionResult
|
||||||
@ -101,6 +117,7 @@ public:
|
|||||||
Width,
|
Width,
|
||||||
ProportionalHeight,
|
ProportionalHeight,
|
||||||
Id,
|
Id,
|
||||||
|
State,
|
||||||
};
|
};
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
@ -137,8 +154,8 @@ private slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void oldMessagesRetrieved(const mtx::responses::Messages &res);
|
void oldMessagesRetrieved(const mtx::responses::Messages &res);
|
||||||
void messageFailed(const std::string txn_id);
|
void messageFailed(QString txn_id);
|
||||||
void messageSent(const std::string txn_id, std::string event_id);
|
void messageSent(QString txn_id, QString event_id);
|
||||||
void currentIndexChanged(int index);
|
void currentIndexChanged(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -148,6 +165,7 @@ private:
|
|||||||
const std::vector<mtx::events::collections::TimelineEvents> &timeline);
|
const std::vector<mtx::events::collections::TimelineEvents> &timeline);
|
||||||
|
|
||||||
QHash<QString, mtx::events::collections::TimelineEvents> events;
|
QHash<QString, mtx::events::collections::TimelineEvents> events;
|
||||||
|
QSet<QString> pending, failed;
|
||||||
std::vector<QString> eventOrder;
|
std::vector<QString> eventOrder;
|
||||||
|
|
||||||
QString room_id_;
|
QString room_id_;
|
||||||
@ -165,19 +183,36 @@ void
|
|||||||
TimelineModel::sendMessage(const T &msg)
|
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>(
|
http::client()->send_room_message<T, mtx::events::EventType::RoomMessage>(
|
||||||
room_id_.toStdString(),
|
room_id_.toStdString(),
|
||||||
txn_id,
|
txn_id,
|
||||||
msg,
|
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) {
|
if (err) {
|
||||||
const int status_code = static_cast<int>(err->status_code);
|
const int status_code = static_cast<int>(err->status_code);
|
||||||
nhlog::net()->warn("[{}] failed to send message: {} {}",
|
nhlog::net()->warn("[{}] failed to send message: {} {}",
|
||||||
txn_id,
|
txn_id,
|
||||||
err->matrix_error.error,
|
err->matrix_error.error,
|
||||||
status_code);
|
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()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user