Alex Jbanca fd99b96cb5 feat(WalletConnect): Handle sign request expiration
Implementing the user-story for sign request expiry and add qml tests
+ other minor fixes

## Acceptance Criteria

```
//Always show the expiration
Given the sign/transaction request dialog is shown
When request has an expiration date
Then the user sees a 1 minute countdown in the dialog
```

```
// Show 1 minute timer
Given the sign/transaction request dialog is shown
When the request has 1 minute or less before expiring
Then the user sees a 1 second countdown in the dialog
```

```
Given the sign/transaction dialog is open
When the request expires
Then the Accept button is removed
And the only option for the user is to close the dialog
```

```
Given the sign/transaction request dialog is open
When the request expired
Then the `Sign` and `Reject` buttons are removed
And the `Close` button is visible
```

```
Given the sign/transaction request expired
Then a toast message is showing
And it contains the "<dapp domain> sign request timed out" message
```

```
Given the sign/transaction request dialog is open
When the request expired
Then the sign/transaction request dialog is still visible
```

```
Given the sign/transaction request expires
Then a console message is shown
And it contains 'WC WalletConnectSDK.onSessionRequestExpire; id: ${id}`'
```
2024-10-10 12:49:15 +03:00

126 lines
3.8 KiB
QML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick 2.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import AppLayouts.Communities.controls 1.0
IssuePill {
id: root
// request timestamp
required property date timestamp
onTimestampChanged: Qt.callLater(reset)
// expiration timeout in seconds; min 5 minutes, max 7 days
required property int expirationSeconds
onExpirationSecondsChanged: Qt.callLater(reset)
readonly property bool isExpired: remainingSeconds <= 0
readonly property int remainingSeconds: d.secsDiff
signal expired
iconLoaderComponent: StatusCircularProgressBar {
value: d.progress
primaryColor: root.baseColor
secondaryColor: root.background.border.color
}
implicitHeight: 32
text: d.secsDiff < 0 ? qsTr("Expired") : d.formatSeconds(d.secsDiff + 1)
type: {
if (d.secsDiff < 60) // under 1 minute
return CountdownPill.Type.Error
if (d.secsDiff < 60 * 5) // under 5 minutes
return CountdownPill.Type.Warning
return CountdownPill.Type.Primary
}
font.family: Theme.palette.codeFont.name
function reset() {
if (expirationSeconds === 0) {
timer.stop()
d.secsDiff = -1
root.expired()
return
}
const newTimestamp = timestamp
newTimestamp.setSeconds(newTimestamp.getSeconds() + expirationSeconds)
d.expirationTimestamp = newTimestamp
d.ticker = 0
d.secsDiff = 0
if (d.expirationTimestamp <= new Date()) {
console.warn("Expiration time set in past, or expired already on:", d.expirationTimestamp)
d.secsDiff = -1
timer.stop()
root.expired()
return
}
timer.restart()
}
QtObject {
id: d
readonly property real progress: d.secsDiff >= 0 ? d.secsDiff/(d.expirationTimestamp.valueOf() - timestamp.valueOf()) * 1000
: 0
property date expirationTimestamp: root.timestamp
property int secsDiff
property int ticker
function formatSeconds(seconds) {
const days = Math.floor(seconds / 86400)
const hrs = Math.floor(seconds / 3600) % 24
const mins = Math.floor(seconds / 60) % 60
const result = []
if (days > 0)
result.push(qsTr("%1d", "x days").arg(days))
if (hrs > 0)
result.push(qsTr("%1h", "x hours").arg(hrs))
if (mins > 0) {
if (days === 0 && hrs === 0 ) // long form
result.push(qsTr("%nmin(s)", "", mins))
else
result.push(qsTr("%1m", "x minutes").arg(mins))
}
if (days === 0 && hrs === 0 && mins === 0) {
const secs = Math.floor(seconds)
if (secs >= 0)
result.push(qsTr("%nsec(s)", "", secs))
}
return result.join(" ")
}
}
Timer {
id: timer
repeat: true
interval: 1000
triggeredOnStart: true
onTriggered: {
d.ticker++
d.secsDiff = (d.expirationTimestamp.valueOf() - root.timestamp.valueOf() - d.ticker*1000)/1000
if (d.secsDiff < 0) { // we let it run 1 more second to finish the animation
timer.stop()
root.expired()
}
}
}
StatusToolTip {
id: tooltip
visible: root.hovered && !!text
text: root.isExpired ? qsTr("Expired on: %1").arg(LocaleUtils.formatDateTime(d.expirationTimestamp))
: qsTr("Expires on: %1").arg(LocaleUtils.formatDateTime(d.expirationTimestamp))
}
}