From 2904018173081998b63516ef08b3134b30649bc7 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Sat, 7 May 2022 13:45:15 +0200 Subject: [PATCH] fix(@desktop/settings): content on the right side for all subsections need to have the same geometry Structure of the all subsection of the settings section has the same high level structure Fixes #5650 --- ui/app/AppLayouts/Profile/ProfileLayout.qml | 255 ++++-- .../controls/WalletNetworkDelegate.qml | 9 +- .../Profile/panels/ContactsListPanel.qml | 1 + .../Profile/stores/ProfileSectionStore.qml | 29 + ui/app/AppLayouts/Profile/views/AboutView.qml | 30 +- .../AppLayouts/Profile/views/AdvancedView.qml | 97 ++- .../Profile/views/AppearanceView.qml | 18 +- .../AppLayouts/Profile/views/BrowserView.qml | 53 +- .../AppLayouts/Profile/views/ContactsView.qml | 267 +++---- .../AppLayouts/Profile/views/DevicesView.qml | 385 +++++---- ui/app/AppLayouts/Profile/views/HelpView.qml | 354 ++++----- .../AppLayouts/Profile/views/LanguageView.qml | 282 +++---- .../Profile/views/MessagingView.qml | 745 +++++++++--------- .../Profile/views/MyProfileView.qml | 277 ++++--- .../Profile/views/NotificationsView.qml | 63 +- .../AppLayouts/Profile/views/PrivacyView.qml | 39 +- .../Profile/views/SettingsContentBase.qml | 98 +++ .../AppLayouts/Profile/views/SoundsView.qml | 32 +- .../AppLayouts/Profile/views/WalletView.qml | 116 +-- .../Profile/views/wallet/AccountView.qml | 25 +- .../views/wallet/DappPermissionsView.qml | 37 +- .../Profile/views/wallet/MainView.qml | 13 - .../Profile/views/wallet/NetworksView.qml | 49 +- .../views/wallet/PermissionsListView.qml | 2 - ui/imports/utils/Constants.qml | 5 + 25 files changed, 1672 insertions(+), 1609 deletions(-) create mode 100644 ui/app/AppLayouts/Profile/views/SettingsContentBase.qml diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml index ef55f7a635..83684a8083 100644 --- a/ui/app/AppLayouts/Profile/ProfileLayout.qml +++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml @@ -10,6 +10,7 @@ import "popups" import "views" import StatusQ.Layout 0.1 +import StatusQ.Controls 0.1 StatusAppTwoPanelLayout { id: profileView @@ -24,10 +25,14 @@ StatusAppTwoPanelLayout { } QtObject { - id: _internal - readonly property int contentMaxWidth: 624 - readonly property int contentMinWidth: 450 - property int profileContentWidth: Math.max(contentMinWidth, Math.min(profileContainer.width * 0.8, contentMaxWidth)) + id: d + + readonly property int topMargin: 0 + readonly property int bottomMargin: 56 + readonly property int leftMargin: 48 + readonly property int rightMargin: 48 + + readonly property int contentWidth: 560 } leftPanel: LeftTabView { @@ -36,100 +41,194 @@ StatusAppTwoPanelLayout { anchors.fill: parent } - rightPanel: StackLayout { - id: profileContainer - + rightPanel: Item { anchors.fill: parent - currentIndex: Global.settingsSubsection - - onCurrentIndexChanged: { - if(visibleChildren[0] === ensContainer){ - ensContainer.goToStart(); + StatusBanner { + id: banner + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + visible: profileContainer.currentIndex === Constants.settingsSubsection.wallet && + profileView.store.walletStore.areTestNetworksEnabled + type: StatusBanner.Type.Danger + statusText: { + if(profileContainer.currentIndex === Constants.settingsSubsection.wallet && + profileView.store.walletStore.areTestNetworksEnabled) + return qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.") + return "" } } - MyProfileView { - Layout.fillWidth: true - Layout.fillHeight: true + StackLayout { + id: profileContainer - profileStore: profileView.store.profileStore - profileContentWidth: _internal.profileContentWidth - } + anchors.top: banner.visible? banner.bottom : parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.topMargin: d.topMargin + anchors.bottomMargin: d.bottomMargin + anchors.leftMargin: d.leftMargin + anchors.rightMargin: d.rightMargin - ContactsView { - contactsStore: profileView.store.contactsStore - profileContentWidth: _internal.profileContentWidth - } + currentIndex: Global.settingsSubsection - EnsView { - id: ensContainer - ensUsernamesStore: profileView.store.ensUsernamesStore - contactsStore: profileView.store.contactsStore - stickersStore: profileView.store.stickersStore - profileContentWidth: _internal.profileContentWidth - } + onCurrentIndexChanged: { + if(visibleChildren[0] === ensContainer){ + ensContainer.goToStart(); + } + } - MessagingView { - id: messagingView - messagingStore: profileView.store.messagingStore - profileContentWidth: _internal.profileContentWidth - } + MyProfileView { + Layout.fillWidth: true + Layout.fillHeight: true - WalletView { - id: walletView - walletStore: profileView.store.walletStore - emojiPopup: profileView.emojiPopup - } + profileStore: profileView.store.profileStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.profile) + contentWidth: d.contentWidth + } - PrivacyView { - privacyStore: profileView.store.privacyStore - profileContentWidth: _internal.profileContentWidth - } + ContactsView { + Layout.fillWidth: true + Layout.fillHeight: true - AppearanceView { - appearanceStore: profileView.store.appearanceStore - profileContentWidth: _internal.profileContentWidth - systemPalette: profileView.systemPalette - } + contactsStore: profileView.store.contactsStore + sectionTitle: qsTr("Contacts") + contentWidth: d.contentWidth - SoundsView { - profileContentWidth: _internal.profileContentWidth - } + backButtonName: profileView.store.getNameForSubsection(Constants.settingsSubsection.messaging) - LanguageView { - languageStore: profileView.store.languageStore - currencyStore: profileView.store.walletStore.currencyStore - profileContentWidth: _internal.profileContentWidth - } + onBackButtonClicked: { + Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.messaging) + } + } - NotificationsView { - notificationsStore: profileView.store.notificationsStore - profileContentWidth: _internal.profileContentWidth - } + EnsView { + // TODO: we need to align structure for the entire this part using `SettingsContentBase` as root component - DevicesView { - devicesStore: profileView.store.devicesStore - } + // TODO: handle structure for this subsection to match style used in onther sections + // using `SettingsContentBase` component as base. + id: ensContainer + Layout.fillWidth: true + Layout.fillHeight: true - BrowserView { - store: profileView.store - profileContentWidth: _internal.profileContentWidth - } + ensUsernamesStore: profileView.store.ensUsernamesStore + contactsStore: profileView.store.contactsStore + stickersStore: profileView.store.stickersStore - AdvancedView { - advancedStore: profileView.store.advancedStore - profileContentWidth: _internal.profileContentWidth - } + profileContentWidth: d.contentWidth + } - HelpView { - profileContentWidth: _internal.profileContentWidth - } + MessagingView { + Layout.fillWidth: true + Layout.fillHeight: true - AboutView { - store: profileView.store - globalStore: profileView.globalStore - profileContentWidth: _internal.profileContentWidth + messagingStore: profileView.store.messagingStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.messaging) + contentWidth: d.contentWidth + } + + WalletView { + Layout.fillWidth: true + Layout.fillHeight: true + + walletStore: profileView.store.walletStore + emojiPopup: profileView.emojiPopup + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.wallet) + contentWidth: d.contentWidth + } + + PrivacyView { + Layout.fillWidth: true + Layout.fillHeight: true + + privacyStore: profileView.store.privacyStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.privacyAndSecurity) + contentWidth: d.contentWidth + } + + AppearanceView { + Layout.fillWidth: true + Layout.fillHeight: true + + appearanceStore: profileView.store.appearanceStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.appearance) + contentWidth: d.contentWidth + systemPalette: profileView.systemPalette + } + + SoundsView { + Layout.fillWidth: true + Layout.fillHeight: true + + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.sound) + contentWidth: d.contentWidth + } + + LanguageView { + Layout.fillWidth: true + Layout.fillHeight: true + + languageStore: profileView.store.languageStore + currencyStore: profileView.store.walletStore.currencyStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.language) + contentWidth: d.contentWidth + } + + NotificationsView { + Layout.fillWidth: true + Layout.fillHeight: true + + notificationsStore: profileView.store.notificationsStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.notifications) + contentWidth: d.contentWidth + } + + DevicesView { + Layout.fillWidth: true + Layout.fillHeight: true + + devicesStore: profileView.store.devicesStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.devicesSettings) + contentWidth: d.contentWidth + } + + BrowserView { + Layout.fillWidth: true + Layout.fillHeight: true + + store: profileView.store + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.browserSettings) + contentWidth: d.contentWidth + } + + AdvancedView { + Layout.fillWidth: true + Layout.fillHeight: true + + advancedStore: profileView.store.advancedStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.advanced) + contentWidth: d.contentWidth + } + + HelpView { + Layout.fillWidth: true + Layout.fillHeight: true + + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.needHelp) + contentWidth: d.contentWidth + } + + AboutView { + Layout.fillWidth: true + Layout.fillHeight: true + + store: profileView.store + globalStore: profileView.globalStore + sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.about) + contentWidth: d.contentWidth + } } } } diff --git a/ui/app/AppLayouts/Profile/controls/WalletNetworkDelegate.qml b/ui/app/AppLayouts/Profile/controls/WalletNetworkDelegate.qml index 81c6ee9715..e61f09899f 100644 --- a/ui/app/AppLayouts/Profile/controls/WalletNetworkDelegate.qml +++ b/ui/app/AppLayouts/Profile/controls/WalletNetworkDelegate.qml @@ -1,15 +1,16 @@ +import utils 1.0 + import StatusQ.Components 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 - StatusListItem { property var network title: network.chainName icon.isLetterIdenticon: true width: parent.width - leftPadding: 0 - rightPadding: 0 + leftPadding: Style.current.padding + rightPadding: Style.current.padding components: [ StatusIcon { icon: "chevron-down" @@ -17,4 +18,4 @@ StatusListItem { color: Theme.palette.baseColor1 } ] -} \ No newline at end of file +} diff --git a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml index 2c7cb406d7..408be0c26c 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml @@ -36,6 +36,7 @@ Item { id: title anchors.top: parent.top anchors.left: parent.left + anchors.leftMargin: Style.current.padding visible: contactListRoot.title !== "" text: contactListRoot.title font.weight: Font.Medium diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index 32008b2082..8b7bd8b4ad 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -139,4 +139,33 @@ QtObject { function checkForUpdates() { aboutModuleInst.checkForUpdates() } + + function getNameForSubsection(subsection) { + let i = 0; + for (; i < mainMenuItems.count; i++) { + let elem = mainMenuItems.get(i) + if(elem.subsection === subsection) + return elem.text + } + + for (i=0; i < appsMenuItems.count; i++) { + let elem = appsMenuItems.get(i) + if(elem.subsection === subsection) + return elem.text + } + + for (i=0; i < settingsMenuItems.count; i++) { + let elem = settingsMenuItems.get(i) + if(elem.subsection === subsection) + return elem.text + } + + for (i=0; i < extraMenuItems.count; i++) { + let elem = extraMenuItems.get(i) + if(elem.subsection === subsection) + return elem.text + } + + return "" + } } diff --git a/ui/app/AppLayouts/Profile/views/AboutView.qml b/ui/app/AppLayouts/Profile/views/AboutView.qml index 9938dca7a4..0523ecc427 100644 --- a/ui/app/AppLayouts/Profile/views/AboutView.qml +++ b/ui/app/AppLayouts/Profile/views/AboutView.qml @@ -10,26 +10,21 @@ import utils 1.0 import shared 1.0 import shared.status 1.0 -Item { +SettingsContentBase { id: root - Layout.fillHeight: true - Layout.fillWidth: true - clip: true property var store property var globalStore - property int profileContentWidth - Column { - id: generalColumn - spacing: Style.current.bigPadding - anchors.top: parent.top - anchors.topMargin: 46 - width: profileContentWidth - anchors.horizontalCenter: parent.horizontalCenter + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth // TODO: replace with StatusListItem StatusSectionDescItem { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding //% "App version" name: qsTrId("version") //% "Version: %1" @@ -40,6 +35,9 @@ Item { // TODO: replace with StatusListItem StatusSectionDescItem { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding //% "Node version " name: qsTrId("node-version-") description: root.store.nodeVersion() @@ -47,16 +45,20 @@ Item { } StatusFlatButton { + Layout.fillWidth: true + leftPadding: Style.current.padding + rightPadding: Style.current.padding //% "Check for updates" text: qsTrId("check-for-updates") loading: root.store.fetchingUpdate onClicked: root.store.checkForUpdates() icon.width: 0 - anchors.left: parent.left - anchors.leftMargin: -leftPadding } StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding //% "Privacy Policy" text: qsTrId("privacy-policy") font.pixelSize: 15 diff --git a/ui/app/AppLayouts/Profile/views/AdvancedView.qml b/ui/app/AppLayouts/Profile/views/AdvancedView.qml index 444279800e..6bcfed87c0 100644 --- a/ui/app/AppLayouts/Profile/views/AdvancedView.qml +++ b/ui/app/AppLayouts/Profile/views/AdvancedView.qml @@ -14,33 +14,26 @@ import "../controls" import "../popups" import "../panels" -ScrollView { +SettingsContentBase { id: root - property int profileContentWidth - - height: parent.height - width: parent.width - contentHeight: advancedContainer.height + 100 - clip: true - property AdvancedStore advancedStore Item { id: advancedContainer - width: profileContentWidth - anchors.horizontalCenter: parent.horizontalCenter + width: root.contentWidth height: generalColumn.height Column { id: generalColumn anchors.top: parent.top - anchors.topMargin: 64 anchors.left: parent.left - anchors.right: parent.right + width: root.contentWidth // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Network" text: qsTrId("network") visible: !localAccountSensitiveSettings.isMultiNetworkEnabled @@ -50,6 +43,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Fleet" text: qsTrId("fleet") currentValue: root.advancedStore.fleet @@ -58,6 +53,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Minimize on close" text: qsTrId("minimize-on-close") isSwitch: true @@ -69,6 +66,10 @@ ScrollView { // TODO: replace with StatusQ component StyledText { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding //% "Application Logs" text: qsTr("Application Logs") font.pixelSize: 15 @@ -94,14 +95,14 @@ ScrollView { } Separator { - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + width: parent.width } StatusSectionHeadline { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding //% "Experimental features" text: qsTrId("experimental-features") topPadding: Style.current.bigPadding @@ -110,6 +111,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Wallet" text: qsTrId("wallet") isSwitch: true @@ -126,6 +129,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Dapp Browser" text: qsTrId("dapp-browser") isSwitch: true @@ -142,6 +147,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Communities" text: qsTrId("communities") isSwitch: true @@ -157,6 +164,8 @@ ScrollView { } StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Communities" text: qsTrId("Community History Archive Protocol") isSwitch: true @@ -173,6 +182,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Activity Center" text: qsTrId("activity-center") isSwitch: true @@ -189,6 +200,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Node Management" text: qsTrId("node-management") isSwitch: true @@ -205,6 +218,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Multi network") isSwitch: true switchChecked: localAccountSensitiveSettings.isMultiNetworkEnabled @@ -220,6 +235,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Keycard" text: qsTr("Keycard") isSwitch: true @@ -230,6 +247,10 @@ ScrollView { } StatusSectionHeadline { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding visible: !root.advancedStore.isWakuV2 //% "Bloom filter level" text: qsTrId("bloom-filter-level") @@ -238,6 +259,10 @@ ScrollView { } Row { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding visible: !root.advancedStore.isWakuV2 spacing: 11 @@ -316,6 +341,10 @@ ScrollView { } StatusSectionHeadline { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding visible: root.advancedStore.isWakuV2 text: qsTr("WakuV2 mode") topPadding: Style.current.bigPadding @@ -323,6 +352,10 @@ ScrollView { } Row { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding spacing: 11 visible: root.advancedStore.isWakuV2 Component { @@ -382,21 +415,23 @@ ScrollView { } } - StatusSectionHeadline { + StatusSectionHeadline { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding text: qsTr("Developer features") topPadding: Style.current.bigPadding bottomPadding: Style.current.padding } Separator { - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + width: parent.width } StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Full developer mode") isEnabled: { return !localAccountSensitiveSettings.downloadChannelMessagesEnabled || @@ -410,15 +445,13 @@ ScrollView { } Separator { - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + width: parent.width } // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Download messages") isSwitch: true switchChecked: localAccountSensitiveSettings.downloadChannelMessagesEnabled @@ -429,6 +462,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Telemetry") isSwitch: true switchChecked: root.advancedStore.isTelemetryEnabled @@ -439,6 +474,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Debug") isSwitch: true switchChecked: root.advancedStore.isDebugEnabled @@ -449,6 +486,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Auto message") isSwitch: true switchChecked: root.advancedStore.isAutoMessageEnabled @@ -459,6 +498,8 @@ ScrollView { // TODO: replace with StatusQ component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 text: qsTr("Stickers/ENS on ropsten") visible: root.advancedStore.currentNetworkId === Constants.networkRopsten isSwitch: true diff --git a/ui/app/AppLayouts/Profile/views/AppearanceView.qml b/ui/app/AppLayouts/Profile/views/AppearanceView.qml index 7b634ba2fa..9a56fb4bd3 100644 --- a/ui/app/AppLayouts/Profile/views/AppearanceView.qml +++ b/ui/app/AppLayouts/Profile/views/AppearanceView.qml @@ -16,17 +16,12 @@ import StatusQ.Controls 0.1 as StatusQ import "../popups" import "../stores" -ScrollView { +SettingsContentBase { id: appearanceView - height: parent.height - width: parent.width - contentHeight: appearanceContainer.height - clip: true property AppearanceStore appearanceStore property var systemPalette - property int profileContentWidth enum Theme { Light, @@ -49,9 +44,9 @@ ScrollView { Item { id: appearanceContainer - width: profileContentWidth - - anchors.horizontalCenter: parent.horizontalCenter + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + width: appearanceView.contentWidth - 2 * Style.current.padding height: this.childrenRect.height + 100 ButtonGroup { @@ -69,6 +64,7 @@ ScrollView { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right + anchors.topMargin: 0 } Rectangle { @@ -76,9 +72,7 @@ ScrollView { anchors.top: sectionHeadlinePreview.bottom anchors.topMargin: Style.current.smallPadding anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right - anchors.rightMargin: -Style.current.padding height: paceholderMessage.height + Style.current.padding*4 radius: Style.current.radius border.color: Style.current.border @@ -313,9 +307,7 @@ ScrollView { anchors.top: sectionHeadlineAppearance.bottom anchors.topMargin: Style.current.padding anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right - anchors.rightMargin: -Style.current.padding StatusImageRadioButton { padding: Style.current.smallPadding diff --git a/ui/app/AppLayouts/Profile/views/BrowserView.qml b/ui/app/AppLayouts/Profile/views/BrowserView.qml index 8c28100880..98fdb19fb1 100644 --- a/ui/app/AppLayouts/Profile/views/BrowserView.qml +++ b/ui/app/AppLayouts/Profile/views/BrowserView.qml @@ -17,52 +17,38 @@ import "../stores" import "browser" import "wallet" -ScrollView { +SettingsContentBase { id: root - Layout.fillHeight: true - Layout.fillWidth: true - clip: true property ProfileSectionStore store - property real profileContentWidth property Component searchEngineModal: SearchEngineModal {} - contentHeight: rootItem.height - Item { id: rootItem - width: parent.width + width: root.contentWidth height: childrenRect.height Column { id: layout anchors.top: parent.top - anchors.topMargin: 24 anchors.left: parent.left - anchors.leftMargin: 48 - width: profileContentWidth + width: parent.width spacing: 10 - StatusBaseText { - id: titleText - text: qsTr("Browser") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 - } - - Item { - height: 25 - width: 1 - } HomePageView { id: homePageView homepage: localAccountSensitiveSettings.browserHomepage + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } // TODO: Replace with StatusQ StatusListItem component StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Search engine used in the address bar" text: qsTrId("search-engine-used-in-the-address-bar") currentValue: { @@ -80,12 +66,15 @@ ScrollView { DefaultDAppExplorerView { id: dAppExplorerView + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } StatusListItem { id: showFavouritesItem - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width + Style.current.padding * 2 + width: parent.width title: qsTr("Show Favorites Bar") components: [ StatusSwitch { @@ -99,22 +88,26 @@ ScrollView { Separator { id: separator1 - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + width: parent.width } StatusBaseText { text: qsTr("Connected DApps") font.pixelSize: 15 color: Theme.palette.baseColor1 + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } PermissionsListView { id: permissionListView - width: parent.width walletStore: root.store.walletStore + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } } // Column } // Item diff --git a/ui/app/AppLayouts/Profile/views/ContactsView.qml b/ui/app/AppLayouts/Profile/views/ContactsView.qml index 287e363bd2..b46877e008 100644 --- a/ui/app/AppLayouts/Profile/views/ContactsView.qml +++ b/ui/app/AppLayouts/Profile/views/ContactsView.qml @@ -20,75 +20,42 @@ import "../popups" // TODO remove this import when the ContactRequestPanel is moved to the the Profile completely import "../../Chat/panels" -Item { +SettingsContentBase { id: root property ContactsStore contactsStore - property int profileContentWidth property alias searchStr: searchBox.text property bool isPending: false - height: parent.height - Layout.fillWidth: true - clip: true - Item { - height: parent.height - width: profileContentWidth - - anchors.horizontalCenter: parent.horizontalCenter - - StatusFlatButton { - icon.name: "arrow-left" - icon.width: 20 - icon.height: 20 - text: qsTr("Messaging") - size: StatusBaseButton.Size.Large - anchors.top: parent.top - anchors.topMargin: 8 - anchors.left: parent.left - anchors.leftMargin: -40 - onClicked: Global.changeAppSectionBySectionType(Constants.appSection.profile, - Constants.settingsSubsection.messaging) - } - - RowLayout { - id: contactsHeader - anchors.top: parent.top - anchors.topMargin: 56 - anchors.left: parent.left - anchors.right: parent.right - - StatusBaseText { - Layout.fillWidth: true - text: qsTr("Contacts") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 - } - - StatusButton { - text: qsTr("Send contact request to chat key") - onClicked: { - sendContactRequest.open() - } + headerComponents: [ + StatusButton { + text: qsTr("Send contact request to chat key") + onClicked: { + sendContactRequest.open() } } + ] + + ColumnLayout { + spacing: 0 + width: root.contentWidth SearchBox { id: searchBox - anchors.top: contactsHeader.bottom - anchors.topMargin: 32 - width: parent.width + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding input.implicitHeight: 44 input.placeholderText: qsTr("Search by a display name or chat key") } TabBar { id: contactsTabBar - width: parent.width - anchors.top: searchBox.bottom - anchors.topMargin: Style.current.padding + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + Layout.topMargin: Style.current.padding height: contactsBtn.height background: Rectangle { color: Style.current.transparent @@ -107,13 +74,13 @@ Item { badge.value: contactList.count } // Temporary commented until we provide appropriate flags on the `status-go` side to cover all sections. -// StatusTabButton { -// id: rejectedRequestsBtn -// addToWidth: Style.current.bigPadding -// enabled: root.contactsStore.receivedButRejectedContactRequestsModel.count > 0 || -// root.contactsStore.sentButRejectedContactRequestsModel.count > 0 -// btnText: qsTr("Rejected Requests") -// } + // StatusTabButton { + // id: rejectedRequestsBtn + // addToWidth: Style.current.bigPadding + // enabled: root.contactsStore.receivedButRejectedContactRequestsModel.count > 0 || + // root.contactsStore.sentButRejectedContactRequestsModel.count > 0 + // btnText: qsTr("Rejected Requests") + // } StatusTabButton { id: blockedBtn addToWidth: Style.current.bigPadding @@ -124,10 +91,8 @@ Item { StackLayout { id: stackLayout - width: parent.width - anchors.bottom: parent.bottom - anchors.top: contactsTabBar.bottom - anchors.topMargin: Style.current.padding + Layout.fillWidth: true + Layout.fillHeight: true currentIndex: contactsTabBar.currentIndex // CONTACTS @@ -140,7 +105,7 @@ Item { ContactsListPanel { Layout.fillWidth: true - Layout.preferredHeight: parent.height * 0.5 + Layout.preferredHeight: root.height * 0.5 contactsModel: root.contactsStore.myContactsModel clip: true title: qsTr("Identity Verified Contacts") @@ -162,7 +127,7 @@ Item { ContactsListPanel { Layout.fillWidth: true - Layout.preferredHeight: parent.height * 0.5 + Layout.preferredHeight: root.height * 0.5 contactsModel: root.contactsStore.myContactsModel clip: true title: qsTr("Contacts") @@ -207,7 +172,7 @@ Item { ContactsListPanel { Layout.fillWidth: true - Layout.preferredHeight: parent.height * 0.5 + Layout.preferredHeight: root.height * 0.5 clip: true title: qsTr("Received") searchString: searchBox.text @@ -233,7 +198,7 @@ Item { ContactsListPanel { Layout.fillWidth: true - Layout.preferredHeight: parent.height * 0.5 + Layout.preferredHeight: root.height * 0.5 clip: true title: qsTr("Sent") searchString: searchBox.text @@ -257,60 +222,60 @@ Item { } // Temporary commented until we provide appropriate flags on the `status-go` side to cover all sections. -// // REJECTED REQUESTS -// Item { -// Layout.fillWidth: true -// Layout.fillHeight: true + // // REJECTED REQUESTS + // Item { + // Layout.fillWidth: true + // Layout.fillHeight: true -// ColumnLayout { -// anchors.fill: parent + // ColumnLayout { + // anchors.fill: parent -// ContactsListPanel { -// Layout.fillWidth: true -// Layout.preferredHeight: parent.height * 0.5 -// clip: true -// title: qsTr("Received") -// searchString: searchBox.text -// contactsModel: root.contactsStore.receivedButRejectedContactRequestsModel -// panelUsage: Constants.contactsPanelUsage.rejectedReceivedContactRequest + // ContactsListPanel { + // Layout.fillWidth: true + // Layout.preferredHeight: root.height * 0.5 + // clip: true + // title: qsTr("Received") + // searchString: searchBox.text + // contactsModel: root.contactsStore.receivedButRejectedContactRequestsModel + // panelUsage: Constants.contactsPanelUsage.rejectedReceivedContactRequest -// onOpenProfilePopup: { -// Global.openProfilePopup(publicKey) -// } + // onOpenProfilePopup: { + // Global.openProfilePopup(publicKey) + // } -// onOpenChangeNicknamePopup: { -// Global.openProfilePopup(publicKey, null, true) -// } + // onOpenChangeNicknamePopup: { + // Global.openProfilePopup(publicKey, null, true) + // } -// onRejectionRemoved: { -// root.contactsStore.removeContactRequestRejection(publicKey) -// } -// } + // onRejectionRemoved: { + // root.contactsStore.removeContactRequestRejection(publicKey) + // } + // } -// ContactsListPanel { -// Layout.fillWidth: true -// Layout.preferredHeight: parent.height * 0.5 -// clip: true -// title: qsTr("Sent") -// searchString: searchBox.text -// contactsModel: root.contactsStore.sentButRejectedContactRequestsModel -// panelUsage: Constants.contactsPanelUsage.rejectedSentContactRequest + // ContactsListPanel { + // Layout.fillWidth: true + // Layout.preferredHeight: root.height * 0.5 + // clip: true + // title: qsTr("Sent") + // searchString: searchBox.text + // contactsModel: root.contactsStore.sentButRejectedContactRequestsModel + // panelUsage: Constants.contactsPanelUsage.rejectedSentContactRequest -// onOpenProfilePopup: { -// Global.openProfilePopup(publicKey) -// } + // onOpenProfilePopup: { + // Global.openProfilePopup(publicKey) + // } -// onOpenChangeNicknamePopup: { -// Global.openProfilePopup(publicKey, null, true) -// } -// } + // onOpenChangeNicknamePopup: { + // Global.openProfilePopup(publicKey, null, true) + // } + // } -// Item { -// Layout.fillWidth: true -// Layout.fillHeight: true -// } -// } -// } + // Item { + // Layout.fillWidth: true + // Layout.fillHeight: true + // } + // } + // } // BLOCKED ContactsListPanel { @@ -335,51 +300,51 @@ Item { height: 12 } } - } - // TODO: Make BlockContactConfirmationDialog a dynamic component on a future refactor - BlockContactConfirmationDialog { - id: blockContactConfirmationDialog - onBlockButtonClicked: { - root.contactsStore.blockContact(blockContactConfirmationDialog.contactAddress) - blockContactConfirmationDialog.close() - } - } - - - // TODO: Make ConfirmationDialog a dynamic component on a future refactor - ConfirmationDialog { - id: removeContactConfirmationDialog - //% "Remove contact" - header.title: qsTrId("remove-contact") - //% "Are you sure you want to remove this contact?" - confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-") - onConfirmButtonClicked: { - if (Utils.getContactDetailsAsJson(removeContactConfirmationDialog.value).isContact) { - root.contactsStore.removeContact(removeContactConfirmationDialog.value); + // TODO: Make BlockContactConfirmationDialog a dynamic component on a future refactor + BlockContactConfirmationDialog { + id: blockContactConfirmationDialog + onBlockButtonClicked: { + root.contactsStore.blockContact(blockContactConfirmationDialog.contactAddress) + blockContactConfirmationDialog.close() } - removeContactConfirmationDialog.close() - } - } - - Loader { - id: sendContactRequest - active: false - - function open() { - active = true - sendContactRequest.item.open() - } - function close() { - active = false } - sourceComponent: SendContactRequestModal { - anchors.centerIn: parent - contactsStore: root.contactsStore - onClosed: { - sendContactRequest.close(); + // TODO: Make ConfirmationDialog a dynamic component on a future refactor + ConfirmationDialog { + id: removeContactConfirmationDialog + //% "Remove contact" + header.title: qsTrId("remove-contact") + //% "Are you sure you want to remove this contact?" + confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-") + onConfirmButtonClicked: { + if (Utils.getContactDetailsAsJson(removeContactConfirmationDialog.value).isContact) { + root.contactsStore.removeContact(removeContactConfirmationDialog.value); + } + removeContactConfirmationDialog.close() + } + } + + Loader { + id: sendContactRequest + active: false + + function open() { + active = true + sendContactRequest.item.open() + } + function close() { + active = false + } + + sourceComponent: SendContactRequestModal { + anchors.centerIn: root + contactsStore: root.contactsStore + + onClosed: { + sendContactRequest.close(); + } } } } diff --git a/ui/app/AppLayouts/Profile/views/DevicesView.qml b/ui/app/AppLayouts/Profile/views/DevicesView.qml index 4f03c38a2d..bbd95c2155 100644 --- a/ui/app/AppLayouts/Profile/views/DevicesView.qml +++ b/ui/app/AppLayouts/Profile/views/DevicesView.qml @@ -14,234 +14,231 @@ import shared.controls 1.0 import "../stores" -Item { +SettingsContentBase { id: root property DevicesStore devicesStore property bool isSyncing: false - width: 200 - height: 200 - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - Item { - id: firstTimeSetup - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - anchors.top: parent.top - anchors.topMargin: 24 - anchors.right: parent.right - anchors.rightMargin: Style.current.padding - visible: !root.devicesStore.isDeviceSetup - height: visible ? childrenRect.height : 0 + width: root.contentWidth + height: this.childrenRect.height - StatusBaseText { - id: deviceNameLbl - //% "Please set a name for your device." - text: qsTrId("pairing-please-set-a-name") - font.pixelSize: 14 - color: Theme.palette.directColor1 - } - - Input { - id: deviceNameTxt - //% "Specify a name" - placeholderText: qsTrId("specify-name") - anchors.top: deviceNameLbl.bottom - anchors.topMargin: Style.current.padding - } - - // TODO: replace with StatusQ component - StatusButton { - anchors.top: deviceNameTxt.bottom - anchors.topMargin: 10 - anchors.right: deviceNameTxt.right - //% "Continue" - text: qsTrId("continue") - enabled: deviceNameTxt.text !== "" - onClicked : root.devicesStore.setName(deviceNameTxt.text.trim()) - } - } - - Item { - id: advertiseDeviceItem - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - anchors.top: firstTimeSetup.visible ? firstTimeSetup.bottom : parent.top - anchors.topMargin: Style.current.padding - anchors.right: parent.right - anchors.rightMargin: Style.current.padding - visible: root.devicesStore.isDeviceSetup - height: visible ? childrenRect.height : 0 - - Rectangle { - id: advertiseDevice - height: childrenRect.height - width: 500 + Item { + id: firstTimeSetup anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.top: parent.top anchors.right: parent.right - color: Style.current.transparent - - SVGImage { - id: advertiseImg - height: 32 - width: 32 - anchors.left: parent.left - fillMode: Image.PreserveAspectFit - source: Style.svg("messageActive") - ColorOverlay { - anchors.fill: parent - source: parent - color: Style.current.blue - } - } + anchors.rightMargin: Style.current.padding + visible: !root.devicesStore.isDeviceSetup + height: visible ? childrenRect.height : 0 StatusBaseText { - id: advertiseDeviceTitle - //% "Advertise device" - text: qsTrId("pair-this-device") - font.pixelSize: 18 - font.weight: Font.Bold - color: Theme.palette.primaryColor1 - anchors.left: advertiseImg.right - anchors.leftMargin: Style.current.padding - } - - StatusBaseText { - id: advertiseDeviceDesk - //% "Pair your devices to sync contacts and chats between them" - text: qsTrId("pair-this-device-description") + id: deviceNameLbl + //% "Please set a name for your device." + text: qsTrId("pairing-please-set-a-name") font.pixelSize: 14 - anchors.top: advertiseDeviceTitle.bottom - anchors.topMargin: 6 - anchors.left: advertiseImg.right - anchors.leftMargin: Style.current.padding color: Theme.palette.directColor1 } - MouseArea { - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - onClicked: root.devicesStore.advertise() + Input { + id: deviceNameTxt + //% "Specify a name" + placeholderText: qsTrId("specify-name") + anchors.top: deviceNameLbl.bottom + anchors.topMargin: Style.current.padding } - } - StatusBaseText { - anchors.top: advertiseDevice.bottom - anchors.topMargin: Style.current.padding - //% "Learn more" - text: qsTrId("learn-more") - font.pixelSize: 16 - color: Theme.palette.primaryColor1 - anchors.left: parent.left - MouseArea { - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - onClicked: Global.openLink("https://status.im/user_guides/pairing_devices.html") - } - } - } - - - Item { - id: deviceListItem - anchors.left: root.left - anchors.leftMargin: Style.current.padding - anchors.top: advertiseDeviceItem.visible ? advertiseDeviceItem.bottom : parent.top - anchors.topMargin: Style.current.padding * 2 - anchors.bottom: syncAllBtn.top - anchors.bottomMargin: Style.current.padding - anchors.right: root.right - anchors.rightMargin: Style.current.padding - visible: root.devicesStore.isDeviceSetup - - StatusBaseText { - id: deviceListLbl - //% "Paired devices" - text: qsTrId("paired-devices") - font.pixelSize: 16 - font.weight: Font.Bold - color: Theme.palette.directColor1 - } - - ListView { - id: listView - anchors.bottom: parent.bottom - anchors.top: deviceListLbl.bottom - anchors.topMargin: Style.current.padding - spacing: 5 - anchors.right: parent.right - anchors.left: parent.left // TODO: replace with StatusQ component - delegate: Item { + StatusButton { + anchors.top: deviceNameTxt.bottom + anchors.topMargin: 10 + anchors.right: deviceNameTxt.right + //% "Continue" + text: qsTrId("continue") + enabled: deviceNameTxt.text !== "" + onClicked : root.devicesStore.setName(deviceNameTxt.text.trim()) + } + } + + Item { + id: advertiseDeviceItem + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.top: firstTimeSetup.visible ? firstTimeSetup.bottom : parent.top + anchors.topMargin: Style.current.padding + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + visible: root.devicesStore.isDeviceSetup + height: visible ? childrenRect.height : 0 + + Rectangle { + id: advertiseDevice height: childrenRect.height + width: 500 + anchors.left: parent.left + anchors.right: parent.right + color: Style.current.transparent + SVGImage { - id: enabledIcon - source: Style.svg(devicePairedSwitch.checked ? "messageActive" : "message") - height: 24 - width: 24 + id: advertiseImg + height: 32 + width: 32 + anchors.left: parent.left + fillMode: Image.PreserveAspectFit + source: Style.svg("messageActive") ColorOverlay { anchors.fill: parent source: parent color: Style.current.blue } } + StatusBaseText { - id: deviceItemLbl - text: { - let deviceId = model.installationId.split("-")[0].substr(0, 5) - //% "No info" - let labelText = `${model.name || qsTrId("pairing-no-info")} ` + - //% "you" - `(${model.isCurrentDevice ? qsTrId("you") + ", ": ""}${deviceId})`; - return labelText; - } - elide: Text.ElideRight + id: advertiseDeviceTitle + //% "Advertise device" + text: qsTrId("pair-this-device") + font.pixelSize: 18 + font.weight: Font.Bold + color: Theme.palette.primaryColor1 + anchors.left: advertiseImg.right + anchors.leftMargin: Style.current.padding + } + + StatusBaseText { + id: advertiseDeviceDesk + //% "Pair your devices to sync contacts and chats between them" + text: qsTrId("pair-this-device-description") font.pixelSize: 14 - anchors.left: enabledIcon.right + anchors.top: advertiseDeviceTitle.bottom + anchors.topMargin: 6 + anchors.left: advertiseImg.right anchors.leftMargin: Style.current.padding color: Theme.palette.directColor1 } - StatusSwitch { - id: devicePairedSwitch - visible: !model.isCurrentDevice - checked: model.enabled - anchors.left: deviceItemLbl.right - anchors.leftMargin: Style.current.padding - anchors.top: deviceItemLbl.top - onClicked: root.devicesStore.enableDevice(model.installationId, devicePairedSwitch) + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: root.devicesStore.advertise() } } - model: root.devicesStore.devicesModel + + StatusBaseText { + anchors.top: advertiseDevice.bottom + anchors.topMargin: Style.current.padding + //% "Learn more" + text: qsTrId("learn-more") + font.pixelSize: 16 + color: Theme.palette.primaryColor1 + anchors.left: parent.left + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: Global.openLink("https://status.im/user_guides/pairing_devices.html") + } + } + } + + + Item { + id: deviceListItem + anchors.left: root.left + anchors.leftMargin: Style.current.padding + anchors.top: advertiseDeviceItem.visible ? advertiseDeviceItem.bottom : parent.top + anchors.topMargin: Style.current.padding * 2 + anchors.bottom: syncAllBtn.top + anchors.bottomMargin: Style.current.padding + anchors.right: root.right + anchors.rightMargin: Style.current.padding + visible: root.devicesStore.isDeviceSetup + + StatusBaseText { + id: deviceListLbl + //% "Paired devices" + text: qsTrId("paired-devices") + font.pixelSize: 16 + font.weight: Font.Bold + color: Theme.palette.directColor1 + } + + ListView { + id: listView + anchors.bottom: parent.bottom + anchors.top: deviceListLbl.bottom + anchors.topMargin: Style.current.padding + spacing: 5 + anchors.right: parent.right + anchors.left: parent.left + // TODO: replace with StatusQ component + delegate: Item { + height: childrenRect.height + SVGImage { + id: enabledIcon + source: Style.svg(devicePairedSwitch.checked ? "messageActive" : "message") + height: 24 + width: 24 + ColorOverlay { + anchors.fill: parent + source: parent + color: Style.current.blue + } + } + StatusBaseText { + id: deviceItemLbl + text: { + let deviceId = model.installationId.split("-")[0].substr(0, 5) + //% "No info" + let labelText = `${model.name || qsTrId("pairing-no-info")} ` + + //% "you" + `(${model.isCurrentDevice ? qsTrId("you") + ", ": ""}${deviceId})`; + return labelText; + } + elide: Text.ElideRight + font.pixelSize: 14 + anchors.left: enabledIcon.right + anchors.leftMargin: Style.current.padding + color: Theme.palette.directColor1 + } + StatusSwitch { + id: devicePairedSwitch + visible: !model.isCurrentDevice + checked: model.enabled + anchors.left: deviceItemLbl.right + anchors.leftMargin: Style.current.padding + anchors.top: deviceItemLbl.top + onClicked: root.devicesStore.enableDevice(model.installationId, devicePairedSwitch) + } + } + model: root.devicesStore.devicesModel + } + } + + StatusButton { + id: syncAllBtn + anchors.bottom: parent.bottom + anchors.bottomMargin: Style.current.padding + anchors.horizontalCenter: parent.horizontalCenter + text: isSyncing ? + //% "Syncing..." + qsTrId("sync-in-progress") : + //% "Sync all devices" + qsTrId("sync-all-devices") + enabled: !isSyncing + onClicked : { + isSyncing = true; + root.devicesStore.syncAll() + // Currently we don't know how long it takes, so we just disable for 10s, to avoid spamming + timer.setTimeout(function(){ + isSyncing = false + }, 10000); + } + } + + Timer { + id: timer } } - - StatusButton { - id: syncAllBtn - anchors.bottom: root.bottom - anchors.bottomMargin: Style.current.padding - anchors.horizontalCenter: parent.horizontalCenter - text: isSyncing ? - //% "Syncing..." - qsTrId("sync-in-progress") : - //% "Sync all devices" - qsTrId("sync-all-devices") - enabled: !isSyncing - onClicked : { - isSyncing = true; - root.devicesStore.syncAll() - // Currently we don't know how long it takes, so we just disable for 10s, to avoid spamming - timer.setTimeout(function(){ - isSyncing = false - }, 10000); - } - } - - Timer { - id: timer - } - } diff --git a/ui/app/AppLayouts/Profile/views/HelpView.qml b/ui/app/AppLayouts/Profile/views/HelpView.qml index 0c8c2fd27c..c71b07dcb8 100644 --- a/ui/app/AppLayouts/Profile/views/HelpView.qml +++ b/ui/app/AppLayouts/Profile/views/HelpView.qml @@ -12,208 +12,184 @@ import shared.status 1.0 import "../panels" -Item { - id: helpContainer +SettingsContentBase { + id: root - property int profileContentWidth + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth - height: parent.height - Layout.fillWidth: true - clip: true - - ScrollView { - height: parent.height - width: parent.width - contentHeight: glossary.height + linksSection.height + Style.current.bigPadding * 4 - clip: true - Item { - id: glossary - anchors.top: parent.top - height: this.childrenRect.height - width: profileContentWidth - - anchors.horizontalCenter: parent.horizontalCenter - - StatusSectionHeadline { - id: glossaryTitle - //% "Glossary" - text: qsTrId("glossary") - anchors.left: parent.left - anchors.top: parent.top - } - - GlossaryEntry { - id: entryAccount - anchors.top: glossaryTitle.bottom - anchors.topMargin: Style.current.padding - //% "Account" - name: qsTrId("account-title") - //% "A" - //: This letter corresponds to the section title above, so here it is "A" because the title above is "Account" - letter: qsTrId("a") - //% "Your Status account, accessed by the seed phrase that you create or import during onboarding. A Status account can hold more than one Ethereum address, in addition to the one created during onboarding. We refer to these as additional accounts within the wallet" - description: qsTrId("your-status-account--accessed-by-the-seed-phrase-that-you-create-or-import-during-onboarding--a-status-account-can-hold-more-than-one-ethereum-address--in-addition-to-the-one-created-during-onboarding--we-refer-to-these-as-additional-accounts-within-the-wallet") - } - - GlossaryEntry { - id: entryChatKey - anchors.top: entryAccount.bottom - anchors.topMargin: Style.current.padding - //% "Chat Key" - name: qsTrId("chat-key-title") - //% "C" - //: This letter corresponds to the section title above, so here it is "C" because the title above is "Chat Key" - letter: qsTrId("c") - //% "Messages on the Status chat protocol are sent and received using encryption keys. The public chat key is a string of characters you share with others so they can send you messages in Status." - description: qsTrId("chat-key-content") - } - - GlossaryEntry { - id: entryChatName - anchors.top: entryChatKey.bottom - anchors.topMargin: Style.current.padding - //% "Chat Name" - name: qsTrId("chat-name-title") - //% "Three random words, derived algorithmically from your chat key and used as your default alias in chat. Chat names are completely unique; no other user can have the same three words." - description: qsTrId("chat-name-content") - } - - GlossaryEntry { - id: entryENSName - anchors.top: entryChatName.bottom - anchors.topMargin: Style.current.padding - //% "ENS Name" - name: qsTrId("ens-name-title") - //% "E" - //: This letter corresponds to the section title above, so here it is "E" because the title above is "ENS Name" - letter: qsTrId("e") - //% "Custom alias for your chat key that you can register using the Ethereum Name Service. ENS names are decentralized usernames." - description: qsTrId("ens-name-content") - } - - GlossaryEntry { - id: entryMailserver - anchors.top: entryENSName.bottom - anchors.topMargin: Style.current.padding - //% "Mailserver" - name: qsTrId("mailserver-title") - //% "M" - //: This letter corresponds to the section title above, so here it is "M" because the title above is "Mailserver" - letter: qsTrId("m") - //% "A node in the Status network that routes and stores messages, for up to 30 days." - description: qsTrId("mailserver-content") - } - - GlossaryEntry { - id: entryPeer - anchors.top: entryMailserver.bottom - anchors.topMargin: Style.current.padding - //% "Peer" - name: qsTrId("peer-title") - //% "P" - //: This letter corresponds to the section title above, so here it is "P" because the title above is "Peer" - letter: qsTrId("p") - //% "A device connected to the Status chat network. Each user can represent one or more peers, depending on their number of devices" - description: qsTrId("a-device-connected-to-the-status-chat-network--each-user-can-represent-one-or-more-peers--depending-on-their-number-of-devices") - } - - GlossaryEntry { - id: entrySeedPhrase - anchors.top: entryPeer.bottom - anchors.topMargin: Style.current.padding - //% "Seed Phrase" - name: qsTrId("seed-phrase-title") - //% "S" - //: This letter corresponds to the section title above, so here it is "S" because the title above is "Seed Phrase" - letter: qsTrId("s") - //% "A 64 character hex address based on the Ethereum standard and beginning with 0x. Public-facing, your wallet key is shared with others when you want to receive funds. Also referred to as an “Ethereum address” or “wallet address." - description: qsTrId("a-64-character-hex-address-based-on-the-ethereum-standard-and-beginning-with-0x--public-facing--your-wallet-key-is-shared-with-others-when-you-want-to-receive-funds--also-referred-to-as-an--ethereum-address--or--wallet-address-") - } + GlossaryEntry { + id: entryAccount + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Account" + name: qsTrId("account-title") + //% "A" + //: This letter corresponds to the section title above, so here it is "A" because the title above is "Account" + letter: qsTrId("a") + //% "Your Status account, accessed by the seed phrase that you create or import during onboarding. A Status account can hold more than one Ethereum address, in addition to the one created during onboarding. We refer to these as additional accounts within the wallet" + description: qsTrId("your-status-account--accessed-by-the-seed-phrase-that-you-create-or-import-during-onboarding--a-status-account-can-hold-more-than-one-ethereum-address--in-addition-to-the-one-created-during-onboarding--we-refer-to-these-as-additional-accounts-within-the-wallet") } + GlossaryEntry { + id: entryChatKey + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Chat Key" + name: qsTrId("chat-key-title") + //% "C" + //: This letter corresponds to the section title above, so here it is "C" because the title above is "Chat Key" + letter: qsTrId("c") + //% "Messages on the Status chat protocol are sent and received using encryption keys. The public chat key is a string of characters you share with others so they can send you messages in Status." + description: qsTrId("chat-key-content") + } - Item { - id: linksSection - anchors.top: glossary.bottom - anchors.topMargin: Style.current.bigPadding * 2 - height: this.childrenRect.height - width: profileContentWidth + GlossaryEntry { + id: entryChatName + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Chat Name" + name: qsTrId("chat-name-title") + //% "Three random words, derived algorithmically from your chat key and used as your default alias in chat. Chat names are completely unique; no other user can have the same three words." + description: qsTrId("chat-name-content") + } - anchors.horizontalCenter: parent.horizontalCenter + GlossaryEntry { + id: entryENSName + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "ENS Name" + name: qsTrId("ens-name-title") + //% "E" + //: This letter corresponds to the section title above, so here it is "E" because the title above is "ENS Name" + letter: qsTrId("e") + //% "Custom alias for your chat key that you can register using the Ethereum Name Service. ENS names are decentralized usernames." + description: qsTrId("ens-name-content") + } - StatusBaseText { - id: faqLink - //% "Frequently asked questions" - text: qsTrId("faq") - font.pixelSize: 15 - color: Theme.palette.primaryColor1 - anchors.topMargin: Style.current.bigPadding - anchors.top: parent.top + GlossaryEntry { + id: entryMailserver + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Mailserver" + name: qsTrId("mailserver-title") + //% "M" + //: This letter corresponds to the section title above, so here it is "M" because the title above is "Mailserver" + letter: qsTrId("m") + //% "A node in the Status network that routes and stores messages, for up to 30 days." + description: qsTrId("mailserver-content") + } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: { - parent.font.underline = true - } - onExited: { - parent.font.underline = false - } - onClicked: { - Global.openLink("https://status.im/docs/FAQs.html") - } + GlossaryEntry { + id: entryPeer + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Peer" + name: qsTrId("peer-title") + //% "P" + //: This letter corresponds to the section title above, so here it is "P" because the title above is "Peer" + letter: qsTrId("p") + //% "A device connected to the Status chat network. Each user can represent one or more peers, depending on their number of devices" + description: qsTrId("a-device-connected-to-the-status-chat-network--each-user-can-represent-one-or-more-peers--depending-on-their-number-of-devices") + } + + GlossaryEntry { + id: entrySeedPhrase + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Seed Phrase" + name: qsTrId("seed-phrase-title") + //% "S" + //: This letter corresponds to the section title above, so here it is "S" because the title above is "Seed Phrase" + letter: qsTrId("s") + //% "A 64 character hex address based on the Ethereum standard and beginning with 0x. Public-facing, your wallet key is shared with others when you want to receive funds. Also referred to as an “Ethereum address” or “wallet address." + description: qsTrId("a-64-character-hex-address-based-on-the-ethereum-standard-and-beginning-with-0x--public-facing--your-wallet-key-is-shared-with-others-when-you-want-to-receive-funds--also-referred-to-as-an--ethereum-address--or--wallet-address-") + } + + StatusBaseText { + id: faqLink + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Frequently asked questions" + text: qsTrId("faq") + font.pixelSize: 15 + color: Theme.palette.primaryColor1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: { + parent.font.underline = true } - } - StatusBaseText { - id: issueLink - //% "Submit a bug" - text: qsTrId("submit-bug") - font.pixelSize: 15 - color: Theme.palette.primaryColor1 - anchors.topMargin: Style.current.bigPadding - anchors.top: faqLink.bottom - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: { - parent.font.underline = true - } - onExited: { - parent.font.underline = false - } - onClicked: { - Global.openLink("https://github.com/status-im/nim-status-client/issues/new") - } + onExited: { + parent.font.underline = false } - } - - StatusBaseText { - //% "Request a feature" - text: qsTrId("request-feature") - font.pixelSize: 15 - color: Theme.palette.primaryColor1 - anchors.topMargin: Style.current.bigPadding - anchors.top: issueLink.bottom - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: { - parent.font.underline = true - } - onExited: { - parent.font.underline = false - } - onClicked: { - Global.openLink("https://discuss.status.im/c/features/51") - } + onClicked: { + Global.openLink("https://status.im/docs/FAQs.html") } } } + StatusBaseText { + id: issueLink + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Submit a bug" + text: qsTrId("submit-bug") + font.pixelSize: 15 + color: Theme.palette.primaryColor1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: { + parent.font.underline = true + } + onExited: { + parent.font.underline = false + } + onClicked: { + Global.openLink("https://github.com/status-im/nim-status-client/issues/new") + } + } + } + + StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + //% "Request a feature" + text: qsTrId("request-feature") + font.pixelSize: 15 + color: Theme.palette.primaryColor1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: { + parent.font.underline = true + } + onExited: { + parent.font.underline = false + } + onClicked: { + Global.openLink("https://discuss.status.im/c/features/51") + } + } + } + } } diff --git a/ui/app/AppLayouts/Profile/views/LanguageView.qml b/ui/app/AppLayouts/Profile/views/LanguageView.qml index 6f7b88ddd7..ae1d73bcdd 100644 --- a/ui/app/AppLayouts/Profile/views/LanguageView.qml +++ b/ui/app/AppLayouts/Profile/views/LanguageView.qml @@ -13,28 +13,11 @@ import StatusQ.Components 0.1 import "../popups" import "../stores" -Item { +SettingsContentBase { id: root property LanguageStore languageStore property var currencyStore - property int profileContentWidth - - QtObject { - id: d - property int margins: 64 - property int zOnTop: 100 - - function setViewIdleState() { - currencyPicker.close() - languagePicker.close() - } - } - - z: d.zOnTop - Layout.fillHeight: true - Layout.fillWidth: true - clip: true onVisibleChanged: { if(!visible) d.setViewIdleState()} @@ -43,150 +26,145 @@ Item { root.languageStore.initializeLanguageModel() } - Column { - z: d.zOnTop - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.margins: d.margins - anchors.left: parent.left - spacing: 45 + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth - StatusBaseText { - id: title - text: qsTr("Language & Currency") - font.weight: Font.Bold - font.pixelSize: 22 - color: Theme.palette.directColor1 + QtObject { + id: d + property int margins: 64 + property int zOnTop: 100 + + function setViewIdleState() { + currencyPicker.close() + languagePicker.close() + } } - Column { - z: d.zOnTop - width: 560 - (2 * Style.current.padding) - spacing: 26 + Item { + id: currency + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + height: 38 + z: d.zOnTop + 1 - Item { - id: currency - width: parent.width - height: 38 - z: d.zOnTop + 1 - - StatusBaseText { - text: qsTr("Set Display Currency") - anchors.left: parent.left - font.pixelSize: 15 - color: Theme.palette.directColor1 - } - StatusListPicker { - id: currencyPicker - - property string newKey - - Timer { - id: currencyPause - interval: 100 - onTriggered: { - // updateCurrency function operation blocks a little bit the UI so getting around it with a small pause (timer) in order to get the desired visual behavior - root.currencyStore.updateCurrency(currencyPicker.newKey) - } - } - - z: d.zOnTop + 1 - width: 104 - height: parent.height - anchors.right: parent.right - inputList: root.currencyStore.currenciesModel - printSymbol: true - placeholderSearchText: qsTr("Search Currencies") - - onItemPickerChanged: { - if(selected) { - currencyPicker.newKey = key - currencyPause.start() - } - } - } - } - - Item { - id: language - width: parent.width - height: 38 - z: d.zOnTop - - StatusBaseText { - text: qsTr("Language") - anchors.left: parent.left - font.pixelSize: 15 - color: Theme.palette.directColor1 - } - StatusListPicker { - id: languagePicker - - property string newKey - - Timer { - id: languagePause - interval: 100 - onTriggered: { - // changeLocale function operation blocks a little bit the UI so getting around it with a small pause (timer) in order to get the desired visual behavior - root.languageStore.changeLocale(languagePicker.newKey) - } - } - - z: d.zOnTop - width: 104 - height: parent.height - anchors.right: parent.right - inputList: root.languageStore.languageModel - placeholderSearchText: qsTr("Search Languages") - - onItemPickerChanged: { - if(selected && localAppSettings.locale !== key) { - // TEMPORARY: It should be removed as it is only used in Linux OS but it must be investigated how to change language in execution time, as well, in Linux (will be addressed in another task) - if (Qt.platform.os === Constants.linux) { - linuxConfirmationDialog.active = true - linuxConfirmationDialog.item.newLocale = key - linuxConfirmationDialog.item.open() - } - else { - languagePicker.newKey = key - languagePause.start() - } - } - } - } - } - - Separator { + StatusBaseText { + text: qsTr("Set Display Currency") anchors.left: parent.left - anchors.leftMargin: -Style.current.padding + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + StatusListPicker { + id: currencyPicker + + property string newKey + + Timer { + id: currencyPause + interval: 100 + onTriggered: { + // updateCurrency function operation blocks a little bit the UI so getting around it with a small pause (timer) in order to get the desired visual behavior + root.currencyStore.updateCurrency(currencyPicker.newKey) + } + } + + z: d.zOnTop + 1 + width: 104 + height: parent.height anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + inputList: root.currencyStore.currenciesModel + printSymbol: true + placeholderSearchText: qsTr("Search Currencies") + + onItemPickerChanged: { + if(selected) { + currencyPicker.newKey = key + currencyPause.start() + } + } } } - } - // TEMPORARY: It should be removed as it is only used in Linux OS but it must be investigated how to change language in execution time, as well, in Linux (will be addressed in another task) - Loader { - id: linuxConfirmationDialog - active: false - sourceComponent: ConfirmationDialog { - property string newLocale + Item { + id: language + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + height: 38 + z: d.zOnTop - header.title: qsTr("Change language") - confirmationText: qsTr("Display language has been changed. You must restart the application for changes to take effect.") - confirmButtonLabel: qsTr("Close the app now") - onConfirmButtonClicked: { - root.languageStore.changeLocale(newLocale) - loader.active = false - Qt.quit() - } - } - } + StatusBaseText { + text: qsTr("Language") + anchors.left: parent.left + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + StatusListPicker { + id: languagePicker - // Outsite area - MouseArea { - anchors.fill: parent - onClicked: { d.setViewIdleState() } + property string newKey + + Timer { + id: languagePause + interval: 100 + onTriggered: { + // changeLocale function operation blocks a little bit the UI so getting around it with a small pause (timer) in order to get the desired visual behavior + root.languageStore.changeLocale(languagePicker.newKey) + } + } + + z: d.zOnTop + width: 104 + height: parent.height + anchors.right: parent.right + inputList: root.languageStore.languageModel + placeholderSearchText: qsTr("Search Languages") + + onItemPickerChanged: { + if(selected && localAppSettings.locale !== key) { + // TEMPORARY: It should be removed as it is only used in Linux OS but it must be investigated how to change language in execution time, as well, in Linux (will be addressed in another task) + if (Qt.platform.os === Constants.linux) { + linuxConfirmationDialog.active = true + linuxConfirmationDialog.item.newLocale = key + linuxConfirmationDialog.item.open() + } + else { + languagePicker.newKey = key + languagePause.start() + } + } + } + } + } + + Separator { + Layout.fillWidth: true + } + + + // TEMPORARY: It should be removed as it is only used in Linux OS but it must be investigated how to change language in execution time, as well, in Linux (will be addressed in another task) + Loader { + id: linuxConfirmationDialog + active: false + sourceComponent: ConfirmationDialog { + property string newLocale + + header.title: qsTr("Change language") + confirmationText: qsTr("Display language has been changed. You must restart the application for changes to take effect.") + confirmButtonLabel: qsTr("Close the app now") + onConfirmButtonClicked: { + root.languageStore.changeLocale(newLocale) + loader.active = false + Qt.quit() + } + } + } + + // Outsite area + MouseArea { + anchors.fill: parent + onClicked: { d.setViewIdleState() } + } } } diff --git a/ui/app/AppLayouts/Profile/views/MessagingView.qml b/ui/app/AppLayouts/Profile/views/MessagingView.qml index b3f12d16c7..f7f373b9cc 100644 --- a/ui/app/AppLayouts/Profile/views/MessagingView.qml +++ b/ui/app/AppLayouts/Profile/views/MessagingView.qml @@ -20,23 +20,15 @@ import "../controls" import "../popups" import "../panels" -ScrollView { +SettingsContentBase { id: root - property int profileContentWidth - - height: parent.height - width: parent.width - contentHeight: advancedContainer.height + 100 - clip: true - property MessagingStore messagingStore - Item { - id: advancedContainer - width: profileContentWidth - anchors.horizontalCenter: parent.horizontalCenter - height: generalColumn.height + ColumnLayout { + id: generalColumn + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth ButtonGroup { id: showProfilePictureToGroup @@ -50,395 +42,406 @@ ScrollView { id: browserGroup } + StatusListItem { + id: allowNewContactRequest + + Layout.fillWidth: true + implicitHeight: 64 + + //% "Allow new contact requests" + title: qsTrId("allow-new-contact-requests") + + components: [ + StatusSwitch { + id: switch3 + checked: !root.messagingStore.privacyModule.messagesFromContactsOnly + onCheckedChanged: { + // messagesFromContactsOnly needs to be accessed from the module (view), + // because otherwise doing `messagesFromContactsOnly = value` only changes the bool property on QML + if (root.messagingStore.privacyModule.messagesFromContactsOnly === checked) { + root.messagingStore.privacyModule.messagesFromContactsOnly = !checked + } + } + } + ] + sensor.onClicked: { + switch3.checked = !switch3.checked + } + } + + // SHOW PROFILE PICTURE TO + StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("Show My Profile Picture To") + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("Everyone") + group: showProfilePictureToGroup + checked: root.messagingStore.profilePicturesShowTo === + Constants.profilePicturesShowTo.everyone + onClicked: root.messagingStore.setProfilePicturesShowTo( + Constants.profilePicturesShowTo.everyone + ) + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("Contacts") + group: showProfilePictureToGroup + checked: root.messagingStore.profilePicturesShowTo === + Constants.profilePicturesShowTo.contactsOnly + onClicked: root.messagingStore.setProfilePicturesShowTo( + Constants.profilePicturesShowTo.contactsOnly + ) + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("No One") + group: showProfilePictureToGroup + checked: root.messagingStore.profilePicturesShowTo === + Constants.profilePicturesShowTo.noOne + onClicked: root.messagingStore.setProfilePicturesShowTo( + Constants.profilePicturesShowTo.noOne + ) + } + + Item { + id: spacer1 + Layout.fillWidth: true + Layout.preferredHeight: 6 + } + + // SEE PROFILTE PICTURES FROM + StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("See Profile Pictures From") + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("Everyone") + group: seeProfilePicturesFromGroup + checked: root.messagingStore.profilePicturesVisibility === + Constants.profilePicturesVisibility.everyone + onClicked: root.messagingStore.setProfilePicturesVisibility( + Constants.profilePicturesVisibility.everyone + ) + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("Contacts") + group: seeProfilePicturesFromGroup + checked: root.messagingStore.profilePicturesVisibility === + Constants.profilePicturesVisibility.contactsOnly + onClicked: root.messagingStore.setProfilePicturesVisibility( + Constants.profilePicturesVisibility.contactsOnly + ) + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("No One") + group: seeProfilePicturesFromGroup + checked: root.messagingStore.profilePicturesVisibility === + Constants.profilePicturesVisibility.noOne + onClicked: root.messagingStore.setProfilePicturesVisibility( + Constants.profilePicturesVisibility.noOne + ) + } + + Item { + id: spacer2 + Layout.fillWidth: true + Layout.preferredHeight: 6 + } + + // Open Message Links With + StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("Open Message Links With") + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("Status Browser") + group: browserGroup + checked: localAccountSensitiveSettings.openLinksInStatus + onClicked: { + localAccountSensitiveSettings.openLinksInStatus = true + } + } + + SettingsRadioButton { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + label: qsTr("System Default Browser") + group: browserGroup + checked: !localAccountSensitiveSettings.openLinksInStatus + onClicked: { + localAccountSensitiveSettings.openLinksInStatus = false + } + } + + Separator { + id: separator1 + Layout.fillWidth: true + } + + // CONTACTS SECTION + StatusContactRequestsIndicatorListItem { + Layout.fillWidth: true + title: qsTr("Contacts, Requests, and Blocked Users") + requestsCount: root.messagingStore.contactRequestsModel.count + sensor.onClicked: Global.changeAppSectionBySectionType(Constants.appSection.profile, + Constants.settingsSubsection.contacts) + } + + Separator { + id: separator2 + Layout.fillWidth: true + } + + // MESSAGE LINK PREVIEWS + StatusListItem { + Layout.fillWidth: true + title: qsTr("Display Message Link Previews") + implicitHeight: 64 + components: [ + StatusSwitch { + id: showMessageLinksSwitch + checked: false + onCheckedChanged: { + if (checked === false) { + // Switch all the whitelists to false + imageSwitch.checked = false + for (let i = 0; i < sitesListView.count; i++) { + let item = sitesListView.itemAt(i) + item.whitelistSwitch.checked = false + } + } + } + } + ] + sensor.onClicked: { + showMessageLinksSwitch.checked = !showMessageLinksSwitch.checked + } + } + + function populatePreviewableSites() { + let whitelistAsString = root.messagingStore.getLinkPreviewWhitelist() + if(whitelistAsString == "") + return + let whitelist = JSON.parse(whitelistAsString) + if (!localAccountSensitiveSettings.whitelistedUnfurlingSites) { + localAccountSensitiveSettings.whitelistedUnfurlingSites = {} + } + previewableSites.clear() + var oneEntryIsActive = false + whitelist.forEach(entry => { + entry.isWhitelisted = localAccountSensitiveSettings.whitelistedUnfurlingSites[entry.address] || false + if (entry.isWhitelisted) { + oneEntryIsActive = true + } + previewableSites.append(entry) + }) + if (oneEntryIsActive) { + showMessageLinksSwitch.checked = true + } + } + + Component.onCompleted: { + populatePreviewableSites() + } + + StatusSectionHeadline { + id: labelWebsites + visible: showMessageLinksSwitch.checked + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("Fine tune which sites to allow link previews") + } + Column { - id: generalColumn - spacing: 18 - anchors.top: parent.top - anchors.topMargin: 24 - anchors.left: parent.left - anchors.right: parent.right + id: siteColumn + visible: showMessageLinksSwitch.checked + Layout.fillWidth: true - StatusBaseText { - id: titleText - text: qsTr("Messaging") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 + ListModel { + id: previewableSites } + Connections { + target: Global + onSettingsLoaded: { + generalColumn.populatePreviewableSites() + } + } + + // Manually add switch for the image unfurling StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - //% "Allow new contact requests" - title: qsTrId("allow-new-contact-requests") + width: parent.width implicitHeight: 64 + title: qsTr("Image unfurling") + subTitle: qsTr("All images (links that contain an image extension) will be downloaded and displayed") + // TODO find a better icon for this + image.source: Style.svg('globe') + Component.onCompleted: { + if (localAccountSensitiveSettings.displayChatImages) { + showMessageLinksSwitch.checked = true + } + } components: [ StatusSwitch { - id: switch3 - checked: !root.messagingStore.privacyModule.messagesFromContactsOnly + id: imageSwitch + checked: localAccountSensitiveSettings.displayChatImages onCheckedChanged: { - // messagesFromContactsOnly needs to be accessed from the module (view), - // because otherwise doing `messagesFromContactsOnly = value` only changes the bool property on QML - if (root.messagingStore.privacyModule.messagesFromContactsOnly === checked) { - root.messagingStore.privacyModule.messagesFromContactsOnly = !checked + if (localAccountSensitiveSettings.displayChatImages !== checked) { + localAccountSensitiveSettings.displayChatImages = checked } } } ] sensor.onClicked: { - switch3.checked = !switch3.checked + imageSwitch.checked = !imageSwitch.checked } } - // SHOW PROFILE PICTURE TO - StatusBaseText { - text: qsTr("Show My Profile Picture To") - font.pixelSize: 15 - color: Theme.palette.directColor1 - } + Repeater { + id: sitesListView + model: previewableSites - SettingsRadioButton { - label: qsTr("Everyone") - group: showProfilePictureToGroup - checked: root.messagingStore.profilePicturesShowTo === - Constants.profilePicturesShowTo.everyone - onClicked: root.messagingStore.setProfilePicturesShowTo( - Constants.profilePicturesShowTo.everyone - ) - } - - SettingsRadioButton { - label: qsTr("Contacts") - group: showProfilePictureToGroup - checked: root.messagingStore.profilePicturesShowTo === - Constants.profilePicturesShowTo.contactsOnly - onClicked: root.messagingStore.setProfilePicturesShowTo( - Constants.profilePicturesShowTo.contactsOnly - ) - } - - SettingsRadioButton { - label: qsTr("No One") - group: showProfilePictureToGroup - checked: root.messagingStore.profilePicturesShowTo === - Constants.profilePicturesShowTo.noOne - onClicked: root.messagingStore.setProfilePicturesShowTo( - Constants.profilePicturesShowTo.noOne - ) - } - - Item { - id: spacer1 - width: parent.width - height: 6 - } - - // SEE PROFILTE PICTURES FROM - StatusBaseText { - text: qsTr("See Profile Pictures From") - font.pixelSize: 15 - color: Theme.palette.directColor1 - } - - SettingsRadioButton { - label: qsTr("Everyone") - group: seeProfilePicturesFromGroup - checked: root.messagingStore.profilePicturesVisibility === - Constants.profilePicturesVisibility.everyone - onClicked: root.messagingStore.setProfilePicturesVisibility( - Constants.profilePicturesVisibility.everyone - ) - } - - SettingsRadioButton { - label: qsTr("Contacts") - group: seeProfilePicturesFromGroup - checked: root.messagingStore.profilePicturesVisibility === - Constants.profilePicturesVisibility.contactsOnly - onClicked: root.messagingStore.setProfilePicturesVisibility( - Constants.profilePicturesVisibility.contactsOnly - ) - } - - SettingsRadioButton { - label: qsTr("No One") - group: seeProfilePicturesFromGroup - checked: root.messagingStore.profilePicturesVisibility === - Constants.profilePicturesVisibility.noOne - onClicked: root.messagingStore.setProfilePicturesVisibility( - Constants.profilePicturesVisibility.noOne - ) - } - - Item { - id: spacer2 - width: parent.width - height: 6 - } - - // Open Message Links With - StatusBaseText { - text: qsTr("Open Message Links With") - font.pixelSize: 15 - color: Theme.palette.directColor1 - } - - SettingsRadioButton { - label: qsTr("Status Browser") - group: browserGroup - checked: localAccountSensitiveSettings.openLinksInStatus - onClicked: { - localAccountSensitiveSettings.openLinksInStatus = true - } - } - - SettingsRadioButton { - label: qsTr("System Default Browser") - group: browserGroup - checked: !localAccountSensitiveSettings.openLinksInStatus - onClicked: { - localAccountSensitiveSettings.openLinksInStatus = false - } - } - - Separator { - id: separator1 - } - - // CONTACTS SECTION - StatusContactRequestsIndicatorListItem { - title: qsTr("Contacts, Requests, and Blocked Users") - requestsCount: root.messagingStore.contactRequestsModel.count - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - sensor.onClicked: Global.changeAppSectionBySectionType(Constants.appSection.profile, - Constants.settingsSubsection.contacts) - } - - Separator { - id: separator2 - } - - // MESSAGE LINK PREVIEWS - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - title: qsTr("Display Message Link Previews") - implicitHeight: 64 - components: [ - StatusSwitch { - id: showMessageLinksSwitch - checked: false - onCheckedChanged: { - if (checked === false) { - // Switch all the whitelists to false - imageSwitch.checked = false - for (let i = 0; i < sitesListView.count; i++) { - let item = sitesListView.itemAt(i) - item.whitelistSwitch.checked = false - } + delegate: Component { + StatusListItem { + property alias whitelistSwitch: siteSwitch + width: parent.width + implicitHeight: 64 + title: model.title + subTitle: model.address + image.source: { + let filename; + switch (model.title.toLowerCase()) { + case "youtube": + case "youtube shortener": + filename = "youtube"; break; + case "github": + filename = "github"; break; + case "medium": + filename = "medium"; break; + case "tenor gifs": + filename = "tenor"; break; + case "giphy gifs": + case "giphy gifs shortener": + case "giphy gifs subdomain": + filename = "giphy"; break; + case "github": + filename = "github"; break; + case "status": + filename = "status"; break; + // TODO get a good default icon + default: filename = "../globe" } + return Style.svg(`linkPreviewThumbnails/${filename}`) } - } - ] - sensor.onClicked: { - showMessageLinksSwitch.checked = !showMessageLinksSwitch.checked - } - } + components: [ + StatusSwitch { + id: siteSwitch + checked: !!model.isWhitelisted + onCheckedChanged: { + let settings = localAccountSensitiveSettings.whitelistedUnfurlingSites - function populatePreviewableSites() { - let whitelistAsString = root.messagingStore.getLinkPreviewWhitelist() - if(whitelistAsString == "") - return - let whitelist = JSON.parse(whitelistAsString) - if (!localAccountSensitiveSettings.whitelistedUnfurlingSites) { - localAccountSensitiveSettings.whitelistedUnfurlingSites = {} - } - previewableSites.clear() - var oneEntryIsActive = false - whitelist.forEach(entry => { - entry.isWhitelisted = localAccountSensitiveSettings.whitelistedUnfurlingSites[entry.address] || false - if (entry.isWhitelisted) { - oneEntryIsActive = true - } - previewableSites.append(entry) - }) - if (oneEntryIsActive) { - showMessageLinksSwitch.checked = true - } - } - - Component.onCompleted: { - populatePreviewableSites() - } - - Column { - id: siteColumn - visible: showMessageLinksSwitch.checked - width: parent.width - - StatusSectionHeadline { - id: labelWebsites - text: qsTr("Fine tune which sites to allow link previews") - width: parent.width - } - - ListModel { - id: previewableSites - } - - Connections { - target: Global - onSettingsLoaded: { - generalColumn.populatePreviewableSites() - } - } - - // Manually add switch for the image unfurling - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - implicitHeight: 64 - title: qsTr("Image unfurling") - subTitle: qsTr("All images (links that contain an image extension) will be downloaded and displayed") - // TODO find a better icon for this - image.source: Style.svg('globe') - Component.onCompleted: { - if (localAccountSensitiveSettings.displayChatImages) { - showMessageLinksSwitch.checked = true - } - } - components: [ - StatusSwitch { - id: imageSwitch - checked: localAccountSensitiveSettings.displayChatImages - onCheckedChanged: { - if (localAccountSensitiveSettings.displayChatImages !== checked) { - localAccountSensitiveSettings.displayChatImages = checked - } - } - } - ] - sensor.onClicked: { - imageSwitch.checked = !imageSwitch.checked - } - } - - Repeater { - id: sitesListView - model: previewableSites - - delegate: Component { - StatusListItem { - property alias whitelistSwitch: siteSwitch - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - implicitHeight: 64 - title: model.title - subTitle: model.address - image.source: { - let filename; - switch (model.title.toLowerCase()) { - case "youtube": - case "youtube shortener": - filename = "youtube"; break; - case "github": - filename = "github"; break; - case "medium": - filename = "medium"; break; - case "tenor gifs": - filename = "tenor"; break; - case "giphy gifs": - case "giphy gifs shortener": - case "giphy gifs subdomain": - filename = "giphy"; break; - case "github": - filename = "github"; break; - case "status": - filename = "status"; break; - // TODO get a good default icon - default: filename = "../globe" - } - return Style.svg(`linkPreviewThumbnails/${filename}`) - } - components: [ - StatusSwitch { - id: siteSwitch - checked: !!model.isWhitelisted - onCheckedChanged: { - let settings = localAccountSensitiveSettings.whitelistedUnfurlingSites - - if (!settings) { - settings = {} - } - - if (settings[address] === this.checked) { - return - } - - settings[address] = this.checked - localAccountSensitiveSettings.whitelistedUnfurlingSites = settings + if (!settings) { + settings = {} } + + if (settings[address] === this.checked) { + return + } + + settings[address] = this.checked + localAccountSensitiveSettings.whitelistedUnfurlingSites = settings } - ] - sensor.onClicked: { - siteSwitch.checked = !siteSwitch.checked } + ] + sensor.onClicked: { + siteSwitch.checked = !siteSwitch.checked } } } - } // Site Column - - Separator { - id: separator3 - visible: siteColumn.visible } + } // Site Column - // SYNC WAKU SECTION - StatusSectionHeadline { - text: qsTr("Message syncing") - width: parent.width - } + Separator { + id: separator3 + visible: siteColumn.visible + Layout.fillWidth: true + } - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - title: qsTr("Waku nodes") - label: root.messagingStore.getMailserverNameForNodeAddress(root.messagingStore.activeMailserver) - components: [ - StatusIcon { - icon: "chevron-down" - rotation: 270 - color: Theme.palette.baseColor1 - } - ] - sensor.onClicked: Global.openPopup(wakuNodeModalComponent) - } + // SYNC WAKU SECTION + StatusSectionHeadline { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("Message syncing") + } - Component { - id: wakuNodeModalComponent - WakuNodesModal { - messagingStore: root.messagingStore + StatusListItem { + Layout.fillWidth: true + title: qsTr("Waku nodes") + label: root.messagingStore.getMailserverNameForNodeAddress(root.messagingStore.activeMailserver) + components: [ + StatusIcon { + icon: "chevron-down" + rotation: 270 + color: Theme.palette.baseColor1 } - } + ] + sensor.onClicked: Global.openPopup(wakuNodeModalComponent) + } - StatusSectionHeadline { - text: qsTr("For security reasons, private chat history won't be synced.") - width: parent.width + Component { + id: wakuNodeModalComponent + WakuNodesModal { + messagingStore: root.messagingStore } - - } // Column - } // Item -} // ScrollView + } + + StatusSectionHeadline { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + text: qsTr("For security reasons, private chat history won't be synced.") + } + } +} diff --git a/ui/app/AppLayouts/Profile/views/MyProfileView.qml b/ui/app/AppLayouts/Profile/views/MyProfileView.qml index 964f76c4a3..bc7895068b 100644 --- a/ui/app/AppLayouts/Profile/views/MyProfileView.qml +++ b/ui/app/AppLayouts/Profile/views/MyProfileView.qml @@ -1,7 +1,6 @@ import QtQuick 2.13 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.13 -import QtGraphicalEffects 1.13 import utils 1.0 import shared 1.0 @@ -17,170 +16,168 @@ import StatusQ.Core.Theme 0.1 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 -ColumnLayout { +SettingsContentBase { id: root property ProfileStore profileStore - property int profileContentWidth - clip: true - spacing: 16 + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth - Item { - Layout.preferredHeight: 32 - Layout.fillWidth: true - } + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding - RowLayout { - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: root.profileContentWidth + StatusBaseText { + id: profileName + text: root.profileStore.name + font.weight: Font.Bold + font.pixelSize: 20 + color: Theme.palette.directColor1 + } - StatusBaseText { - id: profileName + StatusButton { + text: "Edit" + onClicked: Global.openPopup(displayNamePopupComponent) + } - text: root.profileStore.name - font.weight: Font.Bold - font.pixelSize: 20 - color: Theme.palette.directColor1 + Item { + Layout.fillWidth: true + } + + StatusFlatRoundButton { + id: qrCodeButton + + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 + + icon.name: "qr" + type: StatusFlatRoundButton.Type.Quaternary + onClicked: qrCodePopup.open() + } } - StatusButton { - text: "Edit" - onClicked: Global.openPopup(displayNamePopupComponent) - } - - Item { + Separator { Layout.fillWidth: true } - StatusFlatRoundButton { - id: qrCodeButton + ProfileHeader { + id: profileImgNameContainer - Layout.preferredWidth: 32 - Layout.preferredHeight: 32 + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding - icon.name: "qr" - type: StatusFlatRoundButton.Type.Quaternary - onClicked: qrCodePopup.open() - } - } + displayName: profileStore.name + pubkey: profileStore.pubkey + icon: profileStore.icon - Separator { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: root.profileContentWidth - } + displayNameVisible: false + pubkeyVisible: false - ProfileHeader { - id: profileImgNameContainer + imageWidth: 80 + imageHeight: 80 + emojiSize: Qt.size(20,20) + supersampling: true - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: root.profileContentWidth + imageOverlay: Item { + StatusFlatRoundButton { + width: 24 + height: 24 - displayName: profileStore.name - pubkey: profileStore.pubkey - icon: profileStore.icon + anchors { + right: parent.right + bottom: parent.bottom + rightMargin: -8 + } - displayNameVisible: false - pubkeyVisible: false + type: StatusFlatRoundButton.Type.Secondary + icon.name: "pencil" + icon.color: Theme.palette.directColor1 + icon.width: 12.5 + icon.height: 12.5 - imageWidth: 80 - imageHeight: 80 - emojiSize: Qt.size(20,20) - supersampling: true - - imageOverlay: Item { - StatusFlatRoundButton { - width: 24 - height: 24 - - anchors { - right: parent.right - bottom: parent.bottom - rightMargin: -8 + onClicked: Global.openChangeProfilePicPopup() } + } + } - type: StatusFlatRoundButton.Type.Secondary - icon.name: "pencil" - icon.color: Theme.palette.directColor1 - icon.width: 12.5 - icon.height: 12.5 + StatusDescriptionListItem { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding - onClicked: Global.openChangeProfilePicPopup() + title: qsTr("ENS username") + subTitle: root.profileStore.ensName + tooltip.text: qsTr("Copy to clipboard") + icon.name: "copy" + visible: !!root.profileStore.ensName + iconButton.onClicked: { + root.profileStore.copyToClipboard(root.profileStore.ensName) + tooltip.visible = !tooltip.visible + } + } + + StatusDescriptionListItem { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + + title: qsTr("Chat key") + subTitle: Utils.getCompressedPk(root.profileStore.pubkey) + subTitleComponent.elide: Text.ElideMiddle + subTitleComponent.width: 320 + subTitleComponent.font.family: Theme.palette.monoFont.name + tooltip.text: qsTr("Copy to clipboard") + icon.name: "copy" + iconButton.onClicked: { + root.profileStore.copyToClipboard(subTitle) + tooltip.visible = !tooltip.visible + } + } + + StatusDescriptionListItem { + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + + title: qsTr("Share Profile URL") + subTitle: `${Constants.userLinkPrefix}${root.profileStore.ensName !== "" ? root.profileStore.ensName : (root.profileStore.pubkey.substring(0, 5) + "..." + root.profileStore.pubkey.substring(root.profileStore.pubkey.length - 5))}` + tooltip.text: qsTr("Copy to clipboard") + icon.name: "copy" + iconButton.onClicked: { + root.profileStore.copyToClipboard(Constants.userLinkPrefix + (root.profileStore.ensName !== "" ? root.profileStore.ensName : root.profileStore.pubkey)) + tooltip.visible = !tooltip.visible + } + } + + Component { + id: displayNamePopupComponent + DisplayNamePopup { + profileStore: root.profileStore + anchors.centerIn: Overlay.overlay + onClosed: { destroy() } + } + } + + ModalPopup { + id: qrCodePopup + width: 420 + height: 420 + Image { + asynchronous: true + fillMode: Image.PreserveAspectFit + source: root.profileStore.getQrCodeSource(root.profileStore.pubkey) + anchors.verticalCenterOffset: 20 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + height: 312 + width: 312 + mipmap: true + smooth: false } } } - - StatusDescriptionListItem { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: root.profileContentWidth - - title: qsTr("ENS username") - subTitle: root.profileStore.ensName - tooltip.text: qsTr("Copy to clipboard") - icon.name: "copy" - visible: !!root.profileStore.ensName - iconButton.onClicked: { - root.profileStore.copyToClipboard(root.profileStore.ensName) - tooltip.visible = !tooltip.visible - } - } - - StatusDescriptionListItem { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: root.profileContentWidth - - title: qsTr("Chat key") - subTitle: Utils.getCompressedPk(root.profileStore.pubkey) - subTitleComponent.elide: Text.ElideMiddle - subTitleComponent.width: 320 - subTitleComponent.font.family: Theme.palette.monoFont.name - tooltip.text: qsTr("Copy to clipboard") - icon.name: "copy" - iconButton.onClicked: { - root.profileStore.copyToClipboard(subTitle) - tooltip.visible = !tooltip.visible - } - } - - StatusDescriptionListItem { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: root.profileContentWidth - - title: qsTr("Share Profile URL") - subTitle: `${Constants.userLinkPrefix}${root.profileStore.ensName !== "" ? root.profileStore.ensName : (root.profileStore.pubkey.substring(0, 5) + "..." + root.profileStore.pubkey.substring(root.profileStore.pubkey.length - 5))}` - tooltip.text: qsTr("Copy to clipboard") - icon.name: "copy" - iconButton.onClicked: { - root.profileStore.copyToClipboard(Constants.userLinkPrefix + (root.profileStore.ensName !== "" ? root.profileStore.ensName : root.profileStore.pubkey)) - tooltip.visible = !tooltip.visible - } - } - - Component { - id: displayNamePopupComponent - DisplayNamePopup { - profileStore: root.profileStore - anchors.centerIn: Overlay.overlay - onClosed: { destroy() } - } - } - - ModalPopup { - id: qrCodePopup - width: 420 - height: 420 - Image { - asynchronous: true - fillMode: Image.PreserveAspectFit - source: root.profileStore.getQrCodeSource(root.profileStore.pubkey) - anchors.verticalCenterOffset: 20 - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - height: 312 - width: 312 - mipmap: true - smooth: false - } - } } - diff --git a/ui/app/AppLayouts/Profile/views/NotificationsView.qml b/ui/app/AppLayouts/Profile/views/NotificationsView.qml index 3ae5a08fec..e7ef40cf58 100644 --- a/ui/app/AppLayouts/Profile/views/NotificationsView.qml +++ b/ui/app/AppLayouts/Profile/views/NotificationsView.qml @@ -17,22 +17,14 @@ import "../popups" import "../panels" import "./" -ScrollView { +SettingsContentBase { id: root property NotificationsStore notificationsStore - property int profileContentWidth - - height: parent.height - width: parent.width - contentHeight: notificationsContainer.height - clip: true - Item { id: notificationsContainer - width: profileContentWidth - anchors.horizontalCenter: parent.horizontalCenter + width: root.contentWidth height: this.childrenRect.height + 100 property Component mutedChatsModalComponent: MutedChatsModal {} @@ -50,23 +42,15 @@ ScrollView { id: messageSetting } - StatusSectionHeadline { - id: sectionHeadlineNotifications - //% "Notification preferences" - text: qsTrId("notifications-preferences") - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - } - Column { id: column - anchors.top: sectionHeadlineNotifications.bottom - anchors.topMargin: Style.current.smallPadding + anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right RadioButtonSelector { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "All messages" title: qsTrId("all-messages") buttonGroup: notificationSetting @@ -79,6 +63,8 @@ ScrollView { } RadioButtonSelector { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Just @mentions" title: qsTrId("just--mentions") buttonGroup: notificationSetting @@ -91,6 +77,8 @@ ScrollView { } RadioButtonSelector { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Nothing" title: qsTrId("nothing") buttonGroup: notificationSetting @@ -108,9 +96,7 @@ ScrollView { anchors.top: column.bottom anchors.topMargin: Style.current.bigPadding anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right - anchors.rightMargin: -Style.current.padding } StatusSectionHeadline { @@ -120,6 +106,8 @@ ScrollView { anchors.top: separator.bottom anchors.left: parent.left anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } Column { @@ -132,6 +120,8 @@ ScrollView { // TODO: replace with StatusListItem StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Play a sound when receiving a notification" text: qsTrId("play-a-sound-when-receiving-a-notification") isSwitch: true @@ -143,6 +133,8 @@ ScrollView { // TODO: replace with StatusListItem StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Use your operating system's notifications" text: qsTrId("use-your-operating-system-s-notifications") isSwitch: true @@ -181,12 +173,13 @@ ScrollView { font.pixelSize: 15 anchors.left: parent.left anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding color: Theme.palette.directColor1 } Column { anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right spacing: 10 @@ -241,6 +234,8 @@ ScrollView { text: qsTrId("no-preview-or-advanced--go-to-notification-center") font.pixelSize: 15 anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding color: Theme.palette.directColor1 } } @@ -250,9 +245,7 @@ ScrollView { anchors.top: column3.bottom anchors.topMargin: Style.current.bigPadding anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right - anchors.rightMargin: -Style.current.padding } StatusSectionHeadline { @@ -262,6 +255,8 @@ ScrollView { anchors.top: separator2.bottom anchors.left: parent.left anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding } Column { @@ -274,6 +269,8 @@ ScrollView { // TODO: replace with StatusListItem StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Notify on new requests" text: qsTrId("notify-on-new-requests") isSwitch: true @@ -285,6 +282,8 @@ ScrollView { // TODO: replace with StatusListItem StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Muted users" text: qsTrId("muted-users") currentValue: root.notificationsStore.mutedContactsModel.count > 0 ? @@ -309,6 +308,8 @@ ScrollView { // TODO: replace with StatusListItem StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 //% "Muted chats" text: qsTrId("muted-chats") currentValue: root.notificationsStore.mutedChatsModel.count > 0 ? @@ -350,9 +351,7 @@ ScrollView { anchors.top: column4.bottom anchors.topMargin: Style.current.bigPadding anchors.left: parent.left - anchors.leftMargin: -Style.current.padding anchors.right: parent.right - anchors.rightMargin: -Style.current.padding } Column { @@ -365,6 +364,10 @@ ScrollView { width: parent.width StatusBaseText { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding //% "Reset notification settings" text: qsTrId("reset-notification-settings") font.pixelSize: 15 @@ -389,6 +392,10 @@ ScrollView { } StatusBaseText { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding //% "Restore default notification settings and unmute all chats and users" text: qsTrId("restore-default-notification-settings-and-unmute-all-chats-and-users") font.pixelSize: 15 diff --git a/ui/app/AppLayouts/Profile/views/PrivacyView.qml b/ui/app/AppLayouts/Profile/views/PrivacyView.qml index 6975adb938..384826ad4f 100644 --- a/ui/app/AppLayouts/Profile/views/PrivacyView.qml +++ b/ui/app/AppLayouts/Profile/views/PrivacyView.qml @@ -15,37 +15,18 @@ import StatusQ.Controls 0.1 as StatusQControls import "../popups" import "../stores" -Item { +SettingsContentBase { id: root - Layout.fillHeight: true - Layout.fillWidth: true - clip: true property PrivacyStore privacyStore - property int profileContentWidth - - Column { - id: containerColumn - anchors.top: parent.top - anchors.topMargin: 64 - width: profileContentWidth - - anchors.horizontalCenter: parent.horizontalCenter - - StatusSectionHeadline { - id: labelSecurity - //% "Security" - text: qsTrId("security") - bottomPadding: Style.current.halfPadding - } + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth StatusListItem { id: backupSeedPhrase - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + Layout.fillWidth: true //% "Backup Seed Phrase" title: qsTrId("backup-seed-phrase") enabled: !root.privacyStore.mnemonicBackedUp @@ -66,10 +47,7 @@ Item { } StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + Layout.fillWidth: true title: qsTr("Change password") implicitHeight: 52 components: [ @@ -83,10 +61,7 @@ Item { } StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding + Layout.fillWidth: true title: qsTr("Store pass to Keychain") implicitHeight: 52 visible: Qt.platform.os == "osx" // For now, this is available only on MacOS diff --git a/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml b/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml new file mode 100644 index 0000000000..af7c7a3dbb --- /dev/null +++ b/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml @@ -0,0 +1,98 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.13 + +import utils 1.0 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +Item { + id: root + + property string sectionTitle + property int contentWidth + + property string backButtonName: "" + + property alias titleRowComponentLoader: loader + property list headerComponents + default property Item content + + signal backButtonClicked() + + QtObject { + id: d + + readonly property int topHeaderHeight: 56 + readonly property int titleRowHeight: 56 + } + + Component.onCompleted: { + content.parent = contentWrapper + + if (headerComponents.length) { + for (let i in headerComponents) { + headerComponents[i].parent = titleRow + } + } + } + + Item { + id: topHeader + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: -Style.current.padding + width: root.contentWidth + Style.current.padding + height: d.topHeaderHeight + + StatusFlatButton { + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: Style.current.halfPadding + visible: root.backButtonName != "" + icon.name: "arrow-left" + icon.width: 20 + icon.height: 20 + text: root.backButtonName + size: StatusBaseButton.Size.Large + onClicked: root.backButtonClicked() + } + } + + RowLayout { + id: titleRow + anchors.left: parent.left + anchors.top: topHeader.bottom + anchors.leftMargin: Style.current.padding + width: root.contentWidth - 2 * Style.current.padding + height: d.titleRowHeight + visible: root.sectionTitle !== "" + + StatusBaseText { + Layout.fillWidth: true + text: root.sectionTitle + font.weight: Font.Bold + font.pixelSize: Constants.settingsSection.mainHeaderFontSize + color: Theme.palette.directColor1 + } + + Loader { + id: loader + } + } + + ScrollView { + anchors.top: titleRow.visible? titleRow.bottom : topHeader.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Style.current.bigPadding + clip: true + + Column { + id: contentWrapper + } + } +} diff --git a/ui/app/AppLayouts/Profile/views/SoundsView.qml b/ui/app/AppLayouts/Profile/views/SoundsView.qml index b7ff72792b..ddf0a5e04f 100644 --- a/ui/app/AppLayouts/Profile/views/SoundsView.qml +++ b/ui/app/AppLayouts/Profile/views/SoundsView.qml @@ -8,27 +8,18 @@ import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Controls 0.1 -Item { - id: soundsContainer +SettingsContentBase { + id: root - property var store - property int profileContentWidth - - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - - Item { - width: profileContentWidth - - anchors.horizontalCenter: parent.horizontalCenter + ColumnLayout { + spacing: Constants.settingsSection.itemSpacing + width: root.contentWidth StatusBaseText { id: labelVolume - anchors.top: parent.top - anchors.topMargin: 24 - anchors.left: parent.left - anchors.leftMargin: 24 + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding //% "Sound volume" text: qsTrId("sound-volume") + " " + volume.value.toPrecision(1) font.pixelSize: 15 @@ -37,10 +28,9 @@ Item { StatusSlider { id: volume - anchors.top: labelVolume.bottom - anchors.topMargin: Style.current.padding - anchors.left: parent.left - anchors.leftMargin: 24 + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding from: 0.0 to: 1.0 stepSize: 0.1 diff --git a/ui/app/AppLayouts/Profile/views/WalletView.qml b/ui/app/AppLayouts/Profile/views/WalletView.qml index 3fe1f4932a..e9e9045b28 100644 --- a/ui/app/AppLayouts/Profile/views/WalletView.qml +++ b/ui/app/AppLayouts/Profile/views/WalletView.qml @@ -17,82 +17,96 @@ import "../popups" import "../panels" import "./wallet" -Item { +SettingsContentBase { id: root property var emojiPopup property WalletStore walletStore - anchors.fill: parent - readonly property int mainViewIndex: 0; readonly property int networksViewIndex: 1; readonly property int accountViewIndex: 2; readonly property int dappPermissionViewIndex: 3; - StatusBanner { - id: banner - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - visible: walletStore.areTestNetworksEnabled - type: StatusBanner.Type.Danger - statusText: qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.") + onBackButtonClicked: { + stackContainer.currentIndex = mainViewIndex } - ScrollView { + StackLayout { + id: stackContainer - anchors.top: banner.visible ? banner.bottom: parent.top - anchors.bottom: parent.bottom - clip: true + width: root.contentWidth + currentIndex: mainViewIndex - StackLayout { - id: stackContainer + onCurrentIndexChanged: { + root.backButtonName = "" + root.sectionTitle = qsTr("Wallet") + root.titleRowComponentLoader.sourceComponent = undefined - anchors.fill: parent - currentIndex: mainViewIndex + if(currentIndex == root.networksViewIndex) { + root.backButtonName = qsTr("Wallet") + root.sectionTitle = qsTr("Networks") - MainView { - id: main - Layout.preferredWidth: 560 - leftPadding: 64 - topPadding: 64 - walletStore: root.walletStore + root.titleRowComponentLoader.sourceComponent = testnetModeSwitchComponent + } + else if(currentIndex == root.accountViewIndex) { + root.backButtonName = qsTr("Wallet") + root.sectionTitle = "" + } + else if(currentIndex == root.dappPermissionViewIndex) { + root.backButtonName = qsTr("Wallet") + root.sectionTitle = qsTr("DApp Permissions") + } + } - onGoToNetworksView: { - stackContainer.currentIndex = networksViewIndex - } + MainView { + id: main - onGoToAccountView: { - root.walletStore.switchAccountByAddress(address) - stackContainer.currentIndex = accountViewIndex - } + Layout.fillWidth: true - onGoToDappPermissionsView: { - stackContainer.currentIndex = dappPermissionViewIndex - } + walletStore: root.walletStore + + onGoToNetworksView: { + stackContainer.currentIndex = networksViewIndex } - NetworksView { - walletStore: root.walletStore - onGoBack: { - stackContainer.currentIndex = mainViewIndex - } + onGoToAccountView: { + root.walletStore.switchAccountByAddress(address) + stackContainer.currentIndex = accountViewIndex } - AccountView { - walletStore: root.walletStore - emojiPopup: root.emojiPopup - onGoBack: { - stackContainer.currentIndex = mainViewIndex - } + onGoToDappPermissionsView: { + stackContainer.currentIndex = dappPermissionViewIndex } + } - DappPermissionsView { - walletStore: root.walletStore - onGoBack: { - stackContainer.currentIndex = mainViewIndex - } + NetworksView { + walletStore: root.walletStore + + onGoBack: { + stackContainer.currentIndex = mainViewIndex + } + } + + AccountView { + walletStore: root.walletStore + emojiPopup: root.emojiPopup + + onGoBack: { + stackContainer.currentIndex = mainViewIndex + } + } + + DappPermissionsView { + walletStore: root.walletStore + } + + Component { + id: testnetModeSwitchComponent + StatusSwitch { + text: qsTr("Testnet Mode") + checked: walletStore.areTestNetworksEnabled + onClicked: walletStore.toggleTestNetworksEnabled() } } } diff --git a/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml b/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml index 2c884b607e..e46599e3de 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml @@ -19,28 +19,13 @@ Item { property WalletStore walletStore property var emojiPopup - StatusFlatButton { - id: backButton - anchors.top: parent.top - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: Style.current.bigPadding - icon.name: "arrow-left" - icon.height: 13.5 - icon.width: 17.5 - text: qsTr("Wallet") - onClicked: { - root.goBack() - } - } - Column { id: column - anchors.topMargin: Style.current.xlPadding - anchors.top: backButton.bottom - anchors.leftMargin: Style.current.xlPadding * 2 - anchors.left: root.left - width: 560 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding Row { id: header diff --git a/ui/app/AppLayouts/Profile/views/wallet/DappPermissionsView.qml b/ui/app/AppLayouts/Profile/views/wallet/DappPermissionsView.qml index a3f6edfd25..74a84edf38 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/DappPermissionsView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/DappPermissionsView.qml @@ -12,46 +12,15 @@ import "../../controls" Item { id: root - signal goBack property WalletStore walletStore - StatusFlatButton { - id: backButton - anchors.top: parent.top - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: Style.current.bigPadding - icon.name: "arrow-left" - icon.height: 13.5 - icon.width: 17.5 - text: qsTr("Wallet") - onClicked: { - root.goBack() - } - } - Column { id: column - anchors.topMargin: Style.current.xlPadding - anchors.top: backButton.bottom - anchors.leftMargin: Style.current.xlPadding * 2 - anchors.left: root.left - width: 560 + anchors.top: parent.top + anchors.left: parent.left + width: parent.width - StatusBaseText { - id: titleText - text: qsTr("DApp Permissions") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 - } - - - Item { - height: Style.current.bigPadding - width: parent.width - } PermissionsListView { id: permissionsList width: parent.width diff --git a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml index a0a9a92c4a..aee0e54403 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml @@ -20,19 +20,6 @@ Column { signal goToAccountView(address: string) signal goToDappPermissionsView() - StatusBaseText { - id: titleText - text: qsTr("Wallet") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 - } - - Item { - height: Style.current.bigPadding - width: parent.width - } - StatusListItem { title: qsTr("Manage Assets & List") height: 64 diff --git a/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml b/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml index a5d73a6427..c3633bbb53 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml @@ -15,52 +15,11 @@ Item { property WalletStore walletStore - StatusFlatButton { - id: backButton - anchors.top: parent.top - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.leftMargin: Style.current.bigPadding - icon.name: "arrow-left" - icon.height: 13.5 - icon.width: 17.5 - text: qsTr("Wallet") - onClicked: { - root.goBack() - } - } - Column { id: column - anchors.topMargin: Style.current.xlPadding - anchors.top: backButton.bottom - anchors.leftMargin: Style.current.xlPadding * 2 - anchors.left: root.left - width: 560 - - Row { - spacing: 200 - StatusBaseText { - id: titleText - text: qsTr("Networks") - font.weight: Font.Bold - font.pixelSize: 28 - color: Theme.palette.directColor1 - } - - StatusSwitch { - text: qsTr("Testnet Mode") - checked: walletStore.areTestNetworksEnabled - onClicked: walletStore.toggleTestNetworksEnabled() - } - - } - - - Item { - height: Style.current.bigPadding - width: parent.width - } + anchors.top: parent.top + anchors.left: parent.left + width: parent.width Repeater { id: layer1List @@ -71,6 +30,8 @@ Item { } StatusSectionHeadline { + leftPadding: Style.current.padding + rightPadding: Style.current.padding text: qsTr("Layer 2") topPadding: Style.current.bigPadding bottomPadding: Style.current.padding diff --git a/ui/app/AppLayouts/Profile/views/wallet/PermissionsListView.qml b/ui/app/AppLayouts/Profile/views/wallet/PermissionsListView.qml index c59ac649e7..e45b6d537e 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/PermissionsListView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/PermissionsListView.qml @@ -13,8 +13,6 @@ Column { property WalletStore walletStore - width: 560 - Repeater { id: permissionsList model: walletStore.dappList diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 2f681206e4..083e494256 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -149,6 +149,11 @@ QtObject { ] } + readonly property QtObject settingsSection: QtObject { + readonly property int itemSpacing: 10 + readonly property int mainHeaderFontSize: 28 + } + readonly property int communityImported: 0 readonly property int communityImportingInProgress: 1 readonly property int communityImportingError: 2