From 76241cb772bb53901cdd3308262f76ba718d9a47 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 31 Aug 2016 17:49:43 +0900 Subject: Dismiss ConnectionPrivate, long live Connection::Private Closer investigation found out that there are only two actually used member functions in ConnectionPrivate - provideRoom() and resolveServer(). These two have been transferred to Connection; and data members from ConnectionPrivate found new home in Connection::Private. Factoring out room management and user management from Connection is still a pending task. --- CMakeLists.txt | 1 - connection.cpp | 93 +++++++++++++++++++++++++++++++------ connection.h | 19 ++++++-- connectionprivate.cpp | 126 -------------------------------------------------- connectionprivate.h | 66 -------------------------- 5 files changed, 95 insertions(+), 210 deletions(-) delete mode 100644 connectionprivate.cpp delete mode 100644 connectionprivate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e5dae8f..80ffcfd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ message( STATUS ) set(libqmatrixclient_SRCS connectiondata.cpp connection.cpp - connectionprivate.cpp room.cpp user.cpp logmessage.cpp diff --git a/connection.cpp b/connection.cpp index 904649bc..d357599b 100644 --- a/connection.cpp +++ b/connection.cpp @@ -18,7 +18,7 @@ #include "connection.h" #include "connectiondata.h" -#include "connectionprivate.h" +//#include "connectionprivate.h" #include "user.h" #include "events/event.h" #include "room.h" @@ -33,15 +33,39 @@ #include "jobs/syncjob.h" #include "jobs/mediathumbnailjob.h" +#include #include using namespace QMatrixClient; +class Connection::Private +{ + public: + explicit Private(QUrl serverUrl) + : q(nullptr) + , data(new ConnectionData(serverUrl)) + , isConnected(false) + { } + Private(Private&) = delete; + ~Private() { delete data; } + + Room* provideRoom( QString id ); + + Connection* q; + ConnectionData* data; + QHash roomMap; + QHash userMap; + bool isConnected; + QString username; + QString password; + QString userId; +}; + Connection::Connection(QUrl server, QObject* parent) : QObject(parent) + , d(new Private(server)) { - d = new ConnectionPrivate(this); - d->data = new ConnectionData(server); + d->q = this; // All d initialization should occur before this line } Connection::Connection() @@ -56,7 +80,26 @@ Connection::~Connection() void Connection::resolveServer(QString domain) { - d->resolveServer( domain ); + // Find the Matrix server for the given domain. + QScopedPointer dns { new QDnsLookup() }; + dns->setType(QDnsLookup::SRV); + dns->setName("_matrix._tcp." + domain); + + dns->lookup(); + connect(dns.data(), &QDnsLookup::finished, [&]() { + // Check the lookup succeeded. + if (dns->error() != QDnsLookup::NoError || + dns->serviceRecords().isEmpty()) { + emit resolveError("DNS lookup failed"); + return; + } + + // Handle the results. + auto record = dns->serviceRecords().front(); + d->data->setHost(record.target()); + d->data->setPort(record.port()); + emit resolved(); + }); } void Connection::connectToServer(QString user, QString password) @@ -113,9 +156,9 @@ SyncJob* Connection::sync(int timeout) syncJob->setTimeout(timeout); connect( syncJob, &SyncJob::success, [=] () { d->data->setLastEvent(syncJob->nextBatch()); - for( const auto roomData: syncJob->roomData() ) + for( const auto& roomData: syncJob->roomData() ) { - if ( Room* r = d->provideRoom(roomData.roomId) ) + if ( Room* r = provideRoom(roomData.roomId) ) r->updateData(roomData); } emit syncDone(); @@ -147,7 +190,7 @@ void Connection::joinRoom(QString roomAlias) { JoinRoomJob* job = new JoinRoomJob(d->data, roomAlias); connect( job, &SyncJob::success, [=] () { - if ( Room* r = d->provideRoom(job->roomId()) ) + if ( Room* r = provideRoom(job->roomId()) ) emit joinedRoom(r); }); job->start(); @@ -159,12 +202,12 @@ void Connection::leaveRoom(Room* room) job->start(); } -void Connection::getMembers(Room* room) -{ - RoomMembersJob* job = new RoomMembersJob(d->data, room); - connect( job, &RoomMembersJob::result, d, &ConnectionPrivate::gotRoomMembers ); - job->start(); -} +//void Connection::getMembers(Room* room) +//{ +// RoomMembersJob* job = new RoomMembersJob(d->data, room); +// connect( job, &RoomMembersJob::result, d, &ConnectionPrivate::gotRoomMembers ); +// job->start(); +//} RoomMessagesJob* Connection::getMessages(Room* room, QString from) { @@ -221,6 +264,30 @@ ConnectionData* Connection::connectionData() return d->data; } +Room* Connection::provideRoom(QString id) +{ + if (id.isEmpty()) + { + qDebug() << "ConnectionPrivate::provideRoom() with empty id, doing nothing"; + return nullptr; + } + + if (d->roomMap.contains(id)) + return d->roomMap.value(id); + + // Not yet in the map, create a new one. + Room* room = createRoom(id); + if (room) + { + d->roomMap.insert( id, room ); + emit newRoom(room); + } else { + qCritical() << "Failed to create a room!!!" << id; + } + + return room; +} + User* Connection::createUser(QString userId) { return new User(userId, this); diff --git a/connection.h b/connection.h index f3a15cba..2816895e 100644 --- a/connection.h +++ b/connection.h @@ -55,7 +55,7 @@ namespace QMatrixClient Q_INVOKABLE virtual PostReceiptJob* postReceipt( Room* room, Event* event ); Q_INVOKABLE virtual void joinRoom( QString roomAlias ); Q_INVOKABLE virtual void leaveRoom( Room* room ); - Q_INVOKABLE virtual void getMembers( Room* room ); +// Q_INVOKABLE virtual void getMembers( Room* room ); Q_INVOKABLE virtual RoomMessagesJob* getMessages( Room* room, QString from ); virtual MediaThumbnailJob* getThumbnail( QUrl url, int requestedWidth, int requestedHeight ); @@ -81,10 +81,21 @@ namespace QMatrixClient protected: /** - * Access the underlying ConnectionData class + * @brief Access the underlying ConnectionData class */ ConnectionData* connectionData(); + /** + * @brief Find a (possibly new) Room object for the specified id + * Use this method whenever you need to find a Room object in + * the local list of rooms. Note that this does not interact with + * the server; in particular, does not automatically create rooms + * on the server. + * @return a pointer to a Room object with the specified id; nullptr + * if roomId is empty if createRoom() failed to create a Room object. + */ + Room* provideRoom(QString roomId); + /** * makes it possible for derived classes to have its own User class */ @@ -96,8 +107,8 @@ namespace QMatrixClient virtual Room* createRoom(QString roomId); private: - friend class ConnectionPrivate; - ConnectionPrivate* d; + class Private; + Private* d; }; } diff --git a/connectionprivate.cpp b/connectionprivate.cpp deleted file mode 100644 index 6556a8ee..00000000 --- a/connectionprivate.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "connectionprivate.h" -#include "connection.h" -#include "state.h" -#include "room.h" -#include "user.h" -#include "jobs/passwordlogin.h" -#include "jobs/syncjob.h" -#include "jobs/joinroomjob.h" -#include "jobs/roommembersjob.h" -#include "events/event.h" -#include "events/roommessageevent.h" -#include "events/roommemberevent.h" - -#include -#include - -using namespace QMatrixClient; - -ConnectionPrivate::ConnectionPrivate(Connection* parent) - : q(parent) -{ - isConnected = false; - data = nullptr; -} - -ConnectionPrivate::~ConnectionPrivate() -{ - delete data; -} - -void ConnectionPrivate::resolveServer(QString domain) -{ - // Find the Matrix server for the given domain. - QDnsLookup* dns = new QDnsLookup(); - dns->setType(QDnsLookup::SRV); - dns->setName("_matrix._tcp." + domain); - - connect(dns, &QDnsLookup::finished, [this,dns]() { - // Check the lookup succeeded. - if (dns->error() != QDnsLookup::NoError || - dns->serviceRecords().isEmpty()) { - emit q->resolveError("DNS lookup failed"); - dns->deleteLater(); - return; - } - - // Handle the results. - QDnsServiceRecord record = dns->serviceRecords().first(); - data->setHost(record.target()); - data->setPort(record.port()); - emit q->resolved(); - dns->deleteLater(); - }); - dns->lookup(); -} - -void ConnectionPrivate::processState(State* state) -{ - if( state->event()->type() == QMatrixClient::EventType::RoomMember ) - { - QMatrixClient::RoomMemberEvent* e = static_cast(state->event()); - User* user = q->user(e->userId()); - user->processEvent(e); - } - - if ( Room* r = provideRoom(state->event()->roomId()) ) - r->addInitialState(state); -} - -Room* ConnectionPrivate::provideRoom(QString id) -{ - if (id.isEmpty()) - { - qDebug() << "ConnectionPrivate::provideRoom() with empty id, doing nothing"; - return nullptr; - } - - if (roomMap.contains(id)) - return roomMap.value(id); - - // Not yet in the map, create a new one. - Room* room = q->createRoom(id); - if (!room) - qCritical() << "Failed to create a room!!!" << id; - - roomMap.insert( id, room ); - emit q->newRoom(room); - return room; -} - -void ConnectionPrivate::gotRoomMembers(BaseJob* job) -{ - RoomMembersJob* membersJob = static_cast(job); - if( !membersJob->error() ) - { - for( State* state: membersJob->states() ) - { - processState(state); - } - qDebug() << membersJob->states().count() << " processed..."; - } - else - { - qDebug() << "MembersJob error: " <errorString(); - if( membersJob->error() == BaseJob::NetworkError ) - emit q->connectionError( membersJob->errorString() ); - } -} diff --git a/connectionprivate.h b/connectionprivate.h deleted file mode 100644 index 424ef998..00000000 --- a/connectionprivate.h +++ /dev/null @@ -1,66 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef QMATRIXCLIENT_CONNECTIONPRIVATE_H -#define QMATRIXCLIENT_CONNECTIONPRIVATE_H - -#include -#include -#include - -#include "connection.h" -#include "connectiondata.h" - -namespace QMatrixClient -{ - class Connection; - class Event; - class State; - class User; - class BaseJob; - class SyncRoomData; - - class ConnectionPrivate : public QObject - { - Q_OBJECT - public: - ConnectionPrivate(Connection* parent); - ~ConnectionPrivate(); - - void resolveServer( QString domain ); - - void processState( State* state ); - - /** Finds a room with this id or creates a new one and adds it to roomMap. */ - Room* provideRoom( QString id ); - - Connection* q; - ConnectionData* data; - QHash roomMap; - QHash userMap; - bool isConnected; - QString username; - QString password; - QString userId; - - public slots: - void gotRoomMembers(BaseJob* job); - }; -} - -#endif // QMATRIXCLIENT_CONNECTIONPRIVATE_H -- cgit v1.2.3 From 4004992d8dd55ee27c3d39434d6a4498b8f515ef Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 31 Aug 2016 17:57:27 +0900 Subject: Room: Drop unused code --- room.cpp | 96 ---------------------------------------------------------------- room.h | 3 -- 2 files changed, 99 deletions(-) diff --git a/room.cpp b/room.cpp index 768a0dac..161f5385 100644 --- a/room.cpp +++ b/room.cpp @@ -51,8 +51,6 @@ class Room::Private Room* q; - //static LogMessage* parseMessage(const QJsonObject& message); - // This updates the room displayname field (which is the way a room // should be shown in the room list) It should be called whenever the // list of members or the room name (m.room.name) or canonical alias change. @@ -330,18 +328,6 @@ QString Room::roomMembername(QString userId) const return roomMembername(connection()->user(userId)); } -void Room::addMessage(Event* event) -{ - processMessageEvent(event); - emit newMessage(event); - //d->addState(event); -} - -void Room::addInitialState(State* state) -{ - processStateEvent(state->event()); -} - void Room::updateData(const SyncRoomData& data) { if( d->prevBatch.isEmpty() ) @@ -578,85 +564,3 @@ void Room::Private::updateDisplayname() if (old_name != displayname) emit q->displaynameChanged(q); } - -// void Room::setAlias(QString alias) -// { -// d->alias = alias; -// emit aliasChanged(this); -// } -// -// bool Room::parseEvents(const QJsonObject& json) -// { -// QList newMessages; -// QJsonValue value = json.value("messages").toObject().value("chunk"); -// if( !value.isArray() ) -// { -// return false; -// } -// QJsonArray messages = value.toArray(); -// for(const QJsonValue& val: messages ) -// { -// if( !val.isObject() ) -// continue; -// LogMessage* msg = Private::parseMessage(val.toObject()); -// if( msg ) -// { -// newMessages.append(msg); -// } -// -// } -// addMessages(newMessages); -// return true; -// } -// -// bool Room::parseSingleEvent(const QJsonObject& json) -// { -// qDebug() << "parseSingleEvent"; -// LogMessage* msg = Private::parseMessage(json); -// if( msg ) -// { -// addMessage(msg); -// return true; -// } -// return false; -// } -// -// bool Room::parseState(const QJsonObject& json) -// { -// QJsonValue value = json.value("state"); -// if( !value.isArray() ) -// { -// return false; -// } -// QJsonArray states = value.toArray(); -// for( const QJsonValue& val: states ) -// { -// QJsonObject state = val.toObject(); -// QString type = state.value("type").toString(); -// if( type == "m.room.aliases" ) -// { -// QJsonArray aliases = state.value("content").toObject().value("aliases").toArray(); -// if( aliases.count() > 0 ) -// { -// setAlias(aliases.at(0).toString()); -// } -// } -// } -// return true; -// } -// -// LogMessage* Room::Private::parseMessage(const QJsonObject& message) -// { -// if( message.value("type") == "m.room.message" ) -// { -// QJsonObject content = message.value("content").toObject(); -// if( content.value("msgtype").toString() != "m.text" ) -// return 0; -// QString user = message.value("user_id").toString(); -// QString body = content.value("body").toString(); -// LogMessage* msg = new LogMessage( LogMessage::UserMessage, body, user ); -// return msg; -// } -// return 0; -// } - diff --git a/room.h b/room.h index 8cf393a2..25eae11b 100644 --- a/room.h +++ b/room.h @@ -30,7 +30,6 @@ namespace QMatrixClient { class Event; - class State; class Connection; class User; @@ -65,8 +64,6 @@ namespace QMatrixClient */ Q_INVOKABLE QString roomMembername(QString userId) const; - Q_INVOKABLE void addMessage( Event* event ); - Q_INVOKABLE void addInitialState( State* state ); Q_INVOKABLE void updateData( const SyncRoomData& data ); Q_INVOKABLE void setJoinState( JoinState state ); -- cgit v1.2.3 From 0fe83d59d76cd8f9c8f92d40cc58d9f5b082a84a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 31 Aug 2016 18:27:43 +0900 Subject: Event::fromJson(): made the code more compact That might be not as efficient as a solution on variadic templates; but arguably easier to understand. --- events/event.cpp | 55 ++++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/events/event.cpp b/events/event.cpp index d05d666f..1235780d 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -82,40 +82,33 @@ QString Event::originalJson() const return d->originalJson; } +template +Event* make(const QJsonObject& obj) +{ + return T::fromJson(obj); +} + Event* Event::fromJson(const QJsonObject& obj) { - //qDebug() << obj.value("type").toString(); - if( obj.value("type").toString() == "m.room.message" ) - { - return RoomMessageEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.room.name" ) - { - return RoomNameEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.room.aliases" ) - { - return RoomAliasesEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.room.canonical_alias" ) - { - return RoomCanonicalAliasEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.room.member" ) - { - return RoomMemberEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.room.topic" ) - { - return RoomTopicEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.typing" ) - { - return TypingEvent::fromJson(obj); - } - if( obj.value("type").toString() == "m.receipt" ) + struct Factory { + QString type; + Event* (*make)(const QJsonObject& obj); + }; + const Factory evTypes[] { + { "m.room.message", make }, + { "m.room.name", make }, + { "m.room.aliases", make }, + { "m.room.canonical_alias", make }, + { "m.room.member", make }, + { "m.room.topic", make }, + { "m.room.typing", make }, + { "m.room.receipt", make }, + // Insert new types before this line + }; + for (auto e: evTypes) { - return ReceiptEvent::fromJson(obj); + if (obj["type"].toString() == e.type) + return e.make(obj); } return UnknownEvent::fromJson(obj); } -- cgit v1.2.3 From b1b23e3500ff80d62b54153341e66fdfef4534db Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 31 Aug 2016 18:30:42 +0900 Subject: QList -> using Events=QVector --- events/event.cpp | 10 +++++----- events/event.h | 12 ++++++++++-- jobs/roommessagesjob.cpp | 7 +++---- jobs/roommessagesjob.h | 5 +++-- jobs/syncjob.cpp | 3 +-- jobs/syncjob.h | 5 ++--- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/events/event.cpp b/events/event.cpp index 1235780d..11ed1cc9 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -144,11 +144,11 @@ bool Event::parseJson(const QJsonObject& obj) return correct; } -QList QMatrixClient::eventListFromJson(const QJsonArray& json) +Events QMatrixClient::eventsFromJson(const QJsonArray& json) { - QList l; - l.reserve(json.size()); + Events evs; + evs.reserve(json.size()); for (auto event: json) - l.push_back(Event::fromJson(event.toObject())); - return l; + evs.push_back(Event::fromJson(event.toObject())); + return evs; } diff --git a/events/event.h b/events/event.h index 72d26208..d9316747 100644 --- a/events/event.h +++ b/events/event.h @@ -24,6 +24,7 @@ #include #include #include +#include class QJsonArray; @@ -38,7 +39,8 @@ namespace QMatrixClient class Event { public: - Event(EventType type); + explicit Event(EventType type); + Event(Event&) = delete; virtual ~Event(); EventType type() const; @@ -57,8 +59,9 @@ namespace QMatrixClient class Private; Private* d; }; + using Events = QVector; - QList eventListFromJson(const QJsonArray& contents); + Events eventsFromJson(const QJsonArray& contents); /** * Finds a place in the timeline where a new event/message could be inserted. @@ -71,6 +74,11 @@ namespace QMatrixClient { return std::lower_bound (timeline.begin(), timeline.end(), item, [](const typename ContT::value_type a, const ItemT * b) { + // FIXME: We should not order the message list by origin timestamp. + // Rather, an order of receiving should be used (which actually + // poses a question on whether this method is needed at all - + // or we'd just prepend and append, depending on whether we + // received something from /sync or from /messages. return a->timestamp() < b->timestamp(); } ); diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp index ba075007..f880713d 100644 --- a/jobs/roommessagesjob.cpp +++ b/jobs/roommessagesjob.cpp @@ -18,7 +18,6 @@ #include "roommessagesjob.h" #include "../room.h" -#include "../events/event.h" #include #include @@ -35,7 +34,7 @@ class RoomMessagesJob::Private FetchDirectory dir; int limit; - QList events; + Events events; QString end; }; @@ -54,7 +53,7 @@ RoomMessagesJob::~RoomMessagesJob() delete d; } -QList RoomMessagesJob::events() +Events RoomMessagesJob::events() { return d->events; } @@ -84,7 +83,7 @@ QUrlQuery RoomMessagesJob::query() const BaseJob::Status RoomMessagesJob::parseJson(const QJsonDocument& data) { QJsonObject obj = data.object(); - d->events = eventListFromJson(obj.value("chunk").toArray()); + d->events = eventsFromJson(obj.value("chunk").toArray()); d->end = obj.value("end").toString(); return Success; } diff --git a/jobs/roommessagesjob.h b/jobs/roommessagesjob.h index 52a72f70..9bedcad9 100644 --- a/jobs/roommessagesjob.h +++ b/jobs/roommessagesjob.h @@ -21,10 +21,11 @@ #include "basejob.h" +#include "../events/event.h" + namespace QMatrixClient { class Room; - class Event; enum class FetchDirectory { Backwards, Forward }; @@ -34,7 +35,7 @@ namespace QMatrixClient RoomMessagesJob(ConnectionData* data, Room* room, QString from, FetchDirectory dir = FetchDirectory::Backwards, int limit=10); virtual ~RoomMessagesJob(); - QList events(); + Events events(); QString end(); protected: diff --git a/jobs/syncjob.cpp b/jobs/syncjob.cpp index a9865b55..2b2705b1 100644 --- a/jobs/syncjob.cpp +++ b/jobs/syncjob.cpp @@ -26,7 +26,6 @@ #include "../room.h" #include "../connectiondata.h" -#include "../events/event.h" using namespace QMatrixClient; @@ -139,7 +138,7 @@ BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) void SyncRoomData::EventList::fromJson(const QJsonObject& roomContents) { - auto l = eventListFromJson(roomContents[jsonKey].toObject()["events"].toArray()); + auto l = eventsFromJson(roomContents[jsonKey].toObject()["events"].toArray()); swap(l); } diff --git a/jobs/syncjob.h b/jobs/syncjob.h index f08a6b56..ed99b38b 100644 --- a/jobs/syncjob.h +++ b/jobs/syncjob.h @@ -22,15 +22,14 @@ #include "basejob.h" #include "../joinstate.h" +#include "../events/event.h" namespace QMatrixClient { - class Event; - class SyncRoomData { public: - class EventList : public QList + class EventList : public Events { private: QString jsonKey; -- cgit v1.2.3 From 4e70dd91bb70fa2b9b338686e4d5ab2560947723 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 12:02:56 +0900 Subject: Connection: homeserver(); Connection and ConnectionData: accessToken(); accessors de-virtualized Details: - New: Connection::homeserver(), returns the homeserver URL from inside ConnectionData - New/Modify: Connection::accessToken() and ConnectionData::accessToken() - because we will also have refresh tokens eventually; deprecate token() for the same reason - Modify: Connection::user(), token(), accessToken() are no more virtual (they should have never been, these are plain accessors, after all) --- connection.cpp | 21 ++++++++++++++++----- connection.h | 12 ++++++++---- connectiondata.cpp | 8 ++++---- connectiondata.h | 4 ++-- jobs/basejob.cpp | 2 +- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/connection.cpp b/connection.cpp index d357599b..f7f09c04 100644 --- a/connection.cpp +++ b/connection.cpp @@ -106,7 +106,6 @@ void Connection::connectToServer(QString user, QString password) { PasswordLogin* loginJob = new PasswordLogin(d->data, user, password); connect( loginJob, &PasswordLogin::success, [=] () { - qDebug() << "Our user ID: " << loginJob->id(); connectWithToken(loginJob->id(), loginJob->token()); }); connect( loginJob, &PasswordLogin::failure, [=] () { @@ -122,7 +121,9 @@ void Connection::connectWithToken(QString userId, QString token) d->isConnected = true; d->userId = userId; d->data->setToken(token); - qDebug() << "Connected with token:"; + qDebug() << "Accessing" << d->data->baseUrl() + << "by user" << userId + << "with the following access token:"; qDebug() << token; emit connected(); } @@ -223,6 +224,11 @@ MediaThumbnailJob* Connection::getThumbnail(QUrl url, int requestedWidth, int re return job; } +QUrl Connection::homeserver() const +{ + return d->data->baseUrl(); +} + User* Connection::user(QString userId) { if( d->userMap.contains(userId) ) @@ -239,14 +245,19 @@ User *Connection::user() return user(d->userId); } -QString Connection::userId() +QString Connection::userId() const { return d->userId; } -QString Connection::token() +QString Connection::token() const +{ + return accessToken(); +} + +QString Connection::accessToken() const { - return d->data->token(); + return d->data->accessToken(); } QHash< QString, Room* > Connection::roomMap() const diff --git a/connection.h b/connection.h index 2816895e..bfb4d377 100644 --- a/connection.h +++ b/connection.h @@ -20,6 +20,7 @@ #define QMATRIXCLIENT_CONNECTION_H #include +#include namespace QMatrixClient { @@ -59,10 +60,13 @@ namespace QMatrixClient Q_INVOKABLE virtual RoomMessagesJob* getMessages( Room* room, QString from ); virtual MediaThumbnailJob* getThumbnail( QUrl url, int requestedWidth, int requestedHeight ); - Q_INVOKABLE virtual User* user(QString userId); - Q_INVOKABLE virtual User* user(); - Q_INVOKABLE virtual QString userId(); - Q_INVOKABLE virtual QString token(); + Q_INVOKABLE QUrl homeserver() const; + Q_INVOKABLE User* user(QString userId); + Q_INVOKABLE User* user(); + Q_INVOKABLE QString userId() const; + /** @deprecated Use accessToken() instead. */ + Q_INVOKABLE QString token() const; + Q_INVOKABLE QString accessToken() const; signals: void resolved(); diff --git a/connectiondata.cpp b/connectiondata.cpp index 4a3bd47f..72e8f3d8 100644 --- a/connectiondata.cpp +++ b/connectiondata.cpp @@ -29,7 +29,7 @@ class ConnectionData::Private QUrl baseUrl; //bool isConnected; - QString token; + QString accessToken; QString lastEvent; QNetworkAccessManager* nam; }; @@ -52,9 +52,9 @@ ConnectionData::~ConnectionData() // return d->isConnected; // } -QString ConnectionData::token() const +QString ConnectionData::accessToken() const { - return d->token; + return d->accessToken; } QUrl ConnectionData::baseUrl() const @@ -69,7 +69,7 @@ QNetworkAccessManager* ConnectionData::nam() const void ConnectionData::setToken(QString token) { - d->token = token; + d->accessToken = token; } void ConnectionData::setHost(QString host) diff --git a/connectiondata.h b/connectiondata.h index 6407780c..5c8342d7 100644 --- a/connectiondata.h +++ b/connectiondata.h @@ -32,11 +32,11 @@ namespace QMatrixClient virtual ~ConnectionData(); //bool isConnected() const; - QString token() const; + QString accessToken() const; QUrl baseUrl() const; QNetworkAccessManager* nam() const; - void setToken( QString token ); + void setToken( QString accessToken ); void setHost( QString host ); void setPort( int port ); diff --git a/jobs/basejob.cpp b/jobs/basejob.cpp index e0dff287..20b45a15 100644 --- a/jobs/basejob.cpp +++ b/jobs/basejob.cpp @@ -92,7 +92,7 @@ void BaseJob::start() url.setPath( url.path() + "/" + apiPath() ); QUrlQuery query = this->query(); if( d->needsToken ) - query.addQueryItem("access_token", connection()->token()); + query.addQueryItem("access_token", connection()->accessToken()); url.setQuery(query); QNetworkRequest req = QNetworkRequest(url); req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); -- cgit v1.2.3 From bd5ef6e3d9d4105be2f4eea16c774c01c8a84379 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 12:11:19 +0900 Subject: Settings, SettingsGroup, AccountSettings Settings is a trivial wrapper around QSettings that makes value() and setValue() available from QML (Tensor has the same class atm). SettingsGroup is a cleaner way to reach for sections inside QSettings. It doesn't refer to another QSettings class, rather derives from it and overrides functions that deal with groups. AccountSettings, contrary to the two above, is by no means generic: it serves the specific purpose of storing settings of a single account in a uniform way. Rationale of it is that key literals like "keep_logged_in" cannot be enforced, while function names can; and the same goes for QVariants stored inside - the class enforces specific types while allowing further extension by clients. Note that functions in QSettings are not virtual, so all these classes are not polymorphic. --- CMakeLists.txt | 1 + settings.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ settings.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 settings.cpp create mode 100644 settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 80ffcfd7..e9127752 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(libqmatrixclient_SRCS user.cpp logmessage.cpp state.cpp + settings.cpp events/event.cpp events/roommessageevent.cpp events/roomnameevent.cpp diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 00000000..45af33e3 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,102 @@ +#include "settings.h" + +#include +#include + +using namespace QMatrixClient; + +Settings::~Settings() +{ } + +void Settings::setValue(const QString& key, const QVariant& value) +{ +// qDebug() << "Setting" << key << "to" << value; + QSettings::setValue(key, value); +} + +QVariant Settings::value(const QString& key, const QVariant& defaultValue) const +{ + return QSettings::value(key, defaultValue); +} + +SettingsGroup::~SettingsGroup() +{ } + +void SettingsGroup::setValue(const QString& key, const QVariant& value) +{ + Settings::setValue(groupPath + "/" + key, value); +} + +bool SettingsGroup::contains(const QString& key) const +{ + return Settings::contains(groupPath + "/" + key); +} + +QVariant SettingsGroup::value(const QString& key, const QVariant& defaultValue) const +{ + return Settings::value(groupPath + "/" + key, defaultValue); +} + +QString SettingsGroup::group() const +{ + return groupPath; +} + +QStringList SettingsGroup::childGroups() const +{ + const_cast(this)->beginGroup(groupPath); + QStringList l { Settings::childGroups() }; + const_cast(this)->endGroup(); + return l; +} + +void SettingsGroup::remove(const QString& key) +{ + QString fullKey { groupPath }; + if (!key.isEmpty()) + fullKey += "/" + key; + Settings::remove(fullKey); +} + +AccountSettings::~AccountSettings() +{ } + +bool AccountSettings::keepLoggedIn() const +{ + return value("keep_logged_in", false).toBool(); +} + +void AccountSettings::setKeepLoggedIn(bool newSetting) +{ + setValue("keep_logged_in", newSetting); +} + +QUrl AccountSettings::homeserver() const +{ + return QUrl::fromUserInput(value("homeserver").toString()); +} + +void AccountSettings::setHomeserver(const QUrl& url) +{ + setValue("homeserver", url.toString()); +} + +QString AccountSettings::userId() const +{ + return group().section('/', -1); +} + +QString AccountSettings::accessToken() const +{ + return value("access_token").toString(); +} + +void AccountSettings::setAccessToken(const QString& accessToken) +{ + setValue("access_token", accessToken); +} + +void AccountSettings::clearAccessToken() +{ + remove("access_token"); +} diff --git a/settings.h b/settings.h new file mode 100644 index 00000000..005b6cef --- /dev/null +++ b/settings.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * Copyright (C) 2016 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include +#include + +class QVariant; +class QUrl; + +namespace QMatrixClient +{ + class Settings: public QSettings + { + public: + using QSettings::QSettings; + virtual ~Settings(); + + Q_INVOKABLE void setValue(const QString &key, + const QVariant &value); + Q_INVOKABLE QVariant value(const QString &key, + const QVariant &defaultValue = {}) const; + }; + + class SettingsGroup: public Settings + { + public: + template + explicit SettingsGroup(const QString& path, ArgTs... qsettingsArgs) + : Settings(qsettingsArgs...) + , groupPath(path) + { } + virtual ~SettingsGroup(); + + Q_INVOKABLE bool contains(const QString& key) const; + Q_INVOKABLE QVariant value(const QString &key, + const QVariant &defaultValue = {}) const; + Q_INVOKABLE QString group() const; + Q_INVOKABLE QStringList childGroups() const; + Q_INVOKABLE void setValue(const QString &key, + const QVariant &value); + + Q_INVOKABLE void remove(const QString& key); + + private: + QString groupPath; + }; + + class AccountSettings: public SettingsGroup + { + Q_OBJECT + Q_PROPERTY(QString userId READ userId) + Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver) + Q_PROPERTY(bool keepLoggedIn READ keepLoggedIn WRITE setKeepLoggedIn) + Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) + public: + template + AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) + : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) + { } + virtual ~AccountSettings(); + + QString userId() const; + + QUrl homeserver() const; + void setHomeserver(const QUrl& url); + + bool keepLoggedIn() const; + void setKeepLoggedIn(bool newSetting); + + QString accessToken() const; + void setAccessToken(const QString& accessToken); + Q_INVOKABLE void clearAccessToken(); + }; +} -- cgit v1.2.3 From 334e4ba4ce410c8426abcd78a361b9a23b14eec7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 16:09:46 +0900 Subject: Cleanup after moving ConnectionPrivate::provideRoom() --- connection.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/connection.cpp b/connection.cpp index f7f09c04..f13026fc 100644 --- a/connection.cpp +++ b/connection.cpp @@ -49,8 +49,6 @@ class Connection::Private Private(Private&) = delete; ~Private() { delete data; } - Room* provideRoom( QString id ); - Connection* q; ConnectionData* data; QHash roomMap; @@ -279,7 +277,7 @@ Room* Connection::provideRoom(QString id) { if (id.isEmpty()) { - qDebug() << "ConnectionPrivate::provideRoom() with empty id, doing nothing"; + qDebug() << "Connection::provideRoom() with empty id, doing nothing"; return nullptr; } -- cgit v1.2.3 From 866cb97668fadff50dea8d16792c3b6736d531dc Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 16:12:11 +0900 Subject: Remember the current SyncJob and allow to explicitly abandon it --- connection.cpp | 45 ++++++++++++++++++++++++++++++++++----------- connection.h | 1 + 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/connection.cpp b/connection.cpp index f13026fc..98ff914a 100644 --- a/connection.cpp +++ b/connection.cpp @@ -45,6 +45,7 @@ class Connection::Private : q(nullptr) , data(new ConnectionData(serverUrl)) , isConnected(false) + , syncJob(nullptr) { } Private(Private&) = delete; ~Private() { delete data; } @@ -57,6 +58,10 @@ class Connection::Private QString username; QString password; QString userId; + + SyncJob* syncJob; + + SyncJob* startSyncJob(const QString& filter, int timeout); }; Connection::Connection(QUrl server, QObject* parent) @@ -140,6 +145,12 @@ void Connection::reconnect() loginJob->start(); } +void Connection::disconnectFromServer() +{ + d->syncJob->abandon(); + d->isConnected = false; +} + void Connection::logout() { auto job = new LogoutJob(d->data); @@ -149,27 +160,39 @@ void Connection::logout() SyncJob* Connection::sync(int timeout) { - QString filter = "{\"room\": { \"timeline\": { \"limit\": 100 } } }"; - SyncJob* syncJob = new SyncJob(d->data, d->data->lastEvent()); - syncJob->setFilter(filter); - syncJob->setTimeout(timeout); - connect( syncJob, &SyncJob::success, [=] () { - d->data->setLastEvent(syncJob->nextBatch()); - for( const auto& roomData: syncJob->roomData() ) + if (d->syncJob) + return d->syncJob; + + const QString filter = "{\"room\": { \"timeline\": { \"limit\": 100 } } }"; + auto job = d->startSyncJob(filter, timeout); + connect( job, &SyncJob::success, [=] () { + d->data->setLastEvent(job->nextBatch()); + for( const auto& roomData: job->roomData() ) { if ( Room* r = provideRoom(roomData.roomId) ) r->updateData(roomData); } + d->syncJob = nullptr; emit syncDone(); }); - connect( syncJob, &SyncJob::failure, [=] () { - if (syncJob->error() == BaseJob::ContentAccessError) - emit loginError(syncJob->errorString()); + connect( job, &SyncJob::failure, [=] () { + d->syncJob = nullptr; + if (job->error() == BaseJob::ContentAccessError) + emit loginError(job->errorString()); else - emit connectionError(syncJob->errorString()); + emit connectionError(job->errorString()); }); + return job; +} + +SyncJob* Connection::Private::startSyncJob(const QString& filter, int timeout) +{ + syncJob = new SyncJob(data, data->lastEvent()); + syncJob->setFilter(filter); + syncJob->setTimeout(timeout); syncJob->start(); return syncJob; + } void Connection::postMessage(Room* room, QString type, QString message) diff --git a/connection.h b/connection.h index bfb4d377..c221d7eb 100644 --- a/connection.h +++ b/connection.h @@ -49,6 +49,7 @@ namespace QMatrixClient Q_INVOKABLE virtual void connectToServer( QString user, QString password ); Q_INVOKABLE virtual void connectWithToken( QString userId, QString token ); Q_INVOKABLE virtual void reconnect(); + Q_INVOKABLE virtual void disconnectFromServer(); Q_INVOKABLE virtual void logout(); Q_INVOKABLE virtual SyncJob* sync(int timeout=-1); -- cgit v1.2.3 From e24ebc6dc5382ad05077575d267f66efac39e8dd Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 18:36:40 +0900 Subject: Fix building with VS2013 VS2013 doesn't like 'using' statements if a base class has private constructors (as in QSettings - Q_DISABLE_COPY makes a copy constructor private and deleted). Hence a workaround. --- settings.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/settings.h b/settings.h index 005b6cef..7cc7895b 100644 --- a/settings.h +++ b/settings.h @@ -29,7 +29,13 @@ namespace QMatrixClient class Settings: public QSettings { public: +#if defined(_MSC_VER) && _MSC_VER <= 1200 + // VS 2013 (and probably older) aren't friends with 'using' statements + // that involve private constructors + explicit Settings(QObject* parent = 0) : QSettings(parent) { } +#else using QSettings::QSettings; +#endif virtual ~Settings(); Q_INVOKABLE void setValue(const QString &key, @@ -71,7 +77,7 @@ namespace QMatrixClient Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) public: template - AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) + explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) { } virtual ~AccountSettings(); -- cgit v1.2.3 From ea865bade764c095aff175d10a4c2601a9ee34e0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 22:12:19 +0900 Subject: Corrected the faulty VS version --- settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.h b/settings.h index 7cc7895b..47b3ef85 100644 --- a/settings.h +++ b/settings.h @@ -29,7 +29,7 @@ namespace QMatrixClient class Settings: public QSettings { public: -#if defined(_MSC_VER) && _MSC_VER <= 1200 +#if defined(_MSC_VER) && _MSC_VER <= 1900 // VS 2013 (and probably older) aren't friends with 'using' statements // that involve private constructors explicit Settings(QObject* parent = 0) : QSettings(parent) { } -- cgit v1.2.3 From ae0b65440834a76f3ce6c266ee4b7d683b215842 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 6 Sep 2016 22:56:51 +0900 Subject: Relaxed VS version a bit. --- settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.h b/settings.h index 47b3ef85..10b2bb0a 100644 --- a/settings.h +++ b/settings.h @@ -29,7 +29,7 @@ namespace QMatrixClient class Settings: public QSettings { public: -#if defined(_MSC_VER) && _MSC_VER <= 1900 +#if defined(_MSC_VER) && _MSC_VER < 1900 // VS 2013 (and probably older) aren't friends with 'using' statements // that involve private constructors explicit Settings(QObject* parent = 0) : QSettings(parent) { } -- cgit v1.2.3 From 573e0e478c4a904ba69e6b523dead41eb28121c1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 7 Sep 2016 08:14:29 +0900 Subject: Temporarily revert "Event::fromJson(): made the code more compact" as it breaks highlighting This reverts commit 0fe83d59d76cd8f9c8f92d40cc58d9f5b082a84a. --- events/event.cpp | 55 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/events/event.cpp b/events/event.cpp index 11ed1cc9..0c7700ee 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -82,33 +82,40 @@ QString Event::originalJson() const return d->originalJson; } -template -Event* make(const QJsonObject& obj) -{ - return T::fromJson(obj); -} - Event* Event::fromJson(const QJsonObject& obj) { - struct Factory { - QString type; - Event* (*make)(const QJsonObject& obj); - }; - const Factory evTypes[] { - { "m.room.message", make }, - { "m.room.name", make }, - { "m.room.aliases", make }, - { "m.room.canonical_alias", make }, - { "m.room.member", make }, - { "m.room.topic", make }, - { "m.room.typing", make }, - { "m.room.receipt", make }, - // Insert new types before this line - }; - for (auto e: evTypes) + //qDebug() << obj.value("type").toString(); + if( obj.value("type").toString() == "m.room.message" ) + { + return RoomMessageEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.room.name" ) + { + return RoomNameEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.room.aliases" ) + { + return RoomAliasesEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.room.canonical_alias" ) + { + return RoomCanonicalAliasEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.room.member" ) + { + return RoomMemberEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.room.topic" ) + { + return RoomTopicEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.typing" ) + { + return TypingEvent::fromJson(obj); + } + if( obj.value("type").toString() == "m.receipt" ) { - if (obj["type"].toString() == e.type) - return e.make(obj); + return ReceiptEvent::fromJson(obj); } return UnknownEvent::fromJson(obj); } -- cgit v1.2.3