Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving UX by hiding editing buttons for readers of a project #3682

Merged
merged 23 commits into from
Jan 23, 2025
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
36b2f39
wip - reader and guests cant edit
VitorVieiraZ Nov 18, 2024
8ecf262
in progress
VitorVieiraZ Nov 21, 2024
b3ded65
in progress
VitorVieiraZ Nov 22, 2024
5da5eca
updating project role on opening a project from active project and ch…
VitorVieiraZ Nov 27, 2024
7a01cc6
updating role every time we open a project + visibility condition on …
VitorVieiraZ Nov 28, 2024
eabfc85
in progress
VitorVieiraZ Dec 9, 2024
6596705
ensuring user role is cached/updated each time we open a project
VitorVieiraZ Dec 10, 2024
8a92bff
cleaning code
VitorVieiraZ Dec 10, 2024
193574d
merging master to branch
VitorVieiraZ Dec 10, 2024
493bb18
Merge branch 'master' into enhancementUiUx/readerCantEdit
VitorVieiraZ Jan 10, 2025
4a6deda
post review changes
VitorVieiraZ Jan 13, 2025
e0f23b2
tests and removing truncate
VitorVieiraZ Jan 13, 2025
9cd5999
decoupling mergin api from activeProject and rebuilding connections
VitorVieiraZ Jan 15, 2025
713b347
final adjustments
VitorVieiraZ Jan 18, 2025
3b94135
Merge branch 'master' into enhancementUiUx/readerCantEdit
VitorVieiraZ Jan 18, 2025
48d244f
replaceValueInJson tests
VitorVieiraZ Jan 20, 2025
585412e
post review updates
VitorVieiraZ Jan 21, 2025
709b3fc
merging master
VitorVieiraZ Jan 21, 2025
97888cb
code layout adjustment
VitorVieiraZ Jan 21, 2025
8e4a046
small fix
VitorVieiraZ Jan 21, 2025
0b28d3b
include merginprojectmetadata in activeproject
VitorVieiraZ Jan 21, 2025
49b2a6a
updating tests
VitorVieiraZ Jan 22, 2025
721c762
fixing tests
VitorVieiraZ Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
in progress
VitorVieiraZ committed Dec 9, 2024
commit eabfc850684c6ea26a61b5af2f01043652391d1b
66 changes: 15 additions & 51 deletions app/activeproject.cpp
Original file line number Diff line number Diff line change
@@ -76,6 +76,17 @@ ActiveProject::ActiveProject( AppSettings &appSettings
setAutosyncEnabled( mAppSettings.autosyncAllowed() );

QObject::connect( &mAppSettings, &AppSettings::autosyncAllowedChanged, this, &ActiveProject::setAutosyncEnabled );

QObject::connect(
mMerginApi,
&MerginApi::projectMetadataRoleUpdated,
this, [this]( const QString & projectFullName, const QString & role )
{
if ( projectFullName == this->projectFullName() )
{
setProjectRole( role );
}
} );
}

ActiveProject::~ActiveProject() = default;
@@ -103,7 +114,7 @@ bool ActiveProject::load( const QString &filePath )
bool ActiveProject::forceLoad( const QString &filePath, bool force )
{
// update user's role each time a project is opened, following #3174
updateProjectMetadata();
mMerginApi->updateProjectMetadataRole( projectFullName() );

CoreUtils::log( QStringLiteral( "Project loading" ), filePath + " " + ( force ? "true" : "false" ) );

@@ -559,56 +570,9 @@ bool ActiveProject::positionTrackingSupported() const
return mQgsProject->readBoolEntry( QStringLiteral( "Mergin" ), QStringLiteral( "PositionTracking/Enabled" ), false );
}

bool ActiveProject::updateProjectMetadata()
{
if ( !mMerginApi )
{
return false;
}

QNetworkReply *reply = mMerginApi->getProjectInfo( projectFullName() );
if ( !reply )
{
restoreCachedRole();
return false;
}

reply->request().setAttribute( static_cast<QNetworkRequest::Attribute>( mMerginApi->AttrProjectFullName ), projectFullName() );

connect( reply, &QNetworkReply::finished, this, &ActiveProject::updateProjectMetadataReplyFinished );

return true;
}

void ActiveProject::updateProjectMetadataReplyFinished()
{
QNetworkReply *r = qobject_cast<QNetworkReply *>( sender() );
Q_ASSERT( r );

QString projectFullName = r->request().attribute( static_cast<QNetworkRequest::Attribute>( mMerginApi->AttrProjectFullName ) ).toString();

if ( r->error() == QNetworkReply::NoError )
{
QByteArray data = r->readAll();

MerginProjectMetadata serverProject = MerginProjectMetadata::fromJson( data );
QString role = serverProject.role;
setProjectRole( role );
}
else
{
restoreCachedRole();
}

r->deleteLater();
}

void ActiveProject::restoreCachedRole()
{
MerginProjectMetadata cachedProjectMetadata = MerginProjectMetadata::fromCachedJson( mLocalProject.projectDir + "/" + mMerginApi->sMetadataFile );
QString role = cachedProjectMetadata.role;
setProjectRole( role );
}
// read cached role from metadata -> get reply -> update if changed
// metadataRoleFetchFinishedSignal -> (data)
// store role in app settings or update only role in cached json or nothing :)

QString ActiveProject::projectRole() const
{
13 changes: 1 addition & 12 deletions app/activeproject.h
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
#include "localprojectsmanager.h"
#include "autosynccontroller.h"
#include "inputmapsettings.h"
#include "../core/merginapi.h"
#include "merginapi.h"

/**
* \brief The ActiveProject class can load a QGIS project and holds its data.
@@ -128,17 +128,6 @@ class ActiveProject: public QObject

void setProjectRole( const QString &role );

/**
* Restores cached project role in metadata
*/
void restoreCachedRole();

/**
* Creates a network request to fetch latest project information and define user role in this project
*/
Q_INVOKABLE bool updateProjectMetadata();
void updateProjectMetadataReplyFinished();

signals:
void qgsProjectChanged();
void localProjectChanged( LocalProject project );
2 changes: 1 addition & 1 deletion app/qml/project/MMProjectController.qml
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ Item {
// just hide the panel - project already loaded
hidePanel()
// update user's role in project ( in case user has changed )
__activeProject.updateProjectMetadata()
__merginApi.updateProjectMetadataRole( __activeProject.projectFullName() )
}
else
{
95 changes: 95 additions & 0 deletions core/merginapi.cpp
Original file line number Diff line number Diff line change
@@ -3449,6 +3449,45 @@ bool MerginApi::writeData( const QByteArray &data, const QString &path )
return true;
}

bool MerginApi::updateCachedProjectRole( const QString &projectFullName, const QString &newRole )
{
LocalProject project = mLocalProjects.projectFromMerginName( projectFullName );
if ( !project.isValid() )
{
return false;
}

QString metadataPath = project.projectDir + "/" + sMetadataFile;

QFile file( metadataPath );
if ( !file.open( QIODevice::ReadOnly ) )
{
return false;
}

QByteArray data = file.readAll();
file.close();

QJsonDocument doc = QJsonDocument::fromJson( data );
if ( !doc.isObject() )
{
return false;
}

QJsonObject obj = doc.object();
obj["role"] = newRole;
doc.setObject( obj );

if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
{
return false;
}

bool success = ( file.write( doc.toJson() ) != -1 );
file.close();

return success;
}

void MerginApi::createPathIfNotExists( const QString &filePath )
{
@@ -3945,3 +3984,59 @@ DownloadQueueItem::DownloadQueueItem( const QString &fp, qint64 s, int v, qint64
tempFileName = CoreUtils::uuidWithoutBraces( QUuid::createUuid() );
}

void MerginApi::updateProjectMetadataRole( const QString &projectFullName )
{
if ( projectFullName.isEmpty() )
{
return;
}

QString projectDir = mLocalProjects.projectFromMerginName( projectFullName ).projectDir;
MerginProjectMetadata cachedProjectMetadata = MerginProjectMetadata::fromCachedJson( projectDir + "/" + sMetadataFile );
QString cachedRole = cachedProjectMetadata.role;

QNetworkReply *reply = getProjectInfo( projectFullName );
if ( !reply )
{
emit projectMetadataRoleUpdated( projectFullName, cachedRole );
return;
}

reply->request().setAttribute( static_cast<QNetworkRequest::Attribute>( AttrProjectFullName ), projectFullName );
reply->request().setAttribute( static_cast<QNetworkRequest::Attribute>( AttrCachedRole ), cachedRole );
connect( reply, &QNetworkReply::finished, this, &MerginApi::updateProjectMetadataRoleReplyFinished );
}

void MerginApi::updateProjectMetadataRoleReplyFinished()
{
QNetworkReply *r = qobject_cast<QNetworkReply *>( sender() );
Q_ASSERT( r );

QString projectFullName = r->request().attribute( static_cast<QNetworkRequest::Attribute>( AttrProjectFullName ) ).toString();
QString cachedRole = r->request().attribute( static_cast<QNetworkRequest::Attribute>( AttrCachedRole ) ).toString();

if ( r->error() == QNetworkReply::NoError )
{
QByteArray data = r->readAll();
MerginProjectMetadata serverProject = MerginProjectMetadata::fromJson( data );
QString role = serverProject.role;

if ( role != cachedRole )
{
if ( updateCachedProjectRole( projectFullName, role ) )
{
emit projectMetadataRoleUpdated( projectFullName, role );
}
else
{
CoreUtils::log( "metadata", QString( "Failed to update cached role for project %1" ).arg( projectFullName ) );
}
}
}
else
{
emit projectMetadataRoleUpdated( projectFullName, cachedRole );
}

r->deleteLater();
}
42 changes: 26 additions & 16 deletions core/merginapi.h
Original file line number Diff line number Diff line change
@@ -573,23 +573,10 @@ class MerginApi: public QObject
*/
bool apiSupportsWorkspaces();

/** Creates a request to get project details (list of project files).
*/
QNetworkReply *getProjectInfo( const QString &projectFullName, bool withAuth = true );

enum CustomAttribute
{
AttrProjectFullName = QNetworkRequest::User,
AttrTempFileName = QNetworkRequest::User + 1,
AttrWorkspaceName = QNetworkRequest::User + 2,
AttrAcceptFlag = QNetworkRequest::User + 3,
};

/**
* Extracts detail (message) of an error json. If its not json or detail cannot be parsed, the whole data are return;
* \param data Data received from mergin server on a request failed.
*/
QString extractServerErrorMsg( const QByteArray &data );
* Updates project metadata role by fetching latest information from server.
*/
Q_INVOKABLE void updateProjectMetadataRole( const QString &projectFullName );

signals:
void apiSupportsSubscriptionsChanged();
@@ -668,6 +655,7 @@ class MerginApi: public QObject
void apiSupportsWorkspacesChanged();

void serverWasUpgraded();
void projectMetadataRoleUpdated( const QString &projectFullName, const QString &role );

private slots:
void listProjectsReplyFinished( QString requestId );
@@ -762,11 +750,20 @@ class MerginApi: public QObject
*/
QVariant extractServerErrorValue( const QByteArray &data, const QString &key );
/**
* Extracts detail (message) of an error json. If its not json or detail cannot be parsed, the whole data are return;
* \param data Data received from mergin server on a request failed.
*/
QString extractServerErrorMsg( const QByteArray &data );
/**
* Returns a temporary project path.
* \param projectFullName
*/
QString getTempProjectDir( const QString &projectFullName );

/** Creates a request to get project details (list of project files).
*/
QNetworkReply *getProjectInfo( const QString &projectFullName, bool withAuth = true );

//! Called when pull of project data has finished to finalize things and emit sync finished signal
void finalizeProjectPull( const QString &projectFullName );

@@ -798,6 +795,10 @@ class MerginApi: public QObject

bool projectFileHasBeenUpdated( const ProjectDiff &diff );

void updateProjectMetadataRoleReplyFinished();

bool updateCachedProjectRole( const QString &projectFullName, const QString &newRole );

QNetworkAccessManager mManager;
QString mApiRoot;
LocalProjectsManager &mLocalProjects;
@@ -808,6 +809,15 @@ class MerginApi: public QObject
MerginSubscriptionInfo *mSubscriptionInfo; //owned by this (qml grouped-properties)
MerginUserAuth *mUserAuth; //owned by this (qml grouped-properties)

enum CustomAttribute
{
AttrProjectFullName = QNetworkRequest::User,
AttrTempFileName = QNetworkRequest::User + 1,
AttrWorkspaceName = QNetworkRequest::User + 2,
AttrAcceptFlag = QNetworkRequest::User + 3,
AttrCachedRole = QNetworkRequest::User + 4
};

Transactions mTransactionalStatus; //projectFullname -> transactionStatus
static const QSet<QString> sIgnoreExtensions;
static const QSet<QString> sIgnoreImageExtensions;