diff --git a/src/db/script.sql b/src/db/script.sql index 125ad44..000e1c6 100644 --- a/src/db/script.sql +++ b/src/db/script.sql @@ -1,52 +1,50 @@ - 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) +PRIMARY KEY(server, stamp), FOREIGN KEY(id) REFERENCES NOTES(id) ); CREATE TABLE IF NOT EXISTS BOOKS ( 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, adddate DATE, -modified DATE -PRIMARY KEY(id, book) +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/booklet.cpp b/src/models/books/booklet.cpp index e90da4e..5768aad 100644 --- a/src/models/books/booklet.cpp +++ b/src/models/books/booklet.cpp @@ -1,77 +1,77 @@ #include "booklet.h" #include "syncer.h" Booklet::Booklet(Syncer *_syncer, QObject *parent) : MauiList(parent), syncer(_syncer) { } 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) { if (m_book == book) return; m_book = book; emit bookChanged(m_book); } void Booklet::insert(const QVariantMap &data) { emit this->preItemAppended(); auto __booklet = FMH::toModel(data); __booklet[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(Qt::TextDate); __booklet[FMH::MODEL_KEY::ADDDATE] = QDateTime::currentDateTime().toString(Qt::TextDate); - this->syncer->insertBooklet(__booklet); + this->syncer->insertBooklet(this->m_book, __booklet); this->m_list << __booklet; emit this->postItemAppended(); } void Booklet::update(const QVariantMap &data, const int &index) { } void Booklet::remove(const int &index) { } void Booklet::sortList() { } diff --git a/src/models/books/books.cpp b/src/models/books/books.cpp index 3139ea3..2cfe04b 100644 --- a/src/models/books/books.cpp +++ b/src/models/books/books.cpp @@ -1,94 +1,114 @@ #include "books.h" #include "syncer.h" #include "nextnote.h" #include "booklet.h" Books::Books(QObject *parent) : MauiList(parent), syncer(new Syncer(this)), m_booklet(new Booklet(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; } +Booklet *Books::getBooklet() const +{ + return m_booklet; +} + +int Books::getCurrentBook() const +{ + return m_currentBook; +} + 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->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; } void Books::openBook(const int &index) { if(index >= this->m_list.size() || index < 0) return; this->m_booklet->setBook(this->m_list.at(index)[FMH::MODEL_KEY::ID]); } + +void Books::setCurrentBook(int currentBook) +{ + if (m_currentBook == currentBook) + return; + + m_currentBook = currentBook; + this->openBook(m_currentBook); + emit currentBookChanged(m_currentBook); +} diff --git a/src/models/books/books.h b/src/models/books/books.h index 9a1747a..cf8976c 100644 --- a/src/models/books/books.h +++ b/src/models/books/books.h @@ -1,91 +1,97 @@ #ifndef BOOKS_H #define BOOKS_H #include #include "owl.h" #ifdef STATIC_MAUIKIT #include "fmh.h" #include "mauilist.h" #else #include #include #endif class Booklet; class Syncer; class Books : public MauiList { Q_OBJECT Q_PROPERTY(SORTBY sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged) Q_PROPERTY(ORDER order READ getOrder WRITE setOrder NOTIFY orderChanged) Q_PROPERTY(Booklet *booklet READ getBooklet NOTIFY bookletChanged) + Q_PROPERTY(int currentBook READ getCurrentBook WRITE setCurrentBook NOTIFY currentBookChanged) + public: enum ORDER : uint8_t { DESC, ASC }; Q_ENUM(ORDER) enum SORTBY : uint8_t { TITLE = FMH::MODEL_KEY::TITLE, ADDDATE = FMH::MODEL_KEY::ADDDATE, MODIFIED = FMH::MODEL_KEY::MODIFIED, COLOR = FMH::MODEL_KEY::COLOR, FAVORITE = FMH::MODEL_KEY::FAVORITE, PIN = FMH::MODEL_KEY::PIN }; Q_ENUM(SORTBY) Books(QObject *parent = nullptr); FMH::MODEL_LIST items() const override final; void setSortBy(const SORTBY &sort); SORTBY getSortBy() const; void setOrder(const ORDER &order); ORDER getOrder() const; - Booklet * getBooklet() const - { - return m_booklet; - } + Booklet * getBooklet() const; + + int getCurrentBook() const; private: Syncer *syncer; Booklet * m_booklet; FMH::MODEL_LIST m_list; void sortList(); + void openBook(const int &index); SORTBY sort = SORTBY::MODIFIED; ORDER order = ORDER::DESC; + int m_currentBook; + signals: void sortByChanged(); void orderChanged(); void bookletChanged(Booklet * booklet); + void currentBookChanged(int currentBook); + public slots: QVariantMap get(const int &index) const; /** * @brief insert * insertes a new book by using the syncer interface * @param note * @return */ bool insert(const QVariantMap &book); bool update(const QVariantMap &data, const int &index); bool remove(const int &index); - void openBook(const int &index); + void setCurrentBook(int currentBook); }; #endif // BOOKS_H diff --git a/src/syncing/syncer.cpp b/src/syncing/syncer.cpp index 458cddf..cdbbd4f 100644 --- a/src/syncing/syncer.cpp +++ b/src/syncing/syncer.cpp @@ -1,437 +1,501 @@ #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->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->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->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->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->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->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"; emit this->booksReady(books); } void Syncer::insertBook(FMH::MODEL &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::insertBooklet(FMH::MODEL &booklet) +void Syncer::insertBooklet(const QString &bookId, FMH::MODEL &booklet) { - this->insertBookletLocal(booklet); + if(!this->insertBookletLocal(bookId, booklet)) + { + qWarning()<< "Could not insert Booklet, Syncer::insertBooklet"; + return; + } + + emit this->bookletInserted(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet inserted locally sucessfully"}); } void Syncer::addId(FMH::MODEL &model) { const auto id = QUuid::createUuid().toString(); 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->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->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->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::insertNoteLocal(FMH::MODEL ¬e) -{ - qDebug()<<"TAGS"<< note[FMH::MODEL_KEY::TAG]; +{ + if(note.isEmpty()) + { + qWarning()<< "Could not insert note locally. The note is empty"; + return false; + } Syncer::addId(note); //adds a local id to the note // create a file for the note - const auto __notePath = Syncer::saveNoteFile(note); + const auto __notePath = Syncer::saveNoteFile(OWL::NotesPath, note); + + if(__notePath.isEmpty()) + { + qWarning()<< "File could not be saved. Syncer::insertNoteLocal."; + return false; + } + 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::insertNoteRemote(FMH::MODEL ¬e) { if(this->provider && this->provider->isValid()) this->provider->insertNote(note); } 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); + this->saveNoteFile(OWL::NotesPath, 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::updateNoteRemote(const QString &id, const FMH::MODEL ¬e) { if(this->provider && this->provider->isValid()) this->provider->updateNote(id, note); } 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::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) +bool Syncer::insertBookletLocal(const QString &bookId, FMH::MODEL &booklet) { qDebug()<< "trying to insert booklet" << booklet; + if(bookId.isEmpty() || booklet.isEmpty()) + { + qWarning()<< "Could not insert booklet. Reference to book id or booklet are empty"; + return false; + } + + Syncer::addId(booklet); //adds a local id to the booklet + + // create a file for the note + const auto __bookTitle = [&]() -> QString { + const auto data = this->db->getDBData(QString("select title from books where id = '%1'").arg(bookId)); + return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::TITLE]; } (); + + if(__bookTitle.isEmpty() || !FMH::fileExists(QUrl::fromLocalFile(OWL::BooksPath+__bookTitle))) + { + qWarning()<< "The book does not exists in the db or the directory is missing. Syncer::insertBookletLocal"; + return false; + } + + const auto __bookletPath = Syncer::saveNoteFile(OWL::BooksPath+__bookTitle+"/", booklet); + + if(__bookletPath.isEmpty()) + { + qWarning()<< "File could not be saved. Syncer::insertBookletLocal"; + return false; + } + + booklet[FMH::MODEL_KEY::URL] = __bookletPath.toString(); + booklet[FMH::MODEL_KEY::BOOK] = bookId; + qDebug()<< "booklet saved to <<" << __bookletPath; + + auto __bookletMap = FMH::toMap(FMH::filterModel(booklet, {FMH::MODEL_KEY::ID, + FMH::MODEL_KEY::BOOK, + FMH::MODEL_KEY::TITLE, + FMH::MODEL_KEY::URL, + FMH::MODEL_KEY::MODIFIED, + FMH::MODEL_KEY::ADDDATE})); + + + if(this->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; } -void Syncer::insertBookletRemote(FMH::MODEL &booklet) +void Syncer::insertBookletRemote(const QString &bookId, 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) +const QUrl Syncer::saveNoteFile(const QString &dir, const FMH::MODEL &data) { - if(note.isEmpty() || !note.contains(FMH::MODEL_KEY::CONTENT)) + if(data.isEmpty() /*|| !data.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")); + QFile file(dir+data[FMH::MODEL_KEY::ID]+QStringLiteral(".txt")); file.open(QFile::WriteOnly); - file.write(note[FMH::MODEL_KEY::CONTENT].toUtf8()); + file.write(data[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 595f1bb..94d2130 100644 --- a/src/syncing/syncer.h +++ b/src/syncing/syncer.h @@ -1,290 +1,297 @@ #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(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(FMH::MODEL &booklet); + void insertBooklet(const QString &bookId, 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 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 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 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 insertBookletLocal(const QString &bookId, FMH::MODEL &booklet); + void insertBookletRemote(const QString &bookId, 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 QUrl saveNoteFile(const QString &dir, const FMH::MODEL &data); 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); + //FOR BOOKLETS + void bookletInserted(FMH::MODEL booklet, STATE state); + void bookletUpdated(FMH::MODEL booklet, STATE state); + void bookletRemoved(FMH::MODEL booklet, STATE state); + void bookletReady(FMH::MODEL booklet); + void bookletReady(FMH::MODEL_LIST booklets); + public slots: }; #endif // SYNCER_H diff --git a/src/utils/owl.h b/src/utils/owl.h index 3e477c8..df54e5b 100644 --- a/src/utils/owl.h +++ b/src/utils/owl.h @@ -1,154 +1,143 @@ #ifndef OWL_H #define OWL_H #include #include #include #include #include #include #include #include #include #include #include namespace OWL { Q_NAMESPACE enum class TABLE : uint8_t { NOTES, - NOTES_TAGS, NOTES_SYNC, - TAGS, BOOKS, - PAGES, - BOOKS_PAGES, + BOOKLETS, LINKS, - LINKS_TAGS, - PAGES_TAGS, NONE }; static const QMap TABLEMAP = { {TABLE::NOTES,"notes"}, - {TABLE::NOTES_TAGS,"notes_tags"}, {TABLE::NOTES_SYNC,"notes_sync"}, - {TABLE::TAGS,"tags"}, {TABLE::BOOKS,"books"}, - {TABLE::PAGES,"pages"}, - {TABLE::BOOKS_PAGES,"books_pages"}, + {TABLE::BOOKLETS,"booklets"}, {TABLE::LINKS,"links"}, - {TABLE::LINKS_TAGS,"links_tags"}, - {TABLE::PAGES_TAGS,"pages_tags"}, - {TABLE::LINKS_TAGS,"links_tags"} }; // enum KEY : uint8_t // { // URL, // UPDATED, // ID, // TITLE, // BODY, // FAV, // COLOR, // ADD_DATE, // TAG, // PREVIEW, // IMAGE, // LINK, // PIN, // NONE // }; Q_ENUM_NS(KEY); // typedef QHash DB; // typedef QList DB_LIST; // static const DB KEYMAP = // { // {KEY::ID, "id"}, // {KEY::BODY, "body"}, // {KEY::UPDATED, "updated"}, // {KEY::TITLE, "title"}, // {KEY::URL, "url"}, // {KEY::FAV, "fav"}, // {KEY::PIN, "pin"}, // {KEY::COLOR, "color"}, // {KEY::ADD_DATE, "addDate"}, // {KEY::TAG, "tag"}, // {KEY::PREVIEW, "preview"}, // {KEY::IMAGE, "image"}, // {KEY::LINK, "link"} // }; // static const QHash MAPKEY = // { // {KEYMAP[KEY::ID], KEY::ID}, // {KEYMAP[KEY::BODY], KEY::BODY}, // {KEYMAP[KEY::UPDATED], KEY::UPDATED}, // {KEYMAP[KEY::TITLE], KEY::TITLE}, // {KEYMAP[KEY::URL], KEY::URL}, // {KEYMAP[KEY::FAV], KEY::FAV}, // {KEYMAP[KEY::PIN], KEY::PIN}, // {KEYMAP[KEY::COLOR], KEY::COLOR}, // {KEYMAP[KEY::ADD_DATE], KEY::ADD_DATE}, // {KEYMAP[KEY::TAG], KEY::TAG}, // {KEYMAP[KEY::PREVIEW], KEY::PREVIEW}, // {KEYMAP[KEY::IMAGE], KEY::IMAGE}, // {KEYMAP[KEY::LINK], KEY::LINK} // }; const QString CollectionDBPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/"; const QString NotesPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/notes/"; const QString BooksPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/books/"; const QString LinksPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/links/"; const QString App = "Buho"; const QString version = "1.0"; const QString comment = "Notes taking and link collector manager"; const QString DBName = "collection.db"; inline void saveJson(QJsonDocument document, QString fileName) { QFile jsonFile(fileName); jsonFile.open(QFile::WriteOnly); jsonFile.write(document.toJson()); jsonFile.close(); } inline QVariantMap openJson(const QString &url) { QString val; QFile file; file.setFileName(url); file.open(QIODevice::ReadOnly | QIODevice::Text); val = file.readAll(); file.close(); QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); QJsonObject obj = d.object(); return obj.toVariantMap(); } inline QString saveImage(QByteArray array, const QString &path) { if(!array.isNull()&&!array.isEmpty()) { QImage img; img.loadFromData(array); QString name = path; name.replace("/", "-"); name.replace("&", "-"); QString format = "JPEG"; if (img.save(path+".jpg", format.toLatin1(), 100)) return path+".jpg"; else qDebug() << "couldn't save artwork"; }else qDebug()<<"array is empty"; return QString(); } } #endif // OWL_H diff --git a/src/views/books/BooksView.qml b/src/views/books/BooksView.qml index 76811ae..64be9c5 100644 --- a/src/views/books/BooksView.qml +++ b/src/views/books/BooksView.qml @@ -1,170 +1,172 @@ 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 + currentBook: cardsView.currentIndex } 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.huge cellWidth: showDetails ? parent.width : itemSize * 1.5 cellHeight: itemSize * 1.5 model: _booksModel delegate: ItemDelegate { id: _delegate width: cardsView.cellWidth * 0.9 height: cardsView.cellHeight hoverEnabled: !isMobile background: Rectangle { color: "transparent" } ColumnLayout { anchors.fill : parent 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) + cardsView.currentIndex = index _stackView.push(_bookletComponent) } } } } } }