fix(MyProfileView): New design

This commit is contained in:
Igor Sirotin 2022-06-22 15:16:21 +03:00 committed by Igor Sirotin
parent 5c6b5f1f47
commit c7d2157d20
26 changed files with 937 additions and 741 deletions

View File

@ -1,12 +1,12 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import shared.popups 1.0
Item {
id: root
@ -20,6 +20,9 @@ Item {
property bool editable: false
readonly property Item contentItem: contentLoader.item
readonly property size settingsDirtyToastMessageImplicitSize:
Qt.size(settingsDirtyToastMessage.implicitWidth,
settingsDirtyToastMessage.implicitHeight + settingsDirtyToastMessage.anchors.bottomMargin)
signal previousPageClicked
signal saveChangesClicked
@ -31,9 +34,7 @@ Item {
}
function notifyDirty() {
cancelChangesButtonAnimation.running = true
saveChangesButtonAnimation.running = true
saveChangesButton.forceActiveFocus()
settingsDirtyToastMessage.notifyDirty()
}
implicitWidth: layout.implicitWidth
@ -81,69 +82,20 @@ Item {
sourceComponent: root.content
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: buttonsLayout.implicitHeight
color: Theme.palette.statusToastMessage.backgroundColor
visible: root.editable
RowLayout {
id: buttonsLayout
anchors.fill: parent
enabled: root.dirty
Item {
Layout.fillWidth: true
}
StatusButton {
id: cancelChangesButton
text: qsTr("Cancel changes")
type: StatusBaseButton.Type.Danger
border.color: textColor
border.width: 0
onClicked: root.resetChangesClicked()
NumberAnimation on border.width {
id: cancelChangesButtonAnimation
from: 0
to: 2
loops: 2
duration: 600
onFinished: cancelChangesButton.border.width = 0
}
}
StatusButton {
id: saveChangesButton
text: qsTr("Save changes")
border.color: textColor
border.width: 0
onClicked: root.saveChangesClicked()
NumberAnimation on border.width {
id: saveChangesButtonAnimation
from: 0
to: 2
loops: 2
duration: 600
onFinished: saveChangesButton.border.width = 0
}
}
}
SettingsDirtyToastMessage {
id: settingsDirtyToastMessage
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
bottomMargin: 16
}
active: root.dirty
flickable: root.contentItem
saveChangesButtonEnabled: root.contentItem && root.contentItem.saveChangesButtonEnabled
onResetChangesClicked: root.resetChangesClicked()
onSaveChangesClicked: root.saveChangesClicked()
}
}

View File

@ -40,6 +40,11 @@ Flickable {
property alias bannerPath: bannerPicker.source
property alias bannerCropRect: bannerPicker.cropRect
property size bottomReservedSpace: Qt.size(0, 0)
property bool bottomReservedSpaceActive: false
readonly property bool saveChangesButtonEnabled: true
contentWidth: layout.width
contentHeight: layout.height
clip: true
@ -149,7 +154,10 @@ Flickable {
}
Item {
Layout.fillHeight: true
// settingsDirtyToastMessage placeholder
visible: root.bottomReservedSpaceActive
implicitWidth: root.bottomReservedSpace.width
implicitHeight: root.bottomReservedSpace.height
}
}
}

View File

@ -184,6 +184,9 @@ StackLayout {
pinMessagesEnabled: root.pinMessagesEnabled
}
bottomReservedSpace: editCommunityPage.settingsDirtyToastMessageImplicitSize
bottomReservedSpaceActive: editCommunityPage.dirty
Component.onCompleted: {
editCommunityPage.dirty =
Qt.binding(() => {

View File

@ -41,6 +41,12 @@ StatusAppTwoPanelLayout {
store: profileView.store
anchors.fill: parent
anchors.topMargin: d.topMargin
onMenuItemClicked: {
if (profileContainer.currentItem.dirty) {
event.accepted = true;
profileContainer.currentItem.notifyDirty();
}
}
}
rightPanel: Item {
@ -65,6 +71,8 @@ StatusAppTwoPanelLayout {
StackLayout {
id: profileContainer
readonly property var currentItem: (currentIndex >= 0 && currentIndex < children.length) ? children[currentIndex] : null
anchors.top: banner.visible? banner.bottom : parent.top
anchors.left: parent.left
anchors.right: parent.right
@ -86,7 +94,9 @@ StatusAppTwoPanelLayout {
implicitWidth: parent.width
implicitHeight: parent.height
walletStore: profileView.store.walletStore
profileStore: profileView.store.profileStore
privacyStore: profileView.store.privacyStore
sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.profile)
contentWidth: d.contentWidth
}
@ -138,15 +148,6 @@ StatusAppTwoPanelLayout {
contentWidth: d.contentWidth
}
PrivacyView {
implicitWidth: parent.width
implicitHeight: parent.height
privacyStore: profileView.store.privacyStore
sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.privacyAndSecurity)
contentWidth: d.contentWidth
}
AppearanceView {
implicitWidth: parent.width
implicitHeight: parent.height

View File

@ -0,0 +1,21 @@
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
StatusListItem {
property var community
title: community.name
subTitle: community.amISectionAdmin ? qsTr("Admin") : qsTr("Member")
image {
source: community.image
}
icon {
name: community.name
letterSize: 14
isLetterIdenticon: !community.image
color: community.color
}
}

View File

@ -4,9 +4,12 @@ import StatusQ.Core 0.1
StatusListItem {
signal goToAccountView()
id: root
property var account
property bool showShevronIcon: true
signal goToAccountView()
title: account.name
subTitle: account.address
@ -17,15 +20,18 @@ StatusListItem {
icon.isLetterIdenticon: !!account.emoji
icon.background.color: Theme.palette.indirectColor1
width: parent.width
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
components: !showShevronIcon ? [] : [ shevronIcon ]
onClicked: {
goToAccountView()
}
StatusIcon {
id: shevronIcon
visible: root.showShevronIcon
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
}

View File

@ -23,19 +23,28 @@ StatusModal {
function onChangePasswordResponse(success, errorMsg) {
if (success) {
if (Qt.platform.os === "osx") {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
root.privacyStore.storeToKeyChain(d.passwordProcessing);
}
passwordChanged()
submitBtn.enabled = false
} else {
}
else {
view.reset()
view.errorMsgText = errorMsg
console.warn("TODO: Display error message when change password action failure! ")
}
submitBtn.loading = false
d.passwordProcessing = "";
submitBtn.enabled = false;
}
QtObject {
id: d
// We temporarly store the password during "changePassword" call
// to store it to KeyChain after successfull change operation.
property string passwordProcessing: ""
function submit() {
submitBtn.loading = true
// ChangePassword operation blocks the UI so loading = true; will never have any affect until changePassword/createPassword is done.
@ -77,6 +86,7 @@ StatusModal {
interval: 20
onTriggered: {
// Change current password call action to the backend
d.passwordProcessing = view.newPswText
root.privacyStore.changePassword(view.currentPswText, view.newPswText)
}
}

View File

@ -1,162 +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
import "../../Onboarding/shared" as OnboardingComponents
// TODO: replace with StatusModal
ModalPopup {
id: popup
property var privacyStore
title: qsTr("Store pass to Keychain")
onClosed: {
destroy()
}
function updateListState() {
if (localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueStore)
storeBtn.checked = true
else if (localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueNotNow ||
localAccountSettings.storeToKeychainValue === "")
notNowBtn.checked = true
else if (localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueNever)
neverBtn.checked = true
}
function offerToStorePassword(password, runStoreToKeychainPopup) {
if(Qt.platform.os == "osx")
{
if(runStoreToKeychainPopup)
Global.openPopup(storeToKeychainConfirmationPopupComponent, { password: password })
}
}
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
Connections {
target: localAccountSettings
onStoreToKeychainValueChanged: {
updateListState()
}
}
ButtonGroup {
id: openLinksWithGroup
}
RadioButtonSelector {
id: storeBtn
title: qsTr("Store")
buttonGroup: openLinksWithGroup
checked: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueStore
onCheckedChanged: {
if (checked && localAccountSettings.storeToKeychainValue !== Constants.storeToKeychainValueStore) {
// TODO: REFACTOR TO NEW PASWORD VIEW AND
// DELETE StoreToKeychainSelectionModal.qml
// AND CreatePasswordModal.qml IF NOT NEEDED
var storePassPopup = Global.openPopup(storePasswordModal)
if(storePassPopup)
{
storePassPopup.closed.connect(function(){
updateListState()
})
}
}
}
}
RadioButtonSelector {
id: notNowBtn
title: qsTr("Not now")
buttonGroup: openLinksWithGroup
checked: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueNotNow ||
localAccountSettings.storeToKeychainValue === ""
onCheckedChanged: {
if (checked) {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNotNow
}
}
}
RadioButtonSelector {
id: neverBtn
title: qsTr("Never")
buttonGroup: openLinksWithGroup
checked: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueNever
onCheckedChanged: {
if (checked) {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever
}
}
}
Component {
id: storePasswordModal
OnboardingComponents.CreatePasswordModal {
privacyStore: popup.privacyStore
storingPasswordModal: true
height: 350
onOfferToStorePassword: {
popup.offerToStorePassword(password, runStoreToKeychainPopup)
}
}
}
Component {
id: storeToKeychainConfirmationPopupComponent
ConfirmationDialog {
id: storeToKeychainConfirmationPopup
property string password: ""
height: 200
confirmationText: qsTr("Would you like to store password to the Keychain?")
showRejectButton: true
showCancelButton: true
confirmButtonLabel: qsTr("Store")
rejectButtonLabel: qsTr("Not now")
cancelButtonLabel: qsTr("Never")
onClosed: {
destroy()
}
function finish()
{
password = ""
storeToKeychainConfirmationPopup.close()
}
onConfirmButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore
root.privacyStore.storeToKeyChain(password)
finish()
}
onRejectButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNotNow
finish()
}
onCancelButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever
finish()
}
}
}
}
}

View File

@ -100,9 +100,6 @@ QtObject {
property ListModel settingsMenuItems: ListModel {
Component.onCompleted: {
append({subsection: Constants.settingsSubsection.privacyAndSecurity,
text: qsTr("Privacy and security"),
icon: "security"})
append({subsection: Constants.settingsSubsection.appearance,
text: qsTr("Appearance"),
icon: "appearance"})

View File

@ -20,6 +20,8 @@ QtObject {
}
}
property var details: Utils.getContactDetailsAsJson(pubkey)
function uploadImage(source, aX, aY, bX, bY) {
return root.profileModule.upload(source, aX, aY, bX, bY)
}

View File

@ -16,6 +16,7 @@ QtObject {
networksModule.toggleTestNetworksEnabled()
}
property var accounts: walletSectionAccounts.model
property var importedAccounts: walletSectionAccounts.imported
property var generatedAccounts: walletSectionAccounts.generated
property var watchOnlyAccounts: walletSectionAccounts.watchOnly

View File

@ -8,9 +8,12 @@ import shared.popups 1.0
import "../panels"
Item {
id: root
property var store
signal menuItemClicked(var event)
StatusNavigationPanelHeadline {
id: title
text: qsTr("Settings")
@ -46,12 +49,20 @@ Item {
onMenuItemClicked: {
if (menu_item.subsection === Constants.settingsSubsection.backUpSeed) {
Global.openBackUpSeedPopup();
} else {
if (menu_item.subsection === Constants.settingsSubsection.signout) {
return confirmDialog.open();
}
Global.settingsSubsection = menu_item.subsection;
return;
}
let event = { accepted: false, item: menu_item.subsection };
root.menuItemClicked(event);
if (event.accepted)
return;
if (menu_item.subsection === Constants.settingsSubsection.signout)
return confirmDialog.open()
Global.settingsSubsection = menu_item.subsection
}
}
}

View File

@ -10,6 +10,8 @@ import shared.controls.chat 1.0
import "../popups"
import "../stores"
import "../controls"
import "./profile"
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
@ -19,161 +21,69 @@ import StatusQ.Controls 0.1
SettingsContentBase {
id: root
property WalletStore walletStore
property ProfileStore profileStore
property PrivacyStore privacyStore
titleRowComponentLoader.sourceComponent: StatusButton {
text: qsTr("Change Password")
onClicked: changePasswordModal.open()
}
dirty: settingsView.dirty
saveChangesButtonEnabled: settingsView.valid
onResetChangesClicked: settingsView.reset()
onSaveChangesClicked: settingsView.save()
ColumnLayout {
id: layout
spacing: Constants.settingsSection.itemSpacing
width: root.contentWidth
RowLayout {
StatusTabBar {
id: editPreviwTabBar
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
StatusBaseText {
id: profileName
text: root.profileStore.name
font.weight: Font.Bold
font.pixelSize: 20
color: Theme.palette.directColor1
StatusTabButton {
width: implicitWidth
text: qsTr("Edit")
}
StatusButton {
text: "Edit"
onClicked: Global.openPopup(displayNamePopupComponent)
StatusTabButton {
width: implicitWidth
text: qsTr("Preview")
}
}
Item {
StackLayout {
Layout.fillWidth: true
currentIndex: editPreviwTabBar.currentIndex
MyProfileSettingsView {
id: settingsView
Layout.fillWidth: true
profileStore: root.profileStore
privacyStore: root.privacyStore
walletStore: root.walletStore
}
MyProfilePreview {
id: profilePreview
Layout.fillWidth: true
}
StatusFlatRoundButton {
id: qrCodeButton
Layout.preferredWidth: 32
Layout.preferredHeight: 32
icon.name: "qr"
type: StatusFlatRoundButton.Type.Quaternary
onClicked: qrCodePopup.open()
}
}
Separator {
Layout.fillWidth: true
}
ProfileHeader {
id: profileImgNameContainer
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
displayName: profileStore.name
pubkey: profileStore.pubkey
icon: profileStore.icon
displayNameVisible: false
pubkeyVisible: false
compact: false
imageOverlay: Item {
StatusFlatRoundButton {
width: 24
height: 24
anchors {
right: parent.right
bottom: parent.bottom
rightMargin: -8
}
type: StatusFlatRoundButton.Type.Secondary
icon.name: "pencil"
icon.color: Theme.palette.directColor1
icon.width: 12.5
icon.height: 12.5
onClicked: Global.openChangeProfilePicPopup()
}
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
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
}
ChangePasswordModal {
id: changePasswordModal
privacyStore: root.privacyStore
anchors.centerIn: parent
onPasswordChanged: successPopup.open()
}
ChangePasswordSuccessModal {
id: successPopup
anchors.centerIn: parent
}
}
}

View File

@ -1,86 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtGraphicalEffects 1.13
import utils 1.0
import shared.panels 1.0
import shared.status 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1 as StatusQControls
import "../popups"
import "../stores"
SettingsContentBase {
id: root
property PrivacyStore privacyStore
ColumnLayout {
spacing: Constants.settingsSection.itemSpacing
width: root.contentWidth
StatusListItem {
Layout.fillWidth: true
title: qsTr("Change password")
implicitHeight: 52
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
sensor.onClicked: changePasswordModal.open()
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Store pass to Keychain")
implicitHeight: 52
visible: Qt.platform.os == "osx" // For now, this is available only on MacOS
label: {
let value = localAccountSettings.storeToKeychainValue
if(value == Constants.storeToKeychainValueStore)
return qsTr("Store")
if(value == Constants.storeToKeychainValueNever)
return qsTr("Never")
return qsTr("Not now")
}
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
sensor.onClicked: Global.openPopup(storeToKeychainSelectionModal)
Component {
id: storeToKeychainSelectionModal
StoreToKeychainSelectionModal {
privacyStore: root.privacyStore
}
}
}
ChangePasswordModal {
id: changePasswordModal
privacyStore: root.privacyStore
anchors.centerIn: parent
onPasswordChanged: successPopup.open()
}
ChangePasswordSuccessModal {
id: successPopup
anchors.centerIn: parent
}
}
}

View File

@ -3,6 +3,7 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.13
import utils 1.0
import shared.popups 1.0
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
@ -20,8 +21,17 @@ Item {
property list<Item> headerComponents
default property Item content
property bool dirty: false
property bool saveChangesButtonEnabled: false
signal backButtonClicked()
signal baseAreaClicked()
signal saveChangesClicked()
signal resetChangesClicked()
function notifyDirty() {
settingsDirtyToastMessage.notifyDirty();
}
QtObject {
id: d
@ -90,21 +100,57 @@ Item {
}
ScrollView {
anchors.top: titleRow.visible? titleRow.bottom : topHeader.bottom
id: 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
contentWidth: Math.max(contentWrapper.implicitWidth, width)
contentHeight: Math.max(contentWrapper.implicitHeight, height)+anchors.topMargin
width: root.contentWidth
clip: true
MouseArea {
anchors.fill: parent
onClicked: { root.baseAreaClicked() }
Flickable {
id: contentFliackable
contentWidth: Math.max(contentLayout.implicitWidth, scrollView.width)
contentHeight: Math.max(contentLayout.implicitHeight, scrollView.height) + scrollView.anchors.topMargin
Column {
id: contentWrapper
id: contentLayout
anchors.fill: parent.contentItem
MouseArea {
onClicked: root.baseAreaClicked()
width: contentWrapper.implicitWidth
height: contentWrapper.implicitHeight
Column {
id: contentWrapper
}
}
Item {
// This is a settingsDirtyToastMessage placeholder
width: settingsDirtyToastMessage.implicitWidth
height: settingsDirtyToastMessage.active ? settingsDirtyToastMessage.implicitHeight : 0
Behavior on implicitHeight {
NumberAnimation {
duration: 150
easing.type: Easing.InOutQuad
}
}
}
}
}
}
SettingsDirtyToastMessage {
id: settingsDirtyToastMessage
anchors.bottom: scrollView.bottom
anchors.horizontalCenter: scrollView.horizontalCenter
active: root.dirty
flickable: contentFliackable
saveChangesButtonEnabled: root.saveChangesButtonEnabled
onResetChangesClicked: root.resetChangesClicked()
onSaveChangesClicked: root.saveChangesClicked()
}
}

View File

@ -0,0 +1,35 @@
import QtQuick 2.13
import QtGraphicalEffects 1.14
import shared.views 1.0 as SharedViews
import StatusQ.Core.Theme 0.1
Item {
property alias profileStore: profilePreview.profileStore
implicitHeight: profilePreview.implicitHeight
+ profilePreview.anchors.topMargin
+ profilePreview.anchors.bottomMargin
implicitWidth: profilePreview.implicitWidth
+ profilePreview.anchors.leftMargin
+ profilePreview.anchors.rightMargin
SharedViews.ProfileView {
id: profilePreview
anchors.fill: parent
anchors.margins: 24
}
DropShadow {
id: shadow
anchors.fill: profilePreview
horizontalOffset: 0
verticalOffset: 4
radius: 16
samples: 12
color: "#40000000"
source: profilePreview
}
}

View File

@ -0,0 +1,192 @@
import QtQuick 2.13
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.13
import utils 1.0
import shared 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 "../../../Onboarding/shared" as OnboardingComponents
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
ColumnLayout {
id: root
property PrivacyStore privacyStore
property ProfileStore profileStore
property WalletStore walletStore
readonly property bool dirty: displayNameInput.text != profileStore.displayName
|| biometricsSwitch.checked != biometricsSwitch.currentStoredValue
readonly property bool valid: !!displayNameInput.text && displayNameInput.valid
function reset() {
displayNameInput.text = Qt.binding(() => { return profileStore.displayName })
biometricsSwitch.checked = Qt.binding(() => { return biometricsSwitch.currentStoredValue })
}
function save() {
profileStore.setDisplayName(displayNameInput.text)
if (biometricsSwitch.checked)
Global.openPopup(storePasswordModal)
else
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever;
}
function offerToStorePassword(password, runStoreToKeyChainPopup)
{
if (Qt.platform.os !== "osx")
return;
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
root.privacyStore.storeToKeyChain(password);
}
ProfileHeader {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
displayName: profileStore.name
pubkey: profileStore.pubkey
icon: profileStore.icon
imageSize: ProfileHeader.ImageSize.Big
displayNameVisible: false
pubkeyVisible: false
emojiHashVisible: false
editImageButtonVisible: true
}
StatusInput {
id: displayNameInput
Layout.fillWidth: true
label: qsTr("Display name")
input.placeholderText: qsTr("Display Name")
charLimit: 24
input.text: root.profileStore.displayName
validators: Constants.validators.displayName
}
StatusListItem {
Layout.fillWidth: true
visible: Qt.platform.os == "osx"
title: qsTr("Biometric login and transaction authentication")
icon.name: "touch-id"
components: [ StatusSwitch {
id: biometricsSwitch
readonly property bool currentStoredValue: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueStore
checked: currentStoredValue
} ]
sensor.onClicked: biometricsSwitch.toggle()
}
StatusTabBar {
id: showcaseTabBar
Layout.fillWidth: true
function validateCurrentIndex() {
let processedButtons = 0;
while (!itemAt(currentIndex).enabled) {
if (++processedButtons == count) {
currentIndex = -1;
break;
}
currentIndex = (currentIndex + 1) % count;
}
}
StatusTabButton {
enabled: localAccountSensitiveSettings.communitiesEnabled
width: enabled ? implicitWidth : 0
text: qsTr("Communities")
onEnabledChanged: showcaseTabBar.validateCurrentIndex()
}
StatusTabButton {
enabled: localAccountSensitiveSettings.isWalletEnabled
width: enabled ? implicitWidth : 0
text: qsTr("Accounts")
onEnabledChanged: showcaseTabBar.validateCurrentIndex()
}
}
StackLayout {
Layout.fillWidth: true
currentIndex: showcaseTabBar.currentIndex
Column {
Layout.fillWidth: true
StatusBaseText {
visible: communitiesRepeater.count == 0
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
color: Theme.palette.directColor1
text: qsTr("You haven't joined any communities yet")
}
Repeater {
id: communitiesRepeater
model: communitiesModule.model
CommunityDelegate {
width: parent.width
visible: joined
community: model
enabled: false
}
}
}
Column {
StatusBaseText {
visible: accountsRepeater.count == 0
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
color: Theme.palette.directColor1
text: qsTr("You don't have any wallet accounts yet")
}
Repeater {
id: accountsRepeater
model: root.walletStore.accounts
WalletAccountDelegate {
width: parent.width
account: model
showShevronIcon: false
enabled: false
}
}
}
}
Component {
id: storePasswordModal
OnboardingComponents.CreatePasswordModal {
privacyStore: root.privacyStore
storingPasswordModal: true
onOfferToStorePassword: {
root.offerToStorePassword(password, runStoreToKeychainPopup)
}
}
}
}

View File

@ -98,7 +98,7 @@ Item {
}
onOpenChangeProfilePicPopup: {
var popup = changeProfilePicComponent.createObject(appMain);
popup.open();
popup.chooseImageToCrop();
}
onOpenBackUpSeedPopup: {
var popup = backupSeedModalComponent.createObject(appMain)
@ -196,8 +196,16 @@ Item {
}
property Component changeProfilePicComponent: Component {
ChangeProfilePicModal {
profileStore: appMain.rootStore.profileSectionStore.profileStore
ImageCropWorkflow {
title: qsTr("Profile Picture")
acceptButtonText: qsTr("Make this my Profile Pic")
onImageCropped: {
appMain.rootStore.profileSectionStore.profileStore.uploadImage(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed());
}
}
}

View File

@ -5,27 +5,47 @@ import utils 1.0
import shared.panels 1.0
import shared.controls 1.0
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
Item {
id: root
enum ImageSize {
Compact,
Middle,
Big
}
property string displayName
property string pubkey
property string icon
property bool compact: true
property int imageSize: ProfileHeader.ImageSize.Compact
property bool displayNameVisible: true
property bool pubkeyVisible: true
property alias imageOverlay: imageOverlay.sourceComponent
property bool emojiHashVisible: true
property bool editImageButtonVisible: false
readonly property bool compact: root.imageSize == ProfileHeader.ImageSize.Compact
signal clicked()
height: visible ? contentContainer.height : 0
implicitHeight: contentContainer.implicitHeight
QtObject {
id: d
function getSize(compact, normal, big) {
switch(root.imageSize) {
case ProfileHeader.ImageSize.Compact: return compact;
case ProfileHeader.ImageSize.Middle: return normal;
case ProfileHeader.ImageSize.Big: return big;
return normal;
}
}
}
ColumnLayout {
id: contentContainer
@ -38,21 +58,36 @@ Item {
rightMargin: Style.current.smallPadding
}
UserImage {
id: userImage
Item {
Layout.alignment: Qt.AlignHCenter
implicitWidth: userImage.width
implicitHeight: userImage.height
name: root.displayName
pubkey: root.pubkey
image: root.icon
interactive: false
imageWidth: root.compact ? 36 : 80
imageHeight: imageWidth
UserImage {
id: userImage
name: root.displayName
pubkey: root.pubkey
image: root.icon
interactive: false
imageWidth: d.getSize(36, 80, 160)
imageHeight: imageWidth
}
Loader {
id: imageOverlay
anchors.fill: parent
StatusRoundButton {
visible: root.editImageButtonVisible
anchors.bottom: userImage.bottom
anchors.right: userImage.right
anchors.rightMargin: Math.round(userImage.width / 10)
width: d.getSize(10, 24, 40)
height: width
type: StatusRoundButton.Type.Secondary
icon.name: "edit_pencil"
icon.width: d.getSize(8, 12, 20)
icon.height: d.getSize(8, 12, 20)
onClicked: Global.openChangeProfilePicPopup()
}
}
@ -87,9 +122,8 @@ Item {
EmojiHash {
id: emojiHash
Layout.alignment: Qt.AlignHCenter
visible: root.emojiHashVisible
publicKey: root.pubkey
size: root.compact ? 16 : 20
}

View File

@ -7,6 +7,7 @@ import utils 1.0
import shared 1.0
import shared.popups 1.0
import shared.stores 1.0
import shared.views 1.0 as SharedViews
import shared.controls.chat 1.0
import StatusQ.Core 0.1
@ -29,23 +30,18 @@ StatusModal {
property string userNickname: ""
property string userEnsName: ""
property string userIcon: ""
property string text: ""
property bool userIsEnsVerified: false
property bool userIsBlocked: false
property bool isCurrentUser: false
property bool isAddedContact: false
signal blockButtonClicked(name: string, address: string)
signal unblockButtonClicked(name: string, address: string)
signal removeButtonClicked(address: string)
signal contactUnblocked(publicKey: string)
signal contactBlocked(publicKey: string)
function openPopup(publicKey, state = "") {
// All this should be improved more, but for now we leave it like this.
let contactDetails = Utils.getContactDetailsAsJson(publicKey);
const contactDetails = Utils.getContactDetailsAsJson(publicKey);
userPublicKey = publicKey;
userDisplayName = contactDetails.displayName;
userName = contactDetails.alias;
@ -55,9 +51,8 @@ StatusModal {
userIsEnsVerified = contactDetails.ensVerified;
userIsBlocked = contactDetails.isBlocked;
isAddedContact = contactDetails.isContact;
text = ""; // this is most likely unneeded
isCurrentUser = popup.profileStore.pubkey === publicKey;
showFooter = !isCurrentUser;
popup.open();
@ -73,20 +68,21 @@ StatusModal {
}
function blockUser() {
contentItem.blockContactConfirmationDialog.contactName = userName;
contentItem.blockContactConfirmationDialog.contactAddress = userPublicKey;
contentItem.blockContactConfirmationDialog.open();
profileView.blockContactConfirmationDialog.contactName = userName;
profileView.blockContactConfirmationDialog.contactAddress = userPublicKey;
profileView.blockContactConfirmationDialog.open();
}
function unblockUser() {
contentItem.unblockContactConfirmationDialog.contactName = userName;
contentItem.unblockContactConfirmationDialog.contactAddress = userPublicKey;
contentItem.unblockContactConfirmationDialog.open();
profileView.unblockContactConfirmationDialog.contactName = userName;
profileView.unblockContactConfirmationDialog.contactAddress = userPublicKey;
profileView.unblockContactConfirmationDialog.open();
}
header.title: userDisplayName + qsTr("'s Profile")
header.subTitle: userIsEnsVerified ? userName : Utils.getElidedCompressedPk(userPublicKey)
header.subTitleElide: Text.ElideMiddle
padding: 8
QtObject {
id: d
@ -103,213 +99,47 @@ StatusModal {
icon.width: 20
icon.height: 20
icon.name: "qr"
onClicked: contentItem.qrCodePopup.open()
onClicked: profileView.qrCodePopup.open()
}
Component {
id: contactTopComponent
SharedViews.ProfileView {
id: profileView
anchors.fill: parent
ProfileHeader {
displayName: popup.userDisplayName
pubkey: popup.userPublicKey
icon: popup.isCurrentUser ? popup.profileStore.icon : popup.userIcon
profileStore: popup.profileStore
contactsStore: popup.contactsStore
displayNameVisible: false
pubkeyVisible: false
compact: false
userPublicKey: popup.userPublicKey
userDisplayName: popup.userDisplayName
userName: popup.userName
userNickname: popup.userNickname
userEnsName: popup.userEnsName
userIcon: popup.userIcon
userIsEnsVerified: popup.userIsEnsVerified
userIsBlocked: popup.userIsBlocked
isAddedContact: popup.isAddedContact
isCurrentUser: popup.isCurrentUser
imageOverlay: Item {
visible: popup.isCurrentUser
StatusFlatRoundButton {
width: 24
height: 24
anchors {
right: parent.right
bottom: parent.bottom
rightMargin: -8
}
type: StatusFlatRoundButton.Type.Secondary
icon.name: "pencil"
icon.color: Theme.palette.directColor1
icon.width: 12.5
icon.height: 12.5
onClicked: Global.openChangeProfilePicPopup()
}
}
}
}
contentItem: ColumnLayout {
id: modalContent
property alias qrCodePopup: qrCodePopup
property alias unblockContactConfirmationDialog: unblockContactConfirmationDialog
property alias blockContactConfirmationDialog: blockContactConfirmationDialog
property alias removeContactConfirmationDialog: removeContactConfirmationDialog
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: d.contentMargins
spacing: d.contentSpacing
clip: true
Item {
implicitHeight: d.contentSpacing
Layout.fillWidth: true
}
Loader {
sourceComponent: contactTopComponent
Layout.fillWidth: true
}
StatusBanner {
visible: popup.userIsBlocked
type: StatusBanner.Type.Danger
statusText: qsTr("Blocked")
Layout.fillWidth: true
}
StatusDescriptionListItem {
title: userIsEnsVerified ?
qsTr("ENS username") :
qsTr("Username")
subTitle: userIsEnsVerified ? userEnsName : userName
tooltip.text: qsTr("Copy to clipboard")
icon.name: "copy"
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.visible = !tooltip.visible
}
Layout.fillWidth: true
}
StatusDescriptionListItem {
title: qsTr("Chat key")
subTitle: Utils.getCompressedPk(userPublicKey)
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: {
globalUtils.copyToClipboard(subTitle)
tooltip.visible = !tooltip.visible
}
Layout.fillWidth: true
}
StatusDescriptionListItem {
title: qsTr("Share Profile URL")
subTitle: {
let user = ""
if (isCurrentUser) {
user = popup.profileStore.ensName !== "" ? popup.profileStore.ensName :
(popup.profileStore.pubkey.substring(0, 5) + "..." + popup.profileStore.pubkey.substring(popup.profileStore.pubkey.length - 5))
} else if (userIsEnsVerified) {
user = userEnsName
}
if (user === ""){
user = userPublicKey.substr(0, 4) + "..." + userPublicKey.substr(userPublicKey.length - 5)
}
return Constants.userLinkPrefix + user;
}
tooltip.text: qsTr("Copy to clipboard")
icon.name: "copy"
iconButton.onClicked: {
let user = ""
if (isCurrentUser) {
user = popup.profileStore.ensName !== "" ? popup.profileStore.ensName : popup.profileStore.pubkey
} else {
user = (userEnsName !== "" ? userEnsName : userPublicKey)
}
popup.profileStore.copyToClipboard(Constants.userLinkPrefix + user)
tooltip.visible = !tooltip.visible
}
Layout.fillWidth: true
}
StatusDescriptionListItem {
visible: !isCurrentUser
title: qsTr("Chat settings")
subTitle: qsTr("Nickname")
value: userNickname ? userNickname : qsTr("None")
sensor.enabled: true
sensor.onClicked: {
nicknamePopup.open()
}
Layout.fillWidth: true
}
}
// TODO: replace with StatusStackModal
ModalPopup {
id: qrCodePopup
width: 320
height: 320
Image {
asynchronous: true
fillMode: Image.PreserveAspectFit
source: globalUtils.qrCode(userPublicKey)
anchors.horizontalCenter: parent.horizontalCenter
height: 212
width: 212
mipmap: true
smooth: false
}
}
UnblockContactConfirmationDialog {
id: unblockContactConfirmationDialog
onUnblockButtonClicked: {
popup.contactsStore.unblockContact(userPublicKey)
unblockContactConfirmationDialog.close();
onContactUnblocked: {
popup.close()
popup.contactUnblocked(userPublicKey)
popup.contactUnblocked(publicKey)
}
}
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
popup.contactsStore.blockContact(userPublicKey)
blockContactConfirmationDialog.close();
onContactBlocked: {
popup.close()
popup.contactBlocked(userPublicKey)
popup.contactBlocked(publicKey)
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr("Remove contact")
confirmationText: qsTr("Are you sure you want to remove this contact?")
onConfirmButtonClicked: {
if (isAddedContact) {
popup.contactsStore.removeContact(userPublicKey);
}
removeContactConfirmationDialog.close();
popup.close();
onContactAdded: {
popup.close()
popup.contactAdded(publicKey)
}
}
NicknamePopup {
id: nicknamePopup
nickname: popup.userNickname
header.subTitle: popup.header.subTitle
header.subTitleElide: popup.header.subTitleElide
onEditDone: {
if(popup.userNickname !== newNickname)
{
popup.userNickname = newNickname;
popup.contactsStore.changeContactNickname(userPublicKey, newNickname);
}
onContactRemoved: {
popup.close()
}
onNicknameEdited: {
popup.close()
}
}
@ -322,7 +152,9 @@ StatusModal {
height: popup.height
visible: false
header.title: qsTr("Send Contact Request to") + " " + userDisplayName
topComponent: contactTopComponent
userPublicKey: popup.userPublicKey
userDisplayName: popup.userDisplayName
userIcon: popup.userIcon
onAccepted: popup.contactsStore.sendContactRequest(userPublicKey, message)
onClosed: popup.close()
}
@ -341,8 +173,8 @@ StatusModal {
type: StatusBaseButton.Type.Danger
text: qsTr('Remove Contact')
onClicked: {
contentItem.removeContactConfirmationDialog.parentPopup = popup;
contentItem.removeContactConfirmationDialog.open();
profileView.removeContactConfirmationDialog.parentPopup = popup;
profileView.removeContactConfirmationDialog.open();
}
},

View File

@ -3,6 +3,7 @@ import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import utils 1.0
import shared.controls.chat 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
@ -13,9 +14,13 @@ import StatusQ.Popups 0.1
StatusModal {
id: root
property string userPublicKey: ""
property string userDisplayName: ""
property string userIcon: ""
signal accepted(string message)
property alias topComponent: topComponentLoader.sourceComponent
padding: 16
QtObject {
id: d
@ -24,19 +29,24 @@ StatusModal {
readonly property int minMsgLength: 1
readonly property int msgHeight: 152
readonly property int contentSpacing: 5
readonly property int contentMargins: 16
}
ColumnLayout {
id: content
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: d.contentMargins
anchors.fill: parent
spacing: d.contentSpacing
Loader {
id: topComponentLoader
ProfileHeader {
Layout.fillWidth: true
displayName: root.userDisplayName
pubkey: root.userPublicKey
icon: root.userIcon
displayNameVisible: false
pubkeyVisible: false
imageSize: ProfileHeader.ImageSize.Middle
editImageButtonVisible: false
}
StatusInput {
@ -65,4 +75,4 @@ StatusModal {
root.close();
}
}
}
}

View File

@ -0,0 +1,125 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.13
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Rectangle {
id: root
property bool active: false
property bool saveChangesButtonEnabled: false
property Flickable flickable: null
signal saveChangesClicked
signal resetChangesClicked
function notifyDirty() {
toastAlertAnimation.running = true
saveChangesButton.forceActiveFocus()
}
implicitHeight: toastContent.implicitHeight + toastContent.anchors.topMargin + toastContent.anchors.bottomMargin
implicitWidth: toastContent.implicitWidth + toastContent.anchors.leftMargin + toastContent.anchors.rightMargin
opacity: active ? 1 : 0
color: Theme.palette.statusToastMessage.backgroundColor
radius: 8
border.color: Theme.palette.dangerColor2
border.width: 2
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: Theme.palette.dangerColor2
spread: 0.1
}
onActiveChanged: {
if (!active || !flickable)
return;
const item = Global.applicationWindow.activeFocusItem;
const h1 = this.height;
const y1 = this.mapToGlobal(0, 0).y;
const h2 = item.height;
const y2 = item.mapToGlobal(0, 0).y;
const margin = 20;
const offset = h2 - (y1 - y2);
if (offset <= 0)
return;
toastFlickAnimation.from = flickable.contentY;
toastFlickAnimation.to = flickable.contentY + offset + margin;
toastFlickAnimation.start()
}
NumberAnimation {
id: toastFlickAnimation
target: root.flickable
property: "contentY"
duration: 150
easing.type: Easing.InOutQuad
}
NumberAnimation on border.width {
id: toastAlertAnimation
from: 0
to: 4
loops: 2
duration: 600
onFinished: root.border.width = 2
}
Behavior on opacity {
NumberAnimation {}
}
MouseArea {
anchors.fill: parent
visible: root.active // This is required not to change cursorShape
enabled: root.active
hoverEnabled: true
}
RowLayout {
id: toastContent
anchors {
fill: parent
margins: 16
}
StatusBaseText {
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 8
horizontalAlignment: Text.AlignHCenter
color: Theme.palette.directColor1
text: qsTr("Changes detected")
}
StatusButton {
text: qsTr("Cancel")
enabled: root.active
type: StatusBaseButton.Type.Danger
onClicked: root.resetChangesClicked()
}
StatusButton {
id: saveChangesButton
enabled: root.active && root.saveChangesButtonEnabled
text: qsTr("Save changes")
onClicked: root.saveChangesClicked()
}
}
}

View File

@ -1,11 +1,13 @@
ChatCommandsPopup 1.0 ChatCommandsPopup.qml
BlockContactConfirmationDialog 1.0 BlockContactConfirmationDialog.qml
SettingsDirtyToastMessage 1.0 SettingsDirtyToastMessage.qml
ConfirmationDialog 1.0 ConfirmationDialog.qml
CommunityIntroDialog 1.0 CommunityIntroDialog.qml
DownloadModal 1.0 DownloadModal.qml
DownloadPage 1.0 DownloadPage.qml
ImageCropperModal 1.0 ImageCropperModal.qml
InviteFriendsPopup 1.0 InviteFriendsPopup.qml
NicknamePopup 1.0 NicknamePopup.qml
ModalPopup 1.0 ModalPopup.qml
PopupMenu 1.0 PopupMenu.qml
SendModal 1.0 SendModal.qml

View File

@ -0,0 +1,238 @@
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.popups 1.0
import shared.stores 1.0
import shared.controls.chat 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
Rectangle {
id: root
property Popup parentPopup
property var profileStore
property var contactsStore
property string userPublicKey: profileStore.pubkey
property string userDisplayName: profileStore.displayName
property string userName: profileStore.username
property string userNickname: profileStore.details.localNickname
property string userEnsName: profileStore.ensName
property string userIcon: profileStore.profileLargeImage
property string text: ""
property bool userIsEnsVerified: profileStore.details.ensVerified
property bool userIsBlocked: profileStore.details.isBlocked
property bool isCurrentUser: profileStore.pubkey === userPublicKey
property bool isAddedContact: false
readonly property alias qrCodePopup: qrCodePopup
readonly property alias unblockContactConfirmationDialog: unblockContactConfirmationDialog
readonly property alias blockContactConfirmationDialog: blockContactConfirmationDialog
readonly property alias removeContactConfirmationDialog: removeContactConfirmationDialog
signal contactUnblocked(publicKey: string)
signal contactBlocked(publicKey: string)
signal contactAdded(publicKey: string)
signal contactRemoved(publicKey: string)
signal nicknameEdited(publicKey: string)
implicitWidth: modalContent.implicitWidth
implicitHeight: modalContent.implicitHeight
color: Theme.palette.statusAppLayout.backgroundColor
radius: 8
QtObject {
id: d
readonly property string subTitle: root.userIsEnsVerified ? root.userName : Utils.getElidedCompressedPk(userPublicKey)
readonly property int subTitleElide: Text.ElideMiddle
}
ColumnLayout {
id: modalContent
anchors.fill: parent
Item {
Layout.fillWidth: true
implicitHeight: 16
}
ProfileHeader {
Layout.fillWidth: true
displayName: root.userDisplayName
pubkey: root.userPublicKey
icon: root.isCurrentUser ? root.profileStore.icon : root.userIcon
displayNameVisible: false
pubkeyVisible: false
imageSize: ProfileHeader.ImageSize.Middle
editImageButtonVisible: root.isCurrentUser
}
StatusBanner {
Layout.fillWidth: true
visible: root.userIsBlocked
type: StatusBanner.Type.Danger
statusText: qsTr("Blocked")
}
Item {
Layout.fillWidth: true
implicitHeight: 16
}
StatusDescriptionListItem {
Layout.fillWidth: true
title: root.userIsEnsVerified ? qsTr("ENS username") : qsTr("Username")
subTitle: root.userIsEnsVerified ? root.userEnsName : root.userName
tooltip.text: qsTr("Copied to clipboard")
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.open();
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
title: qsTr("Chat key")
subTitle: Utils.getCompressedPk(root.userPublicKey)
subTitleComponent.elide: Text.ElideMiddle
subTitleComponent.width: 320
subTitleComponent.font.family: Theme.palette.monoFont.name
tooltip.text: qsTr("Copied to clipboard")
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.open();
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
title: qsTr("Share Profile URL")
subTitle: {
let user = ""
if (isCurrentUser) {
user = root.profileStore.ensName !== "" ? root.profileStore.ensName
: (root.profileStore.pubkey.substring(0, 5) + "..." + root.profileStore.pubkey.substring(root.profileStore.pubkey.length - 5))
} else if (userIsEnsVerified) {
user = userEnsName
}
if (user === ""){
user = userPublicKey.substr(0, 4) + "..." + userPublicKey.substr(userPublicKey.length - 5)
}
return Constants.userLinkPrefix + user;
}
tooltip.text: qsTr("Copied to clipboard")
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
let user = ""
if (isCurrentUser) {
user = root.profileStore.ensName !== "" ? root.profileStore.ensName : root.profileStore.pubkey
} else {
user = (userEnsName !== "" ? userEnsName : userPublicKey)
}
root.profileStore.copyToClipboard(Constants.userLinkPrefix + user)
tooltip.open();
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
visible: !isCurrentUser
title: qsTr("Chat settings")
subTitle: qsTr("Nickname")
value: userNickname ? userNickname : qsTr("None")
sensor.enabled: true
sensor.onClicked: {
nicknamePopup.open()
}
}
Item {
Layout.fillWidth: true
visible: !isCurrentUser
implicitHeight: 16
}
}
// TODO: replace with StatusModal
ModalPopup {
id: qrCodePopup
width: 320
height: 320
Image {
asynchronous: true
fillMode: Image.PreserveAspectFit
source: globalUtils.qrCode(userPublicKey)
anchors.horizontalCenter: parent.horizontalCenter
height: 212
width: 212
mipmap: true
smooth: false
}
}
UnblockContactConfirmationDialog {
id: unblockContactConfirmationDialog
onUnblockButtonClicked: {
root.contactsStore.unblockContact(userPublicKey)
unblockContactConfirmationDialog.close();
root.contactUnblocked(userPublicKey)
}
}
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
root.contactsStore.blockContact(userPublicKey)
blockContactConfirmationDialog.close();
root.contactBlocked(userPublicKey)
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr("Remove contact")
confirmationText: qsTr("Are you sure you want to remove this contact?")
onConfirmButtonClicked: {
if (isAddedContact) {
root.contactsStore.removeContact(userPublicKey);
}
removeContactConfirmationDialog.close();
root.contactRemoved(userPublicKey)
}
}
NicknamePopup {
id: nicknamePopup
nickname: root.userNickname
header.subTitle: d.subTitle
header.subTitleElide: d.subTitleElide
onEditDone: {
if (root.userNickname !== newNickname)
{
root.userNickname = newNickname;
root.contactsStore.changeContactNickname(userPublicKey, newNickname);
}
root.nicknameEdited(userPublicKey)
}
}
}

View File

@ -6,5 +6,6 @@ TransactionPreview 1.0 TransactionPreview.qml
TransactionSigner 1.0 TransactionSigner.qml
TransactionStackView 1.0 TransactionStackView.qml
PasswordView 1.0 PasswordView.qml
ProfileView 1.0 ProfileView.qml
AssetsView 1.0 AssetsView.qml
HistoryView 1.0 HistoryView.qml

View File

@ -37,17 +37,16 @@ QtObject {
property int ensUsernames: 2
property int messaging: 3
property int wallet: 4
property int privacyAndSecurity: 5
property int appearance: 6
property int language: 7
property int notifications: 8
property int devicesSettings: 9
property int browserSettings: 10
property int advanced: 11
property int about: 12
property int communitiesSettings: 13
property int signout: 14
property int backUpSeed: 15
property int appearance: 5
property int language: 6
property int notifications: 7
property int devicesSettings: 8
property int browserSettings: 9
property int advanced: 10
property int about: 11
property int communitiesSettings: 12
property int signout: 13
property int backUpSeed: 14
}
readonly property QtObject userStatus: QtObject{