1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Display notes

This commit is contained in:
Laurent Cozic 2017-01-12 17:59:19 +01:00
parent d3e8ce55f9
commit 4e549695cf
23 changed files with 178 additions and 158 deletions

View File

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1

View File

@ -11,7 +11,6 @@ SOURCES += \
models/notemodel.cpp \
models/note.cpp \
application.cpp \
models/notecollection.cpp \
models/qmlnote.cpp \
webapi.cpp \
synchronizer.cpp \
@ -45,7 +44,6 @@ HEADERS += \
models/notemodel.h \
models/note.h \
application.h \
models/notecollection.h \
sparsevector.hpp \
models/qmlnote.h \
webapi.h \

View File

@ -32,14 +32,4 @@ LoginPageForm {
}
}
// Connections {
// target: dispatcher
// onLoginFailed: {
// root.enabled = true;
// }
// onLoginSuccess: {
// root.enabled = true;
// }
// }
}

View File

@ -1,6 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Controls 1.4
//import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
Item {
@ -103,7 +103,7 @@ Item {
text: "Logout"
anchors.right: syncButton.left
anchors.top: parent.top
onClicked: appRoot.logoutClicked() //dispatcher.logoutClicked()
onClicked: appRoot.logoutClicked()
}
}

View File

@ -58,12 +58,10 @@ Item {
}
function emitLoginStarted() {
print("CALLING emitLoginStarted");
root.loginStarted();
}
function emitLoginFailed() {
print("CALLING emitLoginFailed");
root.loginFailed();
}

View File

@ -17,8 +17,7 @@ using namespace jop;
Application::Application(int &argc, char **argv) :
QGuiApplication(argc, argv),
db_(jop::db()),
synchronizer_(db_),
folderModel_(db_)
synchronizer_(db_)
{
@ -51,7 +50,6 @@ Application::Application(int &argc, char **argv) :
ctxt->setContextProperty("folderListModel", &folderModel_);
ctxt->setContextProperty("noteListModel", &noteModel_);
ctxt->setContextProperty("noteModel", &selectedQmlNote_);
ctxt->setContextProperty("dispatcher", &dispatcher());
ctxt->setContextProperty("settings", qmlSettings);
view_.setSource(QUrl("qrc:/app.qml"));
@ -65,12 +63,6 @@ Application::Application(int &argc, char **argv) :
connect(rootObject, SIGNAL(loginClicked(QString,QString,QString)), this, SLOT(dispatcher_loginClicked(QString,QString,QString)));
connect(rootObject, SIGNAL(logoutClicked()), this, SLOT(dispatcher_logoutClicked()));
connect(&dispatcher(), SIGNAL(folderCreated(QString)), this, SLOT(dispatcher_folderCreated(QString)));
connect(&dispatcher(), SIGNAL(folderUpdated(QString)), this, SLOT(dispatcher_folderUpdated(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(logoutClicked()), this, SLOT(dispatcher_logoutClicked()));
view_.show();
synchronizerTimer_.setInterval(1000 * 120);
@ -81,6 +73,7 @@ Application::Application(int &argc, char **argv) :
connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&)));
if (!settings.contains("user.email") || !settings.contains("session.id") || !settings.contains("api.baseUrl")) {
synchronizer_.freeze();
view_.showPage("login");
} else {
afterSessionInitialization();
@ -121,9 +114,7 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta
if (tag == "getSession") {
if (response.contains("error")) {
qWarning() << "Could not get session:" << response.value("error").toString();
//dispatcher().emitLoginFailed();
view_.emitSignal("loginFailed");
//qDebug() << "FAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILEDFAILED";
view_.showPage("login");
} else {
QString sessionId = response.value("id").toString();
@ -131,7 +122,6 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta
Settings settings;
settings.setValue("session.id", sessionId);
afterSessionInitialization();
//dispatcher().emitLoginSuccess();
view_.emitSignal("loginSuccess");
view_.showPage("main");
}
@ -139,21 +129,6 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta
}
}
void Application::dispatcher_folderCreated(const QString &folderId) {
qDebug() << "Folder created" << folderId;
//synchronizerTimer_.start(1000 * 3);
}
void Application::dispatcher_folderUpdated(const QString &folderId) {
qDebug() << "Folder udpated" << folderId;
//synchronizerTimer_.start(1000 * 3);
}
void Application::dispatcher_folderDeleted(const QString &folderId) {
qDebug() << "Folder deleted" << folderId;
//synchronizerTimer_.start(1000 * 3);
}
void Application::dispatcher_loginClicked(const QString &apiBaseUrl, const QString &email, const QString &password) {
qDebug() << apiBaseUrl << email << password;
@ -188,7 +163,7 @@ void Application::dispatcher_logoutClicked() {
synchronizer_.freeze();
Settings settings;
settings.setValue("session.id", "");
settings.remove("session.id");
api_.setSessionId("");
synchronizer_.setSessionId("");
@ -232,9 +207,8 @@ void Application::afterSessionInitialization() {
}
void Application::view_currentFolderChanged() {
// QString folderId = selectedFolderId();
// noteCollection_ = NoteCollection(db_, folderId, "title ASC");
// noteModel_.setCollection(noteCollection_);
QString folderId = selectedFolderId();
noteModel_.setFolderId(folderId);
}
void Application::view_currentNoteChanged() {

View File

@ -5,7 +5,6 @@
#include "database.h"
#include "models/foldermodel.h"
#include "models/notecollection.h"
#include "models/notemodel.h"
#include "models/qmlnote.h"
#include "webapi.h"
@ -27,7 +26,6 @@ private:
Window view_;
Database& db_;
NoteCollection noteCollection_;
FolderModel folderModel_;
NoteModel noteModel_;
QString selectedFolderId() const;
@ -49,9 +47,6 @@ public slots:
void api_requestDone(const QJsonObject& response, const QString& tag);
void dispatcher_folderCreated(const QString& folderId);
void dispatcher_folderUpdated(const QString& folderId);
void dispatcher_folderDeleted(const QString& folderId);
void dispatcher_loginClicked(const QString &domain, const QString &email, const QString &password);
void dispatcher_logoutClicked();

View File

@ -7,6 +7,7 @@ Database::Database() {}
void Database::initialize(const QString &path) {
version_ = -1;
transactionCount_ = 0;
logQueries_ = false;
//QFile::remove(path);
@ -19,9 +20,6 @@ void Database::initialize(const QString &path) {
qDebug() << "Database: connection ok";
}
// execQuery("DELETE FROM folders");
// execQuery("DELETE FROM settings");
upgrade();
}
@ -131,13 +129,15 @@ bool Database::commit() {
}
bool Database::execQuery(QSqlQuery &query) {
// qDebug().noquote() << "SQL:" << query.lastQuery();
if (logQueries_) {
qDebug().noquote() << "SQL:" << query.lastQuery();
// QMapIterator<QString, QVariant> i(query.boundValues());
// while (i.hasNext()) {
// i.next();
// qDebug().noquote() << "SQL:" << i.key() << "=" << i.value().toString();
// }
QMapIterator<QString, QVariant> i(query.boundValues());
while (i.hasNext()) {
i.next();
qDebug().noquote() << "SQL:" << i.key() << "=" << i.value().toString();
}
}
return query.exec();
}

View File

@ -33,6 +33,7 @@ private:
mutable int version_;
QStringList sqlStringToLines(const QString& sql);
int transactionCount_;
bool logQueries_;
};

View File

@ -52,8 +52,6 @@ bool BaseModel::load(const QString &id) {
bool BaseModel::save(bool trackChanges) {
bool isNew = this->isNew();
qDebug() << "SAVING" << valuesToString();
if (!changedFields_.size() && !isNew) return true;
QStringList fields = changedFields();
@ -170,7 +168,7 @@ bool BaseModel::dispose() {
}
Table BaseModel::table() const {
qCritical() << "BaseModel::table() must be overriden";
qFatal("BaseModel::table() must be overriden");
return jop::UndefinedTable;
}
@ -209,12 +207,33 @@ QVector<BaseModel::Field> BaseModel::tableFields(jop::Table table) {
output.push_back(createField("title", QMetaType::QString ));
output.push_back(createField("created_time", QMetaType::Int ));
output.push_back(createField("updated_time", QMetaType::Int ));
} else if (table == jop::NotesTable) {
output.push_back(createField("id", QMetaType::Int ));
output.push_back(createField("title", QMetaType::QString ));
output.push_back(createField("body", QMetaType::QString ));
output.push_back(createField("parent_id", QMetaType::QString ));
output.push_back(createField("created_time", QMetaType::Int ));
output.push_back(createField("updated_time", QMetaType::Int ));
output.push_back(createField("latitude", QMetaType::QString ));
output.push_back(createField("longitude", QMetaType::QString ));
output.push_back(createField("altitude", QMetaType::QString ));
output.push_back(createField("source", QMetaType::QString ));
output.push_back(createField("author", QMetaType::QString ));
output.push_back(createField("source_url", QMetaType::QString ));
output.push_back(createField("is_todo", QMetaType::Int ));
output.push_back(createField("todo_due", QMetaType::Int ));
output.push_back(createField("todo_completed", QMetaType::Int ));
output.push_back(createField("source_application", QMetaType::QString ));
output.push_back(createField("application_data", QMetaType::QString ));
output.push_back(createField("order", QMetaType::Int ));
} else if (table == jop::ChangesTable) {
output.push_back(createField("id", QMetaType::Int ));
output.push_back(createField("type", QMetaType::Int ));
output.push_back(createField("item_id", QMetaType::QString ));
output.push_back(createField("item_type", QMetaType::Int ));
output.push_back(createField("item_field", QMetaType::QString ));
} else {
qFatal("Field not defined for table %d", table);
}
BaseModel::tableFields_[table] = output;
@ -238,6 +257,16 @@ QStringList BaseModel::tableFieldNames(Table table) {
return output;
}
QString BaseModel::sqlTableFields(Table table) {
QString output = "";
QStringList fields = BaseModel::tableFieldNames(table);
for (int i = 0; i < fields.size(); i++) {
if (output != "") output += ",";
output += QString("`%1`").arg(fields[i]);
}
return output;
}
bool BaseModel::isValidFieldName(Table table, const QString &name) {
QVector<BaseModel::Field> fields = BaseModel::tableFields(table);
foreach (BaseModel::Field col, fields) {
@ -348,7 +377,7 @@ void BaseModel::setValue(const QString &name, const QJsonValue &value, QMetaType
} else if (type == QMetaType::Int) {
setValue(name, value.toInt());
} else {
qCritical() << "Unsupported value type" << name << type;
qFatal("Unsupported value type %s %d", name.toStdString(), type);
}
}
@ -370,7 +399,7 @@ QString BaseModel::tableName(Table t) {
if (t == jop::FoldersTable) return "folders";
if (t == jop::NotesTable) return "notes";
if (t == jop::ChangesTable) return "changes";
return "UNDEFINED";
qFatal("Unknown table %d", t);
}
QVariant BaseModel::cacheGet(const QString &key) {

View File

@ -56,6 +56,7 @@ public:
static QVector<BaseModel::Field> tableFields(Table table);
static bool hasField(jop::Table table, const QString& name);
static QStringList tableFieldNames(Table table);
static QString sqlTableFields(Table table);
static bool isValidFieldName(Table table, const QString& name);
static void deleteAll(Table table);

View File

@ -20,6 +20,35 @@ bool Folder::trackChanges() const {
return true;
}
int Folder::noteCount() const {
QSqlQuery q = jop::db().prepare(QString("SELECT count(*) AS row_count FROM %1 WHERE parent_id = :parent_id").arg(BaseModel::tableName(jop::NotesTable)));
q.bindValue(":parent_id", id().toString());
jop::db().execQuery(q);
q.next();
return q.value(0).toInt();
}
QVector<Note> Folder::notes(const QString &orderBy, int limit, int offset) const {
QVector<Note> output;
QSqlQuery q = jop::db().prepare(QString("SELECT %1 FROM notes WHERE parent_id = :parent_id ORDER BY %2 LIMIT %3 OFFSET %4")
.arg(BaseModel::sqlTableFields(jop::NotesTable))
.arg(orderBy)
.arg(limit)
.arg(offset));
q.bindValue(":parent_id", id().toString());
jop::db().execQuery(q);
if (!jop::db().errorCheck(q)) return output;
while (q.next()) {
Note note;
note.loadSqlQuery(q);
output.push_back(note);
}
return output;
}
int Folder::count() {
return BaseModel::count(jop::FoldersTable);
}

View File

@ -3,6 +3,7 @@
#include <stable.h>
#include "models/item.h"
#include "models/note.h"
namespace jop {
@ -18,6 +19,8 @@ public:
Table table() const;
bool primaryKeyIsUuid() const;
bool trackChanges() const;
int noteCount() const;
QVector<Note> notes(const QString& orderBy, int limit, int offset) const;
// bool save();
// bool dispose();

View File

@ -4,7 +4,7 @@
using namespace jop;
FolderModel::FolderModel(Database &database) : QAbstractListModel(), db_(database), orderBy_("title") {
FolderModel::FolderModel() : QAbstractListModel(), orderBy_("title") {
virtualItemShown_ = false;
connect(&dispatcher(), SIGNAL(folderCreated(QString)), this, SLOT(dispatcher_folderCreated(QString)));
@ -17,9 +17,6 @@ int FolderModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent);
return Folder::count() + (virtualItemShown_ ? 1 : 0);
}
// NOTE: to lazy load - send back "Loading..." if item not currently loaded
// queue the item for loading.
// Then batch load them a bit later.
QVariant FolderModel::data(const QModelIndex & index, int role) const {
Folder folder;
@ -47,10 +44,6 @@ bool FolderModel::setData(const QModelIndex &index, const QVariant &value, int r
folder.setValue("title", value);
if (!folder.save()) return false;
cache_.clear();
// QVector<int> roles;
// roles << Qt::DisplayRole;
// emit dataChanged(this->index(0), this->index(rowCount() - 1), roles);
return true;
}
@ -134,36 +127,12 @@ void FolderModel::addData(const QString &title) {
folder.setValue("title", title);
if (!folder.save()) return;
//cache_.clear();
lastInsertId_ = folder.id().toString();
// QVector<int> roles;
// roles << Qt::DisplayRole;
// int from = 0;
// int to = rowCount() - 1;
// // Necessary to make sure a new item is added to the view, even
// // though it might not be positioned there due to sorting
// beginInsertRows(QModelIndex(), to, to);
// endInsertRows();
// emit dataChanged(this->index(from), this->index(to), roles);
}
void FolderModel::deleteData(const int index) {
Folder folder = atIndex(index);
if (!folder.dispose()) return;
// cache_.clear();
// beginRemoveRows(QModelIndex(), index, index);
// endRemoveRows();
// QVector<int> roles;
// roles << Qt::DisplayRole;
// emit dataChanged(this->index(0), this->index(rowCount() - 1), roles);
}
// TODO: instead of clearing the whole cache every time, the individual items

View File

@ -20,7 +20,7 @@ public:
RawRole
};
FolderModel(Database& database);
FolderModel();
void addFolder(Folder* folder);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
@ -38,7 +38,6 @@ private:
QList<Folder> folders_;
bool virtualItemShown_;
QString orderBy_;
Database& db_;
mutable QVector<Folder> cache_;
QString lastInsertId_;

View File

@ -2,27 +2,16 @@
using namespace jop;
Note::Note()
{
Note::Note() : Item() {}
Table Note::table() const {
return jop::NotesTable;
}
//QString Note::body() const {
// return body_;
//}
bool Note::primaryKeyIsUuid() const {
return true;
}
//void Note::setBody(const QString &v) {
// body_ = v;
//}
//QStringList Note::dbFields() {
// QStringList output = Item::dbFields();
// output << "body";
// return output;
//}
//void Note::fromSqlQuery(const QSqlQuery &q) {
// Item::fromSqlQuery(q);
// int idx = Item::dbFields().size();
// body_ = q.value(idx).toString();
//}
bool Note::trackChanges() const {
return true;
}

View File

@ -11,14 +11,9 @@ class Note : public Item {
public:
Note();
// QString body() const;
// void setBody(const QString& v);
// static QStringList dbFields();
// void fromSqlQuery(const QSqlQuery &q);
//private:
// QString body_;
Table table() const;
bool primaryKeyIsUuid() const;
bool trackChanges() const;
};

View File

@ -78,5 +78,6 @@ Note NoteCollection::byId(const QString& id) const {
// note.setId(q.value(0).toString());
// note.setTitle(q.value(1).toString());
// note.setBody(q.value(2).toString());
// return note;
// return note;
}

View File

@ -1,35 +1,65 @@
#include "notemodel.h"
jop::NoteModel::NoteModel()
{
jop::NoteModel::NoteModel() {
folderId_ = "";
orderBy_ = "title";
}
int jop::NoteModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return collection_.count();
return folder().noteCount();
}
QVariant jop::NoteModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() >= rowCount()) return QVariant();
Note note = atIndex(index.row());
// Note note = collection_.at(index.row());
if (role == IdRole) {
return QVariant(note.id().toString());
}
// if (role == IdRole) {
// return QVariant(note.id());
// }
return QVariant(note.value("title").toString());
}
// return QVariant(note.title());
jop::Note jop::NoteModel::atIndex(int index) const {
if (folderId_ == "") return Note();
if (index < 0 || index >= rowCount()) return Note();
if (cache_.isset(index)) return cache_.get(index);
std::vector<int> indexes = cache_.availableBufferAround(index, 32);
if (!indexes.size()) {
qWarning() << "Couldn't acquire buffer"; // "Cannot happen"
return Note();
}
int from = indexes[0];
int to = indexes[indexes.size() - 1];
Folder folder = this->folder();
QVector<Note> notes = folder.notes(orderBy_, to - from + 1, from);
int noteIndex = from;
for (int i = 0; i < notes.size(); i++) {
cache_.set(noteIndex, notes[i]);
noteIndex++;
}
return cache_.get(index);
}
void jop::NoteModel::setFolderId(const QString &v) {
if (v == folderId_) return;
beginResetModel();
cache_.clear();
folderId_ = v;
endResetModel();
}
void jop::NoteModel::setCollection(jop::NoteCollection &noteCollection) {
beginResetModel();
collection_ = noteCollection;
endResetModel();
jop::Folder jop::NoteModel::folder() const {
Folder folder;
if (folderId_ == "") return folder;
folder.load(folderId_);
return folder;
}
QHash<int, QByteArray> jop::NoteModel::roleNames() const {

View File

@ -3,7 +3,8 @@
#include <stable.h>
#include "models/notecollection.h"
#include "models/folder.h"
#include "sparsevector.hpp"
namespace jop {
@ -21,8 +22,9 @@ public:
NoteModel();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
Note atIndex(int index) const;
void setFolderId(const QString& v);
void setCollection(NoteCollection& noteCollection);
Folder folder() const;
protected:
@ -32,7 +34,8 @@ private:
QList<Note> notes_;
QString folderId_;
NoteCollection collection_;
QString orderBy_;
mutable SparseVector<Note> cache_;
};

View File

@ -6,7 +6,8 @@ using namespace jop;
void Setting::setSettings(const QSettings::SettingsMap &map) {
jop::db().transaction();
QString sql = "INSERT OR REPLACE INTO settings (`key`, `value`, `type`) VALUES (:key, :value, :type)";
jop::db().execQuery("DELETE FROM settings");
QString sql = "INSERT INTO settings (`key`, `value`, `type`) VALUES (:key, :value, :type)";
QSqlQuery query = jop::db().prepare(sql);
for (QSettings::SettingsMap::const_iterator it = map.begin(); it != map.end(); ++it) {
query.bindValue(":key", it.key());

View File

@ -9,7 +9,7 @@ CREATE TABLE notes (
id TEXT PRIMARY KEY,
title TEXT,
body TEXT,
parent_id INT,
parent_id TEXT,
created_time INT,
updated_time INT,
latitude NUMERIC,

View File

@ -355,8 +355,10 @@ int main(int argc, char *argv[]) {
qsrand(QTime::currentTime().msec());
QString dbPath = "D:/Web/www/joplin/QtClient/data/notes.sqlite";
QString resourceDir = "D:/Web/www/joplin/QtClient/data/resources";
QString dbPath = "C:/Users/Laurent/AppData/Local/Joplin/Joplin.sqlite";
QString resourceDir = "C:/Users/Laurent/AppData/Local/Joplin/resources";
QDir(resourceDir).mkpath(".");
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbPath);
@ -371,9 +373,11 @@ int main(int argc, char *argv[]) {
// TODO: REMOVE REMOVE REMOVE
db.exec("DELETE FROM folders");
db.exec("DELETE FROM notes");
db.exec("DELETE FROM changes");
db.exec("DELETE FROM resources");
db.exec("DELETE FROM note_resources");
db.exec("DELETE FROM tags");
db.exec("DELETE FROM settings WHERE key = 'lastRevId'");
// TODO: REMOVE REMOVE REMOVE
QDir dir("S:/Docs/Textes/Calendrier/EvernoteBackup/Enex20161219");
@ -388,13 +392,24 @@ int main(int argc, char *argv[]) {
QString folderId = createUuid(QString("%1%2%3%4").arg(fileInfo.baseName()).arg(fileInfo.created().toTime_t()).arg((int)qrand()).arg(QDateTime::currentMSecsSinceEpoch()));
QSqlQuery query(db);
query.prepare("INSERT INTO folders (id, title, created_time, updated_time) VALUES (?, ?, ?, ?)");
query.addBindValue(folderId);
query.addBindValue(fileInfo.baseName());
query.addBindValue(fileInfo.created().toTime_t());
query.addBindValue(fileInfo.created().toTime_t());
query.exec();
{
QSqlQuery query(db);
query.prepare("INSERT INTO folders (id, title, created_time, updated_time) VALUES (?, ?, ?, ?)");
query.addBindValue(folderId);
query.addBindValue(fileInfo.baseName());
query.addBindValue(fileInfo.created().toTime_t());
query.addBindValue(fileInfo.created().toTime_t());
query.exec();
}
{
QSqlQuery query(db);
query.prepare("INSERT INTO changes (type, item_id, item_type) VALUES (?, ?, ?)");
query.addBindValue(1);
query.addBindValue(folderId);
query.addBindValue(1);
query.exec();
}
std::vector<Note> notes = parseXmlFile(fileInfo.absoluteFilePath());