diff --git a/qml/components/NotesApi.qml b/qml/components/NotesApi.qml index 0fab928..b3e71d2 100644 --- a/qml/components/NotesApi.qml +++ b/qml/components/NotesApi.qml @@ -3,11 +3,9 @@ import Sailfish.Silica 1.0 import Nemo.Configuration 1.0 Item { - property string uuid - property string response property var categories: [ ] - property string file: StandardPaths.data + "/" + uuid + ".json" + property string file: StandardPaths.data + "/" + appSettings.currentAccount + ".json" property bool saveFile: false property bool busy: jobsRunning > 0 property int jobsRunning: 0 @@ -17,13 +15,17 @@ Item { onStatusChanged: { console.log("Network status: " + statusText + " (" + status + ")") } - onUuidChanged: { - appSettings.currentAccount = uuid - account.path = "/apps/harbour-nextcloudnotes/accounts/" + uuid - onUuidChanged: console.log("Account : " + account.name) - } - /*function clear() { - account.clear() + + /*function getNote(id) { + var dict + if (id) { + for (var i = 0; i < model.count; i++) { + dict = model.get(i) + if (dict.id === id) { + return dict + } + } + } }*/ function apiCall(method, data) { @@ -52,7 +54,7 @@ Item { apiReq.setRequestHeader("Content-Type", "application/json") apiReq.setRequestHeader("Authorization", "Basic " + Qt.btoa(account.username + ":" + account.password)) apiReq.withCredentials = true - apiReq.timeout = 5000 + //apiReq.timeout = 5000 apiReq.onreadystatechange = function() { if (apiReq.readyState === XMLHttpRequest.DONE) { statusText = apiReq.statusText @@ -92,18 +94,6 @@ Item { } } - function getNote(id) { - var dict - if (id) { - for (var i = 0; i < model.count; i++) { - dict = model.get(i) - if (dict.id === id) { - return dict - } - } - } - } - function getNotesFromApi() { apiCall("GET") } @@ -132,7 +122,7 @@ Item { } // source: https://stackoverflow.com/a/14339782 - function getPrettyDate(date) { + /*function getPrettyDate(date) { var today = new Date() today.setHours(0) today.setMinutes(0) @@ -154,7 +144,7 @@ Item { } else { return compDate.toLocaleDateString(Qt.locale(), "MMMM yyyy") } - } + }*/ /*Component.onCompleted: { if (saveFile) { diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index bc2eb85..720153c 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -19,6 +19,7 @@ ApplicationWindow ConfigurationGroup { id: account path: "/apps/harbour-nextcloudnotes/accounts/" + appSettings.currentAccount + property string name: value("name", "", String) property url server: value("server", "", String) property string version: value("version", "v0.2", String) @@ -28,22 +29,27 @@ ApplicationWindow property bool unencryptedConnection: account.value("unencryptedConnection", false, Boolean) property date update: value("update", "", Date) onValuesChanged: console.log("A property of the current account has changed") + onNameChanged: console.log("Account: " + name) } ConfigurationGroup { id: appSettings path: "/apps/harbour-nextcloudnotes/settings" - property string currentAccount: value("currentAccount", "") - property var accountIDs: value("accountIDs", [ ]) - property int autoSyncInterval: value("autoSyncInterval", 0) - property int previewLineCount: value("previewLineCount", 4) + property string currentAccount: value("currentAccount", "", String) + property var accountIDs: value("accountIDs", [ ], Array) + property int autoSyncInterval: value("autoSyncInterval", 0, Number) + property int previewLineCount: value("previewLineCount", 4, Number) property bool favoritesOnTop: value("favoritesOnTop", true, Boolean) property string sortBy: value("sortBy", "date", String) property bool showSeparator: value("showSeparator", false, Boolean) property bool useMonoFont: value("useMonoFont", false, Boolean) property bool useCapitalX: value("useCapitalX", false, Boolean) - onCurrentAccountChanged: api.uuid = currentAccount + onCurrentAccountChanged: { + account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount + noteListModel.clear() + api.getNotesFromApi() + } function addAccount() { var uuid = uuidv4() @@ -66,13 +72,15 @@ ApplicationWindow } }) accounts.value = newIds - for (var i = accountIDs.length-1; i > 0; i--) { + for (var i = accountIDs.length-1; i >= 0; i--) { if (accountIDs[i] !== uuid) { api.uuid = accountIDs[i] break } } - autoSyncTimer.start() + if (autoSyncInterval > 0 && appWindow.visible) { + autoSyncTimer.start() + } } function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { @@ -103,31 +111,24 @@ ApplicationWindow triggeredOnStart = true } } - onIntervalChanged: console.log("Auto-Sync every " + interval / 1000 + " seconds") + onIntervalChanged: { + if (interval > 0) { + console.log("Auto-Sync every " + interval / 1000 + " seconds") + } + } } NotesApi { id: api - uuid: appSettings.currentAccount onResponseChanged: noteListModel.applyJSON(response) - onUuidChanged: noteListModel.clear() } NotesModel { id: noteListModel - sortBy: 0 // TODO + sortBy: appSettings.sortBy favoritesOnTop: appSettings.favoritesOnTop } - /*NoteDelegateModel { - id: noteListModel - model: api.model - favoritesOnTop: appSettings.favoritesOnTop - sortBy: appSettings.sortBy - showSeparator: appSettings.showSeparator - previewLineCount: appSettings.previewLineCount - }*/ - initialPage: Component { NotesPage { } } cover: Qt.resolvedUrl("cover/CoverPage.qml") allowedOrientations: defaultAllowedOrientations diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index ba969df..144c159 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -60,10 +60,9 @@ Page { TextSwitch { id: accountTextSwitch automaticCheck: false - checked: modelData === api.uuid + checked: modelData === appSettings.currentAccount onClicked: { - api.uuid = modelData - api.getNotesFromApi() + appSettings.currentAccount = modelData } onPressAndHold: openMenu() } @@ -144,27 +143,30 @@ Page { } ComboBox { id: sortByComboBox - property var names: [qsTr("Last edited"), qsTr("Category"), qsTr("Title alphabetically")] + property var names: { "date" : qsTr("Last edited"), + "category" : qsTr("Category"), + "title" : qsTr("Title alphabetically"), + "none" : qsTr("No sorting") } label: qsTr("Sort notes by") description: qsTr("This will also change how the notes are grouped") menu: ContextMenu { Repeater { id: sortByRepeater - model: ["date", "category", "title"] + model: noteListModel.sortingCriteria() MenuItem { - text: sortByComboBox.names[index] - //enabled: modelData !== "title" + text: sortByComboBox.names[modelData] Component.onCompleted: { if (modelData === appSettings.sortBy) { sortByComboBox.currentIndex = index } } + onClicked: appSettings.sortBy = modelData } } } - onCurrentIndexChanged: { + /*onCurrentIndexChanged: { appSettings.sortBy = sortByRepeater.model[currentIndex] - } + }*/ } TextSwitch { text: qsTr("Favorites on top") diff --git a/src/note.cpp b/src/note.cpp index 1c30e06..955e38f 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -16,6 +16,7 @@ Note::Note(const Note& note, QObject *parent) : QObject(parent) { m_etag = note.etag(); m_error = note.error(); m_errorMessage = note.errorMessage(); + m_date = note.date(); } Note& Note::operator=(const Note& note) { @@ -28,6 +29,7 @@ Note& Note::operator=(const Note& note) { m_etag = note.etag(); m_error = note.error(); m_errorMessage = note.errorMessage(); + m_date = note.date(); return *this; } diff --git a/src/note.h b/src/note.h index 15e6286..fff92dc 100644 --- a/src/note.h +++ b/src/note.h @@ -3,6 +3,8 @@ #include #include +#include +#include class Note : public QObject { Q_OBJECT @@ -16,6 +18,7 @@ class Note : public QObject { Q_PROPERTY(QString etag READ etag WRITE setEtag NOTIFY etagChanged) Q_PROPERTY(bool error READ error WRITE setError NOTIFY errorChanged) Q_PROPERTY(QString errorMessage READ errorMessage WRITE setErrorMessage NOTIFY errorMessageChanged) + Q_PROPERTY(QString date READ date NOTIFY dateChanged) public: Note(QObject *parent = NULL); @@ -30,9 +33,30 @@ public: QString etag() const { return m_etag; } bool error() const { return m_error; } QString errorMessage() const { return m_errorMessage; } + QString date() const { return m_date; } void setId(int id) { if (id != m_id) { m_id = id; emit idChanged(id); } } - void setModified(uint modified) { if (modified != m_modified) { m_modified = modified; emit modifiedChanged(modified); } } + void setModified(uint modified) { + if (modified != m_modified) { + m_modified = modified; + QDateTime date; + QString newDate; + date.setTime_t(modified); + qint64 diff = date.daysTo(QDateTime::currentDateTime()); + if (diff == 0) + newDate = "Today"; + else if (diff == 1) + newDate = "Yesterday"; + else if (diff < 7) + newDate = date.toLocalTime().toString("dddd"); + else if (date.toLocalTime().toString("yyyy") == QDateTime::currentDateTime().toString("yyyy")) + newDate = date.toLocalTime().toString("MMMM"); + else + newDate = date.toLocalTime().toString("MMMM yyyy"); + if (newDate != m_date) { m_date = newDate; emit dateChanged(newDate); } + emit modifiedChanged(modified); + } + } void setTitle(QString title) { if (title != m_title) { m_title = title; emit titleChanged(title); } } void setCategory(QString category) { if (category != m_category) { m_category = category; emit categoryChanged(category); } } void setContent(QString content) { if (content != m_content) { m_content = content; emit contentChanged(content); } } @@ -40,6 +64,7 @@ public: void setEtag(QString etag) { if (etag != m_etag) { m_etag = etag; emit etagChanged(etag); } } void setError(bool error) { if (error != m_error) { m_error = error; emit errorChanged(error); } } void setErrorMessage(QString errorMessage) { if (errorMessage != m_errorMessage) { m_errorMessage = errorMessage; emit errorMessageChanged(errorMessage); } } + void setDate(QString date) { if (date != m_date) { m_date = date; emit dateChanged(date); } } Note& operator=(const Note& note); bool operator==(const Note& note) const { @@ -91,6 +116,7 @@ signals: void etagChanged(QString etag); void errorChanged(bool error); void errorMessageChanged(QString errorMessage); + void dateChanged(QString date); private: int m_id; @@ -102,6 +128,7 @@ private: QString m_etag; bool m_error; QString m_errorMessage; + QString m_date; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Note::SearchAttributes) diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index d30c140..846b564 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -15,8 +15,8 @@ NotesModel::~NotesModel() { clear(); } -void NotesModel::setSortBy(int sortBy) { - if (sortBy != m_sortBy && sortBy > 0 && sortBy <= noSorting) { +void NotesModel::setSortBy(QString sortBy) { + if (sortBy != m_sortBy && sortingNames().values().contains(sortBy.toLocal8Bit())) { m_sortBy = sortBy; sort(); emit sortByChanged(m_sortBy); @@ -24,6 +24,7 @@ void NotesModel::setSortBy(int sortBy) { } void NotesModel::setFavoritesOnTop(bool favoritesOnTop) { + qDebug() << "Changed favorites on top:" << favoritesOnTop; if (favoritesOnTop != m_favoritesOnTop) { m_favoritesOnTop = favoritesOnTop; sort(); @@ -56,7 +57,7 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) { QJsonObject jobj = jval.toObject(); if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) { //qDebug() << "Adding it to the model..."; - Note* note = Note::fromjson(jobj); + Note* note = Note::fromjson(jobj); // TODO connect signals int position = indexOf(note->id()); if (position >= 0 && replaceIfArray) { //qDebug() << "Replacing note" << note.title << "on position" << position; @@ -103,7 +104,7 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) { //qDebug() << "It's a single object..."; QJsonObject jobj = jdoc.object(); if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) { - Note* note = Note::fromjson(jobj); + Note* note = Note::fromjson(jobj); // TODO connect signals int position = indexOf(note->id()); if (position >= 0 && replaceIfArray) { m_notes[position].note = note; @@ -146,7 +147,7 @@ void NotesModel::clear() { m_searchText.clear(); beginRemoveRows(QModelIndex(), 0, rowCount()); for (int i = 0; i < m_notes.size(); i++) { - delete m_notes[i].note; + delete m_notes[i].note; // TODO disconnect signals } m_notes.clear(); endRemoveRows(); @@ -212,7 +213,8 @@ QHash NotesModel::roleNames() const { {NotesModel::FavoriteRole, "favorite"}, {NotesModel::EtagRole, "etag"}, {NotesModel::ErrorRole, "error"}, - {NotesModel::ErrorMessageRole, "errorMessage"} + {NotesModel::ErrorMessageRole, "errorMessage"}, + {NotesModel::DateRole, "date"} }; } @@ -225,6 +227,14 @@ QHash NotesModel::sortingNames() const { return criteria; } +QStringList NotesModel::sortingCriteria() const { + QStringList criteria; + QHash names = sortingNames(); + for (int i = 0; i <= noSorting; i++) + criteria << names[i]; + return criteria; +} + Qt::ItemFlags NotesModel::flags(const QModelIndex &index) const { if (index.isValid()) { return Qt::ItemIsEnabled | Qt::ItemIsEditable; // | Qt::ItemIsSelectable @@ -255,6 +265,7 @@ QVariant NotesModel::data(const QModelIndex &index, int role) const { else if (role == EtagRole) return m_notes[index.row()].note->etag(); else if (role == ErrorRole) return m_notes[index.row()].note->error(); else if (role == ErrorMessageRole) return m_notes[index.row()].note->errorMessage(); + else if (role == DateRole) return m_notes[index.row()].note->date(); return QVariant(); } @@ -273,25 +284,26 @@ bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int ro if (!index.isValid()) return false; else if (role == ModifiedRole && m_notes[index.row()].note->modified() != value.toUInt()) { m_notes[index.row()].note->setModified(value.toInt()); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); // TODO remove when signals from Note are connected + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, DateRole} ); // TODO remove when signals from Note are connected sort(); return true; } else if (role == CategoryRole && m_notes[index.row()].note->category() != value.toString()) { m_notes[index.row()].note->setCategory(value.toString()); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); // TODO remove when signals from Note are connected sort(); return true; } else if (role == ContentRole && m_notes[index.row()].note->content() != value.toString()) { m_notes[index.row()].note->setContent(value.toString()); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); // TODO remove when signals from Note are connected sort(); return true; } else if (role == FavoriteRole && m_notes[index.row()].note->favorite() != value.toBool()) { m_notes[index.row()].note->setFavorite(value.toBool()); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); // TODO remove when signals from Note are connected sort(); return true; } @@ -311,11 +323,11 @@ bool NotesModel::setItemData(const QModelIndex &index, const QMap } void NotesModel::sort() { + qDebug() << "Sorting notes in the model"; QList > notes; QMap > map; QMap > favorites; - switch (m_sortBy) { - case sortByDate: + if (m_sortBy == sortingNames()[sortByDate]) { emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { if (m_favoritesOnTop && m_notes[i].note->favorite()) @@ -327,8 +339,8 @@ void NotesModel::sort() { notes.append(map.values()); m_notes = notes; emit layoutChanged(QList (), VerticalSortHint); - break; - case sortByCategory: + } + else if (m_sortBy == sortingNames()[sortByCategory]) { emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { if (m_favoritesOnTop && m_notes[i].note->favorite()) @@ -340,8 +352,8 @@ void NotesModel::sort() { notes.append(map.values()); m_notes = notes; emit layoutChanged(QList (), VerticalSortHint); - break; - case sortByTitle: + } + else if (m_sortBy == sortingNames()[sortByTitle]) { emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { if (m_favoritesOnTop && m_notes[i].note->favorite()) @@ -353,34 +365,31 @@ void NotesModel::sort() { notes.append(map.values()); m_notes = notes; emit layoutChanged(QList (), VerticalSortHint); - break; - default: - break; } } bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const { - switch (m_sortBy) { - case sortByDate: + if (m_sortBy == sortingNames()[sortByDate]) { if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return n1.favorite(); else return n1.modified() > n2.modified(); - break; - case sortByCategory: + } + else if (m_sortBy == sortingNames()[sortByCategory]) { if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return n1.favorite(); else return n1.category() < n2.category(); - break; - case sortByTitle: + } + else if (m_sortBy == sortingNames()[sortByTitle]) { if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return n1.favorite(); else return n1.title() < n2.title(); - break; - default: - break; + } + else { + if (m_favoritesOnTop && n1.favorite() != n2.favorite()) + return n1.favorite(); } return true; } diff --git a/src/notesmodel.h b/src/notesmodel.h index 36a2f4f..d184d98 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -17,9 +17,9 @@ public: explicit NotesModel(QObject *parent = 0); virtual ~NotesModel(); - Q_PROPERTY(int sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged) - int sortBy() const { return m_sortBy; } - void setSortBy(int sortBy); + Q_PROPERTY(QString sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged) + QString sortBy() const { return m_sortBy; } + void setSortBy(QString sortBy); Q_PROPERTY(bool favoritesOnTop READ favoritesOnTop WRITE setFavoritesOnTop NOTIFY favoritesOnTopChanged) bool favoritesOnTop() const { return m_favoritesOnTop; } @@ -49,7 +49,8 @@ public: FavoriteRole = Qt::UserRole + 6, EtagRole = Qt::UserRole + 7, ErrorRole = Qt::UserRole + 8, - ErrorMessageRole = Qt::UserRole + 9 + ErrorMessageRole = Qt::UserRole + 9, + DateRole = Qt::UserRole + 10 }; QHash roleNames() const; @@ -60,6 +61,7 @@ public: noSorting }; QHash sortingNames() const; + Q_INVOKABLE QStringList sortingCriteria() const; Qt::ItemFlags flags(const QModelIndex &index) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; @@ -78,13 +80,13 @@ protected: signals: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector ()); - void sortByChanged(int sortBy); + void sortByChanged(QString sortBy); void favoritesOnTopChanged(bool favoritesOnTop); void searchTextChanged(QString searchText); private: QList > m_notes; - int m_sortBy; + QString m_sortBy; bool m_favoritesOnTop; QString m_searchText; diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index 4681a53..f0f51e8 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -223,14 +223,6 @@ NotesApi - - Today - Heute - - - Yesterday - Gestern - Unable to connect @@ -457,6 +449,10 @@ Show notes marked as favorite above the others + + No sorting + + SyntaxPage diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index fa76b20..817bc03 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -223,14 +223,6 @@ NotesApi - - Today - I dag - - - Yesterday - I går - Unable to connect @@ -457,6 +449,10 @@ Show notes marked as favorite above the others + + No sorting + + SyntaxPage diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index ae97265..17a93ef 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -272,20 +272,10 @@ NotesApi - + Unable to connect - - - Today - - - - - Yesterday - - NotesPage @@ -413,87 +403,92 @@ - + Edit - + Delete - + Deleting account - + Add account - + Synchronization - + Auto-Sync - + Periodically pull notes from the server - + Disabled - + every - + Minutes - + Seconds - + The Answer is 42 - + Congratulation you found the Answer to the Ultimate Question of Life, The Universe, and Everything! - + Appearance - + + No sorting + + + + Favorites on top - + Show notes marked as favorite above the others - + Last edited @@ -503,62 +498,62 @@ - + Title alphabetically - + Sort notes by - + This will also change how the notes are grouped - + Show separator - + Show a separator line between the notes - + lines - + Number of lines in the preview - + Editing - + Monospaced font - + Use a monospeced font to edit a note - + Capital 'X' in checkboxes - + For interoperability with other apps such as Joplin