diff --git a/src/controllers/books/bookscontroller.cpp b/src/controllers/books/bookscontroller.cpp index 65bf98d..39bec2c 100644 --- a/src/controllers/books/bookscontroller.cpp +++ b/src/controllers/books/bookscontroller.cpp @@ -1,145 +1,150 @@ #include "bookscontroller.h" #include #include "db/db.h" BooksController::BooksController(QObject *parent) : QObject(parent) ,m_db(DB::getInstance()) { } void BooksController::getBooklet(const QString &bookId) { auto booklets = this->m_db->getDBData(QString("select * from booklets where id = '%1'").arg(bookId)); for(auto &booklet : booklets) emit this->bookletReady(booklet); } bool BooksController::insertBooklet(QString bookId, FMH::MODEL &booklet) { if(bookId.isEmpty() || booklet.isEmpty()) { qWarning()<< "Could not insert booklet. Reference to book id or booklet are empty"; return false; } FMH::MODEL book; book[FMH::MODEL_KEY::TITLE] = bookId; if(!this->m_db->checkExistance(OWL::TABLEMAP[OWL::TABLE::BOOKS], "title", bookId)) { qWarning()<< "The book does not exists in the db or the directory is missing. BooksSyncer::insertBookletLocal. " "Creating a new book registry" << bookId; if(!this->insertBook(book)) return false; } booklet[FMH::MODEL_KEY::ID] = OWL::createId(); const auto url = QUrl(OWL::BooksPath.toString()+bookId+"/"+booklet[FMH::MODEL_KEY::ID]+booklet[FMH::MODEL_KEY::FORMAT]); qDebug()<< "trying to insert booklet" << booklet[FMH::MODEL_KEY::ID] << booklet[FMH::MODEL_KEY::CATEGORY] << url; if(!OWL::saveNoteFile(url, booklet[FMH::MODEL_KEY::CONTENT].toUtf8())) { qWarning()<< "File could not be saved. BooksSyncer::insertBookletLocal" <m_db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKLETS], __bookletMap)) { // for(const auto &tg : booklet[FMH::MODEL_KEY::TAG].split(",", QString::SplitBehavior::SkipEmptyParts)) // this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], booklet[FMH::MODEL_KEY::ID], booklet[FMH::MODEL_KEY::COLOR]); return true; } return false; } bool BooksController::updateBooklet(FMH::MODEL &booklet, QString id) { // for(const auto &tg : booklet[FMH::MODEL_KEY::TAG]) // this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], id); - const QUrl __path = QFileInfo(booklet[FMH::MODEL_KEY::URL]).dir().path(); -// const auto __bookletPath = BooksSyncer::saveNoteFile(__path.toLocalFile()+"/", booklet); -// qDebug()<< "Updating local txt file as"<< __path.toLocalFile() << __bookletPath; - -// if(__bookletPath.isEmpty()) -// { -// qWarning()<< "File could not be saved. BooksSyncer::insertBookletLocal"; -// return false; -// } - - return this->m_db->update(OWL::TABLEMAP[OWL::TABLE::BOOKLETS], - FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::TITLE, - FMH::MODEL_KEY::MODIFIED, - FMH::MODEL_KEY::FAVORITE})), - QVariantMap {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::BOOK], booklet[FMH::MODEL_KEY::BOOK]}}); + const QUrl __path = FMH::fileDir(booklet[FMH::MODEL_KEY::URL]); + + if(!FMH::fileExists(__path)) + return false; + + if(booklet[FMH::MODEL_KEY::URL].isEmpty()) + return false; + + if(!OWL::saveNoteFile(booklet[FMH::MODEL_KEY::URL], booklet[FMH::MODEL_KEY::CONTENT].toUtf8())) + return false; + + return true; } bool BooksController::removeBooklet(const QString &id) { - return false; + const auto url = QUrl([&]() -> const QString { + const auto data = DB::getInstance ()->getDBData(QString("select url from booklets where id = '%1'").arg(id)); + return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL]; + }()); + + this->m_db->remove(OWL::TABLEMAP[OWL::TABLE::BOOKLETS_SYNC], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); + + FMStatic::removeFile(url); + + return this->m_db->remove(OWL::TABLEMAP[OWL::TABLE::BOOKLETS], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); } void BooksController::getBooks() { auto books = this->m_db->getDBData("select * from books"); for(auto &book : books) emit this->bookReady(book); emit this->booksReady(books); } void BooksController::getBooklets(const QString &book) { auto booklets = this->m_db->getDBData(QString("select * from booklets where book = '%1'").arg(book)); for(auto &booklet : booklets) { booklet[FMH::MODEL_KEY::CONTENT] = OWL::fileContentPreview (booklet[FMH::MODEL_KEY::URL]); emit this->bookletReady(booklet); } emit this->bookletsReady(booklets); } bool BooksController::insertBook(FMH::MODEL &book) { const auto __path = QUrl(OWL::BooksPath.toString()+book[FMH::MODEL_KEY::TITLE]); if(FMH::fileExists(__path)) { qWarning()<< "The directory for the book already exists. BooksSyncer::insertBookLocal" << book[FMH::MODEL_KEY::TITLE]; return false; } const auto url = QUrl(OWL::BooksPath.toString()+ book[FMH::MODEL_KEY::TITLE]); QDir dir(url.toLocalFile()); if(!dir.mkpath(".")) { qWarning() << "Could not create directory for the given book name. BooksSyncer::insertBookLocal" << book[FMH::MODEL_KEY::TITLE]; return false; } book[FMH::MODEL_KEY::URL] = url.toString(); emit this->bookInserted(book); return(this->m_db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKS], FMH::toMap(FMH::filterModel(book, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::URL})))); } bool BooksController::updateBook(const QString &id, const FMH::MODEL &book) { return false; } diff --git a/src/controllers/notes/notescontroller.cpp b/src/controllers/notes/notescontroller.cpp index d5955c1..baed830 100644 --- a/src/controllers/notes/notescontroller.cpp +++ b/src/controllers/notes/notescontroller.cpp @@ -1,137 +1,136 @@ #include "notescontroller.h" #include "owl.h" #include "db/db.h" #include #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #include "mauiaccounts.h" #else #include #include #include #include #endif Q_DECLARE_METATYPE (FMH::MODEL_LIST) Q_DECLARE_METATYPE (FMH::MODEL) NotesController::NotesController(QObject *parent) : QObject(parent) ,m_db(DB::getInstance()) { qRegisterMetaType("MODEL_LIST"); qRegisterMetaType("MODEL"); auto m_loader = new NotesLoader; m_loader->moveToThread(&m_worker); connect(&m_worker, &QThread::finished, m_loader, &QObject::deleteLater); connect(this, &NotesController::fetchNotes, m_loader, &NotesLoader::fetchNotes); connect(m_loader, &NotesLoader::noteReady, this, &NotesController::noteReady); connect(m_loader, &NotesLoader::notesReady, this, &NotesController::notesReady); m_worker.start(); } NotesController::~NotesController() { m_worker.quit(); m_worker.wait(); } bool NotesController::insertNote(FMH::MODEL ¬e) { if(note.isEmpty()) { qWarning()<< "Could not insert note locally. The note is empty. NotesController::insertNote."; return false; } if((OWL::NotesPath.isLocalFile() && !FMH::fileExists(OWL::NotesPath)) || OWL::NotesPath.isEmpty() || !OWL::NotesPath.isValid()) { qWarning() << "The url destination is not valid or does not exists, therefore it could not be saved into a file" << OWL::NotesPath; qWarning()<< "File could not be saved. NotesController::insertNote."; return false; } note[FMH::MODEL_KEY::ID] = OWL::createId(); const auto url_ = QUrl(OWL::NotesPath.toString()+note[FMH::MODEL_KEY::ID]+note[FMH::MODEL_KEY::FORMAT]); if(!OWL::saveNoteFile(url_, note[FMH::MODEL_KEY::CONTENT].toUtf8())) return false; note[FMH::MODEL_KEY::URL] = url_.toString(); for(const auto &tg : note[FMH::MODEL_KEY::TAG].split(",", QString::SplitBehavior::SkipEmptyParts)) Tagging::getInstance()->tagUrl(url_.toString(), tg, note[FMH::MODEL_KEY::COLOR]); return(this->m_db->insert(OWL::TABLEMAP[OWL::TABLE::NOTES], FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::URL, FMH::MODEL_KEY::ID, FMH::MODEL_KEY::COLOR, FMH::MODEL_KEY::PIN})))); } bool NotesController::updateNote(FMH::MODEL ¬e, QString id) { if(note.isEmpty()) return false; if(!note[FMH::MODEL_KEY::TAG].isEmpty ()) Tagging::getInstance ()->updateUrlTags (note[FMH::MODEL_KEY::URL], note[FMH::MODEL_KEY::TAG].split (",")); if(note[FMH::MODEL_KEY::URL].isEmpty()) note[FMH::MODEL_KEY::URL] = [&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select url from notes where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL]; }(); if(note[FMH::MODEL_KEY::URL].isEmpty()) return false; if(!OWL::saveNoteFile(note[FMH::MODEL_KEY::URL], note[FMH::MODEL_KEY::CONTENT].toUtf8())) return false; const auto f_note = FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::COLOR, FMH::MODEL_KEY::PIN})); if(f_note.isEmpty()) return true; return this->m_db->update(OWL::TABLEMAP[OWL::TABLE::NOTES], f_note, QVariantMap {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); } bool NotesController::removeNote(const QString &id) { - const auto url = QUrl([&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select url from notes where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL]; }()); this->m_db->remove(OWL::TABLEMAP[OWL::TABLE::NOTES_SYNC], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); FMStatic::removeFile(url); return this->m_db->remove(OWL::TABLEMAP[OWL::TABLE::NOTES], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); } void NotesController::getNotes() { emit this->fetchNotes(this->m_db->getDBData("select * from notes")); } void NotesLoader::fetchNotes(FMH::MODEL_LIST notes) { for(auto ¬e : notes) { const auto url = QUrl(note[FMH::MODEL_KEY::URL]); const auto contentPreview = OWL::fileContentPreview (url); note[FMH::MODEL_KEY::CONTENT] = contentPreview; emit this->noteReady (note); } qDebug()<< "FINISHED FETCHING URLS"; emit this->notesReady (notes); } diff --git a/src/models/books/booklet.cpp b/src/models/books/booklet.cpp index 059122a..35c6d73 100644 --- a/src/models/books/booklet.cpp +++ b/src/models/books/booklet.cpp @@ -1,133 +1,130 @@ #include "booklet.h" #include "bookssyncer.h" #include "nextnote.h" Booklet::Booklet(BooksSyncer *_syncer, QObject *parent) : MauiList(parent), syncer(_syncer) { connect(this->syncer, &BooksSyncer::bookletReady,this, &Booklet::appendBooklet); - connect(this->syncer, &BooksSyncer::bookletInserted,this, &Booklet::appendBooklet); + connect(this->syncer, &BooksSyncer::bookletInserted,[&](FMH::MODEL item, STATE state) + { + if(state.type == STATE::TYPE::LOCAL) + this->appendBooklet(item); + }); } FMH::MODEL_LIST Booklet::items() const { return this->m_list; } void Booklet::setSortBy(const Booklet::SORTBY &sort) { } Booklet::SORTBY Booklet::getSortBy() const { return this->sort; } void Booklet::setOrder(const Booklet::ORDER &order) { } Booklet::ORDER Booklet::getOrder() const { return this->order; } QString Booklet::getBook() const { return m_book; } void Booklet::setBook(const QString &book) //book id title { if (m_book == book) return; this->setBookTitle(book); m_book = book; this->clear(); this->syncer->getBooklets(this->m_book); emit bookChanged(m_book); } void Booklet::insert(const QVariantMap &data) { auto booklet = FMH::toModel(data); this->syncer->insertBooklet(this->m_book, booklet); } void Booklet::update(const QVariantMap &data, const int &index) { qDebug()<< "Trying to udpate a booklet" << data << index; if(index < 0 || index >= this->m_list.size()) return; - auto newData = this->m_list[index]; - QVector roles; - for(const auto &key : data.keys()) - if(newData[FMH::MODEL_NAME_KEY[key]] != data[key].toString()) - { - newData[FMH::MODEL_NAME_KEY[key]] = data[key].toString(); - roles << FMH::MODEL_NAME_KEY[key]; - } - - this->m_list[index] = newData; - - newData[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(Qt::TextDate); -// this->syncer->updateBooklet(newData[FMH::MODEL_KEY::ID], this->m_book, newData); - - emit this->updateModel(index, roles); + this->m_list[index] = this->m_list[index].unite(FMH::toModel(data)); + this->syncer->updateBooklet(this->m_list[index][FMH::MODEL_KEY::ID], this->m_book, this->m_list[index]); } void Booklet::remove(const int &index) { + qDebug()<< "Trying to remove a booklet" << index; + if(index < 0 || index >= this->m_list.size()) + return; + emit this->preItemRemoved(index); + this->syncer->removeBooklet(this->m_list.takeAt(index)[FMH::MODEL_KEY::ID]); + emit this->postItemRemoved(); } void Booklet::clear() { emit this->preListChanged(); this->m_list.clear(); emit this->postListChanged(); } void Booklet::sortList() { } void Booklet::appendBooklet(FMH::MODEL booklet) { emit this->preItemAppended(); booklet = booklet.unite(FMH::getFileInfoModel(booklet[FMH::MODEL_KEY::URL])); booklet[FMH::MODEL_KEY::TITLE] = [&]() { const auto lines = booklet[FMH::MODEL_KEY::CONTENT].split("\n"); return lines.isEmpty() ? QString() : lines.first().trimmed(); }(); this->m_list << booklet; - emit this->preItemAppended(); + emit this->postItemAppended(); } void Booklet::setBookTitle(const QString &title) { if (m_bookTitle == title) return; m_bookTitle = title; emit bookTitleChanged(m_bookTitle); } QVariantMap Booklet::get(const int &index) const { if(index >= this->m_list.size() || index < 0) return QVariantMap(); return FMH::toMap(this->m_list.at(index)); } diff --git a/src/providers/nextnote.cpp b/src/providers/nextnote.cpp index ee9841c..1c04193 100644 --- a/src/providers/nextnote.cpp +++ b/src/providers/nextnote.cpp @@ -1,360 +1,362 @@ #include "nextnote.h" #include #include #include #include #include #ifdef STATIC_MAUIKIT #include "fm.h" #else #include #endif const QString NextNote::API = QStringLiteral("https://PROVIDER/index.php/apps/notes/api/v0.2/"); static const inline QNetworkRequest formRequest(const QUrl &url, const QString &user, const QString &password) { if(!url.isValid() && !user.isEmpty() && !password.isEmpty()) return QNetworkRequest(); const QString concatenated = user + ":" + password; const QByteArray data = concatenated.toLocal8Bit().toBase64(); const QString headerData = "Basic " + data; // QVariantMap headers // { // {"Authorization", headerData.toLocal8Bit()}, // {QString::number(QNetworkRequest::ContentTypeHeader),"application/json"} // }; QNetworkRequest request; request.setUrl(QUrl(url)); request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json"); request.setRawHeader(QString("Authorization").toLocal8Bit(), headerData.toLocal8Bit()); return request; } NextNote::NextNote(QObject *parent) : AbstractNotesProvider(parent) { } NextNote::~NextNote() { } void NextNote::getNote(const QString &id) { auto url = QString(NextNote::API+"%1%2").replace("PROVIDER", this->m_provider).arg("notes/", id); QString concatenated = this->m_user + ":" + this->m_password; QByteArray data = concatenated.toLocal8Bit().toBase64(); QString headerData = "Basic " + data; QMap header {{"Authorization", headerData.toLocal8Bit()}}; auto downloader = new FMH::Downloader; connect(downloader, &FMH::Downloader::dataReady, [=](QByteArray array) { const auto notes = this->parseNotes(array); emit this->noteReady(notes.isEmpty() ? FMH::MODEL() : notes.first()); downloader->deleteLater(); }); downloader->getArray(url, header); } void NextNote::getBooklet(const QString &id) { } void NextNote::sendNotes(QByteArray array) { // emit this->notesReady(notes); } void NextNote::getNotes() { auto url = NextNote::formatUrl(this->m_user, this->m_password, this->m_provider)+"notes"; QString concatenated = this->m_user + ":" + this->m_password; QByteArray data = concatenated.toLocal8Bit().toBase64(); QString headerData = "Basic " + data; QMap header {{"Authorization", headerData.toLocal8Bit()}}; const auto downloader = new FMH::Downloader; connect(downloader, &FMH::Downloader::dataReady, [=](QByteArray array) { //exclude notes that have its own category FMH::MODEL_LIST notes; for(const auto &data : this->parseNotes(array)) { auto item = data; item[FMH::MODEL_KEY::STAMP] = item[FMH::MODEL_KEY::ID]; item[FMH::MODEL_KEY::USER] = this->user (); item[FMH::MODEL_KEY::SERVER] = this->provider (); item[FMH::MODEL_KEY::FORMAT] = ".txt"; if(item[FMH::MODEL_KEY::CATEGORY].isEmpty() || item[FMH::MODEL_KEY::CATEGORY].isNull()) notes << item; } emit this->notesReady(notes); downloader->deleteLater(); }); downloader->getArray(url, header); } void NextNote::getBooklets() { auto url = NextNote::formatUrl(this->m_user, this->m_password, this->m_provider)+"notes"; QString concatenated = this->m_user + ":" + this->m_password; QByteArray data = concatenated.toLocal8Bit().toBase64(); QString headerData = "Basic " + data; QMap header {{"Authorization", headerData.toLocal8Bit()}}; const auto downloader = new FMH::Downloader; connect(downloader, &FMH::Downloader::dataReady, [=](QByteArray array) { //exclude notes that do not have their own category FMH::MODEL_LIST booklets; for(const auto &data : this->parseNotes(array)) { auto item = data; item[FMH::MODEL_KEY::STAMP] = item[FMH::MODEL_KEY::ID]; item[FMH::MODEL_KEY::USER] = this->user (); item[FMH::MODEL_KEY::SERVER] = this->provider (); item[FMH::MODEL_KEY::FORMAT] = ".txt"; if(!item[FMH::MODEL_KEY::CATEGORY].isEmpty() && !item[FMH::MODEL_KEY::CATEGORY].isNull()) booklets << item; } emit this->bookletsReady(booklets); downloader->deleteLater(); }); downloader->getArray(url, header); } void NextNote::insertNote(const FMH::MODEL ¬e) { QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::FAVORITE}))).toJson(); qDebug() << "UPLOADING NEW NOT" << QVariant(payload).toString(); const auto url = QString(NextNote::API+"%1").replace("PROVIDER", this->m_provider).arg("notes"); const auto request = formRequest(url, this->m_user, this->m_password); auto restclient = new QNetworkAccessManager; //constructor QNetworkReply *reply = restclient->post(request,payload); connect(reply, &QNetworkReply::finished, [=, __note = note]() { qDebug() << "Note insertyion finished?"; const auto notes = this->parseNotes(reply->readAll()); emit this->noteInserted([&]() -> FMH::MODEL { FMH::MODEL note; if(!notes.isEmpty()) { note = notes.first(); note[FMH::MODEL_KEY::STAMP] = note[FMH::MODEL_KEY::ID]; //adds the id of the uploaded note as a stamp note[FMH::MODEL_KEY::ID] = __note[FMH::MODEL_KEY::ID]; //adds the url of the original local note note[FMH::MODEL_KEY::SERVER] = this->m_provider; //adds the provider server address note[FMH::MODEL_KEY::USER] = this->m_user; //adds the user name } return note; }()); reply->deleteLater (); restclient->deleteLater(); }); } void NextNote::insertBooklet(const FMH::MODEL &booklet) { - QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(booklet)).toJson(); + QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::FAVORITE, FMH::MODEL_KEY::CATEGORY}))).toJson(); qDebug() << "UPLOADING NEW BOOKLET" << QVariant(payload).toString(); const auto url = QString(NextNote::API+"%1").replace("PROVIDER", this->m_provider).arg("notes"); const auto request = formRequest(url, this->m_user, this->m_password); auto restclient = new QNetworkAccessManager; //constructor QNetworkReply *reply = restclient->post(request,payload); connect(reply, &QNetworkReply::finished, [=, __booklet = booklet]() { qDebug() << "Note insertyion finished?"; - const auto notes = this->parseNotes(reply->readAll()); + const auto booklets = this->parseNotes(reply->readAll()); emit this->bookletInserted([&]() -> FMH::MODEL { - FMH::MODEL note; - if(!notes.isEmpty()) + FMH::MODEL p_booklet; + if(!booklets.isEmpty()) { - note = notes.first(); - note[FMH::MODEL_KEY::STAMP] = note[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp - note[FMH::MODEL_KEY::ID] = __booklet[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp - note[FMH::MODEL_KEY::SERVER] = this->m_provider; //adds the provider server address - note[FMH::MODEL_KEY::USER] = this->m_user; //adds the user name + p_booklet = booklets.first(); + p_booklet[FMH::MODEL_KEY::STAMP] = p_booklet[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp + p_booklet[FMH::MODEL_KEY::ID] = __booklet[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp + p_booklet[FMH::MODEL_KEY::SERVER] = this->m_provider; //adds the provider server address + p_booklet[FMH::MODEL_KEY::USER] = this->m_user; //adds the user name } - return note; + return p_booklet; }()); restclient->deleteLater(); + reply->deleteLater(); }); } void NextNote::updateNote(const QString &id, const FMH::MODEL ¬e) { if(id.isEmpty() || note.isEmpty()) { qWarning()<< "The id or note are empty. Can not proceed. NextNote::update"; return; } QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::FAVORITE, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::CATEGORY}))).toJson(); qDebug() << "UPDATING NOTE" << QVariant(payload).toString(); const auto url = QString(NextNote::API+"%1%2").replace("PROVIDER", this->m_provider).arg("notes/", id); qDebug()<< "tryiong to update note" << url; const auto request = formRequest(url, this->m_user, this->m_password); auto restclient = new QNetworkAccessManager; //constructor QNetworkReply *reply = restclient->put(request, payload); connect(reply, &QNetworkReply::finished, [=, __note = note]() { qDebug() << "Note update finished?" << reply->errorString(); const auto notes = this->parseNotes(reply->readAll()); emit this->noteUpdated([&]() -> FMH::MODEL { FMH::MODEL note; if(notes.isEmpty()) return note; note = notes.first(); note[FMH::MODEL_KEY::STAMP] = note[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp note[FMH::MODEL_KEY::ID] = __note[FMH::MODEL_KEY::ID]; //adds the id of the local note as a stamp note[FMH::MODEL_KEY::SERVER] = this->m_provider; //adds the provider server address note[FMH::MODEL_KEY::USER] = this->m_user; //adds the user name return note; }()); restclient->deleteLater(); + reply->deleteLater(); }); } void NextNote::updateBooklet(const QString &id, const FMH::MODEL &booklet) { if(id.isEmpty() || booklet.isEmpty()) { qWarning()<< "The id or note are empty. Can not proceed. NextNote::update"; return; } QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::CONTENT, - FMH::MODEL_KEY::FAVORITE, - FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::CATEGORY}))).toJson(); qDebug() << "UPDATING BOOKLET" << QVariant(payload).toString(); const auto url = QString(NextNote::API+"%1%2").replace("PROVIDER", this->m_provider).arg("notes/", id); qDebug()<< "tryiong to update note" << url; const auto request = formRequest(url, this->m_user, this->m_password); auto restclient = new QNetworkAccessManager; //constructor QNetworkReply *reply = restclient->put(request, payload); connect(reply, &QNetworkReply::finished, [=, __booklet = booklet]() { qDebug() << "Note update finished?" << reply->errorString(); const auto booklets = this->parseNotes(reply->readAll()); emit this->bookletUpdated([&]() -> FMH::MODEL { FMH::MODEL booklet; if(booklets.isEmpty()) return booklet; booklet = booklets.first(); booklet[FMH::MODEL_KEY::STAMP] = booklet[FMH::MODEL_KEY::ID]; //adds the stamp to the local note form the remote id - booklet[FMH::MODEL_KEY::ID] = __booklet[FMH::MODEL_KEY::TITLE]; //adds back the id of the local booklet + booklet[FMH::MODEL_KEY::ID] = __booklet[FMH::MODEL_KEY::ID]; //adds back the id of the local booklet booklet[FMH::MODEL_KEY::SERVER] = this->m_provider; //adds the provider server address booklet[FMH::MODEL_KEY::USER] = this->m_user; //adds the user name return booklet; }()); restclient->deleteLater(); + reply->deleteLater(); }); } void NextNote::removeNote(const QString &id) { if(id.isEmpty()) { qWarning()<< "The id is empty. Can not proceed. NextNote::remove"; return; } const auto url = QString(NextNote::API+"%1%2").replace("PROVIDER", this->m_provider).arg("notes/", id); const auto request = formRequest(url, this->m_user, this->m_password); qDebug()<< "trying to remove nextnote <<" << url; auto restclient = new QNetworkAccessManager; //constructor QNetworkReply *reply = restclient->deleteResource(request); connect(reply, &QNetworkReply::finished, [=]() { qDebug() << "Note remove finished?" << reply->errorString(); emit this->noteRemoved(); restclient->deleteLater(); + reply->deleteLater(); }); } void NextNote::removeBooklet(const QString &id) { - + this->removeNote(id); } const QString NextNote::formatUrl(const QString &user, const QString &password, const QString &provider) { auto url = NextNote::API; url.replace("USER", user); url.replace("PASSWORD", password); url.replace("PROVIDER", provider); return url; } const FMH::MODEL_LIST NextNote::parseNotes(const QByteArray &array) { FMH::MODEL_LIST res; // qDebug()<< "trying to parse notes" << array; QJsonParseError jsonParseError; QJsonDocument jsonResponse = QJsonDocument::fromJson(static_cast(array).toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qDebug()<< "ERROR PARSING" << array; return res; } const auto data = jsonResponse.toVariant(); if(data.isNull() || !data.isValid()) return res; if(!data.toList().isEmpty()) res << FMH::toModelList(data.toList()); else res << FMH::toModel(data.toMap()); return res; } diff --git a/src/syncing/bookssyncer.cpp b/src/syncing/bookssyncer.cpp index 001fb19..4c0d82a 100644 --- a/src/syncing/bookssyncer.cpp +++ b/src/syncing/bookssyncer.cpp @@ -1,224 +1,247 @@ #include "bookssyncer.h" #include "db/db.h" #include "controllers/books/bookscontroller.h" #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #include "mauiaccounts.h" #else #include #include #include #include #endif BooksSyncer::BooksSyncer(QObject *parent) : Syncer(parent), tag(Tagging::getInstance()), db(DB::getInstance()), m_booksController(new BooksController(this)) //local handler for notes { connect(MauiAccounts::instance(), &MauiAccounts::currentAccountChanged, [&](QVariantMap) { this->getRemoteBooks(); }); connect( this->m_booksController, &BooksController::bookReady, this, &BooksSyncer::bookReady); connect( this->m_booksController, &BooksController::bookInserted, [this](FMH::MODEL book) { emit this->bookInserted(book, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Book inserted locally sucessfully"}); }); connect( this->m_booksController, &BooksController::bookletReady, this, &BooksSyncer::bookletReady); } void BooksSyncer::getBooks() { this->getLocalBooks(); this->getRemoteBooks(); } void BooksSyncer::getRemoteBooks() { if(this->validProvider()) this->getProvider().getBooklets(); else qWarning()<< "Credentials are missing to get booklets or the provider has not been set"; } void BooksSyncer::getLocalBooks() { this->m_booksController->getBooks(); } void BooksSyncer::insertBook(FMH::MODEL &book) { if(!this->m_booksController->insertBook(book)) { qWarning()<< "Could not insert Book, BooksSyncer::insertBook"; return; } } void BooksSyncer::getBooklet(const QString &id) { this->m_booksController->getBooklet(id); } void BooksSyncer::getBooklets(const QString &book) { this->m_booksController->getBooklets(book); } void BooksSyncer::updateBooklet(const QString &id, const QString &bookId, FMH::MODEL &booklet) { if(!this->m_booksController->updateBooklet(booklet, id)) { qWarning()<< "The booklet could not be updated locally, " "therefore it was not attempted to update it on the remote server provider, " "even if it existed."; return; } // to update remote booklet we need to pass the stamp as the id const auto stamp = BooksSyncer::bookletStampFromId(id); qDebug()<< "booklet stamp from id" << stamp; if(!stamp.isEmpty()) { booklet[FMH::MODEL_KEY::CATEGORY] = bookId; if(this->validProvider()) - this->getProvider().updateBooklet(id, booklet); + this->getProvider().updateBooklet(stamp, booklet); } emit this->bookletUpdated(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet updated locally on the DB"}); } +void BooksSyncer::removeBooklet(const QString &id) +{ + //to remove the remote booklet we need to pass the stamp as the id, + //and before removing the note locally we need to retireved first + + const auto stamp = BooksSyncer::bookletStampFromId(id); + if(!this->m_booksController->removeBooklet(id)) + { + qWarning()<< "The note could not be inserted locally, " + "therefore it was not attempted to insert it to the remote provider server, " + "even if it existed."; + return; + } + + if(!stamp.isEmpty()) + { + if(this->validProvider()) + this->getProvider().removeBooklet(stamp); + } + + emit this->bookletRemoved(FMH::MODEL(), {STATE::TYPE::LOCAL, STATE::STATUS::OK, "The booklet has been removed from the local DB"}); +} + void BooksSyncer::insertBooklet(const QString &bookId, FMH::MODEL &booklet) { if(!m_booksController->insertBooklet(bookId, booklet)) { qWarning()<< "Could not insert Booklet, BooksSyncer::insertBooklet"; return; } + booklet[FMH::MODEL_KEY::CATEGORY] = bookId; if(this->validProvider()) this->getProvider().insertBooklet(booklet); emit this->bookletInserted(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet inserted locally sucessfully"}); } const QString BooksSyncer::bookletIdFromStamp(const QString &provider, const QString &stamp) { return [&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select id from booklets_sync where server = '%1' AND stamp = '%2'").arg(provider, stamp)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::ID]; }(); } const QString BooksSyncer::bookletStampFromId(const QString &id) { return [&]() ->const QString { const auto data = DB::getInstance ()->getDBData(QString("select stamp from booklets_sync where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::STAMP]; }(); } void BooksSyncer::setConections() { - connect(&this->getProvider(), &AbstractNotesProvider::bookletInserted, [&](FMH::MODEL booklet) { - qDebug()<< "STAMP OF THE NEWLY INSERTED BOOKLET" << booklet[FMH::MODEL_KEY::ID] << booklet; + qDebug()<< "STAMP OF THE NEWLY INSERTED BOOKLET" << booklet[FMH::MODEL_KEY::ID] << booklet[FMH::MODEL_KEY::STAMP] << booklet; this->db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKLETS_SYNC], FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::ID, FMH::MODEL_KEY::STAMP, FMH::MODEL_KEY::USER, FMH::MODEL_KEY::SERVER}))); emit this->bookletInserted(booklet, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Booklet inserted on server provider"}); }); connect(&this->getProvider(), &AbstractNotesProvider::bookletsReady, [&](FMH::MODEL_LIST booklets) { // qDebug()<< "SERVER NOETS READY "<< notes; //if there are no notes in the provider server, then just return if(booklets.isEmpty()) return; // there might be two case scenarios: // the booklet exists locally in the db, so it needs to be updated with the server version // the booklet does not exists locally, so it needs to be inserted into the db for(auto &booklet : booklets) { qDebug()<< "Booklets READY << " << booklet[FMH::MODEL_KEY::TITLE] << booklet[FMH::MODEL_KEY::CATEGORY]; const auto id = BooksSyncer::bookletIdFromStamp(this->getProvider().provider(), booklet[FMH::MODEL_KEY::STAMP]); //the id is actually the stamp id booklet[FMH::MODEL_KEY::ID] = id; // if the title is empty then the booklet does not exists, so insert the booklet into the local db if(id.isEmpty()) { //here insert the note into the db if(!this->m_booksController->insertBooklet(booklet[FMH::MODEL_KEY::CATEGORY], booklet)) { qWarning()<< "Remote booklet could not be inserted to the local storage"; continue; } this->db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKLETS_SYNC], FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::ID, FMH::MODEL_KEY::STAMP, FMH::MODEL_KEY::USER, FMH::MODEL_KEY::SERVER}))); emit this->bookletInserted(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet inserted on local db, from the server provider"}); }else { //the booklet exists in the db locally, so update it booklet[FMH::MODEL_KEY::BOOK] = booklet[FMH::MODEL_KEY::CATEGORY]; booklet[FMH::MODEL_KEY::URL] = [&]()-> QString { const auto data = this->db->getDBData(QString("select url from booklets where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL]; }(); qDebug()<< " trying to update local booklets with url" << booklet[FMH::MODEL_KEY::URL] << booklet[FMH::MODEL_KEY::BOOK] << booklet[FMH::MODEL_KEY::CONTENT] ; auto remoteDate = QDateTime::fromSecsSinceEpoch(booklet[FMH::MODEL_KEY::MODIFIED].toInt()); auto localDate = QFileInfo(QUrl(booklet[FMH::MODEL_KEY::URL]).toLocalFile()).lastModified(); - if(remoteDate <= localDate) - continue; +// if(remoteDate <= localDate) +// continue; - if(! this->m_booksController->updateBook(id, booklet)) + if(! this->m_booksController->updateBooklet(booklet, id)) continue; emit this->bookletUpdated(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet updated on local db, from the server provider"}); } } emit this->booksReady(this->collectAllBooks()); //??? }); connect(&this->getProvider(), &AbstractNotesProvider::bookletUpdated, [&](FMH::MODEL booklet) { const auto id = BooksSyncer::bookletIdFromStamp(this->getProvider().provider(), booklet[FMH::MODEL_KEY::ID]); if(!booklet.isEmpty()) { booklet[FMH::MODEL_KEY::ID] = id; booklet[FMH::MODEL_KEY::BOOK] = booklet[FMH::MODEL_KEY::CATEGORY]; booklet[FMH::MODEL_KEY::URL] = [&]()-> QString { const auto data = this->db->getDBData(QString("select url from booklets where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL]; }(); this->m_booksController->updateBooklet(booklet, id); } emit this->bookletUpdated(booklet, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Booklet updated on server provider"}); }); } const FMH::MODEL_LIST BooksSyncer::collectAllBooks() { // return this->db->getDBData("select b.*, count(distinct bl.id) as count from books b inner join booklets bl on bl.book = b.id"); return this->db->getDBData("select * from books"); } diff --git a/src/syncing/notessyncer.cpp b/src/syncing/notessyncer.cpp index 4f42a22..6c64647 100644 --- a/src/syncing/notessyncer.cpp +++ b/src/syncing/notessyncer.cpp @@ -1,209 +1,205 @@ #include "notessyncer.h" #include "db/db.h" #include "controllers/notes/notescontroller.h" #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #include "mauiaccounts.h" #else #include #include #include #include #endif NotesSyncer::NotesSyncer(QObject *parent) : Syncer(parent), tag(Tagging::getInstance()), db(DB::getInstance()), m_notesController(new NotesController(this)) //local handler for notes { connect(MauiAccounts::instance(), &MauiAccounts::currentAccountChanged, [&](QVariantMap) { this->getRemoteNotes(); }); connect(this->m_notesController, &NotesController::noteReady, this, &NotesSyncer::noteReady); } void NotesSyncer::insertNote(FMH::MODEL ¬e) { if(!this->m_notesController->insertNote(note)) return; if(this->validProvider()) this->getProvider().insertNote(note); emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note saved locally"}); } void NotesSyncer::updateNote(QString id, FMH::MODEL ¬e) { if(!this->m_notesController->updateNote (note, id)) { qWarning()<< "The note could not be updated locally, " "therefore it was not attempted to update it on the remote server provider, " "even if it existed."; return; } //to update remote note we need to pass the stamp as the id const auto stamp = NotesSyncer::noteStampFromId(id); if(!stamp.isEmpty()) - this->updateNoteRemote(stamp, note); + { + if(this->validProvider()) + this->getProvider().updateNote(stamp, note); + } emit this->noteUpdated(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note updated on the DB locally"}); } void NotesSyncer::removeNote(const QString &id) { //to remove the remote note we need to pass the stamp as the id, //and before removing the note locally we need to retireved first const auto stamp = NotesSyncer::noteStampFromId(id); if(!this->m_notesController->removeNote(id)) { qWarning()<< "The note could not be inserted locally, " "therefore it was not attempted to insert it to the remote provider server, " "even if it existed."; return; } if(!stamp.isEmpty()) - this->removeNoteRemote(stamp); + { + if(this->validProvider()) + this->getProvider().removeNote(stamp); + } emit this->noteRemoved(FMH::MODEL(), {STATE::TYPE::LOCAL, STATE::STATUS::OK, "The note has been removed from the local DB"}); } void NotesSyncer::getNotes() { this->getLocalNotes(); this->getRemoteNotes(); } void NotesSyncer::getLocalNotes() { this->m_notesController->getNotes(); } void NotesSyncer::getRemoteNotes() { if(this->validProvider()) this->getProvider().getNotes(); else qWarning()<< "Failed to fetch online notes. Credentials are missing or the provider has not been set"; } const QString NotesSyncer::noteIdFromStamp(const QString &provider, const QString &stamp) { return [&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select id from notes_sync where server = '%1' AND stamp = '%2'").arg(provider, stamp)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::ID]; }(); } const QString NotesSyncer::noteStampFromId(const QString &id) { return [&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select stamp from notes_sync where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::STAMP]; }(); } void NotesSyncer::setConections() { connect(&this->getProvider(), &AbstractNotesProvider::noteInserted, [&](FMH::MODEL note) { qDebug()<< "STAMP ID OF THE NEWLY INSERTED NOTE" << note[FMH::MODEL_KEY::STAMP] << note; this->db->insert(OWL::TABLEMAP[OWL::TABLE::NOTES_SYNC], FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::ID, FMH::MODEL_KEY::STAMP, FMH::MODEL_KEY::USER, FMH::MODEL_KEY::SERVER}))); emit this->noteInserted(note, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Note inserted on the server provider"}); }); connect(&this->getProvider(), &AbstractNotesProvider::notesReady, [&](FMH::MODEL_LIST notes) { // qDebug()<< "SERVER NOETS READY "<< notes; //if there are no notes in the provider server, then just return if(notes.isEmpty()) return; // there might be two case scenarios: // the note exists locally in the db, so it needs to be updated with the server version // the note does not exists locally, so it needs to be inserted into the db for(auto ¬e : notes) { const auto id = NotesSyncer::noteIdFromStamp(this->getProvider().provider(), note[FMH::MODEL_KEY::STAMP]); note[FMH::MODEL_KEY::ID] = id; qDebug()<< "REMOTE NOTE MAPPED ID" << id << note[FMH::MODEL_KEY::STAMP]; // if the id is empty then the note does not exists, so the note is inserted locally if(id.isEmpty()) { if(!this->m_notesController->insertNote(note)) continue; this->db->insert(OWL::TABLEMAP[OWL::TABLE::NOTES_SYNC], FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::ID, FMH::MODEL_KEY::STAMP, FMH::MODEL_KEY::USER, FMH::MODEL_KEY::SERVER}))); emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note inserted on local db, from the server provider"}); }else { //the note does exists locally, so update it note[FMH::MODEL_KEY::URL] = [&]() -> const QString { const auto data = DB::getInstance ()->getDBData(QString("select url from notes where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::URL];}(); auto remoteDate = QDateTime::fromSecsSinceEpoch(note[FMH::MODEL_KEY::MODIFIED].toInt()); auto localDate = QFileInfo(QUrl(note[FMH::MODEL_KEY::URL]).toLocalFile()).lastModified(); qDebug()<< "UPDATING FROM REMOTE" << note[FMH::MODEL_KEY::URL] << localDate.secsTo(QDateTime::currentDateTime()) << remoteDate.secsTo(QDateTime::currentDateTime()); if(remoteDate <= localDate) continue; if(!this->m_notesController->updateNote (note, id)) continue; emit this->noteUpdated(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note updated on local db, from the server provider"}); } } }); connect(&this->getProvider(), &AbstractNotesProvider::noteUpdated, [&](FMH::MODEL note) { const auto id = NotesSyncer::noteIdFromStamp(this->getProvider().provider(), note[FMH::MODEL_KEY::STAMP]); note[FMH::MODEL_KEY::ID] = id; if(!note.isEmpty()) this->m_notesController->updateNote (note, id); emit this->noteUpdated(note, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Note updated on server provider"}); }); connect(&this->getProvider(), &AbstractNotesProvider::noteRemoved, [&]() { emit this->noteRemoved(FMH::MODEL(), {STATE::TYPE::REMOTE, STATE::STATUS::OK, "The note has been removed from the remove server provider"}); }); } -void NotesSyncer::updateNoteRemote(const QString &id, const FMH::MODEL ¬e) -{ - if(this->validProvider()) - this->getProvider().updateNote(id, note); -} -void NotesSyncer::removeNoteRemote(const QString &id) -{ - if(this->validProvider()) - this->getProvider().removeNote(id); -} diff --git a/src/syncing/notessyncer.h b/src/syncing/notessyncer.h index fde43e1..8ddeb23 100644 --- a/src/syncing/notessyncer.h +++ b/src/syncing/notessyncer.h @@ -1,129 +1,124 @@ #ifndef NOTESSYNCER_H #define NOTESSYNCER_H #include #include #ifdef STATIC_MAUIKIT #include "fmh.h" #else #include #endif /** * @brief The Syncer class * This interfaces between local storage and cloud * Its work is to try and keep thing synced and do the background work on updating notes * from local to cloud and viceversa. * This interface should be used to handle the whol offline and online work, * instead of manually inserting to the db or the cloud providers */ class DB; class NotesController; class Tagging; class NotesSyncer: public Syncer { Q_OBJECT public: explicit NotesSyncer(QObject *parent = nullptr); //// NOTES INTERFACES /// interfaces with the the notes from both, local and remote /** * @brief insertNote * saves a new note online and offline * The signal Syncer::noteInserted(FMH::MODEL, STATE) is emitted, * indicating the created note and the transaction resulting state * @param note * the note to be stored represented by FMH::MODEL */ void insertNote(FMH::MODEL ¬e); /** * @brief updateNote * Update online and offline an existing note. * The signal Syncer::noteUpdated(FMH::MODEL, STATE) is emitted, * indicating the updated note and the transaction resulting state * @param id * ID of the existing note * @param note * the new note contents represented by FMH::MODEL */ void updateNote(QString id, FMH::MODEL ¬e); /** * @brief removeNote * remove a note from online and offline storage * The signal Syncer::noteRemoved(FMH::MODEL, STATE) is emitted, * indicating the removed note and the transaction resulting state * @param id * ID of the exisiting note */ void removeNote(const QString &id); /** * @brief getNote * Retrieves an existing note, whether the note is located offline or online. * When the note is ready the signal Syncer::noteReady(FMH::MODEL) is emitted * @param id * ID of the exisiting note */ void getNote(const QString &id); /** * @brief getNotes * Retrieves all the notes, online and offline notes. * When the notes are ready the signal Syncer::notesReady(FMH::MODEL_LIST) is emitted. */ void getNotes(); void getLocalNotes(); void getRemoteNotes(); private: /** * @brief tag * Instance of the Maui project tag-ger. It adds tags to the abtract notes * For online tagging one could use the categories ? */ Tagging *tag; /** * @brief db * Instance to the data base storing the notes information and location, * offline and online. */ DB *db; NotesController *m_notesController; /** * @brief syncNote * Has the job to sync a note between the offline and online versions * @param id * ID of the note to be synced */ void syncNote(const QString &id); static const QString noteIdFromStamp(const QString &provider, const QString &stamp) ; static const QString noteStampFromId(const QString &id); void setConections() override final; - -protected: - void updateNoteRemote(const QString &id, const FMH::MODEL ¬e); - void removeNoteRemote(const QString &id); - signals: //FOR NOTES void noteInserted(FMH::MODEL note, STATE state); void noteUpdated(FMH::MODEL note, STATE state); void noteRemoved(FMH::MODEL note, STATE state); void noteReady(FMH::MODEL note); void notesReady(FMH::MODEL_LIST notes); public slots: }; #endif // NOTESSYNCER_H diff --git a/src/views/books/BookletPage.qml b/src/views/books/BookletPage.qml index 823470f..a6ea61c 100644 --- a/src/views/books/BookletPage.qml +++ b/src/views/books/BookletPage.qml @@ -1,186 +1,193 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Item { id: control property var currentBooklet : null signal exit() onCurrentBookletChanged: { editor.document.load(currentBooklet.url) _sidebar.title = currentBook.title } Maui.BaseModel { id: _bookletModel list: _booksList.booklet } Maui.Page { id: _page anchors.fill: parent title: currentBooklet.title - headBar.rightContent: ToolButton - { - icon.name: "document-save" - text: qsTr("Save") - onClicked: - { - currentBooklet.content = editor.text - currentBooklet.title = title.text - _booksList.booklet.update(currentBooklet, _listView.currentIndex) - } - } - headBar.leftContent: [ ToolButton { icon.name: "go-previous" onClicked: control.exit() }, Kirigami.Separator { + Layout.fillHeight: true }, ToolButton { icon.name: "view-calendar-list" text: qsTr("Chapters") - onClicked: - { - console.log(_layout.visibleChildren, _layout.visibleChildren) - _layout.currentIndex = 0 - } - + onClicked: checked ? _layout.currentIndex = 0 : _layout.currentIndex = 1 checked: _layout.firstVisibleItem === _sidebar + } ] Maui.Holder { id: _holder visible: !_listView.count || !currentBooklet emoji: "qrc:/document-edit.svg" emojiSize: Maui.Style.iconSizes.huge isMask: false title : qsTr("Nothing to edit!") body: qsTr("Select a chapter or create a new one") } Kirigami.PageRow { id: _layout anchors.fill: parent initialPage: [_sidebar, editor] interactive: true defaultColumnWidth: Kirigami.Units.gridUnit * 11 Maui.Editor { id: editor visible: !_holder.visible footBar.visible: false + document.autoReload: true + + headBar.rightContent: [ + + ToolButton + { + icon.name: "document-save" + text: qsTr("Save") + onClicked: + { + currentBooklet.content = editor.text + _booksList.booklet.update(currentBooklet, _listView.currentIndex) + } + }, + ToolButton + { + icon.name: "edit-delete" + icon.color: Kirigami.Theme.negativeTextColor + onClicked: + { + _booksList.booklet.remove(_listView.currentIndex) + } + } + ] } Maui.Page { id: _sidebar headBar.visible: true headBar.rightContent: ToolButton { icon.name: "view-sort" } background: Rectangle { color: "transparent" } Maui.Holder { anchors.margins: Maui.Style.space.huge visible: !_listView.count emoji: "qrc:/document-edit.svg" emojiSize: Maui.Style.iconSizes.huge isMask: false title : qsTr("This book is empty!") body: qsTr("Start by creating a new chapter for your book by clicking the + icon") } ListView { id: _listView anchors.fill: parent model: _bookletModel clip: true onCountChanged: { _listView.currentIndex = count-1 control.currentBooklet = _booksList.booklet.get(_listView.currentIndex) } + footerPositioning: ListView.OverlayFooter + footer: Button + { + text: qsTr("New chapter") + onClicked: _newChapter.open() + + height: Maui.Style.rowHeight + width: parent.width + Kirigami.Theme.backgroundColor: Kirigami.Theme.positiveTextColor + Kirigami.Theme.textColor: "white" + } + delegate: Maui.LabelDelegate { id: _delegate width: parent.width label: index+1 + " - " + model.title Connections { target:_delegate onClicked: { _listView.currentIndex = index currentBooklet = _booksList.booklet.get(index) } } } } - - Maui.FloatingButton - { - z: 999 - anchors.bottom: parent.bottom - anchors.margins: height - anchors.horizontalCenter: parent.horizontalCenter - height: Maui.Style.toolBarHeight - width: height - Kirigami.Theme.backgroundColor: Kirigami.Theme.positiveTextColor - icon.name: "list-add" - icon.color: "white" - onClicked: _newChapter.open() - } } } Maui.Dialog { id: _newChapter title: qsTr("New Chapter") message: qsTr("Create a new chapter for your current book. Give it a title") entryField: true page.padding: Maui.Style.space.huge onAccepted: { - _booksList.booklet.insert({title: textEntry.text}) + _booksList.booklet.insert({content: textEntry.text}) } } } }