2023-02-22 17:10:46 +00:00
import QtQuick 2.14
import QtQuick . Layouts 1.14
import StatusQ . Core 0.1
import StatusQ . Core . Theme 0.1
import StatusQ . Controls 0.1
import StatusQ . Controls . Validators 0.1
2023-03-17 15:09:27 +00:00
import StatusQ . Components 0.1
2023-05-22 15:47:06 +00:00
import StatusQ . Core . Utils 0.1 as SQUtils
2023-02-22 17:10:46 +00:00
import utils 1.0
2023-06-23 06:17:04 +00:00
import AppLayouts . Communities . helpers 1.0
2023-07-27 20:29:31 +00:00
import AppLayouts . Communities . panels 1.0
2023-05-04 10:01:59 +00:00
import AppLayouts . Wallet . controls 1.0
2023-02-22 17:10:46 +00:00
import shared . panels 1.0
2023-05-04 10:01:59 +00:00
import shared . popups 1.0
2023-02-22 17:10:46 +00:00
2023-06-08 12:50:33 +00:00
import SortFilterProxyModel 0.2
2023-02-22 17:10:46 +00:00
StatusScrollView {
id: root
property int viewWidth: 560 // by design
2023-05-22 13:00:40 +00:00
property bool isAssetView: false
2023-06-09 16:42:35 +00:00
property int validationMode: StatusInput . ValidationMode . OnlyWhenDirty
2023-05-22 15:47:06 +00:00
property var tokensModel
2023-07-04 13:21:15 +00:00
property var tokensModelWallet
2023-03-07 11:32:45 +00:00
2023-06-22 21:24:30 +00:00
property TokenObject collectible: TokenObject {
2023-07-03 14:44:02 +00:00
type: Constants . TokenType . ERC721
2023-06-22 21:24:30 +00:00
}
property TokenObject asset: TokenObject {
2023-07-03 14:44:02 +00:00
type: Constants . TokenType . ERC20
2023-06-22 21:24:30 +00:00
}
2023-05-22 13:00:40 +00:00
2023-06-09 16:42:35 +00:00
// Used for reference validation when editing a failed deployment
property string referenceName: ""
property string referenceSymbol: ""
2023-03-08 13:44:47 +00:00
// Network related properties:
property var layer1Networks
property var layer2Networks
property var enabledNetworks
property var allNetworks
2023-06-20 10:12:56 +00:00
// Account expected roles: address, name, color, emoji, walletType
2023-03-17 15:09:27 +00:00
property var accounts
2023-07-27 20:29:31 +00:00
property string feeText
property string feeErrorText
property bool isFeeLoading
readonly property string feeLabel:
isAssetView ? qsTr ( "Mint asset on %1" ) . arg ( asset . chainName )
: qsTr ( "Mint collectible on %1" ) . arg ( collectible . chainName )
2023-02-22 17:10:46 +00:00
signal chooseArtWork
signal previewClicked
2023-07-27 20:29:31 +00:00
signal deployFeesRequested
2023-02-22 17:10:46 +00:00
QtObject {
id: d
2023-06-01 10:38:56 +00:00
readonly property bool isFullyFilled: dropAreaItem . artworkSource . toString ( ) . length > 0
2023-05-22 15:47:06 +00:00
&& nameInput . valid
&& descriptionInput . valid
&& symbolInput . valid
2023-06-01 10:38:56 +00:00
&& ( unlimitedSupplyChecker . checked || ( ! unlimitedSupplyChecker . checked && parseInt ( supplyInput . text ) > 0 ) )
&& ( ! root . isAssetView || ( root . isAssetView && assetDecimalsInput . valid ) )
2023-07-27 20:29:31 +00:00
&& ! root . isFeeLoading && root . feeErrorText === "" && ! requestFeeDelayTimer . running
2023-02-22 17:10:46 +00:00
2023-05-22 15:47:06 +00:00
readonly property int imageSelectorRectWidth: root . isAssetView ? 128 : 290
2023-07-04 11:24:22 +00:00
function hasEmoji ( text ) {
return SQUtils . Emoji . hasEmoji ( SQUtils . Emoji . parse ( text ) ) ;
}
2023-02-22 17:10:46 +00:00
}
padding: 0
2023-06-20 06:55:16 +00:00
contentWidth: mainLayout . width
contentHeight: mainLayout . height
2023-02-22 17:10:46 +00:00
2023-06-13 13:55:07 +00:00
Component.onCompleted: {
if ( root . isAssetView )
networkSelector . setChain ( asset . chainId )
else
networkSelector . setChain ( collectible . chainId )
}
2023-02-22 17:10:46 +00:00
ColumnLayout {
id: mainLayout
width: root . viewWidth
spacing: Style . current . padding
2023-05-04 10:01:59 +00:00
StatusBaseText {
elide: Text . ElideRight
font.pixelSize: Theme . primaryTextFontSize
2023-05-22 13:00:40 +00:00
text: root . isAssetView ? qsTr ( "Icon" ) : qsTr ( "Artwork" )
2023-05-04 10:01:59 +00:00
}
2023-05-16 15:07:21 +00:00
DropAndEditImagePanel {
id: dropAreaItem
2023-06-01 10:38:56 +00:00
2023-05-16 15:07:21 +00:00
Layout.fillWidth: true
2023-05-04 10:01:59 +00:00
Layout.preferredHeight: d . imageSelectorRectWidth
2023-06-09 11:50:22 +00:00
dataImage: root . isAssetView ? asset.artworkSource : collectible . artworkSource
artworkSource: root . isAssetView ? asset.artworkSource : collectible . artworkSource
2023-06-22 08:36:52 +00:00
editorAnchorLeft: false
2023-05-22 15:47:06 +00:00
editorRoundedImage: root . isAssetView
uploadTextLabel.uploadText: root . isAssetView ? qsTr ( "Upload" ) : qsTr ( "Drag and Drop or Upload Artwork" )
2023-05-16 15:07:21 +00:00
uploadTextLabel.additionalText: qsTr ( "Images only" )
2023-05-22 15:47:06 +00:00
uploadTextLabel.showAdditionalInfo: ! root . isAssetView
editorTitle: root . isAssetView ? qsTr ( "Asset icon" ) : qsTr ( "Collectible artwork" )
acceptButtonText: root . isAssetView ? qsTr ( "Upload asset icon" ) : qsTr ( "Upload collectible artwork" )
2023-06-01 10:38:56 +00:00
onArtworkSourceChanged: {
if ( root . isAssetView )
asset . artworkSource = artworkSource
else
collectible . artworkSource = artworkSource
}
onArtworkCropRectChanged: {
if ( root . isAssetView )
asset . artworkCropRect = artworkCropRect
else
collectible . artworkCropRect = artworkCropRect
}
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: nameInput
label: qsTr ( "Name" )
2023-06-01 10:38:56 +00:00
text: root . isAssetView ? asset.name : collectible . name
2023-05-11 15:51:50 +00:00
charLimit: 15
2023-02-22 17:10:46 +00:00
placeholderText: qsTr ( "Name" )
2023-06-09 16:42:35 +00:00
validationMode: root . validationMode
2023-05-22 15:47:06 +00:00
minLengthValidator.errorMessage: qsTr ( "Please name your token name (use A-Z and 0-9, hyphens and underscores only)" )
2023-07-04 11:24:22 +00:00
regexValidator.errorMessage: d . hasEmoji ( text ) ?
qsTr ( "Your token name is too cool (use A-Z and 0-9, hyphens and underscores only)" ) :
qsTr ( "Your token name contains invalid characters (use A-Z and 0-9, hyphens and underscores only)" )
2023-06-09 16:42:35 +00:00
extraValidator.validate: function ( value ) {
2023-06-27 19:20:32 +00:00
// If minting failed, we can retry same deployment, so same name allowed
const allowRepeatedName = ( root . isAssetView ? asset.deployState : collectible . deployState ) === Constants . ContractTransactionStatus . Failed
2023-06-09 16:42:35 +00:00
if ( allowRepeatedName )
if ( nameInput . text === root . referenceName )
return true
// Otherwise, no repeated names allowed:
2023-06-27 19:20:32 +00:00
return ! SQUtils . ModelUtils . contains ( root . tokensModel , "name" , nameInput . text , Qt . CaseInsensitive )
2023-06-09 16:42:35 +00:00
}
2023-05-22 15:47:06 +00:00
extraValidator.errorMessage: qsTr ( "You have used this token name before" )
2023-06-01 10:38:56 +00:00
onTextChanged: {
if ( root . isAssetView )
asset . name = text
else
collectible . name = text
}
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: descriptionInput
label: qsTr ( "Description" )
2023-06-01 10:38:56 +00:00
text: root . isAssetView ? asset.description : collectible . description
2023-02-22 17:10:46 +00:00
charLimit: 280
2023-05-22 13:00:40 +00:00
placeholderText: root . isAssetView ? qsTr ( "Describe your asset" ) : qsTr ( "Describe your collectible" )
2023-02-22 17:10:46 +00:00
input.multiline: true
input.verticalAlignment: Qt . AlignTop
input.placeholder.verticalAlignment: Qt . AlignTop
minimumHeight: 108
maximumHeight: minimumHeight
2023-06-09 16:42:35 +00:00
validationMode: root . validationMode
2023-05-22 15:47:06 +00:00
minLengthValidator.errorMessage: qsTr ( "Please enter a token description" )
2023-06-09 16:42:35 +00:00
regexValidator.regularExpression: Constants . regularExpressions . ascii
2023-05-22 15:47:06 +00:00
regexValidator.errorMessage: qsTr ( "Only A-Z, 0-9 and standard punctuation allowed" )
2023-06-01 10:38:56 +00:00
onTextChanged: {
if ( root . isAssetView )
asset . description = text
else
collectible . description = text
}
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: symbolInput
2023-05-11 15:51:50 +00:00
label: qsTr ( "Symbol" )
2023-06-01 10:38:56 +00:00
text: root . isAssetView ? asset.symbol : collectible . symbol
2023-05-11 15:51:50 +00:00
charLimit: 6
2023-06-23 09:15:21 +00:00
placeholderText: root . isAssetView ? qsTr ( "e.g. ETH" ) : qsTr ( "e.g. DOODLE" )
2023-06-09 16:42:35 +00:00
validationMode: root . validationMode
2023-05-22 15:47:06 +00:00
minLengthValidator.errorMessage: qsTr ( "Please enter your token symbol (use A-Z only)" )
2023-07-04 11:24:22 +00:00
regexValidator.errorMessage: d . hasEmoji ( text ) ? qsTr ( "Your token symbol is too cool (use A-Z only)" ) :
qsTr ( "Your token symbol contains invalid characters (use A-Z only)" )
2023-05-22 15:47:06 +00:00
regexValidator.regularExpression: Constants . regularExpressions . capitalOnly
2023-06-09 16:42:35 +00:00
extraValidator.validate: function ( value ) {
2023-06-27 08:15:49 +00:00
// If minting failed, we can retry same deployment, so same symbol allowed
const allowRepeatedName = ( root . isAssetView ? asset.deployState : collectible . deployState ) === Constants . ContractTransactionStatus . Failed
2023-06-09 16:42:35 +00:00
if ( allowRepeatedName )
2023-06-27 08:15:49 +00:00
if ( symbolInput . text . toUpperCase ( ) === root . referenceSymbol . toUpperCase ( ) )
2023-06-09 16:42:35 +00:00
return true
// Otherwise, no repeated names allowed:
2023-07-04 13:21:15 +00:00
return ( ! SQUtils . ModelUtils . contains ( root . tokensModel , "symbol" , symbolInput . text ) &&
! SQUtils . ModelUtils . contains ( root . tokensModelWallet , "symbol" , symbolInput . text ) )
2023-06-09 16:42:35 +00:00
}
2023-07-04 13:21:15 +00:00
extraValidator.errorMessage: SQUtils . ModelUtils . contains ( root . tokensModelWallet , "symbol" , symbolInput . text ) ?
qsTr ( "This token symbol is already in use" ) : qsTr ( "You have used this token symbol before" )
2023-06-01 10:38:56 +00:00
onTextChanged: {
2023-06-27 08:15:49 +00:00
const cursorPos = input . edit . cursorPosition
const upperSymbol = text . toUpperCase ( )
2023-06-01 10:38:56 +00:00
if ( root . isAssetView )
2023-06-27 08:15:49 +00:00
asset . symbol = upperSymbol
2023-06-01 10:38:56 +00:00
else
2023-06-27 08:15:49 +00:00
collectible . symbol = upperSymbol
text = upperSymbol // breaking the binding on purpose but so does validate() and onTextChanged() internal handler
input . edit . cursorPosition = cursorPos
2023-06-01 10:38:56 +00:00
}
2023-02-22 17:10:46 +00:00
}
2023-03-17 15:09:27 +00:00
CustomNetworkFilterRowComponent {
2023-06-13 13:55:07 +00:00
id: networkSelector
2023-02-22 17:10:46 +00:00
label: qsTr ( "Select network" )
description: qsTr ( "The network on which this token will be minted" )
}
2023-03-17 15:09:27 +00:00
CustomSwitchRowComponent {
2023-03-07 11:32:45 +00:00
id: unlimitedSupplyChecker
2023-02-22 17:10:46 +00:00
label: qsTr ( "Unlimited supply" )
2023-05-11 15:51:50 +00:00
description: qsTr ( "Enable to allow the minting of additional tokens in the future. Disable to specify a finite supply" )
2023-06-01 10:38:56 +00:00
checked: root . isAssetView ? asset.infiniteSupply : collectible . infiniteSupply
onCheckedChanged: {
if ( ! checked ) supplyInput . forceActiveFocus ( )
2023-05-11 15:51:50 +00:00
2023-06-01 10:38:56 +00:00
if ( root . isAssetView )
asset . infiniteSupply = checked
else
collectible . infiniteSupply = checked
}
2023-02-22 17:10:46 +00:00
}
2023-05-22 15:47:06 +00:00
CustomStatusInput {
2023-02-22 17:10:46 +00:00
id: supplyInput
2023-03-07 11:32:45 +00:00
visible: ! unlimitedSupplyChecker . checked
2023-02-22 17:10:46 +00:00
label: qsTr ( "Total finite supply" )
2023-06-01 10:38:56 +00:00
text: root . isAssetView ? asset.supply : collectible . supply
2023-05-11 15:51:50 +00:00
placeholderText: qsTr ( "e.g. 300" )
2023-05-22 15:47:06 +00:00
minLengthValidator.errorMessage: qsTr ( "Please enter a total finite supply" )
2023-07-04 11:24:22 +00:00
regexValidator.errorMessage: d . hasEmoji ( text ) ? qsTr ( "Your total finite supply is too cool (use 0-9 only)" ) :
qsTr ( "Your total finite supply contains invalid characters (use 0-9 only)" )
2023-05-22 15:47:06 +00:00
regexValidator.regularExpression: Constants . regularExpressions . numerical
2023-06-22 08:37:30 +00:00
extraValidator.validate: function ( value ) { return parseInt ( value ) > 0 && parseInt ( value ) <= 999999999 }
extraValidator.errorMessage: qsTr ( "Enter a number between 1 and 999,999,999" )
2023-06-01 10:38:56 +00:00
onTextChanged: {
2023-06-22 08:38:38 +00:00
const amount = parseInt ( text )
2023-07-06 14:30:14 +00:00
if ( Number . isNaN ( amount ) || Object . values ( errors ) . length )
2023-06-22 08:38:38 +00:00
return
2023-06-01 10:38:56 +00:00
if ( root . isAssetView )
2023-06-22 08:38:38 +00:00
asset . supply = amount
2023-06-01 10:38:56 +00:00
else
2023-06-22 08:38:38 +00:00
collectible . supply = amount
2023-06-01 10:38:56 +00:00
}
2023-02-22 17:10:46 +00:00
}
2023-03-17 15:09:27 +00:00
CustomSwitchRowComponent {
2023-03-07 11:32:45 +00:00
id: transferableChecker
2023-05-22 13:00:40 +00:00
visible: ! root . isAssetView
2023-03-17 15:09:27 +00:00
label: checked ? qsTr ( "Not transferable (Soulbound)" ) : qsTr ( "Transferable" )
2023-02-22 17:10:46 +00:00
description: qsTr ( "If enabled, the token is locked to the first address it is sent to and can never be transferred to another address. Useful for tokens that represent Admin permissions" )
2023-06-01 10:38:56 +00:00
checked: ! collectible . transferable
onCheckedChanged: collectible . transferable = ! checked
2023-02-22 17:10:46 +00:00
}
2023-03-17 15:09:27 +00:00
CustomSwitchRowComponent {
2023-06-01 10:38:56 +00:00
id: remotelyDestructChecker
2023-05-22 13:00:40 +00:00
visible: ! root . isAssetView
2023-05-11 15:51:50 +00:00
label: qsTr ( "Remotely destructible" )
2023-02-22 17:10:46 +00:00
description: qsTr ( "Enable to allow you to destroy tokens remotely. Useful for revoking permissions from individuals" )
2023-06-01 10:38:56 +00:00
checked: ! ! collectible ? collectible.remotelyDestruct : true
onCheckedChanged: collectible . remotelyDestruct = checked
2023-02-22 17:10:46 +00:00
}
2023-05-16 15:07:21 +00:00
CustomStatusInput {
2023-05-22 13:00:40 +00:00
id: assetDecimalsInput
visible: root . isAssetView
2023-05-25 10:31:32 +00:00
label: qsTr ( "Decimals (DP)" )
2023-05-22 13:00:40 +00:00
charLimit: 2
charLimitLabel: qsTr ( "Max 10" )
placeholderText: "2"
2023-06-01 10:38:56 +00:00
text: ! ! asset ? asset.decimals : ""
2023-05-22 13:00:40 +00:00
validationMode: StatusInput . ValidationMode . Always
2023-05-22 15:47:06 +00:00
minLengthValidator.errorMessage: qsTr ( "Please enter how many decimals your token should have" )
2023-07-04 11:24:22 +00:00
regexValidator.errorMessage: d . hasEmoji ( text ) ? qsTr ( "Your decimal amount is too cool (use 0-9 only)" ) :
qsTr ( "Your decimal amount contains invalid characters (use 0-9 only)" )
2023-05-22 15:47:06 +00:00
regexValidator.regularExpression: Constants . regularExpressions . numerical
2023-06-23 12:14:22 +00:00
extraValidator.validate: function ( value ) { return parseInt ( value ) > 0 && parseInt ( value ) <= 10 }
extraValidator.errorMessage: qsTr ( "Enter a number between 1 and 10" )
2023-06-01 10:38:56 +00:00
onTextChanged: asset . decimals = parseInt ( text )
2023-05-16 15:07:21 +00:00
}
2023-07-27 20:29:31 +00:00
FeesBox {
id: feesBox
Layout.fillWidth: true
Layout.topMargin: Style . current . padding
accountErrorText: root . feeErrorText
implicitWidth: 0
model: QtObject {
id: singleFeeModel
readonly property string title: root . feeLabel
readonly property string feeText: root . isFeeLoading ?
"" : root . feeText
readonly property bool error: root . feeErrorText !== ""
}
Timer {
id: requestFeeDelayTimer
interval: 500
onTriggered: root . deployFeesRequested ( )
}
readonly property bool triggerFeeReevaluation: {
dropAreaItem . artworkSource
nameInput . text
descriptionInput . text
symbolInput . text
supplyInput . text
unlimitedSupplyChecker . checked
transferableChecker . checked
remotelyDestructChecker . checked
feesBox . accountsSelector . currentIndex
asset . chainId
collectible . chainId
requestFeeDelayTimer . restart ( )
return true
}
2023-08-09 12:31:58 +00:00
accountsSelector.model: root . accounts
2023-07-27 20:29:31 +00:00
readonly property TokenObject token: root . isAssetView ? root . asset
: root . collectible
// account can be changed also on preview page and it should be
// reflected in the form after navigating back
Connections {
target: feesBox . token
function onAccountAddressChanged ( ) {
const idx = SQUtils . ModelUtils . indexOf (
feesBox . accountsSelector . model , "address" ,
feesBox . token . accountAddress )
feesBox . accountsSelector . currentIndex = idx
}
}
accountsSelector.onCurrentIndexChanged: {
if ( accountsSelector . currentIndex < 0 )
return
const item = SQUtils . ModelUtils . get (
accountsSelector . model , accountsSelector . currentIndex )
token . accountAddress = item . address
token . accountName = item . name
}
}
2023-02-22 17:10:46 +00:00
StatusButton {
Layout.preferredHeight: 44
Layout.alignment: Qt . AlignHCenter
Layout.fillWidth: true
Layout.topMargin: Style . current . padding
2023-03-10 15:55:50 +00:00
Layout.bottomMargin: Style . current . padding
2023-02-22 17:10:46 +00:00
text: qsTr ( "Preview" )
enabled: d . isFullyFilled
2023-03-07 11:32:45 +00:00
onClicked: root . previewClicked ( )
2023-02-22 17:10:46 +00:00
}
}
2023-03-17 15:09:27 +00:00
// Inline components definition:
component CustomStatusInput: StatusInput {
id: customInput
2023-05-22 15:47:06 +00:00
property alias minLengthValidator: minLengthValidatorItem
property alias regexValidator: regexValidatorItem
property alias extraValidator: extraValidatorItem
2023-03-17 15:09:27 +00:00
Layout.fillWidth: true
validators: [
StatusMinLengthValidator {
2023-05-22 15:47:06 +00:00
id: minLengthValidatorItem
2023-03-17 15:09:27 +00:00
minLength: 1
} ,
StatusRegularExpressionValidator {
2023-05-22 15:47:06 +00:00
id: regexValidatorItem
2023-06-09 16:42:35 +00:00
regularExpression: Constants . regularExpressions . alphanumericalExpanded
2023-07-04 11:24:22 +00:00
onErrorMessageChanged: {
customInput . validate ( ) ;
}
2023-05-22 15:47:06 +00:00
} ,
StatusValidator {
id: extraValidatorItem
2023-07-04 13:21:15 +00:00
onErrorMessageChanged: {
customInput . validate ( ) ;
}
2023-03-17 15:09:27 +00:00
}
]
}
component CustomLabelDescriptionComponent: ColumnLayout {
id: labelDescComponent
property string label
property string description
Layout.fillWidth: true
StatusBaseText {
text: labelDescComponent . label
color: Theme . palette . directColor1
font.pixelSize: Theme . primaryTextFontSize
}
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
text: labelDescComponent . description
color: Theme . palette . baseColor1
font.pixelSize: Theme . primaryTextFontSize
lineHeight: 1.2
wrapMode: Text . WordWrap
}
}
component CustomSwitchRowComponent: RowLayout {
id: rowComponent
property string label
property string description
property alias checked: switch_ . checked
Layout.fillWidth: true
Layout.topMargin: Style . current . padding
spacing: 64
CustomLabelDescriptionComponent {
label: rowComponent . label
description: rowComponent . description
}
StatusSwitch {
id: switch_
}
}
component CustomNetworkFilterRowComponent: RowLayout {
id: networkComponent
property string label
property string description
2023-06-13 13:55:07 +00:00
function setChain ( chainId ) { netFilter . setChain ( chainId ) }
2023-03-17 15:09:27 +00:00
Layout.fillWidth: true
Layout.topMargin: Style . current . padding
spacing: 32
CustomLabelDescriptionComponent {
label: networkComponent . label
description: networkComponent . description
}
NetworkFilter {
2023-06-13 13:55:07 +00:00
id: netFilter
2023-03-17 15:09:27 +00:00
Layout.preferredWidth: 160
2023-04-05 11:10:44 +00:00
allNetworks: root . allNetworks
2023-03-17 15:09:27 +00:00
layer1Networks: root . layer1Networks
layer2Networks: root . layer2Networks
enabledNetworks: root . enabledNetworks
2023-04-05 11:10:44 +00:00
2023-03-17 15:09:27 +00:00
multiSelection: false
2023-07-27 20:29:31 +00:00
onToggleNetwork: ( network ) = > {
if ( root . isAssetView ) {
asset . chainId = network . chainId
asset . chainName = network . chainName
asset . chainIcon = network . iconUrl
} else {
collectible . chainId = network . chainId
collectible . chainName = network . chainName
collectible . chainIcon = network . iconUrl
}
}
2023-03-17 15:09:27 +00:00
}
}
2023-02-22 17:10:46 +00:00
}