diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-11-22 16:51:49 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-11-22 16:53:59 +0900 |
commit | 5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0 (patch) | |
tree | 203d3533550e6c665220578f7922317833efb22c | |
parent | 0c3a45356a803baa0eb5e553262a85cac897ac4f (diff) | |
download | libquotient-5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0.tar.gz libquotient-5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0.zip |
Save state cache per-room
Closes #257.
-rw-r--r-- | lib/connection.cpp | 67 | ||||
-rw-r--r-- | lib/connection.h | 7 | ||||
-rw-r--r-- | lib/room.cpp | 3 | ||||
-rw-r--r-- | lib/syncdata.cpp | 37 | ||||
-rw-r--r-- | lib/syncdata.h | 5 |
5 files changed, 70 insertions, 49 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp index 8a451a79..099d6a4e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1058,41 +1058,55 @@ void Connection::setHomeserver(const QUrl& url) emit homeserverChanged(homeserver()); } -void Connection::saveState(const QUrl &toFile) const +void Connection::saveRoomState(Room* r) const { + Q_ASSERT(r); if (!d->cacheState) return; - QElapsedTimer et; et.start(); + QFile outRoomFile { stateCachePath() % SyncData::fileNameForRoom(r->id()) }; + if (outRoomFile.open(QFile::WriteOnly)) + { + QJsonDocument json { r->toJson() }; + auto data = d->cacheToBinary ? json.toBinaryData() + : json.toJson(QJsonDocument::Compact); + outRoomFile.write(data.data(), data.size()); + } else { + qCWarning(MAIN) << "Error opening" << outRoomFile.fileName() + << ":" << outRoomFile.errorString(); + } +} - QFileInfo stateFile { - toFile.isEmpty() ? stateCachePath() : toFile.toLocalFile() - }; - if (!stateFile.dir().exists()) - stateFile.dir().mkpath("."); +void Connection::saveState() const +{ + if (!d->cacheState) + return; - QFile outfile { stateFile.absoluteFilePath() }; - if (!outfile.open(QFile::WriteOnly)) + QElapsedTimer et; et.start(); + + QFile outFile { stateCachePath() % "state.json" }; + if (!outFile.open(QFile::WriteOnly)) { - qCWarning(MAIN) << "Error opening" << stateFile.absoluteFilePath() - << ":" << outfile.errorString(); + qCWarning(MAIN) << "Error opening" << outFile.fileName() + << ":" << outFile.errorString(); qCWarning(MAIN) << "Caching the rooms state disabled"; d->cacheState = false; return; } - QJsonObject rootObj; + QJsonObject rootObj { + { QStringLiteral("cache_version"), QJsonObject { + { QStringLiteral("major"), SyncData::cacheVersion().first }, + { QStringLiteral("minor"), SyncData::cacheVersion().second } + }}}; { QJsonObject rooms; QJsonObject inviteRooms; for (const auto* i : roomMap()) // Pass on rooms in Leave state { - // TODO: instead of adding the room JSON add a file name and save - // the JSON to that file. - if (i->joinState() == JoinState::Invite) - inviteRooms.insert(i->id(), i->toJson()); - else - rooms.insert(i->id(), i->toJson()); + auto& targetArray = i->joinState() == JoinState::Invite + ? inviteRooms : rooms; + targetArray.insert(i->id(), QJsonObject()); QElapsedTimer et1; et1.start(); QCoreApplication::processEvents(); if (et1.elapsed() > 1) @@ -1120,29 +1134,23 @@ void Connection::saveState(const QUrl &toFile) const QJsonObject {{ QStringLiteral("events"), accountDataEvents }}); } - QJsonObject versionObj; - versionObj.insert("major", SyncData::cacheVersion().first); - versionObj.insert("minor", SyncData::cacheVersion().second); - rootObj.insert("cache_version", versionObj); - QJsonDocument json { rootObj }; auto data = d->cacheToBinary ? json.toBinaryData() : json.toJson(QJsonDocument::Compact); qCDebug(PROFILER) << "Cache for" << userId() << "generated in" << et; - outfile.write(data.data(), data.size()); - qCDebug(MAIN) << "State cache saved to" << outfile.fileName(); + outFile.write(data.data(), data.size()); + qCDebug(MAIN) << "State cache saved to" << outFile.fileName(); } -void Connection::loadState(const QUrl &fromFile) +void Connection::loadState() { if (!d->cacheState) return; QElapsedTimer et; et.start(); - SyncData sync { - fromFile.isEmpty() ? stateCachePath() : fromFile.toLocalFile() }; + SyncData sync { stateCachePath() % "state.json" }; if (sync.nextBatch().isEmpty()) // No token means no cache by definition return; @@ -1162,8 +1170,7 @@ QString Connection::stateCachePath() const { auto safeUserId = userId(); safeUserId.replace(':', '_'); - return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) - % '/' % safeUserId % "_state.json"; + return cacheLocation(safeUserId); } bool Connection::cacheState() const diff --git a/lib/connection.h b/lib/connection.h index 20dade76..20a1f47e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -280,7 +280,7 @@ namespace QMatrixClient * to be QML-friendly. Empty parameter means using a path * defined by stateCachePath(). */ - Q_INVOKABLE void loadState(const QUrl &fromFile = {}); + Q_INVOKABLE void loadState(); /** * This method saves the current state of rooms (but not messages * in them) to a local cache file, so that it could be loaded by @@ -290,7 +290,10 @@ namespace QMatrixClient * QML-friendly. Empty parameter means using a path defined by * stateCachePath(). */ - Q_INVOKABLE void saveState(const QUrl &toFile = {}) const; + Q_INVOKABLE void saveState() const; + + /// This method saves the current state of a single room. + void saveRoomState(Room* r) const; /** * The default path to store the cached room state, defined as diff --git a/lib/room.cpp b/lib/room.cpp index 55923ed8..2d958dca 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1170,7 +1170,10 @@ void Room::updateData(SyncRoomData&& data) emit notificationCountChanged(this); } if (roomChanges != Change::NoChange) + { emit changed(roomChanges); + connection()->saveRoomState(this); + } } QString Room::Private::sendEvent(RoomEventPtr&& event) diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index f0d55fd6..d141a7cc 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -21,6 +21,7 @@ #include "events/eventloader.h" #include <QtCore/QFile> +#include <QtCore/QFileInfo> using namespace QMatrixClient; @@ -69,7 +70,17 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, SyncData::SyncData(const QString& cacheFileName) { - parseJson(loadJson(cacheFileName)); + QFileInfo cacheFileInfo { cacheFileName }; + auto json = loadJson(cacheFileName); + auto requiredVersion = std::get<0>(cacheVersion()); + auto actualVersion = json.value("cache_version").toObject() + .value("major").toInt(); + if (actualVersion == requiredVersion) + parseJson(json, cacheFileInfo.absolutePath() + '/'); + else + qCWarning(MAIN) + << "Major version of the cache file is" << actualVersion << "but" + << requiredVersion << "is required; discarding the cache"; } SyncDataList&& SyncData::takeRoomData() @@ -77,6 +88,12 @@ SyncDataList&& SyncData::takeRoomData() return move(roomData); } +QString SyncData::fileNameForRoom(QString roomId) +{ + roomId.replace(':', '_'); + return roomId + ".json"; +} + Events&& SyncData::takePresenceData() { return std::move(presenceData); @@ -115,22 +132,11 @@ QJsonObject SyncData::loadJson(const QString& fileName) { qCWarning(MAIN) << "State cache in" << fileName << "is broken or empty, discarding"; - return {}; - } - auto requiredVersion = std::get<0>(cacheVersion()); - auto actualVersion = json.value("cache_version").toObject() - .value("major").toInt(); - if (actualVersion < requiredVersion) - { - qCWarning(MAIN) - << "Major version of the cache file is" << actualVersion << "but" - << requiredVersion << "is required; discarding the cache"; - return {}; } return json; } -void SyncData::parseJson(const QJsonObject& json) +void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) { QElapsedTimer et; et.start(); @@ -150,8 +156,9 @@ void SyncData::parseJson(const QJsonObject& json) roomData.reserve(static_cast<size_t>(rs.size())); for(auto roomIt = rs.begin(); roomIt != rs.end(); ++roomIt) { - auto roomJson = roomIt->isString() ? loadJson(roomIt->toString()) - : roomIt->toObject(); + auto roomJson = roomIt->isString() + ? loadJson(baseDir + fileNameForRoom(roomIt.key())) + : roomIt->toObject(); if (roomJson.isEmpty()) { unresolvedRoomIds.push_back(roomIt.key()); diff --git a/lib/syncdata.h b/lib/syncdata.h index d8007db9..aa8948bc 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -59,7 +59,7 @@ namespace QMatrixClient { * \return the list of rooms with missing cache files; always * empty when parsing response from /sync */ - void parseJson(const QJsonObject& json); + void parseJson(const QJsonObject& json, const QString& baseDir = {}); Events&& takePresenceData(); Events&& takeAccountData(); @@ -70,7 +70,8 @@ namespace QMatrixClient { QStringList unresolvedRooms() const { return unresolvedRoomIds; } - static std::pair<int, int> cacheVersion() { return { 8, 0 }; } + static std::pair<int, int> cacheVersion() { return { 9, 0 }; } + static QString fileNameForRoom(QString roomId); private: QString nextBatch_; |