From e1b7475884a8d1f8ab3c39a3e05abaf03821129b Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Mon, 7 Mar 2022 15:34:59 -0500 Subject: [PATCH] feat(profile): add Messaging settings view in profile Fixes #4931 --- ui/StatusQ | 2 +- ui/app/AppLayouts/Profile/ProfileLayout.qml | 11 +- .../AppLayouts/Profile/panels/MenuPanel.qml | 17 +- .../Profile/popups/AddWakuNodeModal.qml | 82 ++++ .../Profile/popups/ChatLinksPreviewModal.qml | 225 --------- .../Profile/popups/OpenLinksWithModal.qml | 62 --- .../Profile/popups/WakuNodesModal.qml | 146 ++++++ .../Profile/stores/MessagingStore.qml | 54 +++ .../Profile/stores/PrivacyStore.qml | 9 - .../Profile/stores/ProfileSectionStore.qml | 20 +- .../AppLayouts/Profile/stores/SyncStore.qml | 30 -- .../AppLayouts/Profile/views/ContactsView.qml | 32 +- .../AppLayouts/Profile/views/LeftTabView.qml | 3 +- .../Profile/views/MessagingView.qml | 444 ++++++++++++++++++ .../AppLayouts/Profile/views/PrivacyView.qml | 163 ------- ui/app/AppLayouts/Profile/views/SyncView.qml | 194 -------- ui/app/AppLayouts/stores/RootStore.qml | 1 + ui/app/mainui/AppMain.qml | 2 +- ui/imports/assets/images/profile/chat.svg | 3 + .../shared/controls/SettingsRadioButton.qml | 40 ++ ui/imports/shared/controls/qmldir | 1 + .../shared/views/chat/LinksMessageView.qml | 2 +- ui/imports/utils/Constants.qml | 20 +- 23 files changed, 845 insertions(+), 718 deletions(-) create mode 100644 ui/app/AppLayouts/Profile/popups/AddWakuNodeModal.qml delete mode 100644 ui/app/AppLayouts/Profile/popups/ChatLinksPreviewModal.qml delete mode 100644 ui/app/AppLayouts/Profile/popups/OpenLinksWithModal.qml create mode 100644 ui/app/AppLayouts/Profile/popups/WakuNodesModal.qml create mode 100644 ui/app/AppLayouts/Profile/stores/MessagingStore.qml delete mode 100644 ui/app/AppLayouts/Profile/stores/SyncStore.qml create mode 100644 ui/app/AppLayouts/Profile/views/MessagingView.qml delete mode 100644 ui/app/AppLayouts/Profile/views/SyncView.qml create mode 100644 ui/imports/assets/images/profile/chat.svg create mode 100644 ui/imports/shared/controls/SettingsRadioButton.qml diff --git a/ui/StatusQ b/ui/StatusQ index 082bb8ef45..8af7028efc 160000 --- a/ui/StatusQ +++ b/ui/StatusQ @@ -1 +1 @@ -Subproject commit 082bb8ef45b64986be1bf67f4d7c95b1a8ea1440 +Subproject commit 8af7028efc61725e415518bea21a3c4de4bb6481 diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml index b883634e6d..80bfbe3bd4 100644 --- a/ui/app/AppLayouts/Profile/ProfileLayout.qml +++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml @@ -69,6 +69,12 @@ StatusAppTwoPanelLayout { profileContentWidth: _internal.profileContentWidth } + MessagingView { + id: messagingView + messagingStore: profileView.store.messagingStore + profileContentWidth: _internal.profileContentWidth + } + WalletView { id: walletView walletStore: profileView.store.walletStore @@ -100,11 +106,6 @@ StatusAppTwoPanelLayout { profileContentWidth: _internal.profileContentWidth } - SyncView { - syncStore: profileView.store.syncStore - profileContentWidth: _internal.profileContentWidth - } - DevicesView { devicesStore: profileView.store.devicesStore } diff --git a/ui/app/AppLayouts/Profile/panels/MenuPanel.qml b/ui/app/AppLayouts/Profile/panels/MenuPanel.qml index 2015cdbea7..62262fb50f 100644 --- a/ui/app/AppLayouts/Profile/panels/MenuPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/MenuPanel.qml @@ -11,13 +11,14 @@ Column { spacing: 4 property var privacyStore + property var messagingStore property alias mainMenuItems: mainMenuItems.model property alias settingsMenuItems: settingsMenuItems.model property alias extraMenuItems: extraMenuItems.model property alias appsMenuItems: appsMenuItems.model property bool browserMenuItemEnabled: false - property bool appsMenuItemsEnabled: false + property bool walletMenuItemEnabled: false signal menuItemClicked(var menu_item) @@ -34,7 +35,6 @@ Column { StatusListSectionHeadline { text: qsTr("Apps") - visible: root.appsMenuItemsEnabled || root.browserMenuItemEnabled } Repeater { @@ -47,8 +47,9 @@ Column { selected: Global.settingsSubsection === model.subsection onClicked: root.menuItemClicked(model) visible: { + (model.subsection !== Constants.settingsSubsection.browserSettings && model.subsection !== Constants.settingsSubsection.wallet) || (model.subsection === Constants.settingsSubsection.browserSettings && root.browserMenuItemEnabled) || - (model.subsection === Constants.settingsSubsection.wallet && root.appsMenuItemsEnabled) + (model.subsection === Constants.settingsSubsection.wallet && root.walletMenuItemEnabled) } } } @@ -65,7 +66,15 @@ Column { selected: Global.settingsSubsection === model.subsection onClicked: root.menuItemClicked(model) visible: model.subsection !== Constants.settingsSubsection.browserSettings || root.browserMenuItemEnabled - badge.value: !root.privacyStore.mnemonicBackedUp && settingsMenuDelegate.title === qsTr("Privacy and security") + badge.value: { + switch (model.subsection) { + case Constants.settingsSubsection.privacyAndSecurity: + return !root.privacyStore.mnemonicBackedUp + case Constants.settingsSubsection.messaging: + return root.messagingStore.contactRequestsModel.count + default: return "" + } + } } } diff --git a/ui/app/AppLayouts/Profile/popups/AddWakuNodeModal.qml b/ui/app/AppLayouts/Profile/popups/AddWakuNodeModal.qml new file mode 100644 index 0000000000..20cbbf41a1 --- /dev/null +++ b/ui/app/AppLayouts/Profile/popups/AddWakuNodeModal.qml @@ -0,0 +1,82 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.3 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Controls.Validators 0.1 +import StatusQ.Popups 0.1 + + +import utils 1.0 + +StatusModal { + id: popup + + anchors.centerIn: parent + height: 560 + header.title: qsTr("Waku nodes") + + property var messagingStore + + onClosed: { + destroy() + } + + onOpened: { + nameInput.text = ""; + enodeInput.text = ""; + } + + contentItem: Item { + width: parent.width + height: parent.height + + StatusInput { + id: nameInput + //% "Name" + label: qsTrId("name") + //% "Specify a name" + input.placeholderText: qsTrId("specify-name") + validators: [StatusMinLengthValidator { + minLength: 1 + //% "You need to enter a name" + errorMessage: qsTrId("you-need-to-enter-a-name") + }] + validationMode: StatusInput.ValidationMode.OnlyWhenDirty + } + + StatusInput { + id: enodeInput + //% "History node address" + label: qsTrId("history-node-address") + input.placeholderText: "enode://{enode-id}:{password}@{ip-address}:{port-number}" + validators: [StatusMinLengthValidator { + minLength: 1 + //% "You need to enter the enode address" + errorMessage: qsTrId("you-need-to-enter-the-enode-address") + }, + StatusRegularExpressionValidator { + errorMessage: qsTr("The format must be: enode://{enode-id}:{password}@{ip-address}:{port}") + regularExpression: /enode:\/\/[a-z0-9]+:[a-z0-9]+@(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}:[0-9]+/ + }] + validationMode: StatusInput.ValidationMode.OnlyWhenDirty + anchors.top: nameInput.bottom + anchors.topMargin: Style.current.bigPadding + } + } + + rightButtons: [ + StatusButton { + //% "Save" + text: qsTrId("save") + enabled: nameInput.valid && enodeInput.valid + // enabled: nameInput.text !== "" && enodeInput.text !== "" + onClicked: { + root.messagingStore.saveNewMailserver(nameInput.text, enodeInput.text) + popup.close() + } + } + ] +} diff --git a/ui/app/AppLayouts/Profile/popups/ChatLinksPreviewModal.qml b/ui/app/AppLayouts/Profile/popups/ChatLinksPreviewModal.qml deleted file mode 100644 index d2bd2d1ea5..0000000000 --- a/ui/app/AppLayouts/Profile/popups/ChatLinksPreviewModal.qml +++ /dev/null @@ -1,225 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 - -import StatusQ.Controls 0.1 -import StatusQ.Core 0.1 -import StatusQ.Core.Theme 0.1 - -import shared.panels 1.0 -import shared.popups 1.0 -import shared.status 1.0 - -import utils 1.0 - -// TODO: replace with StatusModal -ModalPopup { - id: popup - - property var privacyStore - - //% "Chat link previews" - title: qsTrId("chat-link-previews") - - onClosed: { - destroy() - } - - function populatePreviewableSites() { - let whitelistAsString = popup.privacyStore.getLinkPreviewWhitelist() - if(whitelistAsString == "") - return - let whitelist = JSON.parse(whitelistAsString) - if (!localAccountSensitiveSettings.whitelistedUnfurlingSites) { - localAccountSensitiveSettings.whitelistedUnfurlingSites = {} - } - whitelist.forEach(entry => { - entry.isWhitelisted = localAccountSensitiveSettings.whitelistedUnfurlingSites[entry.address] || false - previewableSites.append(entry) - }) - } - - onOpened: { - populatePreviewableSites() - } - - Item { - anchors.fill: parent - - StatusSectionHeadline { - id: labelWebsites - //% "Websites" - text: qsTrId("websites") - width: parent.width - - StatusFlatButton { - //% "Enable all" - text: qsTrId("enable-all") - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - onClicked: { - const count = sitesListView.count - for (let i = 0; i < count; i++) { - sitesListView.itemAtIndex(i).toggleSetting(true) - } - } - } - } - - ListModel { - id: previewableSites - } - - Connections { - target: Global - onSettingsLoaded: { - popup.populatePreviewableSites() - } - } - - ScrollView { - width: parent.width - anchors.top: labelWebsites.bottom - anchors.topMargin: Style.current.bigPadding - anchors.bottom: infoText.top - anchors.bottomMargin: Style.current.bigPadding - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AlwaysOn - clip: true - - ListView { - id: sitesListView - anchors.fill: parent - anchors.rightMargin: Style.current.padding - model: previewableSites - spacing: 0 - - delegate: Component { - Rectangle { - property bool isHovered: false - id: linkRectangle - width: parent.width - height: 64 - color: isHovered ? Style.current.backgroundHover : Style.current.transparent - radius: Style.current.radius - border.width: 0 - - function toggleSetting(newState) { - if (newState !== undefined) { - settingSwitch.checked = newState - return - } - settingSwitch.checked = !settingSwitch.checked - } - - SVGImage { - id: thumbnail - width: 40 - height: 40 - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - - source: { - let filename; - switch (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}`) - } - - Rectangle { - width: parent.width - height: parent.height - radius: width / 2 - color: Style.current.transparent - border.color: Style.current.border - border.width: 1 - } - } - - StatusBaseText { - id: siteTitle - text: title - color: Theme.palette.directColor1 - font.pixelSize: 15 - font.weight: Font.Medium - anchors.top: thumbnail.top - anchors.left: thumbnail.right - anchors.leftMargin: Style.current.padding - } - - StatusBaseText { - text: address - font.pixelSize: 15 - font.weight: Font.Thin - color: Style.current.secondaryText - anchors.top: siteTitle.bottom - anchors.left: siteTitle.left - } - - StatusSwitch { - id: settingSwitch - checked: !!isWhitelisted - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Style.current.padding - onCheckedChanged: function () { - let settings = localAccountSensitiveSettings.whitelistedUnfurlingSites - - if (!settings) { - settings = {} - } - - if (settings[address] === this.checked) { - return - } - - settings[address] = this.checked - localAccountSensitiveSettings.whitelistedUnfurlingSites = settings - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: linkRectangle.isHovered = true - onExited: linkRectangle.isHovered = false - onClicked: toggleSetting() - } - } - } - } - } - - StatusBaseText { - id: infoText - //% "Previewing links from these websites may share your metadata with their owners." - text: qsTrId("previewing-links-from-these-websites-may-share-your-metadata-with-their-owners-") - width: parent.width - wrapMode: Text.WordWrap - font.weight: Font.Thin - color: Style.current.secondaryText - font.pixelSize: 15 - anchors.bottom: parent.bottom - } - } -} diff --git a/ui/app/AppLayouts/Profile/popups/OpenLinksWithModal.qml b/ui/app/AppLayouts/Profile/popups/OpenLinksWithModal.qml deleted file mode 100644 index b6b248c4fc..0000000000 --- a/ui/app/AppLayouts/Profile/popups/OpenLinksWithModal.qml +++ /dev/null @@ -1,62 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 - -import utils 1.0 - -import shared.popups 1.0 -import shared.controls 1.0 - -// TODO: replace with StatusModal -ModalPopup { - id: popup - - //% "Open links with..." - title: qsTrId("open-links-with---") - - onClosed: { - destroy() - } - - Column { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: Style.current.padding - anchors.leftMargin: Style.current.padding - - spacing: 0 - - ButtonGroup { - id: openLinksWithGroup - } - - RadioButtonSelector { - title: "Status" - buttonGroup: openLinksWithGroup - checked: localAccountSensitiveSettings.openLinksInStatus - onCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.openLinksInStatus = true - } - } - } - RadioButtonSelector { - //% "My default browser" - title: qsTrId("my-default-browser") - buttonGroup: openLinksWithGroup - checked: !localAccountSensitiveSettings.openLinksInStatus - onCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.openLinksInStatus = false - } - } - } - } -} - -/*##^## -Designer { - D{i:0;autoSize:true;height:480;width:640} -} -##^##*/ diff --git a/ui/app/AppLayouts/Profile/popups/WakuNodesModal.qml b/ui/app/AppLayouts/Profile/popups/WakuNodesModal.qml new file mode 100644 index 0000000000..a3b034d742 --- /dev/null +++ b/ui/app/AppLayouts/Profile/popups/WakuNodesModal.qml @@ -0,0 +1,146 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.3 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Controls.Validators 0.1 +import StatusQ.Popups 0.1 + +import "." + +import utils 1.0 +import shared 1.0 +import shared.panels 1.0 +import shared.popups 1.0 +import shared.status 1.0 +import shared.controls 1.0 + +StatusModal { + id: popup + + anchors.centerIn: parent + height: 560 + header.title: qsTr("Waku nodes") + + property var messagingStore + property string nameValidationError: "" + property string enodeValidationError: "" + + onClosed: { + destroy() + } + + contentItem: ScrollView { + height: parent.height + width: parent.width + contentHeight: nodesColumn.height + clip: true + + Column { + id: nodesColumn + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + + // TODO re-add that when the status-go API to disconnect from the mailserver is added + // StatusListItem { + // anchors.left: parent.left + // anchors.leftMargin: -Style.current.padding + // anchors.right: parent.right + // anchors.rightMargin: -Style.current.padding + // title: qsTr("Use Waku nodes") + // components: [ + // StatusSwitch { + // // TODO find what this is + // checked: true + // } + // ] + // } + + Separator { + anchors.left: parent.left + anchors.leftMargin: -Style.current.padding + anchors.right: parent.right + anchors.rightMargin: -Style.current.padding + } + + StatusListItem { + anchors.left: parent.left + anchors.leftMargin: -Style.current.padding + anchors.right: parent.right + anchors.rightMargin: -Style.current.padding + title: qsTr("Select node automatically") + components: [ + StatusSwitch { + id: automaticSelectionSwitch + checked: root.messagingStore.automaticMailserverSelection + onCheckedChanged: root.messagingStore.enableAutomaticMailserverSelection(checked) + } + ] + sensor.onClicked: { + automaticSelectionSwitch.checked = !automaticSelectionSwitch.checked + } + } + + StatusSectionHeadline { + text: qsTr("Waku Nodes") + visible: !automaticSelectionSwitch.checked + width: parent.width + height: visible ? implicitHeight : 0 + } + + ButtonGroup { + id: nodesButtonGroup + } + + Repeater { + id: mailServersListView + model: root.messagingStore.mailservers + delegate: Component { + StatusListItem { + title: qsTr("Node %1").arg(index) + subTitle: model.name + visible: !automaticSelectionSwitch.checked + height: visible ? implicitHeight : 0 + components: [ + StatusRadioButton { + id: nodeRadioBtn + ButtonGroup.group: nodesButtonGroup + checked: model.nodeAddress === root.messagingStore.activeMailserver + onCheckedChanged: { + if (checked) { + root.messagingStore.setActiveMailserver(model.nodeAddress) + } + } + } + ] + sensor.onClicked: { + nodeRadioBtn.checked = true + } + } + } + } + + StatusBaseText { + text: qsTr("Add a new node") + color: Theme.palette.primaryColor1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: Global.openPopup(wakuNodeModalComponent) + } + } + } + } + + Component { + id: wakuNodeModalComponent + AddWakuNodeModal { + messagingStore: root.messagingStore + } + } +} diff --git a/ui/app/AppLayouts/Profile/stores/MessagingStore.qml b/ui/app/AppLayouts/Profile/stores/MessagingStore.qml new file mode 100644 index 0000000000..b6eee58807 --- /dev/null +++ b/ui/app/AppLayouts/Profile/stores/MessagingStore.qml @@ -0,0 +1,54 @@ +import QtQuick 2.13 +import utils 1.0 + +QtObject { + id: root + + property var privacyModule + property var syncModule + + property int profilePicturesVisibility: privacyModule.profilePicturesVisibility + property int profilePicturesShowTo: privacyModule.profilePicturesShowTo + + // TODO move contact requests back to the contacts module since we need them in the Profile + // also, having them in the chat section creates some waste, since no community has it + property var chatSectionModule: mainModule.getChatSectionModule() + property var contactRequestsModel: chatSectionModule.contactRequestsModel + + property var mailservers: syncModule.model + + // Module Properties + property bool automaticMailserverSelection: syncModule.automaticSelection + property string activeMailserver: syncModule.activeMailserver + + function getMailserverNameForNodeAddress(nodeAddress) { + return root.syncModule.getMailserverNameForNodeAddress(nodeAddress) + } + + function setActiveMailserver(nodeAddress) { + root.syncModule.setActiveMailserver(nodeAddress) + } + + function saveNewMailserver(name, nodeAddress) { + root.syncModule.saveNewMailserver(name, nodeAddress) + } + + function enableAutomaticMailserverSelection(checked) { + if (automaticMailserverSelection === checked) { + return + } + root.syncModule.enableAutomaticSelection(checked) + } + + function getLinkPreviewWhitelist() { + return root.privacyModule.getLinkPreviewWhitelist() + } + + function setProfilePicturesVisibility(value) { + return root.privacyModule.setProfilePicturesVisibility(value) + } + + function setProfilePicturesShowTo(value) { + return root.privacyModule.setProfilePicturesShowTo(value) + } +} diff --git a/ui/app/AppLayouts/Profile/stores/PrivacyStore.qml b/ui/app/AppLayouts/Profile/stores/PrivacyStore.qml index 2f9cb495a0..e3c17320a8 100644 --- a/ui/app/AppLayouts/Profile/stores/PrivacyStore.qml +++ b/ui/app/AppLayouts/Profile/stores/PrivacyStore.qml @@ -8,11 +8,6 @@ QtObject { // Module Properties property bool mnemonicBackedUp: privacyModule.mnemonicBackedUp - property int profilePicturesVisibility: privacyModule.profilePicturesVisibility - - function getLinkPreviewWhitelist() { - return root.privacyModule.getLinkPreviewWhitelist() - } function changePassword(password, newPassword) { root.privacyModule.changePassword(password, newPassword) @@ -33,8 +28,4 @@ QtObject { function validatePassword(password) { return root.privacyModule.validatePassword(password) } - - function setProfilePicturesVisibility(value) { - return root.privacyModule.setProfilePicturesVisibility(value) - } } diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index c4dccdcc1b..71ea9c708f 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -16,12 +16,13 @@ QtObject { advancedModule: profileSectionModuleInst.advancedModule } - property DevicesStore devicesStore: DevicesStore { - devicesModule: profileSectionModuleInst.devicesModule + property MessagingStore messagingStore: MessagingStore { + privacyModule: profileSectionModuleInst.privacyModule + syncModule: profileSectionModuleInst.syncModule } - property SyncStore syncStore: SyncStore { - syncModule: profileSectionModuleInst.syncModule + property DevicesStore devicesStore: DevicesStore { + devicesModule: profileSectionModuleInst.devicesModule } property NotificationsStore notificationsStore: NotificationsStore { @@ -52,16 +53,13 @@ QtObject { property bool browserMenuItemEnabled: localAccountSensitiveSettings.isBrowserEnabled - property bool appsMenuItemsEnabled: localAccountSensitiveSettings.isWalletEnabled + property bool walletMenuItemEnabled: localAccountSensitiveSettings.isWalletEnabled property ListModel mainMenuItems: ListModel { Component.onCompleted: { append({subsection: Constants.settingsSubsection.profile, text: qsTr("Profile"), icon: "profile"}) - append({subsection: Constants.settingsSubsection.contacts, - text: qsTr("Contacts"), - icon: "contact"}) append({subsection: Constants.settingsSubsection.ensUsernames, text: qsTr("ENS usernames"), icon: "username"}) @@ -70,6 +68,9 @@ QtObject { property ListModel appsMenuItems: ListModel { Component.onCompleted: { + append({subsection: Constants.settingsSubsection.messaging, + text: qsTr("Messaging"), + icon: "chat"}) append({subsection: Constants.settingsSubsection.wallet, text: qsTr("Wallet"), icon: "wallet"}) @@ -96,9 +97,6 @@ QtObject { append({subsection: Constants.settingsSubsection.notifications, text: qsTr("Notifications"), icon: "notification"}) - append({subsection: Constants.settingsSubsection.syncSettings, - text: qsTr("Sync settings"), - icon: "mobile"}) append({subsection: Constants.settingsSubsection.devicesSettings, text: qsTr("Devices settings"), icon: "mobile"}) diff --git a/ui/app/AppLayouts/Profile/stores/SyncStore.qml b/ui/app/AppLayouts/Profile/stores/SyncStore.qml deleted file mode 100644 index ae468cc810..0000000000 --- a/ui/app/AppLayouts/Profile/stores/SyncStore.qml +++ /dev/null @@ -1,30 +0,0 @@ -import QtQuick 2.13 -import utils 1.0 - -QtObject { - id: root - - property var syncModule - - property var mailservers: syncModule.model - - // Module Properties - property bool automaticMailserverSelection: syncModule.automaticSelection - property string activeMailserver: syncModule.activeMailserver - - function getMailserverNameForNodeAddress(nodeAddress) { - return root.syncModule.getMailserverNameForNodeAddress(nodeAddress) - } - - function setActiveMailserver(nodeAddress) { - root.syncModule.setActiveMailserver(nodeAddress) - } - - function saveNewMailserver(name, nodeAddress) { - root.syncModule.saveNewMailserver(name, nodeAddress) - } - - function enableAutomaticMailserverSelection(checked) { - root.syncModule.enableAutomaticSelection(checked) - } -} diff --git a/ui/app/AppLayouts/Profile/views/ContactsView.qml b/ui/app/AppLayouts/Profile/views/ContactsView.qml index 53de47c400..5cdf4d29ab 100644 --- a/ui/app/AppLayouts/Profile/views/ContactsView.qml +++ b/ui/app/AppLayouts/Profile/views/ContactsView.qml @@ -8,6 +8,7 @@ import StatusQ.Core.Theme 0.1 import utils 1.0 +import StatusQ.Core 0.1 import shared.views 1.0 import shared.panels 1.0 import shared.popups 1.0 @@ -30,16 +31,39 @@ Item { clip: true Item { - anchors.top: parent.top - anchors.topMargin: 32 - anchors.bottom: parent.bottom + 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) + } + + StatusBaseText { + id: titleText + text: qsTr("Contacts") + font.weight: Font.Bold + font.pixelSize: 28 + color: Theme.palette.directColor1 + anchors.top: parent.top + anchors.topMargin: 56 + } + SearchBox { id: searchBox - anchors.top: parent.top + anchors.top: titleText.bottom + anchors.topMargin: 32 fontPixelSize: 15 } diff --git a/ui/app/AppLayouts/Profile/views/LeftTabView.qml b/ui/app/AppLayouts/Profile/views/LeftTabView.qml index 76e64c2fdf..b0395a1149 100644 --- a/ui/app/AppLayouts/Profile/views/LeftTabView.qml +++ b/ui/app/AppLayouts/Profile/views/LeftTabView.qml @@ -34,12 +34,13 @@ Item { MenuPanel { id: profileMenu privacyStore: store.privacyStore + messagingStore: store.messagingStore mainMenuItems: store.mainMenuItems settingsMenuItems: store.settingsMenuItems extraMenuItems: store.extraMenuItems appsMenuItems: store.appsMenuItems browserMenuItemEnabled: store.browserMenuItemEnabled - appsMenuItemsEnabled: store.appsMenuItemsEnabled + walletMenuItemEnabled: store.walletMenuItemEnabled onMenuItemClicked: { if (menu_item.subsection === Constants.settingsSubsection.signout) { diff --git a/ui/app/AppLayouts/Profile/views/MessagingView.qml b/ui/app/AppLayouts/Profile/views/MessagingView.qml new file mode 100644 index 0000000000..b3f12d16c7 --- /dev/null +++ b/ui/app/AppLayouts/Profile/views/MessagingView.qml @@ -0,0 +1,444 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import QtGraphicalEffects 1.13 + +import utils 1.0 +import shared 1.0 +import shared.panels 1.0 +import shared.popups 1.0 +import shared.status 1.0 +import shared.controls 1.0 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 + +import "../stores" +import "../controls" +import "../popups" +import "../panels" + +ScrollView { + 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 + + ButtonGroup { + id: showProfilePictureToGroup + } + + ButtonGroup { + id: seeProfilePicturesFromGroup + } + + ButtonGroup { + id: browserGroup + } + + Column { + id: generalColumn + spacing: 18 + anchors.top: parent.top + anchors.topMargin: 24 + anchors.left: parent.left + anchors.right: parent.right + + StatusBaseText { + id: titleText + text: qsTr("Messaging") + font.weight: Font.Bold + font.pixelSize: 28 + color: Theme.palette.directColor1 + } + + 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") + implicitHeight: 64 + 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 { + text: qsTr("Show My Profile Picture To") + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + + 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 + } + } + } + } + ] + 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() + } + + 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 + } + } + ] + sensor.onClicked: { + siteSwitch.checked = !siteSwitch.checked + } + } + } + } + } // Site Column + + Separator { + id: separator3 + visible: siteColumn.visible + } + + // SYNC WAKU SECTION + StatusSectionHeadline { + text: qsTr("Message syncing") + width: parent.width + } + + 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) + } + + Component { + id: wakuNodeModalComponent + WakuNodesModal { + messagingStore: root.messagingStore + } + } + + StatusSectionHeadline { + text: qsTr("For security reasons, private chat history won't be synced.") + width: parent.width + } + + } // Column + } // Item +} // ScrollView diff --git a/ui/app/AppLayouts/Profile/views/PrivacyView.qml b/ui/app/AppLayouts/Profile/views/PrivacyView.qml index 9f8bf42d47..6975adb938 100644 --- a/ui/app/AppLayouts/Profile/views/PrivacyView.qml +++ b/ui/app/AppLayouts/Profile/views/PrivacyView.qml @@ -129,168 +129,5 @@ Item { id: successPopup anchors.centerIn: parent } - - Item { - id: spacer1 - height: Style.current.bigPadding - width: parent.width - } - - Separator { - id: separator - } - - StatusSectionHeadline { - id: labelPrivacy - //% "Privacy" - text: qsTrId("privacy") - topPadding: Style.current.padding - bottomPadding: Style.current.halfPadding - } - - // TODO change this component from a switch to a chooser between, everyone, contacts and no one - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - //% "Display all profile pictures (not only contacts)" - title: qsTrId("display-all-profile-pictures--not-only-contacts-") - implicitHeight: 52 - components: [ - StatusQControls.StatusSwitch { - id: switch1 - checked: root.privacyStore.profilePicturesVisibility === - Constants.profilePicturesVisibility.everyone - onCheckedChanged: { - if (checked && root.privacyStore.profilePicturesVisibility !== - Constants.profilePicturesVisibility.everyone) { - root.privacyStore.setProfilePicturesVisibility( - Constants.profilePicturesVisibility.everyone - ) - } else if (!checked && root.privacyStore.profilePicturesVisibility === - Constants.profilePicturesVisibility.everyone) { - root.privacyStore.setProfilePicturesVisibility( - Constants.profilePicturesVisibility.contacts - ) - } - } - } - ] - sensor.onClicked: { - switch1.checked = !switch1.checked - } - } - - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - //% "Display images in chat automatically" - title: qsTrId("display-images-in-chat-automatically") - implicitHeight: 52 - components: [ - StatusQControls.StatusSwitch { - id: switch2 - checked: localAccountSensitiveSettings.displayChatImages - onCheckedChanged: { - if (localAccountSensitiveSettings.displayChatImages !== checked) { - localAccountSensitiveSettings.displayChatImages = checked - } - } - } - ] - sensor.onClicked: { - switch2.checked = !switch2.checked - } - } - - StatusBaseText { - width: parent.width - //% "All images (links that contain an image extension) will be downloaded and displayed, regardless of the whitelist settings below" - text: qsTrId("all-images--links-that-contain-an-image-extension--will-be-downloaded-and-displayed--regardless-of-the-whitelist-settings-below") - font.pixelSize: 15 - font.weight: Font.Thin - color: Theme.palette.baseColor1 - wrapMode: Text.WordWrap - bottomPadding: Style.current.smallPadding - } - - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - //% "Chat link previews" - title: qsTrId("chat-link-previews") - implicitHeight: 52 - components: [ - StatusIcon { - icon: "chevron-down" - rotation: 270 - color: Theme.palette.baseColor1 - } - ] - sensor.onClicked: Global.openPopup(chatLinksPreviewModal) - } - - Component { - id: chatLinksPreviewModal - ChatLinksPreviewModal { - privacyStore: root.privacyStore - } - } - - Component { - id: openLinksWithModal - OpenLinksWithModal {} - } - - StatusListItem { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - //% "Open links with..." - title: qsTrId("open-links-with---") - implicitHeight: 52 - //% "My default browser" - label: localAccountSensitiveSettings.openLinksInStatus ? "Status" : qsTrId("my-default-browser") - components: [ - StatusIcon { - icon: "chevron-down" - rotation: 270 - color: Theme.palette.baseColor1 - } - ] - sensor.onClicked: Global.openPopup(openLinksWithModal) - } - - 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") - implicitHeight: 52 - components: [ - StatusQControls.StatusSwitch { - id: switch3 - checked: !root.privacyStore.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.privacyStore.privacyModule.messagesFromContactsOnly === checked) { - root.privacyStore.privacyModule.messagesFromContactsOnly = !checked - } - } - } - ] - sensor.onClicked: { - switch3.checked = !switch3.checked - } - } } } diff --git a/ui/app/AppLayouts/Profile/views/SyncView.qml b/ui/app/AppLayouts/Profile/views/SyncView.qml deleted file mode 100644 index aa41621b61..0000000000 --- a/ui/app/AppLayouts/Profile/views/SyncView.qml +++ /dev/null @@ -1,194 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 -import QtQuick.Layouts 1.13 - -import StatusQ.Core 0.1 -import StatusQ.Core.Theme 0.1 -import StatusQ.Controls 0.1 - -import utils 1.0 - -import shared.popups 1.0 -import shared.controls 1.0 - -import "../stores" - -Item { - id: root - - property SyncStore syncStore - - property int profileContentWidth - - Layout.fillHeight: true - Layout.fillWidth: true - - Item { - width: profileContentWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - Component { - id: mailserversList - - // TODO: Replace with StatusQ component - StatusRadioButton { - id: rbSetMailsever - text: name - checked: nodeAddress === root.syncStore.activeMailserver - onClicked: { - if (checked) { - root.syncStore.setActiveMailserver(nodeAddress) - } - } - } - } - - Item { - id: addMailserver - width: parent.width - height: addButton.height - anchors.top: parent.top - anchors.topMargin: 24 - anchors.left: parent.left - anchors.leftMargin: 24 - - StatusFlatRoundButton { - id: addButton - icon.name: "add" - anchors.verticalCenter: parent.verticalCenter - } - - - StatusBaseText { - id: usernameText - //% "Add mailserver" - text: qsTrId("add-mailserver") - color: Theme.palette.primaryColor1 - anchors.left: addButton.right - anchors.leftMargin: Style.current.padding - anchors.verticalCenter: addButton.verticalCenter - font.pixelSize: 15 - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: addMailserverPopup.open() - } - - // TODO: replace with StatusModal - ModalPopup { - id: addMailserverPopup - //% "Add mailserver" - title: qsTrId("add-mailserver") - - property string nameValidationError: "" - property string enodeValidationError: "" - - function validate() { - nameValidationError = "" - enodeValidationError = "" - - if (nameInput.text === "") { - //% "You need to enter a name" - nameValidationError = qsTrId("you-need-to-enter-a-name") - } - - if (enodeInput.text === "") { - //% "You need to enter the enode address" - enodeValidationError = qsTrId("you-need-to-enter-the-enode-address") - } - return !nameValidationError && !enodeValidationError - } - - onOpened: { - nameInput.text = ""; - enodeInput.text = ""; - - nameValidationError = ""; - enodeValidationError = ""; - } - - footer: StatusButton { - anchors.right: parent.right - anchors.rightMargin: Style.current.smallPadding - //% "Save" - text: qsTrId("save") - anchors.bottom: parent.bottom - enabled: nameInput.text !== "" && enodeInput.text !== "" - onClicked: { - if (!addMailserverPopup.validate()) { - return; - } - root.syncStore.saveNewMailserver(nameInput.text, enodeInput.text) - addMailserverPopup.close() - } - } - - Input { - id: nameInput - //% "Name" - label: qsTrId("name") - //% "Specify a name" - placeholderText: qsTrId("specify-name") - validationError: addMailserverPopup.nameValidationError - } - - Input { - id: enodeInput - //% "History node address" - label: qsTrId("history-node-address") - //% "enode://{enode-id}:{password}@{ip-address}:{port-number}" - placeholderText: qsTrId("enode----enode-id---password---ip-address---port-number-") - validationError: addMailserverPopup.enodeValidationError - anchors.top: nameInput.bottom - anchors.topMargin: Style.current.bigPadding - } - - } - } - - StatusBaseText { - id: switchLbl - //% "Automatic mailserver selection" - text: qsTrId("automatic-mailserver-selection") - anchors.left: parent.left - anchors.leftMargin: 24 - anchors.top: addMailserver.bottom - anchors.topMargin: 24 - color: Theme.palette.directColor1 - } - - StatusSwitch { - id: automaticSelectionSwitch - checked: root.syncStore.automaticMailserverSelection - onCheckedChanged: root.syncStore.enableAutomaticMailserverSelection(checked) - anchors.top: addMailserver.bottom - anchors.topMargin: Style.current.padding - anchors.left: switchLbl.right - anchors.leftMargin: Style.current.padding - } - - StatusBaseText { - //% "..." - text: qsTr("Active mailserver: %1").arg(root.syncStore.getMailserverNameForNodeAddress(root.syncStore.activeMailserver)) - anchors.left: parent.left - anchors.leftMargin: 24 - anchors.top: switchLbl.bottom - anchors.topMargin: 24 - visible: automaticSelectionSwitch.checked - color: Theme.palette.directColor1 - } - - ListView { - id: mailServersListView - anchors.topMargin: 20 - anchors.top: automaticSelectionSwitch.bottom - anchors.bottom: parent.bottom - model: root.syncStore.mailservers - delegate: mailserversList - visible: !automaticSelectionSwitch.checked - } - } -} diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 23992bcb5f..55d614e175 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -42,6 +42,7 @@ QtObject { property var contactStore: profileSectionStore.contactsStore property var privacyStore: profileSectionStore.privacyStore + property var messagingStore: profileSectionStore.messagingStore property bool hasAddedContacts: contactStore.myContactsModel.count > 0 property var assets: walletSectionAccountTokens.model diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index c03d800a00..53f1a1c867 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -860,7 +860,7 @@ Item { Component.onCompleted: { Global.appMain = this; - const whitelist = appMain.rootStore.privacyStore.getLinkPreviewWhitelist() + const whitelist = appMain.rootStore.messagingStore.getLinkPreviewWhitelist() try { const whiteListedSites = JSON.parse(whitelist) let settingsUpdated = false diff --git a/ui/imports/assets/images/profile/chat.svg b/ui/imports/assets/images/profile/chat.svg new file mode 100644 index 0000000000..7aa3325110 --- /dev/null +++ b/ui/imports/assets/images/profile/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/imports/shared/controls/SettingsRadioButton.qml b/ui/imports/shared/controls/SettingsRadioButton.qml new file mode 100644 index 0000000000..ad03660e29 --- /dev/null +++ b/ui/imports/shared/controls/SettingsRadioButton.qml @@ -0,0 +1,40 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 + +Item { + property string label + property alias checked: radioButton.checked + property ButtonGroup group + signal clicked() + + id: root + width: childrenRect.width + height: 24 + + StatusRadioButton { + id: radioButton + ButtonGroup.group: root.group + } + + StatusBaseText { + text: root.label + font.pixelSize: 13 + color: Theme.palette.directColor1 + anchors.left: radioButton.right + anchors.leftMargin: 12 + anchors.verticalCenter: radioButton.verticalCenter + } + + MouseArea { + enabled: !radioButton.checked + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: root.clicked() + } +} \ No newline at end of file diff --git a/ui/imports/shared/controls/qmldir b/ui/imports/shared/controls/qmldir index 07d0ba8005..f9c647452d 100644 --- a/ui/imports/shared/controls/qmldir +++ b/ui/imports/shared/controls/qmldir @@ -15,6 +15,7 @@ RecipientSelector 1.0 RecipientSelector.qml SearchBox 1.0 SearchBox.qml SeedPhraseTextArea 1.0 SeedPhraseTextArea.qml SendToContractWarning 1.0 SendToContractWarning.qml +SettingsRadioButton 1.0 SettingsRadioButton.qml StatusTabButton 1.0 StatusTabButton.qml StyledButton 1.0 StyledButton.qml StyledTextArea 1.0 StyledTextArea.qml diff --git a/ui/imports/shared/views/chat/LinksMessageView.qml b/ui/imports/shared/views/chat/LinksMessageView.qml index 2c583e4aad..f063539be0 100644 --- a/ui/imports/shared/views/chat/LinksMessageView.qml +++ b/ui/imports/shared/views/chat/LinksMessageView.qml @@ -373,7 +373,7 @@ Column { //% "Enable in Settings" text: qsTrId("enable-in-settings") onClicked: { - Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.privacyAndSecurity); + Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.messaging); } width: parent.width anchors.top: sep1.bottom diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index bd1cf32526..22e95f4c1e 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -31,13 +31,13 @@ QtObject { property int profile: 0 property int contacts: 1 property int ensUsernames: 2 - property int wallet: 3 - property int privacyAndSecurity: 4 - property int appearance: 5 - property int sound: 6 - property int language: 7 - property int notifications: 8 - property int syncSettings: 9 + property int messaging: 3 + property int wallet: 4 + property int privacyAndSecurity: 5 + property int appearance: 6 + property int sound: 7 + property int language: 8 + property int notifications: 9 property int devicesSettings: 10 property int browserSettings: 11 property int advanced: 12 @@ -85,6 +85,12 @@ QtObject { readonly property int noOne: 3 } + readonly property QtObject profilePicturesShowTo: QtObject { + readonly property int contactsOnly: 1 + readonly property int everyone: 2 + readonly property int noOne: 3 + } + readonly property int communityImported: 0 readonly property int communityImportingInProgress: 1 readonly property int communityImportingError: 2