diff --git a/src/buho.cpp b/src/buho.cpp index adbc5a8..38a7a35 100644 --- a/src/buho.cpp +++ b/src/buho.cpp @@ -1,22 +1,22 @@ #include "buho.h" #include "owl.h" Buho::Buho(QObject *parent) : QObject(parent) { this->setFolders(); } void Buho::setFolders() { - QDir notes_path(OWL::NotesPath); + QDir notes_path(OWL::NotesPath.toLocalFile()); if (!notes_path.exists()) notes_path.mkpath("."); - QDir links_path(OWL::LinksPath); + QDir links_path(OWL::LinksPath.toLocalFile()); if (!links_path.exists()) links_path.mkpath("."); - QDir books_path(OWL::BooksPath); + QDir books_path(OWL::BooksPath.toLocalFile()); if (!books_path.exists()) books_path.mkpath("."); } diff --git a/src/controllers/notes/notescontroller.cpp b/src/controllers/notes/notescontroller.cpp index 5a6453a..a843014 100644 --- a/src/controllers/notes/notescontroller.cpp +++ b/src/controllers/notes/notescontroller.cpp @@ -1,6 +1,86 @@ #include "notescontroller.h" +#include "owl.h" + +#ifdef STATIC_MAUIKIT +#include "tagging.h" +#include "fm.h" +#include "mauiaccounts.h" +#else +#include +#include +#include +#include +#endif NotesController::NotesController(QObject *parent) : QObject(parent) { + 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); + + m_worker.start(); +} + +NotesController::~NotesController() +{ + m_worker.wait(); + m_worker.quit(); +} + +const QUrl NotesController::saveNoteFile(const QUrl &url, const FMH::MODEL &data) +{ + 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(); + } + + qDebug() << "SVAE NOTES TO" << url.toLocalFile(); + QFile file(url.toLocalFile()+data[FMH::MODEL_KEY::TITLE]+QStringLiteral(".txt")); + file.open(QFile::WriteOnly); + file.write(data[FMH::MODEL_KEY::CONTENT].toUtf8()); + file.close(); + return QUrl::fromLocalFile(file.fileName()); +} + +bool NotesController::insertNote(const FMH::MODEL ¬e, const QUrl &url) +{ + if(note.isEmpty()) + { + qWarning()<< "Could not insert note locally. The note is empty"; + return false; + } + + if(url.isEmpty() && !url.isValid()) + { + qWarning()<< "File could not be saved. NotesController::insertNote."; + return false; + } + + const auto __notePath = NotesController::saveNoteFile(url, note); + if(!FMH::fileExists(__notePath)) + return false; + + auto m_note = note; + m_note[FMH::MODEL_KEY::URL] = __notePath.toString(); + qDebug()<< "note saved to <<" << __notePath << m_note[FMH::MODEL_KEY::TAG]; + + for(const auto &tg : m_note[FMH::MODEL_KEY::TAG].split(",", QString::SplitBehavior::SkipEmptyParts)) + Tagging::getInstance()->tagUrl(__notePath.toString(), tg, note[FMH::MODEL_KEY::COLOR]); + + return true; +} + +void NotesController::getNotes(const QUrl &url) +{ + emit this->fetchNotes(url); +} +void NotesLoader::fetchNotes(const QUrl &url) +{ +// QDirIterator it(OWL::NotesPath.toLocalFile(), ) } diff --git a/src/controllers/notes/notescontroller.h b/src/controllers/notes/notescontroller.h index e0188c8..330f8b3 100644 --- a/src/controllers/notes/notescontroller.h +++ b/src/controllers/notes/notescontroller.h @@ -1,16 +1,59 @@ #ifndef NOTESCONTROLLER_H #define NOTESCONTROLLER_H #include +#include + +#ifdef STATIC_MAUIKIT +#include "fmh.h" +#else +#include +#endif + +class NotesLoader : public QObject +{ + Q_OBJECT +public: + void fetchNotes(const QUrl &url); + +signals: + void noteReady(FMH::MODEL note); + void notesReady(FMH::MODEL_LIST notes); +}; class NotesController : public QObject { Q_OBJECT public: explicit NotesController(QObject *parent = nullptr); + ~NotesController(); + +public slots: + /** + * @brief insertNote + * performs the insertion of a new note in the local storage + * @param note + * note to be inserted + * @param url + * url where to save the note + * @return bool + * true if the note was inserted sucessfully in the local storage + */ + bool insertNote(const FMH::MODEL ¬e, const QUrl &url); + + void getNotes(const QUrl &url); + +private: + QThread m_worker; + static inline const QUrl saveNoteFile(const QUrl &url, const FMH::MODEL &data); signals: + void noteReady(FMH::MODEL note); + void notesReady(FMH::MODEL_LIST notes); + void noteInserted(FMH::MODEL note); + + void fetchNotes(QUrl url); }; #endif // NOTESCONTROLLER_H diff --git a/src/models/links/links.cpp b/src/models/links/links.cpp index e0233c6..2d97964 100644 --- a/src/models/links/links.cpp +++ b/src/models/links/links.cpp @@ -1,206 +1,205 @@ #include "links.h" #include #include "db/db.h" #include "linker.h" #ifdef STATIC_MAUIKIT #include "tagging.h" #else #include #endif Links::Links(QObject *parent) : MauiList(parent) { this->db = DB::getInstance(); - this->tag = Tagging::getInstance(); this->sortList(); connect(this, &Links::sortByChanged, this, &Links::sortList); connect(this, &Links::orderChanged, this, &Links::sortList); } void Links::sortList() { emit this->preListChanged(); this->links = this->db->getDBData(QString("select * from links ORDER BY %1 %2").arg( FMH::MODEL_NAME[static_cast(this->sort)], this->order == ORDER::ASC ? "asc" : "desc")); emit this->postListChanged(); } QVariantMap Links::get(const int &index) const { if(index >= this->links.size() || index < 0) return QVariantMap(); QVariantMap res; const auto note = this->links.at(index); for(auto key : note.keys()) res.insert(FMH::MODEL_NAME[key], note[key]); return res; } FMH::MODEL_LIST Links::items() const { return this->links; } void Links::setSortBy(const Links::SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; emit this->sortByChanged(); } Links::SORTBY Links::getSortBy() const { return this->sort; } void Links::setOrder(const Links::ORDER &order) { if(this->order == order) return; this->order = order; emit this->orderChanged(); } Links::ORDER Links::getOrder() const { return this->order; } bool Links::insert(const QVariantMap &link) { emit this->preItemAppended(); auto __model = FMH::toModel(link); __model[FMH::MODEL_KEY::ADDDATE] = QDateTime::currentDateTime().toString(); __model[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(); - __model[FMH::MODEL_KEY::PREVIEW] = OWL::saveImage(Linker::getUrl(__model[FMH::MODEL_KEY::PREVIEW]), OWL::LinksPath+QUuid::createUuid().toString()); + __model[FMH::MODEL_KEY::PREVIEW] = OWL::saveImage(Linker::getUrl(__model[FMH::MODEL_KEY::PREVIEW]), OWL::LinksPath.toString()+QUuid::createUuid().toString()); __model = FMH::filterModel(__model, {FMH::MODEL_KEY::URL, FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::PREVIEW, FMH::MODEL_KEY::COLOR, FMH::MODEL_KEY::FAVORITE, FMH::MODEL_KEY::PIN, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::ADDDATE}); if(this->db->insert(OWL::TABLEMAP[OWL::TABLE::LINKS], FMH::toMap(__model))) { - for(auto tg : __model[FMH::MODEL_KEY::TAG].split(",")) - this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], __model[FMH::MODEL_KEY::URL]); + for(const auto &tg : __model[FMH::MODEL_KEY::TAG].split(",")) + Tagging::getInstance()->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], __model[FMH::MODEL_KEY::URL]); this->links << __model; emit postItemAppended(); return true; } else qDebug()<< "LINK COULD NOT BE INSTED"; return false; } bool Links::update(const int &index, const QVariant &value, const int &role) { if(index < 0 || index >= links.size()) return false; const auto oldValue = this->links[index][static_cast(role)]; if(oldValue == value.toString()) return false; qDebug()<< "VALUE TO UPDATE"<< FMH::MODEL_NAME[static_cast(role)] << oldValue; this->links[index].insert(static_cast(role), value.toString()); this->update(this->links[index]); return true; } bool Links::update(const QVariantMap &data, const int &index) { if(index < 0 || index >= this->links.size()) return false; auto newData = this->links[index]; QVector roles; for(auto key : data.keys()) if(newData[FMH::MODEL_NAME_KEY[key]] != data[key].toString()) { newData.insert(FMH::MODEL_NAME_KEY[key], data[key].toString()); roles << FMH::MODEL_NAME_KEY[key]; } this->links[index] = newData; if(this->update(newData)) { qDebug() << "update link" << newData; emit this->updateModel(index, roles); return true; } return false; } bool Links::update(const FMH::MODEL &link) { auto url = link[FMH::MODEL_KEY::LINK]; auto color = link[FMH::MODEL_KEY::COLOR]; auto pin = link[FMH::MODEL_KEY::PIN].toInt(); auto fav = link[FMH::MODEL_KEY::FAV].toInt(); auto tags = link[FMH::MODEL_KEY::TAG].split(",", QString::SkipEmptyParts); auto updated = link[FMH::MODEL_KEY::MODIFIED]; QVariantMap link_map = { {FMH::MODEL_NAME[FMH::MODEL_KEY::COLOR], color}, {FMH::MODEL_NAME[FMH::MODEL_KEY::PIN], pin}, {FMH::MODEL_NAME[FMH::MODEL_KEY::FAV], fav}, {FMH::MODEL_NAME[FMH::MODEL_KEY::MODIFIED], updated} }; - for(auto tg : tags) - this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], url, color); + for(const auto &tg : tags) + Tagging::getInstance()->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], url, color); return this->db->update(OWL::TABLEMAP[OWL::TABLE::LINKS], link_map, {{FMH::MODEL_NAME[FMH::MODEL_KEY::LINK], url}} ); } bool Links::remove(const int &index) { emit this->preItemRemoved(index); auto linkUrl = this->links.at(index)[FMH::MODEL_KEY::LINK]; QVariantMap link = {{FMH::MODEL_NAME[FMH::MODEL_KEY::LINK], linkUrl}}; if(this->db->remove(OWL::TABLEMAP[OWL::TABLE::LINKS], link)) { this->links.removeAt(index); emit this->postItemRemoved(); return true; } return false; } QVariantList Links::getTags(const int &index) { if(index < 0 || index >= this->links.size()) return QVariantList(); auto link = this->links.at(index)[FMH::MODEL_KEY::LINK]; - return this->tag->getAbstractTags(OWL::TABLEMAP[OWL::TABLE::LINKS], link); + return Tagging::getInstance()->getAbstractTags(OWL::TABLEMAP[OWL::TABLE::LINKS], link); } diff --git a/src/models/links/links.h b/src/models/links/links.h index 9f3770b..77497c8 100644 --- a/src/models/links/links.h +++ b/src/models/links/links.h @@ -1,75 +1,71 @@ #ifndef LINKS_H #define LINKS_H #include #ifdef STATIC_MAUIKIT #include "fmh.h" #include "mauilist.h" #else #include #include #endif class DB; -class Tagging; - class Links : public MauiList { Q_OBJECT Q_PROPERTY(SORTBY sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged) Q_PROPERTY(ORDER order READ getOrder WRITE setOrder NOTIFY orderChanged) 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) explicit Links(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; private: DB *db; - Tagging *tag; - - FMH::MODEL_LIST links; + FMH::MODEL_LIST links; void sortList(); SORTBY sort = SORTBY::MODIFIED; ORDER order = ORDER::DESC; signals: void orderChanged(); void sortByChanged(); public slots: QVariantMap get(const int &index) const; bool insert(const QVariantMap &link); bool update(const int &index, const QVariant &value, const int &role); //deprecrated bool update(const QVariantMap &data, const int &index); bool update(const FMH::MODEL &link); bool remove(const int &index); QVariantList getTags(const int &index); }; #endif // NOTES_H diff --git a/src/models/notes/notes.cpp b/src/models/notes/notes.cpp index a07d88f..0bae0a0 100644 --- a/src/models/notes/notes.cpp +++ b/src/models/notes/notes.cpp @@ -1,210 +1,209 @@ #include "notes.h" #include "syncer.h" #include "nextnote.h" #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #include "mauiaccounts.h" #include "mauiapp.h" #else #include #include #include #endif Notes::Notes(QObject *parent) : MauiList(parent), syncer(new Syncer(this)) { qDebug()<< "CREATING NOTES LIST"; - this->syncer->setProvider(new NextNote); + this->syncer->setProvider(new NextNote); //Syncer takes ownership of NextNote or the provider const auto m_account = MauiAccounts::instance(); - connect(m_account, &MauiAccounts::currentAccountChanged, [&](QVariantMap currentAccount) + connect(m_account, &MauiAccounts::currentAccountChanged, [&](QVariantMap) { - Q_UNUSED(currentAccount) this->syncer->getNotes(); }); connect(this, &Notes::sortByChanged, this, &Notes::sortList); connect(this, &Notes::orderChanged, this, &Notes::sortList); connect(syncer, &Syncer::notesReady, [&](FMH::MODEL_LIST data) { emit this->preListChanged(); this->notes = data; emit this->postListChanged(); }); this->syncer->getNotes(); } void Notes::sortList() { emit this->preListChanged(); const auto key = static_cast(this->sort); qDebug()<< "SORTING LIST BY"<< this->sort; std::sort(this->notes.begin(), this->notes.end(), [&](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { switch(key) { case FMH::MODEL_KEY::FAVORITE: { return e1[key] == "true"; } case FMH::MODEL_KEY::ADDDATE: case FMH::MODEL_KEY::MODIFIED: { const auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); const auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); if(this->order == Notes::ORDER::ASC) { if(date1.secsTo(QDateTime::currentDateTime()) > date2.secsTo(QDateTime::currentDateTime())) return true; } if(this->order == Notes::ORDER::DESC) { if(date1.secsTo(QDateTime::currentDateTime()) < date2.secsTo(QDateTime::currentDateTime())) return true; } break; } case FMH::MODEL_KEY::TITLE: case FMH::MODEL_KEY::COLOR: { const auto str1 = QString(e1[key]).toLower(); const auto str2 = QString(e2[key]).toLower(); if(this->order == Notes::ORDER::ASC) { if(str1 < str2) return true; } if(this->order == Notes::ORDER::DESC) { if(str1 > str2) return true; } break; } default: if(e1[key] < e2[key]) return true; } return false; }); emit this->postListChanged(); } FMH::MODEL_LIST Notes::items() const { return this->notes; } void Notes::setSortBy(const Notes::SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; emit this->sortByChanged(); } Notes::SORTBY Notes::getSortBy() const { return this->sort; } void Notes::setOrder(const Notes::ORDER &order) { if(this->order == order) return; this->order = order; emit this->orderChanged(); } Notes::ORDER Notes::getOrder() const { return this->order; } bool Notes::insert(const QVariantMap ¬e) { emit this->preItemAppended(); auto __note = FMH::toModel(note); __note[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(Qt::TextDate); __note[FMH::MODEL_KEY::ADDDATE] = QDateTime::currentDateTime().toString(Qt::TextDate); this->syncer->insertNote(__note); this->notes << __note; emit this->postItemAppended(); return true; } bool Notes::update(const QVariantMap &data, const int &index) { if(index < 0 || index >= this->notes.size()) return false; auto newData = this->notes[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->notes[index] = newData; newData[FMH::MODEL_KEY::MODIFIED] = QDateTime::currentDateTime().toString(Qt::TextDate); this->syncer->updateNote(newData[FMH::MODEL_KEY::ID], newData); emit this->updateModel(index, roles); return true; } bool Notes::remove(const int &index) { if(index < 0 || index >= this->notes.size()) return false; emit this->preItemRemoved(index); this->syncer->removeNote(this->notes.at(index)[FMH::MODEL_KEY::ID]); this->notes.removeAt(index); emit this->postItemRemoved(); return true; } QVariantList Notes::getTags(const int &index) { // if(index < 0 || index >= this->notes.size()) // return QVariantList(); // auto id = this->notes.at(index)[FMH::MODEL_KEY::ID]; // return this->tag->getAbstractTags(OWL::TABLEMAP[OWL::TABLE::NOTES], id); return QVariantList(); } QVariantMap Notes::get(const int &index) const { if(index >= this->notes.size() || index < 0) return QVariantMap(); return FMH::toMap(this->notes.at(index)); } diff --git a/src/models/notes/notes.h b/src/models/notes/notes.h index 360bfd1..6a860f0 100644 --- a/src/models/notes/notes.h +++ b/src/models/notes/notes.h @@ -1,74 +1,73 @@ #ifndef NOTES_H #define NOTES_H #include #include "owl.h" #ifdef STATIC_MAUIKIT #include "fmh.h" #include "mauilist.h" #else #include #include #endif class Syncer; class Notes : public MauiList { Q_OBJECT Q_PROPERTY(SORTBY sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged) Q_PROPERTY(ORDER order READ getOrder WRITE setOrder NOTIFY orderChanged) 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) explicit Notes(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; private: Syncer *syncer; FMH::MODEL_LIST notes; QVariantMap m_account; void sortList(); SORTBY sort = SORTBY::MODIFIED; ORDER order = ORDER::DESC; signals: void orderChanged(); void sortByChanged(); public slots: QVariantList getTags(const int &index); QVariantMap get(const int &index) const; bool insert(const QVariantMap ¬e); bool update(const QVariantMap &data, const int &index); bool remove(const int &index); - }; #endif // NOTES_H diff --git a/src/providers/abstractnotesprovider.h b/src/providers/abstractnotesprovider.h index bb59175..09555e0 100644 --- a/src/providers/abstractnotesprovider.h +++ b/src/providers/abstractnotesprovider.h @@ -1,169 +1,169 @@ #ifndef ABSTRACTNOTESPROVIDER_H #define ABSTRACTNOTESPROVIDER_H #include #include #ifdef STATIC_MAUIKIT #include "fmh.h" #include "downloader.h" #else #include #include #endif /** * @brief The AbstractNoteSyncer class * is an abstraction for different services backend to sync notes. * Different services to be added to Buho are expected to derived from this. */ class AbstractNotesProvider : public QObject { Q_OBJECT public: AbstractNotesProvider(QObject *parent) : QObject(parent) {} virtual ~AbstractNotesProvider() {} /** * @brief setCredentials * sets the credential to authenticate to the provider server * @param account - * the account data is represented by FMH::MODEL + * the account data is represented by FMH::MODEL and must contain the fileds: USER, PASSWORD and SERVER */ - virtual void setCredentials(const FMH::MODEL &account) final + void setCredentials(const FMH::MODEL &account) { this->m_user = account[FMH::MODEL_KEY::USER]; this->m_password = account[FMH::MODEL_KEY::PASSWORD]; this->m_provider = QUrl(account[FMH::MODEL_KEY::SERVER]).host(); } - virtual QString user() final { return this->m_user; } - virtual QString provider() final { return this->m_provider; } + const QString user() const { return this->m_user; } + const QString provider() const { return this->m_provider; } /** * @brief isValid * check if the account acredentials are valid * by checking they are not empty or null * @return * true if the credentials are all set or false is somethign is missing */ - virtual bool isValid() final + bool isValid() const { return !(this->m_user.isEmpty() || this->m_user.isNull() || this->m_provider.isEmpty() || this->m_provider.isNull() || this->m_password.isEmpty() || this->m_password.isNull()); } /** * @brief getNote * gets a note identified by an ID * @param id * When the process is done it shoudl emit the noteReady(FMH::MODEL) signal */ // virtual FMH::MODEL getNote(const QString &id) = 0; virtual void getNote(const QString &id) = 0; virtual void getBooklet(const QString &id) = 0; /** * @brief getNotes * returns all the notes or queried notes * When the process is done it shoudl emit the notesReady(FMH::MODEL_LIST) signal */ virtual void getNotes() = 0; virtual void getBooklets() = 0; // virtual void getNotes() const {} // virtual FMH::MODEL_LIST getNotes(const QString &query = QString()) = 0; // virtual FMH::MODEL_LIST getNotes(const QString &query = QString()) const = 0; /** * @brief insertNote * inserts a new note to the server * @param note * takes the new note to be inserted represented as FMH::MODEL * When the process is done it shoudl emit the noteInserted(FMH::MODEL) signal */ // virtual bool insertNote(const FMH::MODEL ¬e) = 0; virtual void insertNote(const FMH::MODEL ¬e) = 0; virtual void insertBooklet(const FMH::MODEL &booklet) = 0; /** * @brief updateNote * allows to update a note in the server, it takes an ID and the updated note * @param id * id of the note to be updated * @param note * the note prepresented as FMH::MODEL contening the up-to-date values * When the process is done it shoudl emit the noteUpdated(FMH::MODEL) signal */ // virtual bool updateNote(const QString &id, const FMH::MODEL ¬e) = 0; virtual void updateNote(const QString &id, const FMH::MODEL ¬e) = 0; virtual void updateBooklet(const QString &id, const FMH::MODEL &booklet) = 0; /** * @brief removeNote * removes a note from the server * @param id * ID of the note to be removed * When the process is done it shoudl emit the noteRemoved(FMH::MODEL) signal */ // virtual bool removeNote(const QString &id) = 0; virtual void removeNote(const QString &id) = 0; virtual void removeBooklet(const QString &id) = 0; protected: QString m_user = ""; QString m_password = ""; QString m_provider = ""; template void request(const QString &url, const QMap &header, T cb) // inline void request(const QString &url, const QMap &header, std::functioncb) { auto downloader = new FMH::Downloader; connect(downloader, &FMH::Downloader::dataReady, [&, downloader = std::move(downloader)](const QByteArray &array) { // if(cb != nullptr) cb(array); downloader->deleteLater(); }); downloader->getArray(url, header); } signals: void noteReady(FMH::MODEL note); void bookletReady(FMH::MODEL booklet); void notesReady(FMH::MODEL_LIST notes); void bookletsReady(FMH::MODEL_LIST booklets); void noteInserted(FMH::MODEL note); void bookletInserted(FMH::MODEL booklet); void noteUpdated(FMH::MODEL note); void bookletUpdated(FMH::MODEL booklet); void noteRemoved(); void bookletRemoved(); /** * @brief responseReady * gets emitted when the data is ready after requesting the array * with &Downloader::getArray() */ void responseReady(QByteArray array); /** * @brief responseError * emitted if there's an error when trying to get the array */ void responseError(QString); }; #endif // ABSTRACTNOTESPROVIDER_H diff --git a/src/providers/nextnote.cpp b/src/providers/nextnote.cpp index 47ef285..f2bc888 100644 --- a/src/providers/nextnote.cpp +++ b/src/providers/nextnote.cpp @@ -1,343 +1,343 @@ #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, [&, downloader_ = std::move(downloader)](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, [&, downloader_ = std::move(downloader)](QByteArray array) { //exclude notes that have its own category FMH::MODEL_LIST notes; for(const auto &data : this->parseNotes(array)) if(data[FMH::MODEL_KEY::CATEGORY].isEmpty() || data[FMH::MODEL_KEY::CATEGORY].isNull()) notes << data; 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, [&, downloader_ = std::move(downloader)](QByteArray array) { //exclude notes that have its own category FMH::MODEL_LIST booklets; for(const auto &data : this->parseNotes(array)) if(!data[FMH::MODEL_KEY::CATEGORY].isEmpty() && !data[FMH::MODEL_KEY::CATEGORY].isNull()) booklets << data; emit this->bookletsReady(booklets); downloader_->deleteLater(); }); downloader->getArray(url, header); } void NextNote::insertNote(const FMH::MODEL ¬e) { QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(note)).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 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(); }); } void NextNote::insertBooklet(const FMH::MODEL &booklet) { QByteArray payload = QJsonDocument::fromVariant(FMH::toMap(booklet)).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()); emit this->bookletInserted([&]() -> 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 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 } return note; }()); restclient->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(); }); } 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::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(); }); } 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(); }); } void NextNote::removeBooklet(const QString &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"; return res; } const auto data = jsonResponse.toVariant(); if(data.isNull() || !data.isValid()) return res; if(!data.toList().isEmpty()) - res<< FMH::toModelList(data.toList()); + res << FMH::toModelList(data.toList()); else res << FMH::toModel(data.toMap()); return res; } diff --git a/src/syncing/syncer.cpp b/src/syncing/syncer.cpp index 855c3de..e08f157 100644 --- a/src/syncing/syncer.cpp +++ b/src/syncing/syncer.cpp @@ -1,674 +1,624 @@ #include "syncer.h" #include "db/db.h" #include "abstractnotesprovider.h" +#include "controllers/notes/notescontroller.h" #include #ifdef STATIC_MAUIKIT #include "tagging.h" #include "fm.h" #include "mauiaccounts.h" #else #include #include #include #include #endif Syncer::Syncer(QObject *parent) : QObject(parent), tag(Tagging::getInstance()), db(DB::getInstance()), - provider(nullptr) { - - const auto m_account = MauiAccounts::instance(); - connect(m_account, &MauiAccounts::currentAccountChanged, [&](QVariantMap currentAccount) + m_provider(nullptr), //online service handler + m_notesController(new NotesController(this)) //local handler +{ + connect(MauiAccounts::instance(), &MauiAccounts::currentAccountChanged, [&](QVariantMap currentAccount) { this->setAccount(FMH::toModel(currentAccount)); }); } void Syncer::setAccount(const FMH::MODEL &account) { - if(this->provider) - this->provider->setCredentials(account); + if(this->m_provider) + this->m_provider->setCredentials(account); } void Syncer::setProvider(AbstractNotesProvider *provider) { - this->provider = std::move(provider); - this->provider->setParent(this); - this->provider->disconnect(); + this->m_provider = std::move(provider); + this->m_provider->setParent(this); + this->m_provider->disconnect(); this->setConections(); } void Syncer::insertNote(FMH::MODEL ¬e) { - if(!this->insertNoteLocal(note)) + if(!this->m_notesController->insertNote(note, this->localStoragePath(OWL::NotesPath))) { 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"}); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->insertNote(note); + + emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note saved 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(); + this->collectAllNotes(); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->getNotes(); else - qWarning()<< "Credentials are missing to get notes or the provider has not been set"; - - - emit this->notesReady(notes); + qWarning()<< "Failed to fetch online notes. Credentials are missing or the provider has not been set"; } void Syncer::getBooks() { const auto books = this->collectAllBooks(); - if(this->provider && this->provider->isValid()) - this->provider->getBooklets(); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->getBooklets(); 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::getBooklet(const QString &bookId) { const auto res = this->db->getDBData(QString("select * from booklets where book = '%1'").arg(bookId)); emit this->bookletReady(res); } void Syncer::updateBooklet(const QString &id, const QString &bookId, FMH::MODEL &booklet) { if(!this->updateBookletLocal(id, bookId, booklet)) { 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 = Syncer::bookletStampFromId(this->db, id); qDebug()<< "booklet stamp from id" << stamp; if(!stamp.isEmpty()) this->updateBookletRemote(stamp, bookId, booklet); emit this->bookletUpdated(booklet, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Booklet updated locally on the DB"}); } void Syncer::insertBooklet(const QString &bookId, FMH::MODEL &booklet) { if(!this->insertBookletLocal(bookId, booklet)) { qWarning()<< "Could not insert Booklet, Syncer::insertBooklet"; return; } this->insertBookletRemote(bookId, booklet); 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]; }(); } const QString Syncer::bookletIdFromStamp(DB *_db, const QString &provider, const QString &stamp) { return [&]() -> QString { const auto data = _db->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 Syncer::bookletStampFromId(DB *_db, const QString &id) { return [&]() -> QString { const auto data = _db->getDBData(QString("select stamp from booklets_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) + connect(this->m_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::bookletInserted, [&](FMH::MODEL booklet) + connect(this->m_provider, &AbstractNotesProvider::bookletInserted, [&](FMH::MODEL booklet) { qDebug()<< "STAMP OF THE NEWLY INSERTED BOOKLET" << booklet[FMH::MODEL_KEY::ID] << 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->provider, &AbstractNotesProvider::notesReady, [&](FMH::MODEL_LIST notes) + connect(this->m_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]); + const auto id = Syncer::noteIdFromStamp(this->db, this->m_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)) + if(!this->m_notesController->insertNote(__note, this->localStoragePath(OWL::NotesPath))) { 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(); + __note[FMH::MODEL_KEY::USER] = this->m_provider->user(); + __note[FMH::MODEL_KEY::SERVER] = this->m_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}))); emit this->noteInserted(__note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note inserted on local db, from the server provider"}); }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->noteUpdated(__note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note updated on local db, from the server provider"}); } } - emit this->notesReady(this->collectAllNotes()); + this->collectAllNotes(); }); - connect(this->provider, &AbstractNotesProvider::bookletsReady, [&](FMH::MODEL_LIST booklets) + connect(this->m_provider, &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; qDebug()<< "Booklets READY << " << booklets; // 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(const auto &booklet : booklets) { - const auto id = Syncer::bookletIdFromStamp(this->db, this->provider->provider(), booklet[FMH::MODEL_KEY::ID]); //the id is actually the stamp id + const auto id = Syncer::bookletIdFromStamp(this->db, this->m_provider->provider(), booklet[FMH::MODEL_KEY::ID]); //the id is actually the stamp id // if the id 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 auto __booklet = FMH::filterModel(booklet, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::ADDDATE}); __booklet[FMH::MODEL_KEY::MODIFIED] = QDateTime::fromSecsSinceEpoch(booklet[FMH::MODEL_KEY::MODIFIED].toInt()).toString(Qt::TextDate); __booklet[FMH::MODEL_KEY::ADDDATE] = __booklet[FMH::MODEL_KEY::MODIFIED]; if(!this->insertBookletLocal(booklet[FMH::MODEL_KEY::CATEGORY], __booklet)) { qWarning()<< "Remote booklet could not be inserted to the local storage"; continue; } __booklet[FMH::MODEL_KEY::STAMP] = booklet[FMH::MODEL_KEY::ID]; - __booklet[FMH::MODEL_KEY::USER] = this->provider->user(); - __booklet[FMH::MODEL_KEY::SERVER] = this->provider->provider(); + __booklet[FMH::MODEL_KEY::USER] = this->m_provider->user(); + __booklet[FMH::MODEL_KEY::SERVER] = this->m_provider->provider(); 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 auto __booklet = FMH::filterModel(booklet, {FMH::MODEL_KEY::TITLE, FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::MODIFIED, FMH::MODEL_KEY::FAVORITE}); __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]; }(); qDebug()<< " trying to update local booklets with url" << __booklet[FMH::MODEL_KEY::URL] << __booklet[FMH::MODEL_KEY::BOOK] << __booklet[FMH::MODEL_KEY::CONTENT] ; __booklet[FMH::MODEL_KEY::MODIFIED] = QDateTime::fromSecsSinceEpoch(booklet[FMH::MODEL_KEY::MODIFIED].toInt()).toString(Qt::TextDate); this->updateBookletLocal(id, __booklet[FMH::MODEL_KEY::BOOK], __booklet); 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->provider, &AbstractNotesProvider::noteUpdated, [&](FMH::MODEL note) + connect(this->m_provider, &AbstractNotesProvider::noteUpdated, [&](FMH::MODEL note) { - const auto id = Syncer::noteIdFromStamp(this->db, this->provider->provider(), note[FMH::MODEL_KEY::ID]); + const auto id = Syncer::noteIdFromStamp(this->db, this->m_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::bookletUpdated, [&](FMH::MODEL booklet) + connect(this->m_provider, &AbstractNotesProvider::bookletUpdated, [&](FMH::MODEL booklet) { - const auto id = Syncer::bookletIdFromStamp(this->db, this->provider->provider(), booklet[FMH::MODEL_KEY::ID]); + const auto id = Syncer::bookletIdFromStamp(this->db, this->m_provider->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->updateBookletLocal(id, booklet[FMH::MODEL_KEY::BOOK], FMH::filterModel(booklet, {FMH::MODEL_KEY::TITLE})); } emit this->bookletUpdated(booklet, {STATE::TYPE::REMOTE, STATE::STATUS::OK, "Booklet updated on server provider"}); }); - connect(this->provider, &AbstractNotesProvider::noteRemoved, [&]() + connect(this->m_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) -{ - 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(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(OWL::NotesPath, 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); + if(this->m_provider && this->m_provider->isValid()) + this->m_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); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->removeNote(id); } bool Syncer::insertBookLocal(FMH::MODEL &book) { - const auto __path = QUrl::fromLocalFile(OWL::BooksPath+book[FMH::MODEL_KEY::TITLE]); + const auto __path = QUrl::fromLocalFile(OWL::BooksPath.toString()+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(!FMStatic::createDir(QUrl::fromLocalFile(OWL::BooksPath), book[FMH::MODEL_KEY::TITLE])) + if(!FMStatic::createDir(QUrl::fromLocalFile(OWL::BooksPath.toString()), book[FMH::MODEL_KEY::TITLE])) { qWarning() << "Could not create directory for the given book name. Syncer::insertBookLocal" << book[FMH::MODEL_KEY::TITLE]; return false; } book[FMH::MODEL_KEY::URL] = __path.toString(); return(this->db->insert(OWL::TABLEMAP[OWL::TABLE::BOOKS], FMH::toMap(FMH::filterModel(book,{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(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 - if(!FMH::fileExists(QUrl::fromLocalFile(OWL::BooksPath+bookId))) + if(!FMH::fileExists(QUrl::fromLocalFile(OWL::BooksPath.toString()+bookId))) { qWarning()<< "The book does not exists in the db or the directory is missing. Syncer::insertBookletLocal. " "Creating a new book registry" << bookId; FMH::MODEL __book; __book[FMH::MODEL_KEY::TITLE] = bookId; this->insertBook(__book); } - const auto __bookletPath = Syncer::saveNoteFile(OWL::BooksPath+bookId+"/", booklet); +// const auto __bookletPath = Syncer::saveNoteFile(OWL::BooksPath.toString()+bookId+"/", booklet); - if(__bookletPath.isEmpty()) - { - qWarning()<< "File could not be saved. Syncer::insertBookletLocal"; - return false; - } +// 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; +// 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})); +// 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]); +// 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 true; +// } return false; } void Syncer::insertBookletRemote(const QString &bookId, FMH::MODEL &booklet) { - qDebug()<< "trying to insert booklet remotely" << (this->provider ? "provider exists" : "failed provider") << this->provider->isValid(); + qDebug()<< "trying to insert booklet remotely" << (this->m_provider ? "provider exists" : "failed provider") << this->m_provider->isValid(); booklet[FMH::MODEL_KEY::CATEGORY] = bookId; - if(this->provider && this->provider->isValid()) - this->provider->insertBooklet(booklet); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->insertBooklet(booklet); } bool Syncer::updateBookletLocal(const QString &id, const QString &bookId, const FMH::MODEL &booklet) { // 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 = Syncer::saveNoteFile(__path.toLocalFile()+"/", booklet); - qDebug()<< "Updating local txt file as"<< __path.toLocalFile() << __bookletPath; +// const auto __bookletPath = Syncer::saveNoteFile(__path.toLocalFile()+"/", booklet); +// qDebug()<< "Updating local txt file as"<< __path.toLocalFile() << __bookletPath; - if(__bookletPath.isEmpty()) - { - qWarning()<< "File could not be saved. Syncer::insertBookletLocal"; - return false; - } +// if(__bookletPath.isEmpty()) +// { +// qWarning()<< "File could not be saved. Syncer::insertBookletLocal"; +// return false; +// } return this->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], bookId}}); } void Syncer::updateBookletRemote(const QString &id, const QString &bookId, FMH::MODEL &booklet) { booklet[FMH::MODEL_KEY::CATEGORY] = bookId; - if(this->provider && this->provider->isValid()) - this->provider->updateBooklet(id, booklet); + if(this->m_provider && this->m_provider->isValid()) + this->m_provider->updateBooklet(id, booklet); } bool Syncer::removeBookletLocal(const QString &id) { return false; } void Syncer::removeBookletRemote(const QString &id) { } -const FMH::MODEL_LIST Syncer::collectAllNotes() +void 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; + this->m_notesController->getNotes(OWL::NotesPath); } const FMH::MODEL_LIST Syncer::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"); } -const QUrl Syncer::saveNoteFile(const QString &dir, const FMH::MODEL &data) +const QUrl Syncer::localStoragePath(const QUrl &pathHint) { - if(data.isEmpty() /*|| !data.contains(FMH::MODEL_KEY::CONTENT)*/) + if(!this->m_provider || !this->m_provider->isValid()) { - qWarning() << "the note is empty, therefore it could not be saved into a file"; - return QUrl(); + qWarning()<< "There is not a Provider setup for saving the notes. Saving locally now to " << pathHint; + return pathHint; } - QFile file(dir+data[FMH::MODEL_KEY::ID]+QStringLiteral(".txt")); - file.open(QFile::WriteOnly); - file.write(data[FMH::MODEL_KEY::CONTENT].toUtf8()); - file.close(); + QUrl res = pathHint.toString() + this->m_provider->provider()+"/"+this->m_provider->user()+"/"; + QDir dir(res.toLocalFile()); + if(!dir.exists()) + { + if(dir.mkpath(".")) + return res; + else return pathHint; - return QUrl::fromLocalFile(file.fileName()); + }else return res; } 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 61723d7..0f9ac80 100644 --- a/src/syncing/syncer.h +++ b/src/syncing/syncer.h @@ -1,300 +1,286 @@ #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 NotesController; 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 &bookId); /** * @brief updateBooklet * @param id * @param booklet */ void updateBooklet(const QString &id, const QString &bookId, FMH::MODEL &booklet); /** * @brief insertBooklet * @param 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; + AbstractNotesProvider * m_provider; + 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); /** * @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); static const QString bookletIdFromStamp(DB *_db, const QString &provider, const QString &stamp) ; static const QString bookletStampFromId(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(const QString &bookId, FMH::MODEL &booklet); void insertBookletRemote(const QString &bookId, FMH::MODEL &booklet); bool updateBookletLocal(const QString &id, const QString &bookId, const FMH::MODEL &booklet); void updateBookletRemote(const QString &id, const QString &bookId, FMH::MODEL &booklet); bool removeBookletLocal(const QString &id); void removeBookletRemote(const QString &id); - const FMH::MODEL_LIST collectAllNotes(); + void collectAllNotes(); const FMH::MODEL_LIST collectAllBooks(); - static inline const QUrl saveNoteFile(const QString &dir, const FMH::MODEL &data); + const QUrl localStoragePath(const QUrl &pathHint); 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_LIST booklets); public slots: }; #endif // SYNCER_H diff --git a/src/utils/owl.h b/src/utils/owl.h index b50c540..3b186db 100644 --- a/src/utils/owl.h +++ b/src/utils/owl.h @@ -1,147 +1,68 @@ #ifndef OWL_H #define OWL_H #include #include #include -#include #include -#include -#include -#include -#include -#include -#include +#include +#ifndef STATIC_MAUIKIT #include "../buho_version.h" +#endif namespace OWL { Q_NAMESPACE enum class TABLE : uint8_t { NOTES, NOTES_SYNC, BOOKS, BOOKLETS, BOOKLETS_SYNC, LINKS, NONE }; static const QMap TABLEMAP = { {TABLE::NOTES,"notes"}, {TABLE::NOTES_SYNC,"notes_sync"}, {TABLE::BOOKS,"books"}, {TABLE::BOOKLETS,"booklets"}, {TABLE::BOOKLETS_SYNC,"booklets_sync"}, {TABLE::LINKS,"links"}, }; -// enum KEY : uint8_t -// { -// URL, -// UPDATED, -// ID, -// TITLE, -// BODY, -// FAV, -// COLOR, -// ADD_DATE, -// TAG, -// PREVIEW, -// IMAGE, -// LINK, -// PIN, -// NONE -// }; Q_ENUM_NS(KEY); + const static inline QString CollectionDBPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/"; + const static inline QUrl NotesPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/notes/"); + const static inline QUrl BooksPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/books/"); + const static inline QUrl LinksPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/buho/links/"); -// 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 = BUHO_VERSION_STRING; - 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(); - } + const static inline QString App = "Buho"; + const static inline QString version = BUHO_VERSION_STRING; + const static inline QString comment = "Notes taking and link collector manager"; + const static inline QString DBName = "collection.db"; 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/widgets/NewNoteDialog.qml b/src/widgets/NewNoteDialog.qml index 9a9bce0..32afa1c 100644 --- a/src/widgets/NewNoteDialog.qml +++ b/src/widgets/NewNoteDialog.qml @@ -1,174 +1,171 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Dialog { id: control parent: parent - heightHint: 0.95 - widthHint: 0.95 - - maxWidth: 700 * Maui.Style.unit - - maxHeight: maxWidth property string selectedColor : Kirigami.Theme.backgroundColor property string fgColor: Qt.darker(selectedColor, 3) property bool showEditActions : false - - rejectButton.visible: false signal noteSaved(var note) - page.padding: 0 + Kirigami.Theme.backgroundColor: selectedColor Kirigami.Theme.textColor: fgColor + heightHint: 0.95 + widthHint: 0.95 + maxWidth: 700 * Maui.Style.unit + maxHeight: maxWidth + + page.padding: 0 + + rejectText: qsTr("Discard") + rejectButton.visible: false + acceptText: qsTr("Save") + onAccepted: + { + if(editor.body.text.length > 0) + packNote() + clear() + } + + onRejected: clear() + headBar.middleContent: TextField { id: title Layout.fillWidth: true Layout.margins: Maui.Style.space.medium placeholderText: qsTr("Title") font.weight: Font.Bold font.bold: true font.pointSize: Maui.Style.fontSizes.large - // Kirigami.Theme.backgroundColor: selectedColor - // Kirigami.Theme.textColor: Qt.darker(selectedColor, 2.5) - // color: fgColor + background: Rectangle { color: "transparent" } } footBar.leftContent: [ ToolButton { id: pinButton icon.name: "pin" checkable: true icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor // onClicked: checked = !checked }, ToolButton { id: favButton icon.name: "love" checkable: true icon.color: checked ? "#ff007f" : Kirigami.Theme.textColor }, ToolButton { icon.name: "document-share" onClicked: isAndroid ? Maui.Android.shareText(editor.body.text) : shareDialog.show(editor.body.text) icon.color: Kirigami.Theme.textColor }, ToolButton { icon.name: "document-export" icon.color: Kirigami.Theme.textColor - }, ToolButton { icon.name: "entry-delete" icon.color: Kirigami.Theme.textColor - } - ] - - acceptText: qsTr("Save") - rejectText: qsTr("Discard") - onAccepted: - { - if(editor.body.text.length > 0) - packNote() - clear() - } - - onRejected: clear() + ] ColumnLayout { anchors.fill: parent + spacing: 0 Maui.Editor { id: editor Layout.fillHeight: true Layout.fillWidth: true - Kirigami.Theme.backgroundColor: selectedColor - Kirigami.Theme.textColor: Qt.darker(selectedColor, 2.5) + Kirigami.Theme.backgroundColor: control.selectedColor + Kirigami.Theme.textColor: Qt.darker(control.selectedColor, 2.5) + footBar.visible: false headBar.leftContent: ToolButton { icon.name: "image" icon.color: control.Kirigami.Theme.textColor } headBar.rightContent: ColorsBar { - onColorPicked: selectedColor = color + onColorPicked: control.selectedColor = color } } Maui.TagsBar { id: tagBar + position: ToolBar.Footer Layout.fillWidth: true - allowEditMode: true - list.abstract: true - list.key: "notes" - onTagsEdited: list.updateToAbstract(tags) - onTagRemovedClicked: list.removeFromAbstract(index) + allowEditMode: true + onTagsEdited: list.append(tags) + list.strict: true + list.urls: [""] +// onTagRemovedClicked: list.removeFromAbstract(index) Kirigami.Theme.backgroundColor: "transparent" Kirigami.Theme.textColor: Kirigami.Theme.textColor } } - onOpened: if(isMobile) editor.body.forceActiveFocus() - + onOpened: editor.body.forceActiveFocus() function clear() { title.clear() editor.body.clear() close() } function fill(note) { title.text = note.title editor.body.text = note.content control.selectedColor = note.color ? note.color : Kirigami.Theme.backgroundColor pinButton.checked = note.pin == 1 favButton.checked = note.favorite == 1 - - tagBar.list.lot= note.id - + tagBar.list.urls = [note.url] open() } function packNote() { - noteSaved({ + console.log("TAGS", tagBar.list.tags) + control.noteSaved({ id: notesView.currentNote.id, title: title.text.trim(), content: editor.body.text, color: selectedColor, - tag: tagBar.getTags(), + tag: tagBar.list.tags.join(","), pin: pinButton.checked ? 1 : 0, favorite: favButton.checked ? 1 : 0, modified: new Date() }) } }