diff --git a/storybook/pages/StatusFeeOptionPage.qml b/storybook/pages/StatusFeeOptionPage.qml
new file mode 100644
index 00000000000..80ab296d2b6
--- /dev/null
+++ b/storybook/pages/StatusFeeOptionPage.qml
@@ -0,0 +1,169 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import QtQuick.Controls 2.15
+
+import StatusQ.Controls 0.1
+import StatusQ.Core.Theme 0.1
+
+import Storybook 1.0
+
+SplitView {
+ orientation: Qt.Horizontal
+
+ Logs { id: logs }
+
+ Pane {
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+
+ background: Rectangle {
+ color: Theme.palette.baseColor4
+ }
+
+ StatusFeeOption {
+ id: feeOption
+ anchors.centerIn: parent
+
+ onClicked: {
+ console.warn("control clicked...")
+ selected = !selected
+ }
+ }
+ }
+
+ LogsAndControlsPanel {
+ SplitView.fillHeight: true
+ SplitView.preferredWidth: 300
+
+ logsView.logText: logs.logText
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ ComboBox {
+ model: [
+ {testCase: StatusFeeOption.Type.Normal, name: "Normal"},
+ {testCase: StatusFeeOption.Type.Fast, name: "Fast"},
+ {testCase: StatusFeeOption.Type.Urgent, name: "Urgent"},
+ {testCase: StatusFeeOption.Type.Custom, name: "Custom"}
+ ]
+
+ textRole: "name"
+ valueRole: "testCase"
+ onCurrentValueChanged: {
+ console.warn("valueRole: ", currentValue)
+ feeOption.type = currentValue
+ }
+ }
+
+ RowLayout {
+ TextField {
+ id: price
+ Layout.preferredWidth: 130
+ text: "1.45 EUR"
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+
+ Component.onCompleted: feeOption.subText = price.text
+ }
+
+ StatusButton {
+ text: "Set price"
+ onClicked: {
+ feeOption.subText = price.text
+ }
+ }
+ }
+
+ RowLayout {
+ TextField {
+ id: time
+ Layout.preferredWidth: 130
+ text: "~60s"
+
+ Component.onCompleted: feeOption.additionalText = time.text
+ }
+
+ StatusButton {
+ text: "Set time"
+ onClicked: {
+ feeOption.additionalText = time.text
+ }
+ }
+ }
+
+ RowLayout {
+ TextField {
+ id: unselectedText
+ Layout.preferredWidth: 130
+ text: "Set your own fees & nonce"
+
+ }
+
+ StatusButton {
+ text: "Set unselected text"
+ onClicked: {
+ feeOption.unselectedText = unselectedText.text
+ }
+ }
+ }
+
+ CheckBox {
+ text: "Show subtext"
+ checked: true
+
+ onCheckStateChanged: {
+ feeOption.showSubText = checked
+ }
+
+ Component.onCompleted: feeOption.showSubText = checked
+ }
+
+ CheckBox {
+ text: "Show additional text"
+ checked: true
+
+ onCheckStateChanged: {
+ feeOption.showAdditionalText = checked
+ }
+
+ Component.onCompleted: feeOption.showAdditionalText = checked
+ }
+
+ CheckBox {
+ text: "Show unselected text"
+ checked: false
+
+ onCheckStateChanged: {
+ if (checked) {
+ feeOption.unselectedText = unselectedText.text
+ return
+ }
+ feeOption.unselectedText = ""
+ }
+
+ Component.onCompleted: feeOption.unselectedText = ""
+ }
+
+ CheckBox {
+ id: loading
+ text: "Set loading state"
+ checked: false
+
+ onCheckStateChanged: {
+ if (checked) {
+ feeOption.subText = ""
+ feeOption.additionalText = ""
+ return
+ }
+
+ feeOption.subText = price.text
+ feeOption.additionalText = time.text
+ }
+ }
+
+ Item { Layout.fillHeight: true }
+ }
+ }
+}
+
+// category: Controls
diff --git a/storybook/pages/StatusInputPage.qml b/storybook/pages/StatusInputPage.qml
index 46df3eb6583..3d92da26d7f 100644
--- a/storybook/pages/StatusInputPage.qml
+++ b/storybook/pages/StatusInputPage.qml
@@ -3,6 +3,7 @@ import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
+import StatusQ.Core.Theme 0.1
import Storybook 1.0
import Models 1.0
@@ -32,6 +33,16 @@ SplitView {
enabled: enabledCheckBox.checked
input.edit.readOnly: readOnlyCheckBox.checked
input.clearable: clearableCheckBox.checked
+ label: "main label"
+ secondaryLabel: "secondary label"
+ labelIcon: "info"
+ labelIconColor: Theme.palette.baseColor1
+ labelIconClickable: true
+ leftPadding: 10
+ errorMessageCmp.visible: true
+ errorMessageCmp.text: "Current: 8.2 GWEI"
+ errorMessageCmp.horizontalAlignment: Text.AlignLeft
+ bottomLabelMessageCmp.text: "0.0031 ETH"
}
}
diff --git a/storybook/pages/TransactionSettingsPanelPage.qml b/storybook/pages/TransactionSettingsPanelPage.qml
new file mode 100644
index 00000000000..14b93d13775
--- /dev/null
+++ b/storybook/pages/TransactionSettingsPanelPage.qml
@@ -0,0 +1,85 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+import StatusQ.Controls 0.1
+import StatusQ.Core.Theme 0.1
+
+import AppLayouts.Wallet.views 1.0
+
+import utils 1.0
+
+import Storybook 1.0
+
+SplitView {
+ id: root
+
+ SplitView {
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+
+ orientation: Qt.Vertical
+
+ Rectangle {
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+ color: Theme.palette.baseColor3
+
+ TransactionSettings {
+ id: txSettings
+ anchors.centerIn: parent
+
+ currentBaseFee: "8.2"
+ currentSuggestedMinPriorityFee: "0.06"
+ currentSuggestedMaxPriorityFee: "5.1"
+ currentGasAmount: "31500"
+ currentNonce: 21
+
+ normalPrice: "1.45 EUR"
+ normalTime: "~60s"
+ fastPrice: "1.65 EUR"
+ fastTime: "~40s"
+ urgentPrice: "1.85 EUR"
+ urgentTime: "~15s"
+
+ customPrice: "1.45 EUR"
+ customTime: "~60s"
+
+ customBaseFee: "6.6"
+ customPriorityFee: "7.7"
+ customGasAmount: "35000"
+ customNonce: "22"
+
+ onConfirmClicked: {
+ logs.logEvent("confirm clicked...")
+ logs.logEvent(`selected fee mode: ${txSettings.selectedFeeMode}`)
+ if (selectedFeeMode === StatusFeeOption.Type.Custom) {
+ logs.logEvent(`selected customBaseFee...${txSettings.customBaseFee}`)
+ logs.logEvent(`selected customPriorityFee...${txSettings.customPriorityFee}`)
+ logs.logEvent(`selected customGasAmount...${txSettings.customGasAmount}`)
+ logs.logEvent(`selected customNonce...${txSettings.customNonce}`)
+ }
+ }
+ }
+ }
+
+ Logs {
+ id: logs
+ }
+
+ LogsView {
+ clip: true
+
+ SplitView.preferredHeight: 150
+ SplitView.fillWidth: true
+
+ logText: logs.logText
+ }
+ }
+
+ Pane {
+ SplitView.preferredWidth: 300
+
+ }
+}
+
+// category: Panel
diff --git a/ui/imports/shared/panels/AnimatedText.qml b/ui/StatusQ/src/StatusQ/Controls/StatusColorAnimation.qml
similarity index 50%
rename from ui/imports/shared/panels/AnimatedText.qml
rename to ui/StatusQ/src/StatusQ/Controls/StatusColorAnimation.qml
index 00e5b41cb41..93ab12bd877 100644
--- a/ui/imports/shared/panels/AnimatedText.qml
+++ b/ui/StatusQ/src/StatusQ/Controls/StatusColorAnimation.qml
@@ -2,6 +2,36 @@ import QtQuick 2.15
import StatusQ.Core.Theme 0.1
+/*!
+ \qmltype StatusColorAnimation
+ \inherits SequentialAnimation
+ \inqmlmodule StatusQ.Controls
+ \since StatusQ.Controls 0.1
+ \brief Animates target property (that shold be a color property) from/to color to target component within set duration.
+
+ Example of how to use it:
+
+ \qml
+ StatusBaseText {
+ id: animatedText
+
+ onTextChanged: {
+ if (text === "") {
+ return
+ }
+ animate.restart()
+ }
+
+ StatusColorAnimation {
+ id: animate
+ target: animatedText
+ }
+ }
+ \endqml
+
+ For a list of components available see StatusQ.
+ */
+
SequentialAnimation {
id: root
diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusFeeOption.qml b/ui/StatusQ/src/StatusQ/Controls/StatusFeeOption.qml
new file mode 100644
index 00000000000..f41406518ce
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Controls/StatusFeeOption.qml
@@ -0,0 +1,233 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+import StatusQ.Core 0.1
+import StatusQ.Components 0.1
+import StatusQ.Core.Theme 0.1
+
+/*!
+ \qmltype StatusFeeOption
+ \inherits Control
+ \inqmlmodule StatusQ.Controls
+ \since StatusQ.Controls 0.1
+ \brief Displays a clickable option which content is fully customizable
+
+ By design appearance of subText and additionalText as well as their values should be set from outside.
+ If any of those two need to be displayed a component that has an empty value will be rendering a loading state.
+ The control renders based on the selected property.
+ When control is clicked clicked signal will be emitted.
+
+ Example of how to use it:
+
+ \qml
+ StatusFeeOption {
+ subText: "1.65 EUR"
+ showSubText: true
+
+ additionalText: "~40s"
+ showAdditionalText: true
+
+ onSelectedChanged: {
+ // this option is selected/unselected
+ }
+ }
+ \endqml
+
+ For a list of components available see StatusQ.
+ */
+
+Control {
+ id: root
+
+ enum Type {
+ Normal,
+ Fast,
+ Urgent,
+ Custom
+ }
+
+ property int type: StatusFeeOption.Type.Normal
+
+ property bool selected: false
+
+ property string mainText: {
+ switch(type) {
+ case StatusFeeOption.Type.Fast:
+ return qsTr("Normal")
+ case StatusFeeOption.Type.Urgent:
+ return qsTr("Urgent")
+ case StatusFeeOption.Type.Custom:
+ return qsTr("Custom")
+ case StatusFeeOption.Type.Normal:
+ default:
+ return qsTr("Normal")
+ }
+ }
+
+ property string subText
+ property bool showSubText
+
+ property string additionalText
+ property bool showAdditionalText
+
+ property string unselectedText
+
+ property string icon: {
+ switch(type) {
+ case StatusFeeOption.Type.Fast:
+ return Theme.png("wallet/car")
+ case StatusFeeOption.Type.Urgent:
+ return Theme.png("wallet/rocket")
+ case StatusFeeOption.Type.Custom:
+ return Theme.png("wallet/handwrite")
+ case StatusFeeOption.Type.Normal:
+ default:
+ return Theme.png("wallet/clock")
+ }
+ }
+
+ signal clicked()
+
+ font.family: Theme.baseFont.name
+ font.pixelSize: 12
+
+ horizontalPadding: 8
+ verticalPadding: 8
+
+ component AnimatedText: StatusBaseText {
+ id: animatedText
+ verticalAlignment: Qt.AlignVCenter
+ font.family: root.font.family
+ font.pixelSize: root.font.pixelSize
+ wrapMode: Text.WordWrap
+ elide: Text.ElideRight
+
+ onTextChanged: {
+ if (text === "") {
+ return
+ }
+ animate.restart()
+ }
+
+
+ StatusColorAnimation {
+ id: animate
+ target: animatedText
+ fromColor: animatedText.color
+ }
+ }
+
+ property Component subTextComponent: AnimatedText {
+ text: root.subText
+ }
+
+ property Component additionalTextComponent: AnimatedText {
+ text: root.additionalText
+ color: Theme.palette.baseColor1
+ }
+
+ property Component unselectedTextComponent: StatusBaseText {
+ verticalAlignment: Qt.AlignVCenter
+ text: root.unselectedText
+ color: Theme.palette.baseColor1
+ font.family: root.font.family
+ font.pixelSize: root.font.pixelSize
+ wrapMode: Text.WordWrap
+ elide: Text.ElideRight
+ }
+
+ property Component loaderComponent: LoadingComponent {
+ radius: 4
+ height: root.font.pixelSize
+ }
+
+ background: Rectangle {
+ id: background
+ implicitHeight: 84
+ implicitWidth: 101
+ radius: 8
+ border.width: 1
+ border.color: root.selected? Theme.palette.primaryColor1 : Theme.palette.baseColor2
+ color: {
+ if (root.hovered) {
+ return Theme.palette.baseColor2
+ }
+
+ if (root.selected) {
+ return Theme.palette.alphaColor(Theme.palette.baseColor2, 0.1)
+ }
+
+ return Theme.palette.statusAppLayout.backgroundColor
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ cursorShape: root.hovered? Qt.PointingHandCursor : undefined
+ onClicked: root.clicked()
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 4
+
+ RowLayout {
+
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: Math.max(mText.height, image.height)
+
+ spacing: 4
+
+ StatusBaseText {
+ id: mText
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ verticalAlignment: Qt.AlignVCenter
+ text: root.mainText
+ font.family: root.font.family
+ font.pixelSize: root.font.pixelSize
+ wrapMode: Text.WordWrap
+ elide: Text.ElideRight
+ }
+
+ StatusImage {
+ id: image
+ Layout.alignment: Qt.AlignVCenter
+ visible: root.selected || root.hovered
+ width: 22
+ height: 22
+ source: root.icon
+ }
+ }
+
+ Item {
+ id: spacer
+ Layout.fillWidth: true
+ Layout.preferredHeight: 8 + (unselectedTextLoader.visible? parent.spacing : 0)
+ }
+
+ Loader {
+ visible: root.showSubText
+ Layout.preferredWidth: parent.width
+ Layout.alignment: Qt.AlignVCenter
+ sourceComponent: !!root.subText? subTextComponent : loaderComponent
+ }
+
+ Loader {
+ visible: root.showAdditionalText
+ Layout.preferredWidth: parent.width
+ Layout.alignment: Qt.AlignVCenter
+ sourceComponent: !!root.additionalText? additionalTextComponent : loaderComponent
+ }
+
+ Loader {
+ id: unselectedTextLoader
+ visible: !root.selected && !!root.unselectedText
+ Layout.preferredWidth: parent.width
+ Layout.fillHeight: true
+ Layout.alignment: Qt.AlignVCenter
+ sourceComponent: visible? unselectedTextComponent : loaderComponent
+ }
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusInput.qml b/ui/StatusQ/src/StatusQ/Controls/StatusInput.qml
index 6dddd5c015c..fd76abdc809 100644
--- a/ui/StatusQ/src/StatusQ/Controls/StatusInput.qml
+++ b/ui/StatusQ/src/StatusQ/Controls/StatusInput.qml
@@ -96,6 +96,22 @@ Item {
\endqml
*/
property alias errorMessageCmp: errorMessage
+ /*!
+ \qmlproperty bottomLabelMessageCmp
+ This property represents the bottomLabelMessage shown on statusInput on the right of errorMessageCmp.
+ By default this component is hidden and doesn't have any text set, once the text is set it will become visible.
+
+ Examples of usage
+
+ \qml
+ StatusInput {
+ bottomLabelMessageCmp.text: "some text"
+ bottomLabelMessageCmp.font.pixelSize: 15
+ bottomLabelMessageCmp.font.weight: Font.Medium
+ }
+ \endqml
+ */
+ property alias bottomLabelMessageCmp: bottomLabelMessage
/*!
\qmlproperty int StatusInput::labelPadding
This property sets the padding of the label text.
@@ -111,6 +127,21 @@ Item {
This property sets the secondary label text.
*/
property string secondaryLabel: ""
+ /*!
+ \qmlproperty string StatusInput::labelIcon
+ This property sets the icon displayd on the right of the label.
+ */
+ property string labelIcon: ""
+ /*!
+ \qmlproperty string StatusInput::labelIconColor
+ This property sets the color of the label icon.
+ */
+ property string labelIconColor: Theme.palette.baseColor1
+ /*!
+ \qmlproperty string StatusInput::labelIconClickable
+ This property sets if the label icon is clickable or not, if clickable labelIconClicked signal will be emitted.
+ */
+ property bool labelIconClickable: false
/*!
\qmlproperty int StatusInput::charLimit
This property sets the character limit of the text input.
@@ -205,6 +236,11 @@ Item {
This signal is emitted when the icon is clicked.
*/
signal iconClicked()
+ /*!
+ \qmlsignal
+ This signal is emitted when the label icon is clicked.
+ */
+ signal labelIconClicked()
/*!
\qmlsignal
This signal is emitted when a hard key is pressed passing as parameter the keyboard event.
@@ -428,6 +464,23 @@ Item {
color: Theme.palette.baseColor1
}
+ StatusIcon {
+ id: labelIcon
+ visible: !!root.labelIcon
+ width: 16
+ height: 16
+ icon: root.labelIcon
+ color: root.labelIconColor
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: root.labelIconClickable
+ hoverEnabled: root.labelIconClickable
+ cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
+ onClicked: root.labelIconClicked()
+ }
+ }
+
Item {
Layout.fillWidth: true
}
@@ -467,24 +520,34 @@ Item {
}
}
- StatusBaseText {
- id: errorMessage
- visible: {
- if (!text)
- return false;
+ RowLayout {
+ id: bottomRow
+ Layout.topMargin: 8
+ Layout.fillWidth: true
+
+ StatusBaseText {
+ id: errorMessage
+ Layout.fillWidth: true
+ visible: {
+ if (!text)
+ return false;
- if ((root.validationMode === StatusInput.ValidationMode.OnlyWhenDirty && statusBaseInput.dirty) ||
- root.validationMode === StatusInput.ValidationMode.Always)
- return !statusBaseInput.valid;
+ if ((root.validationMode === StatusInput.ValidationMode.OnlyWhenDirty && statusBaseInput.dirty) ||
+ root.validationMode === StatusInput.ValidationMode.Always)
+ return !statusBaseInput.valid;
- return false;
+ return false;
+ }
+ font.pixelSize: 12
+ color: Theme.palette.dangerColor1
+ wrapMode: Text.WordWrap
+ horizontalAlignment: Text.AlignRight
+ }
+
+ StatusBaseText {
+ id: bottomLabelMessage
+ visible: !!text
}
- font.pixelSize: 12
- color: Theme.palette.dangerColor1
- wrapMode: Text.WordWrap
- horizontalAlignment: Text.AlignRight
- Layout.topMargin: 8
- Layout.fillWidth: true
}
}
}
diff --git a/ui/StatusQ/src/StatusQ/Controls/qmldir b/ui/StatusQ/src/StatusQ/Controls/qmldir
index 0d5757d3418..548cbb88a07 100644
--- a/ui/StatusQ/src/StatusQ/Controls/qmldir
+++ b/ui/StatusQ/src/StatusQ/Controls/qmldir
@@ -14,12 +14,14 @@ StatusChatListCategoryItemButton 0.1 StatusChatListCategoryItemButton.qml
StatusCheckBox 0.1 StatusCheckBox.qml
StatusCircularProgressBar 0.1 StatusCircularProgressBar.qml
StatusClearButton 0.1 StatusClearButton.qml
+StatusColorAnimation 0.1 StatusColorAnimation.qml
StatusColorRadioButton 0.1 StatusColorRadioButton.qml
StatusColorSelector 0.1 StatusColorSelector.qml
StatusColorSelectorGrid 0.1 StatusColorSelectorGrid.qml
StatusComboBox 0.1 StatusComboBox.qml
StatusCommunityTag 0.1 StatusCommunityTag.qml
StatusDropdown 0.1 StatusDropdown.qml
+StatusFeeOption 0.1 StatusFeeOption.qml
StatusFlatButton 0.1 StatusFlatButton.qml
StatusFlatRoundButton 0.1 StatusFlatRoundButton.qml
StatusIconSwitch 0.1 StatusIconSwitch.qml
diff --git a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml
index ce11c13ece4..74f427644ab 100644
--- a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml
+++ b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml
@@ -14,6 +14,13 @@ Rectangle {
property alias leftComponent: leftComponentLoader.sourceComponent
+ property bool internalPopupActive
+ property color internalOverlayColor
+ property int popupFullHeight
+ property Component internalPopupComponent
+
+ signal closeInternalPopup()
+
color: Theme.palette.statusModal.backgroundColor
radius: 8
@@ -73,4 +80,29 @@ Rectangle {
samples: 37
color: Theme.palette.dropShadow
}
+
+ Rectangle {
+ id: internalOverlay
+ anchors.fill: parent
+ anchors.bottomMargin: -1 * root.popupFullHeight + root.height
+ visible: root.internalPopupActive
+ radius: root.radius
+ color: root.internalOverlayColor
+
+ MouseArea {
+ anchors.fill: parent
+ anchors.bottomMargin: popupLoader.height
+ onClicked: {
+ root.closeInternalPopup()
+ }
+ }
+ }
+
+ Loader {
+ id: popupLoader
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: internalOverlay.anchors.bottomMargin
+ active: root.internalPopupActive
+ sourceComponent: root.internalPopupComponent
+ }
}
diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc
index 669bc78b74d..8a8a5bb818c 100644
--- a/ui/StatusQ/src/assets.qrc
+++ b/ui/StatusQ/src/assets.qrc
@@ -281,6 +281,7 @@
assets/img/icons/security.svg
assets/img/icons/seed-phrase.svg
assets/img/icons/send.svg
+ assets/img/icons/settings-advance.svg
assets/img/icons/settings-advanced.svg
assets/img/icons/settings.svg
assets/img/icons/share-android.svg
@@ -8995,8 +8996,12 @@
assets/png/traffic_lights/maximize_pressed.png
assets/png/traffic_lights/minimise.png
assets/png/traffic_lights/minimise_pressed.png
- assets/png/wallet/wallet-green.png
+ assets/png/wallet/car.png
+ assets/png/wallet/clock.png
assets/png/wallet/flying-coin.png
+ assets/png/wallet/handwrite.png
+ assets/png/wallet/rocket.png
+ assets/png/wallet/wallet-green.png
assets/png/appearance-dark.png
assets/png/appearance-light.png
assets/png/appearance-system.png
diff --git a/ui/StatusQ/src/assets/img/icons/settings-advance.svg b/ui/StatusQ/src/assets/img/icons/settings-advance.svg
new file mode 100644
index 00000000000..6aca648d3c2
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/settings-advance.svg
@@ -0,0 +1,6 @@
+
diff --git a/ui/StatusQ/src/assets/png/wallet/car.png b/ui/StatusQ/src/assets/png/wallet/car.png
new file mode 100644
index 00000000000..bb2d81a39ac
Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/car.png differ
diff --git a/ui/StatusQ/src/assets/png/wallet/clock.png b/ui/StatusQ/src/assets/png/wallet/clock.png
new file mode 100644
index 00000000000..842d9bfcc9d
Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/clock.png differ
diff --git a/ui/StatusQ/src/assets/png/wallet/handwrite.png b/ui/StatusQ/src/assets/png/wallet/handwrite.png
new file mode 100644
index 00000000000..217b2a7ac80
Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/handwrite.png differ
diff --git a/ui/StatusQ/src/assets/png/wallet/rocket.png b/ui/StatusQ/src/assets/png/wallet/rocket.png
new file mode 100644
index 00000000000..0c9915aae68
Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/rocket.png differ
diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc
index 1379cbb10e8..0eaf6e9cab0 100644
--- a/ui/StatusQ/src/statusq.qrc
+++ b/ui/StatusQ/src/statusq.qrc
@@ -104,12 +104,14 @@
StatusQ/Controls/StatusCheckBox.qml
StatusQ/Controls/StatusCircularProgressBar.qml
StatusQ/Controls/StatusClearButton.qml
+ StatusQ/Controls/StatusColorAnimation.qml
StatusQ/Controls/StatusColorRadioButton.qml
StatusQ/Controls/StatusColorSelector.qml
StatusQ/Controls/StatusColorSelectorGrid.qml
StatusQ/Controls/StatusComboBox.qml
StatusQ/Controls/StatusCommunityTag.qml
StatusQ/Controls/StatusDropdown.qml
+ StatusQ/Controls/StatusFeeOption.qml
StatusQ/Controls/StatusFlatButton.qml
StatusQ/Controls/StatusFlatRoundButton.qml
StatusQ/Controls/StatusIconSwitch.qml
diff --git a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
index a81e79e8667..dc8fdd1ac2b 100644
--- a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
+++ b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
@@ -100,6 +100,12 @@ StatusDialog {
default property alias contents: contentsLayout.data
+ property bool internalPopupActive: false
+ property color internalOverlayColor: Theme.palette.backdropColor
+ property Component internalPopupComponent
+
+ signal closeInternalPopup()
+
width: 480
padding: 0
@@ -117,6 +123,13 @@ StatusDialog {
actions.closeButton.onClicked: root.close()
leftComponent: root.headerIconComponent
+
+ internalPopupActive: root.internalPopupActive
+ internalOverlayColor: root.internalOverlayColor
+ popupFullHeight: root.height
+ internalPopupComponent: root.internalPopupComponent
+
+ onCloseInternalPopup: root.closeInternalPopup()
}
footer: StatusDialogFooter {
diff --git a/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml b/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml
index 89483243043..84c756864f8 100644
--- a/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml
+++ b/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml
@@ -9,6 +9,7 @@ import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import AppLayouts.Wallet.panels 1.0
+import AppLayouts.Wallet.views 1.0
import AppLayouts.Wallet.popups 1.0
import utils 1.0
@@ -166,8 +167,28 @@ SignTransactionModalBase {
loading: root.feesLoading
}
}
+ StatusFlatButton {
+ tooltip.text: qsTr("Edit transaction settings")
+ icon.name: "settings-advance"
+ textColor: hovered? Theme.palette.directColor1 : Theme.palette.baseColor1
+ onClicked: {
+ root.internalPopupActive = true
+ }
+ }
+ }
+ }
+
+ property Component internalPopup: TransactionSettings {
+
+ onCancelClicked: {
+ root.internalPopupActive = false
}
}
+ internalPopupComponent: internalPopup
+
+ onCloseInternalPopup: {
+ root.internalPopupActive = false
+ }
// Send Asset
SignInfoBox {
@@ -196,6 +217,7 @@ SignTransactionModalBase {
}
]
visible: !root.isCollectible
+ enabled: !root.internalPopupActive
}
// Send Collectible
@@ -222,6 +244,7 @@ SignTransactionModalBase {
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
visible: root.isCollectible
+ enabled: !root.internalPopupActive
}
// From
@@ -263,6 +286,7 @@ SignTransactionModalBase {
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
]
+ enabled: !root.internalPopupActive
}
// Network
diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml
index 0dedca2df9e..578e9420008 100644
--- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml
+++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml
@@ -427,7 +427,7 @@ StatusDialog {
font.weight: Font.Medium
loading: root.swapAdaptor.swapProposalLoading
- AnimatedText {
+ StatusColorAnimation {
id: animation
target: fees
}
diff --git a/ui/app/AppLayouts/Wallet/views/TransactionSettings.qml b/ui/app/AppLayouts/Wallet/views/TransactionSettings.qml
new file mode 100644
index 00000000000..36a7acde990
--- /dev/null
+++ b/ui/app/AppLayouts/Wallet/views/TransactionSettings.qml
@@ -0,0 +1,294 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import QtQuick.Controls 2.15
+
+import StatusQ.Core 0.1
+import StatusQ.Controls 0.1
+import StatusQ.Core.Theme 0.1
+
+import shared.controls 1.0
+import shared.popups 1.0
+
+Rectangle {
+ id: root
+
+ property string currentBaseFee
+ property string currentSuggestedMinPriorityFee
+ property string currentSuggestedMaxPriorityFee
+ property string currentGasAmount
+ property int currentNonce
+
+ property alias normalPrice: optionNormal.subText
+ property alias normalTime: optionNormal.additionalText
+
+ property alias fastPrice: optionFast.subText
+ property alias fastTime: optionFast.additionalText
+
+ property alias urgentPrice: optionUrgent.subText
+ property alias urgentTime: optionUrgent.additionalText
+
+ property alias customPrice: optionCustom.subText
+ property alias customTime: optionCustom.additionalText
+ property alias customBaseFee: customBaseFee.text
+ property alias customPriorityFee: customPriorityFee.text
+ property alias customGasAmount: customGasAmount.text
+ property alias customNonce: customNonce.text
+
+ property int selectedFeeMode
+
+ signal confirmClicked()
+ signal cancelClicked()
+
+ color: Theme.palette.statusModal.backgroundColor
+ radius: 8
+
+ implicitHeight: layout.implicitHeight
+ implicitWidth: layout.implicitWidth
+
+ QtObject {
+ id: d
+
+ readonly property bool customMode: root.selectedFeeMode === StatusFeeOption.Type.Custom
+
+ function showAlert(title, text, note, url) {
+ infoBox.title = title
+ infoBox.text = text
+ infoBox.note = note
+ infoBox.url = url
+ infoBox.active = true
+ }
+ }
+
+ focus: true
+
+ Keys.onReleased: {
+ if (event.key === Qt.Key_Escape) {
+ root.cancelClicked()
+ }
+ }
+
+ Component.onCompleted: root.forceActiveFocus()
+
+ Loader {
+ id: infoBox
+ anchors.centerIn: root
+ active: false
+
+ property string title
+ property string text
+ property string note
+ property string url
+
+ sourceComponent: AlertPopup {
+ title: infoBox.title
+
+ width: root.width - 2 * 20
+
+ acceptBtnText: qsTr("Got it")
+ cancelBtn.text: !!infoBox.url? qsTr("Read more") : ""
+ cancelBtn.icon.name: "external-link"
+ cancelBtn.visible: !!infoBox.url
+
+ alertLabel.text: infoBox.text
+ alertNote.visible: !!infoBox.note
+ alertNote.text: infoBox.note
+ alertNote.color: Theme.palette.baseColor1
+
+ onCancelClicked: {
+ Qt.openUrlExternally(infoBox.url)
+ }
+
+ onClosed: {
+ infoBox.active = false
+ }
+ }
+
+ onLoaded: {
+ infoBox.item.open()
+ }
+ }
+
+ ColumnLayout {
+ id: layout
+
+ ColumnLayout {
+ Layout.margins: 20
+
+ spacing: 16
+
+ StatusBaseText {
+ Layout.preferredWidth: parent.width
+ text: qsTr("Transaction settings")
+ font.pixelSize: 17
+ font.bold: true
+ elide: Text.ElideMiddle
+ }
+
+ RowLayout {
+ id: options
+ spacing: 12
+
+ StatusFeeOption {
+ id: optionNormal
+ type: StatusFeeOption.Type.Normal
+ selected: root.selectedFeeMode === StatusFeeOption.Type.Normal
+ showSubText: true
+ showAdditionalText: true
+
+ onClicked: root.selectedFeeMode = StatusFeeOption.Type.Normal
+ }
+
+ StatusFeeOption {
+ id: optionFast
+ type: StatusFeeOption.Type.Fast
+ selected: root.selectedFeeMode === StatusFeeOption.Type.Fast
+ showSubText: true
+ showAdditionalText: true
+
+ onClicked: root.selectedFeeMode = StatusFeeOption.Type.Fast
+ }
+
+ StatusFeeOption {
+ id: optionUrgent
+ type: StatusFeeOption.Type.Urgent
+ selected: root.selectedFeeMode === StatusFeeOption.Type.Urgent
+ showSubText: true
+ showAdditionalText: true
+
+ onClicked: root.selectedFeeMode = StatusFeeOption.Type.Urgent
+ }
+
+ StatusFeeOption {
+ id: optionCustom
+ type: StatusFeeOption.Type.Custom
+ selected: root.selectedFeeMode === StatusFeeOption.Type.Custom
+ showSubText: !!selected
+ showAdditionalText: !!selected
+ unselectedText: "Set your own fees & nonce"
+
+ onClicked: root.selectedFeeMode = StatusFeeOption.Type.Custom
+ }
+ }
+
+ StatusBaseText {
+ Layout.preferredWidth: parent.width
+ visible: !d.customMode
+ text: qsTr("Increased base and priority fee, incentivising miners to confirm more quickly")
+ color: Theme.palette.baseColor1
+ font.pixelSize: Theme.tertiaryTextFontSize
+ elide: Text.ElideMiddle
+ }
+
+ ShapeRectangle {
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: customLayout.height + customLayout.anchors.margins
+ visible: d.customMode
+
+ ColumnLayout {
+ id: customLayout
+ anchors.left: parent.left
+ anchors.margins: 20
+ width: parent.width - 2 * anchors.margins
+ spacing: 16
+
+ StatusInput {
+ id: customBaseFee
+ Layout.preferredWidth: parent.width
+ Layout.topMargin: 20
+ label: qsTr("Max base fee")
+ labelIcon: "info"
+ labelIconColor: Theme.palette.baseColor1
+ labelIconClickable: true
+ errorMessageCmp.visible: true
+ errorMessageCmp.color: Theme.palette.baseColor1
+ errorMessageCmp.text: qsTr("Current: %1 GWEI").arg(root.currentBaseFee)
+ errorMessageCmp.horizontalAlignment: Text.AlignLeft
+ bottomLabelMessageCmp.text: qsTr("0.0031 ETH")
+ rightPadding: leftPadding
+ input.rightComponent: StatusBaseText {
+ text: "GWEI"
+ color: Theme.palette.baseColor1
+ }
+
+ onLabelIconClicked: d.showAlert(label,
+ qsTr("When your transaction gets included in the block, any difference between your max base fee and the actual base fee will be refunded.\n"),
+ qsTr("Note: the ETH amount shown for this value is calculated:\nMax base fee (in GWEI) * Max gas amount"),
+ "")
+ }
+
+ StatusInput {
+ id: customPriorityFee
+ Layout.preferredWidth: parent.width
+ label: qsTr("Priority fee")
+ labelIcon: "info"
+ labelIconColor: Theme.palette.baseColor1
+ labelIconClickable: true
+ errorMessageCmp.visible: true
+ errorMessageCmp.color: Theme.palette.baseColor1
+ errorMessageCmp.text: qsTr("Current: %1 - %2 GWEI").arg(root.currentSuggestedMinPriorityFee).arg(root.currentSuggestedMaxPriorityFee)
+ errorMessageCmp.horizontalAlignment: Text.AlignLeft
+ bottomLabelMessageCmp.text: qsTr("0.0031 ETH")
+ rightPadding: leftPadding
+ input.rightComponent: StatusBaseText {
+ text: "GWEI"
+ color: Theme.palette.baseColor1
+ }
+
+ onLabelIconClicked: d.showAlert(label,
+ qsTr("AKA miner tip. A voluntary fee you can add to incentivise miners or validators to prioritise your transaction.\n\nThe higher the tip, the faster your transaction is likely to be processed, especially curing periods of higher network congestion.\n"),
+ qsTr("Note: the ETH amount shown for this value is calculated: Priority fee (in GWEI) * Max gas amount"),
+ "")
+ }
+
+ StatusInput {
+ id: customGasAmount
+ Layout.preferredWidth: parent.width
+ label: qsTr("Max gas amount")
+ labelIcon: "info"
+ labelIconColor: Theme.palette.baseColor1
+ labelIconClickable: true
+ errorMessageCmp.visible: true
+ errorMessageCmp.color: Theme.palette.baseColor1
+ errorMessageCmp.text: qsTr("Current: %1").arg(root.currentGasAmount)
+ errorMessageCmp.horizontalAlignment: Text.AlignLeft
+ rightPadding: leftPadding
+ input.rightComponent: StatusBaseText {
+ text: "UNITS"
+ color: Theme.palette.baseColor1
+ }
+
+ onLabelIconClicked: d.showAlert(qsTr("Gas amount"),
+ qsTr("AKA gas limit. Refers to the maximum number of computational steps (or units of gas) that a transaction can consume. It represents the complexity or amount of work required to execute a transaction or smart contract.\n\nThe gas limit is a cap on how much work the transaction can do on the blockchain. If the gas limit is set too low, the transaction may fail due to insufficient gas."),
+ "",
+ "")
+ }
+
+ StatusInput {
+ id: customNonce
+ Layout.preferredWidth: parent.width
+ label: qsTr("Nonce")
+ labelIcon: "info"
+ labelIconColor: Theme.palette.baseColor1
+ labelIconClickable: true
+ errorMessageCmp.visible: true
+ errorMessageCmp.color: Theme.palette.baseColor1
+ errorMessageCmp.text: qsTr("Last transaction: %1").arg(root.currentNonce)
+ errorMessageCmp.horizontalAlignment: Text.AlignLeft
+ rightPadding: leftPadding
+
+ onLabelIconClicked: d.showAlert(label,
+ qsTr("Transaction counter ensuring transactions from your account are processed in the correct order and can’t be replayed. Each new transaction increments the nonce by 1, ensuring uniqueness and preventing double-spending.\n\nIf a transaction with a lower nonce is pending, higher nonce transactions will remain in the queue until the earlier one is confirmed."),
+ "",
+ "")
+ }
+ }
+ }
+
+ StatusButton {
+ Layout.preferredWidth: parent.width
+ text: qsTr("Confirm")
+ onClicked: root.confirmClicked()
+ }
+ }
+ }
+}
diff --git a/ui/app/AppLayouts/Wallet/views/qmldir b/ui/app/AppLayouts/Wallet/views/qmldir
index 12ef9ae06c1..aa6fae11b2b 100644
--- a/ui/app/AppLayouts/Wallet/views/qmldir
+++ b/ui/app/AppLayouts/Wallet/views/qmldir
@@ -8,3 +8,4 @@ TokenSelectorSectionDelegate 1.0 TokenSelectorSectionDelegate.qml
AccountContextMenu 1.0 AccountContextMenu.qml
RecipientView 1.0 RecipientView.qml
SendModalFooter 1.0 SendModalFooter.qml
+TransactionSettings 1.0 TransactionSettings.qml
\ No newline at end of file
diff --git a/ui/imports/shared/panels/qmldir b/ui/imports/shared/panels/qmldir
index 34dc96c8542..ac9137b100d 100644
--- a/ui/imports/shared/panels/qmldir
+++ b/ui/imports/shared/panels/qmldir
@@ -18,4 +18,3 @@ Separator 1.0 Separator.qml
SequenceColumnLayout 1.0 SequenceColumnLayout.qml
StatusAssetSelector 1.0 StatusAssetSelector.qml
StyledText 1.0 StyledText.qml
-AnimatedText 1.0 AnimatedText.qml
diff --git a/ui/imports/shared/popups/AlertPopup.qml b/ui/imports/shared/popups/AlertPopup.qml
index 5930996bb9f..b5392285619 100644
--- a/ui/imports/shared/popups/AlertPopup.qml
+++ b/ui/imports/shared/popups/AlertPopup.qml
@@ -15,8 +15,11 @@ StatusDialog {
id: root
property alias acceptBtnText: acceptBtn.text
+ property alias acceptBtn: acceptBtn
+ property alias cancelBtn: cancelBtn
property alias alertText: contentTextItem.text
property alias alertLabel: contentTextItem
+ property alias alertNote: contentNoteItem
property int acceptBtnType: StatusBaseButton.Type.Danger
property StatusAssetSettings asset: StatusAssetSettings {
@@ -36,12 +39,22 @@ StatusDialog {
implicitWidth: 400 // by design
topPadding: Theme.padding
bottomPadding: topPadding
- contentItem: StatusBaseText {
- id: contentTextItem
-
- font.pixelSize: Theme.primaryTextFontSize
- wrapMode: Text.WordWrap
- lineHeight: 1.2
+ contentItem: Column {
+ StatusBaseText {
+ id: contentTextItem
+ width: parent.width
+ font.pixelSize: Theme.primaryTextFontSize
+ wrapMode: Text.WordWrap
+ lineHeight: 1.2
+ }
+ StatusBaseText {
+ id: contentNoteItem
+ visible: false
+ width: parent.width
+ font.pixelSize: Theme.primaryTextFontSize
+ wrapMode: Text.WordWrap
+ lineHeight: 1.2
+ }
}
header: StatusDialogHeader {
@@ -61,6 +74,7 @@ StatusDialog {
rightButtons: ObjectModel {
StatusButton {
+ id: cancelBtn
text: qsTr("Cancel")
normalColor: "transparent"
@@ -75,6 +89,8 @@ StatusDialog {
type: root.acceptBtnType
+ Component.onCompleted: acceptBtn.forceActiveFocus()
+
onClicked: {
root.acceptClicked()
close()
diff --git a/ui/imports/shared/popups/send/views/TransactionModalFooter.qml b/ui/imports/shared/popups/send/views/TransactionModalFooter.qml
index 50282ceac2d..73787d7e470 100644
--- a/ui/imports/shared/popups/send/views/TransactionModalFooter.qml
+++ b/ui/imports/shared/popups/send/views/TransactionModalFooter.qml
@@ -52,7 +52,7 @@ StatusDialogFooter {
estimatedTimeAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: estimatedTimeAnimation
target: estimatedTime
}
@@ -80,7 +80,7 @@ StatusDialogFooter {
feesAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: feesAnimation
target: fees
}
diff --git a/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml b/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
index b9670a6fb86..17fb93dd320 100644
--- a/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
+++ b/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
@@ -118,7 +118,7 @@ SignTransactionModalBase {
maxFeesAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: maxFeesAnimation
target: maxFees
targetProperty: "customColor"
@@ -148,7 +148,7 @@ SignTransactionModalBase {
estimatedTimeAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: estimatedTimeAnimation
target: estimatedTime
targetProperty: "customColor"
@@ -224,7 +224,7 @@ SignTransactionModalBase {
fiatFeesAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: fiatFeesAnimation
target: fiatFees
targetProperty: "customColor"
@@ -250,7 +250,7 @@ SignTransactionModalBase {
cryptoFeesAnimation.restart()
}
- AnimatedText {
+ StatusColorAnimation {
id: cryptoFeesAnimation
target: cryptoFees
fromColor: cryptoFees.customColor