1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Handle sync and api state before and after login/logout

This commit is contained in:
Laurent Cozic 2017-01-11 11:14:57 +01:00
parent 5c7581ae1d
commit a1bffbb5e5
17 changed files with 185 additions and 11 deletions

View File

@ -22,7 +22,8 @@ SOURCES += \
models/basemodel.cpp \ models/basemodel.cpp \
models/setting.cpp \ models/setting.cpp \
paths.cpp \ paths.cpp \
window.cpp window.cpp \
filters.cpp
RESOURCES += qml.qrc \ RESOURCES += qml.qrc \
database.qrc database.qrc
@ -59,7 +60,8 @@ HEADERS += \
models/setting.h \ models/setting.h \
paths.h \ paths.h \
constants.h \ constants.h \
window.h window.h \
filters.h
DISTFILES += \ DISTFILES += \
AndroidManifest.xml AndroidManifest.xml

View File

@ -6,6 +6,12 @@ LoginPageForm {
id: root id: root
function onShown() {
root.apiBaseUrl = settings.valueString("api.baseUrl");
root.email = settings.valueString("user.email");
root.password = "";
}
Connections { Connections {
target: root target: root
onLoginButtonClicked: { onLoginButtonClicked: {

View File

@ -9,6 +9,10 @@ Item {
property alias currentFolderIndex: folderList.currentIndex property alias currentFolderIndex: folderList.currentIndex
property alias currentNoteIndex: noteList.currentIndex property alias currentNoteIndex: noteList.currentIndex
function onShown() {
}
RowLayout { RowLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
@ -94,4 +98,12 @@ Item {
onClicked: appRoot.syncButtonClicked() onClicked: appRoot.syncButtonClicked()
} }
Button {
id: logoutButton
text: "Logout"
anchors.right: syncButton.left
anchors.top: parent.top
onClicked: dispatcher.logoutClicked()
}
} }

View File

@ -48,6 +48,8 @@ Item {
print("Switching to page: " + pageName); print("Switching to page: " + pageName);
var page = pageByName(pageName); var page = pageByName(pageName);
page.visible = true; page.visible = true;
page.onShown();
} }
MainPage { MainPage {

View File

@ -10,6 +10,7 @@
#include "dispatcher.h" #include "dispatcher.h"
#include "paths.h" #include "paths.h"
#include "constants.h" #include "constants.h"
#include "filters.h"
using namespace jop; using namespace jop;
@ -42,12 +43,15 @@ Application::Application(int &argc, char **argv) :
settings.setValue("clientId", uuid::createUuid()); settings.setValue("clientId", uuid::createUuid());
} }
Settings* qmlSettings = new Settings();
view_.setResizeMode(QQuickView::SizeRootObjectToView); view_.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view_.rootContext(); QQmlContext *ctxt = view_.rootContext();
ctxt->setContextProperty("folderListModel", &folderModel_); ctxt->setContextProperty("folderListModel", &folderModel_);
ctxt->setContextProperty("noteListModel", &noteModel_); ctxt->setContextProperty("noteListModel", &noteModel_);
ctxt->setContextProperty("noteModel", &selectedQmlNote_); ctxt->setContextProperty("noteModel", &selectedQmlNote_);
ctxt->setContextProperty("dispatcher", &dispatcher()); ctxt->setContextProperty("dispatcher", &dispatcher());
ctxt->setContextProperty("settings", qmlSettings);
view_.setSource(QUrl("qrc:/app.qml")); view_.setSource(QUrl("qrc:/app.qml"));
@ -62,6 +66,7 @@ Application::Application(int &argc, char **argv) :
connect(&dispatcher(), SIGNAL(folderUpdated(QString)), this, SLOT(dispatcher_folderUpdated(QString))); connect(&dispatcher(), SIGNAL(folderUpdated(QString)), this, SLOT(dispatcher_folderUpdated(QString)));
connect(&dispatcher(), SIGNAL(folderDeleted(QString)), this, SLOT(dispatcher_folderDeleted(QString))); connect(&dispatcher(), SIGNAL(folderDeleted(QString)), this, SLOT(dispatcher_folderDeleted(QString)));
connect(&dispatcher(), SIGNAL(loginClicked(QString,QString,QString)), this, SLOT(dispatcher_loginClicked(QString,QString,QString))); connect(&dispatcher(), SIGNAL(loginClicked(QString,QString,QString)), this, SLOT(dispatcher_loginClicked(QString,QString,QString)));
connect(&dispatcher(), SIGNAL(logoutClicked()), this, SLOT(dispatcher_logoutClicked()));
view_.show(); view_.show();
@ -75,6 +80,11 @@ Application::Application(int &argc, char **argv) :
if (!settings.contains("user.email") || !settings.contains("session.id") || !settings.contains("api.baseUrl")) { if (!settings.contains("user.email") || !settings.contains("session.id") || !settings.contains("api.baseUrl")) {
view_.showPage("login"); view_.showPage("login");
} else { } else {
afterSessionInitialization();
//QString apiBaseUrl = settings.value("api.baseUrl").toString();
//api_.setBaseUrl(apiBaseUrl);
//synchronizer_.api().setBaseUrl(apiBaseUrl);
//synchronizer_.setSessionId(settings.value("session.id").toString());
view_.showPage("main"); view_.showPage("main");
} }
@ -107,8 +117,9 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta
if (tag == "getSession") { if (tag == "getSession") {
if (response.contains("error")) { if (response.contains("error")) {
qCritical() << "Could not get session:" << response.value("error").toString(); qWarning() << "Could not get session:" << response.value("error").toString();
dispatcher().emitLoginFailed(); dispatcher().emitLoginFailed();
view_.showPage("login");
} else { } else {
QString sessionId = response.value("id").toString(); QString sessionId = response.value("id").toString();
qDebug() << "Got session" << sessionId; qDebug() << "Got session" << sessionId;
@ -116,8 +127,8 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta
settings.setValue("session.id", sessionId); settings.setValue("session.id", sessionId);
afterSessionInitialization(); afterSessionInitialization();
dispatcher().emitLoginSuccess(); dispatcher().emitLoginSuccess();
view_.showPage("main");
} }
view_.showPage("main");
return; return;
} }
} }
@ -142,14 +153,24 @@ void Application::dispatcher_loginClicked(const QString &apiBaseUrl, const QStri
dispatcher().emitLoginStarted(); dispatcher().emitLoginStarted();
Settings settings; Settings settings;
settings.setValue("user.email", email); settings.setValue("user.email", filters::email(email));
settings.setValue("api.baseUrl", apiBaseUrl); settings.setValue("api.baseUrl", filters::apiBaseUrl(apiBaseUrl));
api_.setBaseUrl(apiBaseUrl); api_.setBaseUrl(apiBaseUrl);
login(email, password); login(email, password);
} }
void Application::dispatcher_logoutClicked() {
api_.abortAll();
synchronizer_.abort();
synchronizer_.freeze();
Settings settings;
settings.setValue("session.id", "");
view_.showPage("login");
}
void Application::synchronizerTimer_timeout() { void Application::synchronizerTimer_timeout() {
//synchronizerTimer_.start(1000 * 10); //synchronizerTimer_.start(1000 * 10);
synchronizer_.start(); synchronizer_.start();
@ -178,8 +199,11 @@ void Application::afterSessionInitialization() {
Settings settings; Settings settings;
QString sessionId = settings.value("session.id").toString(); QString sessionId = settings.value("session.id").toString();
qDebug() << "Session:" << sessionId; qDebug() << "Session:" << sessionId;
api_.setBaseUrl(settings.value("api.baseUrl").toString());
api_.setSessionId(sessionId); api_.setSessionId(sessionId);
synchronizer_.api().setBaseUrl(settings.value("api.baseUrl").toString());
synchronizer_.setSessionId(sessionId); synchronizer_.setSessionId(sessionId);
synchronizer_.unfreeze();
synchronizer_.start(); synchronizer_.start();
} }

View File

@ -53,6 +53,7 @@ public slots:
void dispatcher_folderUpdated(const QString& folderId); void dispatcher_folderUpdated(const QString& folderId);
void dispatcher_folderDeleted(const QString& folderId); void dispatcher_folderDeleted(const QString& folderId);
void dispatcher_loginClicked(const QString &domain, const QString &email, const QString &password); void dispatcher_loginClicked(const QString &domain, const QString &email, const QString &password);
void dispatcher_logoutClicked();
void synchronizerTimer_timeout(); void synchronizerTimer_timeout();

View File

@ -20,6 +20,10 @@ void Dispatcher::emitLoginClicked(const QString &apiBaseUrl, const QString &emai
emit loginClicked(apiBaseUrl, email, password); emit loginClicked(apiBaseUrl, email, password);
} }
void Dispatcher::emitLogoutClicked() {
emit logoutClicked();
}
void Dispatcher::emitLoginStarted() { void Dispatcher::emitLoginStarted() {
emit loginStarted(); emit loginStarted();
} }

View File

@ -19,6 +19,7 @@ public slots:
void emitFolderUpdated(const QString& folderId); void emitFolderUpdated(const QString& folderId);
void emitFolderDeleted(const QString& folderId); void emitFolderDeleted(const QString& folderId);
void emitLoginClicked(const QString& domain, const QString& email, const QString &password); void emitLoginClicked(const QString& domain, const QString& email, const QString &password);
void emitLogoutClicked();
void emitLoginStarted(); void emitLoginStarted();
void emitLoginFailed(); void emitLoginFailed();
void emitLoginSuccess(); void emitLoginSuccess();
@ -29,6 +30,7 @@ signals:
void folderUpdated(const QString& folderId); void folderUpdated(const QString& folderId);
void folderDeleted(const QString& folderId); void folderDeleted(const QString& folderId);
void loginClicked(const QString& domain, const QString& email, const QString& password); void loginClicked(const QString& domain, const QString& email, const QString& password);
void logoutClicked();
void loginStarted(); void loginStarted();
void loginFailed(); void loginFailed();
void loginSuccess(); void loginSuccess();

View File

@ -0,0 +1,19 @@
#include "filters.h"
using namespace jop;
QString filters::apiBaseUrl(const QString &baseUrl) {
QString output(baseUrl.trimmed());
if (!output.startsWith("http://") && !output.startsWith("https://")) {
output = "http://" + output;
}
while (output.endsWith("/")) {
output = output.left(output.length() - 1);
}
return output;
}
QString filters::email(const QString &email) {
QString output(email.trimmed());
return output;
}

View File

@ -0,0 +1,15 @@
#ifndef FILTERS_H
#define FILTERS_H
#include <stable.h>
namespace jop {
namespace filters {
QString apiBaseUrl(const QString& apiBaseUrl);
QString email(const QString& email);
}
}
#endif // FILTERS_H

View File

@ -187,7 +187,6 @@ bool BaseModel::trackChanges() const {
} }
bool BaseModel::isNew() const { bool BaseModel::isNew() const {
qDebug() << "EEEEEEEEEEEEEEEEEEEEEE" << isNew_ << primaryKey() << valueIsSet(primaryKey()) << values_["id"].toString();
if (isNew_ == 0) return false; if (isNew_ == 0) return false;
if (isNew_ == 1) return true; if (isNew_ == 1) return true;
return !valueIsSet(primaryKey()); return !valueIsSet(primaryKey());
@ -415,7 +414,6 @@ int BaseModel::Value::toInt() const {
QString BaseModel::Value::toString() const { QString BaseModel::Value::toString() const {
if (type_ == QMetaType::QString) return stringValue_; if (type_ == QMetaType::QString) return stringValue_;
if (type_ == QMetaType::Int) return QString::number(intValue_); if (type_ == QMetaType::Int) return QString::number(intValue_);
qCritical("Unreachable");
return QString(""); return QString("");
} }

View File

@ -26,3 +26,11 @@ void Settings::initialize() {
const QSettings::Format SqliteFormat = QSettings::registerFormat("sqlite", &readSqlite, &writeSqlite); const QSettings::Format SqliteFormat = QSettings::registerFormat("sqlite", &readSqlite, &writeSqlite);
QSettings::setDefaultFormat(SqliteFormat); QSettings::setDefaultFormat(SqliteFormat);
} }
QString Settings::valueString(const QString &name, const QString &defaultValue) {
return value(name, defaultValue).toString();
}
int Settings::valueInt(const QString &name, int defaultValue) {
return value(name, defaultValue).toInt();
}

View File

@ -16,6 +16,11 @@ public:
static void initialize(); static void initialize();
public slots:
QString valueString(const QString& name, const QString& defaultValue = "");
int valueInt(const QString& name, int defaultValue = 0);
}; };
} }

View File

@ -12,6 +12,11 @@ Synchronizer::Synchronizer(Database &database) : db_(database) {
} }
void Synchronizer::start() { void Synchronizer::start() {
if (state_ == Frozen) {
qWarning() << "Cannot start synchronizer while frozen";
return;
}
if (state_ != Idle) { if (state_ != Idle) {
qWarning() << "Cannot start synchronizer because synchronization already in progress. State: " << state_; qWarning() << "Cannot start synchronizer because synchronization already in progress. State: " << state_;
return; return;
@ -26,6 +31,22 @@ void Synchronizer::setSessionId(const QString &v) {
api_.setSessionId(v); api_.setSessionId(v);
} }
void Synchronizer::abort() {
switchState(Aborting);
}
void Synchronizer::freeze() {
switchState(Frozen);
}
void Synchronizer::unfreeze() {
switchState(Idle);
}
WebApi &Synchronizer::api() {
return api_;
}
QUrlQuery Synchronizer::valuesToUrlQuery(const QHash<QString, Change::Value>& values) const { QUrlQuery Synchronizer::valuesToUrlQuery(const QHash<QString, Change::Value>& values) const {
QUrlQuery query; QUrlQuery query;
for (QHash<QString, Change::Value>::const_iterator it = values.begin(); it != values.end(); ++it) { for (QHash<QString, Change::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
@ -57,6 +78,15 @@ void Synchronizer::checkNextState() {
break; break;
case Aborting:
switchState(Idle);
break;
case Frozen:
break;
default: default:
qCritical() << "Synchronizer has invalid state" << state_; qCritical() << "Synchronizer has invalid state" << state_;
@ -142,11 +172,32 @@ void Synchronizer::switchState(Synchronizer::SynchronizationState state) {
query.addQueryItem("last_id", lastRevId); query.addQueryItem("last_id", lastRevId);
api_.get("synchronizer", query, QUrlQuery(), "download:getSynchronizer"); api_.get("synchronizer", query, QUrlQuery(), "download:getSynchronizer");
} else if (state == Aborting) {
// =============================================================================================
// ABORTING STATE
// =============================================================================================
uploadsRemaining_ = 0;
api_.abortAll();
checkNextState();
} else if (state == Frozen) {
// =============================================================================================
// FROZEN STATE
// =============================================================================================
} }
} }
void Synchronizer::api_requestDone(const QJsonObject& response, const QString& tag) { void Synchronizer::api_requestDone(const QJsonObject& response, const QString& tag) {
if (state_ == Frozen) {
qWarning() << "Receiving response while synchronizer is frozen";
return;
}
QStringList parts = tag.split(':'); QStringList parts = tag.split(':');
QString category = parts[0]; QString category = parts[0];
QString action = parts[1]; QString action = parts[1];
@ -164,7 +215,7 @@ void Synchronizer::api_requestDone(const QJsonObject& response, const QString& t
// HANDLE UPLOAD RESPONSE // HANDLE UPLOAD RESPONSE
// ============================================================================================= // =============================================================================================
if (category == "upload") { if (state_ == UploadingChanges) {
uploadsRemaining_--; uploadsRemaining_--;
qDebug() << "Synced folder" << arg1; qDebug() << "Synced folder" << arg1;
@ -191,7 +242,7 @@ void Synchronizer::api_requestDone(const QJsonObject& response, const QString& t
// HANDLE DOWNLOAD RESPONSE // HANDLE DOWNLOAD RESPONSE
// ============================================================================================= // =============================================================================================
} else if (category == "download") { } else if (state_ == DownloadingChanges) {
if (action == "getSynchronizer") { if (action == "getSynchronizer") {
QJsonArray items = response["items"].toArray(); QJsonArray items = response["items"].toArray();
QString maxRevId = ""; QString maxRevId = "";

View File

@ -14,11 +14,15 @@ class Synchronizer : public QObject {
public: public:
enum SynchronizationState { Idle, UploadingChanges, DownloadingChanges }; enum SynchronizationState { Idle, UploadingChanges, DownloadingChanges, Aborting, Frozen };
Synchronizer(Database& database); Synchronizer(Database& database);
void start(); void start();
void setSessionId(const QString& v); void setSessionId(const QString& v);
void abort();
void freeze();
void unfreeze();
WebApi& api();
private: private:

View File

@ -30,6 +30,7 @@ void WebApi::execRequest(HttpMethod method, const QString &path, const QUrlQuery
r.data = data; r.data = data;
r.tag = tag; r.tag = tag;
r.buffer = NULL; r.buffer = NULL;
r.reply = NULL;
queuedRequests_ << r; queuedRequests_ << r;
processQueue(); processQueue();
@ -45,6 +46,25 @@ void WebApi::setSessionId(const QString &v) {
sessionId_ = v; sessionId_ = v;
} }
void WebApi::abortAll() {
for (int i = 0; i < inProgressRequests_.size(); i++) {
QueuedRequest r = inProgressRequests_[i];
if (r.reply) {
r.reply->abort();
// TODO: Delete r.reply?
}
}
for (int i = 0; i < queuedRequests_.size(); i++) {
QueuedRequest r = queuedRequests_[i];
if (r.reply) {
r.reply->abort();
// TODO: Delete r.reply?
}
}
queuedRequests_.size();
}
void WebApi::processQueue() { void WebApi::processQueue() {
if (!queuedRequests_.size() || inProgressRequests_.size() >= 50) return; if (!queuedRequests_.size() || inProgressRequests_.size() >= 50) return;
QueuedRequest r = queuedRequests_.takeFirst(); QueuedRequest r = queuedRequests_.takeFirst();

View File

@ -33,6 +33,7 @@ public:
void del(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = ""); void del(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void patch(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = ""); void patch(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void setSessionId(const QString& v); void setSessionId(const QString& v);
void abortAll();
private: private: