From 47d2f99f78afa6d176f960d1e8be0acb91dfa93b Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 28 Feb 2024 15:47:42 +0700 Subject: [PATCH 1/3] Add missing readProjectBoolEntry function --- src/core/appinterface.cpp | 5 +++++ src/core/appinterface.h | 1 + src/core/qgismobileapp.cpp | 8 ++++++++ src/core/qgismobileapp.h | 2 ++ 4 files changed, 16 insertions(+) diff --git a/src/core/appinterface.cpp b/src/core/appinterface.cpp index 12d92b9b2b..86b7e9ec3d 100644 --- a/src/core/appinterface.cpp +++ b/src/core/appinterface.cpp @@ -96,6 +96,11 @@ double AppInterface::readProjectDoubleEntry( const QString &scope, const QString return mApp->readProjectDoubleEntry( scope, key, def ); } +bool AppInterface::readProjectBoolEntry( const QString &scope, const QString &key, bool def ) const +{ + return mApp->readProjectBoolEntry( scope, key, def ); +} + bool AppInterface::print( const QString &layoutName ) { return mApp->print( layoutName ); diff --git a/src/core/appinterface.h b/src/core/appinterface.h index 47bea68bc3..70c4a66f2a 100644 --- a/src/core/appinterface.h +++ b/src/core/appinterface.h @@ -49,6 +49,7 @@ class AppInterface : public QObject Q_INVOKABLE QString readProjectEntry( const QString &scope, const QString &key, const QString &def = QString() ) const; Q_INVOKABLE int readProjectNumEntry( const QString &scope, const QString &key, int def = 0 ) const; Q_INVOKABLE double readProjectDoubleEntry( const QString &scope, const QString &key, double def = 0.0 ) const; + Q_INVOKABLE bool readProjectBoolEntry( const QString &scope, const QString &key, bool def = false ) const; Q_INVOKABLE bool print( const QString &layoutName ); Q_INVOKABLE bool printAtlasFeatures( const QString &layoutName, const QList &featureIds ); diff --git a/src/core/qgismobileapp.cpp b/src/core/qgismobileapp.cpp index c6d76b8230..a4c3eb5527 100644 --- a/src/core/qgismobileapp.cpp +++ b/src/core/qgismobileapp.cpp @@ -1194,6 +1194,14 @@ double QgisMobileapp::readProjectDoubleEntry( const QString &scope, const QStrin return mProject->readDoubleEntry( scope, key, def ); } +bool QgisMobileapp::readProjectBoolEntry( const QString &scope, const QString &key, bool def ) const +{ + if ( !mProject ) + return def; + + return mProject->readBoolEntry( scope, key, def ); +} + bool QgisMobileapp::print( const QString &layoutName ) { const QList printLayouts = mProject->layoutManager()->printLayouts(); diff --git a/src/core/qgismobileapp.h b/src/core/qgismobileapp.h index 6d9e3afd83..e2cb5bf892 100644 --- a/src/core/qgismobileapp.h +++ b/src/core/qgismobileapp.h @@ -138,6 +138,8 @@ class QFIELD_CORE_EXPORT QgisMobileapp : public QQmlApplicationEngine */ double readProjectDoubleEntry( const QString &scope, const QString &key, double def = 0.0 ) const; + bool readProjectBoolEntry( const QString &scope, const QString &key, bool def = false ) const; + /** * Prints a given layout from the currently opened project to a PDF file * \param layoutName the layout name that will be printed From 1190b8b97b074983aaeb1a8e98f9181f8bf44da0 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Wed, 28 Feb 2024 17:35:29 +0700 Subject: [PATCH 2/3] Enforce auto-push settings coming from the project file --- src/core/qfieldcloudprojectsmodel.cpp | 15 +++++++++++++- src/core/qfieldcloudprojectsmodel.h | 3 +++ src/qml/QFieldCloudPopup.qml | 28 +++++++++++++++++++++++++-- src/qml/qgismobileapp.qml | 4 +++- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/core/qfieldcloudprojectsmodel.cpp b/src/core/qfieldcloudprojectsmodel.cpp index d4f4f382f8..d79be16a01 100644 --- a/src/core/qfieldcloudprojectsmodel.cpp +++ b/src/core/qfieldcloudprojectsmodel.cpp @@ -1961,12 +1961,25 @@ void QFieldCloudProjectsModel::projectSetAutoPushEnabled( const QString &project if ( projectIndex.isValid() ) { CloudProject *project = mProjects[projectIndex.row()]; - project->autoPushEnabled = !project->autoPushEnabled; + project->autoPushEnabled = enabled; QFieldCloudUtils::setProjectSetting( project->id, QStringLiteral( "autoPushEnabled" ), project->autoPushEnabled ); emit dataChanged( projectIndex, projectIndex, QVector() << AutoPushEnabledRole ); } } +void QFieldCloudProjectsModel::projectSetAutoPushIntervalMins( const QString &projectId, int minutes ) +{ + const QModelIndex projectIndex = findProjectIndex( projectId ); + + if ( projectIndex.isValid() ) + { + CloudProject *project = mProjects[projectIndex.row()]; + project->autoPushIntervalMins = minutes; + QFieldCloudUtils::setProjectSetting( project->id, QStringLiteral( "autoPushIntervalMins" ), project->autoPushIntervalMins ); + emit dataChanged( projectIndex, projectIndex, QVector() << AutoPushIntervalMinsRole ); + } +} + void QFieldCloudProjectsModel::reload( const QJsonArray &remoteProjects ) { beginResetModel(); diff --git a/src/core/qfieldcloudprojectsmodel.h b/src/core/qfieldcloudprojectsmodel.h index ce36a57b78..f0955fcd6f 100644 --- a/src/core/qfieldcloudprojectsmodel.h +++ b/src/core/qfieldcloudprojectsmodel.h @@ -279,6 +279,9 @@ class QFieldCloudProjectsModel : public QAbstractListModel //! Toggles the cloud project auto-push enabled state Q_INVOKABLE void projectSetAutoPushEnabled( const QString &projectId, bool enabled ); + //! Sets the interval in \a minutes between which the project will auto-push changes + Q_INVOKABLE void projectSetAutoPushIntervalMins( const QString &projectId, int minutes ); + signals: void cloudConnectionChanged(); void layerObserverChanged(); diff --git a/src/qml/QFieldCloudPopup.qml b/src/qml/QFieldCloudPopup.qml index ff83283ce3..7a1df2da77 100644 --- a/src/qml/QFieldCloudPopup.qml +++ b/src/qml/QFieldCloudPopup.qml @@ -476,13 +476,19 @@ Popup { bottomPadding: 10 font: Theme.tipFont wrapMode: Text.WordWrap - color: autoPush.checked ? Theme.mainTextColor : Theme.secondaryTextColor + color: autoPush.checked || autoPush.forceAutoPush ? Theme.mainTextColor : Theme.secondaryTextColor text: qsTr('Automatically push changes every %n minute(s)','',0 + cloudProjectsModel.currentProjectData.AutoPushIntervalMins) MouseArea { anchors.fill: parent - onClicked: cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, !autoPush.checked) + onClicked: { + if (autoPush.forceAutoPush) { + displayToast(qsTr('The current project does not allow for auto-push to be turned off')) + } else { + cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, !autoPush.checked) + } + } } } @@ -493,7 +499,9 @@ Popup { width: implicitContentWidth small: true + property bool forceAutoPush: false checked: !!cloudProjectsModel.currentProjectData.AutoPushEnabled + onClicked: { cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, checked) } @@ -721,4 +729,20 @@ Popup { cloudProjectsModel.discardLocalChangesFromCurrentProject(cloudProjectsModel.currentProjectId) cloudProjectsModel.downloadProject(cloudProjectsModel.currentProjectId, true) } + + function applyAutoPushProjectSettings() { + if (cloudProjectsModel.currentProjectId !== '') { + const forceAutoPush = iface.readProjectBoolEntry("qfieldsync", "forceAutoPush", false) + if (forceAutoPush) { + autoPush.forceAutoPush = true + autoPush.enabled = false + const forceAutoPushIntervalMins = iface.readProjectNumEntry("qfieldsync", "forceAutoPushIntervalMins", 30) + cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, true) + cloudProjectsModel.projectSetAutoPushIntervalMins(cloudProjectsModel.currentProjectId, forceAutoPushIntervalMins) + } else { + autoPush.forceAutoPush = false + autoPush.enabled = true + } + } + } } diff --git a/src/qml/qgismobileapp.qml b/src/qml/qgismobileapp.qml index a78e187503..8f7d953260 100644 --- a/src/qml/qgismobileapp.qml +++ b/src/qml/qgismobileapp.qml @@ -3377,7 +3377,7 @@ ApplicationWindow { recentProjectListModel.reloadModel() - var cloudProjectId = QFieldCloudUtils.getProjectId(qgisProject.fileName) + const cloudProjectId = QFieldCloudUtils.getProjectId(qgisProject.fileName) cloudProjectsModel.currentProjectId = cloudProjectId cloudProjectsModel.refreshProjectModification(cloudProjectId) if (cloudProjectId !== '') { @@ -3411,6 +3411,8 @@ ApplicationWindow { if (cloudConnection.status === QFieldCloudConnection.LoggedIn) { cloudProjectsModel.refreshProjectFileOutdatedStatus(cloudProjectId) } + + cloudPopup.applyAutoPushProjectSettings(); } else { projectInfo.hasInsertRights = true projectInfo.hasEditRights = true From 48ef7093e3611a99078272a7f4d89b8fbc0e1544 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Thu, 29 Feb 2024 09:21:43 +0700 Subject: [PATCH 3/3] Address review --- src/core/qfieldcloudprojectsmodel.cpp | 30 +++++++++++++++++++++++++++ src/core/qfieldcloudprojectsmodel.h | 5 +++++ src/core/qgismobileapp.h | 9 ++++++++ src/qml/QFieldCloudPopup.qml | 23 +++----------------- src/qml/qgismobileapp.qml | 2 -- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/core/qfieldcloudprojectsmodel.cpp b/src/core/qfieldcloudprojectsmodel.cpp index d79be16a01..f6fb08ee47 100644 --- a/src/core/qfieldcloudprojectsmodel.cpp +++ b/src/core/qfieldcloudprojectsmodel.cpp @@ -159,6 +159,21 @@ void QFieldCloudProjectsModel::setCurrentProjectId( const QString ¤tProjec mCurrentProjectId = currentProjectId; + if ( mCurrentProjectId != QString() ) + { + const bool forceAutoPush = mProject->readBoolEntry( QStringLiteral( "qfieldsync" ), QStringLiteral( "forceAutoPush" ), false ); + if ( forceAutoPush ) + { + projectSetForceAutoPush( mCurrentProjectId, true ); + projectSetAutoPushEnabled( mCurrentProjectId, true ); + projectSetAutoPushIntervalMins( mCurrentProjectId, mProject->readNumEntry( QStringLiteral( "qfieldsync" ), QStringLiteral( "forceAutoPushIntervalMins" ) ) ); + } + else + { + projectSetForceAutoPush( mCurrentProjectId, false ); + } + } + emit currentProjectIdChanged(); emit currentProjectDataChanged(); } @@ -1948,12 +1963,25 @@ QHash QFieldCloudProjectsModel::roleNames() const roles[UserRoleRole] = "UserRole"; roles[UserRoleOriginRole] = "UserRoleOrigin"; roles[DeltaListRole] = "DeltaList"; + roles[ForceAutoPushRole] = "ForceAutoPush"; roles[AutoPushEnabledRole] = "AutoPushEnabled"; roles[AutoPushIntervalMinsRole] = "AutoPushIntervalMins"; return roles; } +void QFieldCloudProjectsModel::projectSetForceAutoPush( const QString &projectId, bool force ) +{ + const QModelIndex projectIndex = findProjectIndex( projectId ); + + if ( projectIndex.isValid() ) + { + CloudProject *project = mProjects[projectIndex.row()]; + project->forceAutoPush = force; + emit dataChanged( projectIndex, projectIndex, QVector() << ForceAutoPushRole ); + } +} + void QFieldCloudProjectsModel::projectSetAutoPushEnabled( const QString &projectId, bool enabled ) { const QModelIndex projectIndex = findProjectIndex( projectId ); @@ -2174,6 +2202,8 @@ QVariant QFieldCloudProjectsModel::data( const QModelIndex &index, int role ) co return mProjects.at( index.row() )->userRoleOrigin; case DeltaListRole: return QVariant::fromValue( mProjects.at( index.row() )->deltaListModel ); + case ForceAutoPushRole: + return mProjects.at( index.row() )->forceAutoPush; case AutoPushEnabledRole: return mProjects.at( index.row() )->autoPushEnabled; case AutoPushIntervalMinsRole: diff --git a/src/core/qfieldcloudprojectsmodel.h b/src/core/qfieldcloudprojectsmodel.h index f0955fcd6f..987d8da8ad 100644 --- a/src/core/qfieldcloudprojectsmodel.h +++ b/src/core/qfieldcloudprojectsmodel.h @@ -68,6 +68,7 @@ class QFieldCloudProjectsModel : public QAbstractListModel UserRoleRole, UserRoleOriginRole, DeltaListRole, + ForceAutoPushRole, AutoPushEnabledRole, AutoPushIntervalMinsRole, }; @@ -276,6 +277,9 @@ class QFieldCloudProjectsModel : public QAbstractListModel //! Cancels ongoing cloud project download with \a projectId. Q_INVOKABLE void projectCancelDownload( const QString &projectId ); + //! Forces the cloud project auto-push enabled state to be TRUE + Q_INVOKABLE void projectSetForceAutoPush( const QString &projectId, bool force ); + //! Toggles the cloud project auto-push enabled state Q_INVOKABLE void projectSetAutoPushEnabled( const QString &projectId, bool enabled ); @@ -437,6 +441,7 @@ class QFieldCloudProjectsModel : public QAbstractListModel QDateTime lastLocalDataLastUpdatedAt; QDateTime lastRefreshedAt; + bool forceAutoPush = false; bool autoPushEnabled = false; int autoPushIntervalMins = 30; diff --git a/src/core/qgismobileapp.h b/src/core/qgismobileapp.h index e2cb5bf892..8afee0e45d 100644 --- a/src/core/qgismobileapp.h +++ b/src/core/qgismobileapp.h @@ -138,6 +138,15 @@ class QFIELD_CORE_EXPORT QgisMobileapp : public QQmlApplicationEngine */ double readProjectDoubleEntry( const QString &scope, const QString &key, double def = 0.0 ) const; + /** + * Reads a boolean from the specified \a scope and \a key from the currently opened project + * + * \param scope entry scope (group) name + * \param key entry key name. Keys are '/'-delimited entries, implying a hierarchy of keys and corresponding values + * \param def default value to return if the specified \a key does not exist within the \a scope + * + * \returns entry value as boolean from \a scope given its \a key + */ bool readProjectBoolEntry( const QString &scope, const QString &key, bool def = false ) const; /** diff --git a/src/qml/QFieldCloudPopup.qml b/src/qml/QFieldCloudPopup.qml index 7a1df2da77..073b903840 100644 --- a/src/qml/QFieldCloudPopup.qml +++ b/src/qml/QFieldCloudPopup.qml @@ -476,14 +476,14 @@ Popup { bottomPadding: 10 font: Theme.tipFont wrapMode: Text.WordWrap - color: autoPush.checked || autoPush.forceAutoPush ? Theme.mainTextColor : Theme.secondaryTextColor + color: autoPush.checked ? Theme.mainTextColor : Theme.secondaryTextColor text: qsTr('Automatically push changes every %n minute(s)','',0 + cloudProjectsModel.currentProjectData.AutoPushIntervalMins) MouseArea { anchors.fill: parent onClicked: { - if (autoPush.forceAutoPush) { + if (!!cloudProjectsModel.currentProjectData.ForceAutoPush) { displayToast(qsTr('The current project does not allow for auto-push to be turned off')) } else { cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, !autoPush.checked) @@ -498,8 +498,7 @@ Popup { Layout.alignment: Qt.AlignVCenter width: implicitContentWidth small: true - - property bool forceAutoPush: false + enabled: !cloudProjectsModel.currentProjectData.ForceAutoPush checked: !!cloudProjectsModel.currentProjectData.AutoPushEnabled onClicked: { @@ -729,20 +728,4 @@ Popup { cloudProjectsModel.discardLocalChangesFromCurrentProject(cloudProjectsModel.currentProjectId) cloudProjectsModel.downloadProject(cloudProjectsModel.currentProjectId, true) } - - function applyAutoPushProjectSettings() { - if (cloudProjectsModel.currentProjectId !== '') { - const forceAutoPush = iface.readProjectBoolEntry("qfieldsync", "forceAutoPush", false) - if (forceAutoPush) { - autoPush.forceAutoPush = true - autoPush.enabled = false - const forceAutoPushIntervalMins = iface.readProjectNumEntry("qfieldsync", "forceAutoPushIntervalMins", 30) - cloudProjectsModel.projectSetAutoPushEnabled(cloudProjectsModel.currentProjectId, true) - cloudProjectsModel.projectSetAutoPushIntervalMins(cloudProjectsModel.currentProjectId, forceAutoPushIntervalMins) - } else { - autoPush.forceAutoPush = false - autoPush.enabled = true - } - } - } } diff --git a/src/qml/qgismobileapp.qml b/src/qml/qgismobileapp.qml index 8f7d953260..e41c123188 100644 --- a/src/qml/qgismobileapp.qml +++ b/src/qml/qgismobileapp.qml @@ -3411,8 +3411,6 @@ ApplicationWindow { if (cloudConnection.status === QFieldCloudConnection.LoggedIn) { cloudProjectsModel.refreshProjectFileOutdatedStatus(cloudProjectId) } - - cloudPopup.applyAutoPushProjectSettings(); } else { projectInfo.hasInsertRights = true projectInfo.hasEditRights = true