Migrate to matrix-structs for event and response parsing

This commit is contained in:
Konstantinos Sideris 2017-12-04 18:41:19 +02:00
parent 1976a3280c
commit a605e4486f
77 changed files with 648 additions and 4962 deletions

View file

@ -22,11 +22,8 @@
#include <QStandardPaths>
#include "Cache.h"
#include "MemberEventContent.h"
#include "RoomState.h"
namespace events = matrix::events;
static const lmdb::val NEXT_BATCH_KEY("next_batch");
static const lmdb::val transactionID("transaction_id");
@ -122,13 +119,10 @@ Cache::setState(const QString &nextBatchToken, const QMap<QString, RoomState> &s
void
Cache::insertRoomState(lmdb::txn &txn, const QString &roomid, const RoomState &state)
{
auto stateEvents = QJsonDocument(state.serialize()).toBinaryData();
auto stateEvents = state.serialize();
auto id = roomid.toUtf8();
lmdb::dbi_put(txn,
roomDb_,
lmdb::val(id.data(), id.size()),
lmdb::val(stateEvents.data(), stateEvents.size()));
lmdb::dbi_put(txn, roomDb_, lmdb::val(id.data(), id.size()), lmdb::val(stateEvents));
for (const auto &membership : state.memberships) {
lmdb::dbi membersDb =
@ -136,31 +130,29 @@ Cache::insertRoomState(lmdb::txn &txn, const QString &roomid, const RoomState &s
// The user_id this membership event relates to, is used
// as the index on the membership database.
auto key = membership.stateKey().toUtf8();
auto memberEvent = QJsonDocument(membership.serialize()).toBinaryData();
auto key = membership.second.state_key;
switch (membership.content().membershipState()) {
// Serialize membership event.
nlohmann::json data = membership.second;
std::string memberEvent = data.dump();
switch (membership.second.content.membership) {
// We add or update (e.g invite -> join) a new user to the membership
// list.
case events::Membership::Invite:
case events::Membership::Join: {
lmdb::dbi_put(txn,
membersDb,
lmdb::val(key.data(), key.size()),
lmdb::val(memberEvent.data(), memberEvent.size()));
case mtx::events::state::Membership::Invite:
case mtx::events::state::Membership::Join: {
lmdb::dbi_put(txn, membersDb, lmdb::val(key), lmdb::val(memberEvent));
break;
}
// We remove the user from the membership list.
case events::Membership::Leave:
case events::Membership::Ban: {
lmdb::dbi_del(txn,
membersDb,
lmdb::val(key.data(), key.size()),
lmdb::val(memberEvent.data(), memberEvent.size()));
case mtx::events::state::Membership::Leave:
case mtx::events::state::Membership::Ban: {
lmdb::dbi_del(txn, membersDb, lmdb::val(key), lmdb::val(memberEvent));
break;
}
case events::Membership::Knock: {
qWarning() << "Skipping knock membership" << roomid << key;
case mtx::events::state::Membership::Knock: {
qWarning()
<< "Skipping knock membership" << roomid << QString::fromStdString(key);
break;
}
}
@ -194,14 +186,13 @@ Cache::states()
// Retrieve all the room names.
while (cursor.get(room, stateData, MDB_NEXT)) {
auto roomid = QString::fromUtf8(room.data(), room.size());
auto json =
QJsonDocument::fromBinaryData(QByteArray(stateData.data(), stateData.size()));
auto json = nlohmann::json::parse(stateData);
RoomState state;
state.parse(json.object());
state.parse(json);
auto memberDb = lmdb::dbi::open(txn, roomid.toStdString().c_str(), MDB_CREATE);
QMap<QString, events::StateEvent<events::MemberEventContent>> members;
std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>> members;
auto memberCursor = lmdb::cursor::open(txn, memberDb);
@ -209,17 +200,15 @@ Cache::states()
std::string memberContent;
while (memberCursor.get(memberId, memberContent, MDB_NEXT)) {
auto userid = QString::fromUtf8(memberId.data(), memberId.size());
auto data = QJsonDocument::fromBinaryData(
QByteArray(memberContent.data(), memberContent.size()));
auto userid = QString::fromStdString(memberId);
try {
events::StateEvent<events::MemberEventContent> member;
member.deserialize(data.object());
members.insert(userid, member);
} catch (const DeserializationException &e) {
qWarning() << e.what();
qWarning() << "Fault while parsing member event" << data.object();
auto data = nlohmann::json::parse(memberContent);
mtx::events::StateEvent<mtx::events::state::Member> member = data;
members.emplace(memberId, member);
} catch (std::exception &e) {
qWarning() << "Fault while parsing member event" << e.what()
<< QString::fromStdString(memberContent);
continue;
}
}

View file

@ -32,7 +32,6 @@
#include "RoomState.h"
#include "SideBarActions.h"
#include "Splitter.h"
#include "Sync.h"
#include "TextInputWidget.h"
#include "Theme.h"
#include "TopRoomBar.h"
@ -44,8 +43,6 @@
constexpr int MAX_INITIAL_SYNC_FAILURES = 5;
constexpr int SYNC_RETRY_TIMEOUT = 10000;
namespace events = matrix::events;
ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
: QWidget(parent)
, client_(client)
@ -219,15 +216,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
view_manager_->queueAudioMessage(roomid, filename, url);
});
connect(client_.data(),
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
this,
SLOT(updateTopBarAvatar(const QString &, const QPixmap &)));
connect(
client_.data(), &MatrixClient::roomAvatarRetrieved, this, &ChatPage::updateTopBarAvatar);
connect(client_.data(),
SIGNAL(initialSyncCompleted(const SyncResponse &)),
&MatrixClient::initialSyncCompleted,
this,
SLOT(initialSyncCompleted(const SyncResponse &)));
&ChatPage::initialSyncCompleted);
connect(client_.data(), &MatrixClient::initialSyncFailed, this, [=](const QString &msg) {
if (client_->getHomeServer().isEmpty()) {
deleteConfigs();
@ -251,29 +246,17 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
client_->initialSync();
});
connect(client_.data(),
SIGNAL(syncCompleted(const SyncResponse &)),
this,
SLOT(syncCompleted(const SyncResponse &)));
connect(client_.data(),
SIGNAL(syncFailed(const QString &)),
this,
SLOT(syncFailed(const QString &)));
connect(client_.data(), &MatrixClient::syncCompleted, this, &ChatPage::syncCompleted);
connect(client_.data(), &MatrixClient::syncFailed, this, &ChatPage::syncFailed);
connect(client_.data(),
&MatrixClient::getOwnProfileResponse,
this,
&ChatPage::updateOwnProfileInfo);
connect(client_.data(),
SIGNAL(ownAvatarRetrieved(const QPixmap &)),
this,
SLOT(setOwnAvatar(const QPixmap &)));
connect(client_.data(), &MatrixClient::ownAvatarRetrieved, this, &ChatPage::setOwnAvatar);
connect(client_.data(), &MatrixClient::joinedRoom, this, [=]() {
emit showNotification("You joined the room.");
});
connect(client_.data(),
SIGNAL(leftRoom(const QString &)),
this,
SLOT(removeRoom(const QString &)));
connect(client_.data(), &MatrixClient::leftRoom, this, &ChatPage::removeRoom);
showContentTimer_ = new QTimer(this);
showContentTimer_->setSingleShot(true);
@ -383,32 +366,34 @@ ChatPage::syncFailed(const QString &msg)
}
void
ChatPage::syncCompleted(const SyncResponse &response)
ChatPage::syncCompleted(const mtx::responses::Sync &response)
{
updateJoinedRooms(response.rooms().join());
removeLeftRooms(response.rooms().leave());
updateJoinedRooms(response.rooms.join);
removeLeftRooms(response.rooms.leave);
auto stateDiff = generateMembershipDifference(response.rooms().join(), state_manager_);
QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), stateDiff);
const auto nextBatchToken = QString::fromStdString(response.next_batch);
auto stateDiff = generateMembershipDifference(response.rooms.join, state_manager_);
QtConcurrent::run(cache_.data(), &Cache::setState, nextBatchToken, stateDiff);
room_list_->sync(state_manager_, settingsManager_);
view_manager_->sync(response.rooms());
view_manager_->sync(response.rooms);
client_->setNextBatchToken(response.nextBatch());
client_->setNextBatchToken(nextBatchToken);
client_->sync();
}
void
ChatPage::initialSyncCompleted(const SyncResponse &response)
ChatPage::initialSyncCompleted(const mtx::responses::Sync &response)
{
auto joined = response.rooms().join();
auto joined = response.rooms.join;
for (auto it = joined.constBegin(); it != joined.constEnd(); ++it) {
for (auto it = joined.cbegin(); it != joined.cend(); ++it) {
RoomState room_state;
// Build the current state from the timeline and state events.
room_state.updateFromEvents(it.value().state().events());
room_state.updateFromEvents(it.value().timeline().events());
room_state.updateFromEvents(it->second.state.events);
room_state.updateFromEvents(it->second.timeline.events);
// Remove redundant memberships.
room_state.removeLeaveMemberships();
@ -417,27 +402,32 @@ ChatPage::initialSyncCompleted(const SyncResponse &response)
room_state.resolveName();
room_state.resolveAvatar();
state_manager_.insert(it.key(), room_state);
settingsManager_.insert(it.key(),
QSharedPointer<RoomSettings>(new RoomSettings(it.key())));
const auto room_id = QString::fromStdString(it->first);
state_manager_.insert(room_id, room_state);
settingsManager_.insert(room_id,
QSharedPointer<RoomSettings>(new RoomSettings(room_id)));
for (const auto membership : room_state.memberships) {
updateUserDisplayName(membership);
updateUserAvatarUrl(membership);
updateUserDisplayName(membership.second);
updateUserAvatarUrl(membership.second);
}
QApplication::processEvents();
}
QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_);
QtConcurrent::run(cache_.data(),
&Cache::setState,
QString::fromStdString(response.next_batch),
state_manager_);
// Populate timelines with messages.
view_manager_->initialize(response.rooms());
view_manager_->initialize(response.rooms);
// Initialize room list.
room_list_->setInitialRooms(settingsManager_, state_manager_);
client_->setNextBatchToken(response.nextBatch());
client_->setNextBatchToken(QString::fromStdString(response.next_batch));
client_->sync();
emit contentLoaded();
@ -527,8 +517,8 @@ ChatPage::loadStateFromCache()
// Resolve user avatars.
for (const auto membership : room_state.memberships) {
updateUserDisplayName(membership);
updateUserAvatarUrl(membership);
updateUserDisplayName(membership.second);
updateUserAvatarUrl(membership.second);
}
}
@ -579,7 +569,8 @@ ChatPage::showQuickSwitcher()
QMap<QString, QString> rooms;
for (auto it = state_manager_.constBegin(); it != state_manager_.constEnd(); ++it) {
QString deambiguator = it.value().canonical_alias.content().alias();
QString deambiguator =
QString::fromStdString(it.value().canonical_alias.content.alias);
if (deambiguator == "")
deambiguator = it.key();
rooms.insert(it.value().getName() + " (" + deambiguator + ")", it.key());
@ -623,7 +614,7 @@ ChatPage::removeRoom(const QString &room_id)
}
void
ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_ids)
ChatPage::updateTypingUsers(const QString &roomid, const std::vector<std::string> &user_ids)
{
QStringList users;
@ -631,9 +622,12 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_id
QString user_id = settings.value("auth/user_id").toString();
for (const auto uid : user_ids) {
if (uid == user_id)
auto user = QString::fromStdString(uid);
if (user == user_id)
continue;
users.append(TimelineViewManager::displayName(uid));
users.append(TimelineViewManager::displayName(user));
}
users.sort();
@ -646,186 +640,118 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_id
}
void
ChatPage::updateUserMetadata(const QJsonArray &events)
ChatPage::updateUserAvatarUrl(const mtx::events::StateEvent<mtx::events::state::Member> &membership)
{
events::EventType ty;
auto uid = QString::fromStdString(membership.sender);
auto url = QString::fromStdString(membership.content.avatar_url);
for (const auto &event : events) {
try {
ty = events::extractEventType(event.toObject());
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
if (!events::isStateEvent(ty))
continue;
try {
switch (ty) {
case events::EventType::RoomMember: {
events::StateEvent<events::MemberEventContent> member;
member.deserialize(event);
updateUserAvatarUrl(member);
updateUserDisplayName(member);
break;
}
default: {
continue;
}
}
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
}
}
void
ChatPage::updateUserAvatarUrl(const events::StateEvent<events::MemberEventContent> &membership)
{
auto uid = membership.sender();
auto url = membership.content().avatarUrl();
if (!url.toString().isEmpty())
if (!url.isEmpty())
AvatarProvider::setAvatarUrl(uid, url);
}
void
ChatPage::updateUserDisplayName(const events::StateEvent<events::MemberEventContent> &membership)
ChatPage::updateUserDisplayName(
const mtx::events::StateEvent<mtx::events::state::Member> &membership)
{
auto displayName = membership.content().displayName();
auto displayName = QString::fromStdString(membership.content.display_name);
auto stateKey = QString::fromStdString(membership.state_key);
if (!displayName.isEmpty())
TimelineViewManager::DISPLAY_NAMES.insert(membership.stateKey(), displayName);
TimelineViewManager::DISPLAY_NAMES.insert(stateKey, displayName);
}
void
ChatPage::removeLeftRooms(const QMap<QString, LeftRoom> &rooms)
ChatPage::removeLeftRooms(const std::map<std::string, mtx::responses::LeftRoom> &rooms)
{
for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
if (state_manager_.contains(it.key()))
removeRoom(it.key());
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
const auto room_id = QString::fromStdString(it->first);
if (state_manager_.contains(room_id))
removeRoom(room_id);
}
}
void
ChatPage::updateJoinedRooms(const QMap<QString, JoinedRoom> &rooms)
ChatPage::updateJoinedRooms(const std::map<std::string, mtx::responses::JoinedRoom> &rooms)
{
for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
updateTypingUsers(it.key(), it.value().typingUserIDs());
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
const auto roomid = QString::fromStdString(it->first);
const auto newStateEvents = it.value().state().events();
const auto newTimelineEvents = it.value().timeline().events();
updateTypingUsers(roomid, it->second.ephemeral.typing);
const auto newStateEvents = it->second.state;
const auto newTimelineEvents = it->second.timeline;
// Merge the new updates for rooms that we are tracking.
if (state_manager_.contains(it.key())) {
auto oldState = &state_manager_[it.key()];
oldState->updateFromEvents(newStateEvents);
oldState->updateFromEvents(newTimelineEvents);
if (state_manager_.contains(roomid)) {
auto oldState = &state_manager_[roomid];
oldState->updateFromEvents(newStateEvents.events);
oldState->updateFromEvents(newTimelineEvents.events);
oldState->resolveName();
oldState->resolveAvatar();
} else {
// Build the current state from the timeline and state events.
RoomState room_state;
room_state.updateFromEvents(newStateEvents);
room_state.updateFromEvents(newTimelineEvents);
room_state.updateFromEvents(newStateEvents.events);
room_state.updateFromEvents(newTimelineEvents.events);
// Resolve room name and avatar. e.g in case of one-to-one chats.
room_state.resolveName();
room_state.resolveAvatar();
state_manager_.insert(it.key(), room_state);
state_manager_.insert(roomid, room_state);
settingsManager_.insert(
it.key(), QSharedPointer<RoomSettings>(new RoomSettings(it.key())));
roomid, QSharedPointer<RoomSettings>(new RoomSettings(roomid)));
view_manager_->addRoom(it.value(), it.key());
view_manager_->addRoom(it->second, roomid);
}
updateUserMetadata(newStateEvents);
updateUserMetadata(newTimelineEvents);
updateUserMetadata(newStateEvents.events);
updateUserMetadata(newTimelineEvents.events);
if (it.key() == current_room_)
changeTopRoomInfo(it.key());
if (roomid == current_room_)
changeTopRoomInfo(roomid);
QApplication::processEvents();
}
}
QMap<QString, RoomState>
ChatPage::generateMembershipDifference(const QMap<QString, JoinedRoom> &rooms,
const QMap<QString, RoomState> &states) const
ChatPage::generateMembershipDifference(
const std::map<std::string, mtx::responses::JoinedRoom> &rooms,
const QMap<QString, RoomState> &states) const
{
QMap<QString, RoomState> stateDiff;
for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
if (!states.contains(it.key()))
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
const auto room_id = QString::fromStdString(it->first);
if (!states.contains(room_id))
continue;
auto events = it.value().state().events();
auto all_memberships = getMemberships(it->second.state.events);
auto timelineMemberships = getMemberships(it->second.timeline.events);
for (auto event : it.value().timeline().events())
events.append(event);
// We have to process first the state events and then the timeline.
for (auto mm = timelineMemberships.cbegin(); mm != timelineMemberships.cend(); ++mm)
all_memberships.emplace(mm->first, mm->second);
RoomState local;
local.aliases = states[it.key()].aliases;
local.avatar = states[it.key()].avatar;
local.canonical_alias = states[it.key()].canonical_alias;
local.history_visibility = states[it.key()].history_visibility;
local.join_rules = states[it.key()].join_rules;
local.name = states[it.key()].name;
local.power_levels = states[it.key()].power_levels;
local.topic = states[it.key()].topic;
local.memberships = getMemberships(events);
local.aliases = states[room_id].aliases;
local.avatar = states[room_id].avatar;
local.canonical_alias = states[room_id].canonical_alias;
local.history_visibility = states[room_id].history_visibility;
local.join_rules = states[room_id].join_rules;
local.name = states[room_id].name;
local.power_levels = states[room_id].power_levels;
local.topic = states[room_id].topic;
local.memberships = all_memberships;
stateDiff.insert(it.key(), local);
stateDiff.insert(room_id, local);
}
return stateDiff;
}
using Memberships = QMap<QString, matrix::events::StateEvent<events::MemberEventContent>>;
Memberships
ChatPage::getMemberships(const QJsonArray &events) const
{
Memberships memberships;
events::EventType ty;
for (const auto &event : events) {
try {
ty = events::extractEventType(event.toObject());
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
if (!events::isStateEvent(ty))
continue;
try {
switch (ty) {
case events::EventType::RoomMember: {
events::StateEvent<events::MemberEventContent> member;
member.deserialize(event);
memberships.insert(member.stateKey(), member);
break;
}
default: {
continue;
}
}
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
}
return memberships;
}
ChatPage::~ChatPage() {}

View file

@ -30,12 +30,7 @@
#include "Login.h"
#include "MatrixClient.h"
#include "MessageEvent.h"
#include "Register.h"
#include "RoomMessages.h"
#include "Sync.h"
#include "mtx.hpp"
MatrixClient::MatrixClient(QString server, QObject *parent)
: QNetworkAccessManager(parent)
@ -239,26 +234,17 @@ MatrixClient::sync() noexcept
return;
}
auto data = reply->readAll();
if (data.isEmpty())
return;
auto json = QJsonDocument::fromJson(data);
SyncResponse response;
try {
response.deserialize(json);
mtx::responses::Sync response = nlohmann::json::parse(reply->readAll());
emit syncCompleted(response);
} catch (DeserializationException &e) {
} catch (std::exception &e) {
qWarning() << "Sync malformed response" << e.what();
}
});
}
void
MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
MatrixClient::sendRoomMessage(mtx::events::MessageType ty,
int txnId,
const QString &roomid,
const QString &msg,
@ -283,19 +269,19 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
QJsonObject info = {{"size", fileinfo.size()}, {"mimetype", mime.name()}};
switch (ty) {
case matrix::events::MessageEventType::Text:
case mtx::events::MessageType::Text:
body = {{"msgtype", "m.text"}, {"body", msg}};
break;
case matrix::events::MessageEventType::Emote:
case mtx::events::MessageType::Emote:
body = {{"msgtype", "m.emote"}, {"body", msg}};
break;
case matrix::events::MessageEventType::Image:
case mtx::events::MessageType::Image:
body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}, {"info", info}};
break;
case matrix::events::MessageEventType::File:
case mtx::events::MessageType::File:
body = {{"msgtype", "m.file"}, {"body", msg}, {"url", url}, {"info", info}};
break;
case matrix::events::MessageEventType::Audio:
case mtx::events::MessageType::Audio:
body = {{"msgtype", "m.audio"}, {"body", msg}, {"url", url}, {"info", info}};
break;
default:
@ -371,23 +357,14 @@ MatrixClient::initialSync() noexcept
return;
}
auto data = reply->readAll();
if (data.isEmpty())
return;
auto json = QJsonDocument::fromJson(data);
SyncResponse response;
try {
response.deserialize(json);
mtx::responses::Sync response = nlohmann::json::parse(reply->readAll());
emit initialSyncCompleted(response);
} catch (DeserializationException &e) {
qWarning() << "Sync malformed response" << e.what();
return;
}
emit initialSyncCompleted(response);
});
}
@ -686,18 +663,15 @@ MatrixClient::messages(const QString &roomid, const QString &from_token, int lim
return;
}
auto data = reply->readAll();
RoomMessages msgs;
try {
msgs.deserialize(QJsonDocument::fromJson(data));
} catch (const DeserializationException &e) {
mtx::responses::Messages messages =
nlohmann::json::parse(reply->readAll().data());
emit messagesRetrieved(roomid, messages);
} catch (std::exception &e) {
qWarning() << "Room messages from" << roomid << e.what();
return;
}
emit messagesRetrieved(roomid, msgs);
});
}

View file

@ -25,7 +25,6 @@
#include "RoomList.h"
#include "RoomSettings.h"
#include "RoomState.h"
#include "Sync.h"
RoomList::RoomList(QSharedPointer<MatrixClient> client, QWidget *parent)
: QWidget(parent)

View file

@ -15,15 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QSettings>
#include "RoomState.h"
namespace events = matrix::events;
RoomState::RoomState() {}
RoomState::RoomState(const QJsonArray &events) { updateFromEvents(events); }
RoomState::RoomState(const mtx::responses::Timeline &timeline)
{
updateFromEvents(timeline.events);
}
RoomState::RoomState(const mtx::responses::State &state) { updateFromEvents(state.events); }
void
RoomState::resolveName()
@ -31,19 +35,19 @@ RoomState::resolveName()
name_ = "Empty Room";
userAvatar_.clear();
if (!name.content().name().isEmpty()) {
name_ = name.content().name().simplified();
if (!name.content.name.empty()) {
name_ = QString::fromStdString(name.content.name).simplified();
return;
}
if (!canonical_alias.content().alias().isEmpty()) {
name_ = canonical_alias.content().alias().simplified();
if (!canonical_alias.content.alias.empty()) {
name_ = QString::fromStdString(canonical_alias.content.alias).simplified();
return;
}
// FIXME: Doesn't follow the spec guidelines.
if (aliases.content().aliases().size() != 0) {
name_ = aliases.content().aliases()[0].simplified();
if (aliases.content.aliases.size() != 0) {
name_ = QString::fromStdString(aliases.content.aliases[0]).simplified();
return;
}
@ -52,16 +56,20 @@ RoomState::resolveName()
// TODO: Display names should be sorted alphabetically.
for (const auto membership : memberships) {
if (membership.stateKey() == user_id)
const auto stateKey = QString::fromStdString(membership.second.state_key);
if (stateKey == user_id)
continue;
if (membership.content().membershipState() == events::Membership::Join) {
userAvatar_ = membership.stateKey();
if (membership.second.content.membership == mtx::events::state::Membership::Join) {
userAvatar_ = stateKey;
auto displayName =
QString::fromStdString(membership.second.content.display_name);
if (membership.content().displayName().isEmpty())
name_ = membership.stateKey();
if (displayName.isEmpty())
name_ = stateKey;
else
name_ = membership.content().displayName();
name_ = displayName;
break;
}
@ -76,12 +84,13 @@ void
RoomState::resolveAvatar()
{
if (userAvatar_.isEmpty()) {
avatar_ = avatar.content().url();
avatar_ = QString::fromStdString(avatar.content.url);
return;
}
if (memberships.contains(userAvatar_)) {
avatar_ = memberships[userAvatar_].content().avatarUrl();
if (memberships.count(userAvatar_.toStdString()) != 0) {
avatar_ =
QString::fromStdString(memberships[userAvatar_.toStdString()].content.avatar_url);
} else {
qWarning() << "Setting room avatar from unknown user id" << userAvatar_;
}
@ -91,8 +100,8 @@ RoomState::resolveAvatar()
void
RoomState::removeLeaveMemberships()
{
for (auto it = memberships.begin(); it != memberships.end();) {
if (it.value().content().membershipState() == events::Membership::Leave) {
for (auto it = memberships.cbegin(); it != memberships.cend();) {
if (it->second.content.membership == mtx::events::state::Membership::Leave) {
it = memberships.erase(it);
} else {
++it;
@ -106,49 +115,51 @@ RoomState::update(const RoomState &state)
bool needsNameCalculation = false;
bool needsAvatarCalculation = false;
if (aliases.eventId() != state.aliases.eventId()) {
if (aliases.event_id != state.aliases.event_id)
aliases = state.aliases;
}
if (avatar.eventId() != state.avatar.eventId()) {
if (avatar.event_id != state.avatar.event_id) {
avatar = state.avatar;
needsAvatarCalculation = true;
}
if (canonical_alias.eventId() != state.canonical_alias.eventId()) {
if (canonical_alias.event_id != state.canonical_alias.event_id) {
canonical_alias = state.canonical_alias;
needsNameCalculation = true;
}
if (create.eventId() != state.create.eventId())
if (create.event_id != state.create.event_id)
create = state.create;
if (history_visibility.eventId() != state.history_visibility.eventId())
if (history_visibility.event_id != state.history_visibility.event_id)
history_visibility = state.history_visibility;
if (join_rules.eventId() != state.join_rules.eventId())
if (join_rules.event_id != state.join_rules.event_id)
join_rules = state.join_rules;
if (name.eventId() != state.name.eventId()) {
if (name.event_id != state.name.event_id) {
name = state.name;
needsNameCalculation = true;
}
if (power_levels.eventId() != state.power_levels.eventId())
if (power_levels.event_id != state.power_levels.event_id)
power_levels = state.power_levels;
if (topic.eventId() != state.topic.eventId())
if (topic.event_id != state.topic.event_id)
topic = state.topic;
for (auto it = state.memberships.constBegin(); it != state.memberships.constEnd(); ++it) {
auto membershipState = it.value().content().membershipState();
for (auto it = state.memberships.cbegin(); it != state.memberships.cend(); ++it) {
auto membershipState = it->second.content.membership;
if (it.key() == userAvatar_) {
if (it->first == userAvatar_.toStdString()) {
needsNameCalculation = true;
needsAvatarCalculation = true;
}
if (membershipState == events::Membership::Leave)
this->memberships.remove(it.key());
if (membershipState == mtx::events::state::Membership::Leave)
this->memberships.erase(this->memberships.find(it->first));
else
this->memberships.insert(it.key(), it.value());
this->memberships.emplace(it->first, it->second);
}
if (needsNameCalculation)
@ -158,235 +169,126 @@ RoomState::update(const RoomState &state)
resolveAvatar();
}
QJsonObject
std::string
RoomState::serialize() const
{
QJsonObject obj;
nlohmann::json obj;
if (!aliases.eventId().isEmpty())
obj["aliases"] = aliases.serialize();
if (!aliases.event_id.empty())
obj["aliases"] = aliases;
if (!avatar.eventId().isEmpty())
obj["avatar"] = avatar.serialize();
if (!avatar.event_id.empty())
obj["avatar"] = avatar;
if (!canonical_alias.eventId().isEmpty())
obj["canonical_alias"] = canonical_alias.serialize();
if (!canonical_alias.event_id.empty())
obj["canonical_alias"] = canonical_alias;
if (!create.eventId().isEmpty())
obj["create"] = create.serialize();
if (!create.event_id.empty())
obj["create"] = create;
if (!history_visibility.eventId().isEmpty())
obj["history_visibility"] = history_visibility.serialize();
if (!history_visibility.event_id.empty())
obj["history_visibility"] = history_visibility;
if (!join_rules.eventId().isEmpty())
obj["join_rules"] = join_rules.serialize();
if (!join_rules.event_id.empty())
obj["join_rules"] = join_rules;
if (!name.eventId().isEmpty())
obj["name"] = name.serialize();
if (!name.event_id.empty())
obj["name"] = name;
if (!power_levels.eventId().isEmpty())
obj["power_levels"] = power_levels.serialize();
if (!power_levels.event_id.empty())
obj["power_levels"] = power_levels;
if (!topic.eventId().isEmpty())
obj["topic"] = topic.serialize();
if (!topic.event_id.empty())
obj["topic"] = topic;
return obj;
return obj.dump();
}
void
RoomState::parse(const QJsonObject &object)
RoomState::parse(const nlohmann::json &object)
{
// FIXME: Make this less versbose.
if (object.contains("aliases")) {
events::StateEvent<events::AliasesEventContent> event;
if (object.count("aliases") != 0) {
try {
event.deserialize(object["aliases"]);
aliases = event;
} catch (const DeserializationException &e) {
aliases = object.at("aliases")
.get<mtx::events::StateEvent<mtx::events::state::Aliases>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - aliases" << e.what();
}
}
if (object.contains("avatar")) {
events::StateEvent<events::AvatarEventContent> event;
if (object.count("avatar") != 0) {
try {
event.deserialize(object["avatar"]);
avatar = event;
} catch (const DeserializationException &e) {
avatar = object.at("avatar")
.get<mtx::events::StateEvent<mtx::events::state::Avatar>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - avatar" << e.what();
}
}
if (object.contains("canonical_alias")) {
events::StateEvent<events::CanonicalAliasEventContent> event;
if (object.count("canonical_alias") != 0) {
try {
event.deserialize(object["canonical_alias"]);
canonical_alias = event;
} catch (const DeserializationException &e) {
canonical_alias =
object.at("canonical_alias")
.get<mtx::events::StateEvent<mtx::events::state::CanonicalAlias>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - canonical_alias" << e.what();
}
}
if (object.contains("create")) {
events::StateEvent<events::CreateEventContent> event;
if (object.count("create") != 0) {
try {
event.deserialize(object["create"]);
create = event;
} catch (const DeserializationException &e) {
create = object.at("create")
.get<mtx::events::StateEvent<mtx::events::state::Create>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - create" << e.what();
}
}
if (object.contains("history_visibility")) {
events::StateEvent<events::HistoryVisibilityEventContent> event;
if (object.count("history_visibility") != 0) {
try {
event.deserialize(object["history_visibility"]);
history_visibility = event;
} catch (const DeserializationException &e) {
history_visibility =
object.at("history_visibility")
.get<mtx::events::StateEvent<mtx::events::state::HistoryVisibility>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - history_visibility" << e.what();
}
}
if (object.contains("join_rules")) {
events::StateEvent<events::JoinRulesEventContent> event;
if (object.count("join_rules") != 0) {
try {
event.deserialize(object["join_rules"]);
join_rules = event;
} catch (const DeserializationException &e) {
join_rules =
object.at("join_rules")
.get<mtx::events::StateEvent<mtx::events::state::JoinRules>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - join_rules" << e.what();
}
}
if (object.contains("name")) {
events::StateEvent<events::NameEventContent> event;
if (object.count("name") != 0) {
try {
event.deserialize(object["name"]);
name = event;
} catch (const DeserializationException &e) {
name = object.at("name")
.get<mtx::events::StateEvent<mtx::events::state::Name>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - name" << e.what();
}
}
if (object.contains("power_levels")) {
events::StateEvent<events::PowerLevelsEventContent> event;
if (object.count("power_levels") != 0) {
try {
event.deserialize(object["power_levels"]);
power_levels = event;
} catch (const DeserializationException &e) {
power_levels =
object.at("power_levels")
.get<mtx::events::StateEvent<mtx::events::state::PowerLevels>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - power_levels" << e.what();
}
}
if (object.contains("topic")) {
events::StateEvent<events::TopicEventContent> event;
if (object.count("topic") != 0) {
try {
event.deserialize(object["topic"]);
topic = event;
} catch (const DeserializationException &e) {
topic = object.at("topic")
.get<mtx::events::StateEvent<mtx::events::state::Topic>>();
} catch (std::exception &e) {
qWarning() << "RoomState::parse - topic" << e.what();
}
}
}
void
RoomState::updateFromEvents(const QJsonArray &events)
{
events::EventType ty;
for (const auto &event : events) {
try {
ty = events::extractEventType(event.toObject());
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
if (!events::isStateEvent(ty))
continue;
try {
switch (ty) {
case events::EventType::RoomAliases: {
events::StateEvent<events::AliasesEventContent> aliases;
aliases.deserialize(event);
this->aliases = aliases;
break;
}
case events::EventType::RoomAvatar: {
events::StateEvent<events::AvatarEventContent> avatar;
avatar.deserialize(event);
this->avatar = avatar;
break;
}
case events::EventType::RoomCanonicalAlias: {
events::StateEvent<events::CanonicalAliasEventContent>
canonical_alias;
canonical_alias.deserialize(event);
this->canonical_alias = canonical_alias;
break;
}
case events::EventType::RoomCreate: {
events::StateEvent<events::CreateEventContent> create;
create.deserialize(event);
this->create = create;
break;
}
case events::EventType::RoomHistoryVisibility: {
events::StateEvent<events::HistoryVisibilityEventContent>
history_visibility;
history_visibility.deserialize(event);
this->history_visibility = history_visibility;
break;
}
case events::EventType::RoomJoinRules: {
events::StateEvent<events::JoinRulesEventContent> join_rules;
join_rules.deserialize(event);
this->join_rules = join_rules;
break;
}
case events::EventType::RoomName: {
events::StateEvent<events::NameEventContent> name;
name.deserialize(event);
this->name = name;
break;
}
case events::EventType::RoomMember: {
events::StateEvent<events::MemberEventContent> member;
member.deserialize(event);
this->memberships.insert(member.stateKey(), member);
break;
}
case events::EventType::RoomPowerLevels: {
events::StateEvent<events::PowerLevelsEventContent> power_levels;
power_levels.deserialize(event);
this->power_levels = power_levels;
break;
}
case events::EventType::RoomTopic: {
events::StateEvent<events::TopicEventContent> topic;
topic.deserialize(event);
this->topic = topic;
break;
}
default: {
continue;
}
}
} catch (const DeserializationException &e) {
qWarning() << e.what() << event;
continue;
}
}
}

View file

@ -1,307 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include "Sync.h"
void
SyncResponse::deserialize(const QJsonDocument &data)
{
if (!data.isObject())
throw DeserializationException("Sync response is not a JSON object");
QJsonObject object = data.object();
if (!object.contains("next_batch"))
throw DeserializationException("Sync: missing next_batch parameter");
if (object.contains("rooms")) {
if (!object.value("rooms").isObject()) {
throw DeserializationException("Sync: rooms is not a JSON object");
}
rooms_.deserialize(object.value("rooms"));
}
if (object.contains("presence")) {
if (!object.value("presence").isObject()) {
throw DeserializationException("Sync: presence is not a JSON object");
}
// TODO: implement presence handling
}
if (object.contains("account_data")) {
if (!object.value("account_data").isObject()) {
throw DeserializationException("Sync: account_data is not a JSON object");
}
// TODO: implement account_data handling
}
if (object.contains("to_device")) {
if (!object.value("to_device").isObject()) {
throw DeserializationException("Sync: to_device is not a JSON object");
}
// TODO: implement to_device handling
}
// for device_lists updates (for e2e)
if (object.contains("device_lists")) {
if (!object.value("device_lists").isObject()) {
throw DeserializationException("Sync: device_lists is not a JSON object");
}
// TODO: implement device_lists handling
}
next_batch_ = object.value("next_batch").toString();
}
void
Rooms::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("Rooms value is not a JSON object");
QJsonObject object = data.toObject();
if (object.contains("join")) {
if (!object.value("join").isObject())
throw DeserializationException("rooms/join must be a JSON object");
auto join = object.value("join").toObject();
for (auto it = join.constBegin(); it != join.constEnd(); ++it) {
JoinedRoom tmp_room;
try {
tmp_room.deserialize(it.value());
join_.insert(it.key(), tmp_room);
} catch (DeserializationException &e) {
qWarning() << e.what();
qWarning() << "Skipping malformed object for room" << it.key();
}
}
}
if (object.contains("invite")) {
if (!object.value("invite").isObject()) {
throw DeserializationException("rooms/invite must be a JSON object");
}
// TODO: Implement invite handling
}
if (object.contains("leave")) {
if (!object.value("leave").isObject()) {
throw DeserializationException("rooms/leave must be a JSON object");
}
auto leave = object.value("leave").toObject();
for (auto it = leave.constBegin(); it != leave.constEnd(); ++it) {
LeftRoom tmp_room;
try {
tmp_room.deserialize(it.value());
leave_.insert(it.key(), tmp_room);
} catch (DeserializationException &e) {
qWarning() << e.what();
qWarning() << "Skipping malformed object for room" << it.key();
}
}
}
}
void
JoinedRoom::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("JoinedRoom is not a JSON object");
QJsonObject object = data.toObject();
if (object.contains("state")) {
if (!object.value("state").isObject()) {
throw DeserializationException("join/state should be an object");
}
QJsonObject state = object.value("state").toObject();
if (state.contains("events")) {
if (!state.value("events").isArray()) {
throw DeserializationException(
"join/state/events should be an array");
}
state_.deserialize(state.value("events"));
}
}
if (object.contains("timeline")) {
if (!object.value("timeline").isObject())
throw DeserializationException("join/timeline should be an object");
timeline_.deserialize(object.value("timeline"));
}
if (object.contains("ephemeral")) {
if (!object.value("ephemeral").isObject())
throw DeserializationException("join/ephemeral should be an object");
QJsonObject ephemeral = object.value("ephemeral").toObject();
if (ephemeral.contains("events")) {
if (!ephemeral.value("events").isArray())
qWarning() << "join/ephemeral/events should be an array";
auto ephemeralEvents = ephemeral.value("events").toArray();
for (const auto e : ephemeralEvents) {
auto obj = e.toObject();
if (obj.contains("type") && obj.value("type") == "m.typing") {
auto ids = obj.value("content")
.toObject()
.value("user_ids")
.toArray();
for (const auto uid : ids)
typingUserIDs_.push_back(uid.toString());
}
}
}
}
if (object.contains("account_data")) {
if (!object.value("account_data").isObject())
throw DeserializationException("join/account_data is not a JSON object");
// TODO: Implement account_data handling
}
if (object.contains("unread_notifications")) {
if (!object.value("unread_notifications").isObject()) {
throw DeserializationException(
"join/unread_notifications is not a JSON object");
}
QJsonObject unreadNotifications = object.value("unread_notifications").toObject();
if (unreadNotifications.contains("highlight_count")) {
// TODO: Implement unread_notifications handling
}
if (unreadNotifications.contains("notification_count")) {
// TODO: Implement unread_notifications handling
}
}
}
void
LeftRoom::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("LeftRoom is not a JSON object");
QJsonObject object = data.toObject();
if (!object.contains("state"))
throw DeserializationException("leave/state is missing");
if (!object.contains("timeline"))
throw DeserializationException("leave/timeline is missing");
if (!object.value("state").isObject())
throw DeserializationException("leave/state should be an object");
QJsonObject state = object.value("state").toObject();
if (!state.contains("events"))
throw DeserializationException("leave/state/events is missing");
state_.deserialize(state.value("events"));
timeline_.deserialize(object.value("timeline"));
}
void
Event::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("Event is not a JSON object");
QJsonObject object = data.toObject();
if (!object.contains("content"))
throw DeserializationException("event/content is missing");
if (!object.contains("unsigned"))
throw DeserializationException("event/content is missing");
if (!object.contains("sender"))
throw DeserializationException("event/sender is missing");
if (!object.contains("event_id"))
throw DeserializationException("event/event_id is missing");
// TODO: Make this optional
/* if (!object.contains("state_key")) */
/* throw DeserializationException("event/state_key is missing"); */
if (!object.contains("type"))
throw DeserializationException("event/type is missing");
if (!object.contains("origin_server_ts"))
throw DeserializationException("event/origin_server_ts is missing");
content_ = object.value("content").toObject();
unsigned_ = object.value("unsigned").toObject();
sender_ = object.value("sender").toString();
state_key_ = object.value("state_key").toString();
type_ = object.value("type").toString();
event_id_ = object.value("event_id").toString();
origin_server_ts_ = object.value("origin_server_ts").toDouble();
}
void
State::deserialize(const QJsonValue &data)
{
if (!data.isArray())
throw DeserializationException("State is not a JSON array");
events_ = data.toArray();
}
void
Timeline::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("Timeline is not a JSON object");
auto object = data.toObject();
if (!object.contains("events"))
throw DeserializationException("timeline/events is missing");
if (!object.contains("prev_batch"))
throw DeserializationException("timeline/prev_batch is missing");
if (!object.contains("limited"))
throw DeserializationException("timeline/limited is missing");
prev_batch_ = object.value("prev_batch").toString();
limited_ = object.value("limited").toBool();
if (!object.value("events").isArray())
throw DeserializationException("timeline/events is not a JSON array");
events_ = object.value("events").toArray();
}

View file

@ -1,55 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QJsonArray>
#include "AliasesEventContent.h"
using namespace matrix::events;
void
AliasesEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("AliasesEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("aliases") == QJsonValue::Undefined)
throw DeserializationException("aliases key is missing");
auto aliases = object.value("aliases").toArray();
for (const auto &alias : aliases)
aliases_.push_back(alias.toString());
}
QJsonObject
AliasesEventContent::serialize() const
{
QJsonObject object;
QJsonArray aliases;
for (const auto &alias : aliases_)
aliases.push_back(alias);
if (aliases.size() > 0)
object["aliases"] = aliases;
return object;
}

View file

@ -1,50 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include "AvatarEventContent.h"
using namespace matrix::events;
void
AvatarEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("AvatarEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("url") == QJsonValue::Undefined)
throw DeserializationException("url key is missing");
url_ = QUrl(object.value("url").toString());
if (!url_.isValid())
qWarning() << "Invalid avatar url" << url_;
}
QJsonObject
AvatarEventContent::serialize() const
{
QJsonObject object;
if (!url_.isEmpty())
object["url"] = url_.toString();
return object;
}

View file

@ -1,45 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CanonicalAliasEventContent.h"
using namespace matrix::events;
void
CanonicalAliasEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("CanonicalAliasEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("alias") == QJsonValue::Undefined)
throw DeserializationException("alias key is missing");
alias_ = object.value("alias").toString();
}
QJsonObject
CanonicalAliasEventContent::serialize() const
{
QJsonObject object;
if (!alias_.isEmpty())
object["alias"] = alias_;
return object;
}

View file

@ -1,45 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CreateEventContent.h"
using namespace matrix::events;
void
CreateEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("CreateEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("creator") == QJsonValue::Undefined)
throw DeserializationException("creator key is missing");
creator_ = object.value("creator").toString();
}
QJsonObject
CreateEventContent::serialize() const
{
QJsonObject object;
if (!creator_.isEmpty())
object["creator"] = creator_;
return object;
}

View file

@ -1,106 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "events/Event.h"
#include "AliasesEventContent.h"
#include "AvatarEventContent.h"
#include "CanonicalAliasEventContent.h"
#include "CreateEventContent.h"
#include "Deserializable.h"
#include "HistoryVisibilityEventContent.h"
#include "JoinRulesEventContent.h"
#include "MemberEventContent.h"
#include "NameEventContent.h"
#include "PowerLevelsEventContent.h"
#include "TopicEventContent.h"
matrix::events::EventType
matrix::events::extractEventType(const QJsonObject &object)
{
if (!object.contains("type"))
throw DeserializationException("Missing event type");
auto type = object.value("type").toString();
if (type == "m.room.aliases")
return EventType::RoomAliases;
else if (type == "m.room.avatar")
return EventType::RoomAvatar;
else if (type == "m.room.canonical_alias")
return EventType::RoomCanonicalAlias;
else if (type == "m.room.create")
return EventType::RoomCreate;
else if (type == "m.room.history_visibility")
return EventType::RoomHistoryVisibility;
else if (type == "m.room.join_rules")
return EventType::RoomJoinRules;
else if (type == "m.room.member")
return EventType::RoomMember;
else if (type == "m.room.message")
return EventType::RoomMessage;
else if (type == "m.room.name")
return EventType::RoomName;
else if (type == "m.room.power_levels")
return EventType::RoomPowerLevels;
else if (type == "m.room.topic")
return EventType::RoomTopic;
else
return EventType::Unsupported;
}
bool
matrix::events::isStateEvent(EventType type)
{
return type == EventType::RoomAliases || type == EventType::RoomAvatar ||
type == EventType::RoomCanonicalAlias || type == EventType::RoomCreate ||
type == EventType::RoomHistoryVisibility || type == EventType::RoomJoinRules ||
type == EventType::RoomMember || type == EventType::RoomName ||
type == EventType::RoomPowerLevels || type == EventType::RoomTopic;
}
bool
matrix::events::isMessageEvent(EventType type)
{
return type == EventType::RoomMessage;
}
void
matrix::events::UnsignedData::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("UnsignedData is not a JSON object");
auto object = data.toObject();
transaction_id_ = object.value("transaction_id").toString();
age_ = object.value("age").toDouble();
}
QJsonObject
matrix::events::UnsignedData::serialize() const
{
QJsonObject object;
if (!transaction_id_.isEmpty())
object["transaction_id"] = transaction_id_;
if (age_ > 0)
object["age"] = age_;
return object;
}

View file

@ -1,64 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "HistoryVisibilityEventContent.h"
using namespace matrix::events;
void
HistoryVisibilityEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException(
"HistoryVisibilityEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("history_visibility") == QJsonValue::Undefined)
throw DeserializationException("history_visibility key is missing");
auto value = object.value("history_visibility").toString();
if (value == "invited")
history_visibility_ = HistoryVisibility::Invited;
else if (value == "joined")
history_visibility_ = HistoryVisibility::Joined;
else if (value == "shared")
history_visibility_ = HistoryVisibility::Shared;
else if (value == "world_readable")
history_visibility_ = HistoryVisibility::WorldReadable;
else
throw DeserializationException(
QString("Unknown history_visibility value: %1").arg(value).toUtf8().constData());
}
QJsonObject
HistoryVisibilityEventContent::serialize() const
{
QJsonObject object;
if (history_visibility_ == HistoryVisibility::Invited)
object["history_visibility"] = "invited";
else if (history_visibility_ == HistoryVisibility::Joined)
object["history_visibility"] = "joined";
else if (history_visibility_ == HistoryVisibility::Shared)
object["history_visibility"] = "shared";
else if (history_visibility_ == HistoryVisibility::WorldReadable)
object["history_visibility"] = "world_readable";
return object;
}

View file

@ -1,63 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "JoinRulesEventContent.h"
using namespace matrix::events;
void
JoinRulesEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("JoinRulesEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("join_rule") == QJsonValue::Undefined)
throw DeserializationException("join_rule key is missing");
auto value = object.value("join_rule").toString();
if (value == "invite")
join_rule_ = JoinRule::Invite;
else if (value == "knock")
join_rule_ = JoinRule::Knock;
else if (value == "private")
join_rule_ = JoinRule::Private;
else if (value == "public")
join_rule_ = JoinRule::Public;
else
throw DeserializationException(
QString("Unknown join_rule value: %1").arg(value).toUtf8().constData());
}
QJsonObject
JoinRulesEventContent::serialize() const
{
QJsonObject object;
if (join_rule_ == JoinRule::Invite)
object["join_rule"] = "invite";
else if (join_rule_ == JoinRule::Knock)
object["join_rule"] = "knock";
else if (join_rule_ == JoinRule::Private)
object["join_rule"] = "private";
else if (join_rule_ == JoinRule::Public)
object["join_rule"] = "public";
return object;
}

View file

@ -1,84 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include "MemberEventContent.h"
using namespace matrix::events;
void
MemberEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("MemberEventContent is not a JSON object");
auto object = data.toObject();
if (!object.contains("membership"))
throw DeserializationException("membership key is missing");
auto value = object.value("membership").toString();
if (value == "ban")
membership_state_ = Membership::Ban;
else if (value == "invite")
membership_state_ = Membership::Invite;
else if (value == "join")
membership_state_ = Membership::Join;
else if (value == "knock")
membership_state_ = Membership::Knock;
else if (value == "leave")
membership_state_ = Membership::Leave;
else
throw DeserializationException(
QString("Unknown membership value: %1").arg(value).toUtf8().constData());
if (object.contains("avatar_url"))
avatar_url_ = QUrl(object.value("avatar_url").toString());
if (!avatar_url_.toString().isEmpty() && !avatar_url_.isValid())
qWarning() << "Invalid avatar url" << avatar_url_;
if (object.contains("displayname"))
display_name_ = object.value("displayname").toString();
}
QJsonObject
MemberEventContent::serialize() const
{
QJsonObject object;
if (membership_state_ == Membership::Ban)
object["membership"] = "ban";
else if (membership_state_ == Membership::Invite)
object["membership"] = "invite";
else if (membership_state_ == Membership::Join)
object["membership"] = "join";
else if (membership_state_ == Membership::Knock)
object["membership"] = "knock";
else if (membership_state_ == Membership::Leave)
object["membership"] = "leave";
if (!avatar_url_.isEmpty())
object["avatar_url"] = avatar_url_.toString();
if (!display_name_.isEmpty())
object["displayname"] = display_name_;
return object;
}

View file

@ -1,74 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include "MessageEventContent.h"
using namespace matrix::events;
MessageEventType
matrix::events::extractMessageEventType(const QJsonObject &data)
{
if (!data.contains("content"))
return MessageEventType::Unknown;
auto content = data.value("content").toObject();
auto msgtype = content.value("msgtype").toString();
if (msgtype == "m.audio")
return MessageEventType::Audio;
else if (msgtype == "m.emote")
return MessageEventType::Emote;
else if (msgtype == "m.file")
return MessageEventType::File;
else if (msgtype == "m.image")
return MessageEventType::Image;
else if (msgtype == "m.location")
return MessageEventType::Location;
else if (msgtype == "m.notice")
return MessageEventType::Notice;
else if (msgtype == "m.text")
return MessageEventType::Text;
else if (msgtype == "m.video")
return MessageEventType::Video;
else
return MessageEventType::Unknown;
}
void
MessageEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("MessageEventContent is not a JSON object");
auto object = data.toObject();
if (!object.contains("body"))
throw DeserializationException("body key is missing");
body_ = object.value("body").toString();
}
QJsonObject
MessageEventContent::serialize() const
{
// TODO: Add for all the message contents.
QJsonObject object;
return object;
}

View file

@ -1,45 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "NameEventContent.h"
using namespace matrix::events;
void
NameEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("NameEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("name") == QJsonValue::Undefined)
throw DeserializationException("name key is missing");
name_ = object.value("name").toString();
}
QJsonObject
NameEventContent::serialize() const
{
QJsonObject object;
if (!name_.isEmpty())
object["name"] = name_;
return object;
}

View file

@ -1,114 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QJsonObject>
#include "Deserializable.h"
#include "PowerLevelsEventContent.h"
using namespace matrix::events;
void
PowerLevelsEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("PowerLevelsEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("ban") != QJsonValue::Undefined)
ban_ = object.value("ban").toInt();
if (object.value("invite") != QJsonValue::Undefined)
invite_ = object.value("invite").toInt();
if (object.value("kick") != QJsonValue::Undefined)
kick_ = object.value("kick").toInt();
if (object.value("redact") != QJsonValue::Undefined)
redact_ = object.value("redact").toInt();
if (object.value("events_default") != QJsonValue::Undefined)
events_default_ = object.value("events_default").toInt();
if (object.value("state_default") != QJsonValue::Undefined)
state_default_ = object.value("state_default").toInt();
if (object.value("users_default") != QJsonValue::Undefined)
users_default_ = object.value("users_default").toInt();
if (object.value("users").isObject()) {
auto users = object.value("users").toObject();
for (auto it = users.constBegin(); it != users.constEnd(); ++it)
users_.insert(it.key(), it.value().toInt());
}
if (object.value("events").isObject()) {
auto events = object.value("events").toObject();
for (auto it = events.constBegin(); it != events.constEnd(); ++it)
events_.insert(it.key(), it.value().toInt());
}
}
QJsonObject
PowerLevelsEventContent::serialize() const
{
QJsonObject object;
object["ban"] = ban_;
object["invite"] = invite_;
object["kick"] = kick_;
object["redact"] = redact_;
object["events_default"] = events_default_;
object["users_default"] = users_default_;
object["state_default"] = state_default_;
QJsonObject users;
QJsonObject events;
for (auto it = users_.constBegin(); it != users_.constEnd(); ++it)
users.insert(it.key(), it.value());
for (auto it = events_.constBegin(); it != events_.constEnd(); ++it)
events.insert(it.key(), it.value());
object["users"] = users;
object["events"] = events;
return object;
}
int
PowerLevelsEventContent::eventLevel(QString event_type) const
{
if (events_.contains(event_type))
return events_[event_type];
return events_default_;
}
int
PowerLevelsEventContent::userLevel(QString userid) const
{
if (users_.contains(userid))
return users_[userid];
return users_default_;
}

View file

@ -1,45 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TopicEventContent.h"
using namespace matrix::events;
void
TopicEventContent::deserialize(const QJsonValue &data)
{
if (!data.isObject())
throw DeserializationException("TopicEventContent is not a JSON object");
auto object = data.toObject();
if (object.value("topic") == QJsonValue::Undefined)
throw DeserializationException("topic key is missing");
topic_ = object.value("topic").toString();
}
QJsonObject
TopicEventContent::serialize() const
{
QJsonObject object;
if (!topic_.isEmpty())
object["topic"] = topic_;
return object;
}

View file

@ -1,40 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Audio.h"
using namespace matrix::events::messages;
void
Audio::deserialize(const QJsonObject &object)
{
if (!object.contains("url"))
throw DeserializationException("url key is missing");
url_ = object.value("url").toString();
if (object.value("msgtype") != "m.audio")
throw DeserializationException("invalid msgtype for audio");
if (object.contains("info")) {
auto info = object.value("info").toObject();
info_.duration = info.value("duration").toInt();
info_.mimetype = info.value("mimetype").toString();
info_.size = info.value("size").toInt();
}
}

View file

@ -1,27 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Emote.h"
using namespace matrix::events::messages;
void
Emote::deserialize(const QJsonObject &object)
{
if (object.value("msgtype") != "m.emote")
throw DeserializationException("invalid msgtype for emote");
}

View file

@ -1,50 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "File.h"
using namespace matrix::events::messages;
void
File::deserialize(const QJsonObject &object)
{
if (!object.contains("url"))
throw DeserializationException("messages::File url key is missing");
if (object.value("msgtype") != "m.file")
throw DeserializationException("invalid msgtype for file");
url_ = object.value("url").toString();
filename_ = object.value("filename").toString();
if (object.contains("info")) {
auto file_info = object.value("info").toObject();
info_.size = file_info.value("size").toInt();
info_.mimetype = file_info.value("mimetype").toString();
info_.thumbnail_url = file_info.value("thumbnail_url").toString();
if (file_info.contains("thumbnail_info")) {
auto thumbinfo = file_info.value("thumbnail_info").toObject();
info_.thumbnail_info.h = thumbinfo.value("h").toInt();
info_.thumbnail_info.w = thumbinfo.value("w").toInt();
info_.thumbnail_info.size = thumbinfo.value("size").toInt();
info_.thumbnail_info.mimetype = thumbinfo.value("mimetype").toString();
}
}
}

View file

@ -1,52 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Image.h"
using namespace matrix::events::messages;
void
Image::deserialize(const QJsonObject &object)
{
if (!object.contains("url"))
throw DeserializationException("messages::Image url key is missing");
url_ = object.value("url").toString();
if (object.value("msgtype") != "m.image")
throw DeserializationException("invalid msgtype for image");
if (object.contains("info")) {
auto imginfo = object.value("info").toObject();
info_.w = imginfo.value("w").toInt();
info_.h = imginfo.value("h").toInt();
info_.size = imginfo.value("size").toInt();
info_.mimetype = imginfo.value("mimetype").toString();
info_.thumbnail_url = imginfo.value("thumbnail_url").toString();
if (imginfo.contains("thumbnail_info")) {
auto thumbinfo = imginfo.value("thumbnail_info").toObject();
info_.thumbnail_info.h = thumbinfo.value("h").toInt();
info_.thumbnail_info.w = thumbinfo.value("w").toInt();
info_.thumbnail_info.size = thumbinfo.value("size").toInt();
info_.thumbnail_info.mimetype = thumbinfo.value("mimetype").toString();
}
}
}

View file

@ -1,47 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Location.h"
using namespace matrix::events::messages;
void
Location::deserialize(const QJsonObject &object)
{
if (!object.contains("geo_uri"))
throw DeserializationException("messages::Location geo_uri key is missing");
if (object.value("msgtype") != "m.location")
throw DeserializationException("invalid msgtype for location");
geo_uri_ = object.value("geo_uri").toString();
if (object.contains("info")) {
auto location_info = object.value("info").toObject();
info_.thumbnail_url = location_info.value("thumbnail_url").toString();
if (location_info.contains("thumbnail_info")) {
auto thumbinfo = location_info.value("thumbnail_info").toObject();
info_.thumbnail_info.h = thumbinfo.value("h").toInt();
info_.thumbnail_info.w = thumbinfo.value("w").toInt();
info_.thumbnail_info.size = thumbinfo.value("size").toInt();
info_.thumbnail_info.mimetype = thumbinfo.value("mimetype").toString();
}
}
}

View file

@ -1,27 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Notice.h"
using namespace matrix::events::messages;
void
Notice::deserialize(const QJsonObject &object)
{
if (object.value("msgtype") != "m.notice")
throw DeserializationException("invalid msgtype for notice");
}

View file

@ -1,27 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Text.h"
using namespace matrix::events::messages;
void
Text::deserialize(const QJsonObject &object)
{
if (object.value("msgtype") != "m.text")
throw DeserializationException("invalid msgtype for text");
}

View file

@ -1,53 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Video.h"
using namespace matrix::events::messages;
void
Video::deserialize(const QJsonObject &object)
{
if (!object.contains("url"))
throw DeserializationException("messages::Video url key is missing");
url_ = object.value("url").toString();
if (object.value("msgtype") != "m.video")
throw DeserializationException("invalid msgtype for video");
if (object.contains("info")) {
auto video_info = object.value("info").toObject();
info_.w = video_info.value("w").toInt();
info_.h = video_info.value("h").toInt();
info_.size = video_info.value("size").toInt();
info_.duration = video_info.value("duration").toInt();
info_.mimetype = video_info.value("mimetype").toString();
info_.thumbnail_url = video_info.value("thumbnail_url").toString();
if (video_info.contains("thumbnail_info")) {
auto thumbinfo = video_info.value("thumbnail_info").toObject();
info_.thumbnail_info.h = thumbinfo.value("h").toInt();
info_.thumbnail_info.w = thumbinfo.value("w").toInt();
info_.thumbnail_info.size = thumbinfo.value("size").toInt();
info_.thumbnail_info.mimetype = thumbinfo.value("mimetype").toString();
}
}
}

View file

@ -21,7 +21,6 @@
#include "Avatar.h"
#include "Config.h"
#include "Sync.h"
#include "timeline/TimelineItem.h"
#include "timeline/widgets/AudioItem.h"
@ -32,9 +31,6 @@
static const QRegExp URL_REGEX("((?:https?|ftp)://\\S+)");
static const QString URL_HTML = "<a href=\"\\1\">\\1</a>";
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
void
TimelineItem::init()
{
@ -71,7 +67,7 @@ TimelineItem::init()
/*
* For messages created locally.
*/
TimelineItem::TimelineItem(events::MessageEventType ty,
TimelineItem::TimelineItem(mtx::events::MessageType ty,
const QString &userid,
QString body,
bool withSender,
@ -83,7 +79,7 @@ TimelineItem::TimelineItem(events::MessageEventType ty,
auto displayName = TimelineViewManager::displayName(userid);
auto timestamp = QDateTime::currentDateTime();
if (ty == events::MessageEventType::Emote) {
if (ty == mtx::events::MessageType::Emote) {
body = QString("* %1 %2").arg(displayName).arg(body);
descriptionMsg_ = {"", userid, body, descriptiveTime(timestamp)};
} else {
@ -152,64 +148,65 @@ TimelineItem::TimelineItem(VideoItem *video,
}
TimelineItem::TimelineItem(ImageItem *image,
const events::MessageEvent<msgs::Image> &event,
const mtx::events::RoomEvent<mtx::events::msg::Image> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
setupWidgetLayout<events::MessageEvent<msgs::Image>, ImageItem>(
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Image>, ImageItem>(
image, event, " sent an image", with_sender);
}
TimelineItem::TimelineItem(FileItem *file,
const events::MessageEvent<msgs::File> &event,
const mtx::events::RoomEvent<mtx::events::msg::File> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
setupWidgetLayout<events::MessageEvent<msgs::File>, FileItem>(
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::File>, FileItem>(
file, event, " sent a file", with_sender);
}
TimelineItem::TimelineItem(AudioItem *audio,
const events::MessageEvent<msgs::Audio> &event,
const mtx::events::RoomEvent<mtx::events::msg::Audio> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
setupWidgetLayout<events::MessageEvent<msgs::Audio>, AudioItem>(
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Audio>, AudioItem>(
audio, event, " sent an audio clip", with_sender);
}
TimelineItem::TimelineItem(VideoItem *video,
const events::MessageEvent<msgs::Video> &event,
const mtx::events::RoomEvent<mtx::events::msg::Video> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
setupWidgetLayout<events::MessageEvent<msgs::Video>, VideoItem>(
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>(
video, event, " sent a video clip", with_sender);
}
/*
* Used to display remote notice messages.
*/
TimelineItem::TimelineItem(const events::MessageEvent<msgs::Notice> &event,
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
init();
event_id_ = event.eventId();
event_id_ = QString::fromStdString(event.event_id);
const auto sender = QString::fromStdString(event.sender);
descriptionMsg_ = {TimelineViewManager::displayName(event.sender()),
event.sender(),
descriptionMsg_ = {TimelineViewManager::displayName(sender),
sender,
" sent a notification",
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.origin_server_ts))};
auto body = event.content().body().trimmed().toHtmlEscaped();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
generateTimestamp(timestamp);
@ -218,14 +215,14 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Notice> &event,
body = "<i>" + body + "</i>";
if (with_sender) {
auto displayName = TimelineViewManager::displayName(event.sender());
auto displayName = TimelineViewManager::displayName(sender);
generateBody(displayName, body);
setupAvatarLayout(displayName);
mainLayout_->addLayout(headerLayout_);
AvatarProvider::resolve(event.sender(), this);
AvatarProvider::resolve(sender, this);
} else {
generateBody(body);
setupSimpleLayout();
@ -237,24 +234,25 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Notice> &event,
/*
* Used to display remote emote messages.
*/
TimelineItem::TimelineItem(const events::MessageEvent<msgs::Emote> &event,
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
init();
event_id_ = event.eventId();
event_id_ = QString::fromStdString(event.event_id);
const auto sender = QString::fromStdString(event.sender);
auto body = event.content().body().trimmed();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
auto displayName = TimelineViewManager::displayName(event.sender());
auto body = QString::fromStdString(event.content.body).trimmed();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
auto displayName = TimelineViewManager::displayName(sender);
auto emoteMsg = QString("* %1 %2").arg(displayName).arg(body);
descriptionMsg_ = {"",
event.sender(),
sender,
emoteMsg,
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.origin_server_ts))};
generateTimestamp(timestamp);
emoteMsg = emoteMsg.toHtmlEscaped();
@ -266,7 +264,7 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Emote> &event,
setupAvatarLayout(displayName);
mainLayout_->addLayout(headerLayout_);
AvatarProvider::resolve(event.sender(), this);
AvatarProvider::resolve(sender, this);
} else {
generateBody(emoteMsg);
setupSimpleLayout();
@ -278,24 +276,25 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Emote> &event,
/*
* Used to display remote text messages.
*/
TimelineItem::TimelineItem(const events::MessageEvent<msgs::Text> &event,
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text> &event,
bool with_sender,
QWidget *parent)
: QWidget(parent)
{
init();
event_id_ = event.eventId();
event_id_ = QString::fromStdString(event.event_id);
const auto sender = QString::fromStdString(event.sender);
auto body = event.content().body().trimmed();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
auto displayName = TimelineViewManager::displayName(event.sender());
auto body = QString::fromStdString(event.content.body).trimmed();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
auto displayName = TimelineViewManager::displayName(sender);
QSettings settings;
descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName,
event.sender(),
descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
sender,
QString(": %1").arg(body),
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.origin_server_ts))};
generateTimestamp(timestamp);
@ -309,7 +308,7 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Text> &event,
mainLayout_->addLayout(headerLayout_);
AvatarProvider::resolve(event.sender(), this);
AvatarProvider::resolve(sender, this);
} else {
generateBody(body);
setupSimpleLayout();

View file

@ -22,7 +22,6 @@
#include "FloatingButton.h"
#include "RoomMessages.h"
#include "ScrollBar.h"
#include "Sync.h"
#include "timeline/TimelineView.h"
#include "timeline/widgets/AudioItem.h"
@ -30,23 +29,7 @@
#include "timeline/widgets/ImageItem.h"
#include "timeline/widgets/VideoItem.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
static bool
isRedactedEvent(const QJsonObject &event)
{
if (event.contains("redacted_because"))
return true;
if (event.contains("unsigned") &&
event.value("unsigned").toObject().contains("redacted_because"))
return true;
return false;
}
TimelineView::TimelineView(const Timeline &timeline,
TimelineView::TimelineView(const mtx::responses::Timeline &timeline,
QSharedPointer<MatrixClient> client,
const QString &room_id,
QWidget *parent)
@ -167,12 +150,12 @@ TimelineView::sliderMoved(int position)
}
void
TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msgs)
TimelineView::addBackwardsEvents(const QString &room_id, const mtx::responses::Messages &msgs)
{
if (room_id_ != room_id)
return;
if (msgs.chunk().count() == 0) {
if (msgs.chunk.size() == 0) {
isTimelineFinished = true;
return;
}
@ -186,12 +169,11 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg
// Parse in reverse order to determine where we should not show sender's
// name.
auto ii = msgs.chunk().size();
auto ii = msgs.chunk.size();
while (ii != 0) {
--ii;
TimelineItem *item =
parseMessageEvent(msgs.chunk().at(ii).toObject(), TimelineDirection::Top);
TimelineItem *item = parseMessageEvent(msgs.chunk[ii], TimelineDirection::Top);
if (item != nullptr)
items.push_back(item);
@ -210,11 +192,11 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg
QApplication::processEvents();
prev_batch_token_ = msgs.end();
prev_batch_token_ = QString::fromStdString(msgs.end);
isPaginationInProgress_ = false;
// Exclude the top stretch.
if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1)
if (msgs.chunk.size() != 0 && scroll_layout_->count() > 1)
notifyForLastEvent();
// If this batch is the first being rendered (i.e the first and the last
@ -224,63 +206,59 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg
}
TimelineItem *
TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction)
TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
TimelineDirection direction)
{
events::EventType ty = events::extractEventType(event);
namespace msg = mtx::events::msg;
using AudioEvent = mtx::events::RoomEvent<msg::Audio>;
using EmoteEvent = mtx::events::RoomEvent<msg::Emote>;
using FileEvent = mtx::events::RoomEvent<msg::File>;
using ImageEvent = mtx::events::RoomEvent<msg::Image>;
using NoticeEvent = mtx::events::RoomEvent<msg::Notice>;
using TextEvent = mtx::events::RoomEvent<msg::Text>;
using VideoEvent = mtx::events::RoomEvent<msg::Video>;
if (ty == events::EventType::RoomMessage) {
events::MessageEventType msg_type = events::extractMessageEventType(event);
using Audio = events::MessageEvent<msgs::Audio>;
using Emote = events::MessageEvent<msgs::Emote>;
using File = events::MessageEvent<msgs::File>;
using Image = events::MessageEvent<msgs::Image>;
using Notice = events::MessageEvent<msgs::Notice>;
using Text = events::MessageEvent<msgs::Text>;
using Video = events::MessageEvent<msgs::Video>;
if (msg_type == events::MessageEventType::Audio) {
return processMessageEvent<Audio, AudioItem>(event, direction);
} else if (msg_type == events::MessageEventType::Emote) {
return processMessageEvent<Emote>(event, direction);
} else if (msg_type == events::MessageEventType::File) {
return processMessageEvent<File, FileItem>(event, direction);
} else if (msg_type == events::MessageEventType::Image) {
return processMessageEvent<Image, ImageItem>(event, direction);
} else if (msg_type == events::MessageEventType::Notice) {
return processMessageEvent<Notice>(event, direction);
} else if (msg_type == events::MessageEventType::Text) {
return processMessageEvent<Text>(event, direction);
} else if (msg_type == events::MessageEventType::Video) {
return processMessageEvent<Video, VideoItem>(event, direction);
} else if (msg_type == events::MessageEventType::Unknown) {
// TODO Handle redacted messages.
// Silenced for now.
if (!isRedactedEvent(event))
qWarning() << "Unknown message type" << event;
return nullptr;
}
if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Audio>>(event)) {
auto audio = mpark::get<mtx::events::RoomEvent<msg::Audio>>(event);
return processMessageEvent<AudioEvent, AudioItem>(audio, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Emote>>(event)) {
auto emote = mpark::get<mtx::events::RoomEvent<msg::Emote>>(event);
return processMessageEvent<EmoteEvent>(emote, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::File>>(event)) {
auto file = mpark::get<mtx::events::RoomEvent<msg::File>>(event);
return processMessageEvent<FileEvent, FileItem>(file, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Image>>(event)) {
auto image = mpark::get<mtx::events::RoomEvent<msg::Image>>(event);
return processMessageEvent<ImageEvent, ImageItem>(image, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Notice>>(event)) {
auto notice = mpark::get<mtx::events::RoomEvent<msg::Notice>>(event);
return processMessageEvent<NoticeEvent>(notice, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Text>>(event)) {
auto text = mpark::get<mtx::events::RoomEvent<msg::Text>>(event);
return processMessageEvent<TextEvent>(text, direction);
} else if (mpark::holds_alternative<mtx::events::RoomEvent<msg::Video>>(event)) {
auto video = mpark::get<mtx::events::RoomEvent<msg::Video>>(event);
return processMessageEvent<VideoEvent, VideoItem>(video, direction);
}
return nullptr;
}
int
TimelineView::addEvents(const Timeline &timeline)
TimelineView::addEvents(const mtx::responses::Timeline &timeline)
{
int message_count = 0;
QSettings settings;
QString localUser = settings.value("auth/user_id").toString();
for (const auto &event : timeline.events()) {
TimelineItem *item = parseMessageEvent(event.toObject(), TimelineDirection::Bottom);
for (const auto &event : timeline.events) {
TimelineItem *item = parseMessageEvent(event, TimelineDirection::Bottom);
if (item != nullptr) {
addTimelineItem(item, TimelineDirection::Bottom);
if (localUser != event.toObject().value("sender").toString())
if (localUser != getEventSender(event))
message_count += 1;
}
}
@ -290,15 +268,15 @@ TimelineView::addEvents(const Timeline &timeline)
QApplication::processEvents();
if (isInitialSync) {
prev_batch_token_ = timeline.previousBatch();
prev_batch_token_ = QString::fromStdString(timeline.prev_batch);
isInitialSync = false;
}
// Exclude the top stretch.
if (!timeline.events().isEmpty() && scroll_layout_->count() > 1)
if (timeline.events.size() != 0 && scroll_layout_->count() > 1)
notifyForLastEvent();
if (isActiveWindow() && isVisible() && timeline.events().size() > 0)
if (isActiveWindow() && isVisible() && timeline.events.size() > 0)
readLastEvent();
return message_count;
@ -403,7 +381,7 @@ TimelineView::updatePendingMessage(int txn_id, QString event_id)
}
void
TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body)
TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
{
QSettings settings;
auto user_id = settings.value("auth/user_id").toString();
@ -439,9 +417,9 @@ TimelineView::sendNextPendingMessage()
PendingMessage &m = pending_msgs_.head();
switch (m.ty) {
case matrix::events::MessageEventType::Audio:
case matrix::events::MessageEventType::Image:
case matrix::events::MessageEventType::File:
case mtx::events::MessageType::Audio:
case mtx::events::MessageType::Image:
case mtx::events::MessageType::File:
// FIXME: Improve the API
client_->sendRoomMessage(m.ty,
m.txn_id,
@ -573,3 +551,81 @@ TimelineView::event(QEvent *event)
return QWidget::event(event);
}
QString
TimelineView::getEventSender(const mtx::events::collections::TimelineEvents &event) const
{
using Aliases = mtx::events::StateEvent<mtx::events::state::Aliases>;
using Avatar = mtx::events::StateEvent<mtx::events::state::Avatar>;
using CanonicalAlias = mtx::events::StateEvent<mtx::events::state::CanonicalAlias>;
using Create = mtx::events::StateEvent<mtx::events::state::Create>;
using HistoryVisibility = mtx::events::StateEvent<mtx::events::state::HistoryVisibility>;
using JoinRules = mtx::events::StateEvent<mtx::events::state::JoinRules>;
using Member = mtx::events::StateEvent<mtx::events::state::Member>;
using Name = mtx::events::StateEvent<mtx::events::state::Name>;
using PowerLevels = mtx::events::StateEvent<mtx::events::state::PowerLevels>;
using Topic = mtx::events::StateEvent<mtx::events::state::Topic>;
using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
using File = mtx::events::RoomEvent<mtx::events::msg::File>;
using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
if (mpark::holds_alternative<Aliases>(event)) {
auto msg = mpark::get<Aliases>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Avatar>(event)) {
auto msg = mpark::get<Avatar>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<CanonicalAlias>(event)) {
auto msg = mpark::get<CanonicalAlias>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Create>(event)) {
auto msg = mpark::get<Create>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<HistoryVisibility>(event)) {
auto msg = mpark::get<HistoryVisibility>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<JoinRules>(event)) {
auto msg = mpark::get<JoinRules>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Name>(event)) {
auto msg = mpark::get<Name>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Member>(event)) {
auto msg = mpark::get<Member>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<PowerLevels>(event)) {
auto msg = mpark::get<PowerLevels>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Topic>(event)) {
auto msg = mpark::get<Topic>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Audio>(event)) {
auto msg = mpark::get<Audio>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Emote>(event)) {
auto msg = mpark::get<Emote>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<File>(event)) {
auto msg = mpark::get<File>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Image>(event)) {
auto msg = mpark::get<Image>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Notice>(event)) {
auto msg = mpark::get<Notice>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Text>(event)) {
auto msg = mpark::get<Text>(event);
return QString::fromStdString(msg.sender);
} else if (mpark::holds_alternative<Video>(event)) {
auto msg = mpark::get<Video>(event);
return QString::fromStdString(msg.sender);
}
return QString("");
}

View file

@ -23,7 +23,6 @@
#include <QSettings>
#include "MatrixClient.h"
#include "Sync.h"
#include "timeline/TimelineView.h"
#include "timeline/TimelineViewManager.h"
@ -72,7 +71,7 @@ TimelineViewManager::queueTextMessage(const QString &msg)
auto room_id = active_room_;
auto view = views_[room_id];
view->addUserMessage(matrix::events::MessageEventType::Text, msg);
view->addUserMessage(mtx::events::MessageType::Text, msg);
}
void
@ -81,7 +80,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
auto room_id = active_room_;
auto view = views_[room_id];
view->addUserMessage(matrix::events::MessageEventType::Emote, msg);
view->addUserMessage(mtx::events::MessageType::Emote, msg);
}
void
@ -96,7 +95,7 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
auto view = views_[roomid];
view->addUserMessage<ImageItem, matrix::events::MessageEventType::Image>(url, filename);
view->addUserMessage<ImageItem, mtx::events::MessageType::Image>(url, filename);
}
void
@ -111,7 +110,7 @@ TimelineViewManager::queueFileMessage(const QString &roomid,
auto view = views_[roomid];
view->addUserMessage<FileItem, matrix::events::MessageEventType::File>(url, filename);
view->addUserMessage<FileItem, mtx::events::MessageType::File>(url, filename);
}
void
@ -126,7 +125,7 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
auto view = views_[roomid];
view->addUserMessage<AudioItem, matrix::events::MessageEventType::Audio>(url, filename);
view->addUserMessage<AudioItem, mtx::events::MessageType::Audio>(url, filename);
}
void
@ -139,10 +138,10 @@ TimelineViewManager::clearAll()
}
void
TimelineViewManager::initialize(const Rooms &rooms)
TimelineViewManager::initialize(const mtx::responses::Rooms &rooms)
{
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) {
addRoom(it.value(), it.key());
for (auto it = rooms.join.cbegin(); it != rooms.join.cend(); ++it) {
addRoom(it->second, QString::fromStdString(it->first));
}
}
@ -155,10 +154,10 @@ TimelineViewManager::initialize(const QList<QString> &rooms)
}
void
TimelineViewManager::addRoom(const JoinedRoom &room, const QString &room_id)
TimelineViewManager::addRoom(const mtx::responses::JoinedRoom &room, const QString &room_id)
{
// Create a history view with the room events.
TimelineView *view = new TimelineView(room.timeline(), client_, room_id);
TimelineView *view = new TimelineView(room.timeline, client_, room_id);
views_.insert(room_id, QSharedPointer<TimelineView>(view));
connect(view,
@ -195,10 +194,10 @@ TimelineViewManager::addRoom(const QString &room_id)
}
void
TimelineViewManager::sync(const Rooms &rooms)
TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
{
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) {
auto roomid = it.key();
for (auto it = rooms.join.cbegin(); it != rooms.join.cend(); ++it) {
auto roomid = QString::fromStdString(it->first);
if (!views_.contains(roomid)) {
qDebug() << "Ignoring event from unknown room" << roomid;
@ -207,7 +206,7 @@ TimelineViewManager::sync(const Rooms &rooms)
auto view = views_.value(roomid);
int msgs_added = view->addEvents(it.value().timeline());
int msgs_added = view->addEvents(it->second.timeline);
if (msgs_added > 0) {
// TODO: When the app window gets active the current

View file

@ -26,9 +26,6 @@
#include "timeline/widgets/AudioItem.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
constexpr int MaxWidth = 400;
constexpr int Height = 70;
constexpr int IconRadius = 22;
@ -77,15 +74,15 @@ AudioItem::init()
}
AudioItem::AudioItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::Audio> &event,
const mtx::events::RoomEvent<mtx::events::msg::Audio> &event,
QWidget *parent)
: QWidget(parent)
, url_{event.msgContent().url()}
, text_{event.content().body()}
, url_{QUrl(QString::fromStdString(event.content.url))}
, text_{QString::fromStdString(event.content.body)}
, event_{event}
, client_{client}
{
readableFileSize_ = calculateFileSize(event.msgContent().info().size);
readableFileSize_ = calculateFileSize(event.content.info.size);
init();
}
@ -151,14 +148,14 @@ AudioItem::mousePressEvent(QMouseEvent *event)
if (filenameToSave_.isEmpty())
return;
client_->downloadFile(event_.eventId(), url_);
client_->downloadFile(QString::fromStdString(event_.event_id), url_);
}
}
void
AudioItem::fileDownloaded(const QString &event_id, const QByteArray &data)
{
if (event_id != event_.eventId())
if (event_id != QString::fromStdString(event_.event_id))
return;
try {

View file

@ -26,9 +26,6 @@
#include "timeline/widgets/FileItem.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
constexpr int MaxWidth = 400;
constexpr int Height = 70;
constexpr int IconRadius = 22;
@ -64,15 +61,15 @@ FileItem::init()
}
FileItem::FileItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::File> &event,
const mtx::events::RoomEvent<mtx::events::msg::File> &event,
QWidget *parent)
: QWidget(parent)
, url_{event.msgContent().url()}
, text_{event.content().body()}
, url_{QString::fromStdString(event.content.url)}
, text_{QString::fromStdString(event.content.body)}
, event_{event}
, client_{client}
{
readableFileSize_ = calculateFileSize(event.msgContent().info().size);
readableFileSize_ = calculateFileSize(event.content.info.size);
init();
}
@ -138,7 +135,7 @@ FileItem::mousePressEvent(QMouseEvent *event)
if (filenameToSave_.isEmpty())
return;
client_->downloadFile(event_.eventId(), url_);
client_->downloadFile(QString::fromStdString(event_.event_id), url_);
} else {
openUrl();
}
@ -147,7 +144,7 @@ FileItem::mousePressEvent(QMouseEvent *event)
void
FileItem::fileDownloaded(const QString &event_id, const QByteArray &data)
{
if (event_id != event_.eventId())
if (event_id != QString::fromStdString(event_.event_id))
return;
try {

View file

@ -25,11 +25,8 @@
#include "dialogs/ImageOverlay.h"
#include "timeline/widgets/ImageItem.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::Image> &event,
const mtx::events::RoomEvent<mtx::events::msg::Image> &event,
QWidget *parent)
: QWidget(parent)
, event_{event}
@ -39,8 +36,8 @@ ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
setCursor(Qt::PointingHandCursor);
setAttribute(Qt::WA_Hover, true);
url_ = event.msgContent().url();
text_ = event.content().body();
url_ = QString::fromStdString(event.content.url);
text_ = QString::fromStdString(event.content.body);
QList<QString> url_parts = url_.toString().split("mxc://");
@ -53,7 +50,7 @@ ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
url_ = QString("%1/_matrix/media/r0/download/%2")
.arg(client_.data()->getHomeServer().toString(), media_params);
client_.data()->downloadImage(event.eventId(), url_);
client_.data()->downloadImage(QString::fromStdString(event.event_id), url_);
connect(client_.data(),
SIGNAL(imageDownloaded(const QString &, const QPixmap &)),
@ -91,7 +88,7 @@ ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
void
ImageItem::imageDownloaded(const QString &event_id, const QPixmap &img)
{
if (event_id != event_.eventId())
if (event_id != QString::fromStdString(event_.event_id))
return;
setImage(img);

View file

@ -21,9 +21,6 @@
#include "timeline/widgets/VideoItem.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
void
VideoItem::init()
{
@ -39,15 +36,15 @@ VideoItem::init()
}
VideoItem::VideoItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::Video> &event,
const mtx::events::RoomEvent<mtx::events::msg::Video> &event,
QWidget *parent)
: QWidget(parent)
, url_{event.msgContent().url()}
, text_{event.content().body()}
, url_{QString::fromStdString(event.content.url)}
, text_{QString::fromStdString(event.content.body)}
, event_{event}
, client_{client}
{
readableFileSize_ = calculateFileSize(event.msgContent().info().size);
readableFileSize_ = calculateFileSize(event.content.info.size);
init();