diff --git a/main.cpp b/main.cpp index a00ca28..304b194 100644 --- a/main.cpp +++ b/main.cpp @@ -1,71 +1,73 @@ #include #include #include #include #ifdef STATIC_KIRIGAMI #include "3rdparty/kirigami/src/kirigamiplugin.h" #endif #ifdef STATIC_MAUIKIT #include "3rdparty/mauikit/src/mauikit.h" #endif #ifdef Q_OS_ANDROID #include #include #include #else #include #include #endif #include "./src/buho.h" #include "./src/linker.h" #include "./src/models/notes/notes.h" +#include "./src/models/books/books.h" #include "./src/models/links/links.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #ifdef Q_OS_ANDROID QGuiApplication app(argc, argv); QtWebView::initialize(); #else QApplication app(argc, argv); // QtWebEngine::initialize(); #endif app.setApplicationName(OWL::App); app.setApplicationVersion(OWL::version); app.setApplicationDisplayName(OWL::App); app.setWindowIcon(QIcon(":/buho.png")); #ifdef STATIC_KIRIGAMI KirigamiPlugin::getInstance().registerTypes(); #endif #ifdef STATIC_MAUIKIT MauiKit::getInstance().registerTypes(); #endif Buho owl; QQmlApplicationEngine engine; auto context = engine.rootContext(); context->setContextProperty("owl", &owl); Linker linker; context->setContextProperty("linker", &linker); qmlRegisterType("Notes", 1, 0, "Notes"); + qmlRegisterType("Books", 1, 0, "Books"); qmlRegisterType("Links", 1, 0, "Links"); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); } diff --git a/main.qml b/main.qml index 0030110..c112ba1 100644 --- a/main.qml +++ b/main.qml @@ -1,250 +1,254 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Layouts 1.3 import "src/widgets" import "src/views/notes" import "src/views/links" import "src/views/books" Maui.ApplicationWindow { id: root title: qsTr("Buho") /***** PROPS *****/ // altToolBars: false /**** BRANDING COLORS ****/ // menuButton.colorScheme.highlightColor: accentColor // searchButton.colorScheme.highlightColor: accentColor // headBarBGColor: viewBackgroundColor // headBarFGColor: textColor // accentColor : "#ff9494" // highlightColor: accentColor // altColorText : "white"/*Qt.darker(accentColor, 2.5)*/ showAccounts: true about.appDescription: qsTr("Buho allows you to take quick notes, collect links and take long notes organized by chapters.") about.appIcon: "qrc:/buho.svg" property int currentView : views.notes readonly property var views : ({ notes: 0, links: 1, books: 2, tags: 3, search: 4 }) property color headBarTint : Qt.lighter(headBarBGColor, 1.25) // headBarFGColor: "red" headBar.middleContent: Kirigami.ActionToolBar { display: isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly position: ToolBar.Header Layout.fillWidth: true actions: [ Kirigami.Action { onTriggered: currentView = views.notes // icon.color: checked ? accentColor : textColor // Kirigami.Theme.highlightColor: accentColor // Kirigami.Theme.textColor: accentColor icon.name: "view-pim-notes" text: qsTr("Notes") checked: currentView === views.notes }, Kirigami.Action { onTriggered: currentView = views.links // icon.color: checked ? accentColor : textColor // Kirigami.Theme.highlightColor: accentColor icon.name: "view-pim-news" text: qsTr("Links") checked: currentView === views.links }, Kirigami.Action { onTriggered: currentView = views.books // icon.color: checked? accentColor : textColor // Kirigami.Theme.highlightColor: accentColor icon.name: "view-pim-journal" text: qsTr("Books") checked: currentView === views.books }, Kirigami.Action { // icon.color: checked ? accentColor : textColor // Kirigami.Theme.highlightColor: accentColor icon.name: "tag" text: qsTr("Tags") checked: currentView === views.tags } ] } // headBar.colorScheme.borderColor: Qt.darker(accentColor, 1.4) headBar.drawBorder: false // headBar.implicitHeight: toolBarHeight * 1.5 Rectangle { z: 999 anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: toolBarHeight anchors.bottomMargin: toolBarHeight height: toolBarHeight width: height color: Kirigami.Theme.highlightColor radius: radiusV Maui.PieButton { id: addButton anchors.fill : parent icon.name: "list-add" icon.color: Kirigami.Theme.highlightedTextColor barHeight: parent.height alignment: Qt.AlignLeft content: [ ToolButton { icon.name: "view-pim-notes" onClicked: newNote() }, ToolButton { icon.name: "view-pim-news" onClicked: newLink() }, ToolButton { icon.name: "view-pim-journal" onClicked: newBook() } ] } } Maui.SyncDialog { id: syncDialog } mainMenu: [ MenuItem { text: qsTr("Syncing") onTriggered: syncDialog.open() } ] // /***** COMPONENTS *****/ NewNoteDialog { id: newNoteDialog onNoteSaved: notesView.list.insert(note) } NewNoteDialog { id: editNote onNoteSaved: notesView.list.update(note, notesView.currentIndex) } NewLinkDialog { id: newLinkDialog onLinkSaved: linksView.list.insert(link) } NewBookDialog { id: newBookDialog - onBookSaved: console.log("saving new vbook", title) + onBookSaved: + { + if(title && title.length) + booksView.list.insert({title: title}) + } } // /***** VIEWS *****/ SwipeView { id: swipeView anchors.fill: parent currentIndex: currentView onCurrentIndexChanged: { currentView = currentIndex // if(currentView === views.notes) // accentColor = "#ff9494" // else if(currentView === views.links) // accentColor = "#25affb" // else if(currentView === views.books) // accentColor = "#6bc5a5" } interactive: isMobile NotesView { id: notesView onNoteClicked: setNote(note) } LinksView { id: linksView onLinkClicked: previewLink(link) } BooksView { id: booksView } } function newNote() { currentView = views.notes newNoteDialog.open() } function newLink() { currentView = views.links newLinkDialog.open() } function newBook() { currentView = views.books newBookDialog.open() } function setNote(note) { var tags = notesView.list.getTags(notesView.currentIndex) note.tags = tags notesView.currentNote = note editNote.fill(note) } function previewLink(link) { var tags = linksView.list.getTags(linksView.currentIndex) link.tags = tags linksView.previewer.show(link) } } diff --git a/src/db/script.sql b/src/db/script.sql index e6c71c0..125ad44 100644 --- a/src/db/script.sql +++ b/src/db/script.sql @@ -1,38 +1,52 @@ CREATE TABLE IF NOT EXISTS NOTES ( id TEXT PRIMARY KEY, title TEXT, url TEXT, color TEXT, favorite INT, pin INT, adddate DATE, modified DATE ); CREATE TABLE IF NOT EXISTS NOTES_SYNC ( id TEXT, server TEXT, user TEXT, stamp TEXT, PRIMARY KEY(server, stamp) FOREIGN KEY(id) REFERENCES NOTES(id) ); CREATE TABLE IF NOT EXISTS BOOKS ( -url TEXT PRIMARY KEY, +id TEXT PRIMARY KEY, +url TEXT, +title TEXT NOT NULL, +favorite INT, +adddate DATE, +modified DATE +); + +CREATE TABLE IF NOT EXISTS BOOKLETS ( +id TEXT, +book TEXT, +url TEXT, title TEXT NOT NULL, -favorite INTEGER NOT NULL, -adddate DATE +adddate DATE, +modified DATE +PRIMARY KEY(id, book) +FOREIGN KEY(book) REFERENCES BOOKS(id) ); + CREATE TABLE IF NOT EXISTS LINKS ( url TEXT PRIMARY KEY, title TEXT, preview TEXT, color TEXT, favorite INT, pin INT, adddate DATE, modified DATE ); diff --git a/src/models/books/books.cpp b/src/models/books/books.cpp index 48be117..faaabf3 100644 --- a/src/models/books/books.cpp +++ b/src/models/books/books.cpp @@ -1,75 +1,86 @@ #include "books.h" #include "syncer.h" #include "nextnote.h" Books::Books(QObject *parent) : MauiList(parent), syncer(new Syncer(this)) { this->syncer->setProvider(new NextNote); + connect(syncer, &Syncer::booksReady, [&](FMH::MODEL_LIST books) + { + emit this->preListChanged(); + this->m_list = books; + qDebug()<< "ALL THE BOOKS ARE < "<< this->m_list; + emit this->postListChanged(); + }); + this->syncer->getBooks(); } FMH::MODEL_LIST Books::items() const { return this->m_list; } void Books::setSortBy(const Books::SORTBY &sort) { } Books::SORTBY Books::getSortBy() const { return this->sort; } void Books::setOrder(const Books::ORDER &order) { } Books::ORDER Books::getOrder() const { return this->order; } void Books::sortList() { } QVariantMap Books::get(const int &index) const { if(index >= this->m_list.size() || index < 0) return QVariantMap(); return FMH::toMap(this->m_list.at(index)); } bool Books::insert(const QVariantMap &book) { emit this->preItemAppended(); auto __book = FMH::toModel(book); + __book[FMH::MODEL_KEY::THUMBNAIL] = "qrc:/booklet.svg"; + __book[FMH::MODEL_KEY::LABEL] =__book[FMH::MODEL_KEY::TITLE]; __book[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(Qt::TextDate); __book[FMH::MODEL_KEY::ADDDATE] = QDateTime::currentDateTime().toString(Qt::TextDate); - this->syncer->insertNote(__book); + this->syncer->insertBook(__book); this->m_list << __book; + qDebug() << m_list; emit this->postItemAppended(); return true; } bool Books::update(const QVariantMap &data, const int &index) { return false; } bool Books::remove(const int &index) { return false; } diff --git a/src/syncing/syncer.cpp b/src/syncing/syncer.cpp index 677c47d..430a861 100644 --- a/src/syncing/syncer.cpp +++ b/src/syncing/syncer.cpp @@ -1,345 +1,431 @@ #include "syncer.h" #include "db/db.h" #include "abstractnotesprovider.h" #include #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #else #include #include #endif Syncer::Syncer(QObject *parent) : QObject(parent), tag(Tagging::getInstance()), db(DB::getInstance()), provider(nullptr) {} void Syncer::setAccount(const FMH::MODEL &account) { if(this->provider) this->provider->setCredentials(account); } void Syncer::setProvider(AbstractNotesProvider *provider) { this->provider = std::move(provider); this->provider->setParent(this); this->provider->disconnect(); this->setConections(); } void Syncer::insertNote(FMH::MODEL ¬e) { - if(!this->insertLocal(note)) + if(!this->insertNoteLocal(note)) { 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; } - this->insertRemote(note); + this->insertNoteRemote(note); emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note inserted on the DB locally"}); } void Syncer::updateNote(const QString &id, const FMH::MODEL ¬e) { - if(!this->updateLocal(id, note)) + if(!this->updateNoteLocal(id, note)) { 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 = Syncer::noteStampFromId(this->db, id); if(!stamp.isEmpty()) - this->updateRemote(stamp, note); + this->updateNoteRemote(stamp, note); emit this->noteUpdated(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note updated on the DB locally"}); } void Syncer::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 = Syncer::noteStampFromId(this->db, id); - if(!this->removeLocal(id)) + if(!this->removeNoteLocal(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->removeRemote(stamp); + this->removeNoteRemote(stamp); emit this->noteRemoved(FMH::MODEL(), {STATE::TYPE::LOCAL, STATE::STATUS::OK, "The note has been removed from the local DB"}); } void Syncer::getNotes() { const auto notes = this->collectAllNotes(); if(this->provider && this->provider->isValid()) this->provider->getNotes(); else qWarning()<< "Credentials are missing to get notes or the provider has not been set"; emit this->notesReady(notes); } void Syncer::getBooks() { const auto books = this->collectAllBooks(); // this service is still missing -// if(this->provider && this->provider->isValid()) -// this->provider->getNotes(); -// else -// qWarning()<< "Credentials are missing to get notes or the provider has not been set"; + // if(this->provider && this->provider->isValid()) + // this->provider->getNotes(); + // else + // qWarning()<< "Credentials are missing to get notes or the provider has not been set"; emit this->booksReady(books); } -void Syncer::insertBook(const FMH::MODEL &book) +void Syncer::insertBook(FMH::MODEL &book) { - if(!this->db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKS], FMH::toMap(book))) + if(!this->insertBookLocal(book)) { qWarning()<< "Could not insert Book, Syncer::insertBook"; + return; } + + emit this->bookInserted(book, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Book inserted locally sucessfully"}); } -void Syncer::stampNote(FMH::MODEL ¬e) +void Syncer::addId(FMH::MODEL &model) { const auto id = QUuid::createUuid().toString(); - note[FMH::MODEL_KEY::ID] = id; + model[FMH::MODEL_KEY::ID] = id; } const QString Syncer::noteIdFromStamp(DB *_db, const QString &provider, const QString &stamp) { return [&]() -> QString { const auto data = _db->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 Syncer::noteStampFromId(DB *_db, const QString &id) { return [&]() -> QString { const auto data = _db->getDBData(QString("select stamp from notes_sync where id = '%1'").arg(id)); return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::STAMP]; }(); } void Syncer::setConections() { connect(this->provider, &AbstractNotesProvider::noteInserted, [&](FMH::MODEL note) { qDebug()<< "STAMP OF THE NEWLY INSERTED NOTE" << note[FMH::MODEL_KEY::ID] << 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 server provider"}); }); connect(this->provider, &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; qDebug()<< "NOETS READY << " << notes; // 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(const auto ¬e : notes) { const auto id = Syncer::noteIdFromStamp(this->db, this->provider->provider(), note[FMH::MODEL_KEY::ID]); // if the id is empty then the note does nto exists, so ithe note is inserted into the local db if(id.isEmpty()) { //here insert the note into the db auto __note = FMH::filterModel(note, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::FAVORITE, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::ADDDATE}); __note[FMH::MODEL_KEY::MODIFIED] = QDateTime::fromSecsSinceEpoch(note[FMH::MODEL_KEY::MODIFIED].toInt()).toString(Qt::TextDate); __note[FMH::MODEL_KEY::ADDDATE] = __note[FMH::MODEL_KEY::MODIFIED]; - if(!this->insertLocal(__note)) + if(!this->insertNoteLocal(__note)) { qWarning()<< "Remote note could not be inserted to the local storage"; continue; } __note[FMH::MODEL_KEY::STAMP] = note[FMH::MODEL_KEY::ID]; __note[FMH::MODEL_KEY::USER] = this->provider->user(); __note[FMH::MODEL_KEY::SERVER] = this->provider->provider(); 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}))); }else { //the note exists in the db locally, so update it auto __note = FMH::filterModel(note, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::FAVORITE}); __note[FMH::MODEL_KEY::MODIFIED] = QDateTime::fromSecsSinceEpoch(note[FMH::MODEL_KEY::MODIFIED].toInt()).toString(Qt::TextDate); - this->updateLocal(id, __note); + this->updateNoteLocal(id, __note); emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note inserted on local db, from the server provider"}); } } emit this->notesReady(this->collectAllNotes()); }); connect(this->provider, &AbstractNotesProvider::noteUpdated, [&](FMH::MODEL note) { const auto id = Syncer::noteIdFromStamp(this->db, this->provider->provider(), note[FMH::MODEL_KEY::ID]); if(!note.isEmpty()) - this->updateLocal(id, FMH::filterModel(note, {FMH::MODEL_KEY::TITLE})); + this->updateNoteLocal(id, FMH::filterModel(note, {FMH::MODEL_KEY::TITLE})); emit this->noteUpdated(note, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Note updated on server provider"}); }); connect(this->provider, &AbstractNotesProvider::noteRemoved, [&]() { emit this->noteRemoved(FMH::MODEL(), {STATE::TYPE::REMOTE, STATE::STATUS::OK, "The note has been removed from the remove server provider"}); }); } -bool Syncer::insertLocal(FMH::MODEL ¬e) +bool Syncer::insertNoteLocal(FMH::MODEL ¬e) { qDebug()<<"TAGS"<< note[FMH::MODEL_KEY::TAG]; - Syncer::stampNote(note); //adds a local id to the note + Syncer::addId(note); //adds a local id to the note // create a file for the note const auto __notePath = Syncer::saveNoteFile(note); note[FMH::MODEL_KEY::URL] = __notePath.toString(); qDebug()<< "note saved to <<" << __notePath; auto __noteMap = FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::ID, FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::COLOR, FMH::MODEL_KEY::URL, FMH::MODEL_KEY::PIN, FMH::MODEL_KEY::FAVORITE, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::ADDDATE})); if(this->db->insert(OWL::TABLEMAP[OWL::TABLE::NOTES], __noteMap)) { for(const auto &tg : note[FMH::MODEL_KEY::TAG].split(",", QString::SplitBehavior::SkipEmptyParts)) this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], note[FMH::MODEL_KEY::ID], note[FMH::MODEL_KEY::COLOR]); return true; } return false; } -void Syncer::insertRemote(FMH::MODEL ¬e) +void Syncer::insertNoteRemote(FMH::MODEL ¬e) { if(this->provider && this->provider->isValid()) this->provider->insertNote(note); } -bool Syncer::updateLocal(const QString &id, const FMH::MODEL ¬e) +bool Syncer::updateNoteLocal(const QString &id, const FMH::MODEL ¬e) { for(const auto &tg : note[FMH::MODEL_KEY::TAG]) this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], id); this->saveNoteFile(note); return this->db->update(OWL::TABLEMAP[OWL::TABLE::NOTES], FMH::toMap(FMH::filterModel(note, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::COLOR, FMH::MODEL_KEY::PIN, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::FAVORITE})), QVariantMap {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); } -void Syncer::updateRemote(const QString &id, const FMH::MODEL ¬e) +void Syncer::updateNoteRemote(const QString &id, const FMH::MODEL ¬e) { if(this->provider && this->provider->isValid()) this->provider->updateNote(id, note); } -bool Syncer::removeLocal(const QString &id) +bool Syncer::removeNoteLocal(const QString &id) { this->db->remove(OWL::TABLEMAP[OWL::TABLE::NOTES_SYNC], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); return this->db->remove(OWL::TABLEMAP[OWL::TABLE::NOTES], {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}}); } -void Syncer::removeRemote(const QString &id) +void Syncer::removeNoteRemote(const QString &id) { if(this->provider && this->provider->isValid()) this->provider->removeNote(id); } +bool Syncer::insertBookLocal(FMH::MODEL &book) +{ + const auto __path = QUrl::fromLocalFile(OWL::BooksPath+book[FMH::MODEL_KEY::TITLE]); + if(FMH::fileExists(__path)) + { + qWarning()<< "The directory for the book already exists. Syncer::insertBookLocal" << book[FMH::MODEL_KEY::TITLE]; + return false; + + } + + if(!FM::createDir(QUrl::fromLocalFile(OWL::BooksPath), book[FMH::MODEL_KEY::TITLE])) + { + qWarning() << "Could not create directory for the given book name. Syncer::insertBookLocal" << book[FMH::MODEL_KEY::TITLE]; + return false; + } + + Syncer::addId(book); + book[FMH::MODEL_KEY::URL] = __path.toString(); + + return(this->db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKS], FMH::toMap(FMH::filterModel(book,{FMH::MODEL_KEY::ID, + FMH::MODEL_KEY::URL, + FMH::MODEL_KEY::TITLE, + FMH::MODEL_KEY::FAVORITE, + FMH::MODEL_KEY::ADDDATE, + FMH::MODEL_KEY::MODIFIED} + )))); +} + +void Syncer::insertBookRemote(FMH::MODEL &book) +{ + +} + +bool Syncer::updateBookLocal(const QString &id, const FMH::MODEL &book) +{ + return false; +} + +void Syncer::updateBookRemote(const QString &id, const FMH::MODEL &book) +{ + +} + +bool Syncer::removeBookLocal(const QString &id) +{ + return false; +} + +void Syncer::removeBookRemote(const QString &id) +{ + +} + +bool Syncer::insertBookletLocal(FMH::MODEL &booklet) +{ + return false; +} + +void Syncer::insertBookletRemote(FMH::MODEL &booklet) +{ + +} + +bool Syncer::updateBookletLocal(const QString &id, const FMH::MODEL &booklet) +{ + return false; +} + +void Syncer::updateBookletRemote(const QString &id, const FMH::MODEL &booklet) +{ + +} + +bool Syncer::removeBookletLocal(const QString &id) +{ + return false; +} + +void Syncer::removeBookletRemote(const QString &id) +{ + +} + const FMH::MODEL_LIST Syncer::collectAllNotes() { FMH::MODEL_LIST __notes = this->db->getDBData("select * from notes"); for(auto ¬e : __notes) note[FMH::MODEL_KEY::CONTENT] = this->noteFileContent(note[FMH::MODEL_KEY::URL]); return __notes; } const FMH::MODEL_LIST Syncer::collectAllBooks() { return this->db->getDBData("select * from books"); } const QUrl Syncer::saveNoteFile(const FMH::MODEL ¬e) { if(note.isEmpty() || !note.contains(FMH::MODEL_KEY::CONTENT)) { qWarning() << "the note is empty, therefore it could not be saved into a file"; return QUrl(); } QFile file(OWL::NotesPath+note[FMH::MODEL_KEY::ID]+QStringLiteral(".txt")); file.open(QFile::WriteOnly); file.write(note[FMH::MODEL_KEY::CONTENT].toUtf8()); file.close(); return QUrl::fromLocalFile(file.fileName()); } const QString Syncer::noteFileContent(const QUrl &path) { if(!path.isLocalFile()) { qWarning()<< "Can not open note file, the url is not a local path"; return QString(); } QFile file(path.toLocalFile()); file.open(QFile::ReadOnly); const auto content = file.readAll(); file.close(); return QString(content); } diff --git a/src/syncing/syncer.h b/src/syncing/syncer.h index 9496cc9..7b358dc 100644 --- a/src/syncing/syncer.h +++ b/src/syncing/syncer.h @@ -1,279 +1,290 @@ #ifndef SYNCER_H #define SYNCER_H #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 */ struct STATE { enum TYPE : uint { LOCAL, REMOTE }; enum STATUS : uint { OK, ERROR }; TYPE type; STATUS status; QString msg = QString(); }; class DB; class AbstractNotesProvider; class Tagging; class Syncer: public QObject { Q_OBJECT public: explicit Syncer(QObject *parent = nullptr); /** * @brief setProviderAccount * sets the credentials to the current account * for the current provider being used * @param account * the account data represented by FMH::MODEL * where the valid keys are: * FMH::MODEL_KEY::USER user name * FMH::MODEL_KEY::PASSWORD users password * FMH::MODEL_KEY::PROVIDER the url to the provider server */ void setAccount(const FMH::MODEL &account); /** * @brief setProvider * sets the provider interface * this allows to change the provider source * @param provider * the provider must inherit the asbtract class AbstractNotesProvider. * The value passed is then moved to this class private property Syncer::provider */ void setProvider(AbstractNotesProvider *provider); //// 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(const QString &id, const 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(); ////BOOKS & BOOKLET INTERFACES /// interfaces with the the books and booklets from both, local and remote /** * @brief getBooks * Retrieves all the books, online and offline. * When the books are ready the signal Syncer::booksReady(FMH::MODEL_LIST) is emitted */ void getBooks(); /** * @brief getBook * @param id */ void getBook(const QString &id); /** * @brief insertBook * @param book */ - void insertBook(const FMH::MODEL &book); + void insertBook(FMH::MODEL &book); /** * @brief updateBook * @param id * @param book */ void updateBook(const QString &id, const FMH::MODEL &book); /** * @brief removeBook * @param id */ void removeBook(const QString &id); //BOOKLETS INTERFACES /** * @brief getBooklet * @param id */ void getBooklet(const QString &id); /** * @brief updateBooklet * @param id * @param booklet */ void updateBooklet(const QString &id, FMH::MODEL &booklet); /** * @brief insertBooklet * @param booklet */ void insertBooklet(const FMH::MODEL &booklet); /** * @brief removeBooklet * @param id */ void removeBooklet(const QString &id); 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; /** * @brief server * Abstract instance to the online server to perfom CRUD actions */ AbstractNotesProvider *provider; /** * @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); /** * @brief stampNote * Adds an stamp id to identify the note offline and online * @param note * the note model is passed by ref and a STAMP key value is inserted */ - static void stampNote(FMH::MODEL ¬e); + static void addId(FMH::MODEL &model); static const QString noteIdFromStamp(DB *_db, const QString &provider, const QString &stamp) ; static const QString noteStampFromId(DB *_db, const QString &id) ; void setConections(); protected: /** * @brief insertLocal * performs the insertion of a new note in the local storage * @param note * note to be inserted * @return bool * true if the note was inserted sucessfully in the local storage */ - bool insertLocal(FMH::MODEL ¬e); + bool insertNoteLocal(FMH::MODEL ¬e); /** * @brief insertRemote * perfroms the insertion of a new note in the remote provider server * @param note * the note to be inserted */ - void insertRemote(FMH::MODEL ¬e); - - - bool updateLocal(const QString &id, const FMH::MODEL ¬e); - void updateRemote(const QString &id, const FMH::MODEL ¬e); - - bool removeLocal(const QString &id); - void removeRemote(const QString &id); + void insertNoteRemote(FMH::MODEL ¬e); + bool updateNoteLocal(const QString &id, const FMH::MODEL ¬e); + void updateNoteRemote(const QString &id, const FMH::MODEL ¬e); + bool removeNoteLocal(const QString &id); + void removeNoteRemote(const QString &id); + + bool insertBookLocal(FMH::MODEL &book); + void insertBookRemote(FMH::MODEL &book); + bool updateBookLocal(const QString &id, const FMH::MODEL &book); + void updateBookRemote(const QString &id, const FMH::MODEL &book); + bool removeBookLocal(const QString &id); + void removeBookRemote(const QString &id); + + bool insertBookletLocal(FMH::MODEL &booklet); + void insertBookletRemote(FMH::MODEL &booklet); + bool updateBookletLocal(const QString &id, const FMH::MODEL &booklet); + void updateBookletRemote(const QString &id, const FMH::MODEL &booklet); + bool removeBookletLocal(const QString &id); + void removeBookletRemote(const QString &id); const FMH::MODEL_LIST collectAllNotes(); const FMH::MODEL_LIST collectAllBooks(); static inline const QUrl saveNoteFile(const FMH::MODEL ¬e); static inline const QString noteFileContent(const QUrl &path); 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); //FOR BOOKS void bookInserted(FMH::MODEL book, STATE state); void bookUpdated(FMH::MODEL book, STATE state); void bookRemoved(FMH::MODEL book, STATE state); void bookReady(FMH::MODEL book); void booksReady(FMH::MODEL_LIST books); public slots: }; #endif // SYNCER_H diff --git a/src/views/books/BooksView.qml b/src/views/books/BooksView.qml index 72b0611..76811ae 100644 --- a/src/views/books/BooksView.qml +++ b/src/views/books/BooksView.qml @@ -1,130 +1,170 @@ import QtQuick 2.9 import "../../widgets" 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 +import Books 1.0 + StackView { id: control + property alias list : _booksList property alias cardsView : cardsView property bool showDetails: false - StackView { id: _stackView anchors.fill: parent initialItem: _booksPage } Component { id: _bookletComponent BookletPage { onExit: _stackView.pop() } } + Maui.BaseModel + { + id: _booksModel + list: _booksList + } + + Books + { + id: _booksList + } Maui.Page { id: _booksPage padding: showDetails ? 0 : space.big title : cardsView.count + " books" // headBar.leftContent: [ // ToolButton // { // icon.name: showDetails ? "view-list-icons" : "view-list-details" // onClicked: // { // showDetails = !showDetails // } // } // ] headBar.rightContent: [ ToolButton { icon.name: "view-sort" } ] Maui.GridView { id: cardsView anchors.fill: parent adaptContent: !showDetails itemSize: showDetails ? iconSizes.big : iconSizes.huge // centerContent: true - spacing: space.big + spacing: space.huge cellWidth: showDetails ? parent.width : itemSize * 1.5 cellHeight: itemSize * 1.5 - model: ListModel + model: _booksModel + + delegate: ItemDelegate { - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 0} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 3} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 10} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 2} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 9} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 2} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 3} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 1} - ListElement {label: "test"; thumbnail:"qrc:/booklet.svg"; mime: "image"; count: 0} + id: _delegate + width: cardsView.cellWidth * 0.9 + height: cardsView.cellHeight - } + hoverEnabled: !isMobile + background: Rectangle + { + color: "transparent" + } - delegate: Maui.IconDelegate - { - id: _delegate + ColumnLayout + { + anchors.fill : parent - isDetails: control.showDetails - folderSize: cardsView.itemSize - showThumbnails: true - showEmblem: false - width: cardsView.cellWidth - height: cardsView.cellHeight * 0.9 + Item + { + Layout.fillWidth: true + Layout.preferredHeight: cardsView.itemSize + + Image + { + id: _img + anchors.centerIn: parent + source: "qrc:/booklet.svg" + sourceSize.width: cardsView.itemSize + sourceSize.height: cardsView.itemSize + } + } + + Rectangle + { + Layout.fillWidth: true + Layout.fillHeight: true + + color: hovered ? Kirigami.Theme.highlightColor : "transparent" + radius: radiusV + Label + { + width: parent.width + height: parent.height + color: hovered ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor + text: model.title + horizontalAlignment: Qt.AlignHCenter + + } + } + + } Maui.Badge { anchors { left: parent.left top: parent.top margins: space.small } Kirigami.Theme.backgroundColor: Kirigami.Theme.neutralTextColor Kirigami.Theme.textColor: Qt.darker(Kirigami.Theme.neutralTextColor, 2.4) text: model.count } Connections { target:_delegate onClicked: { console.log("BOOKLET CLICKED", index) _stackView.push(_bookletComponent) } } } } } }