Commit d1df9d81 authored by camilo higuita's avatar camilo higuita

notes now sync: when creating notes are inserted local and remote, and when...

notes now sync: when creating notes are inserted local and remote, and when retrieved they are updated and inserted if they didn't exists before
parent c66efeba
......@@ -10,6 +10,15 @@ adddate DATE,
modified DATE
);
CREATE TABLE IF NOT EXISTS NOTES_SYNC (
id TEXT,
server TEXT,
user TEXT,
stamp TEXT,
PRIMARY KEY(server, stamp)
FOREIGN KEY(id) REFERENCES NOTES(id)
);
CREATE TABLE IF NOT EXISTS BOOKS (
url TEXT PRIMARY KEY,
title TEXT NOT NULL,
......
......@@ -15,8 +15,18 @@ Notes::Notes(QObject *parent) : MauiList(parent),
qDebug()<< "CREATING NOTES LIST";
this->sortList();
connect(this, &Notes::accountChanged, syncer, &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()
......@@ -166,11 +176,9 @@ bool Notes::remove(const int &index)
void Notes::setAccount(const QVariantMap &account)
{
// this->m_account = account;
// const auto data = FM::toModel(this->m_account);
// syncer->setCredentials(data[FMH::MODEL_KEY::USER], data[FMH::MODEL_KEY::PASSWORD], QUrl(data[FMH::MODEL_KEY::SERVER]).host());
// syncer->getNotes();
// emit accountChanged();
this->m_account = account;
syncer->setAccount(FM::toModel(this->m_account));
emit accountChanged();
}
QVariantMap Notes::getAccount() const
......
......@@ -52,15 +52,15 @@ public:
QVariantMap getAccount() const;
private:
Syncer *syncer;
FMH::MODEL_LIST notes;
QVariantMap m_account;
void sortList();
SORTBY sort = SORTBY::MODIFIED;
ORDER order = ORDER::DESC;
QVariantMap m_account;
signals:
void orderChanged();
......
......@@ -22,11 +22,34 @@ public:
AbstractNotesProvider(QObject *parent) : QObject(parent) {}
virtual ~AbstractNotesProvider() {}
virtual void setCredentials(const QString &user, const QString &password, const QString &provider) final
/**
* @brief setCredentials
* sets the credential to authenticate to the provider server
* @param account
* the account data is represented by FMH::MODEL
*/
virtual void setCredentials(const FMH::MODEL &account) final
{
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; }
/**
* @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()
{
this->m_user = user;
this->m_password = password;
this->m_provider = provider;
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());
}
/**
......
......@@ -71,7 +71,7 @@ void NextNote::getNotes()
void NextNote::insertNote(const FMH::MODEL &note)
{
QByteArray payload=QJsonDocument::fromVariant(FM::toMap(note)).toJson();
QByteArray payload = QJsonDocument::fromVariant(FM::toMap(note)).toJson();
qDebug() << "UPLOADING NEW NOT" << QVariant(payload).toString();
const auto url = QString(NextNote::API+"%1").replace("PROVIDER", this->m_provider).arg("notes");
......@@ -91,16 +91,29 @@ void NextNote::insertNote(const FMH::MODEL &note)
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
request.setRawHeader(QString("Authorization").toLocal8Bit(), headerData.toLocal8Bit());
QNetworkAccessManager *restclient; //in class
restclient = new QNetworkAccessManager(this); //constructor
auto restclient = new QNetworkAccessManager; //constructor
QNetworkReply *reply = restclient->post(request,payload);
connect(reply, &QNetworkReply::finished, [=]()
qDebug() << "AUTH >> " << concatenated << headerData;
connect(reply, &QNetworkReply::finished, [=, __note = note]()
{
qDebug() << "Note insertyion finished?";
const auto notes = this->parseNotes(reply->readAll());
emit this->noteInserted(notes.isEmpty() ? FMH::MODEL() : notes.first());
reply->deleteLater();
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();
});
}
......@@ -134,11 +147,18 @@ FMH::MODEL_LIST NextNote::parseNotes(const QByteArray &array)
return res;
}
auto notes = jsonResponse.toVariant();
for(const auto &map : notes.toList())
const auto data = jsonResponse.toVariant();
if(data.isNull() || !data.isValid())
return res;
if(!data.toList().isEmpty())
{
res << FM::toModel(map.toMap());
for(const auto &map : data.toList())
res << FM::toModel(map.toMap());
}
else
res << FM::toModel(data.toMap());
return res;
}
......
......@@ -16,23 +16,109 @@ Syncer::Syncer(QObject *parent) : QObject(parent),
tag(Tagging::getInstance()),
db(DB::getInstance()),
provider(new NextNote(this))
{}
{
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], FM::toMap(Syncer::filterNote(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;
// 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 &note : notes)
{
const auto id = [&]() -> QString {
const auto data = this->db->getDBData(QString("select id from notes_sync where server = '%1' AND stamp = '%2'").arg(this->provider->provider(),
note[FMH::MODEL_KEY::ID]));
qDebug()<< "trying to update note" << data;
return data.isEmpty() ? QString() : data.first()[FMH::MODEL_KEY::ID];
}();
// if the id is empty then the note does nto exists, so inert it into the db
if(id.isEmpty())
{
//here insert the note into the db
auto __note = Syncer::filterNote(note, {FMH::MODEL_KEY::TITLE,
FMH::MODEL_KEY::CONTENT,
FMH::MODEL_KEY::FAVORITE,
FMH::MODEL_KEY::MODIFIED,
FMH::MODEL_KEY::ADDDATE});
__note[FMH::MODEL_KEY::MODIFIED] = QDateTime::fromSecsSinceEpoch(note[FMH::MODEL_KEY::MODIFIED].toInt()).toString(Qt::TextDate);
__note[FMH::MODEL_KEY::ADDDATE] = __note[FMH::MODEL_KEY::MODIFIED];
if(!this->insertLocal(__note))
{
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], FM::toMap(Syncer::filterNote(__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
this->db->update(OWL::TABLEMAP[OWL::TABLE::NOTES], FM::toMap(Syncer::filterNote(note, {FMH::MODEL_KEY::CONTENT, FMH::MODEL_KEY::FAVORITE})), QVariantMap {{FMH::MODEL_NAME[FMH::MODEL_KEY::ID], id}});
}
}
emit this->notesReady(this->db->getDBData("select * from notes"));
});
}
void Syncer::setAccount(const FMH::MODEL &account)
{
this->provider->setCredentials(account);
}
void Syncer::insertNote(FMH::MODEL &note)
{
qDebug()<<"TAGS"<< note[FMH::MODEL_KEY::TAG];
if(!this->insertLocal(note))
{
qWarning()<< "The note could not be inserted locally, therefore it was not attempted to insert it to the remote provider server, if it existed.";
return;
}
Syncer::stampNote(note);
const auto __noteMap = FM::toMap(Syncer::packNote(note));
this->insertRemote(note);
emit this->noteInserted(note, {STATE::TYPE::LOCAL, STATE::STATUS::OK, "Note inserted on the DB locally"});
}
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]);
void Syncer::getNotes()
{
const auto notes = this->db->getDBData("select * from notes");
// this->provider->insertNote(note);
if(this->provider->isValid())
this->provider->getNotes();
else {
qWarning()<< "Credentials are missing to get notes";
}
emit this->notesReady(notes);
}
void Syncer::stampNote(FMH::MODEL &note)
......@@ -41,16 +127,43 @@ void Syncer::stampNote(FMH::MODEL &note)
note[FMH::MODEL_KEY::ID] = id;
}
FMH::MODEL Syncer::packNote(const FMH::MODEL &note)
FMH::MODEL Syncer::filterNote(const FMH::MODEL &note, const QVector<FMH::MODEL_KEY> &keys)
{
FMH::MODEL res;
for(const auto &key : keys)
res[key] = note[key];
return res;
}
bool Syncer::insertLocal(FMH::MODEL &note)
{
qDebug()<<"TAGS"<< note[FMH::MODEL_KEY::TAG];
Syncer::stampNote(note); //adds a local id to the note
const auto __noteMap = FM::toMap(Syncer::filterNote(note, {FMH::MODEL_KEY::ID,
FMH::MODEL_KEY::TITLE,
FMH::MODEL_KEY::CONTENT,
FMH::MODEL_KEY::COLOR,
FMH::MODEL_KEY::PIN,
FMH::MODEL_KEY::FAVORITE,
FMH::MODEL_KEY::MODIFIED,
FMH::MODEL_KEY::ADDDATE}));
if(this->db->insert(OWL::TABLEMAP[OWL::TABLE::NOTES], __noteMap))
{
for(const auto &tg : note[FMH::MODEL_KEY::TAG].split(",", QString::SplitBehavior::SkipEmptyParts))
this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], note[FMH::MODEL_KEY::ID], note[FMH::MODEL_KEY::COLOR]);
return true;
}
return false;
}
void Syncer::insertRemote(FMH::MODEL &note)
{
return FMH::MODEL {
{FMH::MODEL_KEY::ID, note[FMH::MODEL_KEY::ID]},
{FMH::MODEL_KEY::TITLE, note[FMH::MODEL_KEY::TITLE]},
{FMH::MODEL_KEY::CONTENT, note[FMH::MODEL_KEY::CONTENT]},
{FMH::MODEL_KEY::COLOR, note[FMH::MODEL_KEY::COLOR]},
{FMH::MODEL_KEY::PIN, note[FMH::MODEL_KEY::PIN]},
{FMH::MODEL_KEY::FAVORITE, note[FMH::MODEL_KEY::FAVORITE]},
{FMH::MODEL_KEY::MODIFIED, note[FMH::MODEL_KEY::MODIFIED]},
{FMH::MODEL_KEY::ADDDATE, note[FMH::MODEL_KEY::ADDDATE]}
};
if(this->provider->isValid())
this->provider->insertNote(note);
}
......@@ -19,13 +19,20 @@
struct STATE
{
enum TYPE : uint
{
LOCAL,
REMOTE
};
enum STATUS : uint
{
OK,
ERROR
};
TYPE type;
QString msg;
STATUS status;
QString msg = QString();
};
class DB;
......@@ -39,6 +46,19 @@ class Syncer: public QObject
public:
explicit Syncer(QObject *parent = nullptr);
/**
* @brief setProviderAccount
* sets the credentials to the current account
* for the 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 insertNote
* saves a new note online and offline
......@@ -88,15 +108,15 @@ public:
void getNotes();
private:
/**
* @brief currentAccount
* The current account to store the notes online.
* The account data is represented by FMH::MODEL, using the keys:
* FMH::MODEL_KEY::USER representing the username
* FMH::MODEL_KEY::PASSWORD representing the user password
* FMH::MODEL_KEY::PROVIDER representing the address to the provider server
*/
FMH::MODEL currentAccount;
// /**
// * @brief currentAccount
// * The current account to store the notes online.
// * The account data is represented by FMH::MODEL, using the keys:
// * FMH::MODEL_KEY::USER representing the username
// * FMH::MODEL_KEY::PASSWORD representing the user password
// * FMH::MODEL_KEY::PROVIDER representing the address to the provider server
// */
// FMH::MODEL currentAccount; //no such good idea to have this here, it adds mroe complexity and confusion
/**
* @brief tag
......@@ -141,7 +161,30 @@ private:
* @param note
* @return
*/
static FMH::MODEL packNote(const FMH::MODEL &note);
static FMH::MODEL filterNote(const FMH::MODEL &note, const QVector<FMH::MODEL_KEY> &keys);
protected:
/**
* @brief insertLocal
* performs the insertion of a new note in the local storage
* @param note
* note to be inserted
* @return bool
* true if the note was inserted sucessfully in the local storage
*/
bool insertLocal(FMH::MODEL &note);
/**
* @brief insertRemote
* perfroms the insertion of a new note in the remote provider server
* @param note
* the note to be inserted
*/
void insertRemote(FMH::MODEL &note);
void updateLocal(const QString &id, const FMH::MODEL &note);
void updateRemote(const QString &id, const FMH::MODEL &note);
signals:
void noteInserted(FMH::MODEL note, STATE state);
......@@ -150,9 +193,6 @@ signals:
void noteReady(FMH::MODEL note);
void notesReady(FMH::MODEL_LIST notes);
void accountChanged(FMH::MODEL account);
public slots:
};
......
......@@ -15,43 +15,12 @@
namespace OWL
{
Q_NAMESPACE
enum class W : uint8_t
{
TITLE,
BODY,
IMAGE,
VIDEO,
LINK,
TAG,
AUTHOR,
DATE,
NOTE,
TAGS,
ADD_DATE,
COLOR
};
static const QMap<W, QString> SLANG =
{
{W::TITLE, "title"},
{W::BODY, "body"},
{W::IMAGE, "image"},
{W::VIDEO, "video"},
{W::LINK, "link"},
{W::TAG, "tag"},
{W::AUTHOR, "author"},
{W::DATE, "date"},
{W::NOTE, "note"},
{W::TAGS, "tags"},
{W::ADD_DATE, "addDate"},
{W::COLOR, "color"}
};
Q_NAMESPACE
enum class TABLE : uint8_t
{
NOTES,
NOTES_TAGS,
NOTES_SYNC,
TAGS,
BOOKS,
PAGES,
......@@ -66,6 +35,7 @@ namespace OWL
{
{TABLE::NOTES,"notes"},
{TABLE::NOTES_TAGS,"notes_tags"},
{TABLE::NOTES_SYNC,"notes_sync"},
{TABLE::TAGS,"tags"},
{TABLE::BOOKS,"books"},
{TABLE::PAGES,"pages"},
......@@ -140,18 +110,12 @@ namespace OWL
const QString comment = "Notes taking and link collector manager";
const QString DBName = "collection.db";
inline bool fileExists(const QString &url)
{
QFileInfo path(url);
if (path.exists()) return true;
else return false;
}
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)
......@@ -169,7 +133,6 @@ namespace OWL
inline QString saveImage(QByteArray array, const QString &path)
{
if(!array.isNull()&&!array.isEmpty())
{
QImage img;
......
......@@ -96,7 +96,7 @@ ItemDelegate
Layout.bottomMargin: space.medium
Layout.rightMargin: space.medium
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.preferredHeight: model.preview ? parent.height * 0.4 : undefined
Layout.preferredHeight: if(model.preview) parent.height * 0.4
Layout.fillWidth: true
Layout.fillHeight: true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment