ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #00576
[Merge] lp:~mzanetti/reminders-app/two-job-queues into lp:reminders-app
Michael Zanetti has proposed merging lp:~mzanetti/reminders-app/two-job-queues into lp:reminders-app with lp:~mzanetti/reminders-app/cleanup-debug as a prerequisite.
Commit message:
Further improve the jobqueue by splitting it up into high, medium and low priority queues and optimizing the reply rate.
Requested reviews:
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot): continuous-integration
Riccardo Padovani (rpadovani)
For more details, see:
https://code.launchpad.net/~mzanetti/reminders-app/two-job-queues/+merge/252175
--
Your team Ubuntu Reminders app developers is subscribed to branch lp:reminders-app.
=== modified file 'src/app/qml/reminders.qml'
--- src/app/qml/reminders.qml 2015-03-06 12:16:37 +0000
+++ src/app/qml/reminders.qml 2015-03-08 21:00:47 +0000
@@ -111,6 +111,7 @@
function displayNote(note) {
print("displayNote:", note.guid)
+ note.load(true);
if (root.narrowMode) {
print("creating noteview");
var component = Qt.createComponent(Qt.resolvedUrl("ui/NotePage.qml"));
@@ -127,6 +128,7 @@
}
function switchToEditMode(note) {
+ note.load(true)
if (root.narrowMode) {
if (pagestack.depth > 1) {
pagestack.pop();
=== modified file 'src/app/qml/ui/NotesPage.qml'
--- src/app/qml/ui/NotesPage.qml 2015-03-01 22:32:41 +0000
+++ src/app/qml/ui/NotesPage.qml 2015-03-08 21:00:47 +0000
@@ -177,6 +177,10 @@
syncError: model.syncError
conflicting: model.conflicting
+ Component.onCompleted: {
+ notes.note(model.guid).load(false);
+ }
+
onItemClicked: {
if (!model.conflicting) {
root.selectedNote = NotesStore.note(guid);
=== modified file 'src/libqtevernote/evernoteconnection.cpp'
--- src/libqtevernote/evernoteconnection.cpp 2015-03-06 00:47:45 +0000
+++ src/libqtevernote/evernoteconnection.cpp 2015-03-08 21:00:47 +0000
@@ -153,11 +153,23 @@
return;
}
- foreach (EvernoteJob *job, m_jobQueue) {
- job->emitJobDone(EvernoteConnection::ErrorCodeConnectionLost, "Disconnected from Evernote");
- job->deleteLater();
- }
- m_jobQueue.clear();
+ foreach (EvernoteJob *job, m_highPriorityJobQueue) {
+ job->emitJobDone(EvernoteConnection::ErrorCodeConnectionLost, "Disconnected from Evernote");
+ job->deleteLater();
+ }
+ m_highPriorityJobQueue.clear();
+
+ foreach (EvernoteJob *job, m_mediumPriorityJobQueue) {
+ job->emitJobDone(EvernoteConnection::ErrorCodeConnectionLost, "Disconnected from Evernote");
+ job->deleteLater();
+ }
+ m_mediumPriorityJobQueue.clear();
+
+ foreach (EvernoteJob *job, m_lowPriorityJobQueue) {
+ job->emitJobDone(EvernoteConnection::ErrorCodeConnectionLost, "Disconnected from Evernote");
+ job->deleteLater();
+ }
+ m_lowPriorityJobQueue.clear();
m_errorMessage.clear();
emit errorChanged();
@@ -340,11 +352,38 @@
return false;
}
-EvernoteJob* EvernoteConnection::findDuplicate(EvernoteJob *job)
-{
- foreach (EvernoteJob *queuedJob, m_jobQueue) {
- // explicitly use custom operator==()
- if (job->operator ==(queuedJob)) {
+void EvernoteConnection::attachDuplicate(EvernoteJob *original, EvernoteJob *duplicate)
+{
+ if (duplicate->originatingObject() && duplicate->originatingObject() != original->originatingObject()) {
+ duplicate->attachToDuplicate(m_currentJob);
+ }
+ connect(original, &EvernoteJob::jobFinished, duplicate, &EvernoteJob::deleteLater);
+}
+
+EvernoteJob* EvernoteConnection::findExistingDuplicate(EvernoteJob *job)
+{
+ qWarning(dcJobQueue) << "Length:"
+ << m_highPriorityJobQueue.count() + m_mediumPriorityJobQueue.count() + m_lowPriorityJobQueue.count()
+ << "(High:" << m_highPriorityJobQueue.count() << "Medium:" << m_mediumPriorityJobQueue.count() << "Low:" << m_lowPriorityJobQueue.count() << ")";
+
+ foreach (EvernoteJob *queuedJob, m_highPriorityJobQueue) {
+ // explicitly use custom operator==()
+ if (job->operator ==(queuedJob)) {
+ qCDebug(dcJobQueue) << "Found duplicate in high priority queue";
+ return queuedJob;
+ }
+ }
+ foreach (EvernoteJob *queuedJob, m_mediumPriorityJobQueue) {
+ // explicitly use custom operator==()
+ if (job->operator ==(queuedJob)) {
+ qCDebug(dcJobQueue) << "Found duplicate in medium priority queue";
+ return queuedJob;
+ }
+ }
+ foreach (EvernoteJob *queuedJob, m_lowPriorityJobQueue) {
+ // explicitly use custom operator==()
+ if (job->operator ==(queuedJob)) {
+ qCDebug(dcJobQueue) << "Found duplicate in low priority queue";
return queuedJob;
}
}
@@ -359,26 +398,56 @@
job->deleteLater();
return;
}
- EvernoteJob *duplicate = findDuplicate(job);
- if (duplicate) {
- job->attachToDuplicate(duplicate);
- connect(duplicate, &EvernoteJob::finished, job, &EvernoteJob::deleteLater);
+ if (m_currentJob && m_currentJob->operator ==(job)) {
+ qCDebug(dcJobQueue) << "Duplicate of new job request already running:" << job->toString();
+ if (m_currentJob->isFinished()) {
+ qCWarning(dcJobQueue) << "Job seems to be stuck in a loop. Deleting it:" << job->toString();
+ job->deleteLater();
+ } else {
+ attachDuplicate(m_currentJob, job);
+ }
+ return;
+ }
+ EvernoteJob *existingJob = findExistingDuplicate(job);
+ if (existingJob) {
+ qCDebug(dcJobQueue) << "Duplicate job already queued:" << job->toString();
+ attachDuplicate(existingJob, job);
// reprioritze the repeated request
- qCDebug(dcJobQueue) << "Duplicate job already queued:" << job->toString();
if (job->jobPriority() == EvernoteJob::JobPriorityHigh) {
- qCDebug(dcJobQueue) << "Reprioritising duplicate job:" << job->toString();
- duplicate->setJobPriority(job->jobPriority());
- m_jobQueue.prepend(m_jobQueue.takeAt(m_jobQueue.indexOf(duplicate)));
+ qCDebug(dcJobQueue) << "Reprioritising duplicate job in high priority queue:" << job->toString();
+ existingJob->setJobPriority(job->jobPriority());
+ if (m_highPriorityJobQueue.contains(existingJob)) {
+ m_highPriorityJobQueue.prepend(m_highPriorityJobQueue.takeAt(m_highPriorityJobQueue.indexOf(existingJob)));
+ } else if (m_mediumPriorityJobQueue.contains(existingJob)){
+ m_highPriorityJobQueue.prepend(m_mediumPriorityJobQueue.takeAt(m_mediumPriorityJobQueue.indexOf(existingJob)));
+ } else {
+ m_highPriorityJobQueue.prepend(m_lowPriorityJobQueue.takeAt(m_lowPriorityJobQueue.indexOf(existingJob)));
+ }
+ } else if (job->jobPriority() == EvernoteJob::JobPriorityMedium){
+ if (m_mediumPriorityJobQueue.contains(existingJob)) {
+ qCDebug(dcJobQueue) << "Reprioritising duplicate job in medium priority queue:" << job->toString();
+ m_mediumPriorityJobQueue.prepend(m_mediumPriorityJobQueue.takeAt(m_mediumPriorityJobQueue.indexOf(existingJob)));
+ } else if (m_lowPriorityJobQueue.contains(existingJob)) {
+ m_mediumPriorityJobQueue.prepend(m_lowPriorityJobQueue.takeAt(m_lowPriorityJobQueue.indexOf(existingJob)));
+ }
+ } else if (job->jobPriority() == EvernoteJob::JobPriorityLow) {
+ if (m_lowPriorityJobQueue.contains(existingJob)) {
+ qCDebug(dcJobQueue) << "Reprioritising duplicate job in low priority queue:" << job->toString();
+ m_lowPriorityJobQueue.prepend(m_lowPriorityJobQueue.takeAt(m_lowPriorityJobQueue.indexOf(existingJob)));
+ }
}
} else {
- connect(job, &EvernoteJob::finished, job, &EvernoteJob::deleteLater);
- connect(job, &EvernoteJob::finished, this, &EvernoteConnection::startNextJob);
+ connect(job, &EvernoteJob::jobFinished, job, &EvernoteJob::deleteLater);
+ connect(job, &EvernoteJob::jobFinished, this, &EvernoteConnection::startNextJob);
if (job->jobPriority() == EvernoteJob::JobPriorityHigh) {
- qCDebug(dcJobQueue) << "Prepending high priority job request:" << job->toString();
- m_jobQueue.prepend(job);
+ qCDebug(dcJobQueue) << "Adding high priority job request:" << job->toString();
+ m_highPriorityJobQueue.prepend(job);
+ } else if (job->jobPriority() == EvernoteJob::JobPriorityMedium){
+ qCDebug(dcJobQueue) << "Adding medium priority job request:" << job->toString();
+ m_mediumPriorityJobQueue.prepend(job);
} else {
- qCDebug(dcJobQueue) << "Appending low priority job request:" << job->toString();
- m_jobQueue.append(job);
+ qCDebug(dcJobQueue) << "Adding low priority job request:" << job->toString();
+ m_lowPriorityJobQueue.prepend(job);
}
startJobQueue();
}
@@ -401,16 +470,24 @@
void EvernoteConnection::startJobQueue()
{
- if (m_jobQueue.isEmpty()) {
- return;
- }
-
if (m_currentJob) {
return;
}
- m_currentJob = m_jobQueue.takeFirst();
- qCDebug(dcJobQueue) << "Starting job:" << m_currentJob->toString();
+ if (!m_highPriorityJobQueue.isEmpty()) {
+ m_currentJob = m_highPriorityJobQueue.takeFirst();
+ } else if (!m_mediumPriorityJobQueue.isEmpty()){
+ m_currentJob = m_mediumPriorityJobQueue.takeFirst();
+ } else if (!m_lowPriorityJobQueue.isEmpty()){
+ m_currentJob = m_lowPriorityJobQueue.takeFirst();
+ }
+
+ if (!m_currentJob) {
+ qCDebug(dcJobQueue) << "Queue empty. Nothing to do.";
+ return;
+ }
+
+ qCDebug(dcJobQueue) << QString("Starting job (Priority: %1):").arg(m_currentJob->jobPriority()) << m_currentJob->toString();
m_currentJob->start();
}
=== modified file 'src/libqtevernote/evernoteconnection.h'
--- src/libqtevernote/evernoteconnection.h 2015-02-26 22:47:10 +0000
+++ src/libqtevernote/evernoteconnection.h 2015-03-08 21:00:47 +0000
@@ -73,6 +73,18 @@
QString token() const;
void setToken(const QString &token);
+ // This will add the job to the job queue. The job queue will take ownership of the object
+ // and manage it's lifetime.
+ // * If there is an identical job already existing in the queue, the duplicate will be
+ // attached to original job and not actually fetched a second time from the network in
+ // order to reduce network traffic.
+ // * If the new job has a higher priority than the existing one, the existing one will
+ // reprioritized to the higher priorty.
+ // * If the jobs have different originatingObjects, each job will emit the jobDone signal,
+ // if instead the originatingObject is the same in both jobs, only one of them will emit
+ // a jobDone signal. This is useful if you want to reschedule a job with higher priority
+ // without having to track previously queued jobs and avoid invoking the connected slot
+ // multiple times.
void enqueue(EvernoteJob *job);
bool isConnected() const;
@@ -104,7 +116,10 @@
bool connectUserStore();
bool connectNotesStore();
- EvernoteJob* findDuplicate(EvernoteJob *job);
+ EvernoteJob* findExistingDuplicate(EvernoteJob *job);
+
+ // "duplicate" will be attached to "original"
+ void attachDuplicate(EvernoteJob *original, EvernoteJob *duplicate);
bool m_useSSL;
bool m_isConnected;
@@ -115,7 +130,9 @@
// There must be only one job running at a time
// Do not start jobs other than with startJobQueue()
- QList<EvernoteJob*> m_jobQueue;
+ QList<EvernoteJob*> m_highPriorityJobQueue;
+ QList<EvernoteJob*> m_mediumPriorityJobQueue;
+ QList<EvernoteJob*> m_lowPriorityJobQueue;
EvernoteJob *m_currentJob;
// Those need to be mutexed
=== modified file 'src/libqtevernote/jobs/evernotejob.cpp'
--- src/libqtevernote/jobs/evernotejob.cpp 2015-03-06 00:42:42 +0000
+++ src/libqtevernote/jobs/evernotejob.cpp 2015-03-08 21:00:47 +0000
@@ -38,11 +38,13 @@
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
-EvernoteJob::EvernoteJob(QObject *parent, JobPriority jobPriority) :
- QThread(parent),
+EvernoteJob::EvernoteJob(QObject *originatingObject, JobPriority jobPriority) :
+ QThread(nullptr),
m_token(EvernoteConnection::instance()->token()),
- m_jobPriority(jobPriority)
+ m_jobPriority(jobPriority),
+ m_originatingObject(originatingObject)
{
+ connect(this, &QThread::finished, this, &EvernoteJob::jobFinished);
}
EvernoteJob::~EvernoteJob()
@@ -198,6 +200,11 @@
return metaObject()->className();
}
+QObject *EvernoteJob::originatingObject() const
+{
+ return m_originatingObject;
+}
+
QString EvernoteJob::token()
{
return m_token;
=== modified file 'src/libqtevernote/jobs/evernotejob.h'
--- src/libqtevernote/jobs/evernotejob.h 2015-02-26 22:47:10 +0000
+++ src/libqtevernote/jobs/evernotejob.h 2015-03-08 21:00:47 +0000
@@ -37,8 +37,7 @@
* your job won't be executed but you should instead forward the other's job results.
*
* Jobs can be enqueue()d in NotesStore.
- * They will destroy themselves when finished.
- * The jobqueue will take care about starting them.
+ * The jobqueue will take care about starting them and deleting them.
*/
class EvernoteJob : public QThread
{
@@ -46,10 +45,11 @@
public:
enum JobPriority {
JobPriorityHigh,
+ JobPriorityMedium,
JobPriorityLow
};
- explicit EvernoteJob(QObject *parent = 0, JobPriority jobPriority = JobPriorityHigh);
+ explicit EvernoteJob(QObject *originatingObject = 0, JobPriority jobPriority = JobPriorityHigh);
virtual ~EvernoteJob();
JobPriority jobPriority() const;
@@ -63,9 +63,13 @@
virtual QString toString() const;
+ QObject* originatingObject() const;
+
signals:
void connectionLost(const QString &errorMessage);
+ void jobFinished();
+
protected:
virtual void resetConnection() = 0;
virtual void startJob() = 0;
@@ -76,6 +80,7 @@
private:
QString m_token;
JobPriority m_jobPriority;
+ QObject *m_originatingObject;
friend class EvernoteConnection;
};
=== modified file 'src/libqtevernote/jobs/fetchnotesjob.cpp'
--- src/libqtevernote/jobs/fetchnotesjob.cpp 2015-03-06 00:47:45 +0000
+++ src/libqtevernote/jobs/fetchnotesjob.cpp 2015-03-08 21:00:47 +0000
@@ -41,7 +41,9 @@
return false;
}
return this->m_filterNotebookGuid == otherJob->m_filterNotebookGuid
- && this->m_searchWords == otherJob->m_searchWords;
+ && this->m_searchWords == otherJob->m_searchWords
+ && this->m_startIndex == otherJob->m_startIndex
+ && this->m_chunkSize == otherJob->m_chunkSize;
}
void FetchNotesJob::attachToDuplicate(const EvernoteJob *other)
=== modified file 'src/libqtevernote/note.cpp'
--- src/libqtevernote/note.cpp 2015-03-08 16:01:23 +0000
+++ src/libqtevernote/note.cpp 2015-03-08 21:00:47 +0000
@@ -38,7 +38,6 @@
m_isSearchResult(false),
m_updateSequenceNumber(updateSequenceNumber),
m_loading(false),
- m_loadingHighPriority(false),
m_loaded(false),
m_needsContentSync(false),
m_syncError(false),
@@ -64,15 +63,9 @@
infoFile.beginGroup("resources");
foreach (const QString &hash, infoFile.childGroups()) {
- if (Resource::isCached(hash)) {
- infoFile.beginGroup(hash);
- // Assuming the resource is already cached...
- addResource(QByteArray(), hash, infoFile.value("fileName").toString(), infoFile.value("type").toString());
- infoFile.endGroup();
- } else {
- // uh oh... have a resource description without file... reset sequence number to indicate we need a sync
- qCWarning(dcNotesStore) << "Have a resource description but no resource file for it";
- }
+ infoFile.beginGroup(hash);
+ addResource(hash, infoFile.value("fileName").toString(), infoFile.value("type").toString());
+ infoFile.endGroup();
}
infoFile.endGroup();
@@ -243,7 +236,6 @@
QString Note::enmlContent() const
{
- load();
return m_content.enml();
}
@@ -263,13 +255,11 @@
QString Note::htmlContent() const
{
- load();
return m_content.toHtml(m_guid);
}
QString Note::richTextContent() const
{
- load();
return m_content.toRichText(m_guid);
}
@@ -286,15 +276,11 @@
QString Note::plaintextContent() const
{
- load();
return m_content.toPlaintext().trimmed();
}
QString Note::tagline() const
{
- if (m_tagline.isEmpty()) {
- load(false);
- }
return m_tagline;
}
@@ -488,11 +474,12 @@
QStringList Note::resourceUrls() const
{
QList<QString> ret;
- foreach (const QString &hash, m_resources.keys()) {
- QUrl url("image://resource/" + m_resources.value(hash)->type());
+ foreach (Resource *resource, m_resources) {
+ QUrl url("image://resource/" + resource->type());
QUrlQuery arguments;
arguments.addQueryItem("noteGuid", m_guid);
- arguments.addQueryItem("hash", hash);
+ arguments.addQueryItem("hash", resource->hash());
+ arguments.addQueryItem("loaded", resource->isCached() ? "true" : "false");
url.setQuery(arguments);
ret << url.toString();
}
@@ -505,25 +492,29 @@
}
-Resource* Note::addResource(const QByteArray &data, const QString &hash, const QString &fileName, const QString &type)
+Resource* Note::addResource(const QString &hash, const QString &fileName, const QString &type, const QByteArray &data)
{
+ Resource *resource;
if (m_resources.contains(hash)) {
- return m_resources.value(hash);
+ resource = m_resources.value(hash);
+ if (!data.isEmpty()) {
+ resource->setData(data);
+ }
+ } else {
+ resource = new Resource(data, hash, fileName, type, this);
+ m_resources.insert(hash, resource);
+ QSettings infoFile(m_infoFile, QSettings::IniFormat);
+ infoFile.beginGroup("resources");
+ infoFile.beginGroup(hash);
+ infoFile.setValue("fileName", fileName);
+ infoFile.setValue("type", type);
+ infoFile.endGroup();
+ infoFile.endGroup();
}
- Resource *resource = new Resource(data, hash, fileName, type, this);
- m_resources.insert(hash, resource);
emit resourcesChanged();
emit contentChanged();
- QSettings infoFile(m_infoFile, QSettings::IniFormat);
- infoFile.beginGroup("resources");
- infoFile.beginGroup(hash);
- infoFile.setValue("fileName", fileName);
- infoFile.setValue("type", type);
- infoFile.endGroup();
- infoFile.endGroup();
-
return resource;
}
@@ -593,7 +584,7 @@
note->setUpdateSequenceNumber(m_updateSequenceNumber);
note->setDeleted(m_deleted);
foreach (Resource *resource, m_resources) {
- note->addResource(resource->data(), resource->hash(), resource->fileName(), resource->type());
+ note->addResource(resource->hash(), resource->fileName(), resource->type(), resource->data());
}
note->m_needsContentSync = m_needsContentSync;
@@ -625,12 +616,6 @@
if (m_loading != loading) {
m_loading = loading;
emit loadingChanged();
-
- if (!m_loading) {
- m_loadingHighPriority = false;
- } else {
- m_loadingHighPriority = highPriority;
- }
}
}
@@ -670,13 +655,22 @@
}
}
-void Note::load(bool priorityHigh) const
+void Note::load(bool priorityHigh)
{
if (!m_loaded && isCached()) {
loadFromCacheFile();
- } else if (!m_loaded) {
- if (!m_loading || (priorityHigh && !m_loadingHighPriority)) {
- NotesStore::instance()->refreshNoteContent(m_guid, FetchNoteJob::LoadContent, priorityHigh ? EvernoteJob::JobPriorityHigh : EvernoteJob::JobPriorityLow);
+ }
+
+ if (!m_loaded) {
+ NotesStore::instance()->refreshNoteContent(m_guid, FetchNoteJob::LoadContent, priorityHigh ? EvernoteJob::JobPriorityHigh : EvernoteJob::JobPriorityMedium);
+ return;
+ }
+
+ // Check if resources are loaded
+ foreach (Resource *resource, m_resources) {
+ if (!resource->isCached()) {
+ NotesStore::instance()->refreshNoteContent(m_guid, FetchNoteJob::LoadResources, priorityHigh ? EvernoteJob::JobPriorityHigh : EvernoteJob::JobPriorityLow);
+ break;
}
}
}
=== modified file 'src/libqtevernote/note.h'
--- src/libqtevernote/note.h 2015-03-08 16:01:23 +0000
+++ src/libqtevernote/note.h 2015-03-08 21:00:47 +0000
@@ -157,7 +157,7 @@
QStringList resourceUrls() const;
Q_INVOKABLE Resource* resource(const QString &hash);
QList<Resource*> resources() const;
- Resource *addResource(const QByteArray &data, const QString &hash, const QString &fileName, const QString &type);
+
Q_INVOKABLE void markTodo(const QString &todoId, bool checked);
Q_INVOKABLE void attachFile(int position, const QUrl &fileName);
@@ -167,6 +167,8 @@
int renderWidth() const;
void setRenderWidth(int renderWidth);
+ Q_INVOKABLE void load(bool highPriority = false);
+
public slots:
void save();
void remove();
@@ -210,9 +212,10 @@
void setUpdateSequenceNumber(qint32 updateSequenceNumber);
void setLastSyncedSequenceNumber(qint32 lastSyncedSequenceNumber);
void setConflicting(bool conflicting);
+ Resource *addResource(const QString &hash, const QString &fileName, const QString &type, const QByteArray &data = QByteArray());
+ void addMissingResource();
+ void setMissingResources(int missingResources);
- // const because we want to load on demand in getters. Keep this private!
- void load(bool priorityHigh = true) const;
void loadFromCacheFile() const;
private:
@@ -236,7 +239,6 @@
QString m_infoFile;
bool m_loading;
- bool m_loadingHighPriority;
mutable bool m_loaded;
bool m_synced;
bool m_needsContentSync;
=== modified file 'src/libqtevernote/notesstore.cpp'
--- src/libqtevernote/notesstore.cpp 2015-03-08 16:01:23 +0000
+++ src/libqtevernote/notesstore.cpp 2015-03-08 21:00:47 +0000
@@ -677,7 +677,7 @@
if (note->updateSequenceNumber() < result.updateSequenceNum) {
qCDebug(dcSync) << "refreshing note from network. suequence number changed: " << note->updateSequenceNumber() << "->" << result.updateSequenceNum;
changedRoles = updateFromEDAM(result, note);
- refreshNoteContent(note->guid(), FetchNoteJob::LoadContent, EvernoteJob::JobPriorityLow);
+ refreshNoteContent(note->guid(), FetchNoteJob::LoadContent, EvernoteJob::JobPriorityMedium);
syncToCacheFile(note);
}
} else {
@@ -798,15 +798,18 @@
return;
}
if (EvernoteConnection::instance()->isConnected()) {
- qCDebug(dcNotesStore) << "Fetching note content from network for note" << guid << (what == FetchNoteJob::LoadContent ? "content" : "image");
+ qCDebug(dcNotesStore) << "Fetching note content from network for note" << guid << (what == FetchNoteJob::LoadContent ? "Content" : "Resource") << "Priority:" << priority;
FetchNoteJob *job = new FetchNoteJob(guid, what, this);
job->setJobPriority(priority);
connect(job, &FetchNoteJob::resultReady, this, &NotesStore::fetchNoteJobDone);
EvernoteConnection::instance()->enqueue(job);
+ bool wasLoading = note->loading();
note->setLoading(true, priority == EvernoteJob::JobPriorityHigh);
- int idx = m_notes.indexOf(note);
- emit dataChanged(index(idx), index(idx), QVector<int>() << RoleLoading);
+ if (!wasLoading) {
+ int idx = m_notes.indexOf(note);
+ emit dataChanged(index(idx), index(idx), QVector<int>() << RoleLoading);
+ }
}
}
@@ -818,16 +821,13 @@
qCWarning(dcSync) << "can't find note for this update... ignoring...";
return;
}
+
QModelIndex noteIndex = index(m_notes.indexOf(note));
QVector<int> roles;
- note->setLoading(false);
- roles << RoleLoading;
-
switch (errorCode) {
case EvernoteConnection::ErrorCodeNoError:
// All is well
- emit dataChanged(noteIndex, noteIndex, roles);
break;
case EvernoteConnection::ErrorCodeUserException:
qCWarning(dcSync) << "FetchNoteJobDone: EDAMUserException:" << errorMessage;
@@ -877,15 +877,17 @@
QString mime = QString::fromStdString(resource.mime);
if (what == FetchNoteJob::LoadResources) {
- qCDebug(dcSync) << "Resource fetched for note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
+ qCDebug(dcSync) << "Resource content fetched for note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
QByteArray resourceData = QByteArray(resource.data.body.data(), resource.data.size);
- note->addResource(resourceData, hash, fileName, mime);
- } else if (Resource::isCached(hash)) {
- qCDebug(dcSync) << "Resource already cached for note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
- note->addResource(QByteArray(), hash, fileName, mime);
+ note->addResource(hash, fileName, mime, resourceData);
} else {
- qCDebug(dcSync) << "Resource not yet fetched for note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
- refreshWithResourceData = true;
+ qCDebug(dcSync) << "Adding resource info to note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
+ Resource *resource = note->addResource(hash, fileName, mime);
+
+ if (!resource->isCached()) {
+ qCDebug(dcSync) << "Resource not yet fetched for note:" << note->guid() << "Filename:" << fileName << "Mimetype:" << mime << "Hash:" << hash;
+ refreshWithResourceData = true;
+ }
}
roles << RoleHtmlContent << RoleEnmlContent << RoleResourceUrls;
}
@@ -915,17 +917,20 @@
note->setReminderDoneTime(reminderDoneTime);
roles << RoleReminderDone << RoleReminderDoneTime;
}
+
+ note->setLoading(false);
+ roles << RoleLoading;
+
emit noteChanged(note->guid(), note->notebookGuid());
-
emit dataChanged(noteIndex, noteIndex, roles);
if (refreshWithResourceData) {
qCDebug(dcSync) << "Fetching Note resources:" << note->guid();
- refreshNoteContent(note->guid(), FetchNoteJob::LoadResources, job->jobPriority());
- } else {
- syncToCacheFile(note); // Syncs into the list cache
- note->syncToCacheFile(); // Syncs note's content into notes cache
+ EvernoteJob::JobPriority newPriority = job->jobPriority() == EvernoteJob::JobPriorityMedium ? EvernoteJob::JobPriorityLow : job->jobPriority();
+ refreshNoteContent(note->guid(), FetchNoteJob::LoadResources, newPriority);
}
+ syncToCacheFile(note); // Syncs into the list cache
+ note->syncToCacheFile(); // Syncs note's content into notes cache
}
void NotesStore::refreshNotebooks()
@@ -1004,6 +1009,8 @@
}
}
+ qCDebug(dcSync) << "Remote notebooks merged into storage. Merging local changes to server.";
+
foreach (Notebook *notebook, unhandledNotebooks) {
if (notebook->lastSyncedSequenceNumber() == 0) {
qCDebug(dcSync) << "Have a local notebook that doesn't exist on Evernote. Creating on server:" << notebook->guid();
@@ -1027,6 +1034,8 @@
notebook->deleteLater();
}
}
+
+ qCDebug(dcSync) << "Notebooks merged.";
}
void NotesStore::refreshTags()
=== modified file 'src/libqtevernote/resource.cpp'
--- src/libqtevernote/resource.cpp 2015-03-06 00:47:45 +0000
+++ src/libqtevernote/resource.cpp 2015-03-08 21:00:47 +0000
@@ -52,15 +52,16 @@
}
}
-bool Resource::isCached(const QString &hash)
-{
- QDir cacheDir(NotesStore::instance()->storageLocation());
- foreach (const QString fi, cacheDir.entryList()) {
- if (fi.contains(hash)) {
- return true;
- }
- }
- return false;
+Resource::Resource(const QString &hash, const QString &fileName, const QString &type, QObject *parent):
+ Resource(QByteArray(), hash, fileName, type, parent)
+{
+
+}
+
+bool Resource::isCached()
+{
+ QFileInfo fi(m_filePath);
+ return fi.exists();
}
Resource::Resource(const QString &path, QObject *parent):
@@ -159,3 +160,13 @@
}
return QByteArray();
}
+
+void Resource::setData(const QByteArray &data)
+{
+ QFile file(m_filePath);
+ if (file.open(QFile::WriteOnly | QFile::Truncate)) {
+ file.write(data);
+ } else {
+ qCDebug(dcNotesStore) << "Error saving data for resource:" << m_hash;
+ }
+}
=== modified file 'src/libqtevernote/resource.h'
--- src/libqtevernote/resource.h 2015-02-16 21:57:16 +0000
+++ src/libqtevernote/resource.h 2015-03-08 21:00:47 +0000
@@ -37,10 +37,12 @@
public:
Resource(const QString &path, QObject *parent = 0);
Resource(const QByteArray &data, const QString &hash, const QString &fileName, const QString &type, QObject *parent = 0);
+ Resource(const QString &hash, const QString &fileName, const QString &type, QObject *parent = 0);
- static bool isCached(const QString &hash);
+ bool isCached();
QByteArray data() const;
+ void setData(const QByteArray &data);
QString hash() const;
QString fileName() const;
QString type() const;
=== modified file 'src/libqtevernote/resourceimageprovider.cpp'
--- src/libqtevernote/resourceimageprovider.cpp 2015-03-06 00:47:45 +0000
+++ src/libqtevernote/resourceimageprovider.cpp 2015-03-08 21:00:47 +0000
@@ -5,6 +5,7 @@
#include <note.h>
#include <QUrlQuery>
+#include <QFileInfo>
ResourceImageProvider::ResourceImageProvider():
QQuickImageProvider(QQuickImageProvider::Image)
@@ -18,6 +19,7 @@
QUrlQuery arguments(id.split('?').last());
QString noteGuid = arguments.queryItemValue("noteGuid");
QString resourceHash = arguments.queryItemValue("hash");
+ bool isLoaded = arguments.queryItemValue("loaded") == "true";
Note *note = NotesStore::instance()->note(noteGuid);
if (!note) {
qCWarning(dcNotesStore) << "Unable to find note for resource:" << id;
@@ -26,19 +28,47 @@
QImage image;
if (mediaType.startsWith("image")) {
- QSize tmpSize = requestedSize;
- if (!requestedSize.isValid() || requestedSize.width() > 1024 || requestedSize.height() > 1024) {
- tmpSize = QSize(1024, 1024);
+ if (isLoaded) {
+ QSize tmpSize = requestedSize;
+ if (!requestedSize.isValid() || requestedSize.width() > 1024 || requestedSize.height() > 1024) {
+ tmpSize = QSize(1024, 1024);
+ }
+ image = QImage::fromData(NotesStore::instance()->note(noteGuid)->resource(resourceHash)->imageData(tmpSize));
+ } else {
+ image = loadIcon("image-x-generic-symbolic", requestedSize);
}
- image = QImage::fromData(NotesStore::instance()->note(noteGuid)->resource(resourceHash)->imageData(tmpSize));
} else if (mediaType.startsWith("audio")) {
- image.load("/usr/share/icons/suru/mimetypes/scalable/audio-x-generic-symbolic.svg");
+ image = loadIcon("audio-x-generic-symbolic", requestedSize);
} else if (mediaType == "application/pdf") {
- image.load("/usr/share/icons/suru/mimetypes/scalable/application-pdf-symbolic.svg");
+ image = loadIcon("application-pdf-symbolic", requestedSize);
} else {
- image.load("/usr/share/icons/suru/mimetypes/scalable/empty-symbolic.svg");
+ image = loadIcon("empty-symbolic", requestedSize);
}
*size = image.size();
return image;
}
+
+QImage ResourceImageProvider::loadIcon(const QString &name, const QSize &size)
+{
+ QString path = QString("/home/phablet/.cache/com.ubuntu.reminders/%1_%2x%3.png").arg(name).arg(size.width()).arg(size.height());
+ QFileInfo fi(path);
+ if (fi.exists()) {
+ QImage image;
+ image.load(path);
+ return image;
+ }
+
+ QString svgPath = "/usr/share/icons/suru/mimetypes/scalable/" + name + ".svg";
+ QImage image;
+ image.load(svgPath);
+ if (size.height() > 0 && size.width() > 0) {
+ image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ } else if (size.height() > 0) {
+ image = image.scaledToHeight(size.height(), Qt::SmoothTransformation);
+ } else if (size.width() > 0) {
+ image = image.scaledToWidth(size.width(), Qt::SmoothTransformation);
+ }
+ image.save(path);
+ return image;
+}
=== modified file 'src/libqtevernote/resourceimageprovider.h'
--- src/libqtevernote/resourceimageprovider.h 2014-10-23 21:27:46 +0000
+++ src/libqtevernote/resourceimageprovider.h 2015-03-08 21:00:47 +0000
@@ -9,6 +9,9 @@
explicit ResourceImageProvider();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+
+ void scale(QImage &image, const QSize &size);
+ QImage loadIcon(const QString &name, const QSize &size);
};
#endif // RESOURCEIMAGEPROVIDER_H
=== modified file 'src/libqtevernote/utils/enmldocument.cpp'
--- src/libqtevernote/utils/enmldocument.cpp 2015-03-08 16:01:23 +0000
+++ src/libqtevernote/utils/enmldocument.cpp 2015-03-08 21:00:47 +0000
@@ -296,6 +296,7 @@
QUrlQuery arguments;
arguments.addQueryItem("noteGuid", noteGuid);
arguments.addQueryItem("hash", hash);
+ arguments.addQueryItem("loaded", NotesStore::instance()->note(noteGuid)->resource(hash)->isCached() ? "true" : "false");
url.setQuery(arguments);
return url.toString();
}
Follow ups