mobile: add ability to delete cloud account

Apple store rules require this.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2022-08-07 17:11:05 -07:00
parent 941aaf5b65
commit 32bc034f41
9 changed files with 159 additions and 0 deletions

View file

@ -1,3 +1,4 @@
mobile: allow cloud account deletion (Apple app store requirement)
---
* Always add new entries at the very top of this file above other existing entries and this note.

View file

@ -17,6 +17,7 @@ CloudStorageAuthenticate::CloudStorageAuthenticate(QObject *parent) :
#define CLOUDBACKENDSTORAGE CLOUDURL + "/storage"
#define CLOUDBACKENDVERIFY CLOUDURL + "/verify"
#define CLOUDBACKENDUPDATE CLOUDURL + "/update"
#define CLOUDBACKENDDELETE CLOUDURL + "/delete-account"
QNetworkReply* CloudStorageAuthenticate::backend(const QString& email,const QString& password,const QString& pin,const QString& newpasswd)
{
@ -50,6 +51,34 @@ QNetworkReply* CloudStorageAuthenticate::backend(const QString& email,const QStr
return reply;
}
QNetworkReply* CloudStorageAuthenticate::deleteAccount(const QString& email, const QString& password)
{
QString payload(email + QChar(' ') + password);
QNetworkRequest *request = new QNetworkRequest(QUrl(CLOUDBACKENDDELETE));
request->setRawHeader("Accept", "text/xml, text/plain");
request->setRawHeader("User-Agent", userAgent.toUtf8());
request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
reply = manager()->post(*request, qPrintable(payload));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(reply, &QNetworkReply::finished, this, &CloudStorageAuthenticate::deleteFinished);
connect(reply, &QNetworkReply::sslErrors, this, &CloudStorageAuthenticate::sslErrors);
connect(reply, &QNetworkReply::errorOccurred, this, &CloudStorageAuthenticate::uploadError);
#else
connect(reply, SIGNAL(finished()), this, SLOT(deleteFinished()));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(uploadError(QNetworkReply::NetworkError)));
#endif
return reply;
}
void CloudStorageAuthenticate::deleteFinished()
{
QString cloudAuthReply(reply->readAll());
qDebug() << "Completed connection with cloud storage backend, response" << cloudAuthReply;
emit finishedDelete();
}
void CloudStorageAuthenticate::uploadFinished()
{
static QString myLastError;

View file

@ -9,15 +9,18 @@ class CloudStorageAuthenticate : public QObject {
Q_OBJECT
public:
QNetworkReply* backend(const QString& email,const QString& password,const QString& pin = QString(),const QString& newpasswd = QString());
QNetworkReply* deleteAccount(const QString& email, const QString &passwd);
explicit CloudStorageAuthenticate(QObject *parent);
signals:
void finishedAuthenticate();
void finishedDelete();
void passwordChangeSuccessful();
private
slots:
void uploadError(QNetworkReply::NetworkError error);
void sslErrors(const QList<QSslError> &errorList);
void uploadFinished();
void deleteFinished();
private:
QNetworkReply *reply;
QString userAgent;

View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0
import QtQuick 2.6
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.4 as Kirigami
import org.subsurfacedivelog.mobile 1.0
Kirigami.ScrollablePage {
id: deleteAccountPage
property int pageWidth: deleteAccountPage.width - deleteAccountPage.leftPadding - deleteAccountPage.rightPadding
title: qsTr("Delete Subsurface Cloud Account")
background: Rectangle { color: subsurfaceTheme.backgroundColor }
ColumnLayout {
spacing: Kirigami.Units.largeSpacing
width: deleteAccountPage.width
Layout.margins: Kirigami.Units.gridUnit / 2
Kirigami.Heading {
text: qsTr("Delete Subsurface Cloud Account")
color: subsurfaceTheme.textColor
Layout.topMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: pageWidth
wrapMode: TextEdit.NoWrap
fontSizeMode: Text.Fit
}
Kirigami.Heading {
text: qsTr("Deleting your Subsurface Cloud account is permanent.\n") +
qsTr("There is no way to undo this action.")
level: 4
color: subsurfaceTheme.textColor
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Kirigami.Units.largeSpacing * 3
Layout.maximumWidth: pageWidth
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
anchors.horizontalCenter: parent.Center
horizontalAlignment: Text.AlignHCenter
}
Kirigami.Heading {
text: PrefCloudStorage.cloud_storage_email
level: 4
color: subsurfaceTheme.textColor
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Kirigami.Units.largeSpacing * 3
Layout.maximumWidth: pageWidth
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
anchors.horizontalCenter: parent.Center
horizontalAlignment: Text.AlignHCenter
}
TemplateButton {
id: deleteCloudAccount
Layout.alignment: Qt.AlignHCenter
text: qsTr("delete cloud account")
onClicked: {
manager.appendTextToLog("request to delete account confirmed")
manager.deleteAccount()
rootItem.returnTopPage()
}
}
TemplateButton {
id: dontDeleteCloudAccount
Layout.alignment: Qt.AlignHCenter
text: qsTr("never mind")
onClicked: {
manager.appendTextToLog("request to delete account cancelled")
rootItem.returnTopPage()
}
}
}
}

View file

@ -61,6 +61,15 @@ TemplatePage {
text: describe[Backend.cloud_verification_status]
Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5
}
TemplateButton {
id: deleteCloudAccount
enabled: Backend.cloud_verification_status !== Enums.CS_NOCLOUD
text: qsTr("Delete Account")
onClicked: {
manager.appendTextToLog("requesting account deletion");
showPage(deleteAccount)
}
}
}
TemplateLine {
visible: sectionGeneral.isExpanded

View file

@ -783,6 +783,10 @@ if you have network connectivity and want to sync your data to cloud storage."),
id: settingsWindow
}
DeleteAccount {
id: deleteAccount
}
CopySettings {
id: settingsCopyWindow
visible: false

View file

@ -19,6 +19,7 @@
<!-- ********** qml ********** -->
<file>About.qml</file>
<file>CloudCredentials.qml</file>
<file>DeleteAccount.qml</file>
<file>DiveDetails.qml</file>
<file>DiveDetailsEdit.qml</file>
<file>DiveDetailsView.qml</file>

View file

@ -725,6 +725,42 @@ bool QMLManager::verifyCredentials(QString email, QString password, QString pin)
return true;
}
void QMLManager::deleteAccount()
{
QString email(prefs.cloud_storage_email);
QString passwd(prefs.cloud_storage_password);
if (email.isEmpty() || passwd.isEmpty())
return;
setStartPageText(tr("Deleting cloud account..."));
appendTextToLog(QStringLiteral("user requested that we delete cloud account for email %1").arg(email));
CloudStorageAuthenticate *csa = new CloudStorageAuthenticate(this);
csa->deleteAccount(email, passwd);
// let's wait here for the signal to avoid too many more nested functions
QTimer myTimer;
myTimer.setSingleShot(true);
QEventLoop loop;
connect(csa, &CloudStorageAuthenticate::finishedDelete, &loop, &QEventLoop::quit);
connect(&myTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
myTimer.start(prefs.cloud_timeout * 3 * 1000); // give it extra time
loop.exec();
if (!myTimer.isActive()) {
// got no response from the server
setStartPageText(RED_FONT + tr("No response from cloud server to delete account") + END_FONT);
appendTextToLog(QStringLiteral("no response from cloud server to delete account"));
return;
}
myTimer.stop();
appendTextToLog(QStringLiteral("deleted the account"));
qPrefCloudStorage::set_cloud_storage_email("");
qPrefCloudStorage::set_cloud_storage_email_encoded("");
qPrefCloudStorage::set_cloud_storage_password("");
qPrefCloudStorage::set_cloud_verification_status(qPrefCloudStorage::CS_NOCLOUD);
set_filename(qPrintable(nocloud_localstorage()));
setStartPageText(tr("Cloud storage account deleted."));
return;
}
void QMLManager::loadDivesWithValidCredentials()
{
QString url;

View file

@ -180,6 +180,7 @@ public slots:
void saveChangesCloud(bool forceRemoteSync, bool fromUndo = false);
void selectDive(int id);
void deleteDive(int id);
void deleteAccount();
void toggleDiveInvalid(int id);
void copyDiveData(int id);
void pasteDiveData(int id);