aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--connection.cpp109
-rw-r--r--connection.h40
2 files changed, 120 insertions, 29 deletions
diff --git a/connection.cpp b/connection.cpp
index 27f0a86f..785e0e43 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -35,6 +35,9 @@
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QStringBuilder>
+#include <QtCore/QElapsedTimer>
using namespace QMatrixClient;
@@ -60,6 +63,8 @@ class Connection::Private
QString userId;
SyncJob* syncJob;
+
+ bool cacheState = true;
};
Connection::Connection(const QUrl& server, QObject* parent)
@@ -329,47 +334,101 @@ QByteArray Connection::generateTxnId()
return d->data->generateTxnId();
}
-void Connection::saveState(const QUrl &toFile) {
- QJsonObject rooms;
+void Connection::saveState(const QUrl &toFile) const
+{
+ if (!d->cacheState)
+ return;
+
+ QElapsedTimer et; et.start();
- for (auto i : this->roomMap()) {
- rooms[i->id()] = i->toJson();
+ QFileInfo stateFile {
+ toFile.isEmpty() ? stateCachePath() : toFile.toLocalFile()
+ };
+ if (!stateFile.dir().exists())
+ stateFile.dir().mkpath(".");
+
+ QFile outfile { stateFile.absoluteFilePath() };
+ if (!outfile.open(QFile::WriteOnly))
+ {
+ qCWarning(MAIN) << "Error opening" << stateFile.absoluteFilePath()
+ << ":" << outfile.errorString();
+ qCWarning(MAIN) << "Caching the rooms state disabled";
+ d->cacheState = false;
+ return;
}
QJsonObject roomObj;
- roomObj.insert("leave", QJsonObject());
- roomObj.insert("join", rooms);
- roomObj.insert("invite", QJsonObject());
+ {
+ QJsonObject rooms;
+ QJsonObject inviteRooms;
+ for (auto i : roomMap()) // Pass on rooms in Leave state
+ {
+ if (i->joinState() == JoinState::Invite)
+ inviteRooms.insert(i->id(), i->toJson());
+ else
+ rooms.insert(i->id(), i->toJson());
+ }
+
+ if (!rooms.isEmpty())
+ roomObj.insert("join", rooms);
+ if (!inviteRooms.isEmpty())
+ roomObj.insert("invite", inviteRooms);
+ }
QJsonObject rootObj;
rootObj.insert("next_batch", d->data->lastEvent());
- rootObj.insert("presence", QJsonObject());
rootObj.insert("rooms", roomObj);
- QJsonDocument doc { rootObj };
- QByteArray data = doc.toJson();
+ QByteArray data = QJsonDocument(rootObj).toJson(QJsonDocument::Compact);
- QFileInfo stateFile { toFile.toLocalFile() };
- QFile outfile { stateFile.absoluteFilePath() };
- if (!stateFile.dir().exists()) stateFile.dir().mkpath(".");
+ qCDebug(MAIN) << "Writing state to file" << outfile.fileName();
+ outfile.write(data.data(), data.size());
+ qCDebug(PROFILER) << "*** Cached state for" << userId()
+ << "saved in" << et.elapsed() << "ms";
+}
- if (outfile.open(QFile::WriteOnly)) {
- qCDebug(MAIN) << "Writing state to file=" << outfile.fileName();
- outfile.write(data.data(), data.size());
+void Connection::loadState(const QUrl &fromFile)
+{
+ if (!d->cacheState)
+ return;
- } else {
- qCWarning(MAIN) << outfile.errorString();
+ QElapsedTimer et; et.start();
+ QFile file {
+ fromFile.isEmpty() ? stateCachePath() : fromFile.toLocalFile()
+ };
+ if (!file.exists())
+ {
+ qCDebug(MAIN) << "No state cache file found";
+ return;
}
-}
-
-void Connection::loadState(const QUrl &fromFile) {
- QFile file { fromFile.toLocalFile() };
- if (!file.exists()) return;
file.open(QFile::ReadOnly);
QByteArray data = file.readAll();
- QJsonDocument doc { QJsonDocument::fromJson(data) };
SyncData sync;
- sync.parseJson(doc);
+ sync.parseJson(QJsonDocument::fromJson(data));
onSyncSuccess(std::move(sync));
+ qCDebug(PROFILER) << "*** Cached state for" << userId()
+ << "loaded in" << et.elapsed() << "ms";
+}
+
+QString Connection::stateCachePath() const
+{
+ auto safeUserId = userId();
+ safeUserId.replace(':', '_');
+ return QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ % '/' % safeUserId % "_state.json";
+}
+
+bool Connection::cacheState() const
+{
+ return d->cacheState;
+}
+
+void Connection::setCacheState(bool newValue)
+{
+ if (d->cacheState != newValue)
+ {
+ d->cacheState = newValue;
+ emit cacheStateChanged();
+ }
}
diff --git a/connection.h b/connection.h
index ad161d7c..bf50d7d3 100644
--- a/connection.h
+++ b/connection.h
@@ -39,6 +39,11 @@ namespace QMatrixClient
class Connection: public QObject {
Q_OBJECT
+
+ /** Whether or not the rooms state should be cached locally
+ * \sa loadState(), saveState()
+ */
+ Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged)
public:
explicit Connection(const QUrl& server, QObject* parent = nullptr);
Connection();
@@ -86,10 +91,35 @@ namespace QMatrixClient
/**
* Call this before first sync to load from previously saved file.
- * Uses QUrl to be QML-friendly.
- */
- Q_INVOKABLE void loadState(const QUrl &fromFile);
- Q_INVOKABLE void saveState(const QUrl &toFile);
+ *
+ * \param fromFile A local path to read the state from. Uses QUrl
+ * to be QML-friendly. Empty parameter means using a path
+ * defined by stateCachePath().
+ */
+ Q_INVOKABLE void loadState(const QUrl &fromFile = {});
+ /**
+ * 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
+ * loadState() on a next run of the client.
+ *
+ * \param toFile A local path to save the state to. Uses QUrl to be
+ * QML-friendly. Empty parameter means using a path defined by
+ * stateCachePath().
+ */
+ Q_INVOKABLE void saveState(const QUrl &toFile = {}) const;
+
+ /**
+ * The default path to store the cached room state, defined as
+ * follows:
+ * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) + _safeUserId + "_state.json"
+ * where `_safeUserId` is userId() with `:` (colon) replaced with
+ * `_` (underscore)
+ * /see loadState(), saveState()
+ */
+ Q_INVOKABLE QString stateCachePath() const;
+
+ bool cacheState() const;
+ void setCacheState(bool newValue);
template <typename JobT, typename... JobArgTs>
JobT* callApi(JobArgTs... jobArgs) const
@@ -120,6 +150,8 @@ namespace QMatrixClient
void syncError(QString error);
//void jobError(BaseJob* job);
+ void cacheStateChanged();
+
protected:
/**
* @brief Access the underlying ConnectionData class