From e20cbf2bcc46269845d757d439ad28d0a0e09fcd Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Tue, 16 Mar 2021 11:59:29 +0100 Subject: [PATCH 01/22] incorporate changes from previous pr --- app/models/projectsmodel_future.cpp | 8 ++++---- app/project_future.cpp | 4 ++-- app/project_future.h | 12 +++++------- app/qml/ProjectDelegateItem.qml | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/models/projectsmodel_future.cpp b/app/models/projectsmodel_future.cpp index 0168f391b..9ee830905 100644 --- a/app/models/projectsmodel_future.cpp +++ b/app/models/projectsmodel_future.cpp @@ -120,9 +120,9 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject // TODO: later copy data by copy constructor // TODO: check for project errors (from ListByName API ~> not authorized / no rights / no version) - if ( pendingProjects.contains( mergin->projectIdentifier() ) ) + if ( pendingProjects.contains( mergin->id() ) ) { - TransactionStatus projectTransaction = pendingProjects.value( mergin->projectIdentifier() ); + TransactionStatus projectTransaction = pendingProjects.value( mergin->id() ); mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; mergin->pending = true; } @@ -145,9 +145,9 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject mergin->projectNamespace = remoteEntry.projectNamespace; // TODO: later copy data by copy constructor - if ( pendingProjects.contains( mergin->projectIdentifier() ) ) + if ( pendingProjects.contains( mergin->id() ) ) { - TransactionStatus projectTransaction = pendingProjects.value( mergin->projectIdentifier() ); + TransactionStatus projectTransaction = pendingProjects.value( mergin->id() ); mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; mergin->pending = true; } diff --git a/app/project_future.cpp b/app/project_future.cpp index 8b6be5f46..dcb3cabd8 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -21,12 +21,12 @@ void LocalProject_future::copyValues( const LocalProject_future &other ) localVersion = other.localVersion; } -QString MerginProject_future::projectIdentifier() +QString MerginProject_future::id() { return MerginApi::getFullProjectName( projectNamespace, projectName ); } -QString LocalProject_future::projectIdentifier() +QString LocalProject_future::id() { if ( !projectName.isEmpty() && !projectNamespace.isEmpty() ) return MerginApi::getFullProjectName( projectNamespace, projectName ); diff --git a/app/project_future.h b/app/project_future.h index 6df937e90..cef3229b6 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -33,7 +33,7 @@ struct LocalProject_future QString projectName; QString projectNamespace; - QString projectIdentifier(); + QString id(); QString projectDir; QString projectError; // Error that leads to project not being able to open in app @@ -53,15 +53,16 @@ struct MerginProject_future QString projectName; QString projectNamespace; - QString projectIdentifier(); - - QDateTime serverUpdated; // available latest version of project files on server // TODO: maybe we do not need this at all - only as description + QString id(); + QDateTime serverUpdated; // available latest version of project files on server int serverVersion; ProjectStatus_future status = ProjectStatus_future::_NoVersion; bool pending = false; qreal progress = 0; + + // Maybe better use enum or int for error code QString remoteError; // Error leading to project not being able to sync }; @@ -75,9 +76,6 @@ struct Project_future bool isMergin() { return mergin != nullptr; } bool isLocal() { return local != nullptr; } - - //! Attributes that should be there no matter the project type - QString projectDescription; // rather on the fly }; #endif // PROJECT_FUTURE_H diff --git a/app/qml/ProjectDelegateItem.qml b/app/qml/ProjectDelegateItem.qml index cc59b824a..f6d37351b 100644 --- a/app/qml/ProjectDelegateItem.qml +++ b/app/qml/ProjectDelegateItem.qml @@ -167,7 +167,7 @@ Rectangle { } // Additional item - DelegateButton { + DelegateButton { // TODO: replace with footer property on projects listview visible: itemContainer.isAdditional width: itemContainer.width height: itemContainer.height From 9378c6134f8efc8f57d27f6bb8220e36c2e38f18 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Tue, 16 Mar 2021 12:20:23 +0100 Subject: [PATCH 02/22] remove models subfolder --- app/{models => }/projectsmodel_future.cpp | 2 ++ app/{models => }/projectsmodel_future.h | 0 app/{models => }/projectsproxymodel_future.cpp | 0 app/{models => }/projectsproxymodel_future.h | 0 app/sources.pri | 8 ++++---- 5 files changed, 6 insertions(+), 4 deletions(-) rename app/{models => }/projectsmodel_future.cpp (98%) rename app/{models => }/projectsmodel_future.h (100%) rename app/{models => }/projectsproxymodel_future.cpp (100%) rename app/{models => }/projectsproxymodel_future.h (100%) diff --git a/app/models/projectsmodel_future.cpp b/app/projectsmodel_future.cpp similarity index 98% rename from app/models/projectsmodel_future.cpp rename to app/projectsmodel_future.cpp index 9ee830905..589979c5e 100644 --- a/app/models/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -117,6 +117,8 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject std::unique_ptr mergin = std::unique_ptr( new MerginProject_future() ); mergin->projectName = merginProjects[i].projectName; mergin->projectNamespace = merginProjects[i].projectNamespace; + mergin->serverVersion = merginProjects[i].version; + mergin->serverUpdated = merginProjects[i].serverUpdated; // TODO: later copy data by copy constructor // TODO: check for project errors (from ListByName API ~> not authorized / no rights / no version) diff --git a/app/models/projectsmodel_future.h b/app/projectsmodel_future.h similarity index 100% rename from app/models/projectsmodel_future.h rename to app/projectsmodel_future.h diff --git a/app/models/projectsproxymodel_future.cpp b/app/projectsproxymodel_future.cpp similarity index 100% rename from app/models/projectsproxymodel_future.cpp rename to app/projectsproxymodel_future.cpp diff --git a/app/models/projectsproxymodel_future.h b/app/projectsproxymodel_future.h similarity index 100% rename from app/models/projectsproxymodel_future.h rename to app/projectsproxymodel_future.h diff --git a/app/sources.pri b/app/sources.pri index f270a9b89..99b152f6f 100644 --- a/app/sources.pri +++ b/app/sources.pri @@ -32,8 +32,8 @@ ios/iosutils.cpp \ inputprojutils.cpp \ codefilter.cpp \ qrdecoder.cpp \ -models/projectsproxymodel_future.cpp \ -models/projectsmodel_future.cpp \ +projectsproxymodel_future.cpp \ +projectsmodel_future.cpp \ project_future.cpp \ exists(merginsecrets.cpp) { @@ -77,8 +77,8 @@ ios/iosutils.h \ inputprojutils.h \ codefilter.h \ qrdecoder.h \ -models/projectsproxymodel_future.h \ -models/projectsmodel_future.h \ +projectsproxymodel_future.h \ +projectsmodel_future.h \ project_future.h \ contains(DEFINES, INPUT_TEST) { From 31cca790746301b7fbf60cb75912ce4f9a5af7f3 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 17 Mar 2021 13:47:56 +0100 Subject: [PATCH 03/22] move sync logic to cpp --- app/merginapi.cpp | 2 + app/project_future.cpp | 6 +- app/project_future.h | 39 +++++++--- app/projectsmodel_future.cpp | 137 ++++++++++++++++++++++++++++++++--- app/projectsmodel_future.h | 20 ++++- 5 files changed, 179 insertions(+), 25 deletions(-) diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 265ffb020..e6524fcf1 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -121,6 +121,7 @@ QString MerginApi::listProjectsByName( const QStringList &projectNames ) projects.insert( "projects", projectsArr ); body.setObject( projects ); + qDebug() << "PMR: listProjectsByName(): requesting projects " << projectNames; QUrl url( mApiRoot + QStringLiteral( "/v1/project/by_names" ) ); @@ -1237,6 +1238,7 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) } // for any local projects we can update the latest server version + // TODO: this should now be done inside model so no need to do it here (LocalProjects do not have server version anymore) for ( MerginProjectListEntry project : mRemoteProjects ) { QString fullProjectName = getFullProjectName( project.projectNamespace, project.projectName ); diff --git a/app/project_future.cpp b/app/project_future.cpp index dcb3cabd8..b937b568d 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -8,9 +8,10 @@ ***************************************************************************/ #include "project_future.h" - #include "merginapi.h" +#include + void LocalProject_future::copyValues( const LocalProject_future &other ) { projectName = other.projectName; @@ -31,5 +32,6 @@ QString LocalProject_future::id() if ( !projectName.isEmpty() && !projectNamespace.isEmpty() ) return MerginApi::getFullProjectName( projectNamespace, projectName ); - return projectDir; + QDir dir( projectDir ); + return dir.dirName(); } diff --git a/app/project_future.h b/app/project_future.h index cef3229b6..e4a359c82 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -27,13 +27,13 @@ Q_ENUMS( ProjectStatus_future ) struct LocalProject_future { - LocalProject_future() {}; /*{ qDebug() << "Building LocalProject_future " << this; }*/ - ~LocalProject_future() {}; /*{ qDebug() << "Removing LocalProject_future " << this; }*/ + LocalProject_future() {}; + ~LocalProject_future() {}; QString projectName; QString projectNamespace; - QString id(); + QString id(); //! projectFullName for time being QString projectDir; QString projectError; // Error that leads to project not being able to open in app @@ -47,13 +47,14 @@ struct LocalProject_future struct MerginProject_future { - MerginProject_future() {}; /*{ qDebug() << "Building MerginProject_future " << this; }*/ - ~MerginProject_future() {}; /*{ qDebug() << "Removing MerginProject_future " << this; }*/ + MerginProject_future() {}; + ~MerginProject_future() {}; QString projectName; QString projectNamespace; - QString id(); + QString id(); //! projectFullName for time being + QDateTime serverUpdated; // available latest version of project files on server int serverVersion; @@ -68,14 +69,32 @@ struct MerginProject_future struct Project_future { - Project_future() {}; /*{ qDebug() << "Building Project_future " << this; }*/ - ~Project_future() {}; /*{ qDebug() << "Removing Project_future " << this; }*/ + Project_future() {}; + ~Project_future() {}; std::unique_ptr mergin; std::unique_ptr local; - bool isMergin() { return mergin != nullptr; } - bool isLocal() { return local != nullptr; } + bool isMergin() const { return mergin != nullptr; } + bool isLocal() const { return local != nullptr; } + + bool operator ==( const Project_future &other ) + { + if ( this->isLocal() && other.isLocal() ) + { + return this->local->id() == other.local->id(); + } + else if ( this->isMergin() && other.isMergin() ) + { + return this->mergin->id() == other.mergin->id(); + } + return false; + } + + bool operator !=( const Project_future &other ) + { + return !( *this == other ); + } }; #endif // PROJECT_FUTURE_H diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 589979c5e..6476f64e0 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -10,6 +10,7 @@ #include "projectsmodel_future.h" #include "localprojectsmanager.h" #include "inpututils.h" +#include "merginuserauth.h" ProjectsModel_future::ProjectsModel_future( MerginApi *merginApi, @@ -41,21 +42,29 @@ ProjectsModel_future::ProjectsModel_future( } } -QVariant ProjectsModel_future::data( const QModelIndex &, int ) const +QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const { - return QVariant( "Test data" ); + if ( !index.isValid() ) + return QVariant(); + + std::shared_ptr project = mProjects.at( index.row() ); + + switch ( role ) { + default: return QVariant("TestData"); + } } -QModelIndex ProjectsModel_future::index( int, int, const QModelIndex & ) const +QModelIndex ProjectsModel_future::index( int row, int col, const QModelIndex &parent ) const { - return QModelIndex(); + Q_UNUSED( col ) + Q_UNUSED( parent ) + return createIndex( row, 0, nullptr ); } QHash ProjectsModel_future::roleNames() const { QHash roles; - roles[Roles::Project] = QStringLiteral( "project" ).toLatin1(); - + roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); return roles; } @@ -104,6 +113,8 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject local->projectName = localProject.projectName; local->projectNamespace = localProject.projectNamespace; local->projectDir = localProject.projectDir; + local->projectError = localProject.qgisProjectError; + local->qgisProjectFilePath = localProject.qgisProjectFilePath; // TODO: later copy data by copy constructor project->local = std::move( local ); @@ -215,19 +226,104 @@ void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectList endResetModel(); } +void ProjectsModel_future::syncProject( QString projectNamespace, QString projectName ) +{ + std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); + + if ( project == nullptr ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "not in projects list"; + return; + } + + if ( !project->isMergin() ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not a mergin project"; + return; + } + + if ( project->mergin->pending ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is already "; + return; + } + + if ( project->mergin->status == _NoVersion || project->mergin->status == _OutOfDate ) + { + qDebug() << "PMR: updating project:" << project->mergin->id(); + + bool useAuth = !mBackend->userAuth()->hasAuthData() && mModelType == ProjectModelTypes::ExploreProjectsModel; + mBackend->updateProject( projectNamespace, projectName, useAuth ); + } + else if ( project->mergin->status == _Modified ) + { + qDebug() << "PMR: uploading project:" << project->mergin->id(); + mBackend->uploadProject( projectNamespace, projectName ); + } +} + +void ProjectsModel_future::stopProjectSync( QString projectNamespace, QString projectName ) +{ + std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); + + if ( project == nullptr ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "not in projects list"; + return; + } + + if ( !project->isMergin() ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not a mergin project"; + return; + } + + if ( !project->mergin->pending ) + { + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not pending"; + return; + } + + if ( project->mergin->status == _NoVersion || project->mergin->status == _OutOfDate ) + { + qDebug() << "PMR: cancelling update of project:" << project->mergin->id(); + mBackend->updateCancel( project->mergin->id() ); + } + else if ( project->mergin->status == _Modified ) + { + qDebug() << "PMR: cancelling upload of project:" << project->mergin->id(); + mBackend->uploadCancel( project->mergin->id() ); + } +} + void ProjectsModel_future::onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) { Q_UNUSED( projectDir ) - Q_UNUSED( projectFullName ) - Q_UNUSED( successfully ) + + std::shared_ptr project = projectFromId( projectFullName ); + if ( !project || !project->isMergin() || !successfully ) + return; + + project->mergin->pending = false; + project->mergin->progress = 0; + + QModelIndex ix = index( mProjects.indexOf( project ) ); + emit dataChanged( ix, ix ); qDebug() << "PMR: Project " << projectFullName << " finished sync"; } void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ) { - Q_UNUSED( projectFullName ) - Q_UNUSED( progress ) + std::shared_ptr project = projectFromId( projectFullName ); + if ( !project || !project->isMergin() ) + return; + + project->mergin->pending = progress >= 0; + project->mergin->progress = progress >= 0 ? progress : 0; + + QModelIndex ix = index( mProjects.indexOf( project ) ); + emit dataChanged( ix, ix ); qDebug() << "PMR: Project " << projectFullName << " changed sync progress to " << progress; } @@ -247,7 +343,7 @@ QString ProjectsModel_future::modelTypeToFlag() const void ProjectsModel_future::printProjects() const // TODO: Helper function, remove after refactoring is done { - qDebug() << "Model " << this << " with type " << modelTypeToFlag() << " has projects: "; + qDebug() << "PMR: Model " << this << " with type " << modelTypeToFlag() << " has projects: "; for ( const auto &proj : mProjects ) { QString lcl = proj->isLocal() ? "local" : ""; @@ -277,3 +373,22 @@ QStringList ProjectsModel_future::projectNames() const // TODO: use local projec return projectNames; } + +bool ProjectsModel_future::containsProject( QString projectId ) const +{ + std::shared_ptr proj = projectFromId( projectId ); + return proj != nullptr; +} + +std::shared_ptr ProjectsModel_future::projectFromId( QString projectId ) const +{ + for ( int ix = 0; ix < mProjects.size(); ++ix ) + { + if ( mProjects[ix]->isMergin() && mProjects[ix]->mergin->id() == projectId ) + return mProjects[ix]; + else if ( mProjects[ix]->isLocal() && mProjects[ix]->local->id() == projectId ) + return mProjects[ix]; + } + + return nullptr; +} diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 066b6089b..6a5a9224e 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -41,8 +41,15 @@ class ProjectsModel_future : public QAbstractListModel enum Roles { - // TODO: rewrite to individual roles - Project = Qt::UserRole + 1 + ProjectName = Qt::UserRole + 1, + ProjectNamespace, + ProjectFullName, // or ProjectId, filled with folderName if project is not + ProjectDescription, + ProjectPending, + ProjectIsMergin, + ProjectIsLocal, + ProjectStatus, + ProjectProgress }; Q_ENUMS( Roles ) @@ -61,6 +68,12 @@ class ProjectsModel_future : public QAbstractListModel //! Called to list projects, either fetch more or get first Q_INVOKABLE void listProjectsByName(); + //! Syncs specified project - upload or update + Q_INVOKABLE void syncProject( QString projectNamespace, QString projectName ); + + //! Stops running project upload or update + Q_INVOKABLE void stopProjectSync( QString projectNamespace, QString projectName ); + //! Method detecting local project for remote projects void mergeProjects( const MerginProjectList &merginProjects, Transactions pendingProjects ); @@ -76,6 +89,9 @@ class ProjectsModel_future : public QAbstractListModel void printProjects() const; QStringList projectNames() const; + bool containsProject( QString projectId ) const; + std::shared_ptr projectFromId( QString projectId ) const; + MerginApi *mBackend; LocalProjectsManager &mLocalProjectsManager; QList> mProjects; From 48a8b937e33b6573b6a190f0054f7f999cc4d9a3 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Thu, 18 Mar 2021 08:31:42 +0100 Subject: [PATCH 04/22] add several signals/slots --- app/merginapi.cpp | 3 ++- app/merginapi.h | 2 +- app/project_future.h | 2 +- app/projectsmodel_future.cpp | 19 +++++++++++++++++++ app/projectsmodel_future.h | 10 ++++++++-- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/merginapi.cpp b/app/merginapi.cpp index e6524fcf1..5f13bf47e 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -1149,7 +1149,7 @@ void MerginApi::detachProjectFromMergin( const QString &projectNamespace, const mLocalProjects.reloadProjectDir(); emit notify( tr( "Project detached from Mergin" ) ); - emit projectDetached(); + emit projectDetached( projectFullName ); } QString MerginApi::apiRoot() const @@ -1517,6 +1517,7 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) // add the local project if not there yet if ( !mLocalProjects.projectFromMerginName( projectFullName ).isValid() ) { + qDebug() << "PMR: Downloaded project" << projectFullName; QString projectNamespace, projectName; extractProjectName( projectFullName, projectNamespace, projectName ); diff --git a/app/merginapi.h b/app/merginapi.h index 761920d86..6870fca70 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -443,7 +443,7 @@ class MerginApi: public QObject //! Emitted when upload cancellation request has finished void uploadCanceled( const QString &projectFullName, bool result ); void projectDataChanged( const QString &projectFullName ); - void projectDetached(); + void projectDetached( const QString &projectFullName ); private slots: void listProjectsReplyFinished( QString requestId ); diff --git a/app/project_future.h b/app/project_future.h index e4a359c82..1f0b17668 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -27,7 +27,7 @@ Q_ENUMS( ProjectStatus_future ) struct LocalProject_future { - LocalProject_future() {}; + LocalProject_future() {}; // TODO: define copy constructor ~LocalProject_future() {}; QString projectName; diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 6476f64e0..c3b200078 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -24,6 +24,7 @@ ProjectsModel_future::ProjectsModel_future( { QObject::connect( mBackend, &MerginApi::syncProjectStatusChanged, this, &ProjectsModel_future::onProjectSyncProgressChanged ); QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel_future::onProjectSyncFinished ); + QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel_future::onProjectDetachedFromMergin ); // TODO: connect to signals from LocalProjectsManager // QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectAdded ) @@ -40,6 +41,8 @@ ProjectsModel_future::ProjectsModel_future( { // Implement RecentProjectsModel type } + +// QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); } QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const @@ -328,6 +331,22 @@ void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectF qDebug() << "PMR: Project " << projectFullName << " changed sync progress to " << progress; } +void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) +{ + std::shared_ptr newProject = std::shared_ptr( new Project_future() ); + newProject->local = std::unique_ptr( new LocalProject_future( project ) ); +} + +void ProjectsModel_future::onProjectDeleted( const QString &projectFullName ) +{ + Q_UNUSED( projectFullName ) +} + +void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFullName ) +{ + Q_UNUSED( projectFullName ) +} + QString ProjectsModel_future::modelTypeToFlag() const { switch ( mModelType ) diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 6a5a9224e..19ffd583f 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -74,7 +74,7 @@ class ProjectsModel_future : public QAbstractListModel //! Stops running project upload or update Q_INVOKABLE void stopProjectSync( QString projectNamespace, QString projectName ); - //! Method detecting local project for remote projects + //! Method merging local and remote projects based on the model type void mergeProjects( const MerginProjectList &merginProjects, Transactions pendingProjects ); public slots: @@ -83,6 +83,12 @@ class ProjectsModel_future : public QAbstractListModel void onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); void onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ); + void onProjectAdded( const LocalProject_future &project ); + void onProjectDeleted( const QString &projectFullName ); + + void onProjectDetachedFromMergin( const QString &projectFullName ); + void onProjectAttachedToMergin() {}; + private: QString modelTypeToFlag() const; @@ -99,7 +105,7 @@ class ProjectsModel_future : public QAbstractListModel ProjectModelTypes mModelType; //! For pagination - int mPopulatedPage = -1; + int mPopulatedPage = -1; // -> on the fly in QML:: QML should pass this to model //! For processing only my requests QString mLastRequestId; From b93d2be96f66ffe679ef581ef294d36faf7435a2 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Thu, 18 Mar 2021 10:34:22 +0100 Subject: [PATCH 05/22] LocalProjectManager revamp --- app/inpututils.cpp | 8 + app/inpututils.h | 2 + app/localprojectsmanager.cpp | 266 +++++++++++++++---------------- app/localprojectsmanager.h | 116 +++++++------- app/merginapi.cpp | 24 +-- app/merginapi.h | 1 + app/merginprojectstatusmodel.cpp | 2 +- app/project_future.cpp | 4 +- app/project_future.h | 6 +- app/projectsmodel_future.cpp | 21 ++- app/projectsmodel_future.h | 5 +- app/test/testmerginapi.cpp | 12 +- 12 files changed, 247 insertions(+), 220 deletions(-) diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 2dc33db99..59c2066f9 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -443,6 +443,14 @@ QString InputUtils::createUniqueProjectDirectory( const QString &baseDataDir, co return projectDirPath; } +bool InputUtils::removeDir( const QString &dir ) +{ + if( dir.isEmpty() || dir == "/" ) + return false; + + return QDir( dir ).removeRecursively(); +} + void InputUtils::onQgsLogMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level ) { QString levelStr; diff --git a/app/inpututils.h b/app/inpututils.h index 75dac7830..6fc73dd35 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -152,6 +152,8 @@ class InputUtils: public QObject //! Creates a unique project directory for given project name (used for initial download of a project) static QString createUniqueProjectDirectory( const QString &baseDataDir, const QString &projectName ); + static bool removeDir( const QString &projectDir ); + signals: Q_INVOKABLE void showNotificationRequested( const QString &message ); diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index 2a70fa1a9..fece7d997 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -19,18 +19,18 @@ LocalProjectsManager::LocalProjectsManager( const QString &dataDir ) : mDataDir( dataDir ) { - reloadProjectDir(); + reloadDataDir(); } -void LocalProjectsManager::reloadProjectDir() // TODO: maybe add function to reload one specific project +void LocalProjectsManager::reloadDataDir() // TODO: maybe add function to reload one specific project { mProjects.clear(); QStringList entryList = QDir( mDataDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs ); for ( QString folderName : entryList ) { - LocalProjectInfo info; + LocalProject_future info; info.projectDir = mDataDir + "/" + folderName; - info.qgisProjectFilePath = findQgisProjectFile( info.projectDir, info.qgisProjectError ); + info.qgisProjectFilePath = findQgisProjectFile( info.projectDir, info.projectError ); MerginProjectMetadata metadata = MerginProjectMetadata::fromCachedJson( info.projectDir + "/" + MerginApi::sMetadataFile ); if ( metadata.isValid() ) @@ -39,10 +39,10 @@ void LocalProjectsManager::reloadProjectDir() // TODO: maybe add function to rel info.projectNamespace = metadata.projectNamespace; info.localVersion = metadata.version; } - else - { - info.projectName = folderName; - } +// else +// { +// info.projectName = folderName; +// } mProjects << info; } @@ -50,180 +50,171 @@ void LocalProjectsManager::reloadProjectDir() // TODO: maybe add function to rel qDebug() << "LocalProjectsManager: found" << mProjects.size() << "projects"; } -LocalProjectInfo LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const +LocalProject_future LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const { - for ( const LocalProjectInfo &info : mProjects ) + for ( const LocalProject_future &info : mProjects ) { if ( info.projectDir == projectDir ) return info; } - return LocalProjectInfo(); + return LocalProject_future(); } -LocalProjectInfo LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const +LocalProject_future LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const { - for ( const LocalProjectInfo &info : mProjects ) + for ( const LocalProject_future &info : mProjects ) { if ( info.qgisProjectFilePath == projectFilePath ) return info; } - return LocalProjectInfo(); + return LocalProject_future(); } -LocalProjectInfo LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const +LocalProject_future LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const { - for ( const LocalProjectInfo &info : mProjects ) + for ( const LocalProject_future &info : mProjects ) { - if ( MerginApi::getFullProjectName( info.projectNamespace, info.projectName ) == projectFullName ) + if ( info.id() == projectFullName ) return info; } - return LocalProjectInfo(); + return LocalProject_future(); } -LocalProjectInfo LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const +LocalProject_future LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const { return projectFromMerginName( MerginApi::getFullProjectName( projectNamespace, projectName ) ); } -bool LocalProjectsManager::hasMerginProject( const QString &projectFullName ) const -{ - return projectFromMerginName( projectFullName ).isValid(); -} - -bool LocalProjectsManager::hasMerginProject( const QString &projectNamespace, const QString &projectName ) const -{ - return hasMerginProject( MerginApi::getFullProjectName( projectNamespace, projectName ) ); -} - -void LocalProjectsManager::updateProjectStatus( const QString &projectDir ) -{ - for ( LocalProjectInfo &info : mProjects ) - { - if ( info.projectDir == projectDir ) - { - updateProjectStatus( info ); - return; - } - } - Q_ASSERT( false ); // should not happen -} - -void LocalProjectsManager::addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) +//bool LocalProjectsManager::hasMerginProject( const QString &projectFullName ) const +//{ +// return projectFromMerginName( projectFullName ).isValid(); +//} + +//bool LocalProjectsManager::hasMerginProject( const QString &projectNamespace, const QString &projectName ) const +//{ +// return hasMerginProject( MerginApi::getFullProjectName( projectNamespace, projectName ) ); +//} + +//void LocalProjectsManager::updateProjectStatus( const QString &projectDir ) +//{ +// for ( LocalProject_future &info : mProjects ) +// { +// if ( info.projectDir == projectDir ) +// { +// updateProjectStatus( info ); +// return; +// } +// } +// Q_ASSERT( false ); // should not happen +//} + +void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName, const QString &projectNamespace ) { - LocalProjectInfo project; + LocalProject_future project; project.projectDir = projectDir; - project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.qgisProjectError ); + project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.projectError ); project.projectNamespace = projectNamespace; project.projectName = projectName; mProjects << project; -} - -void LocalProjectsManager::addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) -{ - addProject( projectDir, projectNamespace, projectName ); - // version info and status should be updated afterwards - emit localMerginProjectAdded( projectDir ); -} - -void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName ) -{ - addProject( projectDir, QString(), projectName ); emit localProjectAdded( projectDir ); } -void LocalProjectsManager::removeProject( const QString &projectDir ) -{ - for ( int i = 0; i < mProjects.count(); ++i ) - { - if ( mProjects[i].projectDir == projectDir ) - { - mProjects.removeAt( i ); - emit localProjectRemoved( projectDir ); - return; - } - } -} +//void LocalProjectsManager::addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) +//{ +// addProject( projectDir, projectNamespace, projectName ); +// // version info and status should be updated afterwards +// emit localMerginProjectAdded( projectDir ); +//} -void LocalProjectsManager::resetMerginInfo( const QString &projectNamespace, const QString &projectName ) -{ - for ( int i = 0; i < mProjects.count(); ++i ) - { - if ( mProjects[i].projectNamespace == projectNamespace && mProjects[i].projectName == projectName ) - { - mProjects[i].localVersion = -1; - mProjects[i].serverVersion = -1; - mProjects[i].projectNamespace.clear(); - updateProjectStatus( mProjects[i] ); - emit projectMetadataChanged( mProjects[i].projectDir ); - return; - } - } -} +//void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName ) +//{ +// addProject( projectDir, QString(), projectName ); +// emit localProjectAdded( projectDir ); +//} -void LocalProjectsManager::deleteProjectDirectory( const QString &projectDir ) +void LocalProjectsManager::removeLocalProject( const QString &projectDir ) { for ( int i = 0; i < mProjects.count(); ++i ) { if ( mProjects[i].projectDir == projectDir ) { - Q_ASSERT( !projectDir.isEmpty() && projectDir != "/" ); - QDir( projectDir ).removeRecursively(); + InputUtils::removeDir( mProjects[i].projectDir ); mProjects.removeAt( i ); + + emit localProjectRemoved( projectDir ); return; } } } -void LocalProjectsManager::updateMerginLocalVersion( const QString &projectDir, int version ) +//void LocalProjectsManager::removeMerginInfo( const QString &projectFullName ) +//{ +// for ( int i = 0; i < mProjects.count(); ++i ) +// { +// if ( mProjects[i].id() == projectFullName ) +// { +// mProjects[i].localVersion = -1; +// mProjects[i].projectNamespace.clear(); +// InputUtils::removeDir( mProjects[i].projectDir + "/.mergin" ); + +// emit localProjectDataChanged( mProjects[i].projectDir ); +// return; +// } +// } +//} + +void LocalProjectsManager::updateLocalVersion( const QString &projectDir, int version ) { for ( int i = 0; i < mProjects.count(); ++i ) { if ( mProjects[i].projectDir == projectDir ) { mProjects[i].localVersion = version; - updateProjectStatus( mProjects[i] ); - return; - } - } - Q_ASSERT( false ); // should not happen -} -void LocalProjectsManager::updateMerginServerVersion( const QString &projectDir, int version ) -{ - for ( int i = 0; i < mProjects.count(); ++i ) - { - if ( mProjects[i].projectDir == projectDir ) - { - mProjects[i].serverVersion = version; - updateProjectStatus( mProjects[i] ); + emit localProjectDataChanged( mProjects[i].projectDir ); return; } } Q_ASSERT( false ); // should not happen } -void LocalProjectsManager::updateProjectErrors( const QString &projectDir, const QString &errMsg ) +//void LocalProjectsManager::updateMerginServerVersion( const QString &projectDir, int version ) +//{ +// for ( int i = 0; i < mProjects.count(); ++i ) +// { +// if ( mProjects[i].projectDir == projectDir ) +// { +// mProjects[i].serverVersion = version; +// updateProjectStatus( mProjects[i] ); +// return; +// } +// } +// Q_ASSERT( false ); // should not happen +//} + +//void LocalProjectsManager::updateProjectErrors( const QString &projectDir, const QString &errMsg ) +//{ +// for ( int i = 0; i < mProjects.count(); ++i ) +// { +// if ( mProjects[i].projectDir == projectDir ) +// { +// // Effects only local project list, no need to send projectMetadataChanged +// mProjects[i].qgisProjectError = errMsg; +// return; +// } +// } +//} + +void LocalProjectsManager::updateNamespace( const QString &projectDir, const QString &projectNamespace ) { for ( int i = 0; i < mProjects.count(); ++i ) { if ( mProjects[i].projectDir == projectDir ) { - // Effects only local project list, no need to send projectMetadataChanged - mProjects[i].qgisProjectError = errMsg; - return; - } - } -} - -void LocalProjectsManager::updateMerginNamespace( const QString &projectDir, const QString &projectNamespace ) -{ - for ( int i = 0; i < mProjects.count(); ++i ) - { - if ( mProjects[i].projectDir == projectDir ) - { - // Effects only local project list, no need to send projectMetadataChanged mProjects[i].projectNamespace = projectNamespace; + + emit localProjectDataChanged( mProjects[i].projectDir ); return; } } @@ -301,12 +292,15 @@ static int _getProjectFilesCount( const QString &path ) return count; } -ProjectStatus LocalProjectsManager::currentProjectStatus( const LocalProjectInfo &project ) +ProjectStatus_future LocalProjectsManager::currentProjectStatus( const Project_future &project ) { + if ( !project.isMergin() || !project.isLocal() ) // This is not a Mergin project or not downloaded project + return ProjectStatus_future::_NoVersion; + // There was no sync yet - if ( project.localVersion < 0 ) + if ( project.local->localVersion < 0 ) { - return ProjectStatus::NoVersion; + return ProjectStatus_future::_NoVersion; } // @@ -314,31 +308,31 @@ ProjectStatus LocalProjectsManager::currentProjectStatus( const LocalProjectInfo // // Something has locally changed after last sync with server - QString metadataFilePath = project.projectDir + "/" + MerginApi::sMetadataFile; - QDateTime lastModified = _getLastModifiedFileDateTime( project.projectDir ); + QString metadataFilePath = project.local->projectDir + "/" + MerginApi::sMetadataFile; + QDateTime lastModified = _getLastModifiedFileDateTime( project.local->projectDir ); QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); - int filesCount = _getProjectFilesCount( project.projectDir ); + int filesCount = _getProjectFilesCount( project.local->projectDir ); if ( lastSync < lastModified || meta.files.count() != filesCount ) { - return ProjectStatus::Modified; + return ProjectStatus_future::_Modified; } // Version is lower than latest one, last sync also before updated - if ( project.localVersion < project.serverVersion ) + if ( project.local->localVersion < project.mergin->serverVersion ) { - return ProjectStatus::OutOfDate; + return ProjectStatus_future::_OutOfDate; } - return ProjectStatus::UpToDate; + return ProjectStatus_future::_UpToDate; } -void LocalProjectsManager::updateProjectStatus( LocalProjectInfo &project ) -{ - ProjectStatus newStatus = currentProjectStatus( project ); - if ( newStatus != project.status ) - { - project.status = newStatus; - emit projectMetadataChanged( project.projectDir ); - } -} +//void LocalProjectsManager::updateProjectStatus( LocalProject_future &project ) +//{ +// ProjectStatus newStatus = currentProjectStatus( project ); +// if ( newStatus != project.status ) +// { +// project.status = newStatus; +// emit projectMetadataChanged( project.projectDir ); +// } +//} diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index dfb84a674..23f86c36c 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -13,50 +13,50 @@ #include #include -enum ProjectStatus -{ - NoVersion, //!< the project is not available locally - UpToDate, //!< both server and local copy are in sync with no extra modifications - OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) - Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) - NonProjectItem //!< only for mock projects, acts like a hook to enable extra functionality for models working with projects . -}; -Q_ENUMS( ProjectStatus ) +//enum ProjectStatus +//{ +// NoVersion, //!< the project is not available locally +// UpToDate, //!< both server and local copy are in sync with no extra modifications +// OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) +// Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) +// NonProjectItem //!< only for mock projects, acts like a hook to enable extra functionality for models working with projects . +//}; +//Q_ENUMS( ProjectStatus ) //! Summary information about a local project -struct LocalProjectInfo -{ - bool isValid() const { return !projectDir.isEmpty(); } +//struct LocalProjectInfo +//{ +// bool isValid() const { return !projectDir.isEmpty(); } - bool isShowable() const { return qgisProjectError.isEmpty(); } +// bool isShowable() const { return qgisProjectError.isEmpty(); } - QString projectDir; //!< full path to the project directory +// QString projectDir; //!< full path to the project directory - QString qgisProjectFilePath; //!< path to the .qgs/.qgz file (or empty if not have exactly one such file) +// QString qgisProjectFilePath; //!< path to the .qgs/.qgz file (or empty if not have exactly one such file) - QString qgisProjectError; //!< If project is invalid, projectError carry more information why - // TODO: reset when project is synchronized +// QString qgisProjectError; //!< If project is invalid, projectError carry more information why +// // TODO: reset when project is synchronized - // - // mergin-specific project info (may be empty) - // +// // +// // mergin-specific project info (may be empty) +// // - QString projectName; - QString projectNamespace; +// QString projectName; +// QString projectNamespace; - int localVersion = -1; //!< the project version that is currently available locally - int serverVersion = -1; //!< the project version most recently seen on server (may be -1 if no info from server is available) +// int localVersion = -1; //!< the project version that is currently available locally +// int serverVersion = -1; //!< the project version most recently seen on server (may be -1 if no info from server is available) - ProjectStatus status = NoVersion; +// ProjectStatus status = NoVersion; - bool operator ==( const LocalProjectInfo &other ) { return ( projectName == other.projectName && projectNamespace == other.projectNamespace );} - bool operator !=( const LocalProjectInfo &other ) { return !( *this == other ); } +// bool operator ==( const LocalProjectInfo &other ) { return ( projectName == other.projectName && projectNamespace == other.projectNamespace );} +// bool operator !=( const LocalProjectInfo &other ) { return !( *this == other ); } - // Sync status (e.g. progress) is not kept here because if a project does not exist locally yet - // and it is only being downloaded for the first time, it's not in the list of local projects either - // and we would need to do some workarounds for that. -}; +// // Sync status (e.g. progress) is not kept here because if a project does not exist locally yet +// // and it is only being downloaded for the first time, it's not in the list of local projects either +// // and we would need to do some workarounds for that. +//}; class LocalProjectsManager : public QObject @@ -67,75 +67,75 @@ class LocalProjectsManager : public QObject QString dataDir() const { return mDataDir; } - QList projects() const { return mProjects; } + QList projects() const { return mProjects; } - void reloadProjectDir(); + //! Loads all projects from mDataDir, removes all old projects + void reloadDataDir(); - LocalProjectInfo projectFromDirectory( const QString &projectDir ) const; - LocalProjectInfo projectFromProjectFilePath( const QString &projectDir ) const; + LocalProject_future projectFromDirectory( const QString &projectDir ) const; + LocalProject_future projectFromProjectFilePath( const QString &projectFilePath ) const; - LocalProjectInfo projectFromMerginName( const QString &projectFullName ) const; - LocalProjectInfo projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const; + LocalProject_future projectFromMerginName( const QString &projectFullName ) const; + LocalProject_future projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const; - bool hasMerginProject( const QString &projectFullName ) const; - bool hasMerginProject( const QString &projectNamespace, const QString &projectName ) const; + //! Adds entry about newly created project + void addLocalProject( const QString &projectDir, const QString &projectName, const QString &projectNamespace = QString() ); - void updateProjectStatus( const QString &projectDir ); +// bool hasMerginProject( const QString &projectFullName ) const; +// bool hasMerginProject( const QString &projectNamespace, const QString &projectName ) const; + +// void updateProjectStatus( const QString &projectDir ); //! Should add an entry about newly created Mergin project - void addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); +// void addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); //! Should add an entry about newly created local project - void addLocalProject( const QString &projectDir, const QString &projectName ); +// void addLocalProject( const QString &projectDir, const QString &projectName ); //! Should forget about that project (it has been removed already) - void removeProject( const QString &projectDir ); + void removeLocalProject( const QString &projectDir ); //! Resets mergin related info for given project. - void resetMerginInfo( const QString &projectNamespace, const QString &projectName ); +// void removeMerginInfo( const QString &projectFullName ); //! Recursively removes project's directory (only when it exists in the list) - void deleteProjectDirectory( const QString &projectDir ); +// void deleteProjectDirectory( const QString &projectDir ); // // updates of mergin info // //! after successful update/upload - both server and local version are the same - void updateMerginLocalVersion( const QString &projectDir, int version ); + void updateLocalVersion( const QString &projectDir, int version ); //! after receiving project info with server version (local version stays the same - void updateMerginServerVersion( const QString &projectDir, int version ); +// void updateMerginServerVersion( const QString &projectDir, int version ); //! Updates qgisProjectError (after successful project synced) - void updateProjectErrors( const QString &projectDir, const QString &errMsg ); +// void updateProjectErrors( const QString &projectDir, const QString &errMsg ); //! Updates proejct's namespace - void updateMerginNamespace( const QString &projectDir, const QString &projectNamespace ); + void updateNamespace( const QString &projectDir, const QString &projectNamespace ); //! Finds all QGIS project files and set the err variable if any occured. QString findQgisProjectFile( const QString &projectDir, QString &err ); - static ProjectStatus currentProjectStatus( const LocalProjectInfo &project ); // TODO: local project manager should not have status + static ProjectStatus_future currentProjectStatus( const Project_future &project ); // TODO: maybe move somewhere else? signals: void projectMetadataChanged( const QString &projectDir ); void localMerginProjectAdded( const QString &projectDir ); void localProjectAdded( const QString &projectDir ); void localProjectRemoved( const QString &projectDir ); + void localProjectDataChanged( const QString &projectDir ); - private: - void updateProjectStatus( LocalProjectInfo &project ); // TODO: local project manager should not have status - - //! Should add an entry about newly created project. Emits no signals - void addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); +// private: +// void updateProjectStatus( LocalProject_future &project ); // TODO: local project manager should not have status private: QString mDataDir; //!< directory with all local projects - QList mProjects; - - QList mLocalProjects_future; + QList mProjects; }; diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 5f13bf47e..19dca75e8 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -760,11 +760,12 @@ void MerginApi::createProjectFinished() extractProjectName( projectFullName, projectNamespace, projectName ); // Upload data if createProject has been called for a local project with empty namespace (case of migrating a project) - for ( const LocalProjectInfo &info : mLocalProjects.projects() ) + for ( const LocalProject_future &info : mLocalProjects.projects() ) { if ( info.projectName == projectName && info.projectNamespace.isEmpty() ) { - mLocalProjects.updateMerginNamespace( info.projectDir, projectNamespace ); + mLocalProjects.updateNamespace( info.projectDir, projectNamespace ); + emit projectAttachedToMergin( projectFullName ); QDir projectDir( info.projectDir ); if ( projectDir.exists() && !projectDir.isEmpty() ) @@ -1136,17 +1137,18 @@ void MerginApi::migrateProjectToMergin( const QString &projectName, const QStrin void MerginApi::detachProjectFromMergin( const QString &projectNamespace, const QString &projectName ) { - // remove mergin folder + // Remove mergin folder QString projectFullName = getFullProjectName( projectNamespace, projectName ); - LocalProjectInfo projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + if ( projectInfo.isValid() ) { - QDir merginProjectDir( projectInfo.projectDir + "/.mergin" ); - merginProjectDir.removeRecursively(); + InputUtils::removeDir( projectInfo.projectDir + "/.mergin" ); } - // Update localProjects (updating mMerginProjects can be omitted since it is updated on listing projects) - mLocalProjects.resetMerginInfo( projectNamespace, projectName ); - mLocalProjects.reloadProjectDir(); + + // Update localProject + mLocalProjects.updateNamespace( projectInfo.projectDir, "" ); + mLocalProjects.updateLocalVersion( projectInfo.projectDir, -1 ); emit notify( tr( "Project detached from Mergin" ) ); emit projectDetached( projectFullName ); @@ -2437,7 +2439,9 @@ void MerginApi::finishProjectSync( const QString &projectFullName, bool syncSucc writeData( transaction.projectMetadata, transaction.projectDir + "/" + MerginApi::sMetadataFile ); // update info of local projects - mLocalProjects.updateMerginLocalVersion( transaction.projectDir, transaction.version ); + mLocalProjects.updateLocalVersion( transaction.projectDir, transaction.version ); + + // TODO: emit server version mLocalProjects.updateMerginServerVersion( transaction.projectDir, transaction.version ); InputUtils::log( "sync " + projectFullName, QStringLiteral( "### Finished ### New project version: %1\n" ).arg( transaction.version ) ); diff --git a/app/merginapi.h b/app/merginapi.h index 6870fca70..6c99c31fc 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -444,6 +444,7 @@ class MerginApi: public QObject void uploadCanceled( const QString &projectFullName, bool result ); void projectDataChanged( const QString &projectFullName ); void projectDetached( const QString &projectFullName ); + void projectAttachedToMergin( const QString &projectFullName ); private slots: void listProjectsReplyFinished( QString requestId ); diff --git a/app/merginprojectstatusmodel.cpp b/app/merginprojectstatusmodel.cpp index 3c1d81742..335ec99a1 100644 --- a/app/merginprojectstatusmodel.cpp +++ b/app/merginprojectstatusmodel.cpp @@ -125,7 +125,7 @@ void MerginProjectStatusModel::infoProjectUpdated( const ProjectDiff &projectDif bool MerginProjectStatusModel::loadProjectInfo( const QString &projectFullName ) { - LocalProjectInfo projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( !projectInfo.projectDir.isEmpty() ) { ProjectDiff diff = MerginApi::localProjectChanges( projectInfo.projectDir ); diff --git a/app/project_future.cpp b/app/project_future.cpp index b937b568d..6734b3553 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -22,12 +22,12 @@ void LocalProject_future::copyValues( const LocalProject_future &other ) localVersion = other.localVersion; } -QString MerginProject_future::id() +QString MerginProject_future::id() const { return MerginApi::getFullProjectName( projectNamespace, projectName ); } -QString LocalProject_future::id() +QString LocalProject_future::id() const { if ( !projectName.isEmpty() && !projectNamespace.isEmpty() ) return MerginApi::getFullProjectName( projectNamespace, projectName ); diff --git a/app/project_future.h b/app/project_future.h index 1f0b17668..f76c1f879 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -33,7 +33,7 @@ struct LocalProject_future QString projectName; QString projectNamespace; - QString id(); //! projectFullName for time being + QString id() const; //! projectFullName for time being QString projectDir; QString projectError; // Error that leads to project not being able to open in app @@ -43,6 +43,8 @@ struct LocalProject_future int localVersion = -1; void copyValues( const LocalProject_future &other ); + + bool isValid() { return !projectDir.isEmpty(); } }; struct MerginProject_future @@ -53,7 +55,7 @@ struct MerginProject_future QString projectName; QString projectNamespace; - QString id(); //! projectFullName for time being + QString id() const; //! projectFullName for time being QDateTime serverUpdated; // available latest version of project files on server int serverVersion; diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index c3b200078..52e732be5 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -43,6 +43,7 @@ ProjectsModel_future::ProjectsModel_future( } // QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); + QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); } QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const @@ -331,20 +332,34 @@ void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectF qDebug() << "PMR: Project " << projectFullName << " changed sync progress to " << progress; } -void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) +void ProjectsModel_future::onProjectAdded( const QString &projectDir ) { - std::shared_ptr newProject = std::shared_ptr( new Project_future() ); - newProject->local = std::unique_ptr( new LocalProject_future( project ) ); + Q_UNUSED( projectDir ) + qDebug() << "PMR: Added project" << projectDir; } void ProjectsModel_future::onProjectDeleted( const QString &projectFullName ) { Q_UNUSED( projectFullName ) + qDebug() << "PMR: Deleted project" << projectFullName; +} + +void ProjectsModel_future::onProjectDataChanged( const QString &projectDir ) +{ + Q_UNUSED( projectDir ) + qDebug() << "PMR: Data changed in project" << projectDir; } void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFullName ) { Q_UNUSED( projectFullName ) + qDebug() << "PMR: Project detached from mergin " << projectFullName; +} + +void ProjectsModel_future::onProjectAttachedToMergin(const QString &projectFullName) +{ + Q_UNUSED( projectFullName ) + qDebug() << "PMR: Project attached to mergin " << projectFullName; } QString ProjectsModel_future::modelTypeToFlag() const diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 19ffd583f..4e8dc89fa 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -83,11 +83,12 @@ class ProjectsModel_future : public QAbstractListModel void onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); void onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ); - void onProjectAdded( const LocalProject_future &project ); + void onProjectAdded( const QString &projectDir ); void onProjectDeleted( const QString &projectFullName ); + void onProjectDataChanged( const QString &projectDir ); void onProjectDetachedFromMergin( const QString &projectFullName ); - void onProjectAttachedToMergin() {}; + void onProjectAttachedToMergin( const QString &projectFullName ); private: diff --git a/app/test/testmerginapi.cpp b/app/test/testmerginapi.cpp index f0cbc2656..6f30522bd 100644 --- a/app/test/testmerginapi.cpp +++ b/app/test/testmerginapi.cpp @@ -159,7 +159,7 @@ void TestMerginApi::testDownloadProject() // check that the local projects are updated QVERIFY( mApi->localProjectsManager().hasMerginProject( mUsername, projectName ) ); - LocalProjectInfo project = mApi->localProjectsManager().projectFromMerginName( projectNamespace, projectName ); + LocalProjectInfo project = mApi->localProjectsManager().projectFromFullName( projectNamespace, projectName ); QVERIFY( project.isValid() ); QCOMPARE( project.projectDir, mApi->projectsPath() + "/" + projectName ); QCOMPARE( project.serverVersion, 1 ); @@ -208,7 +208,7 @@ void TestMerginApi::createRemoteProject( MerginApi *api, const QString &projectN QCOMPARE( info.size(), 0 ); QVERIFY( dir.isEmpty() ); - api->localProjectsManager().removeProject( projectDir ); + api->localProjectsManager().removeLocalProject( projectDir ); QCOMPARE( QFileInfo( projectDir ).size(), 0 ); QVERIFY( QDir( projectDir ).isEmpty() ); @@ -1302,7 +1302,7 @@ void TestMerginApi::testMigrateProject() createLocalProject( projectDir ); // reload localmanager after copying the project - mApi->mLocalProjects.reloadProjectDir(); + mApi->mLocalProjects.reloadDataDir(); QStringList entryList = QDir( projectDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs ); // migrate project @@ -1348,7 +1348,7 @@ void TestMerginApi::testMigrateProjectAndSync() // step 1 createLocalProject( projectDir ); - mApi->mLocalProjects.reloadProjectDir(); + mApi->mLocalProjects.reloadDataDir(); // step 2 QSignalSpy spy( mApi, &MerginApi::projectCreated ); QSignalSpy spy2( mApi, &MerginApi::syncProjectFinished ); @@ -1401,7 +1401,7 @@ void TestMerginApi::testMigrateDetachProject() createLocalProject( projectDir ); // reload localmanager after copying the project - mApi->mLocalProjects.reloadProjectDir(); + mApi->mLocalProjects.reloadDataDir(); // migrate project QSignalSpy spy( mApi, &MerginApi::projectCreated ); @@ -1471,7 +1471,7 @@ void TestMerginApi::deleteLocalProject( MerginApi *api, const QString &projectNa QDir projectDir( project.projectDir ); projectDir.removeRecursively(); - api->localProjectsManager().removeProject( project.projectDir ); + api->localProjectsManager().removeLocalProject( project.projectDir ); } void TestMerginApi::downloadRemoteProject( MerginApi *api, const QString &projectNamespace, const QString &projectName ) From c66292be1376f3e95a8b79c8aae6b54de1e4c266 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Thu, 18 Mar 2021 11:09:04 +0100 Subject: [PATCH 06/22] MerginApi revamp --- app/merginapi.cpp | 50 +++++++++++++++++++++++------------------------ app/merginapi.h | 4 ++-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 19dca75e8..e1993fc7e 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -940,7 +940,7 @@ QNetworkReply *MerginApi::getProjectInfo( const QString &projectFullName, bool w } int sinceVersion = -1; - LocalProjectInfo projectInfo = getLocalProject( projectFullName ); + LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( projectInfo.isValid() ) { // let's also fetch the recent history of diffable files @@ -1065,7 +1065,7 @@ QString MerginApi::extractServerErrorMsg( const QByteArray &data ) } -LocalProjectInfo MerginApi::getLocalProject( const QString &projectFullName ) +LocalProject_future MerginApi::getLocalProject( const QString &projectFullName ) { return mLocalProjects.projectFromMerginName( projectFullName ); } @@ -1241,15 +1241,15 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) // for any local projects we can update the latest server version // TODO: this should now be done inside model so no need to do it here (LocalProjects do not have server version anymore) - for ( MerginProjectListEntry project : mRemoteProjects ) - { - QString fullProjectName = getFullProjectName( project.projectNamespace, project.projectName ); - LocalProjectInfo localProject = mLocalProjects.projectFromMerginName( fullProjectName ); - if ( localProject.isValid() ) - { - mLocalProjects.updateMerginServerVersion( localProject.projectDir, project.version ); - } - } +// for ( MerginProjectListEntry project : mRemoteProjects ) +// { +// QString fullProjectName = getFullProjectName( project.projectNamespace, project.projectName ); +// LocalProjectInfo localProject = mLocalProjects.projectFromMerginName( fullProjectName ); +// if ( localProject.isValid() ) +// { +// mLocalProjects.updateMerginServerVersion( localProject.projectDir, project.version ); +// } +// } InputUtils::log( "list projects", QStringLiteral( "Success - got %1 projects" ).arg( mRemoteProjects.count() ) ); } @@ -1416,7 +1416,7 @@ void MerginApi::finalizeProjectUpdateApplyDiff( const QString &projectFullName, // not good... something went wrong in rebase - we need to save the local changes // let's put them into a conflict file and use the server version - LocalProjectInfo info = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject_future info = mLocalProjects.projectFromMerginName( projectFullName ); QString newDest = InputUtils::findUniquePath( generateConflictFileName( dest, info.localVersion ), false ); if ( !QFile::rename( dest, newDest ) ) { @@ -1470,7 +1470,7 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) { // move local file to conflict file QString origPath = projectDir + "/" + finalizationItem.filePath; - LocalProjectInfo info = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject_future info = mLocalProjects.projectFromMerginName( projectFullName ); QString newPath = InputUtils::findUniquePath( generateConflictFileName( origPath, info.localVersion ), false ); if ( !QFile::rename( origPath, newPath ) ) { @@ -1527,7 +1527,7 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) if ( !QFile::remove( InputUtils::downloadInProgressFilePath( transaction.projectDir ) ) ) InputUtils::log( QStringLiteral( "sync %1" ).arg( projectFullName ), QStringLiteral( "Failed to remove download in progress file for project name %1" ).arg( projectName ) ); - mLocalProjects.addMerginProject( projectDir, projectNamespace, projectName ); + mLocalProjects.addLocalProject( projectDir, projectName, projectNamespace ); } finishProjectSync( projectFullName, true ); @@ -1699,8 +1699,8 @@ void MerginApi::startProjectUpdate( const QString &projectFullName, const QByteA Q_ASSERT( mTransactionalStatus.contains( projectFullName ) ); TransactionStatus &transaction = mTransactionalStatus[projectFullName]; - LocalProjectInfo projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); - if ( projectInfo.isValid() ) + LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + if ( projectInfo.isValid() ) // If project is already downloaded { transaction.projectDir = projectInfo.projectDir; } @@ -1876,20 +1876,20 @@ void MerginApi::uploadInfoReplyFinished() transaction.replyUploadProjectInfo->deleteLater(); transaction.replyUploadProjectInfo = nullptr; - LocalProjectInfo projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); transaction.projectDir = projectInfo.projectDir; Q_ASSERT( !transaction.projectDir.isEmpty() ); MerginProjectMetadata serverProject = MerginProjectMetadata::fromJson( data ); // get the latest server version from our reply (we do not update it in LocalProjectsManager though... I guess we don't need to) - projectInfo.serverVersion = serverProject.version; +// projectInfo.serverVersion = serverProject.version; // now let's figure a key question: are we on the most recent version of the project // if we're about to do upload? because if not, we need to do local update first - if ( projectInfo.isValid() && projectInfo.localVersion != -1 && projectInfo.localVersion < projectInfo.serverVersion ) + if ( projectInfo.isValid() && projectInfo.localVersion != -1 && projectInfo.localVersion < serverProject.version ) { InputUtils::log( "push " + projectFullName, QStringLiteral( "Need pull first: local version %1 | server version %2" ) - .arg( projectInfo.localVersion ).arg( projectInfo.serverVersion ) ); + .arg( projectInfo.localVersion ).arg( serverProject.version ) ); transaction.updateBeforeUpload = true; startProjectUpdate( projectFullName, data ); return; @@ -1898,7 +1898,7 @@ void MerginApi::uploadInfoReplyFinished() QList localFiles = getLocalProjectFiles( transaction.projectDir + "/" ); MerginProjectMetadata oldServerProject = MerginProjectMetadata::fromCachedJson( transaction.projectDir + "/" + sMetadataFile ); - mLocalProjects.updateMerginServerVersion( transaction.projectDir, serverProject.version ); +// mLocalProjects.updateMerginServerVersion( transaction.projectDir, serverProject.version ); transaction.diff = compareProjectFiles( oldServerProject.files, serverProject.files, localFiles, transaction.projectDir ); InputUtils::log( "push " + projectFullName, transaction.diff.dump() ); @@ -2307,7 +2307,7 @@ MerginProjectListEntry MerginApi::parseProjectMetadata( const QJsonObject &proj } if ( proj.contains( QStringLiteral( "error" ) ) ) { - // TODO: handle project error (might be orphaned project) + // TODO: handle project error (user might be logged out / do not have write rights / project is on different server / project is orphaned) proj.value( QStringLiteral( "error" ) ).toInt( 0 ); // error code return project; @@ -2441,8 +2441,9 @@ void MerginApi::finishProjectSync( const QString &projectFullName, bool syncSucc // update info of local projects mLocalProjects.updateLocalVersion( transaction.projectDir, transaction.version ); - // TODO: emit server version - mLocalProjects.updateMerginServerVersion( transaction.projectDir, transaction.version ); +// mLocalProjects.updateMerginServerVersion( transaction.projectDir, transaction.version ); +// TODO: Is it neccessary to update server version at all? +// emit updateServerVersion( transaction.projectDir, transaction.version ); InputUtils::log( "sync " + projectFullName, QStringLiteral( "### Finished ### New project version: %1\n" ).arg( transaction.version ) ); } @@ -2480,7 +2481,6 @@ void MerginApi::finishProjectSync( const QString &projectFullName, bool syncSucc } } } - } bool MerginApi::writeData( const QByteArray &data, const QString &path ) diff --git a/app/merginapi.h b/app/merginapi.h index 6c99c31fc..ad46984e6 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -331,7 +331,7 @@ class MerginApi: public QObject Q_INVOKABLE void detachProjectFromMergin( const QString &projectNamespace, const QString &projectName ); - LocalProjectInfo getLocalProject( const QString &projectFullName ); + LocalProject_future getLocalProject( const QString &projectFullName ); // Test function static const int MERGIN_API_VERSION_MAJOR = 2020; static const int MERGIN_API_VERSION_MINOR = 4; @@ -502,7 +502,7 @@ class MerginApi: public QObject void sendUploadCancelRequest( const QString &projectFullName, const QString &transactionUUID ); bool writeData( const QByteArray &data, const QString &path ); - void createPathIfNotExists( const QString &filePath ); + void createPathIfNotExists( const QString &filePath ); // TODO: make static and move to InputUtils static QByteArray getChecksum( const QString &filePath ); static QSet listFiles( const QString &projectPath ); From 471e0be884578b2625a61303ce6eebc526398128 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Mon, 22 Mar 2021 10:08:56 +0100 Subject: [PATCH 07/22] add local/remote projects --- app/localprojectsmanager.cpp | 40 ++++++++++++++++-------------------- app/localprojectsmanager.h | 8 +++++--- app/merginapi.cpp | 2 +- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index fece7d997..e678556da 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -26,7 +26,7 @@ void LocalProjectsManager::reloadDataDir() // TODO: maybe add function to reload { mProjects.clear(); QStringList entryList = QDir( mDataDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs ); - for ( QString folderName : entryList ) + for ( const QString &folderName : entryList ) { LocalProject_future info; info.projectDir = mDataDir + "/" + folderName; @@ -108,30 +108,15 @@ LocalProject_future LocalProjectsManager::projectFromMerginName( const QString & // Q_ASSERT( false ); // should not happen //} -void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName, const QString &projectNamespace ) +void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName ) { - LocalProject_future project; - project.projectDir = projectDir; - project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.projectError ); - project.projectNamespace = projectNamespace; - project.projectName = projectName; - - mProjects << project; - emit localProjectAdded( projectDir ); + addProject( projectDir, QString(), projectName ); } -//void LocalProjectsManager::addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) -//{ -// addProject( projectDir, projectNamespace, projectName ); -// // version info and status should be updated afterwards -// emit localMerginProjectAdded( projectDir ); -//} - -//void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName ) -//{ -// addProject( projectDir, QString(), projectName ); -// emit localProjectAdded( projectDir ); -//} +void LocalProjectsManager::addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) +{ + addProject( projectDir, projectNamespace, projectName ); +} void LocalProjectsManager::removeLocalProject( const QString &projectDir ) { @@ -258,6 +243,17 @@ QString LocalProjectsManager::findQgisProjectFile( const QString &projectDir, QS return QString(); } +void LocalProjectsManager::addProject(const QString &projectDir, const QString &projectNamespace, const QString &projectName) +{ + LocalProject_future project; + project.projectDir = projectDir; + project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.projectError ); + project.projectName = projectName; + project.projectNamespace = projectNamespace; + + mProjects << project; + emit localProjectAdded( projectDir ); +} static QDateTime _getLastModifiedFileDateTime( const QString &path ) { diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 23f86c36c..c1b08bd98 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -79,15 +79,15 @@ class LocalProjectsManager : public QObject LocalProject_future projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const; //! Adds entry about newly created project - void addLocalProject( const QString &projectDir, const QString &projectName, const QString &projectNamespace = QString() ); + void addLocalProject( const QString &projectDir, const QString &projectName ); // bool hasMerginProject( const QString &projectFullName ) const; // bool hasMerginProject( const QString &projectNamespace, const QString &projectName ) const; // void updateProjectStatus( const QString &projectDir ); - //! Should add an entry about newly created Mergin project -// void addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); + //! Adds entry for downloaded project + void addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); //! Should add an entry about newly created local project // void addLocalProject( const QString &projectDir, const QString &projectName ); @@ -134,6 +134,8 @@ class LocalProjectsManager : public QObject // void updateProjectStatus( LocalProject_future &project ); // TODO: local project manager should not have status private: + void addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); + QString mDataDir; //!< directory with all local projects QList mProjects; }; diff --git a/app/merginapi.cpp b/app/merginapi.cpp index e1993fc7e..dd1225ca9 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -1527,7 +1527,7 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) if ( !QFile::remove( InputUtils::downloadInProgressFilePath( transaction.projectDir ) ) ) InputUtils::log( QStringLiteral( "sync %1" ).arg( projectFullName ), QStringLiteral( "Failed to remove download in progress file for project name %1" ).arg( projectName ) ); - mLocalProjects.addLocalProject( projectDir, projectName, projectNamespace ); + mLocalProjects.addMerginProject( projectDir, projectNamespace, projectName ); } finishProjectSync( projectFullName, true ); From 11a7578fe00dd061701e2eac719d4d322f3c80a4 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Mon, 22 Mar 2021 10:11:54 +0100 Subject: [PATCH 08/22] replace MerginProjectEntry with new structure --- app/localprojectsmanager.h | 4 +- app/merginapi.cpp | 54 ++++++++++----------- app/merginapi.h | 47 +++++++++--------- app/project_future.cpp | 10 ---- app/project_future.h | 28 +++++++++-- app/projectsmodel_future.cpp | 93 +++++++++++++----------------------- app/projectsmodel_future.h | 8 ++-- 7 files changed, 112 insertions(+), 132 deletions(-) diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index c1b08bd98..19e16f206 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -67,7 +67,7 @@ class LocalProjectsManager : public QObject QString dataDir() const { return mDataDir; } - QList projects() const { return mProjects; } + LocalProjectsList projects() const { return mProjects; } //! Loads all projects from mDataDir, removes all old projects void reloadDataDir(); @@ -137,7 +137,7 @@ class LocalProjectsManager : public QObject void addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); QString mDataDir; //!< directory with all local projects - QList mProjects; + LocalProjectsList mProjects; }; diff --git a/app/merginapi.cpp b/app/merginapi.cpp index dd1225ca9..520ef081b 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -1188,10 +1188,10 @@ QString MerginApi::merginUserName() const return userAuth()->username(); } -MerginProjectList MerginApi::projects() -{ - return mRemoteProjects; -} +//MerginProjectList MerginApi::projects() +//{ +// return mRemoteProjects; +//} QList MerginApi::getLocalProjectFiles( const QString &projectPath ) { @@ -1220,6 +1220,7 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) int projectCount = -1; int requestedPage = 1; + MerginProjectsList projectList; if ( r->error() == QNetworkReply::NoError ) { @@ -1232,12 +1233,12 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) if ( doc.isObject() ) { projectCount = doc.object().value( "count" ).toInt(); - mRemoteProjects = parseProjectsFromJson( doc ); - } - else - { - mRemoteProjects.clear(); + projectList = parseProjectsFromJson( doc ); } +// else +// { +// mRemoteProjects.clear(); +// } // for any local projects we can update the latest server version // TODO: this should now be done inside model so no need to do it here (LocalProjects do not have server version anymore) @@ -1251,7 +1252,7 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) // } // } - InputUtils::log( "list projects", QStringLiteral( "Success - got %1 projects" ).arg( mRemoteProjects.count() ) ); + InputUtils::log( "list projects", QStringLiteral( "Success - got %1 projects" ).arg( projectList.count() ) ); } else { @@ -1259,14 +1260,14 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) QString message = QStringLiteral( "Network API error: %1(): %2. %3" ).arg( QStringLiteral( "listProjects" ), r->errorString(), serverMsg ); emit networkErrorOccurred( serverMsg, QStringLiteral( "Mergin API error: listProjects" ) ); InputUtils::log( "list projects", QStringLiteral( "FAILED - %1" ).arg( message ) ); - mRemoteProjects.clear(); +// mRemoteProjects.clear(); emit listProjectsFailed(); } r->deleteLater(); - emit listProjectsFinished( mRemoteProjects, mTransactionalStatus, projectCount, requestedPage, requestId ); + emit listProjectsFinished( projectList, mTransactionalStatus, projectCount, requestedPage, requestId ); } void MerginApi::listProjectsByNameReplyFinished( QString requestId ) @@ -1275,20 +1276,13 @@ void MerginApi::listProjectsByNameReplyFinished( QString requestId ) Q_ASSERT( r ); /* TODO: Detect orphaned project? Project that was considered Mergin but did not get info back */ - MerginProjectList projectList; + MerginProjectsList projectList; if ( r->error() == QNetworkReply::NoError ) { QByteArray data = r->readAll(); QJsonDocument json = QJsonDocument::fromJson( data ); - projectList = parseProjectsFromJson( json ); - - for ( MerginProjectListEntry project : qAsConst( projectList ) ) - { - qDebug() << "Project: " << project.projectName; - } - InputUtils::log( "list projects by name", QStringLiteral( "Success - got %1 projects" ).arg( projectList.count() ) ); } else @@ -2297,19 +2291,19 @@ ProjectDiff MerginApi::compareProjectFiles( const QList &oldServerFi return diff; } -MerginProjectListEntry MerginApi::parseProjectMetadata( const QJsonObject &proj ) +MerginProject_future MerginApi::parseProjectMetadata( const QJsonObject &proj ) { - MerginProjectListEntry project; + MerginProject_future project; if ( proj.isEmpty() ) { return project; } + if ( proj.contains( QStringLiteral( "error" ) ) ) { - // TODO: handle project error (user might be logged out / do not have write rights / project is on different server / project is orphaned) - - proj.value( QStringLiteral( "error" ) ).toInt( 0 ); // error code + // handle project error (user might be logged out / do not have write rights / project is on different server / project is orphaned) + project.remoteError = proj.value( QStringLiteral( "error" ) ).toInt( 0 ); // error code return project; } @@ -2319,12 +2313,12 @@ MerginProjectListEntry MerginApi::parseProjectMetadata( const QJsonObject &proj QString versionStr = proj.value( QStringLiteral( "version" ) ).toString(); if ( versionStr.isEmpty() ) { - project.version = 0; + project.serverVersion = 0; } else if ( versionStr.startsWith( "v" ) ) // cut off 'v' part from v123 { versionStr = versionStr.mid( 1 ); - project.version = versionStr.toInt(); + project.serverVersion = versionStr.toInt(); } QDateTime updated = QDateTime::fromString( proj.value( QStringLiteral( "updated" ) ).toString(), Qt::ISODateWithMs ).toUTC(); @@ -2340,13 +2334,13 @@ MerginProjectListEntry MerginApi::parseProjectMetadata( const QJsonObject &proj } -MerginProjectList MerginApi::parseProjectsFromJson( const QJsonDocument &doc ) +MerginProjectsList MerginApi::parseProjectsFromJson( const QJsonDocument &doc ) { if ( !doc.isObject() ) - return MerginProjectList(); + return MerginProjectsList(); QJsonObject object = doc.object(); - MerginProjectList result; + MerginProjectsList result; if ( object.contains( "projects" ) && object.value( "projects" ).isArray() ) // listProjects API { diff --git a/app/merginapi.h b/app/merginapi.h index ad46984e6..620e3cfeb 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -27,6 +27,7 @@ #include "merginsubscriptionstatus.h" #include "merginprojectmetadata.h" #include "localprojectsmanager.h" +#include "project_future.h" class MerginUserAuth; class MerginUserInfo; @@ -166,27 +167,27 @@ struct TransactionStatus }; -struct MerginProjectListEntry // TODO: replace with RemoteProject from Project_future.h -{ - bool isValid() const { return !projectName.isEmpty() && !projectNamespace.isEmpty(); } +//struct MerginProjectListEntry // TODO: replace with RemoteProject from Project_future.h +//{ +// bool isValid() const { return !projectName.isEmpty() && !projectNamespace.isEmpty(); } - QString projectName; - QString projectNamespace; - int version = -1; - QDateTime serverUpdated; // available latest version of project files on server +// QString projectName; +// QString projectNamespace; +// int version = -1; +// QDateTime serverUpdated; // available latest version of project files on server - bool operator ==( const MerginProjectListEntry &other ) - { - return ( this->projectName == other.projectName ) && ( this->projectNamespace == other.projectNamespace ); - } +// bool operator ==( const MerginProjectListEntry &other ) +// { +// return ( this->projectName == other.projectName ) && ( this->projectNamespace == other.projectNamespace ); +// } - bool operator !=( const MerginProjectListEntry &other ) - { - return !( *this == other ); - } -}; +// bool operator !=( const MerginProjectListEntry &other ) +// { +// return !( *this == other ); +// } +//}; -typedef QList MerginProjectList; // TODO: replace with RemoteProject from Project_future.h +//typedef QList MerginProjectList; // TODO: replace with RemoteProject from Project_future.h typedef QHash Transactions; @@ -385,7 +386,7 @@ class MerginApi: public QObject static ProjectDiff compareProjectFiles( const QList &oldServerFiles, const QList &newServerFiles, const QList &localFiles, const QString &projectDir ); //! Returns the most recent list of projects fetched from the server - MerginProjectList projects(); +// MerginProjectList projects(); static QList getLocalProjectFiles( const QString &projectPath ); @@ -413,9 +414,9 @@ class MerginApi: public QObject signals: void apiSupportsSubscriptionsChanged(); - void listProjectsFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, int projectCount, int page, QString requestId ); + void listProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectCount, int page, QString requestId ); void listProjectsFailed(); - void listProjectsByNameFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, QString requestId ); + void listProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ); void syncProjectFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); /** * Emitted when sync starts/finishes or the progress changes - useful to give a clue in the GUI about the status. @@ -470,8 +471,8 @@ class MerginApi: public QObject void pingMerginReplyFinished(); private: - MerginProjectListEntry parseProjectMetadata( const QJsonObject &project ); - MerginProjectList parseProjectsFromJson( const QJsonDocument &object ); + MerginProject_future parseProjectMetadata( const QJsonObject &project ); + MerginProjectsList parseProjectsFromJson( const QJsonDocument &object ); static QStringList generateChunkIdsForSize( qint64 fileSize ); QJsonArray prepareUploadChangesJSON( const QList &files ); static QString getApiKey( const QString &serverName ); @@ -568,7 +569,7 @@ class MerginApi: public QObject QNetworkAccessManager mManager; QString mApiRoot; LocalProjectsManager &mLocalProjects; - MerginProjectList mRemoteProjects; // TODO: remove (no use, only in tests - TBD) +// MerginProjectList mRemoteProjects; // TODO: remove (no use, only in tests - TBD) QString mDataDir; // dir with all projects MerginUserInfo *mUserInfo; //owned by this (qml grouped-properties) diff --git a/app/project_future.cpp b/app/project_future.cpp index 6734b3553..c4dccbecf 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -12,16 +12,6 @@ #include -void LocalProject_future::copyValues( const LocalProject_future &other ) -{ - projectName = other.projectName; - projectNamespace = other.projectNamespace; - projectDir = other.projectDir; - projectError = other.projectError; - qgisProjectFilePath = other.qgisProjectFilePath; - localVersion = other.localVersion; -} - QString MerginProject_future::id() const { return MerginApi::getFullProjectName( projectNamespace, projectName ); diff --git a/app/project_future.h b/app/project_future.h index f76c1f879..43fc74e3c 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -21,7 +21,8 @@ enum ProjectStatus_future _UpToDate, //!< both server and local copy are in sync with no extra modifications _OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) _Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) - // TODO: add orphaned state + + // Maybe orphaned state in future }; Q_ENUMS( ProjectStatus_future ) @@ -42,9 +43,17 @@ struct LocalProject_future int localVersion = -1; - void copyValues( const LocalProject_future &other ); - bool isValid() { return !projectDir.isEmpty(); } + + bool operator ==( const LocalProject_future &other ) + { + return ( this->id() == other.id() ) && ( this->projectDir == other.projectDir ); + } + + bool operator !=( const LocalProject_future &other ) + { + return !( *this == other ); + } }; struct MerginProject_future @@ -67,6 +76,16 @@ struct MerginProject_future // Maybe better use enum or int for error code QString remoteError; // Error leading to project not being able to sync + + bool operator ==( const MerginProject_future &other ) + { + return ( this->id() == other.id() ); + } + + bool operator !=( const MerginProject_future &other ) + { + return !( *this == other ); + } }; struct Project_future @@ -99,4 +118,7 @@ struct Project_future } }; +typedef QList MerginProjectsList; +typedef QList LocalProjectsList; + #endif // PROJECT_FUTURE_H diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 52e732be5..3e1b04ee9 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -26,9 +26,6 @@ ProjectsModel_future::ProjectsModel_future( QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel_future::onProjectSyncFinished ); QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel_future::onProjectDetachedFromMergin ); - // TODO: connect to signals from LocalProjectsManager -// QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectAdded ) - if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { QObject::connect( mBackend, &MerginApi::listProjectsByNameFinished, this, &ProjectsModel_future::onListProjectsByNameFinished ); @@ -42,7 +39,8 @@ ProjectsModel_future::ProjectsModel_future( // Implement RecentProjectsModel type } -// QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); + QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); + QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectRemoved, this, &ProjectsModel_future::onProjectRemoved ); QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); } @@ -99,52 +97,40 @@ void ProjectsModel_future::listProjectsByName() mLastRequestId = mBackend->listProjectsByName( projectNames() ); } -void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProjects, Transactions pendingProjects ) +void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects ) { - QList localProjects = mLocalProjectsManager.projects(); + LocalProjectsList localProjects = mLocalProjectsManager.projects(); qDebug() << "PMR: mergeProjects(): # of local projects = " << localProjects.size() << " # of mergin projects = " << merginProjects.size(); + + // TODO: clear projects only if this is local project or paginated page == 1 mProjects.clear(); if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { // Keep all local projects and ignore all not downloaded remote projects - for ( auto &localProject : localProjects ) + for ( const auto &localProject : localProjects ) { std::shared_ptr project = std::shared_ptr( new Project_future() ); - std::unique_ptr local = std::unique_ptr( new LocalProject_future() ); - - local->projectName = localProject.projectName; - local->projectNamespace = localProject.projectNamespace; - local->projectDir = localProject.projectDir; - local->projectError = localProject.qgisProjectError; - local->qgisProjectFilePath = localProject.qgisProjectFilePath; - // TODO: later copy data by copy constructor + std::unique_ptr local = std::unique_ptr( new LocalProject_future( localProject ) ); + project->local = std::move( local ); - MerginProjectListEntry remoteEntry; + MerginProject_future remoteEntry; remoteEntry.projectName = project->local->projectName; remoteEntry.projectNamespace = project->local->projectNamespace; if ( merginProjects.contains( remoteEntry ) ) { int i = merginProjects.indexOf( remoteEntry ); - std::unique_ptr mergin = std::unique_ptr( new MerginProject_future() ); - mergin->projectName = merginProjects[i].projectName; - mergin->projectNamespace = merginProjects[i].projectNamespace; - mergin->serverVersion = merginProjects[i].version; - mergin->serverUpdated = merginProjects[i].serverUpdated; - // TODO: later copy data by copy constructor - // TODO: check for project errors (from ListByName API ~> not authorized / no rights / no version) - - if ( pendingProjects.contains( mergin->id() ) ) + project->mergin = std::unique_ptr( new MerginProject_future( merginProjects[i] ) ); + + if ( pendingProjects.contains( project->mergin->id() ) ) { - TransactionStatus projectTransaction = pendingProjects.value( mergin->id() ); - mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; - mergin->pending = true; + TransactionStatus projectTransaction = pendingProjects.value( project->mergin->id() ); + project->mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; + project->mergin->pending = true; } - - project->mergin = std::move( mergin ); } mProjects << project; @@ -156,34 +142,24 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject for ( const auto &remoteEntry : merginProjects ) { std::shared_ptr project = std::shared_ptr( new Project_future() ); - std::unique_ptr mergin = std::unique_ptr( new MerginProject_future() ); + project->mergin = std::unique_ptr( new MerginProject_future( remoteEntry ) ); - mergin->projectName = remoteEntry.projectName; - mergin->projectNamespace = remoteEntry.projectNamespace; - // TODO: later copy data by copy constructor - - if ( pendingProjects.contains( mergin->id() ) ) + if ( pendingProjects.contains( project->mergin->id() ) ) { - TransactionStatus projectTransaction = pendingProjects.value( mergin->id() ); - mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; - mergin->pending = true; + TransactionStatus projectTransaction = pendingProjects.value( project->mergin->id() ); + project->mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; + project->mergin->pending = true; } - project->mergin = std::move( mergin ); - // find downloaded projects - LocalProjectInfo localProject; + LocalProject_future localProject; localProject.projectName = project->mergin->projectName; localProject.projectNamespace = project->mergin->projectNamespace; if ( localProjects.contains( localProject ) ) { int ix = localProjects.indexOf( localProject ); - project->local = std::unique_ptr( new LocalProject_future() ); - - project->local->projectName = localProjects[ix].projectName; - project->local->projectNamespace = localProjects[ix].projectNamespace; - // TODO: later copy data by copy constructor + project->local = std::unique_ptr( new LocalProject_future( localProjects[ix] ) ); } mProjects << project; @@ -191,7 +167,7 @@ void ProjectsModel_future::mergeProjects( const MerginProjectList &merginProject } } -void ProjectsModel_future::onListProjectsFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, int projectCount, int page, QString requestId ) +void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) { qDebug() << "PMR: onListProjectsFinished(): received response with requestId = " << requestId; if ( mLastRequestId != requestId ) @@ -200,10 +176,11 @@ void ProjectsModel_future::onListProjectsFinished( const MerginProjectList &merg return; } - Q_UNUSED( projectCount ); + // TODO: save projectsCount and paginatedPage to model so that QML can respond accordingly -> show "fetch more" + Q_UNUSED( projectsCount ); Q_UNUSED( page ); - qDebug() << "PMR: onListProjectsFinished(): project count = " << projectCount << " but mergin projects emited: " << merginProjects.size(); + qDebug() << "PMR: onListProjectsFinished(): project count = " << projectsCount << " but mergin projects emited: " << merginProjects.size(); beginResetModel(); mergeProjects( merginProjects, pendingProjects ); @@ -211,7 +188,7 @@ void ProjectsModel_future::onListProjectsFinished( const MerginProjectList &merg endResetModel(); } -void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, QString requestId ) +void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ) { qDebug() << "PMR: onListProjectsByNameFinished(): received response with requestId = " << requestId; if ( mLastRequestId != requestId ) @@ -220,10 +197,6 @@ void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectList return; } - Q_UNUSED( merginProjects ); - Q_UNUSED( pendingProjects ); - Q_UNUSED( requestId ); - beginResetModel(); mergeProjects( merginProjects, pendingProjects ); printProjects(); @@ -248,7 +221,7 @@ void ProjectsModel_future::syncProject( QString projectNamespace, QString projec if ( project->mergin->pending ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is already "; + qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is already syncing"; return; } @@ -338,7 +311,7 @@ void ProjectsModel_future::onProjectAdded( const QString &projectDir ) qDebug() << "PMR: Added project" << projectDir; } -void ProjectsModel_future::onProjectDeleted( const QString &projectFullName ) +void ProjectsModel_future::onProjectRemoved( const QString &projectFullName ) { Q_UNUSED( projectFullName ) qDebug() << "PMR: Deleted project" << projectFullName; @@ -394,15 +367,15 @@ void ProjectsModel_future::printProjects() const // TODO: Helper function, remov } } -QStringList ProjectsModel_future::projectNames() const // TODO: use local projects instead +QStringList ProjectsModel_future::projectNames() const { QStringList projectNames; - QList projects = mLocalProjectsManager.projects(); + LocalProjectsList projects = mLocalProjectsManager.projects(); for ( const auto &proj : projects ) { if ( !proj.projectName.isEmpty() && !proj.projectNamespace.isEmpty() ) - projectNames << MerginApi::getFullProjectName( proj.projectNamespace, proj.projectName ); + projectNames << proj.id(); } return projectNames; diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 4e8dc89fa..915025af1 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -75,16 +75,16 @@ class ProjectsModel_future : public QAbstractListModel Q_INVOKABLE void stopProjectSync( QString projectNamespace, QString projectName ); //! Method merging local and remote projects based on the model type - void mergeProjects( const MerginProjectList &merginProjects, Transactions pendingProjects ); + void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects ); public slots: - void onListProjectsFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, int projectCount, int page, QString requestId ); - void onListProjectsByNameFinished( const MerginProjectList &merginProjects, Transactions pendingProjects, QString requestId ); + void onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ); + void onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ); void onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); void onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ); void onProjectAdded( const QString &projectDir ); - void onProjectDeleted( const QString &projectFullName ); + void onProjectRemoved( const QString &projectFullName ); void onProjectDataChanged( const QString &projectDir ); void onProjectDetachedFromMergin( const QString &projectFullName ); From 8217994d85c66d06039cd65baa78b0056820aa8a Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Mon, 22 Mar 2021 15:18:42 +0100 Subject: [PATCH 09/22] implement model functions and slots --- app/localprojectsmanager.cpp | 10 +-- app/localprojectsmanager.h | 7 ++- app/project_future.cpp | 12 ++-- app/project_future.h | 24 +++++++- app/projectsmodel_future.cpp | 115 +++++++++++++++++++++++++++++------ app/projectsmodel_future.h | 8 +-- 6 files changed, 140 insertions(+), 36 deletions(-) diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index e678556da..d07fd58e2 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -46,6 +46,7 @@ void LocalProjectsManager::reloadDataDir() // TODO: maybe add function to reload mProjects << info; } + emit dataDirReloaded(); qDebug() << "LocalProjectsManager: found" << mProjects.size() << "projects"; } @@ -124,10 +125,11 @@ void LocalProjectsManager::removeLocalProject( const QString &projectDir ) { if ( mProjects[i].projectDir == projectDir ) { + emit aboutToRemoveLocalProject( mProjects[i] ); + InputUtils::removeDir( mProjects[i].projectDir ); mProjects.removeAt( i ); - emit localProjectRemoved( projectDir ); return; } } @@ -157,7 +159,7 @@ void LocalProjectsManager::updateLocalVersion( const QString &projectDir, int ve { mProjects[i].localVersion = version; - emit localProjectDataChanged( mProjects[i].projectDir ); + emit localProjectDataChanged( mProjects[i] ); return; } } @@ -199,7 +201,7 @@ void LocalProjectsManager::updateNamespace( const QString &projectDir, const QSt { mProjects[i].projectNamespace = projectNamespace; - emit localProjectDataChanged( mProjects[i].projectDir ); + emit localProjectDataChanged( mProjects[i] ); return; } } @@ -252,7 +254,7 @@ void LocalProjectsManager::addProject(const QString &projectDir, const QString & project.projectNamespace = projectNamespace; mProjects << project; - emit localProjectAdded( projectDir ); + emit localProjectAdded( project ); } static QDateTime _getLastModifiedFileDateTime( const QString &path ) diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 19e16f206..8fbd44760 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -126,9 +126,10 @@ class LocalProjectsManager : public QObject signals: void projectMetadataChanged( const QString &projectDir ); void localMerginProjectAdded( const QString &projectDir ); - void localProjectAdded( const QString &projectDir ); - void localProjectRemoved( const QString &projectDir ); - void localProjectDataChanged( const QString &projectDir ); + void localProjectAdded( const LocalProject_future &project ); + void aboutToRemoveLocalProject( const LocalProject_future project ); + void localProjectDataChanged( const LocalProject_future &project ); + void dataDirReloaded(); // private: // void updateProjectStatus( LocalProject_future &project ); // TODO: local project manager should not have status diff --git a/app/project_future.cpp b/app/project_future.cpp index c4dccbecf..c278f0272 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -10,13 +10,6 @@ #include "project_future.h" #include "merginapi.h" -#include - -QString MerginProject_future::id() const -{ - return MerginApi::getFullProjectName( projectNamespace, projectName ); -} - QString LocalProject_future::id() const { if ( !projectName.isEmpty() && !projectNamespace.isEmpty() ) @@ -25,3 +18,8 @@ QString LocalProject_future::id() const QDir dir( projectDir ); return dir.dirName(); } + +QString MerginProject_future::id() const +{ + return MerginApi::getFullProjectName( projectNamespace, projectName ); +} diff --git a/app/project_future.h b/app/project_future.h index 43fc74e3c..1b4946ac9 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -11,9 +11,10 @@ #define PROJECT_FUTURE_H #include -#include #include +#include #include +#include enum ProjectStatus_future { @@ -99,6 +100,27 @@ struct Project_future bool isMergin() const { return mergin != nullptr; } bool isLocal() const { return local != nullptr; } + QString projectName() + { + if ( isLocal() ) return local->projectName; + else if ( isMergin() ) return mergin->projectName; + return QString(); + } + + QString projectNamespace() + { + if ( isLocal() ) return local->projectNamespace; + else if ( isMergin() ) return mergin->projectNamespace; + return QString(); + } + + QString projectId() + { + if ( isLocal() ) return local->id(); + else if ( isMergin() ) return mergin->id(); + return QString(); + } + bool operator ==( const Project_future &other ) { if ( this->isLocal() && other.isLocal() ) diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 3e1b04ee9..c4cc7a296 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -25,6 +25,8 @@ ProjectsModel_future::ProjectsModel_future( QObject::connect( mBackend, &MerginApi::syncProjectStatusChanged, this, &ProjectsModel_future::onProjectSyncProgressChanged ); QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel_future::onProjectSyncFinished ); QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel_future::onProjectDetachedFromMergin ); + QObject::connect( mBackend, &MerginApi::projectAttachedToMergin, this, &ProjectsModel_future::onProjectAttachedToMergin ); + if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { @@ -40,7 +42,7 @@ ProjectsModel_future::ProjectsModel_future( } QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); - QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectRemoved, this, &ProjectsModel_future::onProjectRemoved ); + QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel_future::onAboutToRemoveProject ); QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); } @@ -49,10 +51,29 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const if ( !index.isValid() ) return QVariant(); - std::shared_ptr project = mProjects.at( index.row() ); + std::shared_ptr project = mProjects.at( index.row() ); switch ( role ) { - default: return QVariant("TestData"); + case ProjectName: return QVariant( project->projectName() ); + case ProjectNamespace: return QVariant( project->projectNamespace() ); + case ProjectFullName: return QVariant( project->projectId() ); + case ProjectIsLocal: return QVariant( project->isLocal() ); + case ProjectIsMergin: return QVariant( project->isMergin() ); + case ProjectDescription: { + if ( project->isLocal() && !project->local->projectError.isEmpty() ) + return project->local->projectError; + + QFileInfo fi( project->local->projectDir ); + return fi.lastModified(); // TODO: Better project info + } + default: { + if ( !project->isMergin() ) return QVariant(); + + // Roles only for projects that has mergin part + if ( role == ProjectPending ) return QVariant( project->mergin->pending ); + else if ( role == ProjectSyncProgress ) return QVariant( project->mergin->progress ); + return QVariant(); + } } } @@ -66,7 +87,14 @@ QModelIndex ProjectsModel_future::index( int row, int col, const QModelIndex &pa QHash ProjectsModel_future::roleNames() const { QHash roles; - roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); + roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); + roles[Roles::ProjectNamespace] = QStringLiteral( "ProjectNamespace" ).toLatin1(); + roles[Roles::ProjectFullName] = QStringLiteral( "ProjectFullName" ).toLatin1(); + roles[Roles::ProjectIsLocal] = QStringLiteral( "ProjectIsLocal" ).toLatin1(); + roles[Roles::ProjectIsMergin] = QStringLiteral( "ProjectIsMergin" ).toLatin1(); + roles[Roles::ProjectDescription] = QStringLiteral( "ProjectDescription" ).toLatin1(); + roles[Roles::ProjectPending] = QStringLiteral( "ProjectPending" ).toLatin1(); + roles[Roles::ProjectSyncProgress] = QStringLiteral( "ProjectSyncProgress" ).toLatin1(); return roles; } @@ -305,33 +333,86 @@ void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectF qDebug() << "PMR: Project " << projectFullName << " changed sync progress to " << progress; } -void ProjectsModel_future::onProjectAdded( const QString &projectDir ) +void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) { - Q_UNUSED( projectDir ) - qDebug() << "PMR: Added project" << projectDir; + // Check if such project is already in project list + std::shared_ptr proj = projectFromId( project.id() ); + if ( proj ) + { + // add local information ~ project downloaded + proj->local = std::unique_ptr( new LocalProject_future( project ) ); + + QModelIndex ix = index( mProjects.indexOf( proj ) ); + emit dataChanged( ix, ix ); + } + else if ( mModelType == LocalProjectsModel ) + { + // add project to project list ~ project created + std::shared_ptr newProject = std::shared_ptr( new Project_future() ); + newProject->local = std::unique_ptr( new LocalProject_future( project ) ); + + int insertIndex = mProjects.size() == 0 ? 0 : mProjects.size() - 1; + beginInsertRows( QModelIndex(), insertIndex, insertIndex + 1 ); + mProjects << newProject; + endInsertRows(); + } + + qDebug() << "PMR: Added project" << project.id(); } -void ProjectsModel_future::onProjectRemoved( const QString &projectFullName ) +void ProjectsModel_future::onAboutToRemoveProject( const LocalProject_future project ) { - Q_UNUSED( projectFullName ) - qDebug() << "PMR: Deleted project" << projectFullName; + std::shared_ptr proj = projectFromId( project.id() ); + + if ( proj ) + { + int removeIndex = mProjects.indexOf( proj ); + + beginRemoveRows( QModelIndex(), removeIndex, removeIndex ); + mProjects.removeOne( proj ); + endRemoveRows(); + + qDebug() << "PMR: Deleted project" << project.id(); + } } -void ProjectsModel_future::onProjectDataChanged( const QString &projectDir ) +void ProjectsModel_future::onProjectDataChanged( const LocalProject_future &project ) { - Q_UNUSED( projectDir ) - qDebug() << "PMR: Data changed in project" << projectDir; + std::shared_ptr proj = projectFromId( project.id() ); + + if ( proj ) + { + proj->local = std::unique_ptr( new LocalProject_future( project ) ); + QModelIndex editIndex = index( mProjects.indexOf( proj ) ); + + emit dataChanged( editIndex, editIndex ); + } + qDebug() << "PMR: Data changed in project" << project.id(); } void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFullName ) { - Q_UNUSED( projectFullName ) - qDebug() << "PMR: Project detached from mergin " << projectFullName; + std::shared_ptr proj = projectFromId( projectFullName ); + + if ( proj ) + { + proj->mergin = nullptr; + QModelIndex editIndex = index( mProjects.indexOf( proj ) ); + + emit dataChanged( editIndex, editIndex ); + + // This project should also be removed from project list for remote project model types, + // however, currently one needs to click on "My projects/Shared/Explore" and that sends + // another listProjects request. In new list this project will not be shown. + } } -void ProjectsModel_future::onProjectAttachedToMergin(const QString &projectFullName) +void ProjectsModel_future::onProjectAttachedToMergin( const QString &projectFullName ) { - Q_UNUSED( projectFullName ) + // To ensure project will be in sync with server, send listProjectByName request. + // In theory we could send that request only for this one project. + listProjectsByName(); + qDebug() << "PMR: Project attached to mergin " << projectFullName; } diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 915025af1..d31bed8fa 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -49,7 +49,7 @@ class ProjectsModel_future : public QAbstractListModel ProjectIsMergin, ProjectIsLocal, ProjectStatus, - ProjectProgress + ProjectSyncProgress }; Q_ENUMS( Roles ) @@ -83,9 +83,9 @@ class ProjectsModel_future : public QAbstractListModel void onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); void onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ); - void onProjectAdded( const QString &projectDir ); - void onProjectRemoved( const QString &projectFullName ); - void onProjectDataChanged( const QString &projectDir ); + void onProjectAdded( const LocalProject_future &project ); + void onAboutToRemoveProject( const LocalProject_future project ); + void onProjectDataChanged( const LocalProject_future &project ); void onProjectDetachedFromMergin( const QString &projectFullName ); void onProjectAttachedToMergin( const QString &projectFullName ); From 9006bcf109d7ac8269b758f25b92d8ea72a250f9 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Mon, 22 Mar 2021 16:13:46 +0100 Subject: [PATCH 10/22] add pagination --- app/projectsmodel_future.cpp | 43 ++++++++++++++++++++++++++++-------- app/projectsmodel_future.h | 25 +++++++++++++++------ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index c4cc7a296..02fdbd6f1 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -31,6 +31,7 @@ ProjectsModel_future::ProjectsModel_future( if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { QObject::connect( mBackend, &MerginApi::listProjectsByNameFinished, this, &ProjectsModel_future::onListProjectsByNameFinished ); + loadLocalProjects(); // at app start, we need to fill model with local projects } else if ( mModelType != ProjectModelTypes::RecentProjectsModel ) { @@ -44,6 +45,7 @@ ProjectsModel_future::ProjectsModel_future( QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel_future::onAboutToRemoveProject ); QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); + QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::dataDirReloaded, this, &ProjectsModel_future::loadLocalProjects ); } QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const @@ -103,15 +105,16 @@ int ProjectsModel_future::rowCount( const QModelIndex & ) const return mProjects.count(); } -void ProjectsModel_future::listProjects() +void ProjectsModel_future::listProjects( int page, QString searchExpression ) { if ( mModelType == LocalProjectsModel ) { InputUtils::log( "Input", "Can not call listProjects API on LocalProjectsModel" ); + // maybe call listProjectsByName(); return; } - mLastRequestId = mBackend->listProjects( "", modelTypeToFlag(), "", 1 ); //TODO: pagination + mLastRequestId = mBackend->listProjects( "", "" /*modelTypeToFlag()*/, searchExpression, page ); } void ProjectsModel_future::listProjectsByName() @@ -125,14 +128,14 @@ void ProjectsModel_future::listProjectsByName() mLastRequestId = mBackend->listProjectsByName( projectNames() ); } -void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects ) +void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious ) { LocalProjectsList localProjects = mLocalProjectsManager.projects(); qDebug() << "PMR: mergeProjects(): # of local projects = " << localProjects.size() << " # of mergin projects = " << merginProjects.size(); - // TODO: clear projects only if this is local project or paginated page == 1 - mProjects.clear(); + if ( !keepPrevious ) + mProjects.clear(); if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { @@ -195,6 +198,11 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec } } +int ProjectsModel_future::serverProjectsCount() const +{ + return mServerProjectsCount; +} + void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) { qDebug() << "PMR: onListProjectsFinished(): received response with requestId = " << requestId; @@ -204,14 +212,12 @@ void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &mer return; } - // TODO: save projectsCount and paginatedPage to model so that QML can respond accordingly -> show "fetch more" - Q_UNUSED( projectsCount ); - Q_UNUSED( page ); + setServerProjectsCount( projectsCount ); qDebug() << "PMR: onListProjectsFinished(): project count = " << projectsCount << " but mergin projects emited: " << merginProjects.size(); beginResetModel(); - mergeProjects( merginProjects, pendingProjects ); + mergeProjects( merginProjects, pendingProjects, page != 1 ); // throw projects only if paginating first page printProjects(); endResetModel(); } @@ -416,6 +422,15 @@ void ProjectsModel_future::onProjectAttachedToMergin( const QString &projectFull qDebug() << "PMR: Project attached to mergin " << projectFullName; } +void ProjectsModel_future::setServerProjectsCount( int serverProjectsCount ) +{ + if ( mServerProjectsCount == serverProjectsCount ) + return; + + mServerProjectsCount = serverProjectsCount; + emit serverProjectsCountChanged( mServerProjectsCount ); +} + QString ProjectsModel_future::modelTypeToFlag() const { switch ( mModelType ) @@ -462,6 +477,16 @@ QStringList ProjectsModel_future::projectNames() const return projectNames; } +void ProjectsModel_future::loadLocalProjects() +{ + if ( mModelType == LocalProjectsModel ) + { + beginResetModel(); + mergeProjects( MerginProjectsList(), Transactions() ); // Fills model with local projects + endResetModel(); + } +} + bool ProjectsModel_future::containsProject( QString projectId ) const { std::shared_ptr proj = projectFromId( projectId ); diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index d31bed8fa..161e67692 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -56,6 +56,8 @@ class ProjectsModel_future : public QAbstractListModel ProjectsModel_future( MerginApi *merginApi, ProjectModelTypes modelType, LocalProjectsManager &localProjectsManager, QObject *parent = nullptr ); ~ProjectsModel_future() override {}; + Q_PROPERTY( int serverProjectsCount READ serverProjectsCount WRITE setServerProjectsCount NOTIFY serverProjectsCountChanged ); + // Needed methods from QAbstractListModel Q_INVOKABLE QVariant data( const QModelIndex &index, int role ) const override; Q_INVOKABLE QModelIndex index( int row, int column = 0, const QModelIndex &parent = QModelIndex() ) const override; @@ -63,7 +65,7 @@ class ProjectsModel_future : public QAbstractListModel int rowCount( const QModelIndex &parent = QModelIndex() ) const override; //! Called to list projects, either fetch more or get first - Q_INVOKABLE void listProjects(); + Q_INVOKABLE void listProjects( int page = 1, const QString searchExpression = QString() ); //! Called to list projects, either fetch more or get first Q_INVOKABLE void listProjectsByName(); @@ -75,26 +77,35 @@ class ProjectsModel_future : public QAbstractListModel Q_INVOKABLE void stopProjectSync( QString projectNamespace, QString projectName ); //! Method merging local and remote projects based on the model type - void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects ); + void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious = false ); + + int serverProjectsCount() const; - public slots: +public slots: + // MerginAPI - backend signals void onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ); void onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ); void onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully = true ); void onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ); + void onProjectDetachedFromMergin( const QString &projectFullName ); + void onProjectAttachedToMergin( const QString &projectFullName ); + // LocalProjectsManager signals void onProjectAdded( const LocalProject_future &project ); void onAboutToRemoveProject( const LocalProject_future project ); void onProjectDataChanged( const LocalProject_future &project ); - void onProjectDetachedFromMergin( const QString &projectFullName ); - void onProjectAttachedToMergin( const QString &projectFullName ); + void setServerProjectsCount( int serverProjectsCount ); + +signals: + void serverProjectsCountChanged( int serverProjectsCount ); - private: +private: QString modelTypeToFlag() const; void printProjects() const; QStringList projectNames() const; + void loadLocalProjects(); bool containsProject( QString projectId ) const; std::shared_ptr projectFromId( QString projectId ) const; @@ -106,7 +117,7 @@ class ProjectsModel_future : public QAbstractListModel ProjectModelTypes mModelType; //! For pagination - int mPopulatedPage = -1; // -> on the fly in QML:: QML should pass this to model + int mServerProjectsCount = -1; //! For processing only my requests QString mLastRequestId; From f6c5eea719bd4bf8b25aa5018b587db47617c431 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Mon, 22 Mar 2021 17:11:09 +0100 Subject: [PATCH 11/22] project status and remote error --- app/localprojectsmanager.cpp | 14 +++++++------- app/localprojectsmanager.h | 2 +- app/projectsmodel_future.cpp | 7 +++++-- app/projectsmodel_future.h | 4 +++- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index d07fd58e2..f8b9ad5aa 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -290,13 +290,13 @@ static int _getProjectFilesCount( const QString &path ) return count; } -ProjectStatus_future LocalProjectsManager::currentProjectStatus( const Project_future &project ) +ProjectStatus_future LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) { - if ( !project.isMergin() || !project.isLocal() ) // This is not a Mergin project or not downloaded project + if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project return ProjectStatus_future::_NoVersion; // There was no sync yet - if ( project.local->localVersion < 0 ) + if ( project->local->localVersion < 0 ) { return ProjectStatus_future::_NoVersion; } @@ -306,18 +306,18 @@ ProjectStatus_future LocalProjectsManager::currentProjectStatus( const Project_f // // Something has locally changed after last sync with server - QString metadataFilePath = project.local->projectDir + "/" + MerginApi::sMetadataFile; - QDateTime lastModified = _getLastModifiedFileDateTime( project.local->projectDir ); + QString metadataFilePath = project->local->projectDir + "/" + MerginApi::sMetadataFile; + QDateTime lastModified = _getLastModifiedFileDateTime( project->local->projectDir ); QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); - int filesCount = _getProjectFilesCount( project.local->projectDir ); + int filesCount = _getProjectFilesCount( project->local->projectDir ); if ( lastSync < lastModified || meta.files.count() != filesCount ) { return ProjectStatus_future::_Modified; } // Version is lower than latest one, last sync also before updated - if ( project.local->localVersion < project.mergin->serverVersion ) + if ( project->local->localVersion < project->mergin->serverVersion ) { return ProjectStatus_future::_OutOfDate; } diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 8fbd44760..4a601aae5 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -121,7 +121,7 @@ class LocalProjectsManager : public QObject QString findQgisProjectFile( const QString &projectDir, QString &err ); - static ProjectStatus_future currentProjectStatus( const Project_future &project ); // TODO: maybe move somewhere else? + static ProjectStatus_future currentProjectStatus( const std::shared_ptr project ); // TODO: maybe move somewhere else? signals: void projectMetadataChanged( const QString &projectDir ); diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 02fdbd6f1..000bf1ad9 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -61,12 +61,14 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const case ProjectFullName: return QVariant( project->projectId() ); case ProjectIsLocal: return QVariant( project->isLocal() ); case ProjectIsMergin: return QVariant( project->isMergin() ); + case ProjectStatus: return QVariant( LocalProjectsManager::currentProjectStatus( project ) ); + case ProjectIsValid: return QVariant( !project->isLocal() || ( project->isLocal() && project->local->projectError.isEmpty() ) ); case ProjectDescription: { if ( project->isLocal() && !project->local->projectError.isEmpty() ) return project->local->projectError; QFileInfo fi( project->local->projectDir ); - return fi.lastModified(); // TODO: Better project info + return fi.lastModified(); } default: { if ( !project->isMergin() ) return QVariant(); @@ -74,6 +76,7 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const // Roles only for projects that has mergin part if ( role == ProjectPending ) return QVariant( project->mergin->pending ); else if ( role == ProjectSyncProgress ) return QVariant( project->mergin->progress ); + else if ( role == ProjectRemoteError ) return QVariant( project->mergin->remoteError ); return QVariant(); } } @@ -114,7 +117,7 @@ void ProjectsModel_future::listProjects( int page, QString searchExpression ) return; } - mLastRequestId = mBackend->listProjects( "", "" /*modelTypeToFlag()*/, searchExpression, page ); + mLastRequestId = mBackend->listProjects( "", modelTypeToFlag(), searchExpression, page ); } void ProjectsModel_future::listProjectsByName() diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 161e67692..456f80298 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -48,8 +48,10 @@ class ProjectsModel_future : public QAbstractListModel ProjectPending, ProjectIsMergin, ProjectIsLocal, + ProjectIsValid, ProjectStatus, - ProjectSyncProgress + ProjectSyncProgress, + ProjectRemoteError }; Q_ENUMS( Roles ) From 89e02dfe8e5928bdcfacc16dba6cfff363f3411f Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Tue, 23 Mar 2021 11:54:29 +0100 Subject: [PATCH 12/22] Proxy models --- app/main.cpp | 30 ++++++++++-- app/projectsmodel_future.cpp | 5 ++ app/projectsmodel_future.h | 2 + app/projectsproxymodel_future.cpp | 77 +++++++++++++++++++++++++++++-- app/projectsproxymodel_future.h | 18 ++++++-- 5 files changed, 121 insertions(+), 11 deletions(-) diff --git a/app/main.cpp b/app/main.cpp index ad203e0c0..9ff6994e8 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -54,6 +54,9 @@ #include "projectwizard.h" #include "codefilter.h" +#include "projectsmodel_future.h" +#include "projectsproxymodel_future.h" + #ifdef INPUT_TEST #include "test/testmerginapi.h" #include "test/testlinks.h" @@ -230,6 +233,9 @@ void initDeclarative() qmlRegisterType( "lc", 1, 0, "PositionDirection" ); qmlRegisterType( "lc", 1, 0, "FieldsModel" ); qmlRegisterType( "lc", 1, 0, "CodeFilter" ); + + qmlRegisterUncreatableType( "lc", 1, 0, "ProjectsModelF", "" ); + qmlRegisterUncreatableType( "lc", 1, 0, "ProjectsProxyModelF", "" ); } #ifdef INPUT_TEST @@ -367,6 +373,17 @@ int main( int argc, char *argv[] ) InputHelp help( ma.get(), &iu ); ProjectWizard pw( projectDir ); + // project models - instance for each category + ProjectsModel_future myProjectsModel( ma.get(), ProjectModelTypes::MyProjectsModel, localProjects ); + ProjectsModel_future localProjectsModel( ma.get(), ProjectModelTypes::LocalProjectsModel, localProjects ); + ProjectsModel_future sharedProjectsModel( ma.get(), ProjectModelTypes::SharedProjectsModel, localProjects ); + ProjectsModel_future exploreProjectsModel( ma.get(), ProjectModelTypes::ExploreProjectsModel, localProjects ); + + ProjectsProxyModel_future myProjectsProxyModel( &myProjectsModel ); + ProjectsProxyModel_future localProjectsProxyModel( &localProjectsModel ); + ProjectsProxyModel_future sharedProjectsProxyModel( &sharedProjectsModel ); + ProjectsProxyModel_future exploreProjectsProxyModel( &exploreProjectsModel ); + // layer models LayersModel lm; LayersProxyModel browseLpm( &lm, LayerModelTypes::BrowseDataLayerSelection ); @@ -380,11 +397,11 @@ int main( int argc, char *argv[] ) // Connections QObject::connect( &app, &QGuiApplication::applicationStateChanged, &loader, &Loader::appStateChanged ); QObject::connect( &app, &QCoreApplication::aboutToQuit, &loader, &Loader::appAboutToQuit ); - QObject::connect( ma.get(), &MerginApi::syncProjectFinished, &pm, &ProjectModel::syncedProjectFinished ); - QObject::connect( ma.get(), &MerginApi::projectDetached, &pm, &ProjectModel::findProjectFiles ); +// QObject::connect( ma.get(), &MerginApi::syncProjectFinished, &pm, &ProjectModel::syncedProjectFinished ); +// QObject::connect( ma.get(), &MerginApi::projectDetached, &pm, &ProjectModel::findProjectFiles ); QObject::connect( &pw, &ProjectWizard::projectCreated, &localProjects, &LocalProjectsManager::addLocalProject ); - QObject::connect( ma.get(), &MerginApi::listProjectsFinished, &mpm, &MerginProjectModel::updateModel ); - QObject::connect( ma.get(), &MerginApi::syncProjectStatusChanged, &mpm, &MerginProjectModel::syncProjectStatusChanged ); +// QObject::connect( ma.get(), &MerginApi::listProjectsFinished, &mpm, &MerginProjectModel::updateModel ); +// QObject::connect( ma.get(), &MerginApi::syncProjectStatusChanged, &mpm, &MerginProjectModel::syncProjectStatusChanged ); QObject::connect( ma.get(), &MerginApi::reloadProject, &loader, &Loader::reloadProject ); QObject::connect( &mtm, &MapThemesModel::mapThemeChanged, &recordingLpm, &LayersProxyModel::onMapThemeChanged ); QObject::connect( &loader, &Loader::projectReloaded, vm.get(), &VariablesManager::merginProjectChanged ); @@ -485,6 +502,11 @@ int main( int argc, char *argv[] ) engine.rootContext()->setContextProperty( "__purchasing", purchasing.get() ); engine.rootContext()->setContextProperty( "__projectWizard", &pw ); + engine.rootContext()->setContextProperty( "__myProjectsModel", &myProjectsModel ); // TODO: maybe project models do not need to be exposed? + engine.rootContext()->setContextProperty( "__localProjectsModel", &localProjectsModel ); + engine.rootContext()->setContextProperty( "__myProjectsProxyModel", &myProjectsProxyModel ); + engine.rootContext()->setContextProperty( "__localProjectsProxyModel", &localProjectsProxyModel ); + #ifdef MOBILE_OS engine.rootContext()->setContextProperty( "__appwindowvisibility", QWindow::Maximized ); engine.rootContext()->setContextProperty( "__appwindowwidth", QVariant( 0 ) ); diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 000bf1ad9..046010619 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -508,3 +508,8 @@ std::shared_ptr ProjectsModel_future::projectFromId( QString pro return nullptr; } + +ProjectModelTypes ProjectsModel_future::modelType() const +{ + return mModelType; +} diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 456f80298..9000409e9 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -83,6 +83,8 @@ class ProjectsModel_future : public QAbstractListModel int serverProjectsCount() const; + ProjectModelTypes modelType() const; + public slots: // MerginAPI - backend signals void onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ); diff --git a/app/projectsproxymodel_future.cpp b/app/projectsproxymodel_future.cpp index fcbab74b3..398c704c4 100644 --- a/app/projectsproxymodel_future.cpp +++ b/app/projectsproxymodel_future.cpp @@ -9,15 +9,84 @@ #include "projectsproxymodel_future.h" -ProjectsProxyModel_future::ProjectsProxyModel_future( ProjectModelTypes modelType, QObject *parent ) : +ProjectsProxyModel_future::ProjectsProxyModel_future( ProjectsModel_future *projectsSourceModel, QObject *parent ) : QSortFilterProxyModel( parent ), - mModelType( modelType ) + mModel( projectsSourceModel ) { + setSourceModel( mModel ); + mModelType = mModel->modelType(); + setSortRole( ProjectsModel_future::Roles::ProjectFullName ); + setSortCaseSensitivity( Qt::CaseSensitivity::CaseInsensitive ); } -bool ProjectsProxyModel_future::filterAcceptsRow( int, const QModelIndex & ) const +QString ProjectsProxyModel_future::searchExpression() const +{ + return mSearchExpression; +} + +void ProjectsProxyModel_future::setSearchExpression( QString searchExpression ) +{ + if ( mSearchExpression == searchExpression ) + return; + + mSearchExpression = searchExpression; + + if ( mSearchExpression.isEmpty() ) + invalidate(); + else + setFilterRegularExpression( QRegularExpression( mSearchExpression ) ); + + emit searchExpressionChanged( mSearchExpression ); +} + +bool ProjectsProxyModel_future::filterAcceptsRow( int, const QModelIndex &sourceParent ) const { // return true if it passes search filter - return true; + QString projectName = sourceModel()->data( sourceParent, ProjectsModel_future::Roles::ProjectName ).toString(); + QString projectNamespace = sourceModel()->data( sourceParent, ProjectsModel_future::Roles::ProjectNamespace ).toString(); + + QRegExp filter = filterRegExp(); + if ( filter.isEmpty() ) + return true; + + return ( projectName.contains( filter ) || projectNamespace.contains( filter ) ); } + +//bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelIndex &right ) const +//{ +// TODO: Maybe simply setting sort role as projectFullName would work the same + +// if ( mModelType == LocalProjectsModel ) +// { + /** + * Ordering of local projects: first non-mergin projects (using folder name), + * then mergin projects (sorted first by namespace, then project name) + */ + +// if ( projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) +// { +// return folderName.compare( other.folderName, Qt::CaseInsensitive ) < 0; +// } +// if ( !projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) +// { +// return false; +// } +// if ( projectNamespace.isEmpty() && !other.projectNamespace.isEmpty() ) +// { +// return true; +// } + +// if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) == 0 ) +// { +// return projectName.compare( other.projectName, Qt::CaseInsensitive ) < 0; +// } +// if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) < 0 ) +// { +// return true; +// } +// else +// return false; +// return true; +// } +//} diff --git a/app/projectsproxymodel_future.h b/app/projectsproxymodel_future.h index db02b5fae..66b4539fb 100644 --- a/app/projectsproxymodel_future.h +++ b/app/projectsproxymodel_future.h @@ -22,15 +22,27 @@ class ProjectsProxyModel_future : public QSortFilterProxyModel { Q_OBJECT public: - explicit ProjectsProxyModel_future( ProjectModelTypes modelType, QObject *parent = nullptr ); + explicit ProjectsProxyModel_future( ProjectsModel_future *projectSourceModel, QObject *parent = nullptr ); ~ProjectsProxyModel_future() override {}; - protected: + Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression NOTIFY searchExpressionChanged ) + + QString searchExpression() const; + +public slots: + void setSearchExpression(QString SearchExpression); + +signals: + void searchExpressionChanged(QString SearchExpression); + +protected: bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; -// bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +// bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override; private: + ProjectsModel_future *mModel; ProjectModelTypes mModelType; + QString mSearchExpression; }; #endif // PROJECTSPROXYMODEL_FUTURE_H From 0496ae1e7926fba9d3cb86c9c6496be3398acaf4 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 24 Mar 2021 22:16:44 +0100 Subject: [PATCH 13/22] Make models instantiable from QML --- app/localprojectsmanager.cpp | 12 +-- app/localprojectsmanager.h | 4 +- app/main.cpp | 6 +- app/project_future.h | 37 ++++---- app/projectsmodel_future.cpp | 153 ++++++++++++++++++++++-------- app/projectsmodel_future.h | 70 +++++++++----- app/projectsproxymodel_future.cpp | 109 +++++++++++---------- app/projectsproxymodel_future.h | 23 +++-- 8 files changed, 267 insertions(+), 147 deletions(-) diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index f8b9ad5aa..b06b6950f 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -290,15 +290,15 @@ static int _getProjectFilesCount( const QString &path ) return count; } -ProjectStatus_future LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) +ProjectStatus::Status LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) { if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project - return ProjectStatus_future::_NoVersion; + return ProjectStatus::NoVersion; // There was no sync yet if ( project->local->localVersion < 0 ) { - return ProjectStatus_future::_NoVersion; + return ProjectStatus::NoVersion; } // @@ -313,16 +313,16 @@ ProjectStatus_future LocalProjectsManager::currentProjectStatus( const std::shar int filesCount = _getProjectFilesCount( project->local->projectDir ); if ( lastSync < lastModified || meta.files.count() != filesCount ) { - return ProjectStatus_future::_Modified; + return ProjectStatus::Modified; } // Version is lower than latest one, last sync also before updated if ( project->local->localVersion < project->mergin->serverVersion ) { - return ProjectStatus_future::_OutOfDate; + return ProjectStatus::OutOfDate; } - return ProjectStatus_future::_UpToDate; + return ProjectStatus::UpToDate; } //void LocalProjectsManager::updateProjectStatus( LocalProject_future &project ) diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 4a601aae5..0b5a0652f 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -93,7 +93,7 @@ class LocalProjectsManager : public QObject // void addLocalProject( const QString &projectDir, const QString &projectName ); //! Should forget about that project (it has been removed already) - void removeLocalProject( const QString &projectDir ); + Q_INVOKABLE void removeLocalProject( const QString &projectDir ); //! Resets mergin related info for given project. // void removeMerginInfo( const QString &projectFullName ); @@ -121,7 +121,7 @@ class LocalProjectsManager : public QObject QString findQgisProjectFile( const QString &projectDir, QString &err ); - static ProjectStatus_future currentProjectStatus( const std::shared_ptr project ); // TODO: maybe move somewhere else? + static ProjectStatus::Status currentProjectStatus( const std::shared_ptr project ); // TODO: maybe move somewhere else? signals: void projectMetadataChanged( const QString &projectDir ); diff --git a/app/main.cpp b/app/main.cpp index 9ff6994e8..471441bec 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -56,6 +56,7 @@ #include "projectsmodel_future.h" #include "projectsproxymodel_future.h" +#include "project_future.h" #ifdef INPUT_TEST #include "test/testmerginapi.h" @@ -234,8 +235,9 @@ void initDeclarative() qmlRegisterType( "lc", 1, 0, "FieldsModel" ); qmlRegisterType( "lc", 1, 0, "CodeFilter" ); - qmlRegisterUncreatableType( "lc", 1, 0, "ProjectsModelF", "" ); - qmlRegisterUncreatableType( "lc", 1, 0, "ProjectsProxyModelF", "" ); + qmlRegisterType( "lc", 1, 0, "ProjectsModel" ); + qmlRegisterType( "lc", 1, 0, "ProjectsProxyModel" ); + qmlRegisterUncreatableMetaObject( ProjectStatus::staticMetaObject, "lc", 1, 0, "ProjectStatus", "ProjectStatus Enum" ); } #ifdef INPUT_TEST diff --git a/app/project_future.h b/app/project_future.h index 1b4946ac9..b6ffc43b5 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -16,16 +16,19 @@ #include #include -enum ProjectStatus_future -{ - _NoVersion, //!< the project is not available locally - _UpToDate, //!< both server and local copy are in sync with no extra modifications - _OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) - _Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) +namespace ProjectStatus { + Q_NAMESPACE + enum Status + { + NoVersion, //!< the project is not available locally + UpToDate, //!< both server and local copy are in sync with no extra modifications + OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) + Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) - // Maybe orphaned state in future -}; -Q_ENUMS( ProjectStatus_future ) + // Maybe orphaned state in future + }; + Q_ENUM_NS( Status ) +} struct LocalProject_future { @@ -48,7 +51,7 @@ struct LocalProject_future bool operator ==( const LocalProject_future &other ) { - return ( this->id() == other.id() ) && ( this->projectDir == other.projectDir ); + return ( this->id() == other.id() ); } bool operator !=( const LocalProject_future &other ) @@ -70,7 +73,7 @@ struct MerginProject_future QDateTime serverUpdated; // available latest version of project files on server int serverVersion; - ProjectStatus_future status = ProjectStatus_future::_NoVersion; + ProjectStatus::Status status = ProjectStatus::NoVersion; bool pending = false; qreal progress = 0; @@ -102,22 +105,22 @@ struct Project_future QString projectName() { - if ( isLocal() ) return local->projectName; - else if ( isMergin() ) return mergin->projectName; + if ( isMergin() ) return mergin->projectName; + else if ( isLocal() ) return local->projectName; return QString(); } QString projectNamespace() { - if ( isLocal() ) return local->projectNamespace; - else if ( isMergin() ) return mergin->projectNamespace; + if ( isMergin() ) return mergin->projectNamespace; + else if ( isLocal() ) return local->projectNamespace; return QString(); } QString projectId() { - if ( isLocal() ) return local->id(); - else if ( isMergin() ) return mergin->id(); + if ( isMergin() ) return mergin->id(); + else if ( isLocal() ) return local->id(); return QString(); } diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 046010619..8e99f552c 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -12,26 +12,27 @@ #include "inpututils.h" #include "merginuserauth.h" -ProjectsModel_future::ProjectsModel_future( - MerginApi *merginApi, - ProjectModelTypes modelType, - LocalProjectsManager &localProjectsManager, - QObject *parent ) : - QAbstractListModel( parent ), - mBackend( merginApi ), - mLocalProjectsManager( localProjectsManager ), - mModelType( modelType ) +ProjectsModel_future::ProjectsModel_future( QObject *parent ) : QAbstractListModel( parent ) { + qDebug() << "PMR: Instantiated ProjectsModel! " << this << "MerginAPI: " << mBackend << "LPM:" << mLocalProjectsManager << "Type: " << mModelType; +} + +void ProjectsModel_future::initializeProjectsModel() +{ + if ( !mBackend || !mLocalProjectsManager || mModelType == EmptyProjectsModel ) // Model is not set up properly yet + return; + + qDebug() << "PMR: initializing projects model " << this; + QObject::connect( mBackend, &MerginApi::syncProjectStatusChanged, this, &ProjectsModel_future::onProjectSyncProgressChanged ); QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel_future::onProjectSyncFinished ); QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel_future::onProjectDetachedFromMergin ); QObject::connect( mBackend, &MerginApi::projectAttachedToMergin, this, &ProjectsModel_future::onProjectAttachedToMergin ); - if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { QObject::connect( mBackend, &MerginApi::listProjectsByNameFinished, this, &ProjectsModel_future::onListProjectsByNameFinished ); - loadLocalProjects(); // at app start, we need to fill model with local projects + loadLocalProjects(); } else if ( mModelType != ProjectModelTypes::RecentProjectsModel ) { @@ -42,10 +43,12 @@ ProjectsModel_future::ProjectsModel_future( // Implement RecentProjectsModel type } - QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); - QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel_future::onAboutToRemoveProject ); - QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); - QObject::connect( &mLocalProjectsManager, &LocalProjectsManager::dataDirReloaded, this, &ProjectsModel_future::loadLocalProjects ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel_future::onAboutToRemoveProject ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::dataDirReloaded, this, &ProjectsModel_future::loadLocalProjects ); + + emit modelInitialized(); } QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const @@ -53,22 +56,41 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const if ( !index.isValid() ) return QVariant(); + if ( index.row() < 0 || index.row() >= mProjects.size() ) + return QVariant(); + std::shared_ptr project = mProjects.at( index.row() ); switch ( role ) { case ProjectName: return QVariant( project->projectName() ); case ProjectNamespace: return QVariant( project->projectNamespace() ); - case ProjectFullName: return QVariant( project->projectId() ); + case ProjectFullName: return MerginApi::getFullProjectName( project->projectNamespace(), project->projectName() ); + case ProjectId: return QVariant( project->projectId() ); case ProjectIsLocal: return QVariant( project->isLocal() ); case ProjectIsMergin: return QVariant( project->isMergin() ); - case ProjectStatus: return QVariant( LocalProjectsManager::currentProjectStatus( project ) ); - case ProjectIsValid: return QVariant( !project->isLocal() || ( project->isLocal() && project->local->projectError.isEmpty() ) ); + case ProjectSyncStatus: return QVariant( LocalProjectsManager::currentProjectStatus( project ) ); + case ProjectFilePath: return QVariant( project->isLocal() ? project->local->qgisProjectFilePath : QString() ); + case ProjectDirectory: return QVariant( project->isLocal() ? project->local->projectDir : QString() ); + case ProjectIsValid: { + if ( !project->isLocal() ) + return true; // Mergin projects are by default valid, remote error only affects syncing, not opening of a project + return project->local->projectError.isEmpty(); + } case ProjectDescription: { - if ( project->isLocal() && !project->local->projectError.isEmpty() ) - return project->local->projectError; - - QFileInfo fi( project->local->projectDir ); - return fi.lastModified(); + if ( project->isLocal() ) + { + if ( !project->local->projectError.isEmpty() ) + { + return QVariant( project->local->projectError ); + } + QFileInfo fi( project->local->projectDir ); + return QVariant( fi.lastModified().toLocalTime() ); // Maybe use better timestamp format https://doc.qt.io/qt-5/qdatetime.html#toString-3 + } + else if ( project->isMergin() ) + { + return QVariant( project->mergin->serverUpdated ); + } + return QVariant(); // This should not happen } default: { if ( !project->isMergin() ) return QVariant(); @@ -89,17 +111,33 @@ QModelIndex ProjectsModel_future::index( int row, int col, const QModelIndex &pa return createIndex( row, 0, nullptr ); } +bool ProjectsModel_future::canFetchMore( const QModelIndex & ) const +{ +// return mServerProjectsCount > mProjects.size(); + return true; +} + +void ProjectsModel_future::fetchMore( const QModelIndex & ) +{ + +} + QHash ProjectsModel_future::roleNames() const { QHash roles; roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); roles[Roles::ProjectNamespace] = QStringLiteral( "ProjectNamespace" ).toLatin1(); roles[Roles::ProjectFullName] = QStringLiteral( "ProjectFullName" ).toLatin1(); + roles[Roles::ProjectDirectory] = QStringLiteral( "ProjectDirectory" ).toLatin1(); roles[Roles::ProjectIsLocal] = QStringLiteral( "ProjectIsLocal" ).toLatin1(); roles[Roles::ProjectIsMergin] = QStringLiteral( "ProjectIsMergin" ).toLatin1(); + roles[Roles::ProjectSyncStatus] = QStringLiteral( "ProjectSyncStatus" ).toLatin1(); + roles[Roles::ProjectIsValid] = QStringLiteral( "ProjectIsValid" ).toLatin1(); + roles[Roles::ProjectFilePath] = QStringLiteral( "ProjectFilePath" ).toLatin1(); roles[Roles::ProjectDescription] = QStringLiteral( "ProjectDescription" ).toLatin1(); roles[Roles::ProjectPending] = QStringLiteral( "ProjectPending" ).toLatin1(); roles[Roles::ProjectSyncProgress] = QStringLiteral( "ProjectSyncProgress" ).toLatin1(); + roles[Roles::ProjectRemoteError] = QStringLiteral( "ProjectRemoteError" ).toLatin1(); return roles; } @@ -112,8 +150,7 @@ void ProjectsModel_future::listProjects( int page, QString searchExpression ) { if ( mModelType == LocalProjectsModel ) { - InputUtils::log( "Input", "Can not call listProjects API on LocalProjectsModel" ); - // maybe call listProjectsByName(); + listProjectsByName(); return; } @@ -124,7 +161,6 @@ void ProjectsModel_future::listProjectsByName() { if ( mModelType != LocalProjectsModel ) { - InputUtils::log( "Input", "Can not call listProjectsByName API on not LocalProjectsModel" ); return; } @@ -133,7 +169,7 @@ void ProjectsModel_future::listProjectsByName() void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious ) { - LocalProjectsList localProjects = mLocalProjectsManager.projects(); + LocalProjectsList localProjects = mLocalProjectsManager->projects(); qDebug() << "PMR: mergeProjects(): # of local projects = " << localProjects.size() << " # of mergin projects = " << merginProjects.size(); @@ -166,6 +202,13 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec project->mergin->pending = true; } } + else if ( project->local->localVersion > -1 ) + { + // this is indeed a Mergin project, it has metadata folder in it + project->mergin = std::unique_ptr( new MerginProject_future() ); + project->mergin->projectName = project->local->projectName; + project->mergin->projectNamespace = project->local->projectNamespace; + } mProjects << project; } @@ -240,7 +283,7 @@ void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsLis endResetModel(); } -void ProjectsModel_future::syncProject( QString projectNamespace, QString projectName ) +void ProjectsModel_future::syncProject( const QString &projectNamespace, const QString &projectName ) { std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); @@ -262,21 +305,21 @@ void ProjectsModel_future::syncProject( QString projectNamespace, QString projec return; } - if ( project->mergin->status == _NoVersion || project->mergin->status == _OutOfDate ) + if ( project->mergin->status == ProjectStatus::NoVersion || project->mergin->status == ProjectStatus::OutOfDate ) { qDebug() << "PMR: updating project:" << project->mergin->id(); bool useAuth = !mBackend->userAuth()->hasAuthData() && mModelType == ProjectModelTypes::ExploreProjectsModel; mBackend->updateProject( projectNamespace, projectName, useAuth ); } - else if ( project->mergin->status == _Modified ) + else if ( project->mergin->status == ProjectStatus::Modified ) { qDebug() << "PMR: uploading project:" << project->mergin->id(); mBackend->uploadProject( projectNamespace, projectName ); } } -void ProjectsModel_future::stopProjectSync( QString projectNamespace, QString projectName ) +void ProjectsModel_future::stopProjectSync( const QString &projectNamespace, const QString &projectName ) { std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); @@ -298,18 +341,23 @@ void ProjectsModel_future::stopProjectSync( QString projectNamespace, QString pr return; } - if ( project->mergin->status == _NoVersion || project->mergin->status == _OutOfDate ) + if ( project->mergin->status == ProjectStatus::NoVersion || project->mergin->status == ProjectStatus::OutOfDate ) { qDebug() << "PMR: cancelling update of project:" << project->mergin->id(); mBackend->updateCancel( project->mergin->id() ); } - else if ( project->mergin->status == _Modified ) + else if ( project->mergin->status == ProjectStatus::Modified ) { qDebug() << "PMR: cancelling upload of project:" << project->mergin->id(); mBackend->uploadCancel( project->mergin->id() ); } } +void ProjectsModel_future::removeLocalProject( const QString &projectDir ) +{ + mLocalProjectsManager->removeLocalProject( projectDir ); +} + void ProjectsModel_future::onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) { Q_UNUSED( projectDir ) @@ -360,8 +408,9 @@ void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) std::shared_ptr newProject = std::shared_ptr( new Project_future() ); newProject->local = std::unique_ptr( new LocalProject_future( project ) ); - int insertIndex = mProjects.size() == 0 ? 0 : mProjects.size() - 1; - beginInsertRows( QModelIndex(), insertIndex, insertIndex + 1 ); + int insertIndex = mProjects.size(); + + beginInsertRows( QModelIndex(), insertIndex, insertIndex ); mProjects << newProject; endInsertRows(); } @@ -434,6 +483,36 @@ void ProjectsModel_future::setServerProjectsCount( int serverProjectsCount ) emit serverProjectsCountChanged( mServerProjectsCount ); } +void ProjectsModel_future::setMerginApi( MerginApi *merginApi ) +{ + if ( !merginApi || mBackend == merginApi ) + return; + + mBackend = merginApi; + qDebug() << "PMR: New MA: " << mBackend; + initializeProjectsModel(); +} + +void ProjectsModel_future::setLocalProjectsManager( LocalProjectsManager *localProjectsManager ) +{ + if ( !localProjectsManager || mLocalProjectsManager == localProjectsManager ) + return; + + mLocalProjectsManager = localProjectsManager; + qDebug() << "PMR: New LPM: " << mLocalProjectsManager; + initializeProjectsModel(); +} + +void ProjectsModel_future::setModelType( ProjectsModel_future::ProjectModelTypes modelType ) +{ + if ( mModelType == modelType ) + return; + + mModelType = modelType; + qDebug() << "PMR: New Type: " << mModelType; + initializeProjectsModel(); +} + QString ProjectsModel_future::modelTypeToFlag() const { switch ( mModelType ) @@ -469,7 +548,7 @@ void ProjectsModel_future::printProjects() const // TODO: Helper function, remov QStringList ProjectsModel_future::projectNames() const { QStringList projectNames; - LocalProjectsList projects = mLocalProjectsManager.projects(); + LocalProjectsList projects = mLocalProjectsManager->projects(); for ( const auto &proj : projects ) { @@ -509,7 +588,7 @@ std::shared_ptr ProjectsModel_future::projectFromId( QString pro return nullptr; } -ProjectModelTypes ProjectsModel_future::modelType() const +ProjectsModel_future::ProjectModelTypes ProjectsModel_future::modelType() const { return mModelType; } diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index 9000409e9..e03ba39c0 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -18,18 +18,6 @@ class LocalProjectsManager; -/** - * \brief The ProjectModelTypes enum - */ -enum ProjectModelTypes -{ - LocalProjectsModel = 0, - MyProjectsModel, - SharedProjectsModel, - ExploreProjectsModel, - RecentProjectsModel -}; - /** * \brief The ProjectsModel_future class */ @@ -43,26 +31,50 @@ class ProjectsModel_future : public QAbstractListModel { ProjectName = Qt::UserRole + 1, ProjectNamespace, - ProjectFullName, // or ProjectId, filled with folderName if project is not + ProjectFullName, + ProjectId, // Filled with ProjectFullName for time being + ProjectDirectory, ProjectDescription, ProjectPending, ProjectIsMergin, ProjectIsLocal, + ProjectFilePath, ProjectIsValid, - ProjectStatus, + ProjectSyncStatus, ProjectSyncProgress, ProjectRemoteError }; - Q_ENUMS( Roles ) + Q_ENUM( Roles ) + + /** + * \brief The ProjectModelTypes enum + */ + enum ProjectModelTypes + { + EmptyProjectsModel = 0, // default, holding no projects ~ invalid model + LocalProjectsModel, + MyProjectsModel, + SharedProjectsModel, + ExploreProjectsModel, + RecentProjectsModel + }; + Q_ENUM( ProjectModelTypes ) - ProjectsModel_future( MerginApi *merginApi, ProjectModelTypes modelType, LocalProjectsManager &localProjectsManager, QObject *parent = nullptr ); + ProjectsModel_future( QObject *parent = nullptr ); ~ProjectsModel_future() override {}; - Q_PROPERTY( int serverProjectsCount READ serverProjectsCount WRITE setServerProjectsCount NOTIFY serverProjectsCountChanged ); + Q_PROPERTY( int serverProjectsCount READ serverProjectsCount WRITE setServerProjectsCount NOTIFY serverProjectsCountChanged ) // TODO: replace with builtin canFetchMore + + // From Qt 5.15 we can use REQUIRED keyword here that will ensure object will be always instantiated from QML with these mandatory properties + Q_PROPERTY( MerginApi *merginApi READ merginApi WRITE setMerginApi ) + Q_PROPERTY( LocalProjectsManager *localProjectsManager READ localProjectsManager WRITE setLocalProjectsManager ) + Q_PROPERTY( ProjectModelTypes modelType READ modelType WRITE setModelType ) // Needed methods from QAbstractListModel Q_INVOKABLE QVariant data( const QModelIndex &index, int role ) const override; Q_INVOKABLE QModelIndex index( int row, int column = 0, const QModelIndex &parent = QModelIndex() ) const override; + Q_INVOKABLE bool canFetchMore( const QModelIndex &parent ) const override; + Q_INVOKABLE void fetchMore( const QModelIndex &parent ) override; QHash roleNames() const override; int rowCount( const QModelIndex &parent = QModelIndex() ) const override; @@ -73,17 +85,24 @@ class ProjectsModel_future : public QAbstractListModel Q_INVOKABLE void listProjectsByName(); //! Syncs specified project - upload or update - Q_INVOKABLE void syncProject( QString projectNamespace, QString projectName ); + Q_INVOKABLE void syncProject( const QString &projectNamespace, const QString &projectName ); //! Stops running project upload or update - Q_INVOKABLE void stopProjectSync( QString projectNamespace, QString projectName ); + Q_INVOKABLE void stopProjectSync( const QString &projectNamespace, const QString &projectName ); + + //! Forwards call to LocalProjectsManager to remove local project + Q_INVOKABLE void removeLocalProject( const QString &projectDir ); //! Method merging local and remote projects based on the model type void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious = false ); int serverProjectsCount() const; - ProjectModelTypes modelType() const; + ProjectsModel_future::ProjectModelTypes modelType() const; + + MerginApi *merginApi() const { return mBackend; } + + LocalProjectsManager *localProjectsManager() const { return mLocalProjectsManager; } public slots: // MerginAPI - backend signals @@ -100,9 +119,13 @@ public slots: void onProjectDataChanged( const LocalProject_future &project ); void setServerProjectsCount( int serverProjectsCount ); + void setMerginApi( MerginApi *merginApi ); + void setLocalProjectsManager( LocalProjectsManager *localProjectsManager ); + void setModelType( ProjectModelTypes modelType ); signals: void serverProjectsCountChanged( int serverProjectsCount ); + void modelInitialized(); private: @@ -110,15 +133,16 @@ public slots: void printProjects() const; QStringList projectNames() const; void loadLocalProjects(); + void initializeProjectsModel(); bool containsProject( QString projectId ) const; std::shared_ptr projectFromId( QString projectId ) const; - MerginApi *mBackend; - LocalProjectsManager &mLocalProjectsManager; + MerginApi *mBackend = nullptr; + LocalProjectsManager *mLocalProjectsManager = nullptr; QList> mProjects; - ProjectModelTypes mModelType; + ProjectModelTypes mModelType = EmptyProjectsModel; //! For pagination int mServerProjectsCount = -1; diff --git a/app/projectsproxymodel_future.cpp b/app/projectsproxymodel_future.cpp index 398c704c4..3afb20571 100644 --- a/app/projectsproxymodel_future.cpp +++ b/app/projectsproxymodel_future.cpp @@ -9,15 +9,21 @@ #include "projectsproxymodel_future.h" -ProjectsProxyModel_future::ProjectsProxyModel_future( ProjectsModel_future *projectsSourceModel, QObject *parent ) : - QSortFilterProxyModel( parent ), - mModel( projectsSourceModel ) +ProjectsProxyModel_future::ProjectsProxyModel_future( QObject *parent ) : QSortFilterProxyModel( parent ) { + qDebug() << "PMR: Building proxy model " << this; +} + +void ProjectsProxyModel_future::initialize() +{ + qDebug() << "PMR: Initializing proxy model" << this; setSourceModel( mModel ); mModelType = mModel->modelType(); - setSortRole( ProjectsModel_future::Roles::ProjectFullName ); - setSortCaseSensitivity( Qt::CaseSensitivity::CaseInsensitive ); + setFilterRole( ProjectsModel_future::ProjectFullName ); + setFilterCaseSensitivity( Qt::CaseInsensitive ); + + sort( 0, Qt::AscendingOrder ); } QString ProjectsProxyModel_future::searchExpression() const @@ -25,68 +31,69 @@ QString ProjectsProxyModel_future::searchExpression() const return mSearchExpression; } +ProjectsModel_future *ProjectsProxyModel_future::projectSourceModel() const +{ + return mModel; +} + void ProjectsProxyModel_future::setSearchExpression( QString searchExpression ) { if ( mSearchExpression == searchExpression ) return; mSearchExpression = searchExpression; - - if ( mSearchExpression.isEmpty() ) - invalidate(); - else - setFilterRegularExpression( QRegularExpression( mSearchExpression ) ); - + setFilterFixedString( mSearchExpression ); emit searchExpressionChanged( mSearchExpression ); } -bool ProjectsProxyModel_future::filterAcceptsRow( int, const QModelIndex &sourceParent ) const +void ProjectsProxyModel_future::setProjectSourceModel( ProjectsModel_future *sourceModel ) { - // return true if it passes search filter - QString projectName = sourceModel()->data( sourceParent, ProjectsModel_future::Roles::ProjectName ).toString(); - QString projectNamespace = sourceModel()->data( sourceParent, ProjectsModel_future::Roles::ProjectNamespace ).toString(); - - QRegExp filter = filterRegExp(); - if ( filter.isEmpty() ) - return true; + if ( mModel == sourceModel ) + return; - return ( projectName.contains( filter ) || projectNamespace.contains( filter ) ); + mModel = sourceModel; + QObject::connect( mModel, &ProjectsModel_future::modelInitialized, this, &ProjectsProxyModel_future::initialize ); } -//bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelIndex &right ) const -//{ -// TODO: Maybe simply setting sort role as projectFullName would work the same -// if ( mModelType == LocalProjectsModel ) -// { +bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelIndex &right ) const +{ + if ( mModelType == ProjectsModel_future::LocalProjectsModel ) + { + bool lProjectIsMergin = mModel->data( left, ProjectsModel_future::ProjectIsMergin ).toBool(); + bool rProjectIsMergin = mModel->data( right, ProjectsModel_future::ProjectIsMergin ).toBool(); + /** * Ordering of local projects: first non-mergin projects (using folder name), * then mergin projects (sorted first by namespace, then project name) */ -// if ( projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) -// { -// return folderName.compare( other.folderName, Qt::CaseInsensitive ) < 0; -// } -// if ( !projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) -// { -// return false; -// } -// if ( projectNamespace.isEmpty() && !other.projectNamespace.isEmpty() ) -// { -// return true; -// } - -// if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) == 0 ) -// { -// return projectName.compare( other.projectName, Qt::CaseInsensitive ) < 0; -// } -// if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) < 0 ) -// { -// return true; -// } -// else -// return false; -// return true; -// } -//} + if ( !lProjectIsMergin && !rProjectIsMergin ) + { + QString lProjectFullName = mModel->data( left, ProjectsModel_future::ProjectFullName ).toString(); + QString rProjectFullName = mModel->data( right, ProjectsModel_future::ProjectFullName ).toString(); + + return lProjectFullName.compare( rProjectFullName, Qt::CaseInsensitive ) < 0; + } + if ( !lProjectIsMergin && rProjectIsMergin ) + { + return false; + } + if ( lProjectIsMergin && !rProjectIsMergin ) + { + return true; + } + + QString lNamespace = mModel->data( left, ProjectsModel_future::ProjectNamespace ).toString(); + QString lProjectName = mModel->data( left, ProjectsModel_future::ProjectName ).toString(); + QString rNamespace = mModel->data( right, ProjectsModel_future::ProjectNamespace ).toString(); + QString rProjectName = mModel->data( right, ProjectsModel_future::ProjectName ).toString(); + + if ( lNamespace == rNamespace ) + { + return lProjectName.compare( rProjectName, Qt::CaseInsensitive ) < 0; + } + return lNamespace.compare( rNamespace, Qt::CaseInsensitive ) < 0; + } + return false; +} diff --git a/app/projectsproxymodel_future.h b/app/projectsproxymodel_future.h index 66b4539fb..bf35046ea 100644 --- a/app/projectsproxymodel_future.h +++ b/app/projectsproxymodel_future.h @@ -20,28 +20,33 @@ */ class ProjectsProxyModel_future : public QSortFilterProxyModel { - Q_OBJECT - public: - explicit ProjectsProxyModel_future( ProjectsModel_future *projectSourceModel, QObject *parent = nullptr ); - ~ProjectsProxyModel_future() override {}; + Q_OBJECT Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression NOTIFY searchExpressionChanged ) + Q_PROPERTY( ProjectsModel_future *projectSourceModel READ projectSourceModel WRITE setProjectSourceModel ) + +public: + explicit ProjectsProxyModel_future( QObject *parent = nullptr ); + ~ProjectsProxyModel_future() override {}; QString searchExpression() const; + ProjectsModel_future *projectSourceModel() const; public slots: - void setSearchExpression(QString SearchExpression); + void setSearchExpression( QString searchExpression ); + void setProjectSourceModel( ProjectsModel_future *sourceModel ); signals: - void searchExpressionChanged(QString SearchExpression); + void searchExpressionChanged( QString SearchExpression ); protected: - bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; -// bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override; + bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override; private: + void initialize(); + ProjectsModel_future *mModel; - ProjectModelTypes mModelType; + ProjectsModel_future::ProjectModelTypes mModelType = ProjectsModel_future::EmptyProjectsModel; QString mSearchExpression; }; From c55b0d32642fc2c553f8aa29ae80753f8b28317a Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 12:19:12 +0200 Subject: [PATCH 14/22] refactor qml code --- app/qml/ExtendedMenuItem.qml | 4 +- app/qml/InputStyle.qml | 5 + app/qml/MainPanelButton.qml | 3 +- app/qml/MerginProjectPanel.qml | 914 ---------------- app/qml/ProjectDelegateItem.qml | 179 ---- app/qml/ProjectListPage.qml | 62 ++ app/qml/ProjectPanel.qml | 1095 ++++++++++++++++++++ app/qml/components/ProjectDelegateItem.qml | 367 +++++++ app/qml/components/ProjectList.qml | 179 ++++ app/qml/main.qml | 63 +- app/qml/qml.qrc | 6 +- 11 files changed, 1749 insertions(+), 1128 deletions(-) delete mode 100644 app/qml/MerginProjectPanel.qml delete mode 100644 app/qml/ProjectDelegateItem.qml create mode 100644 app/qml/ProjectListPage.qml create mode 100644 app/qml/ProjectPanel.qml create mode 100644 app/qml/components/ProjectDelegateItem.qml create mode 100644 app/qml/components/ProjectList.qml diff --git a/app/qml/ExtendedMenuItem.qml b/app/qml/ExtendedMenuItem.qml index 68d2bcd93..6e528ba9a 100644 --- a/app/qml/ExtendedMenuItem.qml +++ b/app/qml/ExtendedMenuItem.qml @@ -39,6 +39,7 @@ Item { id: iconContainer height: rowHeight width: rowHeight + anchors.verticalCenter: parent ? parent.verticalCenter : undefined Image { id: icon @@ -64,6 +65,7 @@ Item { x: iconContainer.width + panelMargin width: parent.width - rowHeight height: rowHeight + anchors.verticalCenter: parent ? parent.verticalCenter : undefined Text { id: mainText @@ -87,6 +89,6 @@ Item { width: row.width height: 1 visible: root.showBorder - anchors.bottom: parent.bottom + anchors.bottom: parent ? parent.bottom : undefined } } diff --git a/app/qml/InputStyle.qml b/app/qml/InputStyle.qml index 87a5989cf..b2a8da657 100644 --- a/app/qml/InputStyle.qml +++ b/app/qml/InputStyle.qml @@ -19,6 +19,7 @@ QtObject { property color fontColorBright: "#679D70" property color panelBackground2: "#C6CCC7" property color activeButtonColor: "#006146" + property color activeButtonColorOrange: "#FD9626" // Secondary colors property color clrPanelMain: "white" @@ -81,6 +82,10 @@ QtObject { property var valueRelationIcon: "qrc:/value_relation_open.svg" property var comboboxIcon: "qrc:/combobox.svg" property var qrCodeIcon: "qrc:/qrcode.svg" + property var syncIcon: "qrc:sync.svg" + property var downloadIcon: "qrc:download.svg" + property var stopIcon: "qrc:stop.svg" + property var moreMenuIcon: "qrc:/more_menu.svg" property var vectorPointIcon: "qrc:/mIconPointLayer.svg" property var vectorLineIcon: "qrc:/mIconLineLayer.svg" diff --git a/app/qml/MainPanelButton.qml b/app/qml/MainPanelButton.qml index 363e1c5ee..f164048af 100644 --- a/app/qml/MainPanelButton.qml +++ b/app/qml/MainPanelButton.qml @@ -25,6 +25,7 @@ Rectangle { property color backgroundColor: InputStyle.fontColor property bool isHighlighted: false property bool enabled: true + property bool handleClicks: true // enable property is used also for color property bool faded: false signal activated() @@ -35,7 +36,7 @@ Rectangle { MouseArea { anchors.fill: parent - enabled: root.enabled + enabled: root.enabled && handleClicks onClicked: { root.activated() } diff --git a/app/qml/MerginProjectPanel.qml b/app/qml/MerginProjectPanel.qml deleted file mode 100644 index ca74bfa78..000000000 --- a/app/qml/MerginProjectPanel.qml +++ /dev/null @@ -1,914 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Dialogs 1.2 -import QgsQuick 0.1 as QgsQuick -import lc 1.0 -import "." // import InputStyle singleton -import "./components/" - -Item { - - property int activeProjectIndex: -1 - property string activeProjectPath: __projectsModel.data(__projectsModel.index(activeProjectIndex), ProjectModel.Path) - property var busyIndicator - - property real rowHeight: InputStyle.rowHeightHeader * 1.2 - property real iconSize: rowHeight/3 - property bool showMergin: false - property real panelMargin: InputStyle.panelMargin - - - function openPanel() { - projectsPanel.visible = true - stackView.visible = true - } - - id: projectsPanel - visible: false - focus: true - - onFocusChanged: { // pass focus to stackview - stackView.focus = true - } - - StackView { - id: stackView - initialItem: projectsPanelComp - anchors.fill: parent - focus: true - visible: false - z: projectsPanel.z + 1 - property bool pending: false - - function clearStackAndClose() { - if ( stackView.depth > 1 ) - stackView.pop( null ) // pops everything besides an initialItem - - stackView.visible = false - } - - function popOnePageOrClose() { - if ( stackView.depth > 1 ) - { - stackView.pop() - } - } - - Keys.onReleased: { - if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { - event.accepted = true; - - if (stackView.depth > 1) { - stackView.currentItem.back() - } - else if (projectsPanel.activeProjectPath) { - stackView.clearStackAndClose() - projectsPanel.visible = false - } - } - } - - onVisibleChanged: { - if ( stackView.visible ) - stackView.forceActiveFocus() - } - } - - - BusyIndicator { - id: busyIndicator - width: parent.width/8 - height: width - running: stackView.pending - visible: running - anchors.centerIn: parent - z: parent.z + 1 - } - - Component { - id: projectsPanelComp - Item { - - objectName: "projectsPanel" - - function getStatusIcon(status, pending) { - if (pending) return "stop.svg" - - if (status === "noVersion") return "download.svg" - else if (status === "outOfDate") return "sync.svg" - else if (status === "upToDate") return "check.svg" - else if (status === "modified") return "sync.svg" - - return "more_menu.svg" - } - - function refreshProjectList() { - if (toolbar.highlighted === exploreBtn.text) { - exploreBtn.activated() - } else if (toolbar.highlighted === sharedProjectsBtn.text) { - sharedProjectsBtn.activated() - } else if (toolbar.highlighted === myProjectsBtn.text) { - myProjectsBtn.activated() - } else homeBtn.activated() - } - - Component.onCompleted: { - // load model just after all components are prepared - // otherwise GridView's delegate item is initialized invalidately - grid.model = __projectsModel - merginProjectsList.model = __merginProjectsModel - } - - Connections { - target: __projectsModel - onModelReset: { - var index = __projectsModel.rowAccordingPath(activeProjectPath) - if (index !== activeProjectIndex) { - activeProjectIndex = index - } - } - } - - Connections { - target: __projectWizard - onProjectCreated: { - if (stackView.currentItem.objectName === "projectWizard") { - stackView.popOnePageOrClose() - } - } - } - - Connections { - target: __merginApi - onListProjectsFinished: { - stackView.pending = false - } - onListProjectsFailed: { - reloadList.visible = true - } - onApiVersionStatusChanged: { - stackView.pending = false - if (__merginApi.apiVersionStatus === MerginApiStatus.OK && stackView.currentItem.objectName === "authPanel") { - if (__merginApi.userAuth.hasAuthData()) { - refreshProjectList() - } else if (toolbar.highlighted !== homeBtn.text) { - if (stackView.currentItem.objectName !== "authPanel") { - stackView.push(authPanelComp, {state: "login"}) - } - } - } - } - onAuthRequested: { - stackView.pending = false - stackView.push(authPanelComp, {state: "login"}) - } - onAuthChanged: { - stackView.pending = false - if (__merginApi.userAuth.hasAuthData()) { - stackView.popOnePageOrClose() - refreshProjectList() - projectsPanel.forceActiveFocus() - } else { - homeBtn.activated() - } - - } - onAuthFailed: { - homeBtn.activated() - stackView.pending = false - projectsPanel.forceActiveFocus() - } - onRegistrationFailed: stackView.pending = false - onRegistrationSucceeded: stackView.pending = false - } - - - // background - Rectangle { - width: parent.width - height: parent.height - color: InputStyle.clrPanelMain - } - - - PanelHeader { - id: header - height: InputStyle.rowHeightHeader - width: parent.width - color: InputStyle.clrPanelMain - rowHeight: InputStyle.rowHeightHeader - titleText: qsTr("Projects") - - onBack: { - if (projectsPanel.activeProjectPath) { - projectsPanel.visible = false - stackView.clearStackAndClose() - } - } - withBackButton: projectsPanel.activeProjectPath - - Item { - id: avatar - width: InputStyle.rowHeightHeader * 0.8 - height: InputStyle.rowHeightHeader - anchors.right: parent.right - anchors.rightMargin: projectsPanel.panelMargin - - Rectangle { - id: avatarImage - anchors.centerIn: parent - width: avatar.width - height: avatar.width - color: InputStyle.fontColor - radius: width*0.5 - antialiasing: true - - MouseArea { - anchors.fill: parent - onClicked: { - if (__merginApi.userAuth.hasAuthData() && __merginApi.apiVersionStatus === MerginApiStatus.OK) { - __merginApi.getUserInfo() - stackView.push( accountPanelComp) - reloadList.visible = false - } - else - myProjectsBtn.activated() // open auth form - } - } - - Image { - id: userIcon - anchors.centerIn: avatarImage - source: 'account.svg' - height: avatarImage.height * 0.8 - width: height - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - } - - ColorOverlay { - anchors.fill: userIcon - source: userIcon - color: "#FFFFFF" - } - } - } - } - - SearchBar { - id: searchBar - y: header.height - allowTimer: true - - onSearchTextChanged: { - if (toolbar.highlighted === homeBtn.text) { - __projectsModel.searchExpression = text - } else if (toolbar.highlighted === exploreBtn.text) { - // Filtered by request - exploreBtn.activated() - } else if (toolbar.highlighted === sharedProjectsBtn.text) { - __merginProjectsModel.searchExpression = text - } else if (toolbar.highlighted === myProjectsBtn.text) { - __merginProjectsModel.searchExpression = text - } - } - } - - // Content - ColumnLayout { - id: contentLayout - height: projectsPanel.height-header.height-searchBar.height-toolbar.height - width: parent.width - y: header.height + searchBar.height - spacing: 0 - - // Info label - Item { - id: infoLabel - width: parent.width - height: toolbar.highlighted === exploreBtn.text ? projectsPanel.rowHeight * 2 : 0 - visible: height - - Text { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - color: InputStyle.panelBackgroundDarker - font.pixelSize: InputStyle.fontPixelSizeNormal - text: qsTr("Explore public projects.") - visible: parent.height - } - - // To not propagate click on canvas on background - MouseArea { - anchors.fill: parent - } - - Item { - id: infoLabelHideBtn - height: projectsPanel.iconSize - width: height - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: projectsPanel.panelMargin - anchors.topMargin: projectsPanel.panelMargin - - MouseArea { - anchors.fill: parent - onClicked: infoLabel.visible = false - } - - Image { - id: infoLabelHide - anchors.centerIn: infoLabelHideBtn - source: 'no.svg' - height: infoLabelHideBtn.height - width: height - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - } - - ColorOverlay { - anchors.fill: infoLabelHide - source: infoLabelHide - color: InputStyle.panelBackgroundDark - } - } - - Rectangle { - id: borderLine - color: InputStyle.panelBackground2 - width: parent.width - height: 1 * QgsQuick.Utils.dp - anchors.bottom: parent.bottom - } - } - - ListView { - id: grid - Layout.fillWidth: true - Layout.fillHeight: true - contentWidth: grid.width - clip: true - visible: !showMergin - maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity - - property int cellWidth: width - property int cellHeight: projectsPanel.rowHeight - property int borderWidth: 1 - - delegate: delegateItem - - footer: DelegateButton { - height: projectsPanel.rowHeight - width: parent.width - text: qsTr("Create project") - onClicked: { - if (__inputUtils.hasStoragePermission()) { - stackView.push(projectWizardComp) - } else if (__inputUtils.acquireStoragePermission()) { - restartAppDialog.open() - } - } - } - - Text { - id: noProjectsText - anchors.fill: parent - textFormat: Text.RichText - text: "" + - qsTr("No downloaded projects found.%1Learn %2how to create projects%3 and %4download them%3 onto your device.") - .arg("
") - .arg("") - .arg("") - .arg("") - - onLinkActivated: Qt.openUrlExternally(link) - visible: grid.count === 0 && !storagePermissionText.visible - color: InputStyle.fontColor - font.pixelSize: InputStyle.fontPixelSizeNormal - font.bold: true - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - padding: InputStyle.panelMargin/2 - } - - Text { - id: storagePermissionText - anchors.fill: parent - textFormat: Text.RichText - text: "" + - qsTr("Input needs a storage permission, %1click to grant it%2 and then restart application.") - .arg("") - .arg("") - - onLinkActivated: { - if ( __inputUtils.acquireStoragePermission() ) { - restartAppDialog.open() - } - } - visible: !__inputUtils.hasStoragePermission() - color: InputStyle.fontColor - font.pixelSize: InputStyle.fontPixelSizeNormal - font.bold: true - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - padding: InputStyle.panelMargin/2 - } - } - - ListView { - id: merginProjectsList - visible: showMergin && !busyIndicator.running - Layout.fillWidth: true - Layout.fillHeight: true - contentWidth: grid.width - clip: true - maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity - - onCountChanged: { - if (merginProjectsList.visible || __merginProjectsModel.lastPage > 1) { - merginProjectsList.positionViewAtIndex(merginProjectsList.currentIndex, ListView.End) - } - } - - property int cellWidth: width - property int cellHeight: projectsPanel.rowHeight - property int borderWidth: 1 - - Label { - anchors.fill: parent - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - visible: !merginProjectsList.contentHeight - text: reloadList.visible ? qsTr("Unable to get the list of projects.") : qsTr("No projects found!") - color: InputStyle.fontColor - font.pixelSize: InputStyle.fontPixelSizeNormal - font.bold: true - } - - delegate: delegateItemMergin - } - } - - Component { - id: delegateItem - - ProjectDelegateItem { - id: delegateItemContent - cellWidth: projectsPanel.width - cellHeight: projectsPanel.rowHeight - iconSize: projectsPanel.iconSize - width: cellWidth - height: passesFilter ? cellHeight : 0 - visible: height ? true : false - statusIconSource:"more_menu.svg" - itemMargin: projectsPanel.panelMargin - projectFullName: (projectNamespace && projectName) ? (projectNamespace + "/" + projectName) : folderName - disabled: !isValid // invalid project - highlight: { - if (disabled) return true - return path === projectsPanel.activeProjectPath ? true : false - } - - Menu { - property real menuItemHeight: projectsPanel.rowHeight * 0.8 - property bool isMerginProject: projectNamespace !== "" - id: contextMenu - height: menuItemHeight * 2 - width:Math.min( parent.width, 300 * QgsQuick.Utils.dp ) - leftMargin: Math.max(parent.width - width, 0) - - //! sets y-offset either above or below related item according relative position to end of the list - onAboutToShow: { - var itemRelativeY = parent.y - grid.contentY - if (itemRelativeY + contextMenu.height >= grid.height) - contextMenu.y = -contextMenu.height - else - contextMenu.y = parent.height - } - - MenuItem { - height: contextMenu.isMerginProject ? contextMenu.menuItemHeight : 0 - ExtendedMenuItem { - height: parent.height - rowHeight: parent.height - width: parent.width - contentText: qsTr("Status") - imageSource: InputStyle.infoIcon - overlayImage: true - } - onClicked: { - if (__merginProjectStatusModel.loadProjectInfo(delegateItemContent.projectFullName)) { - stackView.push(statusPanelComp) - } else __inputUtils.showNotification(qsTr("No Changes")) - } - } - - MenuItem { - height: contextMenu.isMerginProject ? 0: contextMenu.menuItemHeight - ExtendedMenuItem { - height: parent.height - rowHeight: parent.height - width: parent.width - contentText: contextMenu.isMerginProject ? qsTr("Detach from Mergin") : qsTr("Upload to Mergin") - imageSource: contextMenu.isMerginProject ? InputStyle.detachIcon : InputStyle.uploadIcon - overlayImage: true - } - onClicked: { - if (!contextMenu.isMerginProject) { - __merginApi.migrateProjectToMergin(projectName) - } - } - } - - MenuItem { - height: contextMenu.menuItemHeight - ExtendedMenuItem { - height: parent.height - rowHeight: parent.height - width: parent.width - contentText: qsTr("Remove from device") - imageSource: InputStyle.removeIcon - overlayImage: true - } - onClicked: { - deleteDialog.relatedProjectIndex = index - deleteDialog.open() - } - } - } - - onItemClicked: { - if (showMergin) return - - projectsPanel.activeProjectIndex = index - projectsPanel.visible = false - } - - onMenuClicked:contextMenu.open() - } - } - - Component { - id: delegateItemMergin - - ProjectDelegateItem { - cellWidth: projectsPanel.width - cellHeight: projectsPanel.rowHeight - width: cellWidth - height: passesFilter ? cellHeight : 0 - visible: height ? true : false - pending: pendingProject - statusIconSource: getStatusIcon(status, pendingProject) - iconSize: projectsPanel.iconSize - projectFullName: __merginApi.getFullProjectName(projectNamespace, projectName) - progressValue: syncProgress - isAdditional: status === "nonProjectItem" - - onMenuClicked: { - if (status === "upToDate") return - - if (pendingProject) { - if (status === "modified") { - __merginApi.uploadCancel(projectFullName) - } - if (status === "noVersion" || status === "outOfDate") { - __merginApi.updateCancel(projectFullName) - } - return - } - - if ( !__inputUtils.hasStoragePermission() ) { - if ( __inputUtils.acquireStoragePermission() ) - restartAppDialog.open() - return - } - - if (status === "noVersion" || status === "outOfDate") { - var withoutAuth = !__merginApi.userAuth.hasAuthData() && toolbar.highlighted === exploreBtn.text - __merginApi.updateProject(projectNamespace, projectName, withoutAuth) - } else if (status === "modified") { - __merginApi.uploadProject(projectNamespace, projectName) - } - } - - onDelegateButtonClicked: { - var flag = "" - var searchText = "" - if (toolbar.highlighted == myProjectsBtn.text) { - flag = "created" - } else if (toolbar.highlighted == sharedProjectsBtn.text) { - flag = "shared" - } else if (toolbar.highlighted == exploreBtn.text) { - searchText = searchBar.text - } - - // Note that current index used to save last item position - merginProjectsList.currentIndex = merginProjectsList.count - 1 - __merginApi.listProjects(searchText, flag, "", __merginProjectsModel.lastPage + 1) - } - - } - } - - // Toolbar - Rectangle { - property int itemSize: toolbar.height * 0.8 - property string highlighted: homeBtn.text - - id: toolbar - height: InputStyle.rowHeightHeader - width: parent.width - anchors.bottom: parent.bottom - color: InputStyle.clrPanelBackground - - MouseArea { - anchors.fill: parent - onClicked: {} // dont do anything, just do not let click event propagate - } - - onHighlightedChanged: { - searchBar.deactivate() - if (toolbar.highlighted === homeBtn.text) { - __projectsModel.searchExpression = "" - } else { - __merginApi.pingMergin() - } - } - - Row { - height: toolbar.height - width: parent.width - anchors.bottom: parent.bottom - - Item { - width: parent.width/parent.children.length - height: parent.height - - MainPanelButton { - - id: homeBtn - width: toolbar.itemSize - text: qsTr("Home") - imageSource: "home.svg" - faded: toolbar.highlighted !== homeBtn.text - - onActivated: { - toolbar.highlighted = homeBtn.text; - showMergin = false - } - } - } - - Item { - width: parent.width/parent.children.length - height: parent.height - MainPanelButton { - id: myProjectsBtn - width: toolbar.itemSize - text: qsTr("My projects") - imageSource: "account.svg" - faded: toolbar.highlighted !== myProjectsBtn.text - - onActivated: { - toolbar.highlighted = myProjectsBtn.text - stackView.pending = true - showMergin = true - __merginApi.listProjects("", "created") - } - } - } - - Item { - width: parent.width/parent.children.length - height: parent.height - MainPanelButton { - id: sharedProjectsBtn - width: toolbar.itemSize - text: parent.width > sharedProjectsBtn.width * 2 ? qsTr("Shared with me") : qsTr("Shared") - imageSource: "account-multi.svg" - faded: toolbar.highlighted !== sharedProjectsBtn.text - - onActivated: { - toolbar.highlighted = sharedProjectsBtn.text - stackView.pending = true - showMergin = true - __merginApi.listProjects("", "shared") - } - } - } - - Item { - width: parent.width/parent.children.length - height: parent.height - MainPanelButton { - id: exploreBtn - width: toolbar.itemSize - text: qsTr("Explore") - imageSource: "explore.svg" - faded: toolbar.highlighted !== exploreBtn.text - - onActivated: { - toolbar.highlighted = exploreBtn.text - stackView.pending = true - showMergin = true - __merginApi.listProjects( searchBar.text ) - } - } - } - } - } - - // Other components - MessageDialog { - id: deleteDialog - visible: false - property int relatedProjectIndex - - title: qsTr( "Remove project" ) - text: qsTr( "Any unsynchronized changes will be lost." ) - icon: StandardIcon.Warning - standardButtons: StandardButton.Ok | StandardButton.Cancel - - //! Using onButtonClicked instead of onAccepted,onRejected which have been called twice - onButtonClicked: { - if (clickedButton === StandardButton.Ok) { - if (relatedProjectIndex < 0) { - return; - } - __projectsModel.deleteProject(relatedProjectIndex) - if (projectsPanel.activeProjectIndex === relatedProjectIndex) { - __loader.load("") - projectsPanel.activeProjectIndex = -1 - } - deleteDialog.relatedProjectIndex = -1 - visible = false - } - else if (clickedButton === StandardButton.Cancel) { - deleteDialog.relatedProjectIndex = -1 - visible = false - } - } - } - - MessageDialog { - id: restartAppDialog - title: qsTr( "Input needs to be restarted" ) - text: qsTr( "To apply changes after granting storage permission, Input needs to be restarted. Click close and open Input again." ) - icon: StandardIcon.Warning - visible: false - standardButtons: StandardButton.Close - onRejected: __inputUtils.quitApp() - } - - Item { - id: reloadList - width: parent.width - height: grid.cellHeight - visible: false - Layout.alignment: Qt.AlignVCenter - y: projectsPanel.height/3 * 2 - - Button { - id: reloadBtn - width: reloadList.width - 2* InputStyle.panelMargin - height: reloadList.height - text: qsTr("Retry") - font.pixelSize: reloadBtn.height/2 - anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - stackView.pending = true - // filters suppose to not change - __merginApi.listProjects( searchBar.text ) - reloadList.visible = false - } - background: Rectangle { - color: InputStyle.highlightColor - } - - contentItem: Text { - text: reloadBtn.text - font: reloadBtn.font - color: InputStyle.clrPanelMain - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - } - } - - } - - Component { - id: authPanelComp - AuthPanel { - id: authPanel - objectName: "authPanel" - visible: false - pending: stackView.pending - height: projectsPanel.height - width: projectsPanel.width - toolbarHeight: InputStyle.rowHeightHeader - onBack: { - stackView.popOnePageOrClose() - if (stackView.currentItem.objectName === "projectsPanel") { - __merginApi.authFailed() // activate homeBtn - } - } - } - } - - - Component { - id: statusPanelComp - ProjectStatusPanel { - id: statusPanel - height: projectsPanel.height - width: projectsPanel.width - visible: false - onBack: stackView.popOnePageOrClose() - } - } - - - Component { - id: accountPanelComp - - AccountPage { - id: accountPanel - height: projectsPanel.height - width: projectsPanel.width - visible: true - onBack: { - stackView.popOnePageOrClose() - } - onManagePlansClicked: { - if (__purchasing.hasInAppPurchases && (__purchasing.hasManageSubscriptionCapability || !__merginApi.userInfo.ownsActiveSubscription )) { - stackView.push( subscribePanelComp) - } else { - Qt.openUrlExternally(__purchasing.subscriptionManageUrl); - } - } - onSignOutClicked: { - if (__merginApi.userAuth.hasAuthData()) { - __merginApi.clearAuth() - stackView.popOnePageOrClose() - } - } - onRestorePurchasesClicked: { - __purchasing.restore() - } - } - } - - - Component { - id: subscribePanelComp - - SubscribePage { - id: subscribePanel - height: projectsPanel.height - width: projectsPanel.width - onBackClicked: { - stackView.popOnePageOrClose() - } - onSubscribeClicked: { - stackView.popOnePageOrClose() - } - } - } - - Component { - id: projectWizardComp - - ProjectWizardPage { - id: projectWizardPanel - objectName: "projectWizard" - height: projectsPanel.height - width: projectsPanel.width - onBack: { - stackView.popOnePageOrClose() - } - } - } - -} diff --git a/app/qml/ProjectDelegateItem.qml b/app/qml/ProjectDelegateItem.qml deleted file mode 100644 index f6d37351b..000000000 --- a/app/qml/ProjectDelegateItem.qml +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 -import lc 1.0 -import QgsQuick 0.1 as QgsQuick -import "." // import InputStyle singleton -import "./components" - -Rectangle { - id: itemContainer - color: itemContainer.highlight ? InputStyle.fontColorBright : itemContainer.primaryColor - - property color primaryColor: InputStyle.clrPanelMain - property color secondaryColor: InputStyle.fontColor - property int cellWidth: width - property int cellHeight: height - property real iconSize: height/2 - property real borderWidth: 1 * QgsQuick.Utils.dp - property bool highlight: false - property bool pending: false - property string statusIconSource: "more_menu.svg" - property string projectFullName // / - property bool disabled: false - property real itemMargin: InputStyle.panelMargin - property real progressValue: 0 - property bool isAdditional: false - - - signal itemClicked(); - signal menuClicked() - signal delegateButtonClicked() - - MouseArea { - anchors.fill: parent - onClicked: if (!disabled) itemClicked() - } - - Rectangle { - id: backgroundRect - visible: disabled - width: parent.width - height: parent.height - color: InputStyle.panelBackgroundDark - } - - Item { - width: parent.width - height: parent.height - visible: !itemContainer.isAdditional - - RowLayout { - id: row - anchors.fill: parent - anchors.leftMargin: itemContainer.itemMargin - spacing: InputStyle.panelMargin - - Item { - id: textContainer - height: itemContainer.cellHeight - Layout.fillWidth: true - - Text { - id: mainText - text: __inputUtils.formatProjectName(itemContainer.projectFullName) - height: textContainer.height/2 - width: textContainer.width - font.pixelSize: InputStyle.fontPixelSizeNormal - color: itemContainer.highlight? itemContainer.primaryColor : itemContainer.secondaryColor - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignBottom - elide: Text.ElideRight - } - - Text { - id: secondaryText - visible: !pending - height: textContainer.height/2 - text: projectInfo ? projectInfo : "" - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.top: mainText.bottom - font.pixelSize: InputStyle.fontPixelSizeSmall - color: itemContainer.highlight ? itemContainer.primaryColor : InputStyle.panelBackgroundDark - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignTop - elide: Text.ElideRight - } - - ProgressBar { - property real itemHeight: InputStyle.fontPixelSizeSmall - - id: progressBar - anchors.top: mainText.bottom - height: InputStyle.fontPixelSizeSmall - width: secondaryText.width - value: progressValue - visible: pending - - background: Rectangle { - implicitWidth: parent.width - implicitHeight: progressBar.itemHeight - color: InputStyle.panelBackgroundLight - } - - contentItem: Item { - implicitWidth: parent.width - implicitHeight: progressBar.itemHeight - - Rectangle { - width: progressBar.visualPosition * parent.width - height: parent.height - color: InputStyle.fontColor - } - } - } - - } - - Item { - id: statusContainer - height: itemContainer.cellHeight - width: height - y: 0 - - MouseArea { - anchors.fill: parent - onClicked:menuClicked() - } - - Image { - id: statusIcon - anchors.centerIn: parent - source: statusIconSource - height: itemContainer.iconSize - width: height - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - } - - ColorOverlay { - anchors.fill: statusIcon - source: statusIcon - color: itemContainer.highlight ? itemContainer.primaryColor : itemContainer.secondaryColor - } - } - } - - Rectangle { - id: borderLine - color: InputStyle.panelBackground2 - width: itemContainer.width - height: itemContainer.borderWidth - anchors.bottom: parent.bottom - } - } - - // Additional item - DelegateButton { // TODO: replace with footer property on projects listview - visible: itemContainer.isAdditional - width: itemContainer.width - height: itemContainer.height - text: qsTr("Fetch more") - - onClicked: itemContainer.delegateButtonClicked() - } - -} diff --git a/app/qml/ProjectListPage.qml b/app/qml/ProjectListPage.qml new file mode 100644 index 000000000..947e235c5 --- /dev/null +++ b/app/qml/ProjectListPage.qml @@ -0,0 +1,62 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick 2.0 +import lc 1.0 + +import "./components" + +Item { + id: root + + property int projectModelType: ProjectsModel.EmptyProjectsModel + property string activeProjectId: "" + + property alias list: projectlist + + signal openProjectRequested( string projectId, string projectFilePath ) + signal showLocalChangesRequested( string projectId ) + + function refreshProjectsList() { + searchBar.deactivate() + projectlist.refreshProjectList() + } + + SearchBar { + id: searchBar + + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: projectlist.top + } + + allowTimer: true + + onSearchTextChanged: projectlist.searchTextChanged( text ) + } + + ProjectList { + id: projectlist + + projectModelType: root.projectModelType + activeProjectId: root.activeProjectId + + anchors { + left: parent.left + right: parent.right + top: searchBar.bottom + bottom: parent.bottom + } + + onOpenProjectRequested: root.openProjectRequested( projectId, projectFilePath ) + onShowLocalChangesRequested: root.showLocalChangesRequested( projectId ) + } +} diff --git a/app/qml/ProjectPanel.qml b/app/qml/ProjectPanel.qml new file mode 100644 index 000000000..477ad49fd --- /dev/null +++ b/app/qml/ProjectPanel.qml @@ -0,0 +1,1095 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.0 +import QtQuick.Dialogs 1.2 +import QgsQuick 0.1 as QgsQuick +import lc 1.0 +import "." // import InputStyle singleton +import "./components/" + +Item { + id: root + + property string activeProjectId: "" + property string activeProjectPath: "" + + property real rowHeight: InputStyle.rowHeightHeader * 1.2 + property real panelMargin: InputStyle.panelMargin + + signal openProjectRequested( string projectId, string projectPath ) + + function openPanel() { + root.visible = true + stackView.visible = true + } + + function hidePanel() { + root.visible = false + stackView.clearStackAndClose() + } + + visible: false + focus: true + + onFocusChanged: { // pass focus to stackview + stackView.focus = true + } + + StackView { + id: stackView + + initialItem: projectsPanelComp + anchors.fill: parent + focus: true + visible: false + z: root.z + 1 + property bool pending: false + + function clearStackAndClose() { + if ( stackView.depth > 1 ) + stackView.pop( null ) // pops everything besides an initialItem + + stackView.visible = false + } + + function popOnePageOrClose() { + if ( stackView.depth > 1 ) + { + stackView.pop() + } + } + + Keys.onReleased: { + if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { + event.accepted = true; + + if (stackView.depth > 1) { + stackView.currentItem.back() + } + else if (root.activeProjectPath) { + stackView.clearStackAndClose() + root.visible = false + } + } + } + + onVisibleChanged: { + if ( stackView.visible ) + stackView.forceActiveFocus() + } + } + + BusyIndicator { + id: busyIndicator + width: parent.width/8 + height: width + running: stackView.pending + visible: running + anchors.centerIn: parent + z: parent.z + 1 + } + + Component { + id: projectsPanelComp + + Page { + id: projectsPage + + function setupProjectOpen( projectId, projectPath ) { + activeProjectId = projectId + activeProjectPath = projectPath + openProjectRequested( projectId, projectPath ) + + if ( projectId && projectPath ) // this is not project reset + hidePanel() + } + + function showChanges( projectId ) { + if ( __merginProjectStatusModel.loadProjectInfo( projectId ) ) { + stackView.push( statusPanelComp ) + } + else __inputUtils.showNotification( qsTr( "No Changes" ) ) + } + + function refreshProjectList() { + + stackView.pending = true + switch( pageContent.state ) { + case "local": + localProjectsPage.refreshProjectsList() + break + case "created": + createdProjectsPage.refreshProjectsList() + break + case "shared": + sharedProjectsPage.refreshProjectsList() + break + case "public": + publicProjectsPage.refreshProjectsList() + break + } + } + + header: PanelHeader { + id: pageHeader + + titleText: qsTr("Projects") + color: InputStyle.clrPanelMain + height: InputStyle.rowHeightHeader + rowHeight: InputStyle.rowHeightHeader + + onBack: { + if ( root.activeProjectId ) { + root.hidePanel() + } + } + withBackButton: root.activeProjectPath + + Item { + id: avatar + + width: InputStyle.rowHeightHeader * 0.8 + height: InputStyle.rowHeightHeader + anchors.right: parent.right + anchors.rightMargin: root.panelMargin + + Rectangle { + id: avatarImage + + anchors.centerIn: parent + width: avatar.width + height: avatar.width + color: InputStyle.fontColor + radius: width*0.5 + antialiasing: true + + MouseArea { + anchors.fill: parent + onClicked: { + if (__merginApi.userAuth.hasAuthData() && __merginApi.apiVersionStatus === MerginApiStatus.OK) { + __merginApi.getUserInfo() + stackView.push( accountPanelComp ) + reloadList.visible = false + } + else + stackView.push( authPanelComp, { state: "login" }) + } + } + + Image { + id: userIcon + + anchors.centerIn: avatarImage + source: 'account.svg' + height: avatarImage.height * 0.8 + width: height + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectFit + } + + ColorOverlay { + anchors.fill: userIcon + source: userIcon + color: "#FFFFFF" + } + } + } + } + + background: Rectangle { + anchors.fill: parent + color: InputStyle.clrPanelMain + } + + Item { + id: pageContent + + anchors.fill: parent + + states: [ + State { + name: "local" + }, + State { + name: "created" + }, + State { + name: "shared" + }, + State { + name: "public" + } + ] + + onStateChanged: { + refreshProjectList() + console.log("New state: ", pageContent.state) + } + + StackLayout { + id: projectListLayout + + anchors.fill: parent + currentIndex: pageFooter.currentIndex + + ProjectListPage { + id: localProjectsPage + + projectModelType: ProjectsModel.LocalProjectsModel + activeProjectId: root.activeProjectId + list.visible: !stackView.pending + + onOpenProjectRequested: setupProjectOpen( projectId, projectFilePath ) + onShowLocalChangesRequested: showChanges( projectId ) + list.onActiveProjectDeleted: setupProjectOpen( "", "" ) + } + + ProjectListPage { + id: createdProjectsPage + + projectModelType: ProjectsModel.CreatedProjectsModel + activeProjectId: root.activeProjectId + list.visible: !stackView.pending + + onOpenProjectRequested: setupProjectOpen( projectId, projectFilePath ) + onShowLocalChangesRequested: showChanges( projectId ) + list.onActiveProjectDeleted: setupProjectOpen( "", "" ) + } + + ProjectListPage { + id: sharedProjectsPage + + projectModelType: ProjectsModel.SharedProjectsModel + activeProjectId: root.activeProjectId + list.visible: !stackView.pending + + onOpenProjectRequested: setupProjectOpen( projectId, projectFilePath ) + onShowLocalChangesRequested: showChanges( projectId ) + list.onActiveProjectDeleted: setupProjectOpen( "", "" ) + } + + ProjectListPage { + id: publicProjectsPage + + projectModelType: ProjectsModel.PublicProjectsModel + activeProjectId: root.activeProjectId + list.visible: !stackView.pending + + onOpenProjectRequested: setupProjectOpen( projectId, projectFilePath ) + onShowLocalChangesRequested: showChanges( projectId ) + list.onActiveProjectDeleted: setupProjectOpen( "", "" ) + } + } + } + + footer: TabBar { + id: pageFooter + + property int itemSize: pageFooter.height * 0.8 + + spacing: 0 + contentHeight: InputStyle.rowHeightHeader + + TabButton { + id: localProjectsBtn + + background: Rectangle { + anchors.fill: parent + color: InputStyle.fontColor + } + + MainPanelButton { + id: localProjectsInnerBtn + + text: qsTr("Home") + imageSource: "home.svg" + width: pageFooter.itemSize + + handleClicks: false + faded: pageFooter.currentIndex !== localProjectsBtn.TabBar.index + } + + onClicked: pageContent.state = "local" + } + + TabButton { + id: createdProjectsBtn + + background: Rectangle { + anchors.fill: parent + color: InputStyle.fontColor + } + + MainPanelButton { + id: createdProjectsInnerBtn + + text: qsTr("My projects") + imageSource: "account.svg" + width: pageFooter.itemSize + + handleClicks: false + faded: pageFooter.currentIndex !== createdProjectsBtn.TabBar.index + } + + onClicked: pageContent.state = "created" + } + + TabButton { + id: sharedProjectsBtn + + background: Rectangle { + anchors.fill: parent + color: InputStyle.fontColor + } + + MainPanelButton { + id: sharedProjectsInnerBtn + + imageSource: "account-multi.svg" + width: pageFooter.itemSize + text: parent.width > sharedProjectsInnerBtn.width * 2 ? qsTr("Shared with me") : qsTr("Shared") + + handleClicks: false + faded: pageFooter.currentIndex !== sharedProjectsBtn.TabBar.index + } + + onClicked: pageContent.state = "shared" + } + + TabButton { + id: publicProjectsBtn + + background: Rectangle { + anchors.fill: parent + color: InputStyle.fontColor + } + + MainPanelButton { + id: publicProjectsInnerBtn + + text: qsTr("Explore") + imageSource: "explore.svg" + width: pageFooter.itemSize + + handleClicks: false + faded: pageFooter.currentIndex !== publicProjectsBtn.TabBar.index + } + + onClicked: pageContent.state = "public" + } + } + +// SearchBar { +// id: searchBar +// y: pageHeader.height +// allowTimer: true + +// onSearchTextChanged: { +// if (toolbar.highlighted === homeBtn.text) { +// __localProjectsProxyModel.searchExpression = text +// } else if (toolbar.highlighted === exploreBtn.text) { +// // Filtered by request +// exploreBtn.activated() +// } else if (toolbar.highlighted === sharedProjectsBtn.text) { +// __merginProjectsModel.searchExpression = text +// } else if (toolbar.highlighted === myProjectsBtn.text) { +// __merginProjectsModel.searchExpression = text +// } +// } +// } + +// // Content +// ColumnLayout { +// id: contentLayout +// height: projectsPanel.height-pageHeader.height-searchBar.height-toolbar.height +// width: parent.width +// y: pageHeader.height + searchBar.height +// spacing: 0 + + // Info label +// Item { +// id: infoLabel +// width: parent.width +// height: toolbar.highlighted === exploreBtn.text ? projectsPanel.rowHeight * 2 : 0 +// visible: height + +// Text { +// anchors.horizontalCenter: parent.horizontalCenter +// anchors.verticalCenter: parent.verticalCenter +// horizontalAlignment: Text.AlignHCenter +// verticalAlignment: Text.AlignVCenter +// wrapMode: Text.WordWrap +// color: InputStyle.panelBackgroundDarker +// font.pixelSize: InputStyle.fontPixelSizeNormal +// text: qsTr("Explore public projects.") +// visible: parent.height +// } + +// // To not propagate click on canvas on background +// MouseArea { +// anchors.fill: parent +// } + +// Item { +// id: infoLabelHideBtn +// height: projectsPanel.iconSize +// width: height +// anchors.right: parent.right +// anchors.top: parent.top +// anchors.rightMargin: projectsPanel.panelMargin +// anchors.topMargin: projectsPanel.panelMargin + +// MouseArea { +// anchors.fill: parent +// onClicked: infoLabel.visible = false +// } + +// Image { +// id: infoLabelHide +// anchors.centerIn: infoLabelHideBtn +// source: 'no.svg' +// height: infoLabelHideBtn.height +// width: height +// sourceSize.width: width +// sourceSize.height: height +// fillMode: Image.PreserveAspectFit +// } + +// ColorOverlay { +// anchors.fill: infoLabelHide +// source: infoLabelHide +// color: InputStyle.panelBackgroundDark +// } +// } + +// Rectangle { +// id: borderLine +// color: InputStyle.panelBackground2 +// width: parent.width +// height: 1 * QgsQuick.Utils.dp +// anchors.bottom: parent.bottom +// } +// } + +// ProjectListPage { +// id: localProjectsList + + +// } + +// ListView { +// id: grid +// Layout.fillWidth: true +// Layout.fillHeight: true +// contentWidth: grid.width +// clip: true +// visible: !showMergin +// maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity + +// model: ProjectsProxyModel { +// projectSourceModel: ProjectsModel { +// id: myModel +// localProjectsManager: __localProjectsManager +// modelType: ProjectsModel.LocalProjectsModel +// merginApi: __merginApi +// } +// } + +// property int cellWidth: width +// property int cellHeight: projectsPanel.rowHeight +// property int borderWidth: 1 + +// delegate: delegateItem + +// footer: DelegateButton { +// height: projectsPanel.rowHeight +// width: parent.width +// text: qsTr("Create project") +// onClicked: { +// if (__inputUtils.hasStoragePermission()) { +// stackView.push(projectWizardComp) +// } else if (__inputUtils.acquireStoragePermission()) { +// restartAppDialog.open() +// } +// } +// } + +// Text { +// id: noProjectsText +// anchors.fill: parent +// textFormat: Text.RichText +// text: "" + +// qsTr("No downloaded projects found.%1Learn %2how to create projects%3 and %4download them%3 onto your device.") +// .arg("
") +// .arg("") +// .arg("") +// .arg("") + +// onLinkActivated: Qt.openUrlExternally(link) +// visible: grid.count === 0 && !storagePermissionText.visible +// color: InputStyle.fontColor +// font.pixelSize: InputStyle.fontPixelSizeNormal +// font.bold: true +// verticalAlignment: Text.AlignVCenter +// horizontalAlignment: Text.AlignHCenter +// wrapMode: Text.WordWrap +// padding: InputStyle.panelMargin/2 +// } + +// Text { +// id: storagePermissionText +// anchors.fill: parent +// textFormat: Text.RichText +// text: "" + +// qsTr("Input needs a storage permission, %1click to grant it%2 and then restart application.") +// .arg("") +// .arg("") + +// onLinkActivated: { +// if ( __inputUtils.acquireStoragePermission() ) { +// restartAppDialog.open() +// } +// } +// visible: !__inputUtils.hasStoragePermission() +// color: InputStyle.fontColor +// font.pixelSize: InputStyle.fontPixelSizeNormal +// font.bold: true +// verticalAlignment: Text.AlignVCenter +// horizontalAlignment: Text.AlignHCenter +// wrapMode: Text.WordWrap +// padding: InputStyle.panelMargin/2 +// } +// } + +// ListView { +// id: merginProjectsList + +// property int paginatedPage: 0 + +// visible: showMergin && !busyIndicator.running +// Layout.fillWidth: true +// Layout.fillHeight: true +// contentWidth: grid.width +// clip: true +// maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity + +// onCountChanged: { +// if (merginProjectsList.visible || paginatedPage > 1) { +// merginProjectsList.positionViewAtIndex(merginProjectsList.currentIndex, ListView.End) +// } +// } + +// property int cellWidth: width +// property int cellHeight: projectsPanel.rowHeight +// property int borderWidth: 1 + +// TODO: unable to get list of the projects +// Label { +// anchors.fill: parent +// horizontalAlignment: Qt.AlignHCenter +// verticalAlignment: Qt.AlignVCenter +// visible: !merginProjectsList.contentHeight +// text: reloadList.visible ? qsTr("Unable to get the list of projects.") : qsTr("No projects found!") +// color: InputStyle.fontColor +// font.pixelSize: InputStyle.fontPixelSizeNormal +// font.bold: true +// } + +// delegate: delegateItemMergin + +// footer: Button { +// text: "ListProjects API" +// onClicked: { +// __myProjectsModel.listProjects(merginProjectsList.paginatedPage + 1) +// merginProjectsList.paginatedPage++ +// } +// } +// } + +// } + +// Component { +// id: delegateItem + +// ProjectDelegateItem { +// id: delegateItemContent + +// projectFullName: model.ProjectFullName +// projectId: model.ProjectId +// projectDescription: model.ProjectDescription +// projectStatus: model.ProjectSyncStatus +// projectIsValid: model.ProjectIsValid +// projectIsPending: model.ProjectPending ? true : false +// projectSyncProgress: model.ProjectSyncProgress ? ProjectSyncProgress : 0 +// projectIsLocal: model.ProjectIsLocal +// projectIsMergin: model.ProjectIsMergin + +// width: parent.width +// height: projectsPanel.rowHeight +// viewContentY: ListView.view.contentY +// viewHeight: ListView.view.height + +// onOpenRequested: console.log( "PMR: Open", projectId ) +// onSyncRequested: console.log( "PMR: Sync", projectId ) +// onMigrateRequested: console.log( "PMR: Upload", projectId ) +// onRemoveRequested: console.log( "PMR: Remove", projectId ) +// onStopSyncRequested: console.log( "PMR: Stop", projectId ) +// onShowChangesRequested: console.log( "PMR: Show changes", projectId ) +// } +// } + +// ProjectDelegateItem { +// id: delegateItemContent +// cellWidth: projectsPanel.width +// cellHeight: projectsPanel.rowHeight +// iconSize: projectsPanel.iconSize +// width: cellWidth +// height: cellHeight +// visible: height ? true : false +// statusIconSource: "more_menu.svg" +// itemMargin: projectsPanel.panelMargin +// projectFullName: ProjectFullName +// projectDescription: getStatusIcon( ProjectSyncStatus, ProjectPending ) +// disabled: !ProjectIsValid // invalid project +// highlight: { +// if (disabled) return true +// return ProjectFilePath === projectsPanel.activeProjectPath ? true : false +// } + +// Menu { +// property real menuItemHeight: projectsPanel.rowHeight * 0.8 +// id: contextMenu +// height: menuItemHeight * 2 +// width:Math.min( parent.width, 300 * QgsQuick.Utils.dp ) +// leftMargin: Math.max(parent.width - width, 0) + +// //! sets y-offset either above or below related item according relative position to end of the list +// onAboutToShow: { +// var itemRelativeY = parent.y - grid.contentY +// if (itemRelativeY + contextMenu.height >= grid.height) +// contextMenu.y = -contextMenu.height +// else +// contextMenu.y = parent.height +// } + +// MenuItem { +// height: ProjectIsMergin ? contextMenu.menuItemHeight : 0 +// ExtendedMenuItem { +// height: parent.height +// rowHeight: parent.height +// width: parent.width +// contentText: qsTr("Status") +// imageSource: InputStyle.infoIcon +// overlayImage: true +// } +// onClicked: { +// if (__merginProjectStatusModel.loadProjectInfo(delegateItemContent.projectFullName)) { +// stackView.push(statusPanelComp) +// } else __inputUtils.showNotification(qsTr("No Changes")) +// } +// } + +// MenuItem { +// height: ProjectIsMergin ? 0: contextMenu.menuItemHeight +// ExtendedMenuItem { +// height: parent.height +// rowHeight: parent.height +// width: parent.width +// contentText: qsTr("Upload to Mergin") +// imageSource: InputStyle.uploadIcon +// overlayImage: true +// } +// onClicked: { +// if (!ProjectIsMergin) { +// __merginApi.migrateProjectToMergin(projectName) +// } +// } +// } + +// MenuItem { +// height: contextMenu.menuItemHeight +// ExtendedMenuItem { +// height: parent.height +// rowHeight: parent.height +// width: parent.width +// contentText: qsTr("Remove from device") +// imageSource: InputStyle.removeIcon +// overlayImage: true +// } +// onClicked: { +// deleteDialog.relatedProjectDirectory = ProjectDirectory +// deleteDialog.open() +// } +// } +// } + +// onItemClicked: { +// if (showMergin) return + +// projectsPanel.activeProjectIndex = index +// projectsPanel.visible = false +// } + +// onMenuClicked:contextMenu.open() +// } + +// Component { +// id: delegateItemMergin + +// Rectangle { +// width: 20 +// height: 20 +// color: "green" +// } + +// ProjectDelegateItem { +// cellWidth: projectsPanel.width +// cellHeight: projectsPanel.rowHeight +// width: cellWidth +// height: cellHeight +// visible: height ? true : false +// pending: ProjectPending +// statusIconSource: getStatusIcon(ProjectSyncStatus, ProjectPending) +// iconSize: projectsPanel.iconSize +// projectFullName: ProjectFullName +// progressValue: ProjectSyncProgress +// projectDescription: ProjectDescription +// isAdditional: __myProjectsModel.canFetchMore() // TODO: replace with delegate button on listview footer + +// onMenuClicked: { +// if ( !__inputUtils.hasStoragePermission() ) { +// if ( __inputUtils.acquireStoragePermission() ) +// restartAppDialog.open() // TODO: replace with reload data! +// return +// } + +// if (ProjectSyncStatus === ProjectStatus.UpToDate) return + +// if ( ProjectPending ) { +// __myProjectsModel.stopProjectSync(ProjectNamespace, ProjectName) +// return +// } + +// __myProjectsModel.syncProject(ProjectNamespace, ProjectName) +// } + +// onDelegateButtonClicked: { // TODO: replace with footer property +// var searchText = searchBar.text + +// // Note that current index used to save last item position +// merginProjectsList.currentIndex = merginProjectsList.count - 1 // TODO: huh? + +// __myProjectsModel.listProjects(merginProjectsList.paginatedPage + 1, searchText) +// } + +// } +// } + + + // Toolbar +// Rectangle { +// property int itemSize: toolbar.height * 0.8 +// property string highlighted: homeBtn.text + +// id: toolbar +// height: InputStyle.rowHeightHeader +// width: parent.width +// anchors.bottom: parent.bottom +// color: InputStyle.clrPanelBackground +// visible: false + +// MouseArea { +// anchors.fill: parent +// onClicked: {} // dont do anything, just do not let click event propagate +// } + +// onHighlightedChanged: { +//// searchBar.deactivate() +// if (toolbar.highlighted === homeBtn.text) { +//// __projectsModel.searchExpression = "" +// } else { +// __merginApi.pingMergin() +// } +// } + +// Row { +// height: toolbar.height +// width: parent.width +// anchors.bottom: parent.bottom + +// Item { +// width: parent.width/parent.children.length +// height: parent.height + +// MainPanelButton { + +// id: homeBtn +// width: toolbar.itemSize +// text: qsTr("Home") +// imageSource: "home.svg" +// faded: toolbar.highlighted !== homeBtn.text + +// onActivated: { +// toolbar.highlighted = homeBtn.text; +// showMergin = false +//// stackView.pending = true +// myModel.listProjectsByName() +// } +// } +// } + +// Item { +// width: parent.width/parent.children.length +// height: parent.height +// MainPanelButton { +// id: myProjectsBtn +// width: toolbar.itemSize +// text: qsTr("My projects") +// imageSource: "account.svg" +// faded: toolbar.highlighted !== myProjectsBtn.text + +// onActivated: { +// toolbar.highlighted = myProjectsBtn.text +// stackView.pending = true +// showMergin = true +// __myProjectsModel.listProjects() +// } +// } +// } + +// Item { +// width: parent.width/parent.children.length +// height: parent.height +// MainPanelButton { +// id: sharedProjectsBtn +// width: toolbar.itemSize +// text: parent.width > sharedProjectsBtn.width * 2 ? qsTr("Shared with me") : qsTr("Shared") +// imageSource: "account-multi.svg" +// faded: toolbar.highlighted !== sharedProjectsBtn.text + +// onActivated: { +// toolbar.highlighted = sharedProjectsBtn.text +// stackView.pending = true +// showMergin = true +// __merginApi.listProjects("", "shared") +// } +// } +// } + +// Item { +// width: parent.width/parent.children.length +// height: parent.height +// MainPanelButton { +// id: exploreBtn +// width: toolbar.itemSize +// text: qsTr("Explore") +// imageSource: "explore.svg" +// faded: toolbar.highlighted !== exploreBtn.text + +// onActivated: { +// toolbar.highlighted = exploreBtn.text +// stackView.pending = true +// showMergin = true +// __merginApi.listProjects( searchBar.text ) +// } +// } +// } +// } +// } + + // Other components + + Item { + id: reloadList + width: parent.width + height: root.rowHeight + visible: false + Layout.alignment: Qt.AlignVCenter + y: root.height/3 * 2 + + Button { + id: reloadBtn + width: reloadList.width - 2* InputStyle.panelMargin + height: reloadList.height + text: qsTr("Retry") + font.pixelSize: reloadBtn.height/2 + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + stackView.pending = true + // filters suppose to not change + __merginApi.listProjects( searchBar.text ) + reloadList.visible = false + } + background: Rectangle { + color: InputStyle.highlightColor + } + + contentItem: Text { + text: reloadBtn.text + font: reloadBtn.font + color: InputStyle.clrPanelMain + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + } + + Connections { + target: __projectWizard + onProjectCreated: { + if (stackView.currentItem.objectName === "projectWizard") { + stackView.popOnePageOrClose() + } + } + } + + Connections { + target: __merginApi + onListProjectsFinished: { + stackView.pending = false + } + onListProjectsFailed: { + reloadList.visible = true + } + onListProjectsByNameFinished: stackView.pending = false + onApiVersionStatusChanged: { + stackView.pending = false + if (__merginApi.apiVersionStatus === MerginApiStatus.OK && stackView.currentItem.objectName === "authPanel") { + if (__merginApi.userAuth.hasAuthData()) { + refreshProjectList() + } else if (toolbar.highlighted !== homeBtn.text) { + if (stackView.currentItem.objectName !== "authPanel") { + stackView.push(authPanelComp, {state: "login"}) + } + } + } + } + onAuthRequested: { + stackView.pending = false + stackView.push(authPanelComp, {state: "login"}) + } + onAuthChanged: { + stackView.pending = false + if (__merginApi.userAuth.hasAuthData()) { + refreshProjectList() + root.forceActiveFocus() + } + stackView.popOnePageOrClose() + + } + onAuthFailed: { + homeBtn.activated() + stackView.pending = false + root.forceActiveFocus() + } + onRegistrationFailed: stackView.pending = false + onRegistrationSucceeded: stackView.pending = false + + //TODO: on sync project failed: push auth panel too + } + } + } + + Component { + id: authPanelComp + AuthPanel { + id: authPanel + objectName: "authPanel" + visible: false + pending: stackView.pending + height: root.height + width: root.width + toolbarHeight: InputStyle.rowHeightHeader + onBack: { + stackView.popOnePageOrClose() + if (stackView.currentItem.objectName === "projectsPanel") { + __merginApi.authFailed() // activate homeBtn + } + } + } + } + + Component { + id: statusPanelComp + ProjectStatusPanel { + id: statusPanel + height: root.height + width: root.width + visible: false + onBack: stackView.popOnePageOrClose() + } + } + + Component { + id: accountPanelComp + + AccountPage { + id: accountPanel + height: root.height + width: root.width + visible: true + onBack: { + stackView.popOnePageOrClose() + } + onManagePlansClicked: { + if (__purchasing.hasInAppPurchases && (__purchasing.hasManageSubscriptionCapability || !__merginApi.userInfo.ownsActiveSubscription )) { + stackView.push( subscribePanelComp) + } else { + Qt.openUrlExternally(__purchasing.subscriptionManageUrl); + } + } + onSignOutClicked: { + if (__merginApi.userAuth.hasAuthData()) { + __merginApi.clearAuth() + stackView.popOnePageOrClose() + } + } + onRestorePurchasesClicked: { + __purchasing.restore() + } + } + } + + Component { + id: subscribePanelComp + + SubscribePage { + id: subscribePanel + height: root.height + width: root.width + onBackClicked: { + stackView.popOnePageOrClose() + } + onSubscribeClicked: { + stackView.popOnePageOrClose() + } + } + } + + Component { + id: projectWizardComp + + ProjectWizardPage { + id: projectWizardPanel + objectName: "projectWizard" + height: root.height + width: root.width + onBack: { + stackView.popOnePageOrClose() + } + } + } + +} diff --git a/app/qml/components/ProjectDelegateItem.qml b/app/qml/components/ProjectDelegateItem.qml new file mode 100644 index 000000000..9611f11a8 --- /dev/null +++ b/app/qml/components/ProjectDelegateItem.qml @@ -0,0 +1,367 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtGraphicalEffects 1.0 +import QtQml.Models 2.14 + +import QgsQuick 0.1 as QgsQuick +import lc 1.0 +import "../" + +Rectangle { + id: root + + // Required properties + property string projectFullName + property string projectId + property string projectDescription + property int projectStatus + property bool projectIsValid + property bool projectIsPending + property real projectSyncProgress + property bool projectIsLocal + property bool projectIsMergin + property string projectRemoteError + + property color primaryColor: InputStyle.clrPanelMain + property color secondaryColor: InputStyle.fontColor + property real itemMargin: InputStyle.panelMargin + + property real iconSize: height * 0.3 + property real borderWidth: 1 * QgsQuick.Utils.dp + property real menuItemHeight: height * 0.8 + + property real viewContentY: 0 + property real viewHeight: 0 + property bool highlight: false + + signal openRequested() + signal syncRequested() + signal migrateRequested() + signal removeRequested() + signal stopSyncRequested() + signal showChangesRequested() + + function getStatusIcon() { + if ( projectIsPending ) + return InputStyle.stopIcon + + if ( projectIsLocal && projectIsMergin ) { + // downloaded mergin projects + if ( projectStatus === ProjectStatus.OutOfDate || + projectStatus === ProjectStatus.Modified ) { + return InputStyle.syncIcon + } + return "" // no icon if this project does not have changes + } + + if ( projectIsMergin && projectStatus === ProjectStatus.NoVersion ) + return InputStyle.downloadIcon + + if ( projectIsLocal && projectStatus === ProjectStatus.NoVersion ) + return InputStyle.uploadIcon + + return "" + } + + function getMoreMenuItems() { + if ( projectIsMergin && projectIsLocal ) + { + if ( ( projectStatus === ProjectStatus.OutOfDate || + projectStatus === ProjectStatus.Modified ) && + projectIsValid ) + return "sync,changes,remove" + + return "changes,remove" + } + else if ( !projectIsMergin && projectIsLocal ) + return "upload,remove" + else + return "download" + } + + function fillMoreMenu() { + // fill more menu with corresponding items + let itemsMap = { + "sync": { + "name": qsTr("Synchronize project"), + "iconSource": InputStyle.syncIcon, + "callback": () => { root.syncRequested() } + }, + "changes": { + "name": qsTr("Local changes"), + "iconSource": InputStyle.infoIcon, + "callback": () => { root.showChangesRequested() } + }, + "remove": { + "name": qsTr("Remove from device"), + "iconSource": InputStyle.removeIcon, + "callback": () => { root.removeRequested() } + }, + "upload": { + "name": qsTr("Upload to Mergin"), + "iconSource": InputStyle.uploadIcon, + "callback": () => { root.migrateRequested() } + }, + "download": { + "name": qsTr("Download from Mergin"), + "iconSource": InputStyle.downloadIcon, + "callback": () => { root.syncRequested() } + } + } + + let items = getMoreMenuItems() + items = items.split(',') + + if ( contextMenu.contentData ) + while( contextMenu.contentData.length > 0 ) + contextMenu.takeItem( 0 ); + + items.forEach( item => { contextMenu.addItem( + menuItemComponent.createObject( null, itemsMap[item] ) ) + }) + contextMenu.height = items.length * root.menuItemHeight + } + + onProjectStatusChanged: { + console.log("Project " + projectId + " status changed to: " + projectStatus) + fillMoreMenu() + } + + color: root.highlight || !projectIsValid ? InputStyle.fontColorBright : root.primaryColor + + MouseArea { + anchors.fill: parent + enabled: projectIsValid + onClicked: openRequested() + } + + Rectangle { + visible: !projectIsValid + width: parent.width + height: parent.height + color: InputStyle.panelBackgroundDark + } + + RowLayout { + id: row + + anchors.fill: parent + anchors.leftMargin: root.itemMargin + spacing: 0 + + Item { + id: textContainer + + height: root.height + Layout.fillWidth: true + + Text { + id: mainText + + text: __inputUtils.formatProjectName( projectFullName ) + height: textContainer.height/2 + width: textContainer.width + font.pixelSize: InputStyle.fontPixelSizeNormal + color: root.highlight || !projectIsValid ? root.primaryColor : root.secondaryColor + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + elide: Text.ElideRight + } + + Text { + id: secondaryText + + visible: !projectIsPending + height: textContainer.height/2 +// text: projectDescription + text: projectStatus + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.top: mainText.bottom + font.pixelSize: InputStyle.fontPixelSizeSmall + color: root.highlight || !projectIsValid ? root.primaryColor : InputStyle.panelBackgroundDark + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignTop + elide: Text.ElideRight + } + + ProgressBar { + id: progressBar + + property real itemHeight: InputStyle.fontPixelSizeSmall + + anchors.top: mainText.bottom + height: InputStyle.fontPixelSizeSmall + width: secondaryText.width + value: projectSyncProgress + visible: projectIsPending + + background: Rectangle { + implicitWidth: parent.width + implicitHeight: progressBar.itemHeight + color: InputStyle.panelBackgroundLight + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: progressBar.itemHeight + + Rectangle { + width: progressBar.visualPosition * parent.width + height: parent.height + color: InputStyle.fontColor + } + } + } + } + + Item { + id: statusIconContainer + + property string iconSource: getStatusIcon() + + visible: projectIsValid && iconSource !== "" + Layout.preferredWidth: root.height + height: root.height + + Image { + id: statusIcon + + anchors.centerIn: parent + source: statusIconContainer.iconSource + height: root.iconSize + width: height + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectFit + } + + ColorOverlay { + anchors.fill: statusIcon + source: statusIcon + color: root.highlight || !projectIsValid ? root.primaryColor : InputStyle.activeButtonColorOrange + } + + MouseArea { + anchors.fill: parent + enabled: projectIsValid + onClicked: { + if ( projectRemoteError ) { + __inputUtils.showNotification( qsTr( "Could not synchronize project, please make sure you are logged in and have sufficient rights." ) ) + return + } + if ( projectIsPending ) + stopSyncRequested() + else if ( !projectIsMergin ) + migrateRequested() + else + syncRequested() + } + } + } + + Item { + id: moreMenuContainer + + Layout.preferredWidth: root.height + height: root.height + + Image { + id: moreMenuIcon + + anchors.centerIn: parent + source: InputStyle.moreMenuIcon + height: root.iconSize + width: height + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectFit + } + + ColorOverlay { + anchors.fill: moreMenuIcon + source: moreMenuIcon + color: root.highlight || !projectIsValid ? root.primaryColor : InputStyle.activeButtonColorOrange + } + + MouseArea { + anchors.fill: parent + onClicked: contextMenu.open() + } + } + } + + Rectangle { // border line + color: InputStyle.panelBackground2 + width: root.width + height: root.borderWidth + anchors.bottom: parent.bottom + } + + // More Menu + Menu { + id: contextMenu + + width: Math.min( root.width, 300 * QgsQuick.Utils.dp ) + leftMargin: Math.max( root.width - width, 0 ) + z: 100 + + enter: Transition { + ParallelAnimation { + NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 100 } + } + } + exit: Transition { + ParallelAnimation { + NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 100 } + } + } + + Component.onCompleted: fillMoreMenu() + + //! sets y-offset either above or below related item according relative position to end of the list + onAboutToShow: { + let itemRelativeY = parent.y - root.viewContentY + if ( itemRelativeY + contextMenu.height >= root.viewHeight ) + contextMenu.y = -contextMenu.height + parent.height / 3 + else + contextMenu.y = ( parent.height * 2 ) / 3 + } + } + + Component { + id: menuItemComponent + + MenuItem { + id: menuItem + + property string name: "" + property var callback: function cb() {} // default callback + property string iconSource: "" + + height: root.menuItemHeight + + ExtendedMenuItem { + height: parent.height + rowHeight: parent.height * 0.8 + width: parent.width + contentText: menuItem.name + imageSource: menuItem.iconSource + overlayImage: true + } + + onClicked: callback() + } + } +} diff --git a/app/qml/components/ProjectList.qml b/app/qml/components/ProjectList.qml new file mode 100644 index 000000000..5ed62bb6c --- /dev/null +++ b/app/qml/components/ProjectList.qml @@ -0,0 +1,179 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.2 +import lc 1.0 +import "../" +import "." + +Item { + id: root + + property int projectModelType: ProjectsModel.EmptyProjectsModel + property string activeProjectId: "" + + signal openProjectRequested( string projectId, string projectFilePath ) + signal showLocalChangesRequested( string projectId ) + signal activeProjectDeleted() + + function searchTextChanged( searchText ) { + if ( projectModelType === ProjectsModel.PublicProjectsModel ) + { + controllerModel.listProjects( searchText ) + } + else viewModel.searchExpression = searchText + } + + function refreshProjectList() { + controllerModel.listProjects() + } + + function modelData( fromRole, fromValue, desiredRole ) { + controllerModel.dataFrom( fromRole, fromValue, desiredRole ) + } + + ListView { + id: listview + + Component.onCompleted: { + // set proper footer (add project / fetch more) + if ( root.projectModelType === ProjectsModel.LocalProjectsModel ) + listview.footer = addProjectButtonComponent + else + listview.footer = fetchMoreButtonComponent + } + + anchors.fill: parent + clip: true + maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity + + // Proxy model with source projects model + model: ProjectsProxyModel { + id: viewModel + + projectSourceModel: ProjectsModel { + id: controllerModel + + merginApi: __merginApi + localProjectsManager: __localProjectsManager + modelType: root.projectModelType + } + } + + // Project delegate + delegate: ProjectDelegateItem { + id: projectDelegate + + width: parent.width + height: InputStyle.rowHeightHeader * 1.2 + + projectFullName: model.ProjectFullName + projectId: model.ProjectId + projectDescription: model.ProjectDescription + projectStatus: model.ProjectSyncStatus ? model.ProjectSyncStatus : ProjectStatus.NoVersion + projectIsValid: model.ProjectIsValid + projectIsPending: model.ProjectPending ? model.ProjectPending : false + projectSyncProgress: model.ProjectSyncProgress ? model.ProjectSyncProgress : -1 + projectIsLocal: model.ProjectIsLocal + projectIsMergin: model.ProjectIsMergin + projectRemoteError: model.ProjectRemoteError ? model.ProjectRemoteError : "" + + highlight: model.ProjectId === root.activeProjectId + + viewContentY: ListView.view.contentY + viewHeight: ListView.view.height + + onOpenRequested: root.openProjectRequested( projectId, model.ProjectFilePath ) + onSyncRequested: controllerModel.syncProject( projectId ) + onMigrateRequested: controllerModel.migrateProject( projectId ) + onRemoveRequested: { + removeDialog.relatedProjectId = projectId + removeDialog.open() + } + onStopSyncRequested: controllerModel.stopProjectSync( projectId ) + onShowChangesRequested: root.showLocalChangesRequested( projectId ) + } + } + + Component { + id: fetchMoreButtonComponent + + DelegateButton { + width: parent.width + height: visible ? InputStyle.rowHeight : 0 + text: qsTr( "Fetch more" ) + + visible: controllerModel.hasMoreProjects + onClicked: controllerModel.fetchAnotherPage( viewModel.searchExpression ) + } + } + + Component { + id: addProjectButtonComponent + + DelegateButton { + width: parent.width + height: InputStyle.rowHeight + text: qsTr("Create project") + + onClicked: { + if ( __inputUtils.hasStoragePermission() ) { + stackView.push(projectWizardComp) + } + else if ( __inputUtils.acquireStoragePermission() ) { +// TODO: reload project dir ~> rather connect to permission granted signal and reload project dir + restartAppDialog.open() + } + } + } + } + + MessageDialog { + id: removeDialog + property string relatedProjectId + + title: qsTr( "Remove project" ) + text: qsTr( "Any unsynchronized changes will be lost." ) + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok | StandardButton.Cancel + + //! Using onButtonClicked instead of onAccepted,onRejected which have been called twice + onButtonClicked: { + if (clickedButton === StandardButton.Ok) { + if (relatedProjectId === "") + return + + if ( root.activeProjectId === relatedProjectId ) + root.activeProjectDeleted() + + controllerModel.removeLocalProject( relatedProjectId ) + + removeDialog.relatedProjectId = "" + visible = false + } + else if (clickedButton === StandardButton.Cancel) { + removeDialog.relatedProjectId = "" + visible = false + } + } + } + + MessageDialog { + id: restartAppDialog + + title: qsTr( "Input needs to be restarted" ) + text: qsTr( "To apply changes after granting storage permission, Input needs to be restarted. Click close and open Input again." ) + icon: StandardIcon.Warning + visible: false + standardButtons: StandardButton.Close + onRejected: __inputUtils.quitApp() + } +} diff --git a/app/qml/main.qml b/app/qml/main.qml index caf6e222d..12b8cfdb9 100644 --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -237,21 +237,24 @@ ApplicationWindow { } Component.onCompleted: { - if (__appSettings.defaultProject) { - var path = __appSettings.defaultProject ? __appSettings.defaultProject : openProjectPanel.activeProjectPath - var defaultIndex = __projectsModel.rowAccordingPath(path) - var isValid = __projectsModel.data(__projectsModel.index(defaultIndex), ProjectModel.IsValid) - if (isValid && __loader.load(path)) { - openProjectPanel.activeProjectIndex = defaultIndex !== -1 ? defaultIndex : 0 - __appSettings.activeProject = path - } else { - // if default project load failed, delete default setting - __appSettings.defaultProject = "" - openProjectPanel.openPanel() - } - } else { - openProjectPanel.openPanel() + // load default project + if ( __appSettings.defaultProject ) { + let path = __appSettings.defaultProject + let isValid = __localProjectsManager.projectIsValid( path ) + let id = __localProjectsManager.projectId( path ) + + if ( isValid && __loader.load( path ) ) { + projectPanel.activeProjectPath = path + projectPanel.activeProjectId = id + __appSettings.activeProject = path } + else { + // if default project load failed, delete default setting + __appSettings.defaultProject = "" + projectPanel.openPanel() + } + } + else projectPanel.openPanel() InputStyle.deviceRatio = window.screen.devicePixelRatio InputStyle.realWidth = window.width @@ -433,7 +436,7 @@ ApplicationWindow { gpsIndicatorColor: getGpsIndicatorColor() - onOpenProjectClicked: openProjectPanel.openPanel() + onOpenProjectClicked: projectPanel.openPanel() onOpenMapThemesClicked: mapThemesPanel.visible = true onMyLocationClicked: { mapCanvas.mapSettings.setCenter(positionKit.projectedPosition) @@ -559,26 +562,26 @@ ApplicationWindow { } } - MerginProjectPanel { - id: openProjectPanel + ProjectPanel { + id: projectPanel height: window.height width: window.width z: zPanel onVisibleChanged: { - if (openProjectPanel.visible) - openProjectPanel.forceActiveFocus() + if (projectPanel.visible) + projectPanel.forceActiveFocus() else { mainPanel.forceActiveFocus() } } - onActiveProjectIndexChanged: { - openProjectPanel.activeProjectPath = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.Path) - __appSettings.defaultProject = openProjectPanel.activeProjectPath - __appSettings.activeProject = openProjectPanel.activeProjectPath - __loader.load(openProjectPanel.activeProjectPath) + onOpenProjectRequested: { + console.log("loading ", projectPath) + __appSettings.defaultProject = projectPath + __appSettings.activeProject = projectPath + __loader.load( projectPath ) } } @@ -657,17 +660,15 @@ ApplicationWindow { var msg = message ? message : qsTr("Failed to communicate with Mergin.%1Try improving your network connection.".arg("
")) showAsDialog ? showDialog(msg) : showMessage(msg) } - onNotify: { - showMessage(message) - } + onNotify: showMessage(message) onProjectDataChanged: { - var projectName = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectName) - var projectNamespace = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectNamespace) - var currentProjectFullName = __merginApi.getFullProjectName(projectNamespace, projectName) +// var projectName = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectName) +// var projectNamespace = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectNamespace) +// var currentProjectFullName = __merginApi.getFullProjectName(projectNamespace, projectName) //! if current project has been updated, refresh canvas - if (projectFullName === currentProjectFullName) { + if (projectFullName === projectPanel.activeProjectId) { mapCanvas.mapSettings.extentChanged() } } diff --git a/app/qml/qml.qrc b/app/qml/qml.qrc index 0eee56e99..f83c98ad8 100644 --- a/app/qml/qml.qrc +++ b/app/qml/qml.qrc @@ -22,13 +22,12 @@ PositionMarker.qml Shadow.qml NumberSpin.qml - ProjectDelegateItem.qml AuthPanel.qml ExternalResourceBundle.qml RecordToolbar.qml RecordCrosshair.qml AboutPanel.qml - MerginProjectPanel.qml + ProjectPanel.qml AccountPage.qml Highlight.qml LoadingIndicator.qml @@ -62,5 +61,8 @@ CodeReader.qml CodeReaderHandler.qml CodeReaderOverlay.qml + components/ProjectList.qml + ProjectListPage.qml + components/ProjectDelegateItem.qml From dc4b336ca8d7be7df1b5a78241f75bd39c891483 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 12:25:21 +0200 Subject: [PATCH 15/22] update c++ code according to qml refactor --- app/inpututils.cpp | 33 ++++ app/inpututils.h | 4 + app/localprojectsmanager.cpp | 165 +++++++++++--------- app/localprojectsmanager.h | 82 +--------- app/merginapi.cpp | 17 +-- app/project_future.cpp | 36 +++++ app/project_future.h | 9 +- app/projectsmodel_future.cpp | 241 +++++++++++++++++++----------- app/projectsmodel_future.h | 34 +++-- app/projectsproxymodel_future.cpp | 7 +- 10 files changed, 362 insertions(+), 266 deletions(-) diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 59c2066f9..8b03c3b68 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -536,6 +536,39 @@ QString InputUtils::renameWithDateTime( const QString &srcPath, const QDateTime return QString(); } +QDateTime InputUtils::getLastModifiedFileDateTime( const QString &path ) +{ + QDateTime lastModified; + QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); + while ( it.hasNext() ) + { + it.next(); + if ( !MerginApi::isInIgnore( it.fileInfo() ) ) + { + if ( it.fileInfo().lastModified() > lastModified ) + { + lastModified = it.fileInfo().lastModified(); + } + } + } + return lastModified.toUTC(); +} + +int InputUtils::getProjectFilesCount( const QString &path ) +{ + int count = 0; + QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); + while ( it.hasNext() ) + { + it.next(); + if ( !MerginApi::isInIgnore( it.fileInfo() ) ) + { + count++; + } + } + return count; +} + QString InputUtils::downloadInProgressFilePath( const QString &projectDir ) { return projectDir + "/.mergin/.project.downloading"; diff --git a/app/inpututils.h b/app/inpututils.h index 6fc73dd35..cbb5ab136 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -154,6 +154,10 @@ class InputUtils: public QObject static bool removeDir( const QString &projectDir ); + static QDateTime getLastModifiedFileDateTime( const QString &path ); + + static int getProjectFilesCount( const QString &path ); + signals: Q_INVOKABLE void showNotificationRequested( const QString &message ); diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index b06b6950f..96ebb2105 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -22,7 +22,7 @@ LocalProjectsManager::LocalProjectsManager( const QString &dataDir ) reloadDataDir(); } -void LocalProjectsManager::reloadDataDir() // TODO: maybe add function to reload one specific project +void LocalProjectsManager::reloadDataDir() { mProjects.clear(); QStringList entryList = QDir( mDataDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs ); @@ -39,16 +39,15 @@ void LocalProjectsManager::reloadDataDir() // TODO: maybe add function to reload info.projectNamespace = metadata.projectNamespace; info.localVersion = metadata.version; } -// else -// { -// info.projectName = folderName; -// } + else + { + info.projectName = folderName; + } mProjects << info; } + qDebug() << "LPM found " << mProjects.size() << " in " << mDataDir; emit dataDirReloaded(); - - qDebug() << "LocalProjectsManager: found" << mProjects.size() << "projects"; } LocalProject_future LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const @@ -119,11 +118,11 @@ void LocalProjectsManager::addMerginProject( const QString &projectDir, const QS addProject( projectDir, projectNamespace, projectName ); } -void LocalProjectsManager::removeLocalProject( const QString &projectDir ) +void LocalProjectsManager::removeLocalProject( const QString &projectId ) { for ( int i = 0; i < mProjects.count(); ++i ) { - if ( mProjects[i].projectDir == projectDir ) + if ( mProjects[i].id() == projectId ) { emit aboutToRemoveLocalProject( mProjects[i] ); @@ -135,6 +134,30 @@ void LocalProjectsManager::removeLocalProject( const QString &projectDir ) } } +bool LocalProjectsManager::projectIsValid( const QString &path ) const +{ + for ( int i = 0; i < mProjects.count(); ++i ) + { + if ( mProjects[i].qgisProjectFilePath == path ) + { + return mProjects[i].projectError.isEmpty(); + } + } + return false; +} + +QString LocalProjectsManager::projectId( const QString &path ) const +{ + for ( int i = 0; i < mProjects.count(); ++i ) + { + if ( mProjects[i].qgisProjectFilePath == path ) + { + return mProjects[i].id(); + } + } + return QString(); +} + //void LocalProjectsManager::removeMerginInfo( const QString &projectFullName ) //{ // for ( int i = 0; i < mProjects.count(); ++i ) @@ -245,7 +268,7 @@ QString LocalProjectsManager::findQgisProjectFile( const QString &projectDir, QS return QString(); } -void LocalProjectsManager::addProject(const QString &projectDir, const QString &projectNamespace, const QString &projectName) +void LocalProjectsManager::addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) { LocalProject_future project; project.projectDir = projectDir; @@ -257,73 +280,73 @@ void LocalProjectsManager::addProject(const QString &projectDir, const QString & emit localProjectAdded( project ); } -static QDateTime _getLastModifiedFileDateTime( const QString &path ) -{ - QDateTime lastModified; - QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); - while ( it.hasNext() ) - { - it.next(); - if ( !MerginApi::isInIgnore( it.fileInfo() ) ) - { - if ( it.fileInfo().lastModified() > lastModified ) - { - lastModified = it.fileInfo().lastModified(); - } - } - } - return lastModified.toUTC(); -} +//static QDateTime _getLastModifiedFileDateTime( const QString &path ) +//{ +// QDateTime lastModified; +// QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); +// while ( it.hasNext() ) +// { +// it.next(); +// if ( !MerginApi::isInIgnore( it.fileInfo() ) ) +// { +// if ( it.fileInfo().lastModified() > lastModified ) +// { +// lastModified = it.fileInfo().lastModified(); +// } +// } +// } +// return lastModified.toUTC(); +//} -static int _getProjectFilesCount( const QString &path ) -{ - int count = 0; - QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); - while ( it.hasNext() ) - { - it.next(); - if ( !MerginApi::isInIgnore( it.fileInfo() ) ) - { - count++; - } - } - return count; -} +//static int _getProjectFilesCount( const QString &path ) +//{ +// int count = 0; +// QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); +// while ( it.hasNext() ) +// { +// it.next(); +// if ( !MerginApi::isInIgnore( it.fileInfo() ) ) +// { +// count++; +// } +// } +// return count; +//} -ProjectStatus::Status LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) -{ - if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project - return ProjectStatus::NoVersion; +//ProjectStatus::Status LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) +//{ +// if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project +// return ProjectStatus::NoVersion; - // There was no sync yet - if ( project->local->localVersion < 0 ) - { - return ProjectStatus::NoVersion; - } +// // There was no sync yet +// if ( project->local->localVersion < 0 ) +// { +// return ProjectStatus::NoVersion; +// } - // - // TODO: this check for local modifications should be revisited - // - - // Something has locally changed after last sync with server - QString metadataFilePath = project->local->projectDir + "/" + MerginApi::sMetadataFile; - QDateTime lastModified = _getLastModifiedFileDateTime( project->local->projectDir ); - QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); - MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); - int filesCount = _getProjectFilesCount( project->local->projectDir ); - if ( lastSync < lastModified || meta.files.count() != filesCount ) - { - return ProjectStatus::Modified; - } +// // +// // TODO: this check for local modifications should be revisited +// // + +// // Something has locally changed after last sync with server +// QString metadataFilePath = project->local->projectDir + "/" + MerginApi::sMetadataFile; +// QDateTime lastModified = _getLastModifiedFileDateTime( project->local->projectDir ); +// QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); +// MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); +// int filesCount = _getProjectFilesCount( project->local->projectDir ); +// if ( lastSync < lastModified || meta.files.count() != filesCount ) +// { +// return ProjectStatus::Modified; +// } - // Version is lower than latest one, last sync also before updated - if ( project->local->localVersion < project->mergin->serverVersion ) - { - return ProjectStatus::OutOfDate; - } +// // Version is lower than latest one, last sync also before updated +// if ( project->local->localVersion < project->mergin->serverVersion ) +// { +// return ProjectStatus::OutOfDate; +// } - return ProjectStatus::UpToDate; -} +// return ProjectStatus::UpToDate; +//} //void LocalProjectsManager::updateProjectStatus( LocalProject_future &project ) //{ diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 0b5a0652f..0aab423a6 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -13,65 +13,19 @@ #include #include -//enum ProjectStatus -//{ -// NoVersion, //!< the project is not available locally -// UpToDate, //!< both server and local copy are in sync with no extra modifications -// OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) -// Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) -// NonProjectItem //!< only for mock projects, acts like a hook to enable extra functionality for models working with projects . -//}; -//Q_ENUMS( ProjectStatus ) - - -//! Summary information about a local project -//struct LocalProjectInfo -//{ -// bool isValid() const { return !projectDir.isEmpty(); } - -// bool isShowable() const { return qgisProjectError.isEmpty(); } - -// QString projectDir; //!< full path to the project directory - -// QString qgisProjectFilePath; //!< path to the .qgs/.qgz file (or empty if not have exactly one such file) - -// QString qgisProjectError; //!< If project is invalid, projectError carry more information why -// // TODO: reset when project is synchronized - -// // -// // mergin-specific project info (may be empty) -// // - -// QString projectName; -// QString projectNamespace; - -// int localVersion = -1; //!< the project version that is currently available locally -// int serverVersion = -1; //!< the project version most recently seen on server (may be -1 if no info from server is available) - -// ProjectStatus status = NoVersion; - -// bool operator ==( const LocalProjectInfo &other ) { return ( projectName == other.projectName && projectNamespace == other.projectNamespace );} -// bool operator !=( const LocalProjectInfo &other ) { return !( *this == other ); } - -// // Sync status (e.g. progress) is not kept here because if a project does not exist locally yet -// // and it is only being downloaded for the first time, it's not in the list of local projects either -// // and we would need to do some workarounds for that. -//}; - - class LocalProjectsManager : public QObject { Q_OBJECT public: explicit LocalProjectsManager( const QString &dataDir ); + //! Loads all projects from mDataDir, removes all old projects + void reloadDataDir(); + QString dataDir() const { return mDataDir; } LocalProjectsList projects() const { return mProjects; } - //! Loads all projects from mDataDir, removes all old projects - void reloadDataDir(); - LocalProject_future projectFromDirectory( const QString &projectDir ) const; LocalProject_future projectFromProjectFilePath( const QString &projectFilePath ) const; @@ -81,46 +35,25 @@ class LocalProjectsManager : public QObject //! Adds entry about newly created project void addLocalProject( const QString &projectDir, const QString &projectName ); -// bool hasMerginProject( const QString &projectFullName ) const; -// bool hasMerginProject( const QString &projectNamespace, const QString &projectName ) const; - -// void updateProjectStatus( const QString &projectDir ); - //! Adds entry for downloaded project void addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); - //! Should add an entry about newly created local project -// void addLocalProject( const QString &projectDir, const QString &projectName ); - //! Should forget about that project (it has been removed already) - Q_INVOKABLE void removeLocalProject( const QString &projectDir ); + Q_INVOKABLE void removeLocalProject( const QString &projectId ); - //! Resets mergin related info for given project. -// void removeMerginInfo( const QString &projectFullName ); + Q_INVOKABLE bool projectIsValid( const QString &path ) const; - //! Recursively removes project's directory (only when it exists in the list) -// void deleteProjectDirectory( const QString &projectDir ); - - // - // updates of mergin info - // + Q_INVOKABLE QString projectId( const QString &path ) const; //! after successful update/upload - both server and local version are the same void updateLocalVersion( const QString &projectDir, int version ); - //! after receiving project info with server version (local version stays the same -// void updateMerginServerVersion( const QString &projectDir, int version ); - - //! Updates qgisProjectError (after successful project synced) -// void updateProjectErrors( const QString &projectDir, const QString &errMsg ); - //! Updates proejct's namespace void updateNamespace( const QString &projectDir, const QString &projectNamespace ); //! Finds all QGIS project files and set the err variable if any occured. QString findQgisProjectFile( const QString &projectDir, QString &err ); - static ProjectStatus::Status currentProjectStatus( const std::shared_ptr project ); // TODO: maybe move somewhere else? signals: @@ -131,9 +64,6 @@ class LocalProjectsManager : public QObject void localProjectDataChanged( const LocalProject_future &project ); void dataDirReloaded(); -// private: -// void updateProjectStatus( LocalProject_future &project ); // TODO: local project manager should not have status - private: void addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ); diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 520ef081b..f0dc9f9cc 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -1874,9 +1874,8 @@ void MerginApi::uploadInfoReplyFinished() transaction.projectDir = projectInfo.projectDir; Q_ASSERT( !transaction.projectDir.isEmpty() ); - MerginProjectMetadata serverProject = MerginProjectMetadata::fromJson( data ); // get the latest server version from our reply (we do not update it in LocalProjectsManager though... I guess we don't need to) -// projectInfo.serverVersion = serverProject.version; + MerginProjectMetadata serverProject = MerginProjectMetadata::fromJson( data ); // now let's figure a key question: are we on the most recent version of the project // if we're about to do upload? because if not, we need to do local update first @@ -1892,8 +1891,6 @@ void MerginApi::uploadInfoReplyFinished() QList localFiles = getLocalProjectFiles( transaction.projectDir + "/" ); MerginProjectMetadata oldServerProject = MerginProjectMetadata::fromCachedJson( transaction.projectDir + "/" + sMetadataFile ); -// mLocalProjects.updateMerginServerVersion( transaction.projectDir, serverProject.version ); - transaction.diff = compareProjectFiles( oldServerProject.files, serverProject.files, localFiles, transaction.projectDir ); InputUtils::log( "push " + projectFullName, transaction.diff.dump() ); @@ -2355,7 +2352,13 @@ MerginProjectsList MerginApi::parseProjectsFromJson( const QJsonDocument &doc ) { for ( auto it = object.begin(); it != object.end(); ++it ) { - result << parseProjectMetadata( it->toObject() ); + MerginProject_future project = parseProjectMetadata( it->toObject() ); + if ( !project.remoteError.isEmpty() ) + { + // add project namespace/name from object name in case of error + MerginApi::extractProjectName( it.key(), project.projectNamespace, project.projectName ); + } + result << project; } } return result; @@ -2435,10 +2438,6 @@ void MerginApi::finishProjectSync( const QString &projectFullName, bool syncSucc // update info of local projects mLocalProjects.updateLocalVersion( transaction.projectDir, transaction.version ); -// mLocalProjects.updateMerginServerVersion( transaction.projectDir, transaction.version ); -// TODO: Is it neccessary to update server version at all? -// emit updateServerVersion( transaction.projectDir, transaction.version ); - InputUtils::log( "sync " + projectFullName, QStringLiteral( "### Finished ### New project version: %1\n" ).arg( transaction.version ) ); } else diff --git a/app/project_future.cpp b/app/project_future.cpp index c278f0272..d7406850c 100644 --- a/app/project_future.cpp +++ b/app/project_future.cpp @@ -9,6 +9,7 @@ #include "project_future.h" #include "merginapi.h" +#include "inpututils.h" QString LocalProject_future::id() const { @@ -23,3 +24,38 @@ QString MerginProject_future::id() const { return MerginApi::getFullProjectName( projectNamespace, projectName ); } + +ProjectStatus::Status ProjectStatus::projectStatus( const std::shared_ptr project ) +{ + if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project + return ProjectStatus::NoVersion; + + // There was no sync yet + if ( project->local->localVersion < 0 ) + { + return ProjectStatus::NoVersion; + } + + // + // TODO: this check for local modifications should be revisited + // + + // Something has locally changed after last sync with server + QString metadataFilePath = project->local->projectDir + "/" + MerginApi::sMetadataFile; + QDateTime lastModified = InputUtils::getLastModifiedFileDateTime( project->local->projectDir ); + QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); + MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); + int filesCount = InputUtils::getProjectFilesCount( project->local->projectDir ); + if ( lastSync < lastModified || meta.files.count() != filesCount ) + { + return ProjectStatus::Modified; + } + + // Version is lower than latest one, last sync also before updated + if ( project->local->localVersion < project->mergin->serverVersion ) + { + return ProjectStatus::OutOfDate; + } + + return ProjectStatus::UpToDate; +} diff --git a/app/project_future.h b/app/project_future.h index b6ffc43b5..87c433158 100644 --- a/app/project_future.h +++ b/app/project_future.h @@ -16,18 +16,21 @@ #include #include +struct Project_future; + namespace ProjectStatus { Q_NAMESPACE enum Status { - NoVersion, //!< the project is not available locally + NoVersion, //!< the project is not downloaded UpToDate, //!< both server and local copy are in sync with no extra modifications OutOfDate, //!< server has newer version than what is available locally (but the project is not modified locally) - Modified, //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) - + Modified //!< there are some local modifications in the project that need to be pushed (note: also server may have newer version) // Maybe orphaned state in future }; Q_ENUM_NS( Status ) + + Status projectStatus( const std::shared_ptr project ); } struct LocalProject_future diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel_future.cpp index 8e99f552c..042a1a8b2 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel_future.cpp @@ -64,11 +64,11 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const switch ( role ) { case ProjectName: return QVariant( project->projectName() ); case ProjectNamespace: return QVariant( project->projectNamespace() ); - case ProjectFullName: return MerginApi::getFullProjectName( project->projectNamespace(), project->projectName() ); + case ProjectFullName: return QVariant( project->projectId() ); case ProjectId: return QVariant( project->projectId() ); case ProjectIsLocal: return QVariant( project->isLocal() ); case ProjectIsMergin: return QVariant( project->isMergin() ); - case ProjectSyncStatus: return QVariant( LocalProjectsManager::currentProjectStatus( project ) ); + case ProjectSyncStatus: return QVariant( project->isMergin() ? project->mergin->status : ProjectStatus::NoVersion ); case ProjectFilePath: return QVariant( project->isLocal() ? project->local->qgisProjectFilePath : QString() ); case ProjectDirectory: return QVariant( project->isLocal() ? project->local->projectDir : QString() ); case ProjectIsValid: { @@ -111,23 +111,13 @@ QModelIndex ProjectsModel_future::index( int row, int col, const QModelIndex &pa return createIndex( row, 0, nullptr ); } -bool ProjectsModel_future::canFetchMore( const QModelIndex & ) const -{ -// return mServerProjectsCount > mProjects.size(); - return true; -} - -void ProjectsModel_future::fetchMore( const QModelIndex & ) -{ - -} - QHash ProjectsModel_future::roleNames() const { QHash roles; roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); roles[Roles::ProjectNamespace] = QStringLiteral( "ProjectNamespace" ).toLatin1(); roles[Roles::ProjectFullName] = QStringLiteral( "ProjectFullName" ).toLatin1(); + roles[Roles::ProjectId] = QStringLiteral( "ProjectId" ).toLatin1(); roles[Roles::ProjectDirectory] = QStringLiteral( "ProjectDirectory" ).toLatin1(); roles[Roles::ProjectIsLocal] = QStringLiteral( "ProjectIsLocal" ).toLatin1(); roles[Roles::ProjectIsMergin] = QStringLiteral( "ProjectIsMergin" ).toLatin1(); @@ -146,7 +136,7 @@ int ProjectsModel_future::rowCount( const QModelIndex & ) const return mProjects.count(); } -void ProjectsModel_future::listProjects( int page, QString searchExpression ) +void ProjectsModel_future::listProjects( const QString &searchExpression, int page ) { if ( mModelType == LocalProjectsModel ) { @@ -154,7 +144,7 @@ void ProjectsModel_future::listProjects( int page, QString searchExpression ) return; } - mLastRequestId = mBackend->listProjects( "", modelTypeToFlag(), searchExpression, page ); + mLastRequestId = mBackend->listProjects( searchExpression, modelTypeToFlag(), "", page ); } void ProjectsModel_future::listProjectsByName() @@ -167,6 +157,99 @@ void ProjectsModel_future::listProjectsByName() mLastRequestId = mBackend->listProjectsByName( projectNames() ); } +bool ProjectsModel_future::hasMoreProjects() const +{ + if ( mProjects.size() < mServerProjectsCount ) + return true; + + return false; +} + +void ProjectsModel_future::fetchAnotherPage( const QString &searchExpression ) +{ + listProjects( searchExpression, mPaginatedPage + 1 ); +} + +QVariant ProjectsModel_future::dataFrom( int fromRole, QVariant fromValue, int desiredRole ) const +{ + switch ( fromRole ) + { + case ProjectId: + { + std::shared_ptr project = projectFromId( fromValue.toString() ); + if ( project ) + { + QModelIndex ix = index( mProjects.indexOf( project ) ); + return data( ix, desiredRole ); + } + return QVariant(); + } + + case ProjectFilePath: + { + for ( int i = 0; i < mProjects.size(); i++ ) + { + if ( mProjects[i]->isLocal() && mProjects[i]->local->qgisProjectFilePath == fromValue.toString() ) + { + QModelIndex ix = index( i ); + return data( ix, desiredRole ); + } + } + } + default: return QVariant(); + } + + return QVariant(); +} + +void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) +{ + qDebug() << "PMR: onListProjectsFinished(): received response with requestId = " << requestId; + if ( mLastRequestId != requestId ) + { + qDebug() << "PMR: onListProjectsFinished(): ignoring request with id " << requestId; + return; + } + + qDebug() << "PMR: onListProjectsFinished(): project count = " << projectsCount << " but mergin projects emited: " << merginProjects.size(); + + if ( page == 1 ) + { + // if we are populating first page, reset model and throw away previous projects + beginResetModel(); + mergeProjects( merginProjects, pendingProjects, false ); + printProjects(); + endResetModel(); + } + else + { + // paginating next page, keep previous projects and emit model add items + beginInsertRows( QModelIndex(), mProjects.size(), mProjects.size() + merginProjects.size() - 1 ); + mergeProjects( merginProjects, pendingProjects, true ); + printProjects(); + endInsertRows(); + } + + mServerProjectsCount = projectsCount; + mPaginatedPage = page; + emit hasMoreProjectsChanged(); +} + +void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ) +{ + qDebug() << "PMR: onListProjectsByNameFinished(): received response with requestId = " << requestId; + if ( mLastRequestId != requestId ) + { + qDebug() << "PMR: onListProjectsByNameFinished(): ignoring request with id " << requestId; + return; + } + + beginResetModel(); + mergeProjects( merginProjects, pendingProjects ); + printProjects(); + endResetModel(); +} + void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious ) { LocalProjectsList localProjects = mLocalProjectsManager->projects(); @@ -182,9 +265,7 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec for ( const auto &localProject : localProjects ) { std::shared_ptr project = std::shared_ptr( new Project_future() ); - std::unique_ptr local = std::unique_ptr( new LocalProject_future( localProject ) ); - - project->local = std::move( local ); + project->local = std::unique_ptr( new LocalProject_future( localProject ) ); MerginProject_future remoteEntry; remoteEntry.projectName = project->local->projectName; @@ -201,6 +282,7 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec project->mergin->progress = projectTransaction.transferedSize / projectTransaction.totalSize; project->mergin->pending = true; } + project->mergin->status = ProjectStatus::projectStatus( project ); } else if ( project->local->localVersion > -1 ) { @@ -208,6 +290,7 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec project->mergin = std::unique_ptr( new MerginProject_future() ); project->mergin->projectName = project->local->projectName; project->mergin->projectNamespace = project->local->projectNamespace; + project->mergin->status = ProjectStatus::projectStatus( project ); } mProjects << project; @@ -238,70 +321,32 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec int ix = localProjects.indexOf( localProject ); project->local = std::unique_ptr( new LocalProject_future( localProjects[ix] ) ); } + project->mergin->status = ProjectStatus::projectStatus( project ); mProjects << project; } } } -int ProjectsModel_future::serverProjectsCount() const -{ - return mServerProjectsCount; -} - -void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) -{ - qDebug() << "PMR: onListProjectsFinished(): received response with requestId = " << requestId; - if ( mLastRequestId != requestId ) - { - qDebug() << "PMR: onListProjectsFinished(): ignoring request with id " << requestId; - return; - } - - setServerProjectsCount( projectsCount ); - - qDebug() << "PMR: onListProjectsFinished(): project count = " << projectsCount << " but mergin projects emited: " << merginProjects.size(); - - beginResetModel(); - mergeProjects( merginProjects, pendingProjects, page != 1 ); // throw projects only if paginating first page - printProjects(); - endResetModel(); -} - -void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ) -{ - qDebug() << "PMR: onListProjectsByNameFinished(): received response with requestId = " << requestId; - if ( mLastRequestId != requestId ) - { - qDebug() << "PMR: onListProjectsByNameFinished(): ignoring request with id " << requestId; - return; - } - - beginResetModel(); - mergeProjects( merginProjects, pendingProjects ); - printProjects(); - endResetModel(); -} - -void ProjectsModel_future::syncProject( const QString &projectNamespace, const QString &projectName ) +void ProjectsModel_future::syncProject( const QString &projectId ) { - std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); + std::shared_ptr project = projectFromId( projectId ); if ( project == nullptr ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "not in projects list"; + qDebug() << "PMR: project" << projectId << "not in projects list"; return; } if ( !project->isMergin() ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not a mergin project"; + qDebug() << "PMR: project" << projectId << "is not a mergin project"; return; } if ( project->mergin->pending ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is already syncing"; + qDebug() << "PMR: project" << projectId << "is already syncing"; return; } @@ -309,35 +354,35 @@ void ProjectsModel_future::syncProject( const QString &projectNamespace, const Q { qDebug() << "PMR: updating project:" << project->mergin->id(); - bool useAuth = !mBackend->userAuth()->hasAuthData() && mModelType == ProjectModelTypes::ExploreProjectsModel; - mBackend->updateProject( projectNamespace, projectName, useAuth ); + bool useAuth = !mBackend->userAuth()->hasAuthData() && mModelType == ProjectModelTypes::PublicProjectsModel; + mBackend->updateProject( project->mergin->projectNamespace, project->mergin->projectName, useAuth ); } else if ( project->mergin->status == ProjectStatus::Modified ) { qDebug() << "PMR: uploading project:" << project->mergin->id(); - mBackend->uploadProject( projectNamespace, projectName ); + mBackend->uploadProject( project->mergin->projectNamespace, project->mergin->projectName ); } } -void ProjectsModel_future::stopProjectSync( const QString &projectNamespace, const QString &projectName ) +void ProjectsModel_future::stopProjectSync( const QString &projectId ) { - std::shared_ptr project = projectFromId( MerginApi::getFullProjectName( projectNamespace, projectName ) ); + std::shared_ptr project = projectFromId( projectId ); if ( project == nullptr ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "not in projects list"; + qDebug() << "PMR: project" << projectId << "not in projects list"; return; } if ( !project->isMergin() ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not a mergin project"; + qDebug() << "PMR: project" << projectId << "is not a mergin project"; return; } if ( !project->mergin->pending ) { - qDebug() << "PMR: project" << MerginApi::getFullProjectName(projectNamespace, projectName) << "is not pending"; + qDebug() << "PMR: project" << projectId << "is not pending"; return; } @@ -353,9 +398,18 @@ void ProjectsModel_future::stopProjectSync( const QString &projectNamespace, con } } -void ProjectsModel_future::removeLocalProject( const QString &projectDir ) +void ProjectsModel_future::removeLocalProject( const QString &projectId ) +{ + mLocalProjectsManager->removeLocalProject( projectId ); +} + +void ProjectsModel_future::migrateProject( const QString &projectId ) { - mLocalProjectsManager->removeLocalProject( projectDir ); + // if it is indeed a local project + std::shared_ptr project = projectFromId( projectId ); + + if ( project->isLocal() ) + mBackend->migrateProjectToMergin( project->local->projectName ); } void ProjectsModel_future::onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) @@ -398,6 +452,8 @@ void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) { // add local information ~ project downloaded proj->local = std::unique_ptr( new LocalProject_future( project ) ); + if ( proj->isMergin() ) + proj->mergin->status = ProjectStatus::projectStatus( proj ); QModelIndex ix = index( mProjects.indexOf( proj ) ); emit dataChanged( ix, ix ); @@ -424,13 +480,27 @@ void ProjectsModel_future::onAboutToRemoveProject( const LocalProject_future pro if ( proj ) { - int removeIndex = mProjects.indexOf( proj ); + if ( mModelType == LocalProjectsModel ) + { + int removeIndex = mProjects.indexOf( proj ); + + beginRemoveRows( QModelIndex(), removeIndex, removeIndex ); + mProjects.removeOne( proj ); + endRemoveRows(); + + qDebug() << "PMR: Deleted project" << project.id(); + } + else + { + // just remove local part + proj->local.reset(); - beginRemoveRows( QModelIndex(), removeIndex, removeIndex ); - mProjects.removeOne( proj ); - endRemoveRows(); + if ( proj->isMergin() ) + proj->mergin->status = ProjectStatus::projectStatus( proj ); - qDebug() << "PMR: Deleted project" << project.id(); + QModelIndex ix = index( mProjects.indexOf( proj ) ); + emit dataChanged( ix, ix ); + } } } @@ -441,6 +511,9 @@ void ProjectsModel_future::onProjectDataChanged( const LocalProject_future &proj if ( proj ) { proj->local = std::unique_ptr( new LocalProject_future( project ) ); + if ( proj->isMergin() ) + proj->mergin->status = ProjectStatus::projectStatus( proj ); + QModelIndex editIndex = index( mProjects.indexOf( proj ) ); emit dataChanged( editIndex, editIndex ); @@ -454,7 +527,7 @@ void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFu if ( proj ) { - proj->mergin = nullptr; + proj->mergin.reset(); QModelIndex editIndex = index( mProjects.indexOf( proj ) ); emit dataChanged( editIndex, editIndex ); @@ -474,15 +547,6 @@ void ProjectsModel_future::onProjectAttachedToMergin( const QString &projectFull qDebug() << "PMR: Project attached to mergin " << projectFullName; } -void ProjectsModel_future::setServerProjectsCount( int serverProjectsCount ) -{ - if ( mServerProjectsCount == serverProjectsCount ) - return; - - mServerProjectsCount = serverProjectsCount; - emit serverProjectsCountChanged( mServerProjectsCount ); -} - void ProjectsModel_future::setMerginApi( MerginApi *merginApi ) { if ( !merginApi || mBackend == merginApi ) @@ -517,7 +581,7 @@ QString ProjectsModel_future::modelTypeToFlag() const { switch ( mModelType ) { - case MyProjectsModel: + case CreatedProjectsModel: return QStringLiteral( "created" ); case SharedProjectsModel: return QStringLiteral( "shared" ); @@ -565,6 +629,7 @@ void ProjectsModel_future::loadLocalProjects() { beginResetModel(); mergeProjects( MerginProjectsList(), Transactions() ); // Fills model with local projects + printProjects(); endResetModel(); } } diff --git a/app/projectsmodel_future.h b/app/projectsmodel_future.h index e03ba39c0..2dbabfb32 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel_future.h @@ -53,9 +53,9 @@ class ProjectsModel_future : public QAbstractListModel { EmptyProjectsModel = 0, // default, holding no projects ~ invalid model LocalProjectsModel, - MyProjectsModel, + CreatedProjectsModel, SharedProjectsModel, - ExploreProjectsModel, + PublicProjectsModel, RecentProjectsModel }; Q_ENUM( ProjectModelTypes ) @@ -63,47 +63,52 @@ class ProjectsModel_future : public QAbstractListModel ProjectsModel_future( QObject *parent = nullptr ); ~ProjectsModel_future() override {}; - Q_PROPERTY( int serverProjectsCount READ serverProjectsCount WRITE setServerProjectsCount NOTIFY serverProjectsCountChanged ) // TODO: replace with builtin canFetchMore - // From Qt 5.15 we can use REQUIRED keyword here that will ensure object will be always instantiated from QML with these mandatory properties Q_PROPERTY( MerginApi *merginApi READ merginApi WRITE setMerginApi ) Q_PROPERTY( LocalProjectsManager *localProjectsManager READ localProjectsManager WRITE setLocalProjectsManager ) Q_PROPERTY( ProjectModelTypes modelType READ modelType WRITE setModelType ) + Q_PROPERTY( bool hasMoreProjects READ hasMoreProjects NOTIFY hasMoreProjectsChanged ) + // Needed methods from QAbstractListModel Q_INVOKABLE QVariant data( const QModelIndex &index, int role ) const override; Q_INVOKABLE QModelIndex index( int row, int column = 0, const QModelIndex &parent = QModelIndex() ) const override; - Q_INVOKABLE bool canFetchMore( const QModelIndex &parent ) const override; - Q_INVOKABLE void fetchMore( const QModelIndex &parent ) override; QHash roleNames() const override; int rowCount( const QModelIndex &parent = QModelIndex() ) const override; //! Called to list projects, either fetch more or get first - Q_INVOKABLE void listProjects( int page = 1, const QString searchExpression = QString() ); + Q_INVOKABLE void listProjects( const QString &searchExpression = QString(), int page = 1 ); //! Called to list projects, either fetch more or get first Q_INVOKABLE void listProjectsByName(); //! Syncs specified project - upload or update - Q_INVOKABLE void syncProject( const QString &projectNamespace, const QString &projectName ); + Q_INVOKABLE void syncProject( const QString &projectId ); //! Stops running project upload or update - Q_INVOKABLE void stopProjectSync( const QString &projectNamespace, const QString &projectName ); + Q_INVOKABLE void stopProjectSync( const QString &projectId ); //! Forwards call to LocalProjectsManager to remove local project - Q_INVOKABLE void removeLocalProject( const QString &projectDir ); + Q_INVOKABLE void removeLocalProject( const QString &projectId ); + + //! Migrates local project to mergin + Q_INVOKABLE void migrateProject( const QString &projectId ); + + Q_INVOKABLE void fetchAnotherPage( const QString &searchExpression ); + + Q_INVOKABLE QVariant dataFrom( int fromRole, QVariant fromValue, int desiredRole ) const; //! Method merging local and remote projects based on the model type void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious = false ); - int serverProjectsCount() const; - ProjectsModel_future::ProjectModelTypes modelType() const; MerginApi *merginApi() const { return mBackend; } LocalProjectsManager *localProjectsManager() const { return mLocalProjectsManager; } + bool hasMoreProjects() const; + public slots: // MerginAPI - backend signals void onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ); @@ -118,17 +123,15 @@ public slots: void onAboutToRemoveProject( const LocalProject_future project ); void onProjectDataChanged( const LocalProject_future &project ); - void setServerProjectsCount( int serverProjectsCount ); void setMerginApi( MerginApi *merginApi ); void setLocalProjectsManager( LocalProjectsManager *localProjectsManager ); void setModelType( ProjectModelTypes modelType ); signals: - void serverProjectsCountChanged( int serverProjectsCount ); void modelInitialized(); + void hasMoreProjectsChanged(); private: - QString modelTypeToFlag() const; void printProjects() const; QStringList projectNames() const; @@ -146,6 +149,7 @@ public slots: //! For pagination int mServerProjectsCount = -1; + int mPaginatedPage = 1; //! For processing only my requests QString mLastRequestId; diff --git a/app/projectsproxymodel_future.cpp b/app/projectsproxymodel_future.cpp index 3afb20571..8c3332b9d 100644 --- a/app/projectsproxymodel_future.cpp +++ b/app/projectsproxymodel_future.cpp @@ -11,12 +11,10 @@ ProjectsProxyModel_future::ProjectsProxyModel_future( QObject *parent ) : QSortFilterProxyModel( parent ) { - qDebug() << "PMR: Building proxy model " << this; } void ProjectsProxyModel_future::initialize() { - qDebug() << "PMR: Initializing proxy model" << this; setSourceModel( mModel ); mModelType = mModel->modelType(); @@ -72,16 +70,17 @@ bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelI { QString lProjectFullName = mModel->data( left, ProjectsModel_future::ProjectFullName ).toString(); QString rProjectFullName = mModel->data( right, ProjectsModel_future::ProjectFullName ).toString(); + qDebug() << "Comparing " << lProjectFullName << rProjectFullName; return lProjectFullName.compare( rProjectFullName, Qt::CaseInsensitive ) < 0; } if ( !lProjectIsMergin && rProjectIsMergin ) { - return false; + return true; } if ( lProjectIsMergin && !rProjectIsMergin ) { - return true; + return false; } QString lNamespace = mModel->data( left, ProjectsModel_future::ProjectNamespace ).toString(); From 661386ab473bc28d23e3740fb0b35911e66f8ccc Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 12:35:18 +0200 Subject: [PATCH 16/22] removed unused code --- app/localprojectsmanager.cpp | 147 +------------------- app/localprojectsmanager.h | 2 - app/main.cpp | 36 +---- app/merginapi.cpp | 29 +--- app/merginapi.h | 37 +---- app/merginprojectmodel.cpp | 254 ----------------------------------- app/merginprojectmodel.h | 117 ---------------- app/projectsmodel.cpp | 205 ---------------------------- app/projectsmodel.h | 125 ----------------- app/sources.pri | 4 - 10 files changed, 10 insertions(+), 946 deletions(-) delete mode 100644 app/merginprojectmodel.cpp delete mode 100644 app/merginprojectmodel.h delete mode 100644 app/projectsmodel.cpp delete mode 100644 app/projectsmodel.h diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index 96ebb2105..277e20829 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -46,7 +46,8 @@ void LocalProjectsManager::reloadDataDir() mProjects << info; } - qDebug() << "LPM found " << mProjects.size() << " in " << mDataDir; + + qDebug() << "Found " << mProjects.size() << " local projects in " << mDataDir; emit dataDirReloaded(); } @@ -85,29 +86,6 @@ LocalProject_future LocalProjectsManager::projectFromMerginName( const QString & return projectFromMerginName( MerginApi::getFullProjectName( projectNamespace, projectName ) ); } -//bool LocalProjectsManager::hasMerginProject( const QString &projectFullName ) const -//{ -// return projectFromMerginName( projectFullName ).isValid(); -//} - -//bool LocalProjectsManager::hasMerginProject( const QString &projectNamespace, const QString &projectName ) const -//{ -// return hasMerginProject( MerginApi::getFullProjectName( projectNamespace, projectName ) ); -//} - -//void LocalProjectsManager::updateProjectStatus( const QString &projectDir ) -//{ -// for ( LocalProject_future &info : mProjects ) -// { -// if ( info.projectDir == projectDir ) -// { -// updateProjectStatus( info ); -// return; -// } -// } -// Q_ASSERT( false ); // should not happen -//} - void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName ) { addProject( projectDir, QString(), projectName ); @@ -158,22 +136,6 @@ QString LocalProjectsManager::projectId( const QString &path ) const return QString(); } -//void LocalProjectsManager::removeMerginInfo( const QString &projectFullName ) -//{ -// for ( int i = 0; i < mProjects.count(); ++i ) -// { -// if ( mProjects[i].id() == projectFullName ) -// { -// mProjects[i].localVersion = -1; -// mProjects[i].projectNamespace.clear(); -// InputUtils::removeDir( mProjects[i].projectDir + "/.mergin" ); - -// emit localProjectDataChanged( mProjects[i].projectDir ); -// return; -// } -// } -//} - void LocalProjectsManager::updateLocalVersion( const QString &projectDir, int version ) { for ( int i = 0; i < mProjects.count(); ++i ) @@ -189,33 +151,6 @@ void LocalProjectsManager::updateLocalVersion( const QString &projectDir, int ve Q_ASSERT( false ); // should not happen } -//void LocalProjectsManager::updateMerginServerVersion( const QString &projectDir, int version ) -//{ -// for ( int i = 0; i < mProjects.count(); ++i ) -// { -// if ( mProjects[i].projectDir == projectDir ) -// { -// mProjects[i].serverVersion = version; -// updateProjectStatus( mProjects[i] ); -// return; -// } -// } -// Q_ASSERT( false ); // should not happen -//} - -//void LocalProjectsManager::updateProjectErrors( const QString &projectDir, const QString &errMsg ) -//{ -// for ( int i = 0; i < mProjects.count(); ++i ) -// { -// if ( mProjects[i].projectDir == projectDir ) -// { -// // Effects only local project list, no need to send projectMetadataChanged -// mProjects[i].qgisProjectError = errMsg; -// return; -// } -// } -//} - void LocalProjectsManager::updateNamespace( const QString &projectDir, const QString &projectNamespace ) { for ( int i = 0; i < mProjects.count(); ++i ) @@ -279,81 +214,3 @@ void LocalProjectsManager::addProject( const QString &projectDir, const QString mProjects << project; emit localProjectAdded( project ); } - -//static QDateTime _getLastModifiedFileDateTime( const QString &path ) -//{ -// QDateTime lastModified; -// QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); -// while ( it.hasNext() ) -// { -// it.next(); -// if ( !MerginApi::isInIgnore( it.fileInfo() ) ) -// { -// if ( it.fileInfo().lastModified() > lastModified ) -// { -// lastModified = it.fileInfo().lastModified(); -// } -// } -// } -// return lastModified.toUTC(); -//} - -//static int _getProjectFilesCount( const QString &path ) -//{ -// int count = 0; -// QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories ); -// while ( it.hasNext() ) -// { -// it.next(); -// if ( !MerginApi::isInIgnore( it.fileInfo() ) ) -// { -// count++; -// } -// } -// return count; -//} - -//ProjectStatus::Status LocalProjectsManager::currentProjectStatus( const std::shared_ptr project ) -//{ -// if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project -// return ProjectStatus::NoVersion; - -// // There was no sync yet -// if ( project->local->localVersion < 0 ) -// { -// return ProjectStatus::NoVersion; -// } - -// // -// // TODO: this check for local modifications should be revisited -// // - -// // Something has locally changed after last sync with server -// QString metadataFilePath = project->local->projectDir + "/" + MerginApi::sMetadataFile; -// QDateTime lastModified = _getLastModifiedFileDateTime( project->local->projectDir ); -// QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified(); -// MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath ); -// int filesCount = _getProjectFilesCount( project->local->projectDir ); -// if ( lastSync < lastModified || meta.files.count() != filesCount ) -// { -// return ProjectStatus::Modified; -// } - -// // Version is lower than latest one, last sync also before updated -// if ( project->local->localVersion < project->mergin->serverVersion ) -// { -// return ProjectStatus::OutOfDate; -// } - -// return ProjectStatus::UpToDate; -//} - -//void LocalProjectsManager::updateProjectStatus( LocalProject_future &project ) -//{ -// ProjectStatus newStatus = currentProjectStatus( project ); -// if ( newStatus != project.status ) -// { -// project.status = newStatus; -// emit projectMetadataChanged( project.projectDir ); -// } -//} diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index 0aab423a6..f9af46f73 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -54,8 +54,6 @@ class LocalProjectsManager : public QObject //! Finds all QGIS project files and set the err variable if any occured. QString findQgisProjectFile( const QString &projectDir, QString &err ); - static ProjectStatus::Status currentProjectStatus( const std::shared_ptr project ); // TODO: maybe move somewhere else? - signals: void projectMetadataChanged( const QString &projectDir ); void localMerginProjectAdded( const QString &projectDir ); diff --git a/app/main.cpp b/app/main.cpp index 471441bec..a30c07ace 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -32,14 +32,12 @@ #include "ios/iosutils.h" #include "inpututils.h" #include "positiondirection.h" -#include "projectsmodel.h" #include "mapthemesmodel.h" #include "digitizingcontroller.h" #include "merginapi.h" #include "merginapistatus.h" #include "merginsubscriptionstatus.h" #include "merginsubscriptiontype.h" -#include "merginprojectmodel.h" #include "merginprojectstatusmodel.h" #include "layersproxymodel.h" #include "layersmodel.h" @@ -219,7 +217,6 @@ void initDeclarative() qmlRegisterUncreatableType( "lc", 1, 0, "MerginUserAuth", "" ); qmlRegisterUncreatableType( "lc", 1, 0, "MerginUserInfo", "" ); qmlRegisterUncreatableType( "lc", 1, 0, "MerginPlan", "" ); - qmlRegisterUncreatableType( "lc", 1, 0, "ProjectModel", "" ); qmlRegisterUncreatableType( "lc", 1, 0, "MapThemesModel", "" ); qmlRegisterUncreatableType( "lc", 1, 0, "Loader", "" ); qmlRegisterUncreatableType( "lc", 1, 0, "AppSettings", "" ); @@ -365,27 +362,14 @@ int main( int argc, char *argv[] ) // Create Input classes AndroidUtils au; IosUtils iosUtils; - LocalProjectsManager localProjects( projectDir ); - ProjectModel pm( localProjects ); + LocalProjectsManager localProjectsManager( projectDir ); MapThemesModel mtm; - std::unique_ptr ma = std::unique_ptr( new MerginApi( localProjects ) ); + std::unique_ptr ma = std::unique_ptr( new MerginApi( localProjectsManager ) ); InputUtils iu; - MerginProjectModel mpm( localProjects ); - MerginProjectStatusModel mpsm( localProjects ); + MerginProjectStatusModel mpsm( localProjectsManager ); InputHelp help( ma.get(), &iu ); ProjectWizard pw( projectDir ); - // project models - instance for each category - ProjectsModel_future myProjectsModel( ma.get(), ProjectModelTypes::MyProjectsModel, localProjects ); - ProjectsModel_future localProjectsModel( ma.get(), ProjectModelTypes::LocalProjectsModel, localProjects ); - ProjectsModel_future sharedProjectsModel( ma.get(), ProjectModelTypes::SharedProjectsModel, localProjects ); - ProjectsModel_future exploreProjectsModel( ma.get(), ProjectModelTypes::ExploreProjectsModel, localProjects ); - - ProjectsProxyModel_future myProjectsProxyModel( &myProjectsModel ); - ProjectsProxyModel_future localProjectsProxyModel( &localProjectsModel ); - ProjectsProxyModel_future sharedProjectsProxyModel( &sharedProjectsModel ); - ProjectsProxyModel_future exploreProjectsProxyModel( &exploreProjectsModel ); - // layer models LayersModel lm; LayersProxyModel browseLpm( &lm, LayerModelTypes::BrowseDataLayerSelection ); @@ -399,11 +383,7 @@ int main( int argc, char *argv[] ) // Connections QObject::connect( &app, &QGuiApplication::applicationStateChanged, &loader, &Loader::appStateChanged ); QObject::connect( &app, &QCoreApplication::aboutToQuit, &loader, &Loader::appAboutToQuit ); -// QObject::connect( ma.get(), &MerginApi::syncProjectFinished, &pm, &ProjectModel::syncedProjectFinished ); -// QObject::connect( ma.get(), &MerginApi::projectDetached, &pm, &ProjectModel::findProjectFiles ); - QObject::connect( &pw, &ProjectWizard::projectCreated, &localProjects, &LocalProjectsManager::addLocalProject ); -// QObject::connect( ma.get(), &MerginApi::listProjectsFinished, &mpm, &MerginProjectModel::updateModel ); -// QObject::connect( ma.get(), &MerginApi::syncProjectStatusChanged, &mpm, &MerginProjectModel::syncProjectStatusChanged ); + QObject::connect( &pw, &ProjectWizard::projectCreated, &localProjectsManager, &LocalProjectsManager::addLocalProject ); QObject::connect( ma.get(), &MerginApi::reloadProject, &loader, &Loader::reloadProject ); QObject::connect( &mtm, &MapThemesModel::mapThemeChanged, &recordingLpm, &LayersProxyModel::onMapThemeChanged ); QObject::connect( &loader, &Loader::projectReloaded, vm.get(), &VariablesManager::merginProjectChanged ); @@ -491,23 +471,17 @@ int main( int argc, char *argv[] ) engine.rootContext()->setContextProperty( "__inputUtils", &iu ); engine.rootContext()->setContextProperty( "__inputProjUtils", &inputProjUtils ); engine.rootContext()->setContextProperty( "__inputHelp", &help ); - engine.rootContext()->setContextProperty( "__projectsModel", &pm ); engine.rootContext()->setContextProperty( "__loader", &loader ); engine.rootContext()->setContextProperty( "__mapThemesModel", &mtm ); engine.rootContext()->setContextProperty( "__appSettings", &as ); engine.rootContext()->setContextProperty( "__merginApi", ma.get() ); - engine.rootContext()->setContextProperty( "__merginProjectsModel", &mpm ); engine.rootContext()->setContextProperty( "__merginProjectStatusModel", &mpsm ); engine.rootContext()->setContextProperty( "__recordingLayersModel", &recordingLpm ); engine.rootContext()->setContextProperty( "__browseDataLayersModel", &browseLpm ); engine.rootContext()->setContextProperty( "__activeLayer", &al ); engine.rootContext()->setContextProperty( "__purchasing", purchasing.get() ); engine.rootContext()->setContextProperty( "__projectWizard", &pw ); - - engine.rootContext()->setContextProperty( "__myProjectsModel", &myProjectsModel ); // TODO: maybe project models do not need to be exposed? - engine.rootContext()->setContextProperty( "__localProjectsModel", &localProjectsModel ); - engine.rootContext()->setContextProperty( "__myProjectsProxyModel", &myProjectsProxyModel ); - engine.rootContext()->setContextProperty( "__localProjectsProxyModel", &localProjectsProxyModel ); + engine.rootContext()->setContextProperty( "__localProjectsManager", &localProjectsManager ); #ifdef MOBILE_OS engine.rootContext()->setContextProperty( "__appwindowvisibility", QWindow::Maximized ); diff --git a/app/merginapi.cpp b/app/merginapi.cpp index f0dc9f9cc..4fc0a5c29 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -30,8 +30,7 @@ #include const QString MerginApi::sMetadataFile = QStringLiteral( "/.mergin/mergin.json" ); -//const QString MerginApi::sDefaultApiRoot = QStringLiteral( "https://public.cloudmergin.com/" ); -const QString MerginApi::sDefaultApiRoot = QStringLiteral( "https://dev.dev.cloudmergin.com/" ); +const QString MerginApi::sDefaultApiRoot = QStringLiteral( "https://public.cloudmergin.com/" ); const QSet MerginApi::sIgnoreExtensions = QSet() << "gpkg-shm" << "gpkg-wal" << "qgs~" << "qgz~" << "pyc" << "swap"; const QSet MerginApi::sIgnoreFiles = QSet() << "mergin.json" << ".DS_Store"; const int MerginApi::UPLOAD_CHUNK_SIZE = 10 * 1024 * 1024; // Should be the same as on Mergin server @@ -107,13 +106,11 @@ QString MerginApi::listProjects( const QString &searchExpression, const QString InputUtils::log( "list projects", QStringLiteral( "Requesting: " ) + url.toString() ); connect( reply, &QNetworkReply::finished, this, [this, requestId]() {this->listProjectsReplyFinished( requestId );} ); - qDebug() << "MerginAPI: ListProjects, returning requestId: " << requestId; return requestId; } QString MerginApi::listProjectsByName( const QStringList &projectNames ) { - setApiRoot( defaultApiRoot() ); // construct JSON body QJsonDocument body; QJsonObject projects; @@ -121,7 +118,6 @@ QString MerginApi::listProjectsByName( const QStringList &projectNames ) projects.insert( "projects", projectsArr ); body.setObject( projects ); - qDebug() << "PMR: listProjectsByName(): requesting projects " << projectNames; QUrl url( mApiRoot + QStringLiteral( "/v1/project/by_names" ) ); @@ -1188,11 +1184,6 @@ QString MerginApi::merginUserName() const return userAuth()->username(); } -//MerginProjectList MerginApi::projects() -//{ -// return mRemoteProjects; -//} - QList MerginApi::getLocalProjectFiles( const QString &projectPath ) { QList merginFiles; @@ -1235,22 +1226,6 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) projectCount = doc.object().value( "count" ).toInt(); projectList = parseProjectsFromJson( doc ); } -// else -// { -// mRemoteProjects.clear(); -// } - - // for any local projects we can update the latest server version - // TODO: this should now be done inside model so no need to do it here (LocalProjects do not have server version anymore) -// for ( MerginProjectListEntry project : mRemoteProjects ) -// { -// QString fullProjectName = getFullProjectName( project.projectNamespace, project.projectName ); -// LocalProjectInfo localProject = mLocalProjects.projectFromMerginName( fullProjectName ); -// if ( localProject.isValid() ) -// { -// mLocalProjects.updateMerginServerVersion( localProject.projectDir, project.version ); -// } -// } InputUtils::log( "list projects", QStringLiteral( "Success - got %1 projects" ).arg( projectList.count() ) ); } @@ -1260,7 +1235,6 @@ void MerginApi::listProjectsReplyFinished( QString requestId ) QString message = QStringLiteral( "Network API error: %1(): %2. %3" ).arg( QStringLiteral( "listProjects" ), r->errorString(), serverMsg ); emit networkErrorOccurred( serverMsg, QStringLiteral( "Mergin API error: listProjects" ) ); InputUtils::log( "list projects", QStringLiteral( "FAILED - %1" ).arg( message ) ); -// mRemoteProjects.clear(); emit listProjectsFailed(); } @@ -1513,7 +1487,6 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) // add the local project if not there yet if ( !mLocalProjects.projectFromMerginName( projectFullName ).isValid() ) { - qDebug() << "PMR: Downloaded project" << projectFullName; QString projectNamespace, projectName; extractProjectName( projectFullName, projectNamespace, projectName ); diff --git a/app/merginapi.h b/app/merginapi.h index 620e3cfeb..3cb52cfa7 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -166,29 +166,6 @@ struct TransactionStatus ProjectDiff diff; }; - -//struct MerginProjectListEntry // TODO: replace with RemoteProject from Project_future.h -//{ -// bool isValid() const { return !projectName.isEmpty() && !projectNamespace.isEmpty(); } - -// QString projectName; -// QString projectNamespace; -// int version = -1; -// QDateTime serverUpdated; // available latest version of project files on server - -// bool operator ==( const MerginProjectListEntry &other ) -// { -// return ( this->projectName == other.projectName ) && ( this->projectNamespace == other.projectNamespace ); -// } - -// bool operator !=( const MerginProjectListEntry &other ) -// { -// return !( *this == other ); -// } -//}; - -//typedef QList MerginProjectList; // TODO: replace with RemoteProject from Project_future.h - typedef QHash Transactions; Q_DECLARE_METATYPE( Transactions ); @@ -247,7 +224,7 @@ class MerginApi: public QObject /** * Sends non-blocking POST request to the server to download/update a project with a given name. On downloadProjectReplyFinished, * when a response is received, parses data-stream to files and rewrites local files with them. Extra files which don't match server - * files are removed. Eventually emits syncProjectFinished on which MerginProjectModel updates status of the project item. + * files are removed. Eventually emits syncProjectFinished on which ProjectModel updates status of the project item. * If update has been successful, updates metadata file of the project. * Emits also notify signal with a message for the GUI. * \param projectNamespace Project's namespace used in request. @@ -259,7 +236,7 @@ class MerginApi: public QObject /** * Sends non-blocking POST request to the server to upload changes in a project with a given name. * Firstly updateProject is triggered to fetch new changes. If it was successful, sends update post request with list of local changes - * and modified/newly added files in JSON. Eventually emits syncProjectFinished on which MerginProjectModel updates status of the project item. + * and modified/newly added files in JSON. Eventually emits syncProjectFinished on which ProjectModel updates status of the project item. * Emits also notify signal with a message for the GUI. * \param projectNamespace Project's namespace used in request. * \param projectName Project's name used in request. @@ -385,9 +362,6 @@ class MerginApi: public QObject */ static ProjectDiff compareProjectFiles( const QList &oldServerFiles, const QList &newServerFiles, const QList &localFiles, const QString &projectDir ); - //! Returns the most recent list of projects fetched from the server -// MerginProjectList projects(); - static QList getLocalProjectFiles( const QString &projectPath ); QString apiRoot() const; @@ -395,12 +369,6 @@ class MerginApi: public QObject QString merginUserName() const; // TODO: remove (use can be replaced with userInfo->username) - //! Disk usage of current logged in user in Mergin instance in Bytes - int diskUsage() const; // TODO: remove (no use) - - //! Total storage limit of current logged in user in Mergin instance in Bytes - int storageLimit() const; // TODO: remove (no use) - MerginApiStatus::VersionStatus apiVersionStatus() const; void setApiVersionStatus( const MerginApiStatus::VersionStatus &apiVersionStatus ); @@ -569,7 +537,6 @@ class MerginApi: public QObject QNetworkAccessManager mManager; QString mApiRoot; LocalProjectsManager &mLocalProjects; -// MerginProjectList mRemoteProjects; // TODO: remove (no use, only in tests - TBD) QString mDataDir; // dir with all projects MerginUserInfo *mUserInfo; //owned by this (qml grouped-properties) diff --git a/app/merginprojectmodel.cpp b/app/merginprojectmodel.cpp deleted file mode 100644 index d7109cb52..000000000 --- a/app/merginprojectmodel.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "merginprojectmodel.h" - -#include - -MerginProjectModel::MerginProjectModel( LocalProjectsManager &localProjects, QObject *parent ) - : QAbstractListModel( parent ) - , mLocalProjects( localProjects ) -{ - QObject::connect( &mLocalProjects, &LocalProjectsManager::projectMetadataChanged, this, &MerginProjectModel::projectMetadataChanged ); - QObject::connect( &mLocalProjects, &LocalProjectsManager::localMerginProjectAdded, this, &MerginProjectModel::onLocalProjectAdded ); - QObject::connect( &mLocalProjects, &LocalProjectsManager::localProjectRemoved, this, &MerginProjectModel::onLocalProjectRemoved ); - - mAdditionalItem->status = NonProjectItem; -} - -QVariant MerginProjectModel::data( const QModelIndex &index, int role ) const -{ - int row = index.row(); - if ( row < 0 || row >= mMerginProjects.count() ) - return QVariant(); - - const MerginProject *project = mMerginProjects.at( row ).get(); - - switch ( role ) - { - case ProjectName: - return QVariant( project->projectName ); - case ProjectNamespace: return QVariant( project->projectNamespace ); - case ProjectInfo: - { - // TODO: better project info - // TODO: clientUpdated currently not being set - if ( !project->clientUpdated.isValid() ) - { - return project->serverUpdated.toLocalTime().toString(); - } - else - { - return project->clientUpdated.toLocalTime().toString(); - } - } - - case Status: - { - switch ( project->status ) - { - case ProjectStatus::OutOfDate: - return QVariant( QStringLiteral( "outOfDate" ) ); - case ProjectStatus::UpToDate: - return QVariant( QStringLiteral( "upToDate" ) ); - case ProjectStatus::NoVersion: - return QVariant( QStringLiteral( "noVersion" ) ); - case ProjectStatus::Modified: - return QVariant( QStringLiteral( "modified" ) ); - case ProjectStatus::NonProjectItem: - return QVariant( QStringLiteral( "nonProjectItem" ) ); - } - break; - } - case Pending: return QVariant( project->pending ); - case PassesFilter: return mSearchExpression.isEmpty() || project->projectName.contains( mSearchExpression, Qt::CaseInsensitive ) - || project->projectNamespace.contains( mSearchExpression, Qt::CaseInsensitive ); - case SyncProgress: return QVariant( project->progress ); - } - - return QVariant(); -} - - -QHash MerginProjectModel::roleNames() const -{ - QHash roleNames = QAbstractListModel::roleNames(); - roleNames[ProjectName] = "projectName"; - roleNames[ProjectNamespace] = "projectNamespace"; - roleNames[ProjectInfo] = "projectInfo"; - roleNames[Status] = "status"; - roleNames[Pending] = "pendingProject"; - roleNames[PassesFilter] = "passesFilter"; - roleNames[SyncProgress] = "syncProgress"; - return roleNames; -} - -ProjectList MerginProjectModel::projects() -{ - return mMerginProjects; -} - -int MerginProjectModel::rowCount( const QModelIndex &parent ) const -{ - Q_UNUSED( parent ) - return mMerginProjects.count(); -} - -void MerginProjectModel::updateModel( const MerginProjectList &merginProjects, QHash pendingProjects, int expectedProjectCount, int page ) -{ - beginResetModel(); - mMerginProjects.removeOne( mAdditionalItem ); // TODO: remove - - if ( page == 1 ) - { - mMerginProjects.clear(); - } - setLastPage( page ); - - - for ( MerginProjectListEntry entry : merginProjects ) - { - QString fullProjectName = MerginApi::getFullProjectName( entry.projectNamespace, entry.projectName ); - std::shared_ptr project = std::make_shared(); - project->projectNamespace = entry.projectNamespace; - project->projectName = entry.projectName; - project->serverUpdated = entry.serverUpdated; - - // figure out info from local projects (projectDir etc) - LocalProjectInfo localProject = mLocalProjects.projectFromMerginName( entry.projectNamespace, entry.projectName ); - if ( localProject.isValid() ) - { - project->projectDir = localProject.projectDir; - project->status = localProject.status; - // TODO: what else to copy? - } - - if ( pendingProjects.contains( fullProjectName ) ) - { - - TransactionStatus projectTransaction = pendingProjects.value( fullProjectName ); - project->progress = projectTransaction.transferedSize / projectTransaction.totalSize; - project->pending = true; - } - - mMerginProjects << project; - } - - if ( mMerginProjects.count() < expectedProjectCount ) // TODO: remove - { - mMerginProjects << mAdditionalItem; - } - - endResetModel(); -} - -int MerginProjectModel::findProjectIndex( const QString &projectFullName ) -{ - int row = 0; - for ( std::shared_ptr project : mMerginProjects ) - { - if ( MerginApi::getFullProjectName( project->projectNamespace, project->projectName ) == projectFullName ) - return row; - row++; - } - return -1; -} - -void MerginProjectModel::setLastPage( int lastPage ) -{ - mLastPage = lastPage; - emit lastPageChanged(); -} - -int MerginProjectModel::lastPage() const -{ - return mLastPage; -} - -QString MerginProjectModel::searchExpression() const -{ - return mSearchExpression; -} - -void MerginProjectModel::setSearchExpression( const QString &searchExpression ) -{ - if ( searchExpression != mSearchExpression ) - { - mSearchExpression = searchExpression; - // Hack to model changed signal - beginResetModel(); - endResetModel(); - } -} - -void MerginProjectModel::syncProjectStatusChanged( const QString &projectFullName, qreal progress ) -{ - int row = findProjectIndex( projectFullName ); - if ( row == -1 ) - return; - - std::shared_ptr project = mMerginProjects[row]; - project->pending = progress >= 0; - project->progress = progress >= 0 ? progress : 0; - - QModelIndex ix = index( row ); - emit dataChanged( ix, ix ); -} - - -void MerginProjectModel::projectMetadataChanged( const QString &projectDir ) -{ - int row = 0; - for ( std::shared_ptr project : mMerginProjects ) - { - if ( project->projectDir == projectDir ) - { - LocalProjectInfo localProject = mLocalProjects.projectFromDirectory( projectDir ); - if ( !localProject.isValid() ) - return; - - // update cached information - project->status = localProject.status; - - QModelIndex ix = index( row ); - emit dataChanged( ix, ix ); - return; - } - row++; - } -} - -void MerginProjectModel::onLocalProjectAdded( const QString &projectDir ) -{ - LocalProjectInfo localProject = mLocalProjects.projectFromDirectory( projectDir ); - if ( !localProject.isValid() ) - return; - - QString projectFullName = MerginApi::getFullProjectName( localProject.projectNamespace, localProject.projectName ); - int i = findProjectIndex( projectFullName ); - if ( i == -1 ) - return; - - std::shared_ptr project = mMerginProjects[i]; - - // store project dir - project->projectDir = localProject.projectDir; - - // update metadata and emit dataChanged() signal - projectMetadataChanged( projectDir ); -} - -void MerginProjectModel::onLocalProjectRemoved( const QString &projectDir ) -{ - // TODO: implement - // (at this point this is not needed because after removal we need to switch tab - // and that will re-fetch the list of projects) - Q_UNUSED( projectDir ); -} - diff --git a/app/merginprojectmodel.h b/app/merginprojectmodel.h deleted file mode 100644 index e45eee127..000000000 --- a/app/merginprojectmodel.h +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef MERGINPROJECTMODEL_H -#define MERGINPROJECTMODEL_H - -#include -#include -#include -#include -#include "merginapi.h" - - -/** - * Basic information about a remote mergin project from the received list of projects. - * - * We add some local information for project is also available locally: - * - general local info: project dir, downloaded version number, project status - * - sync status: whether sync is active, progress indicator - */ -struct MerginProject -{ - QString projectName; - QString projectNamespace; - QString projectDir; // full path to the project directory - QDateTime clientUpdated; // client's version of project files - QDateTime serverUpdated; // available latest version of project files on server - bool pending = false; // if there is a pending request for downlaod/update a project - ProjectStatus status = NoVersion; - qreal progress = 0; // progress in case of pending download/upload (values [0..1]) -}; - -typedef QList> ProjectList; - - -class MerginProjectModel: public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression ) - Q_PROPERTY( int lastPage READ lastPage NOTIFY lastPageChanged ) - - public: - enum Roles - { - ProjectName = Qt::UserRole + 1, - ProjectNamespace, - Size, - ProjectInfo, - Status, - Pending, - PassesFilter, - SyncProgress - }; - Q_ENUMS( Roles ) - - explicit MerginProjectModel( LocalProjectsManager &localProjects, QObject *parent = nullptr ); - - Q_INVOKABLE QVariant data( const QModelIndex &index, int role ) const override; - - ProjectList projects(); - - QHash roleNames() const override; - - int rowCount( const QModelIndex &parent = QModelIndex() ) const override; - - /** - * Updates list of projects with synchronization progress if a project is pending. - * \param merginProjects List of mergin projects - * \param pendingProjects Projects in pending state - * \param expectedProjectCount Total number of projects - * \param page Int representing page. - */ - void updateModel( const MerginProjectList &merginProjects, QHash pendingProjects, int expectedProjectCount, int page ); - - int filterCreator() const; // TODO: remove, no use - void setFilterCreator( int filterCreator ); - - int filterWriter() const; // TODO: remove, no use - void setFilterWriter( int filterWriter ); - - QString searchExpression() const; // TODO: remove, search will be in proxy model - void setSearchExpression( const QString &searchExpression ); - - int lastPage() const; - void setLastPage( int lastPage ); - - signals: - void lastPageChanged(); - - public slots: - void syncProjectStatusChanged( const QString &projectFullName, qreal progress ); - - private slots: - - void projectMetadataChanged( const QString &projectDir ); - void onLocalProjectAdded( const QString &projectDir ); - void onLocalProjectRemoved( const QString &projectDir ); - - private: - - int findProjectIndex( const QString &projectFullName ); - - ProjectList mMerginProjects; - LocalProjectsManager &mLocalProjects; - QString mSearchExpression; - int mLastPage; - //! Special item as a placeholder for custom component with extended funtionality - std::shared_ptr mAdditionalItem = std::make_shared(); - -}; -#endif // MERGINPROJECTMODEL_H diff --git a/app/projectsmodel.cpp b/app/projectsmodel.cpp deleted file mode 100644 index bc2a2d17c..000000000 --- a/app/projectsmodel.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************** - qgsquicklayertreemodel.cpp - -------------------------------------- - Date : Nov 2017 - Copyright : (C) 2017 by Peter Petrik - Email : zilolv at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "projectsmodel.h" - -#include -#include -#include -#include - -#include "inpututils.h" -#include "merginapi.h" - -ProjectModel::ProjectModel( LocalProjectsManager &localProjects, QObject *parent ) - : QAbstractListModel( parent ) - , mLocalProjects( localProjects ) -{ - findProjectFiles(); - - QObject::connect( &mLocalProjects, &LocalProjectsManager::localProjectAdded, this, &ProjectModel::addLocalProject ); -} - -ProjectModel::~ProjectModel() {} - -void ProjectModel::findProjectFiles() -{ - beginResetModel(); - // populate from mLocalProjects - mProjectFiles.clear(); - const QList projects = mLocalProjects.projects(); - for ( const LocalProjectInfo &project : projects ) - { - QDir dir( project.projectDir ); - QFileInfo fi( project.qgisProjectFilePath ); - - ProjectFile projectFile; - projectFile.path = project.qgisProjectFilePath; - projectFile.folderName = dir.dirName(); - projectFile.projectName = project.projectName; - projectFile.projectNamespace = project.projectNamespace; - projectFile.isValid = project.isShowable(); - QDateTime created = fi.created().toLocalTime(); - if ( projectFile.isValid ) - { - projectFile.info = QString( created.toString() ); - } - else - { - projectFile.info = project.qgisProjectError; - } - mProjectFiles << projectFile; - } - - std::sort( mProjectFiles.begin(), mProjectFiles.end() ); - endResetModel(); -} - - -QVariant ProjectModel::data( const QModelIndex &index, int role ) const -{ - int row = index.row(); - if ( row < 0 || row >= mProjectFiles.count() ) - return QVariant( "" ); - - const ProjectFile &projectFile = mProjectFiles.at( row ); - - switch ( role ) - { - case ProjectName: return QVariant( projectFile.projectName ); - case ProjectNamespace: return QVariant( projectFile.projectNamespace ); - case FolderName: return QVariant( projectFile.folderName ); - case Path: return QVariant( projectFile.path ); - case ProjectInfo: return QVariant( projectFile.info ); - case IsValid: return QVariant( projectFile.isValid ); - case PassesFilter: return mSearchExpression.isEmpty() || projectFile.folderName.contains( mSearchExpression, Qt::CaseInsensitive ); - } - - return QVariant(); -} - -QHash ProjectModel::roleNames() const -{ - QHash roleNames = QAbstractListModel::roleNames(); - roleNames[ProjectName] = "projectName"; - roleNames[ProjectNamespace] = "projectNamespace"; - roleNames[FolderName] = "folderName"; - roleNames[Path] = "path"; - roleNames[ProjectInfo] = "projectInfo"; - roleNames[IsValid] = "isValid"; - roleNames[PassesFilter] = "passesFilter"; - return roleNames; -} - -QModelIndex ProjectModel::index( int row, int column, const QModelIndex &parent ) const -{ - Q_UNUSED( column ); - Q_UNUSED( parent ); - return createIndex( row, 0, nullptr ); -} - -int ProjectModel::rowAccordingPath( QString path ) const -{ - int i = 0; - for ( ProjectFile prj : mProjectFiles ) - { - if ( prj.path == path ) - { - return i; - } - i++; - } - return -1; -} - -void ProjectModel::deleteProject( int row ) -{ - if ( row < 0 || row >= mProjectFiles.length() ) - { - InputUtils::log( "Deleting local project error", QStringLiteral( "Unable to delete local project, index out of bounds" ) ); - return; - } - - ProjectFile project = mProjectFiles.at( row ); - - mLocalProjects.deleteProjectDirectory( mLocalProjects.dataDir() + "/" + project.folderName ); - - beginResetModel(); - mProjectFiles.removeAt( row ); - endResetModel(); -} - -int ProjectModel::rowCount( const QModelIndex &parent ) const -{ - Q_UNUSED( parent ); - return mProjectFiles.count(); -} - -QString ProjectModel::dataDir() const -{ - return mLocalProjects.dataDir(); -} - -QString ProjectModel::searchExpression() const -{ - return mSearchExpression; -} - -void ProjectModel::setSearchExpression( const QString &searchExpression ) -{ - if ( searchExpression != mSearchExpression ) - { - mSearchExpression = searchExpression; - // Hack to model changed signal - beginResetModel(); - endResetModel(); - } -} - -bool ProjectModel::containsProject( const QString &projectNamespace, const QString &projectName ) -{ - return mLocalProjects.hasMerginProject( projectNamespace, projectName ); -} - -void ProjectModel::syncedProjectFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) -{ - - // Do basic validity check - if ( successfully ) - { - QString errMsg; - mLocalProjects.findQgisProjectFile( projectDir, errMsg ); - mLocalProjects.updateProjectErrors( projectDir, errMsg ); - } - - reloadProjectFiles( projectDir, projectFullName, successfully ); -} - -void ProjectModel::addLocalProject( const QString &projectDir ) // TODO: not needed if localProjectsManager emits added signal -{ - Q_UNUSED( projectDir ); - mLocalProjects.reloadProjectDir(); - findProjectFiles(); -} - -void ProjectModel::reloadProjectFiles( QString projectFolder, QString projectName, bool successful ) -{ - if ( !successful ) return; - - if ( projectFolder.isEmpty() ) return; - - Q_UNUSED( projectName ); - findProjectFiles(); -} diff --git a/app/projectsmodel.h b/app/projectsmodel.h deleted file mode 100644 index 6a2905c7b..000000000 --- a/app/projectsmodel.h +++ /dev/null @@ -1,125 +0,0 @@ -/*************************************************************************** - qgsquicklayertreemodel.h - -------------------------------------- - Date : Nov 2017 - Copyright : (C) 2017 by Peter Petrik - Email : zilolv at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - -#ifndef PROJECTSMODEL_H -#define PROJECTSMODEL_H - -#include -#include -#include - -class LocalProjectsManager; - -/* - * Given data directory, find all QGIS projects (*.qgs or *.qgz) in the directory and subdirectories - * and create list model from them. Available are full path to the file, name of the project - * and short name of the project (clipped to N chars) - */ -class ProjectModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY( QString dataDir READ dataDir ) // never changes - Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression ) - - public: - enum Roles - { - ProjectName = Qt::UserRole + 1, // name of a project file - ProjectNamespace, - FolderName, - Path, - ProjectInfo, - Size, - IsValid, - PassesFilter - }; - Q_ENUMS( Roles ) - - explicit ProjectModel( LocalProjectsManager &localProjects, QObject *parent = nullptr ); - ~ProjectModel() override; - - Q_INVOKABLE QVariant data( const QModelIndex &index, int role ) const override; - Q_INVOKABLE QModelIndex index( int row, int column = 0, const QModelIndex &parent = QModelIndex() ) const override; - Q_INVOKABLE int rowAccordingPath( QString path ) const; - Q_INVOKABLE void deleteProject( int row ); - - QHash roleNames() const override; - - int rowCount( const QModelIndex &parent = QModelIndex() ) const override; - - QString dataDir() const; - - QString searchExpression() const; - void setSearchExpression( const QString &searchExpression ); - - // Test function - bool containsProject( const QString &projectNamespace, const QString &projectName ); - - public slots: - void syncedProjectFinished( const QString &projectDir, const QString &projectFullName, bool successfully ); - void addLocalProject( const QString &projectDir ); - void findProjectFiles(); - - private: - void reloadProjectFiles( QString projectFolder, QString projectName, bool successful ); - - struct ProjectFile - { - QString projectName; //!< mergin project name (second part of "namespace/project"). empty for non-mergin project - QString projectNamespace; //!< mergin project namespace (first part of "namespace/project"). empty for non-mergin project - QString folderName; //!< name of the project folder (not the full path) - QString path; //!< path to the .qgs/.qgz project file - QString info; // Description! - bool isValid; - - /** - * Ordering of local projects: first non-mergin projects (using folder name), - * then mergin projects (sorted first by namespace, then project name) - */ - bool operator < ( const ProjectFile &other ) const - { - if ( projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) - { - return folderName.compare( other.folderName, Qt::CaseInsensitive ) < 0; - } - if ( !projectNamespace.isEmpty() && other.projectNamespace.isEmpty() ) - { - return false; - } - if ( projectNamespace.isEmpty() && !other.projectNamespace.isEmpty() ) - { - return true; - } - - if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) == 0 ) - { - return projectName.compare( other.projectName, Qt::CaseInsensitive ) < 0; - } - if ( projectNamespace.compare( other.projectNamespace, Qt::CaseInsensitive ) < 0 ) - { - return true; - } - else - return false; - } - }; - LocalProjectsManager &mLocalProjects; - QList mProjectFiles; - QString mSearchExpression; - -}; - -#endif // PROJECTSMODEL_H diff --git a/app/sources.pri b/app/sources.pri index 99b152f6f..a8110e8f3 100644 --- a/app/sources.pri +++ b/app/sources.pri @@ -7,7 +7,6 @@ layersproxymodel.cpp \ localprojectsmanager.cpp \ main.cpp \ merginprojectmetadata.cpp \ -projectsmodel.cpp \ projectwizard.cpp \ loader.cpp \ digitizingcontroller.cpp \ @@ -17,7 +16,6 @@ merginapi.cpp \ merginapistatus.cpp \ merginsubscriptionstatus.cpp \ merginsubscriptiontype.cpp \ -merginprojectmodel.cpp \ merginprojectstatusmodel.cpp \ merginuserauth.cpp \ merginuserinfo.cpp \ @@ -52,7 +50,6 @@ layersmodel.h \ layersproxymodel.h \ localprojectsmanager.h \ merginprojectmetadata.h \ -projectsmodel.h \ projectwizard.h \ loader.h \ digitizingcontroller.h \ @@ -62,7 +59,6 @@ merginapi.h \ merginapistatus.h \ merginsubscriptionstatus.h \ merginsubscriptiontype.h \ -merginprojectmodel.h \ merginprojectstatusmodel.h \ merginuserauth.h \ merginuserinfo.h \ From 8b7bf8f5ecaa1a64d5af691edb3a5cce1c552a57 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 12:47:05 +0200 Subject: [PATCH 17/22] rename symbols _future --- app/localprojectsmanager.cpp | 24 +-- app/localprojectsmanager.h | 16 +- app/main.cpp | 10 +- app/merginapi.cpp | 22 +-- app/merginapi.h | 6 +- app/merginprojectstatusmodel.cpp | 2 +- app/{project_future.cpp => project.cpp} | 8 +- app/{project_future.h => project.h} | 48 +++--- ...ectsmodel_future.cpp => projectsmodel.cpp} | 142 +++++++++--------- ...projectsmodel_future.h => projectsmodel.h} | 28 ++-- ...odel_future.cpp => projectsproxymodel.cpp} | 38 ++--- ...oxymodel_future.h => projectsproxymodel.h} | 26 ++-- app/sources.pri | 12 +- 13 files changed, 191 insertions(+), 191 deletions(-) rename app/{project_future.cpp => project.cpp} (93%) rename app/{project_future.h => project.h} (76%) rename app/{projectsmodel_future.cpp => projectsmodel.cpp} (75%) rename app/{projectsmodel_future.h => projectsmodel.h} (88%) rename app/{projectsproxymodel_future.cpp => projectsproxymodel.cpp} (57%) rename app/{projectsproxymodel_future.h => projectsproxymodel.h} (61%) diff --git a/app/localprojectsmanager.cpp b/app/localprojectsmanager.cpp index 277e20829..258ada02e 100644 --- a/app/localprojectsmanager.cpp +++ b/app/localprojectsmanager.cpp @@ -28,7 +28,7 @@ void LocalProjectsManager::reloadDataDir() QStringList entryList = QDir( mDataDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs ); for ( const QString &folderName : entryList ) { - LocalProject_future info; + LocalProject info; info.projectDir = mDataDir + "/" + folderName; info.qgisProjectFilePath = findQgisProjectFile( info.projectDir, info.projectError ); @@ -51,37 +51,37 @@ void LocalProjectsManager::reloadDataDir() emit dataDirReloaded(); } -LocalProject_future LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const +LocalProject LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const { - for ( const LocalProject_future &info : mProjects ) + for ( const LocalProject &info : mProjects ) { if ( info.projectDir == projectDir ) return info; } - return LocalProject_future(); + return LocalProject(); } -LocalProject_future LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const +LocalProject LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const { - for ( const LocalProject_future &info : mProjects ) + for ( const LocalProject &info : mProjects ) { if ( info.qgisProjectFilePath == projectFilePath ) return info; } - return LocalProject_future(); + return LocalProject(); } -LocalProject_future LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const +LocalProject LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const { - for ( const LocalProject_future &info : mProjects ) + for ( const LocalProject &info : mProjects ) { if ( info.id() == projectFullName ) return info; } - return LocalProject_future(); + return LocalProject(); } -LocalProject_future LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const +LocalProject LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const { return projectFromMerginName( MerginApi::getFullProjectName( projectNamespace, projectName ) ); } @@ -205,7 +205,7 @@ QString LocalProjectsManager::findQgisProjectFile( const QString &projectDir, QS void LocalProjectsManager::addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName ) { - LocalProject_future project; + LocalProject project; project.projectDir = projectDir; project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.projectError ); project.projectName = projectName; diff --git a/app/localprojectsmanager.h b/app/localprojectsmanager.h index f9af46f73..9ff18bd7a 100644 --- a/app/localprojectsmanager.h +++ b/app/localprojectsmanager.h @@ -11,7 +11,7 @@ #define LOCALPROJECTSMANAGER_H #include -#include +#include class LocalProjectsManager : public QObject { @@ -26,11 +26,11 @@ class LocalProjectsManager : public QObject LocalProjectsList projects() const { return mProjects; } - LocalProject_future projectFromDirectory( const QString &projectDir ) const; - LocalProject_future projectFromProjectFilePath( const QString &projectFilePath ) const; + LocalProject projectFromDirectory( const QString &projectDir ) const; + LocalProject projectFromProjectFilePath( const QString &projectFilePath ) const; - LocalProject_future projectFromMerginName( const QString &projectFullName ) const; - LocalProject_future projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const; + LocalProject projectFromMerginName( const QString &projectFullName ) const; + LocalProject projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const; //! Adds entry about newly created project void addLocalProject( const QString &projectDir, const QString &projectName ); @@ -57,9 +57,9 @@ class LocalProjectsManager : public QObject signals: void projectMetadataChanged( const QString &projectDir ); void localMerginProjectAdded( const QString &projectDir ); - void localProjectAdded( const LocalProject_future &project ); - void aboutToRemoveLocalProject( const LocalProject_future project ); - void localProjectDataChanged( const LocalProject_future &project ); + void localProjectAdded( const LocalProject &project ); + void aboutToRemoveLocalProject( const LocalProject project ); + void localProjectDataChanged( const LocalProject &project ); void dataDirReloaded(); private: diff --git a/app/main.cpp b/app/main.cpp index a30c07ace..c327b7e8f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -52,9 +52,9 @@ #include "projectwizard.h" #include "codefilter.h" -#include "projectsmodel_future.h" -#include "projectsproxymodel_future.h" -#include "project_future.h" +#include "projectsmodel.h" +#include "projectsproxymodel.h" +#include "project.h" #ifdef INPUT_TEST #include "test/testmerginapi.h" @@ -232,8 +232,8 @@ void initDeclarative() qmlRegisterType( "lc", 1, 0, "FieldsModel" ); qmlRegisterType( "lc", 1, 0, "CodeFilter" ); - qmlRegisterType( "lc", 1, 0, "ProjectsModel" ); - qmlRegisterType( "lc", 1, 0, "ProjectsProxyModel" ); + qmlRegisterType( "lc", 1, 0, "ProjectsModel" ); + qmlRegisterType( "lc", 1, 0, "ProjectsProxyModel" ); qmlRegisterUncreatableMetaObject( ProjectStatus::staticMetaObject, "lc", 1, 0, "ProjectStatus", "ProjectStatus Enum" ); } diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 4fc0a5c29..4a2d9e495 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -756,7 +756,7 @@ void MerginApi::createProjectFinished() extractProjectName( projectFullName, projectNamespace, projectName ); // Upload data if createProject has been called for a local project with empty namespace (case of migrating a project) - for ( const LocalProject_future &info : mLocalProjects.projects() ) + for ( const LocalProject &info : mLocalProjects.projects() ) { if ( info.projectName == projectName && info.projectNamespace.isEmpty() ) { @@ -936,7 +936,7 @@ QNetworkReply *MerginApi::getProjectInfo( const QString &projectFullName, bool w } int sinceVersion = -1; - LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( projectInfo.isValid() ) { // let's also fetch the recent history of diffable files @@ -1061,7 +1061,7 @@ QString MerginApi::extractServerErrorMsg( const QByteArray &data ) } -LocalProject_future MerginApi::getLocalProject( const QString &projectFullName ) +LocalProject MerginApi::getLocalProject( const QString &projectFullName ) { return mLocalProjects.projectFromMerginName( projectFullName ); } @@ -1135,7 +1135,7 @@ void MerginApi::detachProjectFromMergin( const QString &projectNamespace, const { // Remove mergin folder QString projectFullName = getFullProjectName( projectNamespace, projectName ); - LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( projectInfo.isValid() ) { @@ -1384,7 +1384,7 @@ void MerginApi::finalizeProjectUpdateApplyDiff( const QString &projectFullName, // not good... something went wrong in rebase - we need to save the local changes // let's put them into a conflict file and use the server version - LocalProject_future info = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject info = mLocalProjects.projectFromMerginName( projectFullName ); QString newDest = InputUtils::findUniquePath( generateConflictFileName( dest, info.localVersion ), false ); if ( !QFile::rename( dest, newDest ) ) { @@ -1438,7 +1438,7 @@ void MerginApi::finalizeProjectUpdate( const QString &projectFullName ) { // move local file to conflict file QString origPath = projectDir + "/" + finalizationItem.filePath; - LocalProject_future info = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject info = mLocalProjects.projectFromMerginName( projectFullName ); QString newPath = InputUtils::findUniquePath( generateConflictFileName( origPath, info.localVersion ), false ); if ( !QFile::rename( origPath, newPath ) ) { @@ -1666,7 +1666,7 @@ void MerginApi::startProjectUpdate( const QString &projectFullName, const QByteA Q_ASSERT( mTransactionalStatus.contains( projectFullName ) ); TransactionStatus &transaction = mTransactionalStatus[projectFullName]; - LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( projectInfo.isValid() ) // If project is already downloaded { transaction.projectDir = projectInfo.projectDir; @@ -1843,7 +1843,7 @@ void MerginApi::uploadInfoReplyFinished() transaction.replyUploadProjectInfo->deleteLater(); transaction.replyUploadProjectInfo = nullptr; - LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); transaction.projectDir = projectInfo.projectDir; Q_ASSERT( !transaction.projectDir.isEmpty() ); @@ -2261,9 +2261,9 @@ ProjectDiff MerginApi::compareProjectFiles( const QList &oldServerFi return diff; } -MerginProject_future MerginApi::parseProjectMetadata( const QJsonObject &proj ) +MerginProject MerginApi::parseProjectMetadata( const QJsonObject &proj ) { - MerginProject_future project; + MerginProject project; if ( proj.isEmpty() ) { @@ -2325,7 +2325,7 @@ MerginProjectsList MerginApi::parseProjectsFromJson( const QJsonDocument &doc ) { for ( auto it = object.begin(); it != object.end(); ++it ) { - MerginProject_future project = parseProjectMetadata( it->toObject() ); + MerginProject project = parseProjectMetadata( it->toObject() ); if ( !project.remoteError.isEmpty() ) { // add project namespace/name from object name in case of error diff --git a/app/merginapi.h b/app/merginapi.h index 3cb52cfa7..d621da22c 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -27,7 +27,7 @@ #include "merginsubscriptionstatus.h" #include "merginprojectmetadata.h" #include "localprojectsmanager.h" -#include "project_future.h" +#include "project.h" class MerginUserAuth; class MerginUserInfo; @@ -309,7 +309,7 @@ class MerginApi: public QObject Q_INVOKABLE void detachProjectFromMergin( const QString &projectNamespace, const QString &projectName ); - LocalProject_future getLocalProject( const QString &projectFullName ); // Test function + LocalProject getLocalProject( const QString &projectFullName ); // Test function static const int MERGIN_API_VERSION_MAJOR = 2020; static const int MERGIN_API_VERSION_MINOR = 4; @@ -439,7 +439,7 @@ class MerginApi: public QObject void pingMerginReplyFinished(); private: - MerginProject_future parseProjectMetadata( const QJsonObject &project ); + MerginProject parseProjectMetadata( const QJsonObject &project ); MerginProjectsList parseProjectsFromJson( const QJsonDocument &object ); static QStringList generateChunkIdsForSize( qint64 fileSize ); QJsonArray prepareUploadChangesJSON( const QList &files ); diff --git a/app/merginprojectstatusmodel.cpp b/app/merginprojectstatusmodel.cpp index 335ec99a1..ff7097835 100644 --- a/app/merginprojectstatusmodel.cpp +++ b/app/merginprojectstatusmodel.cpp @@ -125,7 +125,7 @@ void MerginProjectStatusModel::infoProjectUpdated( const ProjectDiff &projectDif bool MerginProjectStatusModel::loadProjectInfo( const QString &projectFullName ) { - LocalProject_future projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); + LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); if ( !projectInfo.projectDir.isEmpty() ) { ProjectDiff diff = MerginApi::localProjectChanges( projectInfo.projectDir ); diff --git a/app/project_future.cpp b/app/project.cpp similarity index 93% rename from app/project_future.cpp rename to app/project.cpp index d7406850c..f95332d22 100644 --- a/app/project_future.cpp +++ b/app/project.cpp @@ -7,11 +7,11 @@ * * ***************************************************************************/ -#include "project_future.h" +#include "project.h" #include "merginapi.h" #include "inpututils.h" -QString LocalProject_future::id() const +QString LocalProject::id() const { if ( !projectName.isEmpty() && !projectNamespace.isEmpty() ) return MerginApi::getFullProjectName( projectNamespace, projectName ); @@ -20,12 +20,12 @@ QString LocalProject_future::id() const return dir.dirName(); } -QString MerginProject_future::id() const +QString MerginProject::id() const { return MerginApi::getFullProjectName( projectNamespace, projectName ); } -ProjectStatus::Status ProjectStatus::projectStatus( const std::shared_ptr project ) +ProjectStatus::Status ProjectStatus::projectStatus( const std::shared_ptr project ) { if ( !project || !project->isMergin() || !project->isLocal() ) // This is not a Mergin project or not downloaded project return ProjectStatus::NoVersion; diff --git a/app/project_future.h b/app/project.h similarity index 76% rename from app/project_future.h rename to app/project.h index 87c433158..2a9195d01 100644 --- a/app/project_future.h +++ b/app/project.h @@ -7,8 +7,8 @@ * * ***************************************************************************/ -#ifndef PROJECT_FUTURE_H -#define PROJECT_FUTURE_H +#ifndef PROJECT_H +#define PROJECT_H #include #include @@ -16,7 +16,7 @@ #include #include -struct Project_future; +struct Project; namespace ProjectStatus { Q_NAMESPACE @@ -30,13 +30,13 @@ namespace ProjectStatus { }; Q_ENUM_NS( Status ) - Status projectStatus( const std::shared_ptr project ); + Status projectStatus( const std::shared_ptr project ); } -struct LocalProject_future +struct LocalProject { - LocalProject_future() {}; // TODO: define copy constructor - ~LocalProject_future() {}; + LocalProject() {}; // TODO: define copy constructor + ~LocalProject() {}; QString projectName; QString projectNamespace; @@ -52,21 +52,21 @@ struct LocalProject_future bool isValid() { return !projectDir.isEmpty(); } - bool operator ==( const LocalProject_future &other ) + bool operator ==( const LocalProject &other ) { return ( this->id() == other.id() ); } - bool operator !=( const LocalProject_future &other ) + bool operator !=( const LocalProject &other ) { return !( *this == other ); } }; -struct MerginProject_future +struct MerginProject { - MerginProject_future() {}; - ~MerginProject_future() {}; + MerginProject() {}; + ~MerginProject() {}; QString projectName; QString projectNamespace; @@ -84,24 +84,24 @@ struct MerginProject_future // Maybe better use enum or int for error code QString remoteError; // Error leading to project not being able to sync - bool operator ==( const MerginProject_future &other ) + bool operator ==( const MerginProject &other ) { return ( this->id() == other.id() ); } - bool operator !=( const MerginProject_future &other ) + bool operator !=( const MerginProject &other ) { return !( *this == other ); } }; -struct Project_future +struct Project { - Project_future() {}; - ~Project_future() {}; + Project() {}; + ~Project() {}; - std::unique_ptr mergin; - std::unique_ptr local; + std::unique_ptr mergin; + std::unique_ptr local; bool isMergin() const { return mergin != nullptr; } bool isLocal() const { return local != nullptr; } @@ -127,7 +127,7 @@ struct Project_future return QString(); } - bool operator ==( const Project_future &other ) + bool operator ==( const Project &other ) { if ( this->isLocal() && other.isLocal() ) { @@ -140,13 +140,13 @@ struct Project_future return false; } - bool operator !=( const Project_future &other ) + bool operator !=( const Project &other ) { return !( *this == other ); } }; -typedef QList MerginProjectsList; -typedef QList LocalProjectsList; +typedef QList MerginProjectsList; +typedef QList LocalProjectsList; -#endif // PROJECT_FUTURE_H +#endif // PROJECT_H diff --git a/app/projectsmodel_future.cpp b/app/projectsmodel.cpp similarity index 75% rename from app/projectsmodel_future.cpp rename to app/projectsmodel.cpp index 042a1a8b2..1e23e1d8e 100644 --- a/app/projectsmodel_future.cpp +++ b/app/projectsmodel.cpp @@ -7,51 +7,51 @@ * * ***************************************************************************/ -#include "projectsmodel_future.h" +#include "projectsmodel.h" #include "localprojectsmanager.h" #include "inpututils.h" #include "merginuserauth.h" -ProjectsModel_future::ProjectsModel_future( QObject *parent ) : QAbstractListModel( parent ) +ProjectsModel::ProjectsModel( QObject *parent ) : QAbstractListModel( parent ) { qDebug() << "PMR: Instantiated ProjectsModel! " << this << "MerginAPI: " << mBackend << "LPM:" << mLocalProjectsManager << "Type: " << mModelType; } -void ProjectsModel_future::initializeProjectsModel() +void ProjectsModel::initializeProjectsModel() { if ( !mBackend || !mLocalProjectsManager || mModelType == EmptyProjectsModel ) // Model is not set up properly yet return; qDebug() << "PMR: initializing projects model " << this; - QObject::connect( mBackend, &MerginApi::syncProjectStatusChanged, this, &ProjectsModel_future::onProjectSyncProgressChanged ); - QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel_future::onProjectSyncFinished ); - QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel_future::onProjectDetachedFromMergin ); - QObject::connect( mBackend, &MerginApi::projectAttachedToMergin, this, &ProjectsModel_future::onProjectAttachedToMergin ); + QObject::connect( mBackend, &MerginApi::syncProjectStatusChanged, this, &ProjectsModel::onProjectSyncProgressChanged ); + QObject::connect( mBackend, &MerginApi::syncProjectFinished, this, &ProjectsModel::onProjectSyncFinished ); + QObject::connect( mBackend, &MerginApi::projectDetached, this, &ProjectsModel::onProjectDetachedFromMergin ); + QObject::connect( mBackend, &MerginApi::projectAttachedToMergin, this, &ProjectsModel::onProjectAttachedToMergin ); if ( mModelType == ProjectModelTypes::LocalProjectsModel ) { - QObject::connect( mBackend, &MerginApi::listProjectsByNameFinished, this, &ProjectsModel_future::onListProjectsByNameFinished ); + QObject::connect( mBackend, &MerginApi::listProjectsByNameFinished, this, &ProjectsModel::onListProjectsByNameFinished ); loadLocalProjects(); } else if ( mModelType != ProjectModelTypes::RecentProjectsModel ) { - QObject::connect( mBackend, &MerginApi::listProjectsFinished, this, &ProjectsModel_future::onListProjectsFinished ); + QObject::connect( mBackend, &MerginApi::listProjectsFinished, this, &ProjectsModel::onListProjectsFinished ); } else { // Implement RecentProjectsModel type } - QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel_future::onProjectAdded ); - QObject::connect( mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel_future::onAboutToRemoveProject ); - QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel_future::onProjectDataChanged ); - QObject::connect( mLocalProjectsManager, &LocalProjectsManager::dataDirReloaded, this, &ProjectsModel_future::loadLocalProjects ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectAdded, this, &ProjectsModel::onProjectAdded ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::aboutToRemoveLocalProject, this, &ProjectsModel::onAboutToRemoveProject ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::localProjectDataChanged, this, &ProjectsModel::onProjectDataChanged ); + QObject::connect( mLocalProjectsManager, &LocalProjectsManager::dataDirReloaded, this, &ProjectsModel::loadLocalProjects ); emit modelInitialized(); } -QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const +QVariant ProjectsModel::data( const QModelIndex &index, int role ) const { if ( !index.isValid() ) return QVariant(); @@ -59,7 +59,7 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const if ( index.row() < 0 || index.row() >= mProjects.size() ) return QVariant(); - std::shared_ptr project = mProjects.at( index.row() ); + std::shared_ptr project = mProjects.at( index.row() ); switch ( role ) { case ProjectName: return QVariant( project->projectName() ); @@ -104,14 +104,14 @@ QVariant ProjectsModel_future::data( const QModelIndex &index, int role ) const } } -QModelIndex ProjectsModel_future::index( int row, int col, const QModelIndex &parent ) const +QModelIndex ProjectsModel::index( int row, int col, const QModelIndex &parent ) const { Q_UNUSED( col ) Q_UNUSED( parent ) return createIndex( row, 0, nullptr ); } -QHash ProjectsModel_future::roleNames() const +QHash ProjectsModel::roleNames() const { QHash roles; roles[Roles::ProjectName] = QStringLiteral( "ProjectName" ).toLatin1(); @@ -131,12 +131,12 @@ QHash ProjectsModel_future::roleNames() const return roles; } -int ProjectsModel_future::rowCount( const QModelIndex & ) const +int ProjectsModel::rowCount( const QModelIndex & ) const { return mProjects.count(); } -void ProjectsModel_future::listProjects( const QString &searchExpression, int page ) +void ProjectsModel::listProjects( const QString &searchExpression, int page ) { if ( mModelType == LocalProjectsModel ) { @@ -147,7 +147,7 @@ void ProjectsModel_future::listProjects( const QString &searchExpression, int pa mLastRequestId = mBackend->listProjects( searchExpression, modelTypeToFlag(), "", page ); } -void ProjectsModel_future::listProjectsByName() +void ProjectsModel::listProjectsByName() { if ( mModelType != LocalProjectsModel ) { @@ -157,7 +157,7 @@ void ProjectsModel_future::listProjectsByName() mLastRequestId = mBackend->listProjectsByName( projectNames() ); } -bool ProjectsModel_future::hasMoreProjects() const +bool ProjectsModel::hasMoreProjects() const { if ( mProjects.size() < mServerProjectsCount ) return true; @@ -165,18 +165,18 @@ bool ProjectsModel_future::hasMoreProjects() const return false; } -void ProjectsModel_future::fetchAnotherPage( const QString &searchExpression ) +void ProjectsModel::fetchAnotherPage( const QString &searchExpression ) { listProjects( searchExpression, mPaginatedPage + 1 ); } -QVariant ProjectsModel_future::dataFrom( int fromRole, QVariant fromValue, int desiredRole ) const +QVariant ProjectsModel::dataFrom( int fromRole, QVariant fromValue, int desiredRole ) const { switch ( fromRole ) { case ProjectId: { - std::shared_ptr project = projectFromId( fromValue.toString() ); + std::shared_ptr project = projectFromId( fromValue.toString() ); if ( project ) { QModelIndex ix = index( mProjects.indexOf( project ) ); @@ -202,7 +202,7 @@ QVariant ProjectsModel_future::dataFrom( int fromRole, QVariant fromValue, int d return QVariant(); } -void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) +void ProjectsModel::onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ) { qDebug() << "PMR: onListProjectsFinished(): received response with requestId = " << requestId; if ( mLastRequestId != requestId ) @@ -235,7 +235,7 @@ void ProjectsModel_future::onListProjectsFinished( const MerginProjectsList &mer emit hasMoreProjectsChanged(); } -void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ) +void ProjectsModel::onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ) { qDebug() << "PMR: onListProjectsByNameFinished(): received response with requestId = " << requestId; if ( mLastRequestId != requestId ) @@ -250,7 +250,7 @@ void ProjectsModel_future::onListProjectsByNameFinished( const MerginProjectsLis endResetModel(); } -void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious ) +void ProjectsModel::mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious ) { LocalProjectsList localProjects = mLocalProjectsManager->projects(); @@ -264,17 +264,17 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec // Keep all local projects and ignore all not downloaded remote projects for ( const auto &localProject : localProjects ) { - std::shared_ptr project = std::shared_ptr( new Project_future() ); - project->local = std::unique_ptr( new LocalProject_future( localProject ) ); + std::shared_ptr project = std::shared_ptr( new Project() ); + project->local = std::unique_ptr( new LocalProject( localProject ) ); - MerginProject_future remoteEntry; + MerginProject remoteEntry; remoteEntry.projectName = project->local->projectName; remoteEntry.projectNamespace = project->local->projectNamespace; if ( merginProjects.contains( remoteEntry ) ) { int i = merginProjects.indexOf( remoteEntry ); - project->mergin = std::unique_ptr( new MerginProject_future( merginProjects[i] ) ); + project->mergin = std::unique_ptr( new MerginProject( merginProjects[i] ) ); if ( pendingProjects.contains( project->mergin->id() ) ) { @@ -287,7 +287,7 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec else if ( project->local->localVersion > -1 ) { // this is indeed a Mergin project, it has metadata folder in it - project->mergin = std::unique_ptr( new MerginProject_future() ); + project->mergin = std::unique_ptr( new MerginProject() ); project->mergin->projectName = project->local->projectName; project->mergin->projectNamespace = project->local->projectNamespace; project->mergin->status = ProjectStatus::projectStatus( project ); @@ -301,8 +301,8 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec // Keep all remote projects and ignore all non mergin projects from local projects for ( const auto &remoteEntry : merginProjects ) { - std::shared_ptr project = std::shared_ptr( new Project_future() ); - project->mergin = std::unique_ptr( new MerginProject_future( remoteEntry ) ); + std::shared_ptr project = std::shared_ptr( new Project() ); + project->mergin = std::unique_ptr( new MerginProject( remoteEntry ) ); if ( pendingProjects.contains( project->mergin->id() ) ) { @@ -312,14 +312,14 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec } // find downloaded projects - LocalProject_future localProject; + LocalProject localProject; localProject.projectName = project->mergin->projectName; localProject.projectNamespace = project->mergin->projectNamespace; if ( localProjects.contains( localProject ) ) { int ix = localProjects.indexOf( localProject ); - project->local = std::unique_ptr( new LocalProject_future( localProjects[ix] ) ); + project->local = std::unique_ptr( new LocalProject( localProjects[ix] ) ); } project->mergin->status = ProjectStatus::projectStatus( project ); @@ -328,9 +328,9 @@ void ProjectsModel_future::mergeProjects( const MerginProjectsList &merginProjec } } -void ProjectsModel_future::syncProject( const QString &projectId ) +void ProjectsModel::syncProject( const QString &projectId ) { - std::shared_ptr project = projectFromId( projectId ); + std::shared_ptr project = projectFromId( projectId ); if ( project == nullptr ) { @@ -364,9 +364,9 @@ void ProjectsModel_future::syncProject( const QString &projectId ) } } -void ProjectsModel_future::stopProjectSync( const QString &projectId ) +void ProjectsModel::stopProjectSync( const QString &projectId ) { - std::shared_ptr project = projectFromId( projectId ); + std::shared_ptr project = projectFromId( projectId ); if ( project == nullptr ) { @@ -398,25 +398,25 @@ void ProjectsModel_future::stopProjectSync( const QString &projectId ) } } -void ProjectsModel_future::removeLocalProject( const QString &projectId ) +void ProjectsModel::removeLocalProject( const QString &projectId ) { mLocalProjectsManager->removeLocalProject( projectId ); } -void ProjectsModel_future::migrateProject( const QString &projectId ) +void ProjectsModel::migrateProject( const QString &projectId ) { // if it is indeed a local project - std::shared_ptr project = projectFromId( projectId ); + std::shared_ptr project = projectFromId( projectId ); if ( project->isLocal() ) mBackend->migrateProjectToMergin( project->local->projectName ); } -void ProjectsModel_future::onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) +void ProjectsModel::onProjectSyncFinished( const QString &projectDir, const QString &projectFullName, bool successfully ) { Q_UNUSED( projectDir ) - std::shared_ptr project = projectFromId( projectFullName ); + std::shared_ptr project = projectFromId( projectFullName ); if ( !project || !project->isMergin() || !successfully ) return; @@ -429,9 +429,9 @@ void ProjectsModel_future::onProjectSyncFinished( const QString &projectDir, con qDebug() << "PMR: Project " << projectFullName << " finished sync"; } -void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ) +void ProjectsModel::onProjectSyncProgressChanged( const QString &projectFullName, qreal progress ) { - std::shared_ptr project = projectFromId( projectFullName ); + std::shared_ptr project = projectFromId( projectFullName ); if ( !project || !project->isMergin() ) return; @@ -444,14 +444,14 @@ void ProjectsModel_future::onProjectSyncProgressChanged( const QString &projectF qDebug() << "PMR: Project " << projectFullName << " changed sync progress to " << progress; } -void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) +void ProjectsModel::onProjectAdded( const LocalProject &project ) { // Check if such project is already in project list - std::shared_ptr proj = projectFromId( project.id() ); + std::shared_ptr proj = projectFromId( project.id() ); if ( proj ) { // add local information ~ project downloaded - proj->local = std::unique_ptr( new LocalProject_future( project ) ); + proj->local = std::unique_ptr( new LocalProject( project ) ); if ( proj->isMergin() ) proj->mergin->status = ProjectStatus::projectStatus( proj ); @@ -461,8 +461,8 @@ void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) else if ( mModelType == LocalProjectsModel ) { // add project to project list ~ project created - std::shared_ptr newProject = std::shared_ptr( new Project_future() ); - newProject->local = std::unique_ptr( new LocalProject_future( project ) ); + std::shared_ptr newProject = std::shared_ptr( new Project() ); + newProject->local = std::unique_ptr( new LocalProject( project ) ); int insertIndex = mProjects.size(); @@ -474,9 +474,9 @@ void ProjectsModel_future::onProjectAdded( const LocalProject_future &project ) qDebug() << "PMR: Added project" << project.id(); } -void ProjectsModel_future::onAboutToRemoveProject( const LocalProject_future project ) +void ProjectsModel::onAboutToRemoveProject( const LocalProject project ) { - std::shared_ptr proj = projectFromId( project.id() ); + std::shared_ptr proj = projectFromId( project.id() ); if ( proj ) { @@ -504,13 +504,13 @@ void ProjectsModel_future::onAboutToRemoveProject( const LocalProject_future pro } } -void ProjectsModel_future::onProjectDataChanged( const LocalProject_future &project ) +void ProjectsModel::onProjectDataChanged( const LocalProject &project ) { - std::shared_ptr proj = projectFromId( project.id() ); + std::shared_ptr proj = projectFromId( project.id() ); if ( proj ) { - proj->local = std::unique_ptr( new LocalProject_future( project ) ); + proj->local = std::unique_ptr( new LocalProject( project ) ); if ( proj->isMergin() ) proj->mergin->status = ProjectStatus::projectStatus( proj ); @@ -521,9 +521,9 @@ void ProjectsModel_future::onProjectDataChanged( const LocalProject_future &proj qDebug() << "PMR: Data changed in project" << project.id(); } -void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFullName ) +void ProjectsModel::onProjectDetachedFromMergin( const QString &projectFullName ) { - std::shared_ptr proj = projectFromId( projectFullName ); + std::shared_ptr proj = projectFromId( projectFullName ); if ( proj ) { @@ -538,7 +538,7 @@ void ProjectsModel_future::onProjectDetachedFromMergin( const QString &projectFu } } -void ProjectsModel_future::onProjectAttachedToMergin( const QString &projectFullName ) +void ProjectsModel::onProjectAttachedToMergin( const QString &projectFullName ) { // To ensure project will be in sync with server, send listProjectByName request. // In theory we could send that request only for this one project. @@ -547,7 +547,7 @@ void ProjectsModel_future::onProjectAttachedToMergin( const QString &projectFull qDebug() << "PMR: Project attached to mergin " << projectFullName; } -void ProjectsModel_future::setMerginApi( MerginApi *merginApi ) +void ProjectsModel::setMerginApi( MerginApi *merginApi ) { if ( !merginApi || mBackend == merginApi ) return; @@ -557,7 +557,7 @@ void ProjectsModel_future::setMerginApi( MerginApi *merginApi ) initializeProjectsModel(); } -void ProjectsModel_future::setLocalProjectsManager( LocalProjectsManager *localProjectsManager ) +void ProjectsModel::setLocalProjectsManager( LocalProjectsManager *localProjectsManager ) { if ( !localProjectsManager || mLocalProjectsManager == localProjectsManager ) return; @@ -567,7 +567,7 @@ void ProjectsModel_future::setLocalProjectsManager( LocalProjectsManager *localP initializeProjectsModel(); } -void ProjectsModel_future::setModelType( ProjectsModel_future::ProjectModelTypes modelType ) +void ProjectsModel::setModelType( ProjectsModel::ProjectModelTypes modelType ) { if ( mModelType == modelType ) return; @@ -577,7 +577,7 @@ void ProjectsModel_future::setModelType( ProjectsModel_future::ProjectModelTypes initializeProjectsModel(); } -QString ProjectsModel_future::modelTypeToFlag() const +QString ProjectsModel::modelTypeToFlag() const { switch ( mModelType ) { @@ -590,7 +590,7 @@ QString ProjectsModel_future::modelTypeToFlag() const } } -void ProjectsModel_future::printProjects() const // TODO: Helper function, remove after refactoring is done +void ProjectsModel::printProjects() const // TODO: Helper function, remove after refactoring is done { qDebug() << "PMR: Model " << this << " with type " << modelTypeToFlag() << " has projects: "; for ( const auto &proj : mProjects ) @@ -609,7 +609,7 @@ void ProjectsModel_future::printProjects() const // TODO: Helper function, remov } } -QStringList ProjectsModel_future::projectNames() const +QStringList ProjectsModel::projectNames() const { QStringList projectNames; LocalProjectsList projects = mLocalProjectsManager->projects(); @@ -623,7 +623,7 @@ QStringList ProjectsModel_future::projectNames() const return projectNames; } -void ProjectsModel_future::loadLocalProjects() +void ProjectsModel::loadLocalProjects() { if ( mModelType == LocalProjectsModel ) { @@ -634,13 +634,13 @@ void ProjectsModel_future::loadLocalProjects() } } -bool ProjectsModel_future::containsProject( QString projectId ) const +bool ProjectsModel::containsProject( QString projectId ) const { - std::shared_ptr proj = projectFromId( projectId ); + std::shared_ptr proj = projectFromId( projectId ); return proj != nullptr; } -std::shared_ptr ProjectsModel_future::projectFromId( QString projectId ) const +std::shared_ptr ProjectsModel::projectFromId( QString projectId ) const { for ( int ix = 0; ix < mProjects.size(); ++ix ) { @@ -653,7 +653,7 @@ std::shared_ptr ProjectsModel_future::projectFromId( QString pro return nullptr; } -ProjectsModel_future::ProjectModelTypes ProjectsModel_future::modelType() const +ProjectsModel::ProjectModelTypes ProjectsModel::modelType() const { return mModelType; } diff --git a/app/projectsmodel_future.h b/app/projectsmodel.h similarity index 88% rename from app/projectsmodel_future.h rename to app/projectsmodel.h index 2dbabfb32..178395941 100644 --- a/app/projectsmodel_future.h +++ b/app/projectsmodel.h @@ -7,21 +7,21 @@ * * ***************************************************************************/ -#ifndef PROJECTSMODEL_FUTURE_H -#define PROJECTSMODEL_FUTURE_H +#ifndef PROJECTSMODEL_H +#define PROJECTSMODEL_H #include #include -#include "project_future.h" +#include "project.h" #include "merginapi.h" class LocalProjectsManager; /** - * \brief The ProjectsModel_future class + * \brief The ProjectsModel class */ -class ProjectsModel_future : public QAbstractListModel +class ProjectsModel : public QAbstractListModel { Q_OBJECT @@ -60,8 +60,8 @@ class ProjectsModel_future : public QAbstractListModel }; Q_ENUM( ProjectModelTypes ) - ProjectsModel_future( QObject *parent = nullptr ); - ~ProjectsModel_future() override {}; + ProjectsModel( QObject *parent = nullptr ); + ~ProjectsModel() override {}; // From Qt 5.15 we can use REQUIRED keyword here that will ensure object will be always instantiated from QML with these mandatory properties Q_PROPERTY( MerginApi *merginApi READ merginApi WRITE setMerginApi ) @@ -101,7 +101,7 @@ class ProjectsModel_future : public QAbstractListModel //! Method merging local and remote projects based on the model type void mergeProjects( const MerginProjectsList &merginProjects, Transactions pendingProjects, bool keepPrevious = false ); - ProjectsModel_future::ProjectModelTypes modelType() const; + ProjectsModel::ProjectModelTypes modelType() const; MerginApi *merginApi() const { return mBackend; } @@ -119,9 +119,9 @@ public slots: void onProjectAttachedToMergin( const QString &projectFullName ); // LocalProjectsManager signals - void onProjectAdded( const LocalProject_future &project ); - void onAboutToRemoveProject( const LocalProject_future project ); - void onProjectDataChanged( const LocalProject_future &project ); + void onProjectAdded( const LocalProject &project ); + void onAboutToRemoveProject( const LocalProject project ); + void onProjectDataChanged( const LocalProject &project ); void setMerginApi( MerginApi *merginApi ); void setLocalProjectsManager( LocalProjectsManager *localProjectsManager ); @@ -139,11 +139,11 @@ public slots: void initializeProjectsModel(); bool containsProject( QString projectId ) const; - std::shared_ptr projectFromId( QString projectId ) const; + std::shared_ptr projectFromId( QString projectId ) const; MerginApi *mBackend = nullptr; LocalProjectsManager *mLocalProjectsManager = nullptr; - QList> mProjects; + QList> mProjects; ProjectModelTypes mModelType = EmptyProjectsModel; @@ -155,4 +155,4 @@ public slots: QString mLastRequestId; }; -#endif // PROJECTSMODEL_FUTURE_H +#endif // PROJECTSMODEL_H diff --git a/app/projectsproxymodel_future.cpp b/app/projectsproxymodel.cpp similarity index 57% rename from app/projectsproxymodel_future.cpp rename to app/projectsproxymodel.cpp index 8c3332b9d..c355423c7 100644 --- a/app/projectsproxymodel_future.cpp +++ b/app/projectsproxymodel.cpp @@ -7,34 +7,34 @@ * * ***************************************************************************/ -#include "projectsproxymodel_future.h" +#include "projectsproxymodel.h" -ProjectsProxyModel_future::ProjectsProxyModel_future( QObject *parent ) : QSortFilterProxyModel( parent ) +ProjectsProxyModel::ProjectsProxyModel( QObject *parent ) : QSortFilterProxyModel( parent ) { } -void ProjectsProxyModel_future::initialize() +void ProjectsProxyModel::initialize() { setSourceModel( mModel ); mModelType = mModel->modelType(); - setFilterRole( ProjectsModel_future::ProjectFullName ); + setFilterRole( ProjectsModel::ProjectFullName ); setFilterCaseSensitivity( Qt::CaseInsensitive ); sort( 0, Qt::AscendingOrder ); } -QString ProjectsProxyModel_future::searchExpression() const +QString ProjectsProxyModel::searchExpression() const { return mSearchExpression; } -ProjectsModel_future *ProjectsProxyModel_future::projectSourceModel() const +ProjectsModel *ProjectsProxyModel::projectSourceModel() const { return mModel; } -void ProjectsProxyModel_future::setSearchExpression( QString searchExpression ) +void ProjectsProxyModel::setSearchExpression( QString searchExpression ) { if ( mSearchExpression == searchExpression ) return; @@ -44,22 +44,22 @@ void ProjectsProxyModel_future::setSearchExpression( QString searchExpression ) emit searchExpressionChanged( mSearchExpression ); } -void ProjectsProxyModel_future::setProjectSourceModel( ProjectsModel_future *sourceModel ) +void ProjectsProxyModel::setProjectSourceModel( ProjectsModel *sourceModel ) { if ( mModel == sourceModel ) return; mModel = sourceModel; - QObject::connect( mModel, &ProjectsModel_future::modelInitialized, this, &ProjectsProxyModel_future::initialize ); + QObject::connect( mModel, &ProjectsModel::modelInitialized, this, &ProjectsProxyModel::initialize ); } -bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelIndex &right ) const +bool ProjectsProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const { - if ( mModelType == ProjectsModel_future::LocalProjectsModel ) + if ( mModelType == ProjectsModel::LocalProjectsModel ) { - bool lProjectIsMergin = mModel->data( left, ProjectsModel_future::ProjectIsMergin ).toBool(); - bool rProjectIsMergin = mModel->data( right, ProjectsModel_future::ProjectIsMergin ).toBool(); + bool lProjectIsMergin = mModel->data( left, ProjectsModel::ProjectIsMergin ).toBool(); + bool rProjectIsMergin = mModel->data( right, ProjectsModel::ProjectIsMergin ).toBool(); /** * Ordering of local projects: first non-mergin projects (using folder name), @@ -68,8 +68,8 @@ bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelI if ( !lProjectIsMergin && !rProjectIsMergin ) { - QString lProjectFullName = mModel->data( left, ProjectsModel_future::ProjectFullName ).toString(); - QString rProjectFullName = mModel->data( right, ProjectsModel_future::ProjectFullName ).toString(); + QString lProjectFullName = mModel->data( left, ProjectsModel::ProjectFullName ).toString(); + QString rProjectFullName = mModel->data( right, ProjectsModel::ProjectFullName ).toString(); qDebug() << "Comparing " << lProjectFullName << rProjectFullName; return lProjectFullName.compare( rProjectFullName, Qt::CaseInsensitive ) < 0; @@ -83,10 +83,10 @@ bool ProjectsProxyModel_future::lessThan( const QModelIndex &left, const QModelI return false; } - QString lNamespace = mModel->data( left, ProjectsModel_future::ProjectNamespace ).toString(); - QString lProjectName = mModel->data( left, ProjectsModel_future::ProjectName ).toString(); - QString rNamespace = mModel->data( right, ProjectsModel_future::ProjectNamespace ).toString(); - QString rProjectName = mModel->data( right, ProjectsModel_future::ProjectName ).toString(); + QString lNamespace = mModel->data( left, ProjectsModel::ProjectNamespace ).toString(); + QString lProjectName = mModel->data( left, ProjectsModel::ProjectName ).toString(); + QString rNamespace = mModel->data( right, ProjectsModel::ProjectNamespace ).toString(); + QString rProjectName = mModel->data( right, ProjectsModel::ProjectName ).toString(); if ( lNamespace == rNamespace ) { diff --git a/app/projectsproxymodel_future.h b/app/projectsproxymodel.h similarity index 61% rename from app/projectsproxymodel_future.h rename to app/projectsproxymodel.h index bf35046ea..730ae1c48 100644 --- a/app/projectsproxymodel_future.h +++ b/app/projectsproxymodel.h @@ -7,34 +7,34 @@ * * ***************************************************************************/ -#ifndef PROJECTSPROXYMODEL_FUTURE_H -#define PROJECTSPROXYMODEL_FUTURE_H +#ifndef PROJECTSPROXYMODEL_H +#define PROJECTSPROXYMODEL_H #include #include -#include "projectsmodel_future.h" +#include "projectsmodel.h" /** - * @brief The ProjectsProxyModel_future class + * @brief The ProjectsProxyModel class */ -class ProjectsProxyModel_future : public QSortFilterProxyModel +class ProjectsProxyModel : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression NOTIFY searchExpressionChanged ) - Q_PROPERTY( ProjectsModel_future *projectSourceModel READ projectSourceModel WRITE setProjectSourceModel ) + Q_PROPERTY( ProjectsModel *projectSourceModel READ projectSourceModel WRITE setProjectSourceModel ) public: - explicit ProjectsProxyModel_future( QObject *parent = nullptr ); - ~ProjectsProxyModel_future() override {}; + explicit ProjectsProxyModel( QObject *parent = nullptr ); + ~ProjectsProxyModel() override {}; QString searchExpression() const; - ProjectsModel_future *projectSourceModel() const; + ProjectsModel *projectSourceModel() const; public slots: void setSearchExpression( QString searchExpression ); - void setProjectSourceModel( ProjectsModel_future *sourceModel ); + void setProjectSourceModel( ProjectsModel *sourceModel ); signals: void searchExpressionChanged( QString SearchExpression ); @@ -45,9 +45,9 @@ public slots: private: void initialize(); - ProjectsModel_future *mModel; - ProjectsModel_future::ProjectModelTypes mModelType = ProjectsModel_future::EmptyProjectsModel; + ProjectsModel *mModel; + ProjectsModel::ProjectModelTypes mModelType = ProjectsModel::EmptyProjectsModel; QString mSearchExpression; }; -#endif // PROJECTSPROXYMODEL_FUTURE_H +#endif // PROJECTSPROXYMODEL_H diff --git a/app/sources.pri b/app/sources.pri index a8110e8f3..9522138d5 100644 --- a/app/sources.pri +++ b/app/sources.pri @@ -30,9 +30,9 @@ ios/iosutils.cpp \ inputprojutils.cpp \ codefilter.cpp \ qrdecoder.cpp \ -projectsproxymodel_future.cpp \ -projectsmodel_future.cpp \ -project_future.cpp \ +project.cpp \ +projectsmodel.cpp \ +projectsproxymodel.cpp \ exists(merginsecrets.cpp) { message("Using production Mergin API_KEYS") @@ -73,9 +73,9 @@ ios/iosutils.h \ inputprojutils.h \ codefilter.h \ qrdecoder.h \ -projectsproxymodel_future.h \ -projectsmodel_future.h \ -project_future.h \ +project.h \ +projectsmodel.h \ +projectsproxymodel.h \ contains(DEFINES, INPUT_TEST) { From 381ffffce77c6bd8972e9a6b36b1e7009fda4d10 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 12:56:09 +0200 Subject: [PATCH 18/22] remove more unused qml code --- app/qml/ProjectPanel.qml | 458 +-------------------------------------- app/qml/main.qml | 13 +- 2 files changed, 17 insertions(+), 454 deletions(-) diff --git a/app/qml/ProjectPanel.qml b/app/qml/ProjectPanel.qml index 477ad49fd..e0774f153 100644 --- a/app/qml/ProjectPanel.qml +++ b/app/qml/ProjectPanel.qml @@ -390,141 +390,7 @@ Item { } } -// SearchBar { -// id: searchBar -// y: pageHeader.height -// allowTimer: true - -// onSearchTextChanged: { -// if (toolbar.highlighted === homeBtn.text) { -// __localProjectsProxyModel.searchExpression = text -// } else if (toolbar.highlighted === exploreBtn.text) { -// // Filtered by request -// exploreBtn.activated() -// } else if (toolbar.highlighted === sharedProjectsBtn.text) { -// __merginProjectsModel.searchExpression = text -// } else if (toolbar.highlighted === myProjectsBtn.text) { -// __merginProjectsModel.searchExpression = text -// } -// } -// } - -// // Content -// ColumnLayout { -// id: contentLayout -// height: projectsPanel.height-pageHeader.height-searchBar.height-toolbar.height -// width: parent.width -// y: pageHeader.height + searchBar.height -// spacing: 0 - - // Info label -// Item { -// id: infoLabel -// width: parent.width -// height: toolbar.highlighted === exploreBtn.text ? projectsPanel.rowHeight * 2 : 0 -// visible: height - -// Text { -// anchors.horizontalCenter: parent.horizontalCenter -// anchors.verticalCenter: parent.verticalCenter -// horizontalAlignment: Text.AlignHCenter -// verticalAlignment: Text.AlignVCenter -// wrapMode: Text.WordWrap -// color: InputStyle.panelBackgroundDarker -// font.pixelSize: InputStyle.fontPixelSizeNormal -// text: qsTr("Explore public projects.") -// visible: parent.height -// } - -// // To not propagate click on canvas on background -// MouseArea { -// anchors.fill: parent -// } - -// Item { -// id: infoLabelHideBtn -// height: projectsPanel.iconSize -// width: height -// anchors.right: parent.right -// anchors.top: parent.top -// anchors.rightMargin: projectsPanel.panelMargin -// anchors.topMargin: projectsPanel.panelMargin - -// MouseArea { -// anchors.fill: parent -// onClicked: infoLabel.visible = false -// } - -// Image { -// id: infoLabelHide -// anchors.centerIn: infoLabelHideBtn -// source: 'no.svg' -// height: infoLabelHideBtn.height -// width: height -// sourceSize.width: width -// sourceSize.height: height -// fillMode: Image.PreserveAspectFit -// } - -// ColorOverlay { -// anchors.fill: infoLabelHide -// source: infoLabelHide -// color: InputStyle.panelBackgroundDark -// } -// } - -// Rectangle { -// id: borderLine -// color: InputStyle.panelBackground2 -// width: parent.width -// height: 1 * QgsQuick.Utils.dp -// anchors.bottom: parent.bottom -// } -// } - -// ProjectListPage { -// id: localProjectsList - - -// } - -// ListView { -// id: grid -// Layout.fillWidth: true -// Layout.fillHeight: true -// contentWidth: grid.width -// clip: true -// visible: !showMergin -// maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity - -// model: ProjectsProxyModel { -// projectSourceModel: ProjectsModel { -// id: myModel -// localProjectsManager: __localProjectsManager -// modelType: ProjectsModel.LocalProjectsModel -// merginApi: __merginApi -// } -// } - -// property int cellWidth: width -// property int cellHeight: projectsPanel.rowHeight -// property int borderWidth: 1 - -// delegate: delegateItem - -// footer: DelegateButton { -// height: projectsPanel.rowHeight -// width: parent.width -// text: qsTr("Create project") -// onClicked: { -// if (__inputUtils.hasStoragePermission()) { -// stackView.push(projectWizardComp) -// } else if (__inputUtils.acquireStoragePermission()) { -// restartAppDialog.open() -// } -// } -// } - +//------------------------------------------------- // Text { // id: noProjectsText // anchors.fill: parent @@ -571,29 +437,9 @@ Item { // padding: InputStyle.panelMargin/2 // } // } +//------------------------------------------------- -// ListView { -// id: merginProjectsList - -// property int paginatedPage: 0 - -// visible: showMergin && !busyIndicator.running -// Layout.fillWidth: true -// Layout.fillHeight: true -// contentWidth: grid.width -// clip: true -// maximumFlickVelocity: __androidUtils.isAndroid ? InputStyle.scrollVelocityAndroid : maximumFlickVelocity - -// onCountChanged: { -// if (merginProjectsList.visible || paginatedPage > 1) { -// merginProjectsList.positionViewAtIndex(merginProjectsList.currentIndex, ListView.End) -// } -// } - -// property int cellWidth: width -// property int cellHeight: projectsPanel.rowHeight -// property int borderWidth: 1 - +//------------------------------------------------- // TODO: unable to get list of the projects // Label { // anchors.fill: parent @@ -605,308 +451,20 @@ Item { // font.pixelSize: InputStyle.fontPixelSizeNormal // font.bold: true // } +//------------------------------------------------- -// delegate: delegateItemMergin - -// footer: Button { -// text: "ListProjects API" -// onClicked: { -// __myProjectsModel.listProjects(merginProjectsList.paginatedPage + 1) -// merginProjectsList.paginatedPage++ -// } -// } -// } - -// } - -// Component { -// id: delegateItem - -// ProjectDelegateItem { -// id: delegateItemContent - -// projectFullName: model.ProjectFullName -// projectId: model.ProjectId -// projectDescription: model.ProjectDescription -// projectStatus: model.ProjectSyncStatus -// projectIsValid: model.ProjectIsValid -// projectIsPending: model.ProjectPending ? true : false -// projectSyncProgress: model.ProjectSyncProgress ? ProjectSyncProgress : 0 -// projectIsLocal: model.ProjectIsLocal -// projectIsMergin: model.ProjectIsMergin - -// width: parent.width -// height: projectsPanel.rowHeight -// viewContentY: ListView.view.contentY -// viewHeight: ListView.view.height - -// onOpenRequested: console.log( "PMR: Open", projectId ) -// onSyncRequested: console.log( "PMR: Sync", projectId ) -// onMigrateRequested: console.log( "PMR: Upload", projectId ) -// onRemoveRequested: console.log( "PMR: Remove", projectId ) -// onStopSyncRequested: console.log( "PMR: Stop", projectId ) -// onShowChangesRequested: console.log( "PMR: Show changes", projectId ) -// } -// } - -// ProjectDelegateItem { -// id: delegateItemContent -// cellWidth: projectsPanel.width -// cellHeight: projectsPanel.rowHeight -// iconSize: projectsPanel.iconSize -// width: cellWidth -// height: cellHeight -// visible: height ? true : false -// statusIconSource: "more_menu.svg" -// itemMargin: projectsPanel.panelMargin -// projectFullName: ProjectFullName -// projectDescription: getStatusIcon( ProjectSyncStatus, ProjectPending ) -// disabled: !ProjectIsValid // invalid project -// highlight: { -// if (disabled) return true -// return ProjectFilePath === projectsPanel.activeProjectPath ? true : false -// } - -// Menu { -// property real menuItemHeight: projectsPanel.rowHeight * 0.8 -// id: contextMenu -// height: menuItemHeight * 2 -// width:Math.min( parent.width, 300 * QgsQuick.Utils.dp ) -// leftMargin: Math.max(parent.width - width, 0) - -// //! sets y-offset either above or below related item according relative position to end of the list -// onAboutToShow: { -// var itemRelativeY = parent.y - grid.contentY -// if (itemRelativeY + contextMenu.height >= grid.height) -// contextMenu.y = -contextMenu.height -// else -// contextMenu.y = parent.height -// } - -// MenuItem { -// height: ProjectIsMergin ? contextMenu.menuItemHeight : 0 -// ExtendedMenuItem { -// height: parent.height -// rowHeight: parent.height -// width: parent.width -// contentText: qsTr("Status") -// imageSource: InputStyle.infoIcon -// overlayImage: true -// } -// onClicked: { -// if (__merginProjectStatusModel.loadProjectInfo(delegateItemContent.projectFullName)) { -// stackView.push(statusPanelComp) -// } else __inputUtils.showNotification(qsTr("No Changes")) -// } -// } - -// MenuItem { -// height: ProjectIsMergin ? 0: contextMenu.menuItemHeight -// ExtendedMenuItem { -// height: parent.height -// rowHeight: parent.height -// width: parent.width -// contentText: qsTr("Upload to Mergin") -// imageSource: InputStyle.uploadIcon -// overlayImage: true -// } -// onClicked: { -// if (!ProjectIsMergin) { -// __merginApi.migrateProjectToMergin(projectName) -// } -// } -// } - -// MenuItem { -// height: contextMenu.menuItemHeight -// ExtendedMenuItem { -// height: parent.height -// rowHeight: parent.height -// width: parent.width -// contentText: qsTr("Remove from device") -// imageSource: InputStyle.removeIcon -// overlayImage: true -// } -// onClicked: { -// deleteDialog.relatedProjectDirectory = ProjectDirectory -// deleteDialog.open() -// } -// } -// } - -// onItemClicked: { -// if (showMergin) return - -// projectsPanel.activeProjectIndex = index -// projectsPanel.visible = false -// } - -// onMenuClicked:contextMenu.open() -// } - -// Component { -// id: delegateItemMergin - -// Rectangle { -// width: 20 -// height: 20 -// color: "green" -// } - -// ProjectDelegateItem { -// cellWidth: projectsPanel.width -// cellHeight: projectsPanel.rowHeight -// width: cellWidth -// height: cellHeight -// visible: height ? true : false -// pending: ProjectPending -// statusIconSource: getStatusIcon(ProjectSyncStatus, ProjectPending) -// iconSize: projectsPanel.iconSize -// projectFullName: ProjectFullName -// progressValue: ProjectSyncProgress -// projectDescription: ProjectDescription -// isAdditional: __myProjectsModel.canFetchMore() // TODO: replace with delegate button on listview footer - -// onMenuClicked: { -// if ( !__inputUtils.hasStoragePermission() ) { -// if ( __inputUtils.acquireStoragePermission() ) -// restartAppDialog.open() // TODO: replace with reload data! -// return -// } - -// if (ProjectSyncStatus === ProjectStatus.UpToDate) return - -// if ( ProjectPending ) { -// __myProjectsModel.stopProjectSync(ProjectNamespace, ProjectName) -// return -// } - -// __myProjectsModel.syncProject(ProjectNamespace, ProjectName) -// } - -// onDelegateButtonClicked: { // TODO: replace with footer property -// var searchText = searchBar.text - -// // Note that current index used to save last item position -// merginProjectsList.currentIndex = merginProjectsList.count - 1 // TODO: huh? - -// __myProjectsModel.listProjects(merginProjectsList.paginatedPage + 1, searchText) -// } - -// } -// } - - - // Toolbar -// Rectangle { -// property int itemSize: toolbar.height * 0.8 -// property string highlighted: homeBtn.text - -// id: toolbar -// height: InputStyle.rowHeightHeader -// width: parent.width -// anchors.bottom: parent.bottom -// color: InputStyle.clrPanelBackground -// visible: false - -// MouseArea { -// anchors.fill: parent -// onClicked: {} // dont do anything, just do not let click event propagate -// } +// Toolbar +//------------------------------------------------- // onHighlightedChanged: { //// searchBar.deactivate() // if (toolbar.highlighted === homeBtn.text) { //// __projectsModel.searchExpression = "" // } else { -// __merginApi.pingMergin() -// } -// } - -// Row { -// height: toolbar.height -// width: parent.width -// anchors.bottom: parent.bottom - -// Item { -// width: parent.width/parent.children.length -// height: parent.height - -// MainPanelButton { - -// id: homeBtn -// width: toolbar.itemSize -// text: qsTr("Home") -// imageSource: "home.svg" -// faded: toolbar.highlighted !== homeBtn.text - -// onActivated: { -// toolbar.highlighted = homeBtn.text; -// showMergin = false -//// stackView.pending = true -// myModel.listProjectsByName() -// } -// } -// } - -// Item { -// width: parent.width/parent.children.length -// height: parent.height -// MainPanelButton { -// id: myProjectsBtn -// width: toolbar.itemSize -// text: qsTr("My projects") -// imageSource: "account.svg" -// faded: toolbar.highlighted !== myProjectsBtn.text - -// onActivated: { -// toolbar.highlighted = myProjectsBtn.text -// stackView.pending = true -// showMergin = true -// __myProjectsModel.listProjects() -// } -// } -// } - -// Item { -// width: parent.width/parent.children.length -// height: parent.height -// MainPanelButton { -// id: sharedProjectsBtn -// width: toolbar.itemSize -// text: parent.width > sharedProjectsBtn.width * 2 ? qsTr("Shared with me") : qsTr("Shared") -// imageSource: "account-multi.svg" -// faded: toolbar.highlighted !== sharedProjectsBtn.text - -// onActivated: { -// toolbar.highlighted = sharedProjectsBtn.text -// stackView.pending = true -// showMergin = true -// __merginApi.listProjects("", "shared") -// } -// } -// } - -// Item { -// width: parent.width/parent.children.length -// height: parent.height -// MainPanelButton { -// id: exploreBtn -// width: toolbar.itemSize -// text: qsTr("Explore") -// imageSource: "explore.svg" -// faded: toolbar.highlighted !== exploreBtn.text - -// onActivated: { -// toolbar.highlighted = exploreBtn.text -// stackView.pending = true -// showMergin = true -// __merginApi.listProjects( searchBar.text ) -// } -// } +// __merginApi.pingMergin() <------------------!!! // } // } -// } +//------------------------------------------------- // Other components diff --git a/app/qml/main.qml b/app/qml/main.qml index 12b8cfdb9..23ba7538e 100644 --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -268,6 +268,15 @@ ApplicationWindow { if ( __appSettings.activeProject ) mainPanel.forceActiveFocus() + // DO NOT COMMIT!!! vvv + if ( !__androidUtils.isAndroid ) + { + window.width = 423 + window.height = 601 + window.x = 1100 + window.y = 266 + } + console.log("Completed Running!") } @@ -663,10 +672,6 @@ ApplicationWindow { onNotify: showMessage(message) onProjectDataChanged: { -// var projectName = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectName) -// var projectNamespace = __projectsModel.data(__projectsModel.index(openProjectPanel.activeProjectIndex), ProjectModel.ProjectNamespace) -// var currentProjectFullName = __merginApi.getFullProjectName(projectNamespace, projectName) - //! if current project has been updated, refresh canvas if (projectFullName === projectPanel.activeProjectId) { mapCanvas.mapSettings.extentChanged() From 7518fd31de71ab46e6c6a0e604fbd8a05831d3e1 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 13:28:58 +0200 Subject: [PATCH 19/22] check all nit --- app/inpututils.cpp | 2 +- app/project.h | 3 ++- app/projectsmodel.cpp | 14 +++++++++----- app/projectsmodel.h | 6 +++--- app/projectsproxymodel.h | 24 ++++++++++++------------ 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 8b03c3b68..cbe00cac5 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -445,7 +445,7 @@ QString InputUtils::createUniqueProjectDirectory( const QString &baseDataDir, co bool InputUtils::removeDir( const QString &dir ) { - if( dir.isEmpty() || dir == "/" ) + if ( dir.isEmpty() || dir == "/" ) return false; return QDir( dir ).removeRecursively(); diff --git a/app/project.h b/app/project.h index 2a9195d01..f23d752b0 100644 --- a/app/project.h +++ b/app/project.h @@ -18,7 +18,8 @@ struct Project; -namespace ProjectStatus { +namespace ProjectStatus +{ Q_NAMESPACE enum Status { diff --git a/app/projectsmodel.cpp b/app/projectsmodel.cpp index 1e23e1d8e..e65661915 100644 --- a/app/projectsmodel.cpp +++ b/app/projectsmodel.cpp @@ -61,7 +61,8 @@ QVariant ProjectsModel::data( const QModelIndex &index, int role ) const std::shared_ptr project = mProjects.at( index.row() ); - switch ( role ) { + switch ( role ) + { case ProjectName: return QVariant( project->projectName() ); case ProjectNamespace: return QVariant( project->projectNamespace() ); case ProjectFullName: return QVariant( project->projectId() ); @@ -71,12 +72,14 @@ QVariant ProjectsModel::data( const QModelIndex &index, int role ) const case ProjectSyncStatus: return QVariant( project->isMergin() ? project->mergin->status : ProjectStatus::NoVersion ); case ProjectFilePath: return QVariant( project->isLocal() ? project->local->qgisProjectFilePath : QString() ); case ProjectDirectory: return QVariant( project->isLocal() ? project->local->projectDir : QString() ); - case ProjectIsValid: { + case ProjectIsValid: + { if ( !project->isLocal() ) return true; // Mergin projects are by default valid, remote error only affects syncing, not opening of a project return project->local->projectError.isEmpty(); } - case ProjectDescription: { + case ProjectDescription: + { if ( project->isLocal() ) { if ( !project->local->projectError.isEmpty() ) @@ -92,7 +95,8 @@ QVariant ProjectsModel::data( const QModelIndex &index, int role ) const } return QVariant(); // This should not happen } - default: { + default: + { if ( !project->isMergin() ) return QVariant(); // Roles only for projects that has mergin part @@ -655,5 +659,5 @@ std::shared_ptr ProjectsModel::projectFromId( QString projectId ) const ProjectsModel::ProjectModelTypes ProjectsModel::modelType() const { - return mModelType; + return mModelType; } diff --git a/app/projectsmodel.h b/app/projectsmodel.h index 178395941..07814ac1c 100644 --- a/app/projectsmodel.h +++ b/app/projectsmodel.h @@ -109,7 +109,7 @@ class ProjectsModel : public QAbstractListModel bool hasMoreProjects() const; -public slots: + public slots: // MerginAPI - backend signals void onListProjectsFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, int projectsCount, int page, QString requestId ); void onListProjectsByNameFinished( const MerginProjectsList &merginProjects, Transactions pendingProjects, QString requestId ); @@ -127,11 +127,11 @@ public slots: void setLocalProjectsManager( LocalProjectsManager *localProjectsManager ); void setModelType( ProjectModelTypes modelType ); -signals: + signals: void modelInitialized(); void hasMoreProjectsChanged(); -private: + private: QString modelTypeToFlag() const; void printProjects() const; QStringList projectNames() const; diff --git a/app/projectsproxymodel.h b/app/projectsproxymodel.h index 730ae1c48..3c9dd885f 100644 --- a/app/projectsproxymodel.h +++ b/app/projectsproxymodel.h @@ -20,26 +20,26 @@ */ class ProjectsProxyModel : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT - Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression NOTIFY searchExpressionChanged ) - Q_PROPERTY( ProjectsModel *projectSourceModel READ projectSourceModel WRITE setProjectSourceModel ) + Q_PROPERTY( QString searchExpression READ searchExpression WRITE setSearchExpression NOTIFY searchExpressionChanged ) + Q_PROPERTY( ProjectsModel *projectSourceModel READ projectSourceModel WRITE setProjectSourceModel ) -public: + public: explicit ProjectsProxyModel( QObject *parent = nullptr ); ~ProjectsProxyModel() override {}; - QString searchExpression() const; - ProjectsModel *projectSourceModel() const; + QString searchExpression() const; + ProjectsModel *projectSourceModel() const; -public slots: - void setSearchExpression( QString searchExpression ); - void setProjectSourceModel( ProjectsModel *sourceModel ); + public slots: + void setSearchExpression( QString searchExpression ); + void setProjectSourceModel( ProjectsModel *sourceModel ); -signals: - void searchExpressionChanged( QString SearchExpression ); + signals: + void searchExpressionChanged( QString SearchExpression ); -protected: + protected: bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override; private: From e947e404dd27001cb89741c2308d5f30a4382d52 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 15:05:50 +0200 Subject: [PATCH 20/22] revert description --- app/qml/components/ProjectDelegateItem.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/qml/components/ProjectDelegateItem.qml b/app/qml/components/ProjectDelegateItem.qml index 9611f11a8..8377beb8c 100644 --- a/app/qml/components/ProjectDelegateItem.qml +++ b/app/qml/components/ProjectDelegateItem.qml @@ -183,8 +183,7 @@ Rectangle { visible: !projectIsPending height: textContainer.height/2 -// text: projectDescription - text: projectStatus + text: projectDescription anchors.right: parent.right anchors.bottom: parent.bottom anchors.left: parent.left From c0bbc53bc8eb225a66b541b9265b55a2540a0ea3 Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Wed, 31 Mar 2021 15:12:49 +0200 Subject: [PATCH 21/22] show only project name in created --- app/qml/components/ProjectDelegateItem.qml | 4 ++-- app/qml/components/ProjectList.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/qml/components/ProjectDelegateItem.qml b/app/qml/components/ProjectDelegateItem.qml index 8377beb8c..7b475365c 100644 --- a/app/qml/components/ProjectDelegateItem.qml +++ b/app/qml/components/ProjectDelegateItem.qml @@ -21,7 +21,7 @@ Rectangle { id: root // Required properties - property string projectFullName + property string projectDisplayName property string projectId property string projectDescription property int projectStatus @@ -168,7 +168,7 @@ Rectangle { Text { id: mainText - text: __inputUtils.formatProjectName( projectFullName ) + text: __inputUtils.formatProjectName( projectDisplayName ) height: textContainer.height/2 width: textContainer.width font.pixelSize: InputStyle.fontPixelSizeNormal diff --git a/app/qml/components/ProjectList.qml b/app/qml/components/ProjectList.qml index 5ed62bb6c..499ddbefa 100644 --- a/app/qml/components/ProjectList.qml +++ b/app/qml/components/ProjectList.qml @@ -75,7 +75,7 @@ Item { width: parent.width height: InputStyle.rowHeightHeader * 1.2 - projectFullName: model.ProjectFullName + projectDisplayName: root.projectModelType === ProjectsModel.CreatedProjectsModel ? model.ProjectName : model.ProjectFullName projectId: model.ProjectId projectDescription: model.ProjectDescription projectStatus: model.ProjectSyncStatus ? model.ProjectSyncStatus : ProjectStatus.NoVersion From 15ed63b3eb159c74cf5a7a1d7e2e0cfde13b589f Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Tue, 6 Apr 2021 09:40:18 +0200 Subject: [PATCH 22/22] address comments --- app/main.cpp | 1 - app/merginapi.cpp | 2 +- app/merginapi.h | 5 ++--- app/qml/main.qml | 15 ++------------- app/qml/qml.qrc | 2 +- 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/app/main.cpp b/app/main.cpp index c327b7e8f..056d5e3c6 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -231,7 +231,6 @@ void initDeclarative() qmlRegisterType( "lc", 1, 0, "PositionDirection" ); qmlRegisterType( "lc", 1, 0, "FieldsModel" ); qmlRegisterType( "lc", 1, 0, "CodeFilter" ); - qmlRegisterType( "lc", 1, 0, "ProjectsModel" ); qmlRegisterType( "lc", 1, 0, "ProjectsProxyModel" ); qmlRegisterUncreatableMetaObject( ProjectStatus::staticMetaObject, "lc", 1, 0, "ProjectStatus", "ProjectStatus Enum" ); diff --git a/app/merginapi.cpp b/app/merginapi.cpp index 4a2d9e495..1ed478e48 100644 --- a/app/merginapi.cpp +++ b/app/merginapi.cpp @@ -1667,7 +1667,7 @@ void MerginApi::startProjectUpdate( const QString &projectFullName, const QByteA TransactionStatus &transaction = mTransactionalStatus[projectFullName]; LocalProject projectInfo = mLocalProjects.projectFromMerginName( projectFullName ); - if ( projectInfo.isValid() ) // If project is already downloaded + if ( projectInfo.isValid() ) { transaction.projectDir = projectInfo.projectDir; } diff --git a/app/merginapi.h b/app/merginapi.h index d621da22c..338d22c0b 100644 --- a/app/merginapi.h +++ b/app/merginapi.h @@ -308,9 +308,6 @@ class MerginApi: public QObject */ Q_INVOKABLE void detachProjectFromMergin( const QString &projectNamespace, const QString &projectName ); - - LocalProject getLocalProject( const QString &projectFullName ); // Test function - static const int MERGIN_API_VERSION_MAJOR = 2020; static const int MERGIN_API_VERSION_MINOR = 4; static const QString sMetadataFile; @@ -347,6 +344,8 @@ class MerginApi: public QObject */ void deleteProject( const QString &projectNamespace, const QString &projectName ); + LocalProject getLocalProject( const QString &projectFullName ); + // Production and Test functions (therefore not private) /** diff --git a/app/qml/main.qml b/app/qml/main.qml index 23ba7538e..7a3151996 100644 --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -240,12 +240,10 @@ ApplicationWindow { // load default project if ( __appSettings.defaultProject ) { let path = __appSettings.defaultProject - let isValid = __localProjectsManager.projectIsValid( path ) - let id = __localProjectsManager.projectId( path ) - if ( isValid && __loader.load( path ) ) { + if ( __localProjectsManager.projectIsValid( path ) && __loader.load( path ) ) { projectPanel.activeProjectPath = path - projectPanel.activeProjectId = id + projectPanel.activeProjectId = __localProjectsManager.projectId( path ) __appSettings.activeProject = path } else { @@ -268,15 +266,6 @@ ApplicationWindow { if ( __appSettings.activeProject ) mainPanel.forceActiveFocus() - // DO NOT COMMIT!!! vvv - if ( !__androidUtils.isAndroid ) - { - window.width = 423 - window.height = 601 - window.x = 1100 - window.y = 266 - } - console.log("Completed Running!") } diff --git a/app/qml/qml.qrc b/app/qml/qml.qrc index f83c98ad8..17972bdec 100644 --- a/app/qml/qml.qrc +++ b/app/qml/qml.qrc @@ -58,11 +58,11 @@ components/SettingsSwitch.qml components/SimpleTextWithIcon.qml components/Symbol.qml + components/ProjectDelegateItem.qml CodeReader.qml CodeReaderHandler.qml CodeReaderOverlay.qml components/ProjectList.qml ProjectListPage.qml - components/ProjectDelegateItem.qml