2020-06-17 19:18:31 +00:00
|
|
|
import QtQuick 2.13
|
|
|
|
import QtQuick.Controls 2.13
|
2020-08-13 08:24:51 +00:00
|
|
|
import QtQuick.Layouts 1.13
|
2020-08-20 04:45:29 +00:00
|
|
|
import QtQuick.Dialogs 1.3
|
2020-05-27 20:50:39 +00:00
|
|
|
import "../../../imports"
|
|
|
|
import "../../../shared"
|
2020-08-20 04:45:29 +00:00
|
|
|
import "../../../shared/status"
|
2020-06-04 20:56:44 +00:00
|
|
|
import "./components"
|
2020-05-27 20:50:39 +00:00
|
|
|
|
2020-06-26 16:08:51 +00:00
|
|
|
ModalPopup {
|
2020-08-20 04:45:29 +00:00
|
|
|
id: root
|
2020-10-28 07:44:09 +00:00
|
|
|
property alias selectFromAccount: selectFromAccount
|
|
|
|
property alias selectRecipient: selectRecipient
|
|
|
|
property alias stack: stack
|
2020-06-26 16:08:51 +00:00
|
|
|
|
2020-07-06 20:39:55 +00:00
|
|
|
//% "Send"
|
|
|
|
title: qsTrId("command-button-send")
|
2021-07-28 12:53:48 +00:00
|
|
|
height: 540
|
2020-06-26 16:08:51 +00:00
|
|
|
|
2020-08-20 04:45:29 +00:00
|
|
|
property MessageDialog sendingError: MessageDialog {
|
|
|
|
id: sendingError
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Error sending the transaction"
|
|
|
|
title: qsTrId("error-sending-the-transaction")
|
2020-08-20 04:45:29 +00:00
|
|
|
icon: StandardIcon.Critical
|
|
|
|
standardButtons: StandardButton.Ok
|
2020-05-27 20:50:39 +00:00
|
|
|
}
|
2020-08-20 04:45:29 +00:00
|
|
|
|
|
|
|
function sendTransaction() {
|
2020-09-11 14:50:22 +00:00
|
|
|
stack.currentGroup.isPending = true
|
2021-06-29 13:25:05 +00:00
|
|
|
let success = false
|
|
|
|
if(txtAmount.selectedAsset.address == ""){
|
|
|
|
success = walletModel.transactionsView.transferEth(
|
|
|
|
selectFromAccount.selectedAccount.address,
|
|
|
|
selectRecipient.selectedRecipient.address,
|
|
|
|
txtAmount.selectedAmount,
|
|
|
|
gasSelector.selectedGasLimit,
|
|
|
|
gasSelector.selectedGasPrice,
|
|
|
|
transactionSigner.enteredPassword,
|
|
|
|
stack.uuid)
|
|
|
|
} else {
|
|
|
|
success = walletModel.transactionsView.transferTokens(
|
|
|
|
selectFromAccount.selectedAccount.address,
|
2020-08-20 04:45:29 +00:00
|
|
|
selectRecipient.selectedRecipient.address,
|
|
|
|
txtAmount.selectedAsset.address,
|
|
|
|
txtAmount.selectedAmount,
|
|
|
|
gasSelector.selectedGasLimit,
|
|
|
|
gasSelector.selectedGasPrice,
|
2020-10-07 02:47:21 +00:00
|
|
|
transactionSigner.enteredPassword,
|
|
|
|
stack.uuid)
|
2021-06-29 13:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!success){
|
2021-07-16 20:22:50 +00:00
|
|
|
//% "Invalid transaction parameters"
|
|
|
|
sendingError.text = qsTrId("invalid-transaction-parameters")
|
2021-06-30 12:32:14 +00:00
|
|
|
sendingError.open()
|
|
|
|
root.close()
|
2021-06-29 13:25:05 +00:00
|
|
|
}
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TransactionStackView {
|
|
|
|
id: stack
|
|
|
|
anchors.fill: parent
|
|
|
|
anchors.leftMargin: Style.current.padding
|
|
|
|
anchors.rightMargin: Style.current.padding
|
|
|
|
onGroupActivated: {
|
|
|
|
root.title = group.headerText
|
2020-09-11 14:50:22 +00:00
|
|
|
btnNext.text = group.footerText
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
TransactionFormGroup {
|
|
|
|
id: group1
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Send"
|
|
|
|
headerText: qsTrId("command-button-send")
|
|
|
|
//% "Continue"
|
|
|
|
footerText: qsTrId("continue")
|
2020-08-20 04:45:29 +00:00
|
|
|
|
|
|
|
AccountSelector {
|
|
|
|
id: selectFromAccount
|
2021-06-08 12:48:31 +00:00
|
|
|
accounts: walletModel.accountsView.accounts
|
2021-04-22 04:03:46 +00:00
|
|
|
selectedAccount: {
|
2021-06-08 12:48:31 +00:00
|
|
|
const currAcc = walletModel.accountsView.currentAccount
|
2021-04-22 04:03:46 +00:00
|
|
|
if (currAcc.walletType !== Constants.watchWalletType) {
|
|
|
|
return currAcc
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
2021-06-08 12:48:31 +00:00
|
|
|
currency: walletModel.balanceView.defaultCurrency
|
2020-08-20 04:45:29 +00:00
|
|
|
width: stack.width
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "From account"
|
|
|
|
label: qsTrId("from-account")
|
2020-10-07 02:47:21 +00:00
|
|
|
onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
SeparatorWithIcon {
|
|
|
|
id: separator
|
|
|
|
anchors.top: selectFromAccount.bottom
|
|
|
|
anchors.topMargin: 19
|
|
|
|
}
|
|
|
|
RecipientSelector {
|
|
|
|
id: selectRecipient
|
2021-06-08 12:48:31 +00:00
|
|
|
accounts: walletModel.accountsView.accounts
|
2020-12-06 22:15:51 +00:00
|
|
|
contacts: profileModel.contacts.addedContacts
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Recipient"
|
|
|
|
label: qsTrId("recipient")
|
2020-08-20 04:45:29 +00:00
|
|
|
anchors.top: separator.bottom
|
|
|
|
anchors.topMargin: 10
|
|
|
|
width: stack.width
|
2020-10-07 02:47:21 +00:00
|
|
|
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
TransactionFormGroup {
|
|
|
|
id: group2
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Send"
|
|
|
|
headerText: qsTrId("command-button-send")
|
|
|
|
//% "Preview"
|
|
|
|
footerText: qsTrId("preview")
|
2020-08-20 04:45:29 +00:00
|
|
|
|
|
|
|
AssetAndAmountInput {
|
|
|
|
id: txtAmount
|
|
|
|
selectedAccount: selectFromAccount.selectedAccount
|
2021-06-08 12:48:31 +00:00
|
|
|
defaultCurrency: walletModel.balanceView.defaultCurrency
|
|
|
|
getFiatValue: walletModel.balanceView.getFiatValue
|
|
|
|
getCryptoValue: walletModel.balanceView.getCryptoValue
|
2020-08-20 04:45:29 +00:00
|
|
|
width: stack.width
|
2020-10-07 02:47:21 +00:00
|
|
|
onSelectedAssetChanged: if (isValid) { gasSelector.estimateGas() }
|
|
|
|
onSelectedAmountChanged: if (isValid) { gasSelector.estimateGas() }
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
GasSelector {
|
|
|
|
id: gasSelector
|
|
|
|
anchors.top: txtAmount.bottom
|
2020-09-01 03:49:05 +00:00
|
|
|
anchors.topMargin: Style.current.bigPadding * 2
|
2021-06-08 12:48:31 +00:00
|
|
|
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice)
|
|
|
|
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
|
|
|
|
getGasEthValue: walletModel.gasView.getGasEthValue
|
|
|
|
getFiatValue: walletModel.balanceView.getFiatValue
|
|
|
|
defaultCurrency: walletModel.balanceView.defaultCurrency
|
2020-08-20 04:45:29 +00:00
|
|
|
width: stack.width
|
feat: enable token transactions
Fixes #788.
Fixes #853.
Fixes #856.
refactor: gas estimation and transaction sends have been abstracted to allow calling `estimateGas`, `send`, and `call` on the contract method (similar to the web3 API).
Moved sticker pack gas estimation and purchase tx over to the new API
*Sticker purchase:*
- gas estimate is done using new API and debounced using a timer
*Wallet send transaction:*
- tokens can now be sent
- gas is estimated correctly for a token tx, and debounced using a timer
***NOTE***
1. If attempting to send tokens on testnet, you must use a custom token as the token addresses in the pre-built list are for mainnet and will not work on testnet.
2. The new API should support all existing gas estimates, send txs, and calls. The loading of sticker pack data, balance, count, purchased sticker packs, etc, can be moved over to the new API. Almost all of the `eth_sendTransaction`, `eth_gasEstimate`, and `eth_call` could be move over as well (that's the idea at least).
2020-09-07 09:39:17 +00:00
|
|
|
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
|
|
|
|
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
|
|
|
|
selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address &&
|
|
|
|
txtAmount.selectedAsset && txtAmount.selectedAsset.address &&
|
|
|
|
txtAmount.selectedAmount)) return
|
|
|
|
|
2021-06-08 12:48:31 +00:00
|
|
|
let gasEstimate = JSON.parse(walletModel.gasView.estimateGas(
|
feat: enable token transactions
Fixes #788.
Fixes #853.
Fixes #856.
refactor: gas estimation and transaction sends have been abstracted to allow calling `estimateGas`, `send`, and `call` on the contract method (similar to the web3 API).
Moved sticker pack gas estimation and purchase tx over to the new API
*Sticker purchase:*
- gas estimate is done using new API and debounced using a timer
*Wallet send transaction:*
- tokens can now be sent
- gas is estimated correctly for a token tx, and debounced using a timer
***NOTE***
1. If attempting to send tokens on testnet, you must use a custom token as the token addresses in the pre-built list are for mainnet and will not work on testnet.
2. The new API should support all existing gas estimates, send txs, and calls. The loading of sticker pack data, balance, count, purchased sticker packs, etc, can be moved over to the new API. Almost all of the `eth_sendTransaction`, `eth_gasEstimate`, and `eth_call` could be move over as well (that's the idea at least).
2020-09-07 09:39:17 +00:00
|
|
|
selectFromAccount.selectedAccount.address,
|
|
|
|
selectRecipient.selectedRecipient.address,
|
|
|
|
txtAmount.selectedAsset.address,
|
2020-10-29 23:08:07 +00:00
|
|
|
txtAmount.selectedAmount,
|
|
|
|
""))
|
feat: enable token transactions
Fixes #788.
Fixes #853.
Fixes #856.
refactor: gas estimation and transaction sends have been abstracted to allow calling `estimateGas`, `send`, and `call` on the contract method (similar to the web3 API).
Moved sticker pack gas estimation and purchase tx over to the new API
*Sticker purchase:*
- gas estimate is done using new API and debounced using a timer
*Wallet send transaction:*
- tokens can now be sent
- gas is estimated correctly for a token tx, and debounced using a timer
***NOTE***
1. If attempting to send tokens on testnet, you must use a custom token as the token addresses in the pre-built list are for mainnet and will not work on testnet.
2. The new API should support all existing gas estimates, send txs, and calls. The loading of sticker pack data, balance, count, purchased sticker packs, etc, can be moved over to the new API. Almost all of the `eth_sendTransaction`, `eth_gasEstimate`, and `eth_call` could be move over as well (that's the idea at least).
2020-09-07 09:39:17 +00:00
|
|
|
|
2020-09-15 08:53:56 +00:00
|
|
|
if (!gasEstimate.success) {
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Error estimating gas: %1"
|
|
|
|
console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message))
|
feat: enable token transactions
Fixes #788.
Fixes #853.
Fixes #856.
refactor: gas estimation and transaction sends have been abstracted to allow calling `estimateGas`, `send`, and `call` on the contract method (similar to the web3 API).
Moved sticker pack gas estimation and purchase tx over to the new API
*Sticker purchase:*
- gas estimate is done using new API and debounced using a timer
*Wallet send transaction:*
- tokens can now be sent
- gas is estimated correctly for a token tx, and debounced using a timer
***NOTE***
1. If attempting to send tokens on testnet, you must use a custom token as the token addresses in the pre-built list are for mainnet and will not work on testnet.
2. The new API should support all existing gas estimates, send txs, and calls. The loading of sticker pack data, balance, count, purchased sticker packs, etc, can be moved over to the new API. Almost all of the `eth_sendTransaction`, `eth_gasEstimate`, and `eth_call` could be move over as well (that's the idea at least).
2020-09-07 09:39:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
selectedGasLimit = gasEstimate.result
|
|
|
|
})
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
2020-09-01 03:49:05 +00:00
|
|
|
GasValidator {
|
|
|
|
id: gasValidator
|
2021-07-28 12:53:48 +00:00
|
|
|
anchors.top: gasSelector.bottom
|
|
|
|
anchors.topMargin: 8
|
2020-09-01 03:49:05 +00:00
|
|
|
selectedAccount: selectFromAccount.selectedAccount
|
|
|
|
selectedAmount: parseFloat(txtAmount.selectedAmount)
|
|
|
|
selectedAsset: txtAmount.selectedAsset
|
|
|
|
selectedGasEthValue: gasSelector.selectedGasEthValue
|
|
|
|
}
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
TransactionFormGroup {
|
|
|
|
id: group3
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Transaction preview"
|
|
|
|
headerText: qsTrId("transaction-preview")
|
|
|
|
//% "Sign with password"
|
|
|
|
footerText: qsTrId("sign-with-password")
|
2020-08-20 04:45:29 +00:00
|
|
|
|
|
|
|
TransactionPreview {
|
|
|
|
id: pvwTransaction
|
|
|
|
width: stack.width
|
|
|
|
fromAccount: selectFromAccount.selectedAccount
|
|
|
|
gas: {
|
2020-09-01 03:49:05 +00:00
|
|
|
"value": gasSelector.selectedGasEthValue,
|
|
|
|
"symbol": "ETH",
|
|
|
|
"fiatValue": gasSelector.selectedGasFiatValue
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
toAccount: selectRecipient.selectedRecipient
|
|
|
|
asset: txtAmount.selectedAsset
|
|
|
|
amount: { "value": txtAmount.selectedAmount, "fiatValue": txtAmount.selectedFiatAmount }
|
2021-06-08 12:48:31 +00:00
|
|
|
currency: walletModel.balanceView.defaultCurrency
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
2020-10-19 09:39:07 +00:00
|
|
|
SendToContractWarning {
|
|
|
|
id: sendToContractWarning
|
|
|
|
anchors.top: pvwTransaction.bottom
|
|
|
|
selectedRecipient: selectRecipient.selectedRecipient
|
|
|
|
}
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
|
|
|
TransactionFormGroup {
|
|
|
|
id: group4
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Sign with password"
|
|
|
|
headerText: qsTrId("sign-with-password")
|
|
|
|
//% "Send %1 %2"
|
|
|
|
footerText: qsTrId("send--1--2").arg(txtAmount.selectedAmount).arg(!!txtAmount.selectedAsset ? txtAmount.selectedAsset.symbol : "")
|
2020-05-27 20:50:39 +00:00
|
|
|
|
2020-08-20 04:45:29 +00:00
|
|
|
TransactionSigner {
|
|
|
|
id: transactionSigner
|
|
|
|
width: stack.width
|
2021-06-08 12:48:31 +00:00
|
|
|
signingPhrase: walletModel.utilsView.signingPhrase
|
2020-08-20 04:45:29 +00:00
|
|
|
}
|
2020-07-01 14:24:07 +00:00
|
|
|
}
|
2020-05-27 20:50:39 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:24:51 +00:00
|
|
|
footer: Item {
|
2021-01-13 19:15:52 +00:00
|
|
|
width: parent.width
|
|
|
|
height: btnNext.height
|
|
|
|
|
2020-10-07 02:47:21 +00:00
|
|
|
StatusRoundButton {
|
2020-08-13 08:24:51 +00:00
|
|
|
id: btnBack
|
|
|
|
anchors.left: parent.left
|
2020-08-20 04:45:29 +00:00
|
|
|
visible: !stack.isFirstGroup
|
2020-10-07 02:47:21 +00:00
|
|
|
icon.name: "arrow-right"
|
|
|
|
icon.width: 20
|
|
|
|
icon.height: 16
|
|
|
|
rotation: 180
|
2020-08-13 08:24:51 +00:00
|
|
|
onClicked: {
|
2020-08-20 04:45:29 +00:00
|
|
|
stack.back()
|
2020-08-13 08:24:51 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-11 14:50:22 +00:00
|
|
|
StatusButton {
|
2020-08-20 04:45:29 +00:00
|
|
|
id: btnNext
|
2020-08-13 08:24:51 +00:00
|
|
|
anchors.right: parent.right
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Next"
|
|
|
|
text: qsTrId("next")
|
2020-09-17 09:08:31 +00:00
|
|
|
enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
|
2020-09-11 14:50:22 +00:00
|
|
|
state: stack.currentGroup.isPending ? "pending" : "default"
|
2020-08-13 08:24:51 +00:00
|
|
|
onClicked: {
|
2020-09-09 11:04:01 +00:00
|
|
|
const validity = stack.currentGroup.validate()
|
|
|
|
if (validity.isValid && !validity.isPending) {
|
2020-08-20 04:45:29 +00:00
|
|
|
if (stack.isLastGroup) {
|
|
|
|
return root.sendTransaction()
|
|
|
|
}
|
|
|
|
stack.next()
|
2020-08-19 15:29:42 +00:00
|
|
|
}
|
2020-08-13 08:24:51 +00:00
|
|
|
}
|
2020-09-10 18:55:24 +00:00
|
|
|
}
|
2020-09-14 14:58:21 +00:00
|
|
|
|
2020-09-10 18:55:24 +00:00
|
|
|
Connections {
|
2021-06-08 12:48:31 +00:00
|
|
|
target: walletModel.transactionsView
|
2020-09-10 18:55:24 +00:00
|
|
|
onTransactionWasSent: {
|
|
|
|
try {
|
|
|
|
let response = JSON.parse(txResult)
|
|
|
|
|
2020-10-07 02:47:21 +00:00
|
|
|
if (response.uuid !== stack.uuid) return
|
|
|
|
|
|
|
|
stack.currentGroup.isPending = false
|
|
|
|
|
2020-09-15 08:53:56 +00:00
|
|
|
if (!response.success) {
|
fix: prevent crash on generate account wrong password
Fixes #2448.
Currently, if a wrong password is entered when generating a wallet account, the app will crash due to attempting to decode a `GeneratedAccount ` from an rpc response containing only an error.
With this PR, we are detecting if an error is returned in the response, and if so, raising a StatusGoException. This exception is caught in the call chain, and translated in to a `StatusGoError` which is serialised and sent to the QML view, where it is parsed and displayed as an invalid password error in the input box.
refactor: remove string return values as error messages in wallet model
In the wallet model, we were passing back empty strings for no error, or an error as a string. This is not only confusing, but does not benefit from leaning on the compiler and strong types. One has to read the entire code to understand if a string result is returned when there is no error instead of implicitly being able to understand there is no return type.
To alleviate this, account creation fundtions that do not need to return a value have been changed to a void return type, and raise `StatusGoException` if there is an error encountered. This can be caught in the call chain and used as necessary (ie to pass to QML).
refactor: move invalid password string detection to Utils
Currently, we are reading returned view model values and checking to see if they include a known string from Status Go that means there was an invalid password used. This string was placed in the codebased in mulitple locations.
This PR moves the string check to a Utils function and updates all the references to use the function in Utils.
2021-05-13 04:41:48 +00:00
|
|
|
if (Utils.isInvalidPasswordMessage(response.result)){
|
2020-09-14 12:12:47 +00:00
|
|
|
//% "Wrong password"
|
|
|
|
transactionSigner.validationError = qsTrId("wrong-password")
|
2020-09-10 18:55:24 +00:00
|
|
|
return
|
|
|
|
}
|
2020-09-15 08:53:56 +00:00
|
|
|
sendingError.text = response.result
|
2020-09-10 18:55:24 +00:00
|
|
|
return sendingError.open()
|
|
|
|
}
|
|
|
|
|
2020-09-18 06:33:20 +00:00
|
|
|
//% "Transaction pending..."
|
|
|
|
toastMessage.title = qsTrId("ens-transaction-pending")
|
2020-09-14 14:58:21 +00:00
|
|
|
toastMessage.source = "../../img/loading.svg"
|
|
|
|
toastMessage.iconColor = Style.current.primary
|
|
|
|
toastMessage.iconRotates = true
|
2021-06-08 12:48:31 +00:00
|
|
|
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${response.result}`
|
2020-09-11 17:31:13 +00:00
|
|
|
toastMessage.open()
|
|
|
|
root.close()
|
2020-09-10 18:55:24 +00:00
|
|
|
} catch (e) {
|
2020-09-11 17:31:13 +00:00
|
|
|
console.error('Error parsing the response', e)
|
2020-09-10 18:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-14 14:58:21 +00:00
|
|
|
onTransactionCompleted: {
|
|
|
|
if (success) {
|
2020-09-18 06:33:20 +00:00
|
|
|
//% "Transaction completed"
|
|
|
|
toastMessage.title = qsTrId("transaction-completed")
|
2020-09-14 14:58:21 +00:00
|
|
|
toastMessage.source = "../../img/check-circle.svg"
|
|
|
|
toastMessage.iconColor = Style.current.success
|
|
|
|
} else {
|
2020-09-18 06:33:20 +00:00
|
|
|
//% "Transaction failed"
|
|
|
|
toastMessage.title = qsTrId("ens-registration-failed-title")
|
2020-09-14 14:58:21 +00:00
|
|
|
toastMessage.source = "../../img/block-icon.svg"
|
|
|
|
toastMessage.iconColor = Style.current.danger
|
|
|
|
}
|
2021-06-08 12:48:31 +00:00
|
|
|
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
|
2020-09-14 14:58:21 +00:00
|
|
|
toastMessage.open()
|
|
|
|
}
|
2020-05-27 20:50:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*##^##
|
|
|
|
Designer {
|
|
|
|
D{i:0;autoSize:true;height:480;width:640}
|
|
|
|
}
|
|
|
|
##^##*/
|
2020-05-29 15:43:37 +00:00
|
|
|
|