← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~mzanetti/reminders-app/delete-tags-and-notebooks into lp:reminders-app

 

Michael Zanetti has proposed merging lp:~mzanetti/reminders-app/delete-tags-and-notebooks into lp:reminders-app.

Commit message:
implement support for deleting notebooks and tags

both features can only be used with offline mode because the evernote api
doesn't allow 3rd Party applications to execute those methods. However,
this required using ListItemWithAction for notebooks and tags and with
that some polishing of those delegates. Also makes better use of the
error label.

Requested reviews:
  Ubuntu Reminders app developers (reminders-app-dev)
Related bugs:
  Bug #1427837 in Ubuntu Reminders app: "TAGS: no way to delete a tag"
  https://bugs.launchpad.net/reminders-app/+bug/1427837

For more details, see:
https://code.launchpad.net/~mzanetti/reminders-app/delete-tags-and-notebooks/+merge/251677
-- 
Your team Ubuntu Reminders app developers is requested to review the proposed merge of lp:~mzanetti/reminders-app/delete-tags-and-notebooks into lp:reminders-app.
=== modified file 'src/app/qml/components/NotebooksDelegate.qml'
--- src/app/qml/components/NotebooksDelegate.qml	2014-12-16 23:01:22 +0000
+++ src/app/qml/components/NotebooksDelegate.qml	2015-03-04 00:26:40 +0000
@@ -22,12 +22,38 @@
 import Ubuntu.Components.ListItems 1.0
 import Evernote 0.1
 
-Empty {
+ListItemWithActions {
     id: root
-    height: units.gu(10)
 
     property string notebookColor: preferences.colorForNotebook(model.guid)
 
+    signal deleteNotebook();
+    signal setAsDefault();
+    signal renameNotebook();
+
+    leftSideAction: Action {
+        iconName: "delete"
+        text: i18n.tr("Delete")
+        onTriggered: {
+            root.deleteNotebook();
+        }
+    }
+
+    rightSideActions: [
+        Action {
+            iconName: model.isDefaultNotebook ? "starred" : "non-starred"
+            onTriggered: {
+                root.setAsDefault();
+            }
+        },
+        Action {
+            iconName: "edit"
+            onTriggered: {
+                root.renameNotebook();
+            }
+        }
+    ]
+
     Rectangle {
         anchors.fill: parent
         color: "#f9f9f9"
@@ -61,34 +87,6 @@
                 color: root.notebookColor
                 fontSize: "large"
                 Layout.fillWidth: true
-
-                MouseArea {
-                    onPressAndHold: {
-                        notebookTitleLabel.visible = false;
-                        notebookTitleTextField.forceActiveFocus();
-                    }
-                    anchors.fill: parent
-                    propagateComposedEvents: true
-                }
-            }
-
-            TextField {
-                id: notebookTitleTextField
-                text: model.name
-                color: root.notebookColor
-                visible: !notebookTitleLabel.visible
-                Layout.fillWidth: true
-
-                InverseMouseArea {
-                    onClicked: {
-                        if (notebookTitleTextField.text) {
-                            notebooks.notebook(index).name = notebookTitleTextField.text;
-                            NotesStore.saveNotebook(notebooks.notebook(index).guid);
-                            notebookTitleLabel.visible = true;
-                        }
-                    }
-                    anchors.fill: parent
-                }
             }
 
             Label {
@@ -99,14 +97,25 @@
                 Layout.fillWidth: true
             }
 
-            Label {
-                objectName: 'notebookPublishedLabel'
+            Row {
                 Layout.fillHeight: true
-                text: model.published ? i18n.tr("Shared") : i18n.tr("Private")
-                color: model.published ? "black" : "#b3b3b3"
-                fontSize: "x-small"
-                verticalAlignment: Text.AlignVCenter
-                font.bold: model.published
+                spacing: units.gu(1)
+                Icon {
+                    height: parent.height
+                    width: height
+                    name: "starred"
+                    visible: model.isDefaultNotebook
+                }
+
+                Label {
+                    objectName: 'notebookPublishedLabel'
+                    anchors.verticalCenter: parent.verticalCenter
+                    text: model.published ? i18n.tr("Shared") : i18n.tr("Private")
+                    color: model.published ? "black" : "#b3b3b3"
+                    fontSize: "x-small"
+                    verticalAlignment: Text.AlignVCenter
+                    font.bold: model.published
+                }
             }
         }
 

=== modified file 'src/app/qml/components/StatusBar.qml'
--- src/app/qml/components/StatusBar.qml	2014-12-08 10:25:48 +0000
+++ src/app/qml/components/StatusBar.qml	2015-03-04 00:26:40 +0000
@@ -21,20 +21,22 @@
         anchors { left: parent.left; top: parent.top; right: parent.right }
         spacing: units.gu(1)
 
-        RowLayout {
+        Row {
             anchors { left: parent.left; right: parent.right; margins: units.gu(1) }
             spacing: units.gu(1)
+            height: label.height
 
             Icon {
                 id: icon
                 height: units.gu(3)
                 width: height
                 color: UbuntuColors.red
+                anchors.verticalCenter: parent.verticalCenter
             }
 
             Label {
                 id: label
-                Layout.fillWidth: true
+                width: parent.width - x
                 wrapMode: Text.WordWrap
             }
         }

=== modified file 'src/app/qml/components/TagsDelegate.qml'
--- src/app/qml/components/TagsDelegate.qml	2014-12-14 21:52:26 +0000
+++ src/app/qml/components/TagsDelegate.qml	2015-03-04 00:26:40 +0000
@@ -22,10 +22,30 @@
 import Ubuntu.Components.ListItems 1.0
 import Evernote 0.1
 
-Empty {
+ListItemWithActions {
     id: root
     height: units.gu(10)
 
+    signal deleteTag();
+    signal renameTag();
+
+    leftSideAction: Action {
+        iconName: "delete"
+        text: i18n.tr("Delete")
+        onTriggered: {
+            root.deleteTag()
+        }
+    }
+
+    rightSideActions: [
+        Action {
+            iconName: "edit"
+            onTriggered: {
+                root.renameTag();
+            }
+        }
+    ]
+
     Rectangle {
         anchors.fill: parent
         color: "#f9f9f9"
@@ -58,32 +78,6 @@
                 text: model.name
                 fontSize: "large"
                 Layout.fillWidth: true
-
-                MouseArea {
-                    onPressAndHold: {
-                        tagTitleLabel.visible = false;
-                        tagTitleTextField.forceActiveFocus();
-                    }
-                    anchors.fill: parent
-                    propagateComposedEvents: true
-                }
-            }
-
-            TextField {
-                id: tagTitleTextField
-                text: model.name
-                visible: !tagTitleLabel.visible
-
-                InverseMouseArea {
-                    onClicked: {
-                        if (tagTitleTextField.text) {
-                            tags.tag(index).name = tagTitleTextField.text;
-                            NotesStore.saveTag(tags.tag(index).guid);
-                            tagTitleLabel.visible = true;
-                        }
-                    }
-                    anchors.fill: parent
-                }
             }
         }
 

=== modified file 'src/app/qml/reminders.qml'
--- src/app/qml/reminders.qml	2015-03-01 22:32:41 +0000
+++ src/app/qml/reminders.qml	2015-03-04 00:26:40 +0000
@@ -422,9 +422,21 @@
         anchors { left: parent.left; right: parent.right; top: parent.top; topMargin: units.gu(9) }
         color: root.backgroundColor
         shown: text
-        text: EvernoteConnection.error || NotesStore.error || NotesStore.notebooksError || NotesStore.tagsError
+        text: EvernoteConnection.error || NotesStore.error
         iconName: "sync-error"
 
+        Timer {
+            interval: 5000
+            repeat: true
+            running: NotesStore.error
+            onTriggered: NotesStore.clearError();
+        }
+
+        MouseArea {
+            anchors.fill: parent
+            onClicked: NotesStore.clearError();
+        }
+
     }
 
     PageStack {

=== modified file 'src/app/qml/ui/NotebooksPage.qml'
--- src/app/qml/ui/NotebooksPage.qml	2015-03-01 22:32:41 +0000
+++ src/app/qml/ui/NotebooksPage.qml	2015-03-04 00:26:40 +0000
@@ -19,6 +19,7 @@
 import QtQuick 2.3
 import Ubuntu.Components 1.1
 import Ubuntu.Components.ListItems 1.0
+import Ubuntu.Components.Popups 1.0
 import Evernote 0.1
 import "../components"
 
@@ -116,10 +117,30 @@
             }
 
             delegate: NotebooksDelegate {
-                onClicked: {
+                width: parent.width
+                height: units.gu(10)
+                triggerActionOnMouseRelease: true
+
+                onItemClicked: {
                     print("selected notebook:", model.guid)
                     root.openNotebook(model.guid)
                 }
+
+                onDeleteNotebook: {
+                    NotesStore.expungeNotebook(model.guid)
+                }
+
+                onSetAsDefault: {
+                    NotesStore.setDefaultNotebook(model.guid)
+                }
+
+                onRenameNotebook: {
+                    var popup = PopupUtils.open(renameNotebookDialogComponent, root, {name: model.name})
+                    popup.accepted.connect(function(newName) {
+                        notebooks.notebook(index).name = newName;
+                        NotesStore.saveNotebook(model.guid);
+                    })
+                }
             }
 
             BouncingProgressBar {
@@ -170,4 +191,32 @@
             height: Qt.inputMethod.keyboardRectangle.height
         }
     }
+
+    Component {
+        id: renameNotebookDialogComponent
+        Dialog {
+            id: renameNotebookDialog
+            title: i18n.tr("Rename notebook")
+            text: i18n.tr("Enter a new name for notebook %1").arg(name)
+
+            property string name
+
+            signal accepted(string newName)
+
+            TextField {
+                id: nameTextField
+                text: renameNotebookDialog.name
+                placeholderText: i18n.tr("Name cannot be empty")
+            }
+
+            Button {
+                text: i18n.tr("OK")
+                enabled: nameTextField.text
+                onClicked: {
+                    renameNotebookDialog.accepted(nameTextField.text)
+                    PopupUtils.close(renameNotebookDialog)
+                }
+            }
+        }
+    }
 }

=== modified file 'src/app/qml/ui/TagsPage.qml'
--- src/app/qml/ui/TagsPage.qml	2015-03-01 22:32:41 +0000
+++ src/app/qml/ui/TagsPage.qml	2015-03-04 00:26:40 +0000
@@ -19,6 +19,7 @@
 import QtQuick 2.3
 import Ubuntu.Components 1.1
 import Ubuntu.Components.ListItems 1.0
+import Ubuntu.Components.Popups 1.0
 import Evernote 0.1
 import "../components"
 
@@ -80,10 +81,25 @@
             }
 
             delegate: TagsDelegate {
-                onClicked: {
+                width: parent.width
+                height: units.gu(10)
+                triggerActionOnMouseRelease: true
+
+                onItemClicked: {
                     print("selected tag:", model.guid)
                     root.openTaggedNotes(model.guid)
                 }
+                onDeleteTag: {
+                    NotesStore.expungeTag(model.guid);
+                }
+
+                onRenameTag: {
+                    var popup = PopupUtils.open(renameTagDialogComponent, root, {name: model.name})
+                    popup.accepted.connect(function(newName) {
+                        tags.tag(index).name = newName;
+                        NotesStore.saveTag(model.guid);
+                    })
+                }
             }
 
             ActivityIndicator {
@@ -92,15 +108,6 @@
                 visible: running
             }
 
-            Label {
-                anchors.centerIn: parent
-                width: parent.width - units.gu(4)
-                wrapMode: Text.WordWrap
-                horizontalAlignment: Text.AlignHCenter
-                visible: !tags.loading && tags.error
-                text: tags.error
-            }
-
             Scrollbar {
                 flickableItem: parent
             }
@@ -121,4 +128,32 @@
         horizontalAlignment: Text.AlignHCenter
         text: i18n.tr("No tags available. You can tag notes while viewing them.")
     }
+
+    Component {
+        id: renameTagDialogComponent
+        Dialog {
+            id: renameTagDialog
+            title: i18n.tr("Rename tag")
+            text: i18n.tr("Enter a new name for tag %1").arg(name)
+
+            property string name
+
+            signal accepted(string newName)
+
+            TextField {
+                id: nameTextField
+                text: renameTagDialog.name
+                placeholderText: i18n.tr("Name cannot be empty")
+            }
+
+            Button {
+                text: i18n.tr("OK")
+                enabled: nameTextField.text
+                onClicked: {
+                    renameTagDialog.accepted(nameTextField.text)
+                    PopupUtils.close(renameTagDialog)
+                }
+            }
+        }
+    }
 }

=== modified file 'src/libqtevernote/jobs/savenotebookjob.cpp'
--- src/libqtevernote/jobs/savenotebookjob.cpp	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/jobs/savenotebookjob.cpp	2015-03-04 00:26:40 +0000
@@ -56,6 +56,8 @@
     m_resultNotebook.__isset.name = true;
     m_resultNotebook.updateSequenceNum = m_notebook->updateSequenceNumber();
     m_resultNotebook.__isset.updateSequenceNum = true;
+    m_resultNotebook.defaultNotebook = m_notebook->isDefaultNotebook();
+    m_resultNotebook.__isset.defaultNotebook = true;
 
     client()->updateNotebook(token().toStdString(), m_resultNotebook);
 }

=== modified file 'src/libqtevernote/notebook.cpp'
--- src/libqtevernote/notebook.cpp	2015-02-24 22:21:04 +0000
+++ src/libqtevernote/notebook.cpp	2015-03-04 00:26:40 +0000
@@ -32,6 +32,7 @@
     m_updateSequenceNumber(updateSequenceNumber),
     m_guid(guid),
     m_published(false),
+    m_isDefaultNotebook(false),
     m_loading(false),
     m_syncError(false)
 {
@@ -41,6 +42,7 @@
     m_published = infoFile.value("published").toBool();
     m_lastUpdated = infoFile.value("lastUpdated").toDateTime();
     m_lastSyncedSequenceNumber = infoFile.value("lastSyncedSequenceNumber", 0).toUInt();
+    m_isDefaultNotebook = infoFile.value("isDefaultNotebook", false).toBool();
     m_synced = m_lastSyncedSequenceNumber == m_updateSequenceNumber;
 
     foreach (Note *note, NotesStore::instance()->notes()) {
@@ -77,6 +79,11 @@
     return m_notesList.count();
 }
 
+QString Notebook::noteAt(int index) const
+{
+    return m_notesList.at(index);
+}
+
 bool Notebook::published() const
 {
     return m_published;
@@ -129,6 +136,19 @@
     return QString(gettext("on %1 %2")).arg(QLocale::system().standaloneMonthName(updateDate.month())).arg(updateDate.year());
 }
 
+bool Notebook::isDefaultNotebook() const
+{
+    return m_isDefaultNotebook;
+}
+
+void Notebook::setIsDefaultNotebook(bool isDefaultNotebook)
+{
+    if (m_isDefaultNotebook != isDefaultNotebook) {
+        m_isDefaultNotebook = isDefaultNotebook;
+        emit isDefaultNotebookChanged();
+    }
+}
+
 Notebook *Notebook::clone()
 {
     Notebook *notebook = new Notebook(m_guid, m_updateSequenceNumber);
@@ -212,6 +232,7 @@
     infoFile.setValue("published", m_published);
     infoFile.value("lastUpdated", m_lastUpdated);
     infoFile.setValue("lastSyncedSequenceNumber", m_lastSyncedSequenceNumber);
+    infoFile.setValue("isDefaultNotebook", m_isDefaultNotebook);
 }
 
 void Notebook::deleteInfoFile()

=== modified file 'src/libqtevernote/notebook.h'
--- src/libqtevernote/notebook.h	2014-12-13 00:41:41 +0000
+++ src/libqtevernote/notebook.h	2015-03-04 00:26:40 +0000
@@ -36,6 +36,7 @@
     Q_PROPERTY(bool published READ published NOTIFY publishedChanged)
     Q_PROPERTY(QDateTime lastUpdated READ lastUpdated NOTIFY lastUpdatedChanged)
     Q_PROPERTY(QString lastUpdatedString READ lastUpdatedString NOTIFY lastUpdatedChanged)
+    Q_PROPERTY(bool isDefaultNotebook READ isDefaultNotebook WRITE setIsDefaultNotebook NOTIFY isDefaultNotebookChanged)
     // Don't forget to update clone() if you add new properties
 
     Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
@@ -51,6 +52,7 @@
     void setName(const QString &name);
 
     int noteCount() const;
+    QString noteAt(int index) const;
 
     bool published() const;
     void setPublished(bool published);
@@ -60,6 +62,9 @@
 
     QString lastUpdatedString() const;
 
+    bool isDefaultNotebook() const;
+    void setIsDefaultNotebook(bool isDefaultNotebook);
+
     quint32 updateSequenceNumber() const;
     quint32 lastSyncedSequenceNumber() const;
 
@@ -81,6 +86,7 @@
     void loadingChanged();
     void syncedChanged();
     void syncErrorChanged();
+    void isDefaultNotebookChanged();
 
 private slots:
     void noteAdded(const QString &noteGuid, const QString &notebookGuid);
@@ -106,6 +112,7 @@
     QString m_name;
     bool m_published;
     QDateTime m_lastUpdated;
+    bool m_isDefaultNotebook;
     QList<QString> m_notesList;
 
     QString m_infoFile;

=== modified file 'src/libqtevernote/notebooks.cpp'
--- src/libqtevernote/notebooks.cpp	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/notebooks.cpp	2015-03-04 00:26:40 +0000
@@ -32,7 +32,6 @@
     }
 
     connect(NotesStore::instance(), &NotesStore::notebooksLoadingChanged, this, &Notebooks::loadingChanged);
-    connect(NotesStore::instance(), &NotesStore::notebooksErrorChanged, this, &Notebooks::errorChanged);
     connect(NotesStore::instance(), &NotesStore::notebookAdded, this, &Notebooks::notebookAdded);
     connect(NotesStore::instance(), &NotesStore::notebookRemoved, this, &Notebooks::notebookRemoved);
     connect(NotesStore::instance(), &NotesStore::notebookGuidChanged, this, &Notebooks::notebookGuidChanged);
@@ -43,11 +42,6 @@
     return NotesStore::instance()->notebooksLoading();
 }
 
-QString Notebooks::error() const
-{
-    return NotesStore::instance()->notebooksError();
-}
-
 int Notebooks::count() const
 {
     return rowCount();
@@ -76,6 +70,8 @@
         return notebook->synced();
     case RoleSyncError:
         return notebook->syncError();
+    case RoleIsDefaultNotebook:
+        return notebook->isDefaultNotebook();
     }
     return QVariant();
 }
@@ -99,6 +95,7 @@
     roles.insert(RoleLoading, "loading");
     roles.insert(RoleSynced, "synced");
     roles.insert(RoleSyncError, "syncError");
+    roles.insert(RoleIsDefaultNotebook, "isDefaultNotebook");
     return roles;
 }
 
@@ -125,6 +122,7 @@
     connect(notebook, &Notebook::syncedChanged, this, &Notebooks::syncedChanged);
     connect(notebook, &Notebook::loadingChanged, this, &Notebooks::notebookLoadingChanged);
     connect(notebook, &Notebook::syncErrorChanged, this, &Notebooks::syncErrorChanged);
+    connect(notebook, &Notebook::isDefaultNotebookChanged, this, &Notebooks::isDefaultNotebookChanged);
 
     beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
     m_list.append(guid);
@@ -147,6 +145,13 @@
     emit dataChanged(index(idx), index(idx));
 }
 
+void Notebooks::isDefaultNotebookChanged()
+{
+    Notebook *notebook = static_cast<Notebook*>(sender());
+    QModelIndex idx = index(m_list.indexOf((notebook->guid())));
+    emit dataChanged(idx, idx, QVector<int>() << RoleIsDefaultNotebook);
+}
+
 void Notebooks::nameChanged()
 {
     Notebook *notebook = static_cast<Notebook*>(sender());

=== modified file 'src/libqtevernote/notebooks.h'
--- src/libqtevernote/notebooks.h	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/notebooks.h	2015-03-04 00:26:40 +0000
@@ -29,7 +29,6 @@
 {
     Q_OBJECT
     Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
-    Q_PROPERTY(QString error READ error NOTIFY errorChanged)
     Q_PROPERTY(int count READ count NOTIFY countChanged)
 
 public:
@@ -42,7 +41,8 @@
         RoleLastUpdatedString,
         RoleLoading,
         RoleSynced,
-        RoleSyncError
+        RoleSyncError,
+        RoleIsDefaultNotebook
     };
     explicit Notebooks(QObject *parent = 0);
 
@@ -61,7 +61,6 @@
 
 signals:
     void loadingChanged();
-    void errorChanged();
     void countChanged();
 
 private slots:
@@ -76,6 +75,7 @@
     void syncedChanged();
     void notebookLoadingChanged();
     void syncErrorChanged();
+    void isDefaultNotebookChanged();
 
 private:
     QList<QString> m_list;

=== modified file 'src/libqtevernote/notesstore.cpp'
--- src/libqtevernote/notesstore.cpp	2015-02-28 03:59:56 +0000
+++ src/libqtevernote/notesstore.cpp	2015-03-04 00:26:40 +0000
@@ -143,17 +143,7 @@
 
 QString NotesStore::error() const
 {
-    return m_error;
-}
-
-QString NotesStore::notebooksError() const
-{
-    return m_notebooksError;
-}
-
-QString NotesStore::tagsError() const
-{
-    return m_tagsError;
+    return m_errorQueue.count() > 0 ? m_errorQueue.first() : QString();
 }
 
 int NotesStore::count() const
@@ -284,6 +274,9 @@
 {
     Notebook *notebook = new Notebook(QUuid::createUuid().toString().remove(QRegExp("[\{\}]*")), 1, this);
     notebook->setName(name);
+    if (m_notebooks.isEmpty()) {
+        notebook->setIsDefaultNotebook(true);
+    }
 
     m_notebooks.append(notebook);
     m_notebooksHash.insert(notebook->guid(), notebook);
@@ -359,6 +352,27 @@
     emit notebookChanged(notebook->guid());
 }
 
+void NotesStore::setDefaultNotebook(const QString &guid)
+{
+    Notebook *notebook = m_notebooksHash.value(guid);
+    if (!notebook) {
+        qWarning() << "[NotesStore] Notebook guid not found:" << guid;
+        return;
+    }
+
+    qDebug() << "[NotesStore] Setting default notebook:" << guid;
+    foreach (Notebook *tmp, m_notebooks) {
+        if (tmp->isDefaultNotebook()) {
+            tmp->setIsDefaultNotebook(false);
+            saveNotebook(tmp->guid());
+            break;
+        }
+    }
+    notebook->setIsDefaultNotebook(true);
+    saveNotebook(guid);
+    emit defaultNotebookChanged(guid);
+}
+
 void NotesStore::saveTag(const QString &guid)
 {
     Tag *tag = m_tagsHash.value(guid);
@@ -381,9 +395,66 @@
 
 void NotesStore::expungeNotebook(const QString &guid)
 {
-    ExpungeNotebookJob *job = new ExpungeNotebookJob(guid);
-    connect(job, &ExpungeNotebookJob::jobDone, this, &NotesStore::expungeNotebookJobDone);
-    EvernoteConnection::instance()->enqueue(job);
+    if (m_username != "@local") {
+        qWarning() << "[NotesStore] Account managed by Evernote. Cannot delete notebooks.";
+        m_errorQueue.append(QString(gettext("This account is managed by Evernote. Use the Evernote website to delete notebooks.")));
+        emit errorChanged();
+        return;
+    }
+
+    Notebook* notebook = m_notebooksHash.value(guid);
+    if (!notebook) {
+        qWarning() << "[NotesStore] Cannot delete notebook. Notebook not found for guid:" << guid;
+        return;
+    }
+
+    if (notebook->isDefaultNotebook()) {
+        qWarning() << "[NotesStore] Cannot delete the default notebook.";
+        m_errorQueue.append(QString(gettext("Cannot delete the default notebook. Set another notebook to be the default first.")));
+        emit errorChanged();
+        return;
+    }
+
+    if (notebook->noteCount() > 0) {
+        QString defaultNotebook;
+        foreach (const Notebook *notebook, m_notebooks) {
+            if (notebook->isDefaultNotebook()) {
+                defaultNotebook = notebook->guid();
+                break;
+            }
+        }
+        if (defaultNotebook.isEmpty()) {
+            qWarning() << "[NotesStore] No default notebook set. Can't delete notebooks.";
+            return;
+        }
+
+        while (notebook->noteCount() > 0) {
+            QString noteGuid = notebook->noteAt(0);
+            Note *note = m_notesHash.value(noteGuid);
+            if (!note) {
+                qWarning() << "[NotesStore] Notebook holds a noteGuid which cannot be found in notes store";
+                Q_ASSERT(false);
+                continue;
+            }
+            qDebug() << "[NotesStore] Moving note" << noteGuid << "to default Notebook";
+            note->setNotebookGuid(defaultNotebook);
+            saveNote(note->guid());
+            emit noteChanged(note->guid(), defaultNotebook);
+            syncToCacheFile(note);
+        }
+    }
+
+    m_notebooks.removeAll(notebook);
+    m_notebooksHash.remove(notebook->guid());
+    emit notebookRemoved(notebook->guid());
+
+    QSettings settings(m_cacheFile, QSettings::IniFormat);
+    settings.beginGroup("notebooks");
+    settings.remove(notebook->guid());
+    settings.endGroup();
+
+    notebook->deleteInfoFile();
+    notebook->deleteLater();
 }
 
 QList<Tag *> NotesStore::tags() const
@@ -548,11 +619,7 @@
 {
     switch (errorCode) {
     case EvernoteConnection::ErrorCodeNoError:
-        // All is well, reset error code.
-        if (!m_error.isEmpty()) {
-            m_error.clear();
-            emit errorChanged();
-        }
+        // All is well...
         break;
     case EvernoteConnection::ErrorCodeUserException:
         qWarning() << "FetchNotesJobDone: EDAMUserException:" << errorMessage;
@@ -571,8 +638,6 @@
         return; // silently discarding...
     default:
         qWarning() << "FetchNotesJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
-        m_error = QString(gettext("Error refreshing notes: %1")).arg(errorMessage);
-        emit errorChanged();
         m_loading = false;
         emit loadingChanged();
         return;
@@ -875,11 +940,7 @@
 
     switch (errorCode) {
     case EvernoteConnection::ErrorCodeNoError:
-        // All is well, reset error code.
-        if (!m_notebooksError.isEmpty()) {
-            m_notebooksError.clear();
-            emit notebooksErrorChanged();
-        }
+        // All is well...
         break;
     case EvernoteConnection::ErrorCodeUserException:
         qWarning() << "FetchNotebooksJobDone: EDAMUserException:" << errorMessage;
@@ -890,19 +951,19 @@
         return; // silently discarding
     default:
         qWarning() << "FetchNotebooksJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
-        m_notebooksError = tr("Error refreshing notebooks: %1").arg(errorMessage);
-        emit notebooksErrorChanged();
-        return;
+        return; // silently discarding
     }
 
     QList<Notebook*> unhandledNotebooks = m_notebooks;
 
+    qDebug() << "[NotesStore] Have" << results.size() << "from Evernote.";
     for (unsigned int i = 0; i < results.size(); ++i) {
         evernote::edam::Notebook result = results.at(i);
         Notebook *notebook = m_notebooksHash.value(QString::fromStdString(result.guid));
         unhandledNotebooks.removeAll(notebook);
         bool newNotebook = notebook == 0;
         if (newNotebook) {
+            qDebug() << "[NotesStore] Found new notebook on Evernote:" << QString::fromStdString(result.guid);
             notebook = new Notebook(QString::fromStdString(result.guid), 0, this);
             updateFromEDAM(result, notebook);
             m_notebooksHash.insert(notebook->guid(), notebook);
@@ -911,20 +972,25 @@
             syncToCacheFile(notebook);
         } else if (notebook->synced()) {
             if (notebook->updateSequenceNumber() < result.updateSequenceNum) {
+                qDebug() << "[NotesStore] Notebook on Evernote is newer than local copy. Updating:" << notebook->guid();
                 updateFromEDAM(result, notebook);
                 emit notebookChanged(notebook->guid());
                 syncToCacheFile(notebook);
+            } else {
+                qDebug() << "[NotesStore] Notebook is in sync:" << notebook->guid();
             }
         } else {
             // Local notebook changed. See if we can push our changes
             if (result.updateSequenceNum == notebook->lastSyncedSequenceNumber()) {
+                qDebug() << "[NotesStore] Local Notebook changed. Uploading changes to Evernote:" << notebook->guid();
                 SaveNotebookJob *job = new SaveNotebookJob(notebook);
                 connect(job, &SaveNotebookJob::jobDone, this, &NotesStore::saveNotebookJobDone);
                 EvernoteConnection::instance()->enqueue(job);
                 notebook->setLoading(true);
                 emit notebookChanged(notebook->guid());
             } else {
-                qWarning() << "CONFLICT in notebook:" << notebook->name();
+                qWarning() << "[NotesStore] Sync conflict in notebook:" << notebook->name();
+                qWarning() << "[NotesStore] Resolving of sync conflicts is not implemented yet.";
                 notebook->setSyncError(true);
                 emit notebookChanged(notebook->guid());
             }
@@ -933,18 +999,20 @@
 
     foreach (Notebook *notebook, unhandledNotebooks) {
         if (notebook->lastSyncedSequenceNumber() == 0) {
+            qDebug() << "[NotesStore] Have a local notebook that doesn't exist on Evernote. Creating on server:" << notebook->guid();
             notebook->setLoading(true);
             CreateNotebookJob *job = new CreateNotebookJob(notebook);
             connect(job, &CreateNotebookJob::jobDone, this, &NotesStore::createNotebookJobDone);
             EvernoteConnection::instance()->enqueue(job);
             emit notebookChanged(notebook->guid());
         } else {
+            qDebug() << "[NotesStore] Notebook has been deleted on the server. Deleting local copy:" << notebook->guid();
             m_notebooks.removeAll(notebook);
             m_notebooksHash.remove(notebook->guid());
             emit notebookRemoved(notebook->guid());
 
             QSettings settings(m_cacheFile, QSettings::IniFormat);
-            settings.beginGroup("notenooks");
+            settings.beginGroup("notebooks");
             settings.remove(notebook->guid());
             settings.endGroup();
 
@@ -967,6 +1035,14 @@
     EvernoteConnection::instance()->enqueue(job);
 }
 
+void NotesStore::clearError()
+{
+    if (m_errorQueue.count() > 0) {
+        m_errorQueue.takeFirst();
+        emit errorChanged();
+    }
+}
+
 void NotesStore::fetchTagsJobDone(EvernoteConnection::ErrorCode errorCode, const QString &errorMessage, const std::vector<evernote::edam::Tag> &results)
 {
     m_tagsLoading = false;
@@ -974,11 +1050,7 @@
 
     switch (errorCode) {
     case EvernoteConnection::ErrorCodeNoError:
-        // All is well, reset error code.
-        if (!m_tagsError.isEmpty()) {
-            m_tagsError.clear();
-            emit tagsErrorChanged();
-        }
+        // All is well...
         break;
     case EvernoteConnection::ErrorCodeUserException:
         qWarning() << "FetchTagsJobDone: EDAMUserException:" << errorMessage;
@@ -989,9 +1061,7 @@
         return; // silently discarding
     default:
         qWarning() << "FetchTagsJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
-        m_tagsError = tr("Error refreshing tags: %1").arg(errorMessage);
-        emit tagsErrorChanged();
-        return;
+        return; // silently discarding
     }
 
     QHash<QString, Tag*> unhandledTags = m_tagsHash;
@@ -1067,10 +1137,18 @@
     connect(note, &Note::reminderDoneChanged, this, &NotesStore::emitDataChanged);
 
     note->setTitle(title);
-    if (notebookGuid.isEmpty() && m_notebooks.count() > 0) {
-        note->setNotebookGuid(m_notebooks.first()->guid());
-    } else {
+
+    if (!notebookGuid.isEmpty()) {
         note->setNotebookGuid(notebookGuid);
+    } else if (m_notebooks.count() > 0){
+        QString generatedNotebook = m_notebooks.first()->guid();
+        foreach (Notebook *notebook, m_notebooks) {
+            if (notebook->isDefaultNotebook()) {
+                generatedNotebook = notebook->guid();
+                break;
+            }
+        }
+        note->setNotebookGuid(generatedNotebook);
     }
     note->setEnmlContent(content.enml());
     note->setCreated(QDateTime::currentDateTime());
@@ -1534,5 +1612,49 @@
     if (evNotebook.__isset.published && evNotebook.published != notebook->published()) {
         notebook->setPublished(evNotebook.published);
     }
+    qDebug() << "readong from evernote:" << evNotebook.__isset.defaultNotebook << evNotebook.defaultNotebook << notebook->name();
+    if (evNotebook.__isset.defaultNotebook && evNotebook.defaultNotebook != notebook->isDefaultNotebook()) {
+        notebook->setIsDefaultNotebook(evNotebook.defaultNotebook);
+    }
     notebook->setLastSyncedSequenceNumber(evNotebook.updateSequenceNum);
 }
+
+
+void NotesStore::expungeTag(const QString &guid)
+{
+    if (m_username != "@local") {
+        qWarning() << "This account is managed by Evernote. Cannot delete tags.";
+        m_errorQueue.append(gettext("This account is managed by Evernote. Please use the Evernote website to delete tags."));
+        emit errorChanged();
+        return;
+    }
+
+    Tag *tag = m_tagsHash.value(guid);
+    if (!tag) {
+        qWarning() << "[NotesStore] No tag with guid" << guid;
+        return;
+    }
+
+    while (tag->noteCount() > 0) {
+        QString noteGuid = tag->noteAt(0);
+        Note *note = m_notesHash.value(noteGuid);
+        if (!note) {
+            qWarning() << "[NotesStore] Tag holds note" << noteGuid << "which hasn't been found in Notes Store";
+            continue;
+        }
+        untagNote(noteGuid, guid);
+    }
+
+    emit tagRemoved(guid);
+    m_tagsHash.remove(guid);
+    m_tags.removeAll(tag);
+
+    QSettings cacheFile(m_cacheFile, QSettings::IniFormat);
+    cacheFile.beginGroup("tags");
+    cacheFile.remove(guid);
+    cacheFile.endGroup();
+    tag->syncToInfoFile();
+
+    tag->deleteInfoFile();
+    tag->deleteLater();
+}

=== modified file 'src/libqtevernote/notesstore.h'
--- src/libqtevernote/notesstore.h	2015-02-26 22:47:10 +0000
+++ src/libqtevernote/notesstore.h	2015-03-04 00:26:40 +0000
@@ -59,7 +59,6 @@
     Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
     Q_PROPERTY(bool notebooksLoading READ notebooksLoading NOTIFY notebooksLoadingChanged)
     Q_PROPERTY(QString error READ error NOTIFY errorChanged)
-    Q_PROPERTY(QString notebooksError READ notebooksError NOTIFY notebooksErrorChanged)
     Q_PROPERTY(int count READ count NOTIFY countChanged)
 
 public:
@@ -105,8 +104,6 @@
     bool tagsLoading() const;
 
     QString error() const;
-    QString notebooksError() const;
-    QString tagsError() const;
 
     int count() const;
 
@@ -129,6 +126,7 @@
     Q_INVOKABLE Notebook* notebook(const QString &guid);
     Q_INVOKABLE void createNotebook(const QString &name);
     Q_INVOKABLE void saveNotebook(const QString &guid);
+    Q_INVOKABLE void setDefaultNotebook(const QString &guid);
     Q_INVOKABLE void expungeNotebook(const QString &guid);
 
     QList<Tag*> tags() const;
@@ -137,6 +135,7 @@
     Q_INVOKABLE void saveTag(const QString &guid);
     Q_INVOKABLE void tagNote(const QString &noteGuid, const QString &tagGuid);
     Q_INVOKABLE void untagNote(const QString &noteGuid, const QString &tagGuid);
+    Q_INVOKABLE void expungeTag(const QString &guid);
 
 public slots:
     void refreshNotes(const QString &filterNotebookGuid = QString(), int startIndex = 0);
@@ -146,14 +145,14 @@
     void refreshNotebooks();
     void refreshTags();
 
+    void clearError();
+
 signals:
     void usernameChanged();
     void loadingChanged();
     void notebooksLoadingChanged();
     void tagsLoadingChanged();
     void errorChanged();
-    void notebooksErrorChanged();
-    void tagsErrorChanged();
     void countChanged();
 
     void noteCreated(const QString &guid, const QString &notebookGuid);
@@ -167,6 +166,7 @@
     void notebookChanged(const QString &guid);
     void notebookRemoved(const QString &guid);
     void notebookGuidChanged(const QString &oldGuid, const QString &newGuid);
+    void defaultNotebookChanged(const QString &guid);
 
     void tagAdded(const QString &guid);
     void tagChanged(const QString &guid);
@@ -213,9 +213,7 @@
     bool m_notebooksLoading;
     bool m_tagsLoading;
 
-    QString m_error;
-    QString m_notebooksError;
-    QString m_tagsError;
+    QStringList m_errorQueue;
 
     QList<Note*> m_notes;
     QList<Notebook*> m_notebooks;

=== modified file 'src/libqtevernote/tag.cpp'
--- src/libqtevernote/tag.cpp	2015-02-24 22:21:04 +0000
+++ src/libqtevernote/tag.cpp	2015-03-04 00:26:40 +0000
@@ -219,3 +219,9 @@
         emit syncErrorChanged();
     }
 }
+
+
+QString Tag::noteAt(int index) const
+{
+    return m_notesList.at(index);
+}

=== modified file 'src/libqtevernote/tag.h'
--- src/libqtevernote/tag.h	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/tag.h	2015-03-04 00:26:40 +0000
@@ -61,6 +61,7 @@
     void setName(const QString &guid);
 
     int noteCount() const;
+    QString noteAt(int index) const;
 
     bool loading() const;
     bool synced() const;

=== modified file 'src/libqtevernote/tags.cpp'
--- src/libqtevernote/tags.cpp	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/tags.cpp	2015-03-04 00:26:40 +0000
@@ -32,7 +32,6 @@
     }
 
     connect(NotesStore::instance(), &NotesStore::tagsLoadingChanged, this, &Tags::loadingChanged);
-    connect(NotesStore::instance(), &NotesStore::tagsErrorChanged, this, &Tags::errorChanged);
     connect(NotesStore::instance(), &NotesStore::tagAdded, this, &Tags::tagAdded);
     connect(NotesStore::instance(), &NotesStore::tagRemoved, this, &Tags::tagRemoved);
     connect(NotesStore::instance(), &NotesStore::tagGuidChanged, this, &Tags::tagGuidChanged);
@@ -43,11 +42,6 @@
     return NotesStore::instance()->tagsLoading();
 }
 
-QString Tags::error() const
-{
-    return NotesStore::instance()->tagsError();
-}
-
 int Tags::count() const
 {
     return rowCount();

=== modified file 'src/libqtevernote/tags.h'
--- src/libqtevernote/tags.h	2014-12-13 03:55:52 +0000
+++ src/libqtevernote/tags.h	2015-03-04 00:26:40 +0000
@@ -29,7 +29,6 @@
 {
     Q_OBJECT
     Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
-    Q_PROPERTY(QString error READ error NOTIFY errorChanged)
     Q_PROPERTY(int count READ count NOTIFY countChanged)
 
 public:
@@ -44,7 +43,6 @@
     explicit Tags(QObject *parent = 0);
 
     bool loading() const;
-    QString error() const;
     int count() const;
 
     QVariant data(const QModelIndex &index, int role) const;
@@ -58,7 +56,6 @@
 
 signals:
     void loadingChanged();
-    void errorChanged();
     void countChanged();
 
 private slots:


Follow ups