mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Add delete folder
This commit is contained in:
parent
e751dfc793
commit
4570fcac18
@ -5,39 +5,78 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 25
|
height: 25
|
||||||
|
property int mouseAreaDefaultWidth
|
||||||
|
property Menu contextMenu
|
||||||
|
|
||||||
|
signal stoppedEditing;
|
||||||
|
signal editingAccepted(int index, string text);
|
||||||
|
|
||||||
|
function makeEditable(editable) {
|
||||||
|
if (typeof editable === 'undefined') editable = true;
|
||||||
|
|
||||||
|
if (editable === isEditable()) return; // Nothing to do
|
||||||
|
|
||||||
|
if (editable) {
|
||||||
|
label.visible = false
|
||||||
|
mouseArea.anchors.rightMargin = 10000; // Hack because `mouseArea.visible = false` makes the MouseArea ignore the next click event
|
||||||
|
textField.visible = true
|
||||||
|
textField.focus = true
|
||||||
|
textField.text = display
|
||||||
|
root.ListView.view.focus = true;
|
||||||
|
textField.selectAll()
|
||||||
|
} else {
|
||||||
|
mouseArea.anchors.rightMargin = 0;
|
||||||
|
label.visible = true
|
||||||
|
textField.visible = false
|
||||||
|
root.stoppedEditing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEditing() {
|
||||||
|
makeEditable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopEditing() {
|
||||||
|
makeEditable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEditable() {
|
||||||
|
return textField.visible;
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: label
|
id: label
|
||||||
text: display
|
text: display
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
root.ListView.view.currentIndex = index
|
|
||||||
}
|
|
||||||
onDoubleClicked: {
|
|
||||||
label.visible = false
|
|
||||||
textField.visible = true
|
|
||||||
textField.focus = true
|
|
||||||
textField.selectAll()
|
|
||||||
textField.text = display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: textField
|
id: textField
|
||||||
text: display
|
|
||||||
visible: false
|
visible: false
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
root.ListView.view.model.setData(index, text)
|
root.editingAccepted(index, text);
|
||||||
}
|
}
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
label.visible = true
|
stopEditing();
|
||||||
textField.visible = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: {
|
||||||
|
root.ListView.view.currentIndex = index
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
contextMenu.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDoubleClicked: {
|
||||||
|
startEditing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,20 @@ Item {
|
|||||||
property alias model: listView.model
|
property alias model: listView.model
|
||||||
property alias currentIndex: listView.currentIndex
|
property alias currentIndex: listView.currentIndex
|
||||||
property alias currentItem: listView.currentItem
|
property alias currentItem: listView.currentItem
|
||||||
|
property string currentItemId
|
||||||
|
|
||||||
|
signal stoppedEditing;
|
||||||
|
signal editingAccepted(int index, string text);
|
||||||
|
signal deleteButtonClicked(int index);
|
||||||
|
|
||||||
|
function startEditing(index) {
|
||||||
|
currentIndex = model.rowCount() - 1;
|
||||||
|
currentItem.startEditing();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopEditing() {
|
||||||
|
currentItem.stopEditing();
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "#eeeeff"
|
color: "#eeeeff"
|
||||||
@ -14,22 +28,48 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: model
|
||||||
|
onDataChanged: {
|
||||||
|
if (currentItemId !== "") {
|
||||||
|
var newIndex = model.idToIndex(currentItemId);
|
||||||
|
currentIndex = newIndex
|
||||||
|
if (newIndex < 0) currentItemId = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentItemChanged: {
|
||||||
|
currentItemId = model.idAtIndex(currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
id: listView
|
id: listView
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
highlightMoveDuration: 100
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
delegate: folderDelegate
|
delegate: folderDelegate
|
||||||
ScrollBar.vertical: ScrollBar { }
|
ScrollBar.vertical: ScrollBar { }
|
||||||
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
|
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
|
||||||
focus: true
|
focus: true
|
||||||
// onModelChanged: {
|
|
||||||
//// listView.model.onDataChanged = function() {
|
|
||||||
//// console.info("testaaaaaaaaaaaaaaaaaaa")
|
|
||||||
//// }
|
|
||||||
// console.info("MODEL CHANGAID")
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: folderDelegate
|
id: folderDelegate
|
||||||
EditableListItem {}
|
EditableListItem {
|
||||||
|
contextMenu:
|
||||||
|
Menu {
|
||||||
|
MenuItem {
|
||||||
|
text: "Delete"
|
||||||
|
onTriggered: deleteButtonClicked(currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onStoppedEditing: {
|
||||||
|
root.stoppedEditing();
|
||||||
|
}
|
||||||
|
onEditingAccepted: function(index, text) {
|
||||||
|
root.editingAccepted(index, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,9 @@ SOURCES += \
|
|||||||
webapi.cpp \
|
webapi.cpp \
|
||||||
synchronizer.cpp \
|
synchronizer.cpp \
|
||||||
settings.cpp \
|
settings.cpp \
|
||||||
models/foldercollection.cpp
|
models/foldercollection.cpp \
|
||||||
|
uuid.cpp \
|
||||||
|
dispatcher.cpp
|
||||||
|
|
||||||
RESOURCES += qml.qrc \
|
RESOURCES += qml.qrc \
|
||||||
database.qrc
|
database.qrc
|
||||||
@ -45,7 +47,9 @@ HEADERS += \
|
|||||||
synchronizer.h \
|
synchronizer.h \
|
||||||
settings.h \
|
settings.h \
|
||||||
models/foldercollection.h \
|
models/foldercollection.h \
|
||||||
simpletypes.h
|
simpletypes.h \
|
||||||
|
uuid.h \
|
||||||
|
dispatcher.h
|
||||||
|
|
||||||
DISTFILES +=
|
DISTFILES +=
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "models/foldermodel.h"
|
#include "models/foldermodel.h"
|
||||||
#include "services/folderservice.h"
|
#include "services/folderservice.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "uuid.h"
|
||||||
|
#include "dispatcher.h"
|
||||||
|
|
||||||
using namespace jop;
|
using namespace jop;
|
||||||
|
|
||||||
@ -38,21 +40,27 @@ Application::Application(int &argc, char **argv) :
|
|||||||
|
|
||||||
connect(rootObject, SIGNAL(currentFolderChanged()), this, SLOT(view_currentFolderChanged()));
|
connect(rootObject, SIGNAL(currentFolderChanged()), this, SLOT(view_currentFolderChanged()));
|
||||||
connect(rootObject, SIGNAL(currentNoteChanged()), this, SLOT(view_currentNoteChanged()));
|
connect(rootObject, SIGNAL(currentNoteChanged()), this, SLOT(view_currentNoteChanged()));
|
||||||
|
connect(rootObject, SIGNAL(addFolderButtonClicked()), this, SLOT(view_addFolderButtonClicked()));
|
||||||
|
|
||||||
view_.show();
|
view_.show();
|
||||||
|
|
||||||
connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&)));
|
connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&)));
|
||||||
|
|
||||||
QString sessionId = settings.value("sessionId").toString();
|
QString sessionId = settings.value("sessionId").toString();
|
||||||
if (sessionId == "") {
|
//if (sessionId == "") {
|
||||||
QUrlQuery postData;
|
QUrlQuery postData;
|
||||||
postData.addQueryItem("email", "laurent@cozic.net");
|
postData.addQueryItem("email", "laurent@cozic.net");
|
||||||
postData.addQueryItem("password", "12345678");
|
postData.addQueryItem("password", "12345678");
|
||||||
postData.addQueryItem("client_id", "B6E12222B6E12222");
|
postData.addQueryItem("client_id", "B6E12222B6E12222");
|
||||||
api_.post("sessions", QUrlQuery(), postData, "getSession");
|
api_.post("sessions", QUrlQuery(), postData, "getSession");
|
||||||
} else {
|
// } else {
|
||||||
afterSessionInitialization();
|
// afterSessionInitialization();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//emit jop::dispatcher().folderCreated("test");
|
||||||
|
//.folderCreated("tes");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::api_requestDone(const QJsonObject& response, const QString& tag) {
|
void Application::api_requestDone(const QJsonObject& response, const QString& tag) {
|
||||||
@ -91,7 +99,7 @@ void Application::afterSessionInitialization() {
|
|||||||
QString sessionId = settings.value("sessionId").toString();
|
QString sessionId = settings.value("sessionId").toString();
|
||||||
qDebug() << "Session:" << sessionId;
|
qDebug() << "Session:" << sessionId;
|
||||||
api_.setSessionId(sessionId);
|
api_.setSessionId(sessionId);
|
||||||
//synchronizer_.start();
|
synchronizer_.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::view_currentFolderChanged() {
|
void Application::view_currentFolderChanged() {
|
||||||
@ -107,9 +115,18 @@ void Application::view_currentNoteChanged() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::view_addNoteButtonClicked() {
|
void Application::view_addNoteButtonClicked() {
|
||||||
qDebug() << "ici";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::view_addFolderButtonClicked() {
|
void Application::view_addFolderButtonClicked() {
|
||||||
|
// QStringList fields;
|
||||||
|
// fields << "id";
|
||||||
|
// VariantVector values;
|
||||||
|
// values << uuid::createUuid();
|
||||||
|
// QSqlQuery q = db_.buildSqlQuery(Database::Insert, "folders", fields, values);
|
||||||
|
// q.exec();
|
||||||
|
|
||||||
|
// emit jop::dispatcher().folderCreated("test");
|
||||||
|
|
||||||
|
//qDebug() << "Added" << q.lastInsertId().toString();
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ using namespace jop;
|
|||||||
Database::Database(const QString &path) {
|
Database::Database(const QString &path) {
|
||||||
version_ = -1;
|
version_ = -1;
|
||||||
|
|
||||||
//QFile::remove(path);
|
// QFile::remove(path);
|
||||||
|
|
||||||
db_ = QSqlDatabase::addDatabase("QSQLITE");
|
db_ = QSqlDatabase::addDatabase("QSQLITE");
|
||||||
db_.setDatabaseName(path);
|
db_.setDatabaseName(path);
|
||||||
@ -84,13 +84,13 @@ QSqlQuery Database::buildSqlQuery(Database::QueryType type, const QString &table
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() <<"SQL:"<<sql;
|
// qDebug() <<"SQL:"<<sql;
|
||||||
|
|
||||||
QMapIterator<QString, QVariant> i(query.boundValues());
|
// QMapIterator<QString, QVariant> i(query.boundValues());
|
||||||
while (i.hasNext()) {
|
// while (i.hasNext()) {
|
||||||
i.next();
|
// i.next();
|
||||||
qDebug() << i.key() << ":" << i.value().toString();
|
// qDebug() << i.key() << ":" << i.value().toString();
|
||||||
}
|
// }
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
17
QtClient/JoplinQtClient/dispatcher.cpp
Executable file
17
QtClient/JoplinQtClient/dispatcher.cpp
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#include "dispatcher.h"
|
||||||
|
|
||||||
|
using namespace jop;
|
||||||
|
|
||||||
|
Dispatcher::Dispatcher() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher instance_;
|
||||||
|
|
||||||
|
Dispatcher& jop::dispatcher() {
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Dispatcher &Dispatcher::instance() {
|
||||||
|
// return instance_;
|
||||||
|
//}
|
29
QtClient/JoplinQtClient/dispatcher.h
Executable file
29
QtClient/JoplinQtClient/dispatcher.h
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef DISPATCHER_H
|
||||||
|
#define DISPATCHER_H
|
||||||
|
|
||||||
|
namespace jop {
|
||||||
|
|
||||||
|
class Dispatcher : public QObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Dispatcher();
|
||||||
|
//static Dispatcher& instance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void folderCreated(const QString& id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//static Dispatcher& instance_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Dispatcher& dispatcher();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISPATCHER_H
|
@ -32,6 +32,24 @@ Item {
|
|||||||
onCurrentItemChanged: {
|
onCurrentItemChanged: {
|
||||||
root.currentFolderChanged()
|
root.currentFolderChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEditingAccepted: function(index, text) {
|
||||||
|
if (folderList.model.virtualItemShown()) {
|
||||||
|
folderList.model.addData(text)
|
||||||
|
} else {
|
||||||
|
folderList.model.setData(index, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onStoppedEditing: {
|
||||||
|
if (folderList.model.virtualItemShown()) {
|
||||||
|
folderList.model.hideVirtualItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteButtonClicked: {
|
||||||
|
folderList.model.deleteData(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteList {
|
NoteList {
|
||||||
@ -64,7 +82,10 @@ Item {
|
|||||||
id: addButton
|
id: addButton
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
onAddFolderButtonClicked: root.addFolderButtonClicked()
|
onAddFolderButtonClicked: {
|
||||||
|
folderList.model.showVirtualItem();
|
||||||
|
folderList.startEditing(folderList.model.rowCount() - 1);
|
||||||
|
}
|
||||||
onAddNoteButtonClicked: root.addNoteButtonClicked()
|
onAddNoteButtonClicked: root.addNoteButtonClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "foldercollection.h"
|
#include "foldercollection.h"
|
||||||
#include "databaseutils.h"
|
#include "databaseutils.h"
|
||||||
|
#include "dispatcher.h"
|
||||||
|
#include "uuid.h"
|
||||||
|
|
||||||
using namespace jop;
|
using namespace jop;
|
||||||
|
|
||||||
@ -8,24 +10,35 @@ FolderCollection::FolderCollection(Database& db, const QString& parentId, const
|
|||||||
db_ = db;
|
db_ = db;
|
||||||
parentId_ = parentId;
|
parentId_ = parentId;
|
||||||
orderBy_ = orderBy;
|
orderBy_ = orderBy;
|
||||||
|
|
||||||
|
connect(&jop::dispatcher(), SIGNAL(folderCreated(const QString&)), this, SLOT(dispatcher_folderCreated(QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder FolderCollection::at(int index) const {
|
Folder FolderCollection::at(int index) const {
|
||||||
if (cache_.size()) return cache_[index];
|
if (cache_.size()) {
|
||||||
|
if (index < 0 || index >= count()) {
|
||||||
|
qWarning() << "Invalid folder index:" << index;
|
||||||
|
return Folder();
|
||||||
|
}
|
||||||
|
|
||||||
QSqlQuery q = db_.query("SELECT id, title FROM folders ORDER BY " + orderBy_);
|
return cache_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery q = db_.query("SELECT " + Folder::dbFields().join(",") + " FROM folders ORDER BY " + orderBy_);
|
||||||
q.exec();
|
q.exec();
|
||||||
|
|
||||||
while (q.next()) {
|
while (q.next()) {
|
||||||
Folder folder;
|
Folder folder;
|
||||||
folder.setId(q.value(0).toString());
|
folder.fromSqlQuery(q);
|
||||||
folder.setTitle(q.value(1).toString());
|
|
||||||
|
|
||||||
cache_.push_back(folder);
|
cache_.push_back(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cache_.size()) {
|
||||||
return at(index);
|
qWarning() << "Invalid folder index:" << index;
|
||||||
|
return Folder();
|
||||||
|
} else {
|
||||||
|
return at(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cache result
|
// TODO: cache result
|
||||||
@ -37,19 +50,57 @@ int FolderCollection::count() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Folder FolderCollection::byId(const QString& id) const {
|
Folder FolderCollection::byId(const QString& id) const {
|
||||||
|
int index = idToIndex(id);
|
||||||
|
return at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FolderCollection::idToIndex(const QString &id) const {
|
||||||
int count = this->count();
|
int count = this->count();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Folder folder = at(i);
|
Folder folder = at(i);
|
||||||
if (folder.id() == id) return folder;
|
if (folder.id() == id) return i;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
qWarning() << "Invalid folder ID:" << id;
|
|
||||||
return Folder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderCollection::update(const QString &id, const QStringList &fields, const VariantVector &values) {
|
QString FolderCollection::indexToId(int index) const {
|
||||||
|
Folder folder = at(index);
|
||||||
|
return folder.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderCollection::update(const QString &id, QStringList fields, VariantVector values) {
|
||||||
|
if (!fields.contains("synced")) {
|
||||||
|
fields.push_back("synced");
|
||||||
|
values.push_back(QVariant(0));
|
||||||
|
}
|
||||||
QSqlQuery q = db_.buildSqlQuery(Database::Update, "folders", fields, values, "id = \"" + id + "\"");
|
QSqlQuery q = db_.buildSqlQuery(Database::Update, "folders", fields, values, "id = \"" + id + "\"");
|
||||||
q.exec();
|
q.exec();
|
||||||
cache_.clear();
|
cache_.clear();
|
||||||
emit changed(0, count() - 1, fields);
|
emit changed(0, count() - 1, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderCollection::add(QStringList fields, VariantVector values) {
|
||||||
|
fields.push_back("synced");
|
||||||
|
values.push_back(QVariant(0));
|
||||||
|
|
||||||
|
fields.push_back("id");
|
||||||
|
values.push_back(uuid::createUuid());
|
||||||
|
|
||||||
|
QSqlQuery q = db_.buildSqlQuery(Database::Insert, "folders", fields, values);
|
||||||
|
q.exec();
|
||||||
|
cache_.clear();
|
||||||
|
emit changed(0, count() - 1, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderCollection::remove(const QString& id) {
|
||||||
|
QSqlQuery q(db_.database());
|
||||||
|
q.prepare("DELETE FROM folders WHERE id = :id");
|
||||||
|
q.bindValue(":id", id);
|
||||||
|
q.exec();
|
||||||
|
cache_.clear();
|
||||||
|
emit changed(0, count(), QStringList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderCollection::dispatcher_folderCreated(const QString &id) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -22,7 +22,11 @@ public:
|
|||||||
Folder at(int index) const;
|
Folder at(int index) const;
|
||||||
int count() const;
|
int count() const;
|
||||||
Folder byId(const QString &id) const;
|
Folder byId(const QString &id) const;
|
||||||
void update(const QString& id, const QStringList& fields, const VariantVector& values);
|
int idToIndex(const QString& id) const;
|
||||||
|
QString indexToId(int index) const;
|
||||||
|
void update(const QString& id, QStringList fields, VariantVector values);
|
||||||
|
void add(QStringList fields, VariantVector values);
|
||||||
|
void remove(const QString &id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -35,6 +39,10 @@ signals:
|
|||||||
|
|
||||||
void changed(int from, int to, const QStringList& fields);
|
void changed(int from, int to, const QStringList& fields);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void dispatcher_folderCreated(const QString& id);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,27 @@
|
|||||||
|
|
||||||
using namespace jop;
|
using namespace jop;
|
||||||
|
|
||||||
FolderModel::FolderModel(Database &database) : QAbstractListModel(), folderCollection_(database, 0, "title") {
|
FolderModel::FolderModel(Database &database) : QAbstractListModel(), folderCollection_(database, 0, "title"), db_(database), orderBy_("title") {
|
||||||
|
virtualItemShown_ = false;
|
||||||
connect(&folderCollection_, SIGNAL(changed(int,int,const QStringList&)), this, SLOT(folderCollection_changed(int,int,const QStringList&)));
|
connect(&folderCollection_, SIGNAL(changed(int,int,const QStringList&)), this, SLOT(folderCollection_changed(int,int,const QStringList&)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int FolderModel::rowCount(const QModelIndex & parent) const {
|
int FolderModel::rowCount(const QModelIndex & parent) const {
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
return folderCollection_.count();
|
return folderCollection_.count() + (virtualItemShown_ ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: to lazy load - send back "Loading..." if item not currently loaded
|
// NOTE: to lazy load - send back "Loading..." if item not currently loaded
|
||||||
// queue the item for loading.
|
// queue the item for loading.
|
||||||
// Then batch load them a bit later.
|
// Then batch load them a bit later.
|
||||||
QVariant FolderModel::data(const QModelIndex & index, int role) const {
|
QVariant FolderModel::data(const QModelIndex & index, int role) const {
|
||||||
Folder folder = folderCollection_.at(index.row());
|
Folder folder;
|
||||||
|
|
||||||
|
if (virtualItemShown_ && index.row() == rowCount() - 1) {
|
||||||
|
folder.setTitle("Untitled");
|
||||||
|
} else {
|
||||||
|
folder = folderCollection_.at(index.row());
|
||||||
|
}
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
return QVariant(folder.title());
|
return QVariant(folder.title());
|
||||||
@ -32,6 +39,8 @@ bool FolderModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||||||
Folder folder = folderCollection_.at(index.row());
|
Folder folder = folderCollection_.at(index.row());
|
||||||
|
|
||||||
if (role == Qt::EditRole) {
|
if (role == Qt::EditRole) {
|
||||||
|
emit dataChanging();
|
||||||
|
|
||||||
QStringList fields;
|
QStringList fields;
|
||||||
fields << "title";
|
fields << "title";
|
||||||
VariantVector values;
|
VariantVector values;
|
||||||
@ -44,6 +53,30 @@ bool FolderModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderModel::showVirtualItem() {
|
||||||
|
virtualItemShown_ = true;
|
||||||
|
beginInsertRows(QModelIndex(), this->rowCount() - 1, this->rowCount() - 1);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderModel::hideVirtualItem() {
|
||||||
|
beginRemoveRows(QModelIndex(), this->rowCount() - 1, this->rowCount() - 1);
|
||||||
|
virtualItemShown_ = false;
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FolderModel::idAtIndex(int index) const {
|
||||||
|
return data(this->index(index), IdRole).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FolderModel::idToIndex(const QString &id) const {
|
||||||
|
return folderCollection_.idToIndex(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderModel::virtualItemShown() const {
|
||||||
|
return virtualItemShown_;
|
||||||
|
}
|
||||||
|
|
||||||
bool FolderModel::setData(int index, const QVariant &value, int role) {
|
bool FolderModel::setData(int index, const QVariant &value, int role) {
|
||||||
return setData(this->index(index), value, role);
|
return setData(this->index(index), value, role);
|
||||||
}
|
}
|
||||||
@ -56,8 +89,26 @@ QHash<int, QByteArray> FolderModel::roleNames() const {
|
|||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderModel::addData(const QString &title) {
|
||||||
|
emit dataChanging();
|
||||||
|
|
||||||
|
QStringList fields;
|
||||||
|
fields << "title";
|
||||||
|
VariantVector values;
|
||||||
|
values << QVariant(title);
|
||||||
|
folderCollection_.add(fields, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderModel::deleteData(const int index) {
|
||||||
|
QString id = folderCollection_.indexToId(index);
|
||||||
|
folderCollection_.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
void FolderModel::folderCollection_changed(int from, int to, const QStringList& fields) {
|
void FolderModel::folderCollection_changed(int from, int to, const QStringList& fields) {
|
||||||
|
beginRemoveRows(QModelIndex(), from, to);
|
||||||
QVector<int> roles;
|
QVector<int> roles;
|
||||||
roles << Qt::DisplayRole;
|
roles << Qt::DisplayRole;
|
||||||
|
qDebug() << "update" << from << to;
|
||||||
emit dataChanged(this->index(from), this->index(to), roles);
|
emit dataChanged(this->index(from), this->index(to), roles);
|
||||||
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,8 @@ public:
|
|||||||
FolderModel(Database& database);
|
FolderModel(Database& database);
|
||||||
|
|
||||||
void addFolder(Folder* folder);
|
void addFolder(Folder* folder);
|
||||||
|
|
||||||
int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -38,11 +35,25 @@ private:
|
|||||||
|
|
||||||
QList<Folder> folders_;
|
QList<Folder> folders_;
|
||||||
FolderCollection folderCollection_;
|
FolderCollection folderCollection_;
|
||||||
|
bool virtualItemShown_;
|
||||||
|
QString orderBy_;
|
||||||
|
Database& db_;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
void addData(const QString& title);
|
||||||
|
void deleteData(const int index);
|
||||||
bool setData(int index, const QVariant &value, int role = Qt::EditRole);
|
bool setData(int index, const QVariant &value, int role = Qt::EditRole);
|
||||||
void folderCollection_changed(int from, int to, const QStringList &fields);
|
void folderCollection_changed(int from, int to, const QStringList &fields);
|
||||||
|
void showVirtualItem();
|
||||||
|
bool virtualItemShown() const;
|
||||||
|
void hideVirtualItem();
|
||||||
|
QString idAtIndex(int index) const;
|
||||||
|
int idToIndex(const QString& id) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void dataChanging();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using namespace jop;
|
using namespace jop;
|
||||||
|
|
||||||
Item::Item() {
|
Item::Item() {
|
||||||
isPartial_ = true;
|
|
||||||
synced_ = false;
|
synced_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +23,10 @@ int Item::updatedTime() const {
|
|||||||
return updatedTime_;
|
return updatedTime_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Item::synced() const {
|
||||||
|
return synced_;
|
||||||
|
}
|
||||||
|
|
||||||
void Item::setId(const QString& v) {
|
void Item::setId(const QString& v) {
|
||||||
id_ = v;
|
id_ = v;
|
||||||
}
|
}
|
||||||
@ -36,12 +39,8 @@ void Item::setCreatedTime(int v) {
|
|||||||
createdTime_ = v;
|
createdTime_ = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::setIsPartial(bool v) {
|
void Item::setSynced(bool v) {
|
||||||
isPartial_ = v;
|
synced_ = v;
|
||||||
}
|
|
||||||
|
|
||||||
bool Item::isPartial() const {
|
|
||||||
return isPartial_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Item::dbFields() {
|
QStringList Item::dbFields() {
|
||||||
|
@ -15,13 +15,13 @@ public:
|
|||||||
QString title() const;
|
QString title() const;
|
||||||
int createdTime() const;
|
int createdTime() const;
|
||||||
int updatedTime() const;
|
int updatedTime() const;
|
||||||
bool isPartial() const;
|
bool synced() const;
|
||||||
static QStringList dbFields();
|
static QStringList dbFields();
|
||||||
|
|
||||||
void setId(const QString &v);
|
void setId(const QString &v);
|
||||||
void setTitle(const QString& v);
|
void setTitle(const QString& v);
|
||||||
void setCreatedTime(int v);
|
void setCreatedTime(int v);
|
||||||
void setIsPartial(bool v);
|
void setSynced(bool v);
|
||||||
|
|
||||||
void fromSqlQuery(const QSqlQuery& query);
|
void fromSqlQuery(const QSqlQuery& query);
|
||||||
|
|
||||||
@ -33,8 +33,6 @@ private:
|
|||||||
time_t updatedTime_;
|
time_t updatedTime_;
|
||||||
bool synced_;
|
bool synced_;
|
||||||
|
|
||||||
bool isPartial_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,6 @@ Note NoteCollection::at(int index) const {
|
|||||||
int from = indexes[0];
|
int from = indexes[0];
|
||||||
int to = indexes[indexes.size() - 1];
|
int to = indexes[indexes.size() - 1];
|
||||||
|
|
||||||
qDebug() << "Getting from" << from << "to" << to;
|
|
||||||
|
|
||||||
QSqlQuery q = db_.query("SELECT id, title, body FROM notes WHERE parent_id = :parent_id ORDER BY " + orderBy_ + " LIMIT " + QString::number(to - from + 1) + " OFFSET " + QString::number(from));
|
QSqlQuery q = db_.query("SELECT id, title, body FROM notes WHERE parent_id = :parent_id ORDER BY " + orderBy_ + " LIMIT " + QString::number(to - from + 1) + " OFFSET " + QString::number(from));
|
||||||
q.bindValue(":parent_id", parentId_);
|
q.bindValue(":parent_id", parentId_);
|
||||||
q.exec();
|
q.exec();
|
||||||
@ -36,7 +34,6 @@ Note NoteCollection::at(int index) const {
|
|||||||
note.setId(q.value(0).toString());
|
note.setId(q.value(0).toString());
|
||||||
note.setTitle(q.value(1).toString());
|
note.setTitle(q.value(1).toString());
|
||||||
note.setBody(q.value(2).toString());
|
note.setBody(q.value(2).toString());
|
||||||
note.setIsPartial(true);
|
|
||||||
|
|
||||||
cache_.set(noteIndex, note);
|
cache_.set(noteIndex, note);
|
||||||
|
|
||||||
|
@ -64,6 +64,13 @@ CREATE TABLE version (
|
|||||||
version INT
|
version INT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE changes (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
item_id TEXT,
|
||||||
|
item_type INT,
|
||||||
|
item_property TEXT
|
||||||
|
);
|
||||||
|
|
||||||
--CREATE TABLE mimetypes (
|
--CREATE TABLE mimetypes (
|
||||||
-- id INT,
|
-- id INT,
|
||||||
-- mime TEXT
|
-- mime TEXT
|
||||||
|
@ -44,6 +44,8 @@ void Synchronizer::start() {
|
|||||||
api_.put("folders/" + folder.id(), QUrlQuery(), data, "putFolder:" + folder.id());
|
api_.put("folders/" + folder.id(), QUrlQuery(), data, "putFolder:" + folder.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < notes.size(); i++) {
|
for (int i = 0; i < notes.size(); i++) {
|
||||||
Note note = notes[i];
|
Note note = notes[i];
|
||||||
QUrlQuery data;
|
QUrlQuery data;
|
||||||
@ -68,10 +70,10 @@ void Synchronizer::api_requestDone(const QJsonObject& response, const QString& t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action == "putFolder") {
|
if (action == "putFolder") {
|
||||||
// qDebug() << "Done folder" << id;
|
qDebug() << "Synced folder" << id;
|
||||||
// query = db_.query("UPDATE folders SET synced = 1 WHERE id = ?");
|
query = db_.query("UPDATE folders SET synced = 1 WHERE id = ?");
|
||||||
// query.addBindValue(id);
|
query.addBindValue(id);
|
||||||
// query.exec();
|
query.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == "putNote") {
|
if (action == "putNote") {
|
||||||
|
@ -4,19 +4,11 @@
|
|||||||
namespace jop {
|
namespace jop {
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
//QUuid fromString(const QString& s) {
|
QString createUuid(QString s) {
|
||||||
// // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
if (s == "") s = QString("%1%2").arg(qrand()).arg(QDateTime::currentMSecsSinceEpoch());
|
||||||
// QString mod = s;
|
QString hash = QString(QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Sha256).toHex());
|
||||||
// mod.insert(8, '-');
|
return hash.left(32);
|
||||||
// mod.insert(13, '-');
|
}
|
||||||
// mod.insert(18, '-');
|
|
||||||
// mod.insert(23, '-');
|
|
||||||
// mod = "{" + mod + "}";
|
|
||||||
|
|
||||||
// //qDebug() << mod;
|
|
||||||
|
|
||||||
// return QUuid(mod);
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
namespace jop {
|
namespace jop {
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
//QUuid fromString(const QString& s);
|
QString createUuid(QString s = "");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,13 @@ void WebApi::processQueue() {
|
|||||||
QueuedRequest& r = queuedRequests_.takeFirst();
|
QueuedRequest& r = queuedRequests_.takeFirst();
|
||||||
|
|
||||||
QString url = baseUrl_ + "/" + r.path;
|
QString url = baseUrl_ + "/" + r.path;
|
||||||
|
QUrlQuery query = r.query;
|
||||||
|
|
||||||
|
if (sessionId_ != "") {
|
||||||
|
query.addQueryItem("session", sessionId_);
|
||||||
|
}
|
||||||
|
|
||||||
|
url += "?" + query.toString(QUrl::FullyEncoded);
|
||||||
|
|
||||||
QNetworkRequest* request = new QNetworkRequest(url);
|
QNetworkRequest* request = new QNetworkRequest(url);
|
||||||
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
@ -72,11 +79,12 @@ void WebApi::processQueue() {
|
|||||||
cmd << "curl";
|
cmd << "curl";
|
||||||
if (r.method == QNetworkAccessManager::PutOperation) {
|
if (r.method == QNetworkAccessManager::PutOperation) {
|
||||||
cmd << "-X" << "PUT";
|
cmd << "-X" << "PUT";
|
||||||
cmd << "--data" << "'" + r.data.toString(QUrl::FullyEncoded) + "'";
|
|
||||||
cmd << url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//qDebug().noquote() << cmd.join(" ");
|
cmd << "--data" << "'" + r.data.toString(QUrl::FullyEncoded) + "'";
|
||||||
|
cmd << url;
|
||||||
|
|
||||||
|
qDebug().noquote() << cmd.join(" ");
|
||||||
|
|
||||||
inProgressRequests_.push_back(r);
|
inProgressRequests_.push_back(r);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ abstract class ApiController extends Controller {
|
|||||||
protected $session = null;
|
protected $session = null;
|
||||||
protected $user = null;
|
protected $user = null;
|
||||||
|
|
||||||
private $useTestUserAndSession = true;
|
private $useTestUserAndSession = false;
|
||||||
private $testClientNum = 1;
|
private $testClientNum = 1;
|
||||||
|
|
||||||
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null) {
|
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null) {
|
||||||
@ -40,16 +40,21 @@ abstract class ApiController extends Controller {
|
|||||||
echo "\n";
|
echo "\n";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// HACK: get connection once here so that it's initialized and can
|
// HACK: get connection once here so that it's initialized and can
|
||||||
// be accessed from models.
|
// be accessed from models.
|
||||||
$this->db = $this->get('app.eloquent')->connection();
|
$this->db = $this->get('app.eloquent')->connection();
|
||||||
|
|
||||||
$s = $this->session();
|
$s = $this->session();
|
||||||
|
|
||||||
|
// TODO: find less hacky way to get request path
|
||||||
|
$requestPath = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||||
|
$requestPath = ltrim($requestPath, '/');
|
||||||
|
$requestPath = rtrim($requestPath, '?');
|
||||||
|
|
||||||
// TODO: to keep it simple, only respond to logged in users, but in theory some data
|
// TODO: to keep it simple, only respond to logged in users, but in theory some data
|
||||||
// could be public.
|
// could be public.
|
||||||
if (!$s || !$this->user()) throw new UnauthorizedException('A session and user are required');
|
if ($requestPath != 'sessions' && (!$s || !$this->user())) throw new UnauthorizedException('A session and user are required');
|
||||||
|
|
||||||
BaseModel::setClientId($s ? $s->client_id : 0);
|
BaseModel::setClientId($s ? $s->client_id : 0);
|
||||||
}
|
}
|
||||||
|
38
web/client.php
Executable file
38
web/client.php
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function config($name) {
|
||||||
|
$config = array(
|
||||||
|
'baseUrl' => 'http://joplin.local',
|
||||||
|
);
|
||||||
|
if (isset($config[$name])) {
|
||||||
|
return $config[$name];
|
||||||
|
}
|
||||||
|
throw new Exception('Unknown config: ' . $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function execRequest($method, $path, $query = null, $data = null) {
|
||||||
|
$url = config('baseUrl') . '/' . $path;
|
||||||
|
if ($query) $url .= '?' . http_build_query($query);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$output = json_decode($response, true);
|
||||||
|
if ($output === null) {
|
||||||
|
return array('error' => 'Cannot decode JSON', 'body' => $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
$session = execRequest('POST', 'session', null, array(
|
||||||
|
'email' => 'laurent@cozic.net',
|
||||||
|
'password' => '12345678',
|
||||||
|
));
|
||||||
|
|
||||||
|
var_dump($session);
|
||||||
|
|
||||||
|
die();
|
Loading…
Reference in New Issue
Block a user