diff --git a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml index 8b8475dd15..23dff7da21 100644 --- a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml @@ -38,7 +38,6 @@ Item { property bool isReply: false property bool isImage: false property bool isExtendedInput: isReply || isImage - property bool isConnected: false property string contactToRemove: "" property bool isSectionActive: mainModule.activeSection.id === parentModule.getMySectionId() property string activeChatId: parentModule && parentModule.activeItem.id @@ -217,7 +216,6 @@ Item { rootStore: root.rootStore contactsStore: root.contactsStore emojiPopup: root.emojiPopup - isConnected: root.isConnected sendTransactionNoEnsModal: cmpSendTransactionNoEns receiveTransactionModal: cmpReceiveTransaction sendTransactionWithEnsModal: cmpSendTransactionWithEns @@ -266,7 +264,6 @@ Item { clip: true rootStore: root.rootStore contactsStore: root.contactsStore - isConnected: root.isConnected emojiPopup: root.emojiPopup sendTransactionNoEnsModal: cmpSendTransactionNoEns receiveTransactionModal: cmpReceiveTransaction diff --git a/ui/app/AppLayouts/Chat/views/ChatContentView.qml b/ui/app/AppLayouts/Chat/views/ChatContentView.qml index d30023c2ad..7b94232a8a 100644 --- a/ui/app/AppLayouts/Chat/views/ChatContentView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatContentView.qml @@ -35,7 +35,6 @@ ColumnLayout { property var rootStore property var contactsStore property bool isActiveChannel: false - property bool isConnected: false property var emojiPopup property alias textInputField: chatInput property UsersStore usersStore: UsersStore {} @@ -64,52 +63,6 @@ ColumnLayout { } } - Rectangle { - id: connectedStatusRect - Layout.fillWidth: true - height: 40 - Layout.alignment: Qt.AlignHCenter - z: 60 - visible: false - color: isConnected ? Style.current.green : Style.current.darkGrey - Text { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - color: Style.current.white - id: connectedStatusLbl - text: isConnected ? - qsTr("Connected") : - qsTr("Disconnected") - } - - Connections { - target: mainModule - onOnlineStatusChanged: { - if (connected === isConnected) return; - isConnected = connected; - if(isConnected) { - onlineStatusTimer.start(); - } else { - connectedStatusRect.visible = true; - } - } - } - Component.onCompleted: { - isConnected = mainModule.isOnline - if(!isConnected){ - connectedStatusRect.visible = true - } - } - } - - Timer { - id: onlineStatusTimer - interval: 5000 - onTriggered: { - connectedStatusRect.visible = false; - } - } - StatusBanner { Layout.fillWidth: true visible: root.isBlocked diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml index 01c596ea86..c45d92a658 100644 --- a/ui/app/AppLayouts/Profile/ProfileLayout.qml +++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml @@ -30,7 +30,7 @@ StatusSectionLayout { QtObject { id: d - readonly property int topMargin: secureYourSeedPhrase.visible ? secureYourSeedPhrase.height : 0 + readonly property int topMargin: 0 readonly property int bottomMargin: 56 readonly property int leftMargin: 48 readonly property int rightMargin: 48 @@ -53,50 +53,13 @@ StatusSectionLayout { centerPanel: Item { anchors.fill: parent - ModuleWarning { - id: secureYourSeedPhrase - width: parent.width - visible: { - if (profileContainer.currentIndex !== Constants.settingsSubsection.profile) { - return false - } - if (root.store.profileStore.userDeclinedBackupBanner) { - return false - } - return !root.store.profileStore.privacyStore.mnemonicBackedUp - } - color: Style.current.red - btnWidth: 100 - text: qsTr("Secure your seed phrase") - btnText: qsTr("Back up now") - - onClick: function(){ - Global.openBackUpSeedPopup(); - } - - onClosed: { - root.store.profileStore.userDeclinedBackupBanner = true - } - } - - StatusBanner { - id: banner - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: visible ? childrenRect.height : 0 - visible: profileContainer.currentIndex === Constants.settingsSubsection.wallet && - root.store.walletStore.areTestNetworksEnabled - type: StatusBanner.Type.Danger - statusText: qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.") - } StackLayout { id: profileContainer readonly property var currentItem: (currentIndex >= 0 && currentIndex < children.length) ? children[currentIndex] : null - anchors.top: banner.visible? banner.bottom : parent.top + anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index a15a11340f..f0c91d55fc 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -5,6 +5,7 @@ import QtMultimedia 5.13 import Qt.labs.qmlmodels 1.0 import Qt.labs.platform 1.1 import Qt.labs.settings 1.0 +import QtQml.Models 2.14 import AppLayouts.Wallet 1.0 import AppLayouts.Node 1.0 @@ -28,6 +29,7 @@ import StatusQ.Components 0.1 import StatusQ.Controls 0.1 import StatusQ.Layout 0.1 import StatusQ.Popups 0.1 +import StatusQ.Popups.Dialog 0.1 import StatusQ.Core 0.1 import AppLayouts.Browser.stores 1.0 as BrowserStores @@ -46,18 +48,6 @@ Item { // set from main.qml property var sysPalette - Connections { - target: rootStore.aboutModuleInst - onAppVersionFetched: { - rootStore.setLatestVersionInfo(available, version, url); - if (!available) { - versionUpToDate.show() - } else { - versionWarning.show() - } - } - } - Connections { target: rootStore.mainModuleInst onDisplayUserProfile: Global.openProfilePopup(publicKey) @@ -399,53 +389,188 @@ Item { spacing: 0 objectName: "mainRightView" - ModuleWarning { - id: versionWarning - width: parent.width - height: 32 - visible: appMain.rootStore.newVersionAvailable - color: Style.current.green - btnWidth: 100 - text: qsTr("A new version of Status (%1) is available").arg(appMain.rootStore.latestVersion) - btnText: qsTr("Download") - onClick: function(){ - Global.openDownloadModal(appMain.rootStore.newVersionAvailable, appMain.rootStore.latestVersion, appMain.rootStore.downloadURL) - } - onClosed: { - appMain.rootStore.resetLastVersion(); + ColumnLayout { + id: bannersLayout + + property var updateBanner: null + property var connectedBanner: null + readonly property bool isConnected: mainModule.isOnline + + function processUpdateAvailable() { + if (!updateBanner) + updateBanner = updateBannerComponent.createObject(this) } - function show() { - versionWarning.visible = true + function processConnected() { + if (!connectedBanner) + connectedBanner = connectedBannerComponent.createObject(this) } + + Layout.fillWidth: true + Layout.maximumHeight: implicitHeight + spacing: 1 + + onIsConnectedChanged: { + processConnected() + } + + Connections { + target: rootStore.aboutModuleInst + onAppVersionFetched: { + rootStore.setLatestVersionInfo(available, version, url); + bannersLayout.processUpdateAvailable() + } + } + + ModuleWarning { + id: testnetBanner + Layout.fillWidth: true + text: qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.") + buttonText: qsTr("Turn off") + type: ModuleWarning.Danger + active: appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled + + onClicked: { + testnetBannerDialog.open() + } + + onCloseClicked: { + testnetBannerDialog.open() + } + + StatusDialog { + id: testnetBannerDialog + + width: 400 + title: qsTr("Turn off Testnet mode") + + StatusBaseText { + anchors.fill: parent + text: qsTr("Closing this banner will turn off Testnet mode.\nAll future transactions will be on mainnet or other active networks.") + font.pixelSize: 15 + wrapMode: Text.WordWrap + } + + footer: StatusDialogFooter { + rightButtons: ObjectModel { + StatusButton { + type: StatusButton.Danger + text: qsTr("Turn off Testnet") + onClicked: { + appMain.rootStore.profileSectionStore.walletStore.toggleTestNetworksEnabled() + testnetBannerDialog.close() + } + } + } + } + } + } + + ModuleWarning { + id: secureYourSeedPhrase + Layout.fillWidth: true + active: !appMain.rootStore.profileSectionStore.profileStoree.userDeclinedBackupBanner + && !appMain.rootStore.profileSectionStore.profileStore.privacyStore.mnemonicBackedUp + type: ModuleWarning.Danger + text: qsTr("Secure your seed phrase") + buttonText: qsTr("Back up now") + + onClicked: { + Global.openBackUpSeedPopup(); + } + + onCloseClicked: { + appMain.rootStore.profileSectionStore.profileStore.userDeclinedBackupBanner = true + } + } + + Component { + id: connectedBannerComponent + + ModuleWarning { + readonly property bool isConnected: bannersLayout.isConnected + + Layout.fillWidth: true + text: isConnected ? qsTr("Connected") : qsTr("Disconnected") + type: isConnected ? ModuleWarning.Success : ModuleWarning.Danger + + function updateState() { + if (isConnected) + showFor() + else + show(); + } + + Component.onCompleted: { + updateState() + } + onIsConnectedChanged: { + updateState(); + } + onCloseClicked: { + hide(); + } + onHideStarted: { + bannersLayout.connectedBanner = null + } + onHideFinished: { + destroy() + } + } + } + + Component { + id: updateBannerComponent + + ModuleWarning { + readonly property string version: appMain.rootStore.latestVersion + readonly property bool updateAvailable: appMain.rootStore.newVersionAvailable + + Layout.fillWidth: true + type: ModuleWarning.Success + text: updateAvailable ? qsTr("A new version of Status (%1) is available").arg(version) + : qsTr("Your version is up to date") + + buttonText: updateAvailable ? qsTr("Update") + : qsTr("Close") + + function updateState() { + if (updateAvailable) + show() + else + showFor(5000) + } + + Component.onCompleted: { + updateState() + } + onUpdateAvailableChanged: { + updateState(); + } + onClicked: { + if (updateAvailable) + Global.openDownloadModal(appMain.rootStore.newVersionAvailable, + appMain.rootStore.latestVersion, + appMain.rootStore.downloadURL) + else + close() + } + onCloseClicked: { + if (updateAvailable) + appMain.rootStore.resetLastVersion(); + hide() + } + onHideStarted: { + bannersLayout.updateBanner = null + } + onHideFinished: { + destroy() + } + } + } + } - ModuleWarning { - id: versionUpToDate - width: parent.width - height: 32 - visible: false - color: Style.current.green - btnWidth: 100 - text: qsTr("Your version is up to date") - btnText: qsTr("Close") - - Timer { - id: timer - } - function show() { - versionUpToDate.visible = true - timer.setTimeout(function() { - versionUpToDate.close() - }, 4000); - } - - onClick: function(){ - versionUpToDate.close() - } - } - - Item { Layout.fillWidth: true Layout.fillHeight: true @@ -693,6 +818,7 @@ Item { } } } + } // ColumnLayout property var mailserverNotWorkingPopup: null diff --git a/ui/imports/shared/panels/ModuleWarning.qml b/ui/imports/shared/panels/ModuleWarning.qml index 95a5b6efc6..75b9d7c6ee 100644 --- a/ui/imports/shared/panels/ModuleWarning.qml +++ b/ui/imports/shared/panels/ModuleWarning.qml @@ -3,111 +3,188 @@ import QtQuick.Controls 2.13 import QtQuick.Layouts 1.13 import QtGraphicalEffects 1.13 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + import utils 1.0 import "../" import "./" -Rectangle { +Item { id: root objectName: "moduleWarningBanner" - height: visible ? 32 : 0 - implicitHeight: height - color: Style.current.red - property string text: "" - property string btnText: "" - property int btnWidth: 58 - property bool closing: false - - property var onClick: function() {} - - signal closed() - - function close() { - closeBtn.clicked(null) - closed(); + enum Type { + Danger, + Success } - Row { - spacing: Style.current.halfPadding - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter + property bool active: false + property int type: ModuleWarning.Danger + property string text: "" + property alias buttonText: button.text - StyledText { - text: root.text - font.pixelSize: 13 - anchors.verticalCenter: parent.verticalCenter - color: Style.current.white + signal clicked() + signal closeClicked() + signal showStarted() + signal showFinihsed() + signal hideStarted() + signal hideFinished() + + function show() { + hideTimer.stop() + active = true; + } + + function showFor(duration = 5000) { + show(); + hide(duration); + } + + function hide(timeout = 0) { + hideTimer.interval = timeout + hideTimer.start() + } + + function close() { + closeButtonMouseArea.clicked(null) + } + + implicitHeight: root.active ? content.implicitHeight : 0 + visible: implicitHeight > 0 + + onActiveChanged: { + active ? showAnimation.start() : hideAnimation.start() + } + + NumberAnimation { + id: showAnimation + target: root + property: "implicitHeight" + from: 0 + to: content.implicitHeight + duration: 500 + easing.type: Easing.OutCubic + onStarted: { + root.showStarted() + } + onFinished: { + root.showFinihsed() + } + } + + NumberAnimation { + id: hideAnimation + target: root + property: "implicitHeight" + to: 0 + from: content.implicitHeight + duration: 500 + easing.type: Easing.OutCubic + onStarted: { + root.hideStarted() + } + onFinished: { + root.hideFinished() + } + } + + Timer { + id: hideTimer + repeat: false + running: false + onTriggered: { + root.active = false + } + } + + Rectangle { + id: content + anchors.bottom: parent.bottom + width: parent.width + implicitHeight: 32 + + readonly property color baseColor: { + switch (root.type) { + case ModuleWarning.Danger: return Theme.palette.dangerColor1 + case ModuleWarning.Success: return Theme.palette.successColor1 + default: return Theme.palette.baseColor1 + } } - Button { - width: btnWidth - height: 24 - contentItem: Item { - anchors.fill: parent - Text { - text: btnText - font.pixelSize: 13 + color: baseColor + + Behavior on color { + ColorAnimation { + duration: 150 + } + } + + RowLayout { + id: layout + + spacing: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + StatusBaseText { + text: root.text + Layout.alignment: Qt.AlignVCenter + font.pixelSize: 13 + color: Theme.palette.indirectColor1 + } + + Button { + id: button + visible: text != "" + padding: 5 + onClicked: { + root.clicked() + } + contentItem: Text { + text: button.text + font.pixelSize: 12 font.weight: Font.Medium font.family: Style.current.fontRegular.name horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - color: Style.current.white + color: Theme.palette.indirectColor1 + } + background: Rectangle { + radius: 4 + border.width: 1 + border.color: Theme.palette.indirectColor3 + color: Theme.palette.getColor("white", button.hovered ? 0.4 : 0.1) + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor } } - background: Rectangle { - radius: 4 - anchors.fill: parent - border.color: Style.current.white - color: "#19FFFFFF" - } + } + + StatusIcon { + id: closeImg + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 18 + height: 20 + width: 20 + icon: "close-circle" + color: Theme.palette.indirectColor1 + opacity: closeButtonMouseArea.containsMouse ? 1 : 0.7 + MouseArea { - cursorShape: Qt.PointingHandCursor + id: closeButtonMouseArea anchors.fill: parent - onClicked: root.onClick() + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + root.closeClicked() + } } } } - SVGImage { - id: closeImg - anchors.top: parent.top - anchors.topMargin: 6 - anchors.right: parent.right - anchors.rightMargin: 18 - source: Style.svg("close-white") - height: 20 - width: 20 - } - ColorOverlay { - anchors.fill: closeImg - source: closeImg - color: Style.current.white - opacity: 0.7 - } - MouseArea { - id: closeBtn - anchors.fill: closeImg - cursorShape: Qt.PointingHandCursor - onClicked: { - closing = true - } - } - - ParallelAnimation { - running: closing - PropertyAnimation { target: root; property: "visible"; to: false; } - PropertyAnimation { target: root; property: "y"; to: -1 * root.height } - onRunningChanged: { - if(!running){ - closing = false; - root.y = 0; - root.closed(); - } - } - } }