diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml index 43e1fddbd5..71d10bc452 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml @@ -253,6 +253,9 @@ Control { Layout.fillWidth: true Layout.minimumHeight: empty ? Math.floor(hiddenTargetDropArea.height + hiddenTargetDropArea.anchors.topMargin) : d.defaultDelegateHeight * Math.min(count, 4) + implicitHeight: contentHeight + interactive: false + model: root.baseModel readonly property bool empty: !contentHeight @@ -333,5 +336,7 @@ Control { } } } + + Item { Layout.fillHeight: true } } } diff --git a/ui/app/AppLayouts/Profile/panels/ProfileSocialLinksPanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileSocialLinksPanel.qml index af9d17ee40..68fb3d4273 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileSocialLinksPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileSocialLinksPanel.qml @@ -161,5 +161,7 @@ Control { } } } + + Item { Layout.fillHeight: true } } } diff --git a/ui/app/AppLayouts/Profile/panels/qmldir b/ui/app/AppLayouts/Profile/panels/qmldir index e1400e18c9..0349907e16 100644 --- a/ui/app/AppLayouts/Profile/panels/qmldir +++ b/ui/app/AppLayouts/Profile/panels/qmldir @@ -1,3 +1,4 @@ +ProfileDescriptionPanel 1.0 ProfileDescriptionPanel.qml ProfileSocialLinksPanel 1.0 ProfileSocialLinksPanel.qml ProfileShowcaseCommunitiesPanel 1.0 ProfileShowcaseCommunitiesPanel.qml ProfileShowcaseCollectiblesPanel 1.0 ProfileShowcaseCollectiblesPanel.qml diff --git a/ui/app/AppLayouts/Profile/views/MyProfileView.qml b/ui/app/AppLayouts/Profile/views/MyProfileView.qml index 91c49afcff..151f8223e0 100644 --- a/ui/app/AppLayouts/Profile/views/MyProfileView.qml +++ b/ui/app/AppLayouts/Profile/views/MyProfileView.qml @@ -19,6 +19,7 @@ import StatusQ.Core.Theme 0.1 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 +import AppLayouts.Profile.panels 1.0 import AppLayouts.Wallet.stores 1.0 SettingsContentBase { @@ -34,86 +35,221 @@ SettingsContentBase { property var communitiesModel - titleRowComponentLoader.sourceComponent: StatusButton { - objectName: "profileSettingsChangePasswordButton" - text: qsTr("Change Password") - onClicked: Global.openPopup(changePasswordModal) - enabled: !userProfile.isKeycardUser + titleRowComponentLoader.sourceComponent: RowLayout { + StatusButton { + objectName: "profileSettingsChangePasswordButton" + text: qsTr("Change Password") + onClicked: Global.openPopup(changePasswordModal) + enabled: !userProfile.isKeycardUser + } + StatusButton { + text: qsTr("Preview") + onClicked: Global.openPopup(profilePreview) + } } - dirty: settingsView.dirty - saveChangesButtonEnabled: settingsView.valid + dirty: (!descriptionPanel.isEnsName && + descriptionPanel.displayName.text !== profileStore.displayName) || + descriptionPanel.bio.text !== profileStore.bio || + profileStore.socialLinksDirty || + profileHeader.icon !== profileStore.profileLargeImage || + priv.hasAnyProfileShowcaseChanges + saveChangesButtonEnabled: !!descriptionPanel.displayName.text && descriptionPanel.displayName.valid - onResetChangesClicked: { - settingsView.reset() - profilePreview.reload() - } - onSaveChangesClicked: { - settingsView.save() - profilePreview.reload() - } + onResetChangesClicked: priv.reset() + + onSaveChangesClicked: priv.save() bottomHeaderComponents: StatusTabBar { - id: editPreviwTabBar + id: profileTabBar + StatusTabButton { + width: implicitWidth leftPadding: 0 - width: implicitWidth - text: qsTr("Edit") + text: qsTr("Identity") } + StatusTabButton { width: implicitWidth - text: qsTr("Preview") + text: qsTr("Communities") + } + + StatusTabButton { + width: implicitWidth + text: qsTr("Accounts") + } + + StatusTabButton { + width: implicitWidth + text: qsTr("Collectibles") + } + + StatusTabButton { + width: implicitWidth + text: qsTr("Assets") + } + + StatusTabButton { + width: implicitWidth + text: qsTr("Web") + } + } + + onVisibleChanged: if (visible) profileStore.requestProfileShowcasePreferences() + Component.onCompleted: profileStore.requestProfileShowcasePreferences() + + readonly property var priv: QtObject { + id: priv + + property bool hasAnyProfileShowcaseChanges: false + + function reset() { + descriptionPanel.displayName.text = Qt.binding(() => { return profileStore.displayName }) + descriptionPanel.bio.text = Qt.binding(() => { return profileStore.bio }) + profileStore.resetSocialLinks() + profileHeader.icon = Qt.binding(() => { return profileStore.profileLargeImage }) + + profileShowcaseCommunitiesPanel.reset() + profileShowcaseAccountsPanel.reset() + profileShowcaseCollectiblesPanel.reset() + profileShowcaseAssetsPanel.reset() + root.profileStore.requestProfileShowcasePreferences() + hasAnyProfileShowcaseChanges = false + } + + function save() { + if (hasAnyProfileShowcaseChanges) + profileStore.storeProfileShowcasePreferences() + + if (!descriptionPanel.isEnsName) + profileStore.setDisplayName(descriptionPanel.displayName.text) + profileStore.setBio(descriptionPanel.bio.text.trim()) + profileStore.saveSocialLinks() + if (profileHeader.icon === "") { + root.profileStore.removeImage() + } else { + profileStore.uploadImage(profileHeader.icon, + profileHeader.cropRect.x.toFixed(), + profileHeader.cropRect.y.toFixed(), + (profileHeader.cropRect.x + profileHeader.cropRect.width).toFixed(), + (profileHeader.cropRect.y + profileHeader.cropRect.height).toFixed()); + } + + reset() } } ColumnLayout { - id: layout - spacing: Constants.settingsSection.itemSpacing width: root.contentWidth StackLayout { id: stackLayout - currentIndex: editPreviwTabBar.currentIndex + currentIndex: profileTabBar.currentIndex - MyProfileSettingsView { - id: settingsView + // identity + ColumnLayout { objectName: "myProfileSettingsView" - profileStore: root.profileStore - privacyStore: root.privacyStore - walletStore: root.walletStore - communitiesModel: root.communitiesModel - walletAssetsStore: root.walletAssetsStore - currencyStore: root.currencyStore + ProfileHeader { + id: profileHeader + Layout.fillWidth: true + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding - onVisibleChanged: if (visible) stackLayout.Layout.preferredHeight = settingsView.implicitHeight - Component.onCompleted: stackLayout.Layout.preferredHeight = Qt.binding(() => settingsView.implicitHeight) + store: root.profileStore + + displayName: profileStore.name + pubkey: profileStore.pubkey + icon: profileStore.profileLargeImage + imageSize: ProfileHeader.ImageSize.Big + + displayNameVisible: false + pubkeyVisible: false + emojiHashVisible: false + editImageButtonVisible: true + } + + ProfileDescriptionPanel { + id: descriptionPanel + + readonly property bool isEnsName: profileStore.preferredName + + Layout.fillWidth: true + + displayName.focus: !isEnsName + displayName.input.edit.readOnly: isEnsName + displayName.text: profileStore.name + displayName.validationMode: StatusInput.ValidationMode.Always + displayName.validators: isEnsName ? [] : Constants.validators.displayName + bio.text: profileStore.bio + } } - MyProfilePreview { + // communities + ProfileShowcaseCommunitiesPanel { + id: profileShowcaseCommunitiesPanel + baseModel: root.communitiesModel + showcaseModel: root.profileStore.profileShowcaseCommunitiesModel + onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + } + + // accounts + ProfileShowcaseAccountsPanel { + id: profileShowcaseAccountsPanel + baseModel: root.walletStore.accounts + showcaseModel: root.profileStore.profileShowcaseAccountsModel + currentWallet: root.walletStore.overview.mixedcaseAddress + onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + } + + // collectibles + ProfileShowcaseCollectiblesPanel { + id: profileShowcaseCollectiblesPanel + baseModel: root.profileStore.collectiblesModel + showcaseModel: root.profileStore.profileShowcaseCollectiblesModel + onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + } + + // assets + ProfileShowcaseAssetsPanel { + id: profileShowcaseAssetsPanel + baseModel: root.walletAssetsStore.groupedAccountAssetsModel // TODO: instantiate an assets model in profile module + showcaseModel: root.profileStore.profileShowcaseAssetsModel + onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + formatCurrencyAmount: function(amount, symbol) { + return root.currencyStore.formatCurrencyAmount(amount, symbol) + } + } + + // web + ProfileSocialLinksPanel { + profileStore: root.profileStore + socialLinksModel: root.profileStore.temporarySocialLinksModel + } + + Component { + id: changePasswordModal + ChangePasswordModal { + privacyStore: root.privacyStore + onPasswordChanged: Global.openPopup(successPopup) + } + } + + Component { + id: successPopup + ChangePasswordSuccessModal {} + } + + Component { id: profilePreview - - profileStore: root.profileStore - contactsStore: root.contactsStore - networkConnectionStore: root.networkConnectionStore - communitiesModel: root.communitiesModel - dirtyValues: settingsView.dirtyValues - dirty: settingsView.dirty - - onVisibleChanged: if (visible) stackLayout.Layout.preferredHeight = Qt.binding(() => profilePreview.implicitHeight) + ProfileDialog { + publicKey: root.contactsStore.myPublicKey + profileStore: root.profileStore + contactsStore: root.contactsStore + networkConnectionStore: root.networkConnectionStore + communitiesModel: root.communitiesModel + onClosed: destroy() + } } } - - Component { - id: changePasswordModal - ChangePasswordModal { - privacyStore: root.privacyStore - onPasswordChanged: Global.openPopup(successPopup); - } - } - - Component { - id: successPopup - ChangePasswordSuccessModal { } - } } } diff --git a/ui/app/AppLayouts/Profile/views/profile/MyProfileSettingsView.qml b/ui/app/AppLayouts/Profile/views/profile/MyProfileSettingsView.qml deleted file mode 100644 index aaca64c1be..0000000000 --- a/ui/app/AppLayouts/Profile/views/profile/MyProfileSettingsView.qml +++ /dev/null @@ -1,259 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.13 - -import utils 1.0 -import shared 1.0 -import shared.stores 1.0 -import shared.panels 1.0 -import shared.popups 1.0 -import shared.controls.chat 1.0 - -import "../../popups" -import "../../stores" -import "../../controls" -import "../../panels" - -import StatusQ.Core 0.1 -import StatusQ.Core.Theme 0.1 -import StatusQ.Components 0.1 -import StatusQ.Controls 0.1 - -import AppLayouts.Wallet.stores 1.0 - -ColumnLayout { - id: root - - spacing: 20 - - property PrivacyStore privacyStore - property ProfileStore profileStore - property WalletStore walletStore - required property WalletAssetsStore walletAssetsStore - required property CurrenciesStore currencyStore - property var communitiesModel - - property bool hasAnyProfileShowcaseChanges: false - - property QtObject dirtyValues: QtObject { - property string displayName: descriptionPanel.displayName.text - property string bio: descriptionPanel.bio.text - property bool biomentricValue: biometricsSwitch.checked - property url profileLargeImage: profileHeader.previewIcon - } - - readonly property bool dirty: (!descriptionPanel.isEnsName && - descriptionPanel.displayName.text !== profileStore.displayName) || - descriptionPanel.bio.text !== profileStore.bio || - profileStore.socialLinksDirty || - biometricsSwitch.checked !== biometricsSwitch.currentStoredValue || - profileHeader.icon !== profileStore.profileLargeImage || - hasAnyProfileShowcaseChanges - - readonly property bool valid: !!descriptionPanel.displayName.text && descriptionPanel.displayName.valid - - function reset() { - descriptionPanel.displayName.text = Qt.binding(() => { return profileStore.displayName }) - descriptionPanel.bio.text = Qt.binding(() => { return profileStore.bio }) - profileStore.resetSocialLinks() - biometricsSwitch.checked = Qt.binding(() => { return biometricsSwitch.currentStoredValue }) - profileHeader.icon = Qt.binding(() => { return profileStore.profileLargeImage }) - - profileShowcaseCommunitiesPanel.reset() - profileShowcaseAccountsPanel.reset() - profileShowcaseCollectiblesPanel.reset() - profileShowcaseAssetsPanel.reset() - root.profileStore.requestProfileShowcasePreferences() - hasAnyProfileShowcaseChanges = false - } - - function save() { - if (hasAnyProfileShowcaseChanges) - profileStore.storeProfileShowcasePreferences() - - if (!descriptionPanel.isEnsName) - profileStore.setDisplayName(descriptionPanel.displayName.text) - profileStore.setBio(descriptionPanel.bio.text.trim()) - profileStore.saveSocialLinks() - if (profileHeader.icon === "") { - root.profileStore.removeImage() - } else { - profileStore.uploadImage(profileHeader.icon, - profileHeader.cropRect.x.toFixed(), - profileHeader.cropRect.y.toFixed(), - (profileHeader.cropRect.x + profileHeader.cropRect.width).toFixed(), - (profileHeader.cropRect.y + profileHeader.cropRect.height).toFixed()); - } - - if (biometricsSwitch.checked && !biometricsSwitch.currentStoredValue) - root.privacyStore.tryStoreToKeyChain() - else if (!biometricsSwitch.checked) - root.privacyStore.tryRemoveFromKeyChain() - - reset() - } - - onVisibleChanged: if (visible) profileStore.requestProfileShowcasePreferences() - Component.onCompleted: profileStore.requestProfileShowcasePreferences() - - readonly property Connections privacyStoreConnections: Connections { - target: Qt.platform.os === Constants.mac ? root.privacyStore.privacyModule : null - - function onStoreToKeychainError(errorDescription: string) { - root.reset() - } - - function onStoreToKeychainSuccess() { - root.reset() - } - } - - ProfileHeader { - id: profileHeader - Layout.fillWidth: true - Layout.leftMargin: Style.current.padding - Layout.rightMargin: Style.current.padding - - store: root.profileStore - - displayName: profileStore.name - pubkey: profileStore.pubkey - icon: profileStore.profileLargeImage - imageSize: ProfileHeader.ImageSize.Big - - displayNameVisible: false - pubkeyVisible: false - emojiHashVisible: false - editImageButtonVisible: true - } - - ProfileDescriptionPanel { - id: descriptionPanel - - readonly property bool isEnsName: profileStore.preferredName - - Layout.fillWidth: true - - displayName.focus: !isEnsName - displayName.input.edit.readOnly: isEnsName - displayName.text: profileStore.name - displayName.validationMode: StatusInput.ValidationMode.Always - displayName.validators: isEnsName ? [] : Constants.validators.displayName - bio.text: profileStore.bio - } - - Separator { - Layout.fillWidth: true - } - - ProfileSocialLinksPanel { - Layout.fillWidth: true - profileStore: root.profileStore - socialLinksModel: root.profileStore.temporarySocialLinksModel - } - - Separator { - Layout.fillWidth: true - } - - StatusBaseText { - visible: Qt.platform.os === Constants.mac - text: qsTr("Security") - color: Theme.palette.baseColor1 - } - - StatusListItem { - Layout.fillWidth: true - visible: Qt.platform.os === Constants.mac - title: qsTr("Biometric login and transaction authentication") - asset.name: "touch-id" - components: [ StatusSwitch { - id: biometricsSwitch - horizontalPadding: 0 - readonly property bool currentStoredValue: localAccountSettings.storeToKeychainValue === Constants.keychain.storedValue.store - checked: currentStoredValue - } ] - onClicked: biometricsSwitch.toggle() - } - - Separator { - Layout.fillWidth: true - visible: Qt.platform.os === Constants.mac - } - - StatusBaseText { - text: qsTr("Showcase") - color: Theme.palette.baseColor1 - } - - StatusTabBar { - id: showcaseTabBar - - StatusTabButton { - width: implicitWidth - leftPadding: 0 - text: qsTr("Communities") - } - - StatusTabButton { - width: implicitWidth - text: qsTr("Accounts") - } - - StatusTabButton { - width: implicitWidth - text: qsTr("Collectibles") - } - - StatusTabButton { - width: implicitWidth - text: qsTr("Assets") - } - } - - StackLayout { - id: showcaseStack - Layout.fillWidth: true - currentIndex: showcaseTabBar.currentIndex - - ProfileShowcaseCommunitiesPanel { - id: profileShowcaseCommunitiesPanel - Layout.minimumHeight: implicitHeight - Layout.maximumHeight: implicitHeight - baseModel: root.communitiesModel - showcaseModel: root.profileStore.profileShowcaseCommunitiesModel - onShowcaseEntryChanged: hasAnyProfileShowcaseChanges = true - } - - ProfileShowcaseAccountsPanel { - id: profileShowcaseAccountsPanel - Layout.minimumHeight: implicitHeight - Layout.maximumHeight: implicitHeight - baseModel: root.walletStore.accounts - showcaseModel: root.profileStore.profileShowcaseAccountsModel - currentWallet: root.walletStore.overview.mixedcaseAddress - onShowcaseEntryChanged: hasAnyProfileShowcaseChanges = true - } - - ProfileShowcaseCollectiblesPanel { - id: profileShowcaseCollectiblesPanel - Layout.minimumHeight: implicitHeight - Layout.maximumHeight: implicitHeight - baseModel: root.profileStore.collectiblesModel - showcaseModel: root.profileStore.profileShowcaseCollectiblesModel - onShowcaseEntryChanged: hasAnyProfileShowcaseChanges = true - } - - ProfileShowcaseAssetsPanel { - id: profileShowcaseAssetsPanel - Layout.minimumHeight: implicitHeight - Layout.maximumHeight: implicitHeight - baseModel: root.walletAssetsStore.groupedAccountAssetsModel // TODO: instantiate an assets model in profile module - showcaseModel: root.profileStore.profileShowcaseAssetsModel - onShowcaseEntryChanged: hasAnyProfileShowcaseChanges = true - formatCurrencyAmount: function(amount, symbol) { - return root.currencyStore.formatCurrencyAmount(amount, symbol) - } - } - } -}