fe226c4bf5
Replace all user-visible "Bitcoin" and "Electrum" strings across Qt and QML GUIs with "Bitcoin Purple" and "Electrum Purple" respectively. Update the Help menu: replace the Bitcoin Paper link with the Bitcoin Purple whitepaper and point the official website to bitcoinpurpleblockchain.com. Remove the "Distributed by Electrum Technologies GmbH" attribution from the About dialog. No code identifiers, class names or technical references were modified.
335 lines
13 KiB
QML
335 lines
13 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
import QtQuick.Controls.Material
|
|
|
|
import org.electrum 1.0
|
|
|
|
import "controls"
|
|
|
|
ElDialog {
|
|
id: root
|
|
|
|
title: qsTr("Open Lightning Channel")
|
|
iconSource: Qt.resolvedUrl('../../icons/lightning.png')
|
|
|
|
padding: 0
|
|
|
|
width: parent.width
|
|
height: parent.height
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
Flickable {
|
|
Layout.preferredWidth: parent.width
|
|
Layout.fillHeight: true
|
|
|
|
leftMargin: constants.paddingLarge
|
|
rightMargin: constants.paddingLarge
|
|
|
|
contentHeight: rootLayout.height
|
|
clip:true
|
|
interactive: height < contentHeight
|
|
|
|
GridLayout {
|
|
id: rootLayout
|
|
width: parent.width
|
|
|
|
columns: 3
|
|
|
|
InfoTextArea {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 3
|
|
visible: !Daemon.currentWallet.lightningHasDeterministicNodeId
|
|
iconStyle: InfoTextArea.IconStyle.Warn
|
|
text: Daemon.currentWallet.seedType == 'segwit'
|
|
? [ qsTr('Your channels cannot be recovered from seed, because they were created with an old version of Electrum Purple.'), ' ',
|
|
qsTr('This means that you must save a backup of your wallet every time you create a new channel.'),
|
|
'\n\n',
|
|
qsTr('If you want this wallet to have recoverable channels, you must close your existing channels and restore this wallet from seed.')
|
|
].join('')
|
|
: [ qsTr('Your channels cannot be recovered from seed.'), ' ',
|
|
qsTr('This means that you must save a backup of your wallet every time you create a new channel.'),
|
|
'\n\n',
|
|
qsTr('If you want to have recoverable channels, you must create a new wallet with an Electrum Purple seed')
|
|
].join('')
|
|
backgroundColor: constants.darkerDialogBackground
|
|
}
|
|
|
|
InfoTextArea {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 3
|
|
visible: Daemon.currentWallet.lightningHasDeterministicNodeId && !Config.useRecoverableChannels
|
|
iconStyle: InfoTextArea.IconStyle.Warn
|
|
text: [ qsTr('You currently have recoverable channels setting disabled.'),
|
|
qsTr('This means your channels cannot be recovered from seed.')
|
|
].join(' ')
|
|
backgroundColor: constants.darkerDialogBackground
|
|
}
|
|
|
|
// gossip
|
|
RowLayout {
|
|
Layout.columnSpan: 3
|
|
visible: Config.useGossip
|
|
spacing: 0
|
|
|
|
TextArea {
|
|
id: nodeUri
|
|
visible: Config.useGossip
|
|
Layout.fillWidth: true
|
|
Layout.minimumHeight: nodeUriFontMetrics.lineSpacing * 4 + topPadding + bottomPadding
|
|
Layout.topMargin: constants.paddingSmall
|
|
font.family: FixedFont
|
|
wrapMode: Text.Wrap
|
|
placeholderText: qsTr('Paste or scan node uri/pubkey')
|
|
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
|
onTextChanged: {
|
|
if (activeFocus)
|
|
channelopener.connectStr = text
|
|
}
|
|
onActiveFocusChanged: {
|
|
if (!activeFocus)
|
|
channelopener.connectStr = text
|
|
}
|
|
}
|
|
ColumnLayout {
|
|
spacing: 0
|
|
ToolButton {
|
|
icon.source: '../../icons/paste.png'
|
|
icon.height: constants.iconSizeMedium
|
|
icon.width: constants.iconSizeMedium
|
|
onClicked: {
|
|
var cliptext = AppController.clipboardToText()
|
|
if (!cliptext)
|
|
return
|
|
if (channelopener.validateConnectString(cliptext)) {
|
|
channelopener.connectStr = cliptext
|
|
nodeUri.text = channelopener.connectStr
|
|
} else {
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
text: qsTr('Invalid node-id or connect string')
|
|
})
|
|
dialog.open()
|
|
}
|
|
}
|
|
}
|
|
ToolButton {
|
|
icon.source: '../../icons/qrcode.png'
|
|
icon.height: constants.iconSizeMedium
|
|
icon.width: constants.iconSizeMedium
|
|
onClicked: {
|
|
var dialog = app.scanDialog.createObject(app, {
|
|
hint: qsTr('Scan a node-id or a connect string')
|
|
})
|
|
dialog.onFoundText.connect(function(data) {
|
|
if (channelopener.validateConnectString(data)) {
|
|
channelopener.connectStr = data
|
|
nodeUri.text = channelopener.connectStr
|
|
} else {
|
|
var errdialog = app.messageDialog.createObject(app, {
|
|
text: qsTr('Invalid node-id or connect string')
|
|
})
|
|
errdialog.open()
|
|
}
|
|
dialog.close()
|
|
})
|
|
dialog.open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// trampoline
|
|
ComboBox {
|
|
visible: !Config.useGossip
|
|
Layout.columnSpan: 3
|
|
Layout.fillWidth: true
|
|
model: channelopener.trampolineNodeNames
|
|
onCurrentValueChanged: {
|
|
if (activeFocus)
|
|
channelopener.connectStr = currentValue
|
|
}
|
|
// preselect a random node
|
|
Component.onCompleted: {
|
|
if (!Config.useGossip) {
|
|
currentIndex = Math.floor(Math.random() * channelopener.trampolineNodeNames.length)
|
|
channelopener.connectStr = currentValue
|
|
}
|
|
}
|
|
}
|
|
|
|
Item { Layout.columnSpan: 3; width: 1; height: constants.paddingLarge }
|
|
|
|
BtcField {
|
|
id: amountBtc
|
|
fiatfield: amountFiat
|
|
Layout.preferredWidth: amountFontMetrics.advanceWidth('0') * 14 + leftPadding + rightPadding
|
|
onTextAsSatsChanged: {
|
|
if (!is_max.checked)
|
|
channelopener.amount = amountBtc.textAsSats
|
|
}
|
|
readOnly: is_max.checked
|
|
color: readOnly
|
|
? Material.accentColor
|
|
: Material.foreground
|
|
|
|
Connections {
|
|
target: channelopener.amount
|
|
function onSatsIntChanged() {
|
|
if (is_max.checked) // amount updated by max amount estimate
|
|
amountBtc.text = Config.formatSatsForEditing(channelopener.amount.satsInt)
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Label {
|
|
text: Config.baseUnit
|
|
color: Material.accentColor
|
|
}
|
|
Switch {
|
|
id: is_max
|
|
text: qsTr('Max')
|
|
onCheckedChanged: {
|
|
if (activeFocus) {
|
|
channelopener.amount.isMax = checked
|
|
if (checked) {
|
|
channelopener.updateMaxAmount()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Item { width: 1; height: 1; visible: Daemon.fx.enabled }
|
|
|
|
FiatField {
|
|
id: amountFiat
|
|
Layout.preferredWidth: amountFontMetrics.advanceWidth('0') * 14 + leftPadding + rightPadding
|
|
btcfield: amountBtc
|
|
visible: Daemon.fx.enabled
|
|
readOnly: is_max.checked
|
|
color: readOnly
|
|
? Material.accentColor
|
|
: Material.foreground
|
|
}
|
|
|
|
Label {
|
|
visible: Daemon.fx.enabled
|
|
text: Daemon.fx.fiatCurrency
|
|
color: Material.accentColor
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Item { visible: Daemon.fx.enabled ; height: 1; width: 1 }
|
|
|
|
InfoTextArea {
|
|
id: warning
|
|
Layout.topMargin: constants.paddingMedium
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 3
|
|
text: channelopener.warning
|
|
visible: text
|
|
compact: true
|
|
backgroundColor: constants.darkerDialogBackground
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
DialogButtonContainer {
|
|
Layout.fillWidth: true
|
|
FlatButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr('Open Channel...')
|
|
icon.source: '../../icons/confirmed.png'
|
|
enabled: channelopener.valid
|
|
onClicked: channelopener.openChannel()
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: confirmOpenChannelDialog
|
|
ConfirmTxDialog {
|
|
amountLabelText: qsTr('Channel capacity')
|
|
sendButtonText: qsTr('Open Channel')
|
|
finalizer: channelopener.finalizer
|
|
}
|
|
}
|
|
|
|
ChannelOpener {
|
|
id: channelopener
|
|
wallet: Daemon.currentWallet
|
|
onAuthRequired: (method, authMessage) => {
|
|
app.handleAuthRequired(channelopener, method, authMessage)
|
|
}
|
|
onValidationError: (code, message) => {
|
|
if (code == 'invalid_nodeid') {
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
title: qsTr('Error'),
|
|
iconSource: Qt.resolvedUrl('../../icons/warning.png'),
|
|
text: message
|
|
})
|
|
dialog.open()
|
|
}
|
|
}
|
|
onConflictingBackup: (message) => {
|
|
var dialog = app.messageDialog.createObject(app, {
|
|
text: message,
|
|
yesno: true
|
|
})
|
|
dialog.open()
|
|
dialog.accepted.connect(function() {
|
|
channelopener.openChannel(true)
|
|
})
|
|
}
|
|
onFinalizerChanged: {
|
|
var dialog = confirmOpenChannelDialog.createObject(app, {
|
|
satoshis: channelopener.amount
|
|
})
|
|
dialog.accepted.connect(function() {
|
|
dialog.finalizer.signAndSend()
|
|
})
|
|
dialog.open()
|
|
}
|
|
onChannelOpening: (peer) => {
|
|
console.log('Channel is opening')
|
|
app.channelOpenProgressDialog.resetDialog()
|
|
app.channelOpenProgressDialog.peer = peer
|
|
app.channelOpenProgressDialog.open()
|
|
}
|
|
onChannelOpenError: (message) => {
|
|
app.channelOpenProgressDialog.state = 'failed'
|
|
app.channelOpenProgressDialog.error = message
|
|
}
|
|
onChannelOpenSuccess: (cid, has_onchain_backup, min_depth, tx_complete) => {
|
|
var message = qsTr('Channel established.') + ' '
|
|
+ qsTr('This channel will be usable after %1 confirmations').arg(min_depth)
|
|
if (!tx_complete) {
|
|
message = message + '\n\n' + qsTr('Please sign and broadcast the funding transaction.')
|
|
channelopener.wallet.historyModel.initModel(true) // local tx doesn't trigger model update
|
|
}
|
|
app.channelOpenProgressDialog.state = 'success'
|
|
app.channelOpenProgressDialog.info = message
|
|
if (!has_onchain_backup) {
|
|
app.channelOpenProgressDialog.channelBackup = channelopener.channelBackup(cid)
|
|
}
|
|
// TODO: handle incomplete TX
|
|
root.close()
|
|
}
|
|
}
|
|
|
|
FontMetrics {
|
|
id: amountFontMetrics
|
|
font: amountBtc.font
|
|
}
|
|
FontMetrics {
|
|
id: nodeUriFontMetrics
|
|
font: nodeUri.font
|
|
}
|
|
}
|