Move visitors out of TimelineModel

This commit is contained in:
Nicolas Werner 2019-12-27 21:49:55 +01:00
parent 768ae9a5e0
commit e084543cc0
4 changed files with 571 additions and 446 deletions

View File

@ -219,6 +219,7 @@ set(SRC_FILES
src/ChatPage.cpp src/ChatPage.cpp
src/CommunitiesListItem.cpp src/CommunitiesListItem.cpp
src/CommunitiesList.cpp src/CommunitiesList.cpp
src/EventAccessors.cpp
src/InviteeItem.cpp src/InviteeItem.cpp
src/LoginPage.cpp src/LoginPage.cpp
src/Logging.cpp src/Logging.cpp

363
src/EventAccessors.cpp Normal file
View File

@ -0,0 +1,363 @@
#include "EventAccessors.h"
#include <type_traits>
namespace {
struct nonesuch
{
~nonesuch() = delete;
nonesuch(nonesuch const &) = delete;
void operator=(nonesuch const &) = delete;
};
namespace detail {
template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template<class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
} // namespace detail
template<template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
struct EventMsgType
{
template<class E>
using msgtype_t = decltype(E::msgtype);
template<class T>
mtx::events::MessageType operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<msgtype_t, T>::value)
return mtx::events::getMessageType(e.content.msgtype);
return mtx::events::MessageType::Unknown;
}
};
struct EventRoomName
{
template<class T>
std::string operator()(const T &e)
{
if constexpr (std::is_same_v<mtx::events::StateEvent<mtx::events::state::Name>, T>)
return e.content.name;
return "";
}
};
struct EventRoomTopic
{
template<class T>
std::string operator()(const T &e)
{
if constexpr (std::is_same_v<mtx::events::StateEvent<mtx::events::state::Topic>, T>)
return e.content.topic;
return "";
}
};
struct EventBody
{
template<class C>
using body_t = decltype(C::body);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<body_t, T>::value)
return e.content.body;
return "";
}
};
struct EventFormattedBody
{
template<class C>
using formatted_body_t = decltype(C::formatted_body);
template<class T>
std::string operator()(const mtx::events::RoomEvent<T> &e)
{
if constexpr (is_detected<formatted_body_t, T>::value)
return e.content.formatted_body;
return "";
}
};
struct EventFile
{
template<class Content>
using file_t = decltype(Content::file);
template<class T>
std::optional<mtx::crypto::EncryptedFile> operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<file_t, T>::value)
return e.content.file;
return std::nullopt;
}
};
struct EventUrl
{
template<class Content>
using url_t = decltype(Content::url);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<url_t, T>::value) {
if (auto file = EventFile{}(e))
return file->url;
return e.content.url;
}
return "";
}
};
struct EventThumbnailUrl
{
template<class Content>
using thumbnail_url_t = decltype(Content::info.thumbnail_url);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<thumbnail_url_t, T>::value) {
return e.content.info.thumbnail_url;
}
return "";
}
};
struct EventFilename
{
template<class T>
std::string operator()(const mtx::events::Event<T> &)
{
return "";
}
std::string operator()(const mtx::events::RoomEvent<mtx::events::msg::Audio> &e)
{
// body may be the original filename
return e.content.body;
}
std::string operator()(const mtx::events::RoomEvent<mtx::events::msg::Video> &e)
{
// body may be the original filename
return e.content.body;
}
std::string operator()(const mtx::events::RoomEvent<mtx::events::msg::Image> &e)
{
// body may be the original filename
return e.content.body;
}
std::string operator()(const mtx::events::RoomEvent<mtx::events::msg::File> &e)
{
// body may be the original filename
if (!e.content.filename.empty())
return e.content.filename;
return e.content.body;
}
};
struct EventMimeType
{
template<class Content>
using mimetype_t = decltype(Content::info.mimetype);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<mimetype_t, T>::value) {
return e.content.info.mimetype;
}
return "";
}
};
struct EventFilesize
{
template<class Content>
using filesize_t = decltype(Content::info.size);
template<class T>
int64_t operator()(const mtx::events::RoomEvent<T> &e)
{
if constexpr (is_detected<filesize_t, T>::value) {
return e.content.info.size;
}
return 0;
}
};
struct EventInReplyTo
{
template<class Content>
using related_ev_id_t = decltype(Content::relates_to.in_reply_to.event_id);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<related_ev_id_t, T>::value) {
return e.content.relates_to.in_reply_to.event_id;
}
return "";
}
};
struct EventMediaHeight
{
template<class Content>
using h_t = decltype(Content::info.h);
template<class T>
uint64_t operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<h_t, T>::value) {
return e.content.info.h;
}
return -1;
}
};
struct EventMediaWidth
{
template<class Content>
using w_t = decltype(Content::info.w);
template<class T>
uint64_t operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<w_t, T>::value) {
return e.content.info.w;
}
return -1;
}
};
template<class T>
double
eventPropHeight(const mtx::events::RoomEvent<T> &e)
{
auto w = eventWidth(e);
if (w == 0)
w = 1;
double prop = eventHeight(e) / (double)w;
return prop > 0 ? prop : 1.;
}
}
std::string
mtx::accessors::event_id(const mtx::events::collections::TimelineEvents &event)
{
return std::visit([](const auto e) { return e.event_id; }, event);
}
std::string
mtx::accessors::room_id(const mtx::events::collections::TimelineEvents &event)
{
return std::visit([](const auto e) { return e.room_id; }, event);
}
std::string
mtx::accessors::sender(const mtx::events::collections::TimelineEvents &event)
{
return std::visit([](const auto e) { return e.sender; }, event);
}
QDateTime
mtx::accessors::origin_server_ts(const mtx::events::collections::TimelineEvents &event)
{
return QDateTime::fromMSecsSinceEpoch(
std::visit([](const auto e) { return e.origin_server_ts; }, event));
}
std::string
mtx::accessors::filename(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventFilename{}, event);
}
mtx::events::MessageType
mtx::accessors::msg_type(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventMsgType{}, event);
}
std::string
mtx::accessors::room_name(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventRoomName{}, event);
}
std::string
mtx::accessors::room_topic(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventRoomTopic{}, event);
}
std::string
mtx::accessors::body(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventBody{}, event);
}
std::string
mtx::accessors::formatted_body(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventFormattedBody{}, event);
}
QString
mtx::accessors::formattedBodyWithFallback(const mtx::events::collections::TimelineEvents &event)
{
auto formatted = formatted_body(event);
if (!formatted.empty())
return QString::fromStdString(formatted);
else
return QString::fromStdString(body(event)).toHtmlEscaped().replace("\n", "<br>");
}
std::optional<mtx::crypto::EncryptedFile>
mtx::accessors::file(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventFile{}, event);
}
std::string
mtx::accessors::url(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventUrl{}, event);
}
std::string
mtx::accessors::thumbnail_url(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventThumbnailUrl{}, event);
}
std::string
mtx::accessors::mimetype(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventMimeType{}, event);
}
std::string
mtx::accessors::in_reply_to_event(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventInReplyTo{}, event);
}
int64_t
mtx::accessors::filesize(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventFilesize{}, event);
}
uint64_t
mtx::accessors::media_height(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventMediaHeight{}, event);
}
uint64_t
mtx::accessors::media_width(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventMediaWidth{}, event);
}

62
src/EventAccessors.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <string>
#include <QDateTime>
#include <QString>
#include <mtx/events/collections.hpp>
namespace mtx::accessors {
std::string
event_id(const mtx::events::collections::TimelineEvents &event);
std::string
room_id(const mtx::events::collections::TimelineEvents &event);
std::string
sender(const mtx::events::collections::TimelineEvents &event);
QDateTime
origin_server_ts(const mtx::events::collections::TimelineEvents &event);
std::string
filename(const mtx::events::collections::TimelineEvents &event);
mtx::events::MessageType
msg_type(const mtx::events::collections::TimelineEvents &event);
std::string
room_name(const mtx::events::collections::TimelineEvents &event);
std::string
room_topic(const mtx::events::collections::TimelineEvents &event);
std::string
body(const mtx::events::collections::TimelineEvents &event);
std::string
formatted_body(const mtx::events::collections::TimelineEvents &event);
QString
formattedBodyWithFallback(const mtx::events::collections::TimelineEvents &event);
std::optional<mtx::crypto::EncryptedFile>
file(const mtx::events::collections::TimelineEvents &event);
std::string
url(const mtx::events::collections::TimelineEvents &event);
std::string
thumbnail_url(const mtx::events::collections::TimelineEvents &event);
std::string
mimetype(const mtx::events::collections::TimelineEvents &event);
std::string
in_reply_to_event(const mtx::events::collections::TimelineEvents &event);
int64_t
filesize(const mtx::events::collections::TimelineEvents &event);
uint64_t
media_height(const mtx::events::collections::TimelineEvents &event);
uint64_t
media_width(const mtx::events::collections::TimelineEvents &event);
}

View File

@ -9,6 +9,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include "ChatPage.h" #include "ChatPage.h"
#include "EventAccessors.h"
#include "Logging.h" #include "Logging.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "MatrixClient.h" #include "MatrixClient.h"
@ -21,364 +22,100 @@
Q_DECLARE_METATYPE(QModelIndex) Q_DECLARE_METATYPE(QModelIndex)
namespace { namespace {
template<class T> struct RoomEventType
QString
eventId(const mtx::events::RoomEvent<T> &event)
{ {
return QString::fromStdString(event.event_id); template<class T>
} qml_mtx_events::EventType operator()(const mtx::events::Event<T> &e)
template<class T> {
QString using mtx::events::EventType;
roomId(const mtx::events::Event<T> &event) switch (e.type) {
{ case EventType::RoomKeyRequest:
return QString::fromStdString(event.room_id); return qml_mtx_events::EventType::KeyRequest;
} case EventType::RoomAliases:
template<class T> return qml_mtx_events::EventType::Aliases;
QString case EventType::RoomAvatar:
senderId(const mtx::events::RoomEvent<T> &event) return qml_mtx_events::EventType::Avatar;
{ case EventType::RoomCanonicalAlias:
return QString::fromStdString(event.sender); return qml_mtx_events::EventType::CanonicalAlias;
} case EventType::RoomCreate:
return qml_mtx_events::EventType::Create;
template<class T> case EventType::RoomEncrypted:
QDateTime return qml_mtx_events::EventType::Encrypted;
eventTimestamp(const mtx::events::RoomEvent<T> &event) case EventType::RoomEncryption:
{ return qml_mtx_events::EventType::Encryption;
return QDateTime::fromMSecsSinceEpoch(event.origin_server_ts); case EventType::RoomGuestAccess:
} return qml_mtx_events::EventType::GuestAccess;
case EventType::RoomHistoryVisibility:
template<class T> return qml_mtx_events::EventType::HistoryVisibility;
std::string case EventType::RoomJoinRules:
eventMsgType(const mtx::events::Event<T> &) return qml_mtx_events::EventType::JoinRules;
{ case EventType::RoomMember:
return ""; return qml_mtx_events::EventType::Member;
} case EventType::RoomMessage:
template<class T> return qml_mtx_events::EventType::UnknownMessage;
auto case EventType::RoomName:
eventMsgType(const mtx::events::RoomEvent<T> &e) -> decltype(e.content.msgtype) return qml_mtx_events::EventType::Name;
{ case EventType::RoomPowerLevels:
return e.content.msgtype; return qml_mtx_events::EventType::PowerLevels;
} case EventType::RoomTopic:
return qml_mtx_events::EventType::Topic;
template<class T> case EventType::RoomTombstone:
QString return qml_mtx_events::EventType::Tombstone;
eventRoomName(const T &) case EventType::RoomRedaction:
{ return qml_mtx_events::EventType::Redaction;
return ""; case EventType::RoomPinnedEvents:
} return qml_mtx_events::EventType::PinnedEvents;
QString case EventType::Sticker:
eventRoomName(const mtx::events::StateEvent<mtx::events::state::Name> &e) return qml_mtx_events::EventType::Sticker;
{ case EventType::Tag:
return QString::fromStdString(e.content.name); return qml_mtx_events::EventType::Tag;
} case EventType::Unsupported:
default:
template<class T> return qml_mtx_events::EventType::Unsupported;
QString }
eventRoomTopic(const T &)
{
return "";
}
QString
eventRoomTopic(const mtx::events::StateEvent<mtx::events::state::Topic> &e)
{
return QString::fromStdString(e.content.topic);
}
template<class T>
QString
eventBody(const mtx::events::Event<T> &)
{
return QString("");
}
template<class T>
auto
eventBody(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.body), std::string>::value, QString>
{
return QString::fromStdString(e.content.body);
}
template<class T>
QString
eventFormattedBody(const mtx::events::Event<T> &)
{
return QString("");
}
template<class T>
auto
eventFormattedBody(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.formatted_body), std::string>::value, QString>
{
auto temp = e.content.formatted_body;
if (!temp.empty()) {
return QString::fromStdString(temp);
} else {
return QString::fromStdString(e.content.body).toHtmlEscaped().replace("\n", "<br>");
} }
} qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Audio> &)
{
template<class T> return qml_mtx_events::EventType::AudioMessage;
std::optional<mtx::crypto::EncryptedFile>
eventEncryptionInfo(const mtx::events::Event<T> &)
{
return std::nullopt;
}
template<class T>
auto
eventEncryptionInfo(const mtx::events::RoomEvent<T> &e) -> std::enable_if_t<
std::is_same<decltype(e.content.file), std::optional<mtx::crypto::EncryptedFile>>::value,
std::optional<mtx::crypto::EncryptedFile>>
{
return e.content.file;
}
template<class T>
QString
eventUrl(const mtx::events::Event<T> &)
{
return "";
}
QString
eventUrl(const mtx::events::StateEvent<mtx::events::state::Avatar> &e)
{
return QString::fromStdString(e.content.url);
}
template<class T>
auto
eventUrl(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.url), std::string>::value, QString>
{
if (e.content.file)
return QString::fromStdString(e.content.file->url);
return QString::fromStdString(e.content.url);
}
template<class T>
QString
eventThumbnailUrl(const mtx::events::Event<T> &)
{
return "";
}
template<class T>
auto
eventThumbnailUrl(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.info.thumbnail_url), std::string>::value,
QString>
{
return QString::fromStdString(e.content.info.thumbnail_url);
}
template<class T>
QString
eventFilename(const mtx::events::Event<T> &)
{
return "";
}
QString
eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Audio> &e)
{
// body may be the original filename
return QString::fromStdString(e.content.body);
}
QString
eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Video> &e)
{
// body may be the original filename
return QString::fromStdString(e.content.body);
}
QString
eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Image> &e)
{
// body may be the original filename
return QString::fromStdString(e.content.body);
}
QString
eventFilename(const mtx::events::RoomEvent<mtx::events::msg::File> &e)
{
// body may be the original filename
if (!e.content.filename.empty())
return QString::fromStdString(e.content.filename);
return QString::fromStdString(e.content.body);
}
template<class T>
auto
eventFilesize(const mtx::events::RoomEvent<T> &e) -> decltype(e.content.info.size)
{
return e.content.info.size;
}
template<class T>
int64_t
eventFilesize(const mtx::events::Event<T> &)
{
return 0;
}
template<class T>
QString
eventMimeType(const mtx::events::Event<T> &)
{
return QString();
}
template<class T>
auto
eventMimeType(const mtx::events::RoomEvent<T> &e)
-> std::enable_if_t<std::is_same<decltype(e.content.info.mimetype), std::string>::value, QString>
{
return QString::fromStdString(e.content.info.mimetype);
}
template<class T>
QString
eventRelatesTo(const mtx::events::Event<T> &)
{
return QString();
}
template<class T>
auto
eventRelatesTo(const mtx::events::RoomEvent<T> &e) -> std::enable_if_t<
std::is_same<decltype(e.content.relates_to.in_reply_to.event_id), std::string>::value,
QString>
{
return QString::fromStdString(e.content.relates_to.in_reply_to.event_id);
}
template<class T>
qml_mtx_events::EventType
toRoomEventType(const mtx::events::Event<T> &e)
{
using mtx::events::EventType;
switch (e.type) {
case EventType::RoomKeyRequest:
return qml_mtx_events::EventType::KeyRequest;
case EventType::RoomAliases:
return qml_mtx_events::EventType::Aliases;
case EventType::RoomAvatar:
return qml_mtx_events::EventType::Avatar;
case EventType::RoomCanonicalAlias:
return qml_mtx_events::EventType::CanonicalAlias;
case EventType::RoomCreate:
return qml_mtx_events::EventType::Create;
case EventType::RoomEncrypted:
return qml_mtx_events::EventType::Encrypted;
case EventType::RoomEncryption:
return qml_mtx_events::EventType::Encryption;
case EventType::RoomGuestAccess:
return qml_mtx_events::EventType::GuestAccess;
case EventType::RoomHistoryVisibility:
return qml_mtx_events::EventType::HistoryVisibility;
case EventType::RoomJoinRules:
return qml_mtx_events::EventType::JoinRules;
case EventType::RoomMember:
return qml_mtx_events::EventType::Member;
case EventType::RoomMessage:
return qml_mtx_events::EventType::UnknownMessage;
case EventType::RoomName:
return qml_mtx_events::EventType::Name;
case EventType::RoomPowerLevels:
return qml_mtx_events::EventType::PowerLevels;
case EventType::RoomTopic:
return qml_mtx_events::EventType::Topic;
case EventType::RoomTombstone:
return qml_mtx_events::EventType::Tombstone;
case EventType::RoomRedaction:
return qml_mtx_events::EventType::Redaction;
case EventType::RoomPinnedEvents:
return qml_mtx_events::EventType::PinnedEvents;
case EventType::Sticker:
return qml_mtx_events::EventType::Sticker;
case EventType::Tag:
return qml_mtx_events::EventType::Tag;
case EventType::Unsupported:
default:
return qml_mtx_events::EventType::Unsupported;
} }
} qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Emote> &)
qml_mtx_events::EventType {
toRoomEventType(const mtx::events::Event<mtx::events::msg::Audio> &) return qml_mtx_events::EventType::EmoteMessage;
{ }
return qml_mtx_events::EventType::AudioMessage; qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::File> &)
} {
qml_mtx_events::EventType return qml_mtx_events::EventType::FileMessage;
toRoomEventType(const mtx::events::Event<mtx::events::msg::Emote> &) }
{ qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Image> &)
return qml_mtx_events::EventType::EmoteMessage; {
} return qml_mtx_events::EventType::ImageMessage;
qml_mtx_events::EventType }
toRoomEventType(const mtx::events::Event<mtx::events::msg::File> &) qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Notice> &)
{ {
return qml_mtx_events::EventType::FileMessage; return qml_mtx_events::EventType::NoticeMessage;
} }
qml_mtx_events::EventType qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Text> &)
toRoomEventType(const mtx::events::Event<mtx::events::msg::Image> &) {
{ return qml_mtx_events::EventType::TextMessage;
return qml_mtx_events::EventType::ImageMessage; }
} qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Video> &)
qml_mtx_events::EventType {
toRoomEventType(const mtx::events::Event<mtx::events::msg::Notice> &) return qml_mtx_events::EventType::VideoMessage;
{ }
return qml_mtx_events::EventType::NoticeMessage;
} qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Redacted> &)
qml_mtx_events::EventType {
toRoomEventType(const mtx::events::Event<mtx::events::msg::Text> &) return qml_mtx_events::EventType::Redacted;
{ }
return qml_mtx_events::EventType::TextMessage; // ::EventType::Type operator()(const Event<mtx::events::msg::Location> &e) { return
} // ::EventType::LocationMessage; }
qml_mtx_events::EventType };
toRoomEventType(const mtx::events::Event<mtx::events::msg::Video> &)
{
return qml_mtx_events::EventType::VideoMessage;
} }
qml_mtx_events::EventType qml_mtx_events::EventType
toRoomEventType(const mtx::events::Event<mtx::events::msg::Redacted> &) toRoomEventType(const mtx::events::collections::TimelineEvents &event)
{ {
return qml_mtx_events::EventType::Redacted; return std::visit(RoomEventType{}, event);
}
// ::EventType::Type toRoomEventType(const Event<mtx::events::msg::Location> &e) { return
// ::EventType::LocationMessage; }
template<class T>
uint64_t
eventHeight(const mtx::events::Event<T> &)
{
return -1;
}
template<class T>
auto
eventHeight(const mtx::events::RoomEvent<T> &e) -> decltype(e.content.info.h)
{
return e.content.info.h;
}
template<class T>
uint64_t
eventWidth(const mtx::events::Event<T> &)
{
return -1;
}
template<class T>
auto
eventWidth(const mtx::events::RoomEvent<T> &e) -> decltype(e.content.info.w)
{
return e.content.info.w;
}
template<class T>
double
eventPropHeight(const mtx::events::RoomEvent<T> &e)
{
auto w = eventWidth(e);
if (w == 0)
w = 1;
double prop = eventHeight(e) / (double)w;
return prop > 0 ? prop : 1.;
}
} }
TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent) TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent)
@ -477,6 +214,8 @@ TimelineModel::rowCount(const QModelIndex &parent) const
QVariant QVariant
TimelineModel::data(const QModelIndex &index, int role) const TimelineModel::data(const QModelIndex &index, int role) const
{ {
using namespace mtx::accessors;
namespace acc = mtx::accessors;
if (index.row() < 0 && index.row() >= (int)eventOrder.size()) if (index.row() < 0 && index.row() >= (int)eventOrder.size())
return QVariant(); return QVariant();
@ -491,86 +230,71 @@ TimelineModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case Section: { case Section: {
QDateTime date = QDateTime date = origin_server_ts(event);
std::visit([](const auto &e) -> QDateTime { return eventTimestamp(e); }, event);
date.setTime(QTime()); date.setTime(QTime());
QString userId = std::string userId = acc::sender(event);
std::visit([](const auto &e) -> QString { return senderId(e); }, event);
for (int r = index.row() - 1; r > 0; r--) { for (int r = index.row() - 1; r > 0; r--) {
auto tempEv = events.value(eventOrder[r]); auto tempEv = events.value(eventOrder[r]);
QDateTime prevDate = std::visit( QDateTime prevDate = origin_server_ts(tempEv);
[](const auto &e) -> QDateTime { return eventTimestamp(e); }, tempEv);
prevDate.setTime(QTime()); prevDate.setTime(QTime());
if (prevDate != date) if (prevDate != date)
return QString("%2 %1").arg(date.toMSecsSinceEpoch()).arg(userId); return QString("%2 %1")
.arg(date.toMSecsSinceEpoch())
.arg(QString::fromStdString(userId));
QString prevUserId = std::string prevUserId = acc::sender(tempEv);
std::visit([](const auto &e) -> QString { return senderId(e); }, tempEv);
if (userId != prevUserId) if (userId != prevUserId)
break; break;
} }
return QString("%1").arg(userId); return QString("%1").arg(QString::fromStdString(userId));
} }
case UserId: case UserId:
return QVariant( return QVariant(QString::fromStdString(acc::sender(event)));
std::visit([](const auto &e) -> QString { return senderId(e); }, event));
case UserName: case UserName:
return QVariant(displayName( return QVariant(displayName(QString::fromStdString(acc::sender(event))));
std::visit([](const auto &e) -> QString { return senderId(e); }, event)));
case Timestamp: case Timestamp:
return QVariant( return QVariant(origin_server_ts(event));
std::visit([](const auto &e) -> QDateTime { return eventTimestamp(e); }, event));
case Type: case Type:
return QVariant(std::visit( return QVariant(toRoomEventType(event));
[](const auto &e) -> qml_mtx_events::EventType { return toRoomEventType(e); },
event));
case Body: case Body:
return QVariant(utils::replaceEmoji( return QVariant(utils::replaceEmoji(QString::fromStdString(body(event))));
std::visit([](const auto &e) -> QString { return eventBody(e); }, event)));
case FormattedBody: case FormattedBody:
return QVariant( return QVariant(
utils::replaceEmoji( utils::replaceEmoji(utils::linkifyMessage(formattedBodyWithFallback(event)))
utils::linkifyMessage(std::visit(
[](const auto &e) -> QString { return eventFormattedBody(e); }, event)))
.remove("<mx-reply>") .remove("<mx-reply>")
.remove("</mx-reply>")); .remove("</mx-reply>"));
case Url: case Url:
return QVariant( return QVariant(QString::fromStdString(url(event)));
std::visit([](const auto &e) -> QString { return eventUrl(e); }, event));
case ThumbnailUrl: case ThumbnailUrl:
return QVariant( return QVariant(QString::fromStdString(thumbnail_url(event)));
std::visit([](const auto &e) -> QString { return eventThumbnailUrl(e); }, event));
case Filename: case Filename:
return QVariant( return QVariant(QString::fromStdString(filename(event)));
std::visit([](const auto &e) -> QString { return eventFilename(e); }, event));
case Filesize: case Filesize:
return QVariant(std::visit( return QVariant(utils::humanReadableFileSize(filesize(event)));
[](const auto &e) -> QString {
return utils::humanReadableFileSize(eventFilesize(e));
},
event));
case MimeType: case MimeType:
return QVariant( return QVariant(QString::fromStdString(mimetype(event)));
std::visit([](const auto &e) -> QString { return eventMimeType(e); }, event));
case Height: case Height:
return QVariant( return QVariant(qulonglong{media_height(event)});
std::visit([](const auto &e) -> qulonglong { return eventHeight(e); }, event));
case Width: case Width:
return QVariant( return QVariant(qulonglong{media_width(event)});
std::visit([](const auto &e) -> qulonglong { return eventWidth(e); }, event)); case ProportionalHeight: {
case ProportionalHeight: auto w = media_width(event);
return QVariant( if (w == 0)
std::visit([](const auto &e) -> double { return eventPropHeight(e); }, event)); w = 1;
double prop = media_height(event) / (double)w;
return QVariant(prop > 0 ? prop : 1.);
}
case Id: case Id:
return id; return id;
case State: case State:
// only show read receipts for messages not from us // only show read receipts for messages not from us
if (std::visit([](const auto &e) -> QString { return senderId(e); }, event) if (acc::sender(event) != http::client()->user_id().to_string())
.toStdString() != http::client()->user_id().to_string())
return qml_mtx_events::Empty; return qml_mtx_events::Empty;
else if (failed.contains(id)) else if (failed.contains(id))
return qml_mtx_events::Failed; return qml_mtx_events::Failed;
@ -581,21 +305,15 @@ TimelineModel::data(const QModelIndex &index, int role) const
else else
return qml_mtx_events::Received; return qml_mtx_events::Received;
case IsEncrypted: { case IsEncrypted: {
auto tempEvent = events[id];
return std::holds_alternative< return std::holds_alternative<
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(tempEvent); mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(events[id]);
}
case ReplyTo: {
QString evId =
std::visit([](const auto &e) -> QString { return eventRelatesTo(e); }, event);
return QVariant(evId);
} }
case ReplyTo:
return QVariant(QString::fromStdString(in_reply_to_event(event)));
case RoomName: case RoomName:
return QVariant( return QVariant(QString::fromStdString(room_name(event)));
std::visit([](const auto &e) -> QString { return eventRoomName(e); }, event));
case RoomTopic: case RoomTopic:
return QVariant( return QVariant(QString::fromStdString(room_topic(event)));
std::visit([](const auto &e) -> QString { return eventRoomTopic(e); }, event));
default: default:
return QVariant(); return QVariant();
} }
@ -667,7 +385,7 @@ TimelineModel::internalAddEvents(
{ {
std::vector<QString> ids; std::vector<QString> ids;
for (auto e : timeline) { for (auto e : timeline) {
QString id = std::visit([](const auto &e) -> QString { return eventId(e); }, e); QString id = QString::fromStdString(mtx::accessors::event_id(e));
if (this->events.contains(id)) { if (this->events.contains(id)) {
this->events.insert(id, e); this->events.insert(id, e);
@ -708,11 +426,7 @@ TimelineModel::internalAddEvents(
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) { std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
e = decryptEvent(*event).event; e = decryptEvent(*event).event;
} }
auto encInfo = std::visit( auto encInfo = mtx::accessors::file(e);
[](const auto &ev) -> std::optional<mtx::crypto::EncryptedFile> {
return eventEncryptionInfo(ev);
},
e);
if (encInfo) if (encInfo)
emit newEncryptedImage(encInfo.value()); emit newEncryptedImage(encInfo.value());
@ -950,25 +664,18 @@ TimelineModel::replyAction(QString id)
event = decryptEvent(*e).event; event = decryptEvent(*e).event;
} }
RelatedInfo related = std::visit( RelatedInfo related = {};
[](const auto &ev) -> RelatedInfo { related.quoted_user = QString::fromStdString(mtx::accessors::sender(event));
RelatedInfo related_ = {}; related.related_event = mtx::accessors::event_id(event);
related_.quoted_user = QString::fromStdString(ev.sender); related.type = mtx::accessors::msg_type(event);
related_.related_event = ev.event_id; related.quoted_body = mtx::accessors::formattedBodyWithFallback(event);
return related_;
},
event);
related.type = mtx::events::getMessageType(
std::visit([](const auto &e) -> std::string { return eventMsgType(e); }, event));
related.quoted_body =
std::visit([](const auto &e) -> QString { return eventFormattedBody(e); }, event);
related.quoted_body.remove(QRegularExpression( related.quoted_body.remove(QRegularExpression(
"<mx-reply>.*</mx-reply>", QRegularExpression::DotMatchesEverythingOption)); "<mx-reply>.*</mx-reply>", QRegularExpression::DotMatchesEverythingOption));
nhlog::ui()->debug("after replacement: {}", related.quoted_body.toStdString()); nhlog::ui()->debug("after replacement: {}", related.quoted_body.toStdString());
related.room = room_id_; related.room = room_id_;
if (related.quoted_body.isEmpty()) // if (related.quoted_body.isEmpty())
return; // return;
ChatPage::instance()->messageReply(related); ChatPage::instance()->messageReply(related);
} }
@ -1412,8 +1119,7 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
internalAddEvents({event}); internalAddEvents({event});
QString txn_id_qstr = QString txn_id_qstr = QString::fromStdString(mtx::accessors::event_id(event));
std::visit([](const auto &e) -> QString { return eventId(e); }, event);
beginInsertRows(QModelIndex(), beginInsertRows(QModelIndex(),
static_cast<int>(this->eventOrder.size()), static_cast<int>(this->eventOrder.size()),
static_cast<int>(this->eventOrder.size())); static_cast<int>(this->eventOrder.size()));
@ -1436,18 +1142,13 @@ TimelineModel::saveMedia(QString eventId) const
event = decryptEvent(*e).event; event = decryptEvent(*e).event;
} }
QString mxcUrl = std::visit([](const auto &e) -> QString { return eventUrl(e); }, event); QString mxcUrl = QString::fromStdString(mtx::accessors::url(event));
QString originalFilename = QString originalFilename = QString::fromStdString(mtx::accessors::filename(event));
std::visit([](const auto &e) -> QString { return eventFilename(e); }, event); QString mimeType = QString::fromStdString(mtx::accessors::mimetype(event));
QString mimeType =
std::visit([](const auto &e) -> QString { return eventMimeType(e); }, event);
using EncF = std::optional<mtx::crypto::EncryptedFile>; auto encryptionInfo = mtx::accessors::file(event);
EncF encryptionInfo =
std::visit([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event);
qml_mtx_events::EventType eventType = std::visit( qml_mtx_events::EventType eventType = toRoomEventType(event);
[](const auto &e) -> qml_mtx_events::EventType { return toRoomEventType(e); }, event);
QString dialogTitle; QString dialogTitle;
if (eventType == qml_mtx_events::EventType::ImageMessage) { if (eventType == qml_mtx_events::EventType::ImageMessage) {
@ -1513,13 +1214,11 @@ TimelineModel::cacheMedia(QString eventId)
event = decryptEvent(*e).event; event = decryptEvent(*e).event;
} }
QString mxcUrl = std::visit([](const auto &e) -> QString { return eventUrl(e); }, event); QString mxcUrl = QString::fromStdString(mtx::accessors::url(event));
QString mimeType = QString originalFilename = QString::fromStdString(mtx::accessors::filename(event));
std::visit([](const auto &e) -> QString { return eventMimeType(e); }, event); QString mimeType = QString::fromStdString(mtx::accessors::mimetype(event));
using EncF = std::optional<mtx::crypto::EncryptedFile>; auto encryptionInfo = mtx::accessors::file(event);
EncF encryptionInfo =
std::visit([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event);
// If the message is a link to a non mxcUrl, don't download it // If the message is a link to a non mxcUrl, don't download it
if (!mxcUrl.startsWith("mxc://")) { if (!mxcUrl.startsWith("mxc://")) {