Display edits correctly

This commit is contained in:
Nicolas Werner 2021-01-31 22:41:43 +01:00
parent faeaf9dc6b
commit 00fd4eecec
8 changed files with 99 additions and 12 deletions

View file

@ -356,7 +356,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare( FetchContent_Declare(
MatrixClient MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
GIT_TAG 70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b GIT_TAG 31e300546eb63ea25b0b879fb255beee6022da03
) )
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")

View file

@ -220,7 +220,7 @@
"name": "mtxclient", "name": "mtxclient",
"sources": [ "sources": [
{ {
"commit": "70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b", "commit": "31e300546eb63ea25b0b879fb255beee6022da03",
"type": "git", "type": "git",
"url": "https://github.com/Nheko-Reborn/mtxclient.git" "url": "https://github.com/Nheko-Reborn/mtxclient.git"
} }

View file

@ -85,6 +85,20 @@ Item {
width: 16 width: 16
} }
ImageButton {
id: editButton
visible: (Settings.buttonsInTimeline && model.isEditable) || model.isEdited
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
width: 16
hoverEnabled: true
image: ":/icons/icons/ui/edit.png"
ToolTip.visible: hovered
ToolTip.text: model.isEditable ? qsTr("Edit") : qsTr("Edited")
onClicked: if (model.isEditable) chat.model.editAction(model.id)
}
EmojiButton { EmojiButton {
id: reactButton id: reactButton

View file

@ -34,6 +34,20 @@ struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
template<template<class...> class Op, class... Args> template<template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t; using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
struct IsStateEvent
{
template<class T>
bool operator()(const mtx::events::StateEvent<T> &)
{
return true;
}
template<class T>
bool operator()(const mtx::events::Event<T> &)
{
return false;
}
};
struct EventMsgType struct EventMsgType
{ {
template<class E> template<class E>
@ -476,3 +490,9 @@ mtx::accessors::serialize_event(const mtx::events::collections::TimelineEvents &
{ {
return std::visit([](const auto &e) { return nlohmann::json(e); }, event); return std::visit([](const auto &e) { return nlohmann::json(e); }, event);
} }
bool
mtx::accessors::is_state_event(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(IsStateEvent{}, event);
}

View file

@ -17,6 +17,9 @@ room_id(const mtx::events::collections::TimelineEvents &event);
std::string std::string
sender(const mtx::events::collections::TimelineEvents &event); sender(const mtx::events::collections::TimelineEvents &event);
bool
is_state_event(const mtx::events::collections::TimelineEvents &event);
QDateTime QDateTime
origin_server_ts(const mtx::events::collections::TimelineEvents &event); origin_server_ts(const mtx::events::collections::TimelineEvents &event);

View file

@ -774,14 +774,16 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt,
if (id.empty()) if (id.empty())
return nullptr; return nullptr;
std::string id_ = std::string(id); IdIndex index{room_id_, std::string(id)};
if (resolve_edits) { if (resolve_edits) {
auto edits_ = edits(id_); auto edits_ = edits(index.id);
if (!edits_.empty()) if (!edits_.empty()) {
id_ = mtx::accessors::event_id(edits_.back()); index.id = mtx::accessors::event_id(edits_.back());
auto event_ptr =
new mtx::events::collections::TimelineEvents(std::move(edits_.back()));
events_by_id_.insert(index, event_ptr);
}
} }
IdIndex index{room_id_, id_};
auto event_ptr = events_by_id_.object(index); auto event_ptr = events_by_id_.object(index);
if (!event_ptr) { if (!event_ptr) {

View file

@ -288,6 +288,8 @@ TimelineModel::roleNames() const
{ProportionalHeight, "proportionalHeight"}, {ProportionalHeight, "proportionalHeight"},
{Id, "id"}, {Id, "id"},
{State, "state"}, {State, "state"},
{IsEdited, "isEdited"},
{IsEditable, "isEditable"},
{IsEncrypted, "isEncrypted"}, {IsEncrypted, "isEncrypted"},
{IsRoomEncrypted, "isRoomEncrypted"}, {IsRoomEncrypted, "isRoomEncrypted"},
{ReplyTo, "replyTo"}, {ReplyTo, "replyTo"},
@ -409,8 +411,12 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
return QVariant(prop > 0 ? prop : 1.); return QVariant(prop > 0 ? prop : 1.);
} }
case Id: case Id: {
if (auto replaces = relations(event).replaces())
return QVariant(QString::fromStdString(replaces.value()));
else
return QVariant(QString::fromStdString(event_id(event))); return QVariant(QString::fromStdString(event_id(event)));
}
case State: { case State: {
auto id = QString::fromStdString(event_id(event)); auto id = QString::fromStdString(event_id(event));
auto containsOthers = [](const auto &vec) { auto containsOthers = [](const auto &vec) {
@ -430,6 +436,11 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
else else
return qml_mtx_events::Received; return qml_mtx_events::Received;
} }
case IsEdited:
return QVariant(relations(event).replaces().has_value());
case IsEditable:
return QVariant(!is_state_event(event) && mtx::accessors::sender(event) ==
http::client()->user_id().to_string());
case IsEncrypted: { case IsEncrypted: {
auto id = event_id(event); auto id = event_id(event);
auto encrypted_event = events.get(id, id, false); auto encrypted_event = events.get(id, id, false);
@ -444,7 +455,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
case ReplyTo: case ReplyTo:
return QVariant(QString::fromStdString(relations(event).reply_to().value_or(""))); return QVariant(QString::fromStdString(relations(event).reply_to().value_or("")));
case Reactions: { case Reactions: {
auto id = event_id(event); auto id = relations(event).replaces().value_or(event_id(event));
return QVariant::fromValue(events.reactions(id)); return QVariant::fromValue(events.reactions(id));
} }
case RoomId: case RoomId:
@ -813,6 +824,12 @@ TimelineModel::replyAction(QString id)
setReply(id); setReply(id);
} }
void
TimelineModel::editAction(QString id)
{
setEdit(id);
}
RelatedInfo RelatedInfo
TimelineModel::relatedInfo(QString id) TimelineModel::relatedInfo(QString id)
{ {
@ -1501,6 +1518,22 @@ TimelineModel::formatMemberEvent(QString id)
return rendered; return rendered;
} }
void
TimelineModel::setEdit(QString newEdit)
{
if (edit_ != newEdit) {
edit_ = newEdit;
emit editChanged(edit_);
auto ev = events.get(newEdit.toStdString(), "");
if (ev) {
setReply(QString::fromStdString(
mtx::accessors::relations(*ev).reply_to().value_or("")));
// input()->setText(mtx::accessors::body(*ev));
}
}
}
QString QString
TimelineModel::roomName() const TimelineModel::roomName() const
{ {

View file

@ -145,6 +145,7 @@ class TimelineModel : public QAbstractListModel
Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
typingUsersChanged) typingUsersChanged)
Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply) Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
Q_PROPERTY(QString edit READ edit WRITE setEdit NOTIFY editChanged RESET resetEdit)
Q_PROPERTY( Q_PROPERTY(
bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged) bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
@ -181,6 +182,8 @@ public:
ProportionalHeight, ProportionalHeight,
Id, Id,
State, State,
IsEdited,
IsEditable,
IsEncrypted, IsEncrypted,
IsRoomEncrypted, IsRoomEncrypted,
ReplyTo, ReplyTo,
@ -213,6 +216,7 @@ public:
Q_INVOKABLE void viewRawMessage(QString id) const; Q_INVOKABLE void viewRawMessage(QString id) const;
Q_INVOKABLE void viewDecryptedRawMessage(QString id) const; Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
Q_INVOKABLE void openUserProfile(QString userid, bool global = false); Q_INVOKABLE void openUserProfile(QString userid, bool global = false);
Q_INVOKABLE void editAction(QString id);
Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void replyAction(QString id);
Q_INVOKABLE void readReceiptsAction(QString id) const; Q_INVOKABLE void readReceiptsAction(QString id) const;
Q_INVOKABLE void redactEvent(QString id); Q_INVOKABLE void redactEvent(QString id);
@ -268,6 +272,16 @@ public slots:
emit replyChanged(reply_); emit replyChanged(reply_);
} }
} }
QString edit() const { return edit_; }
void setEdit(QString newEdit);
void resetEdit()
{
if (!edit_.isEmpty()) {
edit_ = "";
emit editChanged(edit_);
resetReply();
}
}
void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; } void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
void clearTimeline() { events.clearTimeline(); } void clearTimeline() { events.clearTimeline(); }
void receivedSessionKey(const std::string &session_key) void receivedSessionKey(const std::string &session_key)
@ -292,6 +306,7 @@ signals:
void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo); void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
void typingUsersChanged(std::vector<QString> users); void typingUsersChanged(std::vector<QString> users);
void replyChanged(QString reply); void replyChanged(QString reply);
void editChanged(QString reply);
void paginationInProgressChanged(const bool); void paginationInProgressChanged(const bool);
void newCallEvent(const mtx::events::collections::TimelineEvents &event); void newCallEvent(const mtx::events::collections::TimelineEvents &event);
@ -322,7 +337,7 @@ private:
bool m_paginationInProgress = false; bool m_paginationInProgress = false;
QString currentId; QString currentId;
QString reply_; QString reply_, edit_;
std::vector<QString> typingUsers_; std::vector<QString> typingUsers_;
TimelineViewManager *manager_; TimelineViewManager *manager_;