aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2018-11-22 16:51:49 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2018-11-22 16:53:59 +0900
commit5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0 (patch)
tree203d3533550e6c665220578f7922317833efb22c
parent0c3a45356a803baa0eb5e553262a85cac897ac4f (diff)
downloadlibquotient-5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0.tar.gz
libquotient-5fb74ca3d253b658fe77aaeb6a106cf6c0a9e7f0.zip
Save state cache per-room
Closes #257.
-rw-r--r--lib/connection.cpp67
-rw-r--r--lib/connection.h7
-rw-r--r--lib/room.cpp3
-rw-r--r--lib/syncdata.cpp37
-rw-r--r--lib/syncdata.h5
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_;