fix[onboarding] Broken links to "Terms" and "Privacy"

- display the locally bundled privacy/terms of use texts in a popup
- add support for reading text files with both relative and absolute
files or URLs, useful for having the same file path in both storybook
and the app

Fixes #13877
This commit is contained in:
Lukáš Tinkl 2024-03-11 15:31:28 +01:00 committed by Lukáš Tinkl
parent e7a1f5e831
commit 666ba77051
4 changed files with 149 additions and 85 deletions

View File

@ -0,0 +1,55 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml 2.15
import AppLayouts.Onboarding.popups 1.0
import utils 1.0
import Storybook 1.0
SplitView {
id: root
orientation: Qt.Vertical
Logs { id: logs }
function openDialog() {
popupComponent.createObject(popupBg)
}
Component.onCompleted: openDialog()
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
PopupBackground {
id: popupBg
anchors.fill: parent
Button {
anchors.centerIn: parent
text: "Reopen"
onClicked: openDialog()
}
}
}
Component {
id: popupComponent
BeforeGetStartedModal {
id: popup
anchors.centerIn: parent
modal: false
visible: true
onClosed: destroy()
}
}
}
// category: Popups
// https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?type=design&node-id=38555-18004&mode=design&t=WHoI8vkSC9JScbPx-0

View File

@ -17,15 +17,15 @@ QString StringUtilsInternal::escapeHtml(const QString& unsafe) const
} }
QString resolveFileUsingQmlImportPaths(QQmlEngine *engine, const QString &relativeFilePath) { QString resolveFileUsingQmlImportPaths(QQmlEngine *engine, const QString &relativeFilePath) {
QStringList importPaths = engine->importPathList(); const auto importPaths = engine->importPathList();
for (const auto &path : importPaths) { for (const auto &path : importPaths) {
QString fullPath = path + "/" + relativeFilePath; const auto fullPath = path + QStringLiteral("/") + relativeFilePath;
QFile file(fullPath); QFile file(fullPath);
if (file.exists()) { if (file.exists()) {
return fullPath; return fullPath;
} }
} }
return ""; return {};
} }
QString StringUtilsInternal::readTextFile(const QString& filePath) const QString StringUtilsInternal::readTextFile(const QString& filePath) const
@ -36,18 +36,27 @@ QString StringUtilsInternal::readTextFile(const QString& filePath) const
return {}; return {};
} }
const auto resolvedFilePath = selector->selector()->select(filePath); QString selectedFilePath;
const auto maybeFileUrl = QUrl(filePath).toLocalFile(); // support local file URLs (e.g. "file:///foo/bar/baz.txt")
QFile file(resolvedFilePath); if (QFile::exists(maybeFileUrl))
selectedFilePath = maybeFileUrl;
else
selectedFilePath = selector->selector()->select(filePath);
if (selectedFilePath.startsWith(QLatin1String("qrc:/"))) // for some reason doesn't work with the "qrc:/" prefix, drop it
selectedFilePath.remove(0, 3);
QFile file(selectedFilePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
auto fileUrl = resolveFileUsingQmlImportPaths(m_engine, filePath); const auto resolvedFilePath = resolveFileUsingQmlImportPaths(m_engine, filePath);
if (fileUrl.isEmpty()) { if (resolvedFilePath.isEmpty()) {
qWarning() << Q_FUNC_INFO << "Can't find file in QML import paths" << filePath; qWarning() << Q_FUNC_INFO << "Can't find file in QML import paths" << filePath;
return {}; return {};
} }
file.setFileName(fileUrl); file.setFileName(resolvedFilePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << Q_FUNC_INFO << "Error opening existing file" << fileUrl << "for reading"; qWarning() << Q_FUNC_INFO << "Error opening existing file" << resolvedFilePath << "for reading";
return {}; return {};
} }
} }

View File

@ -6,6 +6,7 @@ import QtQml.Models 2.14
import utils 1.0 import utils 1.0
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1 import StatusQ.Popups.Dialog 0.1
@ -13,7 +14,7 @@ import StatusQ.Popups.Dialog 0.1
StatusDialog { StatusDialog {
id: root id: root
width: 480 width: 600
topPadding: Style.current.bigPadding topPadding: Style.current.bigPadding
bottomPadding: Style.current.bigPadding bottomPadding: Style.current.bigPadding
closePolicy: Popup.NoAutoClose closePolicy: Popup.NoAutoClose
@ -28,34 +29,27 @@ StatusDialog {
StatusButton { StatusButton {
objectName: "getStartedStatusButton" objectName: "getStartedStatusButton"
enabled: acknowledge.checked && termsOfUse.checked enabled: acknowledge.checked && termsOfUse.checked
size: StatusBaseButton.Size.Large text: qsTr("Get started")
font.weight: Font.Medium
text: qsTr("Get Started")
onClicked: root.close() onClicked: root.close()
} }
} }
} }
contentItem: Item { contentItem: ColumnLayout {
Column {
width: 416
spacing: Style.current.padding spacing: Style.current.padding
anchors.centerIn: parent
StatusCheckBox { StatusCheckBox {
Layout.fillWidth: true
id: acknowledge id: acknowledge
objectName: "acknowledgeCheckBox" objectName: "acknowledgeCheckBox"
spacing: Style.current.halfPadding spacing: Style.current.halfPadding
font.pixelSize: 15
width: parent.width
text: qsTr("I acknowledge that Status Desktop is in Beta and by using it I take the full responsibility for all risks concerning my data and funds.") text: qsTr("I acknowledge that Status Desktop is in Beta and by using it I take the full responsibility for all risks concerning my data and funds.")
} }
StatusCheckBox { StatusCheckBox {
Layout.fillWidth: true
id: termsOfUse id: termsOfUse
objectName: "termsOfUseCheckBox" objectName: "termsOfUseCheckBox"
width: parent.width
font.pixelSize: 15
contentItem: Row { contentItem: Row {
spacing: 4 spacing: 4
@ -63,61 +57,66 @@ StatusDialog {
StatusBaseText { StatusBaseText {
text: qsTr("I accept Status") text: qsTr("I accept Status")
font.pixelSize: 15
} }
StatusBaseText { StatusLinkText {
objectName: "termsOfUseLink" objectName: "termsOfUseLink"
text: qsTr("Terms of Use") text: qsTr("Terms of Use")
color: Theme.palette.primaryColor1 color: Theme.palette.primaryColor1
font.pixelSize: 15
font.weight: Font.Medium font.weight: Font.Medium
font.pixelSize: Theme.primaryTextFontSize
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: { onClicked: {
Qt.openUrlExternally("https://status.im/terms-of-use/") detailsPopup.title = qsTr("Status Software Terms of Use")
} detailsPopup.textFile = SQUtils.StringUtils.readTextFile(Qt.resolvedUrl("../../../../imports/assets/docs/terms-of-use.mdwn"))
detailsPopup.open()
} }
} }
StatusBaseText { StatusBaseText {
text: "&" text: "&"
font.pixelSize: 15
} }
StatusBaseText { StatusLinkText {
objectName: "privacyPolicyLink" objectName: "privacyPolicyLink"
text: qsTr("Privacy Policy") text: qsTr("Privacy Policy")
color: Theme.palette.primaryColor1 color: Theme.palette.primaryColor1
font.pixelSize: 15
font.weight: Font.Medium font.weight: Font.Medium
font.pixelSize: Theme.primaryTextFontSize
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: { onClicked: {
Qt.openUrlExternally("https://status.im/privacy-policy/") detailsPopup.title = qsTr("Status Software Privacy Statement")
detailsPopup.textFile = SQUtils.StringUtils.readTextFile(Qt.resolvedUrl("../../../../imports/assets/docs/privacy.mdwn"))
detailsPopup.open()
} }
} }
} }
} }
} }
StatusDialog {
id: detailsPopup
property string textFile
width: 600
padding: 0
standardButtons: Dialog.Ok
anchors.centerIn: parent
visible: false
onClosed: textFile = ""
StatusScrollView {
id: scrollView
anchors.fill: parent
contentWidth: availableWidth
padding: 20
StatusBaseText {
width: scrollView.availableWidth
wrapMode: Text.Wrap
textFormat: Text.MarkdownText
text: detailsPopup.textFile
}
} }
} }
} }

View File

@ -0,0 +1 @@
BeforeGetStartedModal 1.0 BeforeGetStartedModal.qml