diff --git a/ui/StatusQ/doc/src/images/status_card.png b/ui/StatusQ/doc/src/images/status_card.png
new file mode 100644
index 0000000000..17f871cbd7
Binary files /dev/null and b/ui/StatusQ/doc/src/images/status_card.png differ
diff --git a/ui/StatusQ/sandbox/main.qml b/ui/StatusQ/sandbox/main.qml
index 1d4a4b5321..17a22e88d4 100644
--- a/ui/StatusQ/sandbox/main.qml
+++ b/ui/StatusQ/sandbox/main.qml
@@ -336,6 +336,11 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title, true);
}
+ StatusNavigationListItem {
+ title: "StatusCard"
+ selected: viewLoader.source.toString().includes(title)
+ onClicked: mainPageView.page(title, true);
+ }
}
}
}
diff --git a/ui/StatusQ/sandbox/pages/StatusCardPage.qml b/ui/StatusQ/sandbox/pages/StatusCardPage.qml
new file mode 100644
index 0000000000..9ecdb538df
--- /dev/null
+++ b/ui/StatusQ/sandbox/pages/StatusCardPage.qml
@@ -0,0 +1,253 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+
+import Sandbox 0.1
+import StatusQ.Core 0.1
+import StatusQ.Core.Utils 0.1
+import StatusQ.Core.Theme 0.1
+import StatusQ.Controls 0.1
+import StatusQ.Popups 0.1
+import StatusQ.Components 0.1
+
+Item {
+
+ ColumnLayout {
+ id: layout
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ spacing: 20
+
+ RowLayout {
+ StatusSelect {
+ id: select
+ label: "Select Card State"
+ model: ListModel {
+ ListElement {
+ name: "default"
+ }
+ ListElement {
+ name: "unavailable"
+ }
+ ListElement {
+ name: "error"
+ }
+ ListElement {
+ name: "unpreferred"
+ }
+ }
+ selectMenu.delegate: StatusMenuItemDelegate {
+ statusPopupMenu: select
+ action: StatusMenuItem {
+ text: name
+ onTriggered: {
+ selectedItem.text = name
+ card.state = name
+ }
+ }
+ }
+ selectedItemComponent: Item {
+ id: selectedItem
+ anchors.fill: parent
+ property string text: "default"
+
+ StatusBaseText {
+ text: selectedItem.text
+ anchors.centerIn: parent
+ color: Theme.palette.directColor1
+ }
+ }
+ }
+
+ StatusCheckBox {
+ text: "advancedMode"
+ onClicked: {
+ card.advancedMode = checked
+ }
+ }
+ }
+
+ StatusCard {
+ id: card
+ primaryText: "Mainnet"
+ secondaryText: state === "unavailable" ? "No Gas" : "75"
+ tertiaryText: state === "unpreferred" ? "UNPREFERRED" : "BALANCE: " + 250
+ cardIconName: "status"
+ advancedInputText: "75"
+ disabledText: "Disabled"
+ }
+
+ Rectangle {
+ height: 1
+ width: 700
+ color: "black"
+ }
+
+ // Below is an example on how to implement the network routing using StatusCard and Canvas, also the function in Utils to draw an arrow
+ Row {
+ id: cards
+ spacing: 200
+ Column {
+ id: leftColumn
+ spacing: 20
+ Repeater {
+ model: fromNetworksList
+ StatusCard {
+ primaryText: name
+ secondaryText: balance === 0 ? "No Balance" : !hasGas ? "No Gas" : tokensToSend
+ tertiaryText: "BALANCE: " + balance
+ state: balance === 0 || !hasGas ? "unavailable" : "default"
+ cardIconName: iconName
+ advancedMode: card.advancedMode
+ advancedInputText: tokensToSend
+ disabledText: "Disabled"
+ }
+ }
+ }
+
+ Column {
+ id: rightColumn
+ spacing: 20
+ Repeater {
+ model: toNetworksList
+ StatusCard {
+ primaryText: name
+ secondaryText: tokensToReceive
+ tertiaryText: ""
+ state: preferred ? "default" : "unprefeered"
+ cardIconName: iconName
+ opacity: preferred ? 1 : 0
+ advancedMode: card.advancedMode
+ advancedInputText: tokensToReceive
+ disabledText: "Disabled"
+ }
+ }
+ }
+ }
+ }
+
+ Canvas {
+ id: canvas
+ x: layout.x + leftColumn.x
+ y: cards.y
+ width: cards.width
+ height: cards.height
+
+ function clear() {
+ var ctx = getContext("2d");
+ ctx.reset()
+ }
+
+ onPaint: {
+ // Get the canvas context
+ var ctx = getContext("2d");
+
+ for(var i = 0; i< fromNetworksList.count; i++) {
+ if(fromNetworksList.get(i).routedTo !== "") {
+ for(var j = 0; j< toNetworksList.count; j++) {
+ if(fromNetworksList.get(i).routedTo === toNetworksList.get(j).name) {
+ Utils.drawArrow(ctx, leftColumn.children[i].x + leftColumn.children[i].width,
+ leftColumn.children[i].y + leftColumn.children[i].height/2,
+ rightColumn.x + rightColumn.children[j].x,
+ rightColumn.children[j].y + rightColumn.children[j].height/2,
+ '#627EEA')
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ ListModel {
+ id: toNetworksList
+ ListElement {
+ name: "Mainnet"
+ iconName: "status"
+ tokensToReceive: 75
+ preferred: true
+ }
+ ListElement {
+ name: "Aztec"
+ iconName: "status"
+ tokensToReceive: 0
+ preferred: false
+ }
+ ListElement {
+ name: "Hermez"
+ iconName: "status"
+ tokensToReceive: 75
+ preferred: true
+ }
+ ListElement {
+ name: "Loppring"
+ iconName: "status"
+ tokensToReceive: 0
+ preferred: true
+ }
+ ListElement {
+ name: "Optimism"
+ iconName: "status"
+ tokensToReceive: 100
+ preferred: true
+ }
+ ListElement {
+ name: "zkSync"
+ iconName: "status"
+ tokensToReceive: 0
+ preferred: false
+ }
+ }
+
+ ListModel {
+ id: fromNetworksList
+ ListElement {
+ name: "Mainnet"
+ iconName: "status"
+ tokensToSend: 75
+ balance: 75
+ routedTo: "Mainnet"
+ hasGas: true
+ }
+ ListElement {
+ name: "Aztec"
+ iconName: "status"
+ tokensToSend: 0
+ balance: 75
+ routedTo: ""
+ hasGas: false
+ }
+ ListElement {
+ name: "Hermez"
+ iconName: "status"
+ tokensToSend: 75
+ balance: 75
+ routedTo: "Hermez"
+ hasGas: true
+ }
+ ListElement {
+ name: "Loppring"
+ iconName: "status"
+ tokensToSend: 0
+ balance: 0
+ routedTo: ""
+ hasGas: false
+ }
+ ListElement {
+ name: "Optimism"
+ iconName: "status"
+ tokensToSend: 75
+ balance: 75
+ routedTo: "Optimism"
+ hasGas: true
+ }
+ ListElement {
+ name: "zkSync"
+ iconName: "status"
+ tokensToSend: 25
+ balance: 25
+ routedTo: "Optimism"
+ hasGas: true
+ }
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusCard.qml b/ui/StatusQ/src/StatusQ/Components/StatusCard.qml
new file mode 100644
index 0000000000..cb2c5347d7
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusCard.qml
@@ -0,0 +1,498 @@
+import QtQuick 2.13
+import QtQuick.Layouts 1.14
+
+import StatusQ.Core 0.1
+import StatusQ.Core.Theme 0.1
+import StatusQ.Controls 0.1
+
+/*!
+ \qmltype StatusCard
+ \inherits Rectangle
+ \inqmlmodule StatusQ.Components
+ \since StatusQ.Components 0.1
+ \brief This component represents a StatusCard as defined in design under https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=3161%3A171040
+
+ There is an advanced mode avialable where a StatusBaseInput is provided for the user to be able to change values.
+
+ Example of how the component looks like:
+ \image status_card.png
+
+ Example of how to use it:
+ \qml
+ StatusCard {
+ id: card
+ primaryText: "Mainnet"
+ secondaryText: "75"
+ tertiaryText: "BALANCE: " + 250
+ cardIconName: "status"
+ advancedMode: false
+ }
+ \endqml
+ For a list of components available see StatusQ.
+*/
+
+Rectangle {
+ id: root
+
+ /*!
+ \qmlproperty string StatusCard::disabledText
+ This property is the text to be shown when the card is disabled
+ */
+ property string disabledText: ""
+ /*!
+ \qmlproperty bool StatusCard::disabled
+ This property holds if the card is disbaled
+ */
+ property bool disabled: false
+
+ /*!
+ \qmlproperty bool StatusCard::clickable
+ This property holds if the card is clickable
+ */
+ property bool clickable: true
+
+ /*!
+ \qmlproperty bool StatusCard::advancedMode
+ This property holds if advanced mode is on for the StatusCard component
+ */
+ property bool advancedMode: false
+ /*!
+ \qmlproperty int StatusCard::lockTimeout
+ This property enables user to customise the amount of time given to the user to enter a new value in
+ advanced mode before it locked for any new changes
+ */
+ property int lockTimeout: 1500
+
+ /*!
+ \qmlproperty alias StatusCard::primaryText
+ Used to set Primary text in the StatusCard
+ */
+ property alias primaryText: primaryText.text
+ /*!
+ \qmlproperty string StatusCard::secondaryText
+ Used to set Secondary text in the StatusCard
+ */
+ property string secondaryText: ""
+ /*!
+ \qmlproperty alias StatusCard::tertiaryText
+ Used to set Tertiary text in the StatusCard
+ */
+ property alias tertiaryText: tertiaryText.text
+ /*!
+ \qmlproperty alias StatusCard::advancedInputText
+ Used to set text in the StatusBaseInput in advancedMode
+ */
+ property alias advancedInputText: advancedInput.text
+ /*!
+ \qmlproperty alias StatusCard::errorIconName
+ Used to assign an icon to the error icon in StatusCard
+ */
+ property alias errorIconName: errorIcon.icon
+ /*!
+ \qmlproperty alias StatusCard::cardIconName
+ Used to assign an icon to the card icon in StatusCard
+ */
+ property alias cardIconName: cardIcon.icon
+
+ /*!
+ \qmlproperty alias StatusCard::primaryLabel
+ This property allows user to customize the primary label in the StatusCard
+ */
+ property alias primaryLabel: primaryText
+ /*!
+ \qmlproperty alias StatusCard::secondaryLabel
+ This property allows user to customize the secondary label in the StatusCard
+ */
+ property alias secondaryLabel: secondaryLabel
+ /*!
+ \qmlproperty alias StatusCard::tertiaryLabel
+ This property allows user to customize the tertiary label in the StatusCard
+ */
+ property alias tertiaryLabel: tertiaryText
+ /*!
+ \qmlproperty alias StatusCard::advancedInput
+ This property allows user to customize the StatusBaseInput in advanced mode
+ */
+ property alias advancedInput: advancedInput
+ /*!
+ \qmlproperty alias StatusCard::errorIcon
+ This property allows user to customize the error icon in the StatusCard
+ */
+ property alias errorIcon: errorIcon
+ /*!
+ \qmlproperty alias StatusCard::cardIcon
+ This property allows user to customize the card icon in the StatusCard
+ */
+ property alias cardIcon: cardIcon
+
+ /*!
+ \qmlsignal StatusCard::clicked
+ This signal is emitted when the card is clicked
+ */
+ signal clicked()
+
+ /*!
+ \qmlproperty string StatusCard::state
+ This property holds the states of the StatusCard.
+ Possible values are:
+ \ "default" : Normal state
+ \ "unavailable" : Unavailable state
+ \ "unpreferred": Not preffered state
+ \ "error" : Error state
+ */
+ state: "default"
+
+ implicitHeight: advancedInput.visible ? 90 : 76
+ implicitWidth: 128
+ radius: 8
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ acceptedButtons: Qt.LeftButton
+ enabled: root.clickable && root.state !== "unavailable"
+ onClicked: {
+ disabled = !disabled
+ root.clicked()
+ }
+ }
+
+ RowLayout {
+ id: layout
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.leftMargin: 8
+ anchors.rightMargin: 8
+ anchors.topMargin: 8
+ ColumnLayout {
+ Layout.maximumWidth: root.width - cardIcon.width - 24
+ StatusBaseText {
+ id: primaryText
+ Layout.maximumWidth: parent.width
+ font.pixelSize: 15
+ font.weight: Font.Medium
+ elide: Text.ElideRight
+ }
+ RowLayout {
+ id: basicInput
+ StatusBaseText {
+ id: secondaryLabel
+ font.pixelSize: 13
+ font.weight: Font.Medium
+ }
+ StatusIcon {
+ id: errorIcon
+ width: 14
+ height: 14
+ Layout.alignment: Qt.AlignTop
+ icon: "tiny/warning"
+ color: Theme.palette.pinColor1
+ }
+ }
+
+ StatusBaseInput {
+ id: advancedInput
+ property bool locked: false
+ implicitWidth: 80
+ implicitHeight: 32
+ topPadding: 0
+ bottomPadding: 0
+ leftPadding: 8
+ rightPadding: 5
+ edit.font.pixelSize: 13
+ edit.readOnly: locked || disabled
+ rightComponent: Row {
+ width: implicitWidth
+ spacing: 4
+ StatusFlatRoundButton {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 12
+ height: 12
+ icon.name: advancedInput.locked ? "lock" : "unlock"
+ icon.width: 12
+ icon.height: 12
+ icon.color: advancedInput.locked ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
+ type: StatusFlatRoundButton.Type.Secondary
+ enabled: !disabled
+ onClicked: {
+ advancedInput.locked = !advancedInput.locked
+ waitTimer.restart()
+ }
+ }
+ StatusFlatRoundButton {
+ width: 14
+ height: 14
+ icon.name: "clear"
+ icon.width: 14
+ icon.height: 14
+ icon.color: Theme.palette.baseColor1
+ type: StatusFlatRoundButton.Type.Secondary
+ onClicked: advancedInput.edit.clear()
+ }
+ }
+ onTextChanged: {
+ locked = false
+ waitTimer.restart()
+ }
+ Timer {
+ id: waitTimer
+ interval: lockTimeout
+ onTriggered: {
+ if(advancedInput.edit.text)
+ advancedInput.locked = true
+ }
+ }
+ }
+ StatusBaseText {
+ id: tertiaryText
+ font.pixelSize: 10
+ }
+ }
+ StatusIcon {
+ id: cardIcon
+ Layout.alignment: Qt.AlignTop | Qt.AlignRight
+ Layout.preferredHeight: 32
+ Layout.preferredWidth: 32
+ mipmap: true
+ }
+ }
+
+ states: [
+ State {
+ name: "default"
+ PropertyChanges {
+ target: root
+ color: disabled ? Theme.palette.baseColor4 : "transparent"
+ }
+ PropertyChanges {
+ target: root
+ border.color: disabled ? "transparent" : Theme.palette.primaryColor2
+ }
+ PropertyChanges {
+ target: primaryText
+ color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: primaryText
+ visible: primaryText.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ color: disabled ? Theme.palette.directColor5: Theme.palette.primaryColor1
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ visible: !advancedMode && secondaryLabel.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ text: disabled ? disabledText : secondaryText
+ }
+ PropertyChanges {
+ target: tertiaryText
+ color: Theme.palette.directColor5
+ }
+ PropertyChanges {
+ target: tertiaryText
+ visible: tertiaryText.text
+ }
+ PropertyChanges {
+ target: cardIcon
+ opacity: disabled ? 0.4 : 1
+ }
+ PropertyChanges {
+ target: errorIcon
+ visible: false
+ }
+ PropertyChanges {
+ target: advancedInput
+ visible: advancedMode
+ }
+ PropertyChanges {
+ target: advancedInput
+ edit.color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: basicInput
+ visible: !advancedMode
+ }
+ },
+ State {
+ name: "error"
+ PropertyChanges {
+ target: root
+ color: disabled ? Theme.palette.baseColor4 : "transparent"
+ }
+ PropertyChanges {
+ target: root
+ border.color: disabled ? "transparent" : Theme.palette.primaryColor2
+ }
+ PropertyChanges {
+ target: primaryText
+ color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: primaryText
+ visible: primaryText.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ color: disabled ? Theme.palette.directColor5: Theme.palette.dangerColor1
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ visible: !advancedMode && secondaryLabel.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ text: disabled ? disabledText : secondaryText
+ }
+ PropertyChanges {
+ target: tertiaryText
+ color: disabled ? Theme.palette.directColor5 : Theme.palette.dangerColor1
+ }
+ PropertyChanges {
+ target: tertiaryText
+ visible: tertiaryText.text
+ }
+ PropertyChanges {
+ target: cardIcon
+ opacity: disabled ? 0.4 : 1
+ }
+ PropertyChanges {
+ target: errorIcon
+ visible: false
+ }
+ PropertyChanges {
+ target: advancedInput
+ visible: advancedMode
+ }
+ PropertyChanges {
+ target: advancedInput
+ edit.color: disabled ? Theme.palette.directColor5 : Theme.palette.dangerColor1
+ }
+ PropertyChanges {
+ target: basicInput
+ visible: !advancedMode
+ }
+ },
+ State {
+ name: "unpreferred"
+ PropertyChanges {
+ target: root
+ color: disabled ? Theme.palette.baseColor4 : "transparent"
+ }
+ PropertyChanges {
+ target: root
+ border.color: disabled ? "transparent": Theme.palette.pinColor2
+ }
+ PropertyChanges {
+ target: primaryText
+ color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: primaryText
+ visible: primaryText.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ color: disabled ? Theme.palette.directColor5 : Theme.palette.pinColor1
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ visible: !advancedMode && secondaryLabel.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ text: disabled ? disabledText : secondaryText
+ }
+ PropertyChanges {
+ target: tertiaryText
+ color: disabled ? Theme.palette.directColor5 : Theme.palette.pinColor1
+ }
+ PropertyChanges {
+ target: tertiaryText
+ visible: tertiaryText.text
+ }
+ PropertyChanges {
+ target: cardIcon
+ opacity: disabled ? 0.4 : 1
+ }
+ PropertyChanges {
+ target: errorIcon
+ visible: !disabled && !advancedMode
+ }
+ PropertyChanges {
+ target: advancedInput
+ visible: advancedMode
+ }
+ PropertyChanges {
+ target: advancedInput
+ edit.color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: basicInput
+ visible: !advancedMode
+ }
+ },
+ State {
+ name: "unavailable"
+ PropertyChanges {
+ target: root
+ color: "transparent"
+ }
+ PropertyChanges {
+ target: root
+ border.color: "transparent"
+ }
+ PropertyChanges {
+ target: primaryText
+ color: Theme.palette.directColor5
+ }
+ PropertyChanges {
+ target: primaryText
+ visible: primaryText.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ color: Theme.palette.directColor5
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ visible: secondaryLabel.text
+ }
+ PropertyChanges {
+ target: secondaryLabel
+ text: secondaryText
+ }
+ PropertyChanges {
+ target: tertiaryText
+ color: Theme.palette.directColor5
+ }
+ PropertyChanges {
+ target: tertiaryText
+ visible: tertiaryText.text
+ }
+ PropertyChanges {
+ target: cardIcon
+ opacity: 0.4
+ }
+ PropertyChanges {
+ target: errorIcon
+ visible: false
+ }
+ PropertyChanges {
+ target: advancedInput
+ visible: false
+ }
+ PropertyChanges {
+ target: advancedInput
+ edit.color: Theme.palette.directColor1
+ }
+ PropertyChanges {
+ target: basicInput
+ visible: true
+ }
+ }
+ ]}
diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir
index 06ccf65ab3..79e834820c 100644
--- a/ui/StatusQ/src/StatusQ/Components/qmldir
+++ b/ui/StatusQ/src/StatusQ/Components/qmldir
@@ -38,3 +38,4 @@ StatusColorSpace 0.0 StatusColorSpace.qml
StatusCommunityCard 0.1 StatusCommunityCard.qml
StatusCommunityTags 0.1 StatusCommunityTags.qml
StatusItemSelector 0.1 StatusItemSelector.qml
+StatusCard 0.1 StatusCard.qml
diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml b/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml
index 2b8b6b0a9a..3718fbe0f4 100644
--- a/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml
+++ b/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml
@@ -83,6 +83,73 @@ QtObject {
function isHtml(text) {
return (/<\/?[a-z][\s\S]*>/i.test(text))
}
+
+ // function to draw arrow
+ function drawArrow(context, fromx, fromy, tox, toy, color) {
+ const dx = tox - fromx;
+ const dy = toy - fromy;
+ const headlen = 10; // length of head in pixels
+ const angle = 0
+ const radius = 5
+
+ context.strokeStyle = color ? color : '#627EEA'
+
+ // straight line
+ if(dy === 0) {
+ // draw semicircle
+ context.beginPath();
+ context.arc(fromx, fromy, radius, 3*Math.PI/2, Math.PI/2,false);
+ context.stroke();
+
+ // draw straightline
+ // context.setLineDash([5]);
+ context.beginPath();
+ context.moveTo(fromx + radius, fromy);
+ context.lineTo(tox, toy);
+ context.stroke();
+
+ // draw arrow
+ context.beginPath();
+ context.moveTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
+ context.lineTo(tox, toy );
+ context.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
+ context.stroke();
+ }
+ // connecting between 2 different y positions
+ else {
+
+ // draw semicircle
+ context.beginPath();
+ context.arc(fromx, fromy, radius, 3*Math.PI/2, Math.PI/2,false);
+ context.stroke();
+
+ // draw bent line
+ context.beginPath();
+ context.moveTo(fromx + radius, fromy);
+ context.lineTo(fromx + dx / 2, fromy);
+ context.lineTo(fromx + dx / 2, toy - radius);
+ context.stroke();
+
+ // draw connecting circle
+ context.beginPath();
+ context.moveTo(fromx + dx / 2 + radius, toy);
+ context.arc(fromx + dx / 2, toy, radius, 0, 2*Math.PI,false);
+ context.stroke();
+
+ // draw straightline
+ context.beginPath();
+ context.moveTo(fromx + dx / 2 + radius, toy);
+ context.lineTo(tox, toy);
+ context.stroke();
+
+ // draw arrow
+ context.beginPath();
+ context.moveTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
+ context.lineTo(tox, toy );
+ context.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
+ context.stroke();
+ }
+ }
}
diff --git a/ui/StatusQ/src/assets/img/icons/lock.svg b/ui/StatusQ/src/assets/img/icons/lock.svg
new file mode 100644
index 0000000000..fbbb654843
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/lock.svg
@@ -0,0 +1,3 @@
+
diff --git a/ui/StatusQ/src/assets/img/icons/unlock.svg b/ui/StatusQ/src/assets/img/icons/unlock.svg
new file mode 100644
index 0000000000..50bb2b0e3f
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/unlock.svg
@@ -0,0 +1,3 @@
+