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-09-01 09:27:44 +00:00
2023-05-22 15:47:06 +00:00
property var tokensModel
2023-09-01 09:27:44 +00:00
property TokenObject token: TokenObject {
type: root . isAssetView ? Constants.TokenType.ERC20 : Constants . TokenType . ERC721
2023-06-22 21:24:30 +00:00
}
2023-11-22 13:50:07 +00:00
// Used for reference validation
required property var referenceAssetsBySymbolModel
2023-09-01 09:27:44 +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
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:
2023-09-01 09:27:44 +00:00
isAssetView ? qsTr ( "Mint asset on %1" ) . arg ( root . token . chainName )
: qsTr ( "Mint collectible on %1" ) . arg ( root . token . chainName )
2023-07-27 20:29:31 +00:00
2023-02-22 17:10:46 +00:00
signal chooseArtWork
signal previewClicked
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-09-01 09:27:44 +00:00
&& deployFeeSubscriber . feeText !== "" && deployFeeSubscriber . feeErrorText === ""
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
2023-11-22 13:50:07 +00:00
readonly property bool containsAssetReferenceName: root . isAssetView ? checkNameProxy . count > 0 : false
readonly property SortFilterProxyModel checkNameProxy : SortFilterProxyModel {
sourceModel: root . referenceAssetsBySymbolModel
filters: ValueFilter {
roleName: "name"
value: nameInput . text
}
}
readonly property bool containsAssetReferenceSymbol: root . isAssetView ? checkSymbolProxy . count > 0 : false
readonly property SortFilterProxyModel checkSymbolProxy: SortFilterProxyModel {
sourceModel: root . referenceAssetsBySymbolModel
filters: ValueFilter {
roleName: "symbol"
value: symbolInput . text
}
}
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
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-09-01 09:27:44 +00:00
dataImage: root . token . artworkSource
artworkSource: root . token . 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
2023-09-01 09:27:44 +00:00
onArtworkSourceChanged: root . token . artworkSource = artworkSource
onArtworkCropRectChanged: root . token . artworkCropRect = artworkCropRect
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: nameInput
label: qsTr ( "Name" )
2023-09-01 09:27:44 +00:00
text: root . token . 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 ) ?
2023-09-12 09:26:25 +00:00
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
2023-09-01 09:27:44 +00:00
const allowRepeatedName = root . token . 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-11-22 13:50:07 +00:00
return ( ! SQUtils . ModelUtils . contains ( root . tokensModel , "name" , nameInput . text , Qt . CaseInsensitive ) && ! d . containsAssetReferenceName )
2023-06-09 16:42:35 +00:00
}
2023-11-22 13:50:07 +00:00
extraValidator.errorMessage: d . containsAssetReferenceName ? qsTr ( "Asset name already exists" ) :
qsTr ( "You have used this token name before" )
2023-06-01 10:38:56 +00:00
2023-09-01 09:27:44 +00:00
onTextChanged: root . token . name = text
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: descriptionInput
label: qsTr ( "Description" )
2023-09-01 09:27:44 +00:00
text: root . token . description
2023-02-22 17:10:46 +00:00
charLimit: 280
2023-09-12 09:26:25 +00:00
placeholderText: root . isAssetView ? qsTr ( "Describe your asset (will be shown in hodler’ s wallets)" ) : qsTr ( "Describe your collectible (will be shown in hodler’ s wallets)" )
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
2023-09-01 09:27:44 +00:00
onTextChanged: root . token . description
2023-02-22 17:10:46 +00:00
}
CustomStatusInput {
id: symbolInput
2023-05-11 15:51:50 +00:00
label: qsTr ( "Symbol" )
2023-09-01 09:27:44 +00:00
text: root . token . 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)" ) :
2023-09-12 09:26:25 +00:00
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
2023-09-01 09:27:44 +00:00
const allowRepeatedName = root . token . 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-11-22 13:50:07 +00:00
return ( ! SQUtils . ModelUtils . contains ( root . tokensModel , "symbol" , symbolInput . text ) && ! d . containsAssetReferenceSymbol )
2023-06-09 16:42:35 +00:00
}
2023-11-22 13:50:07 +00:00
extraValidator.errorMessage: d . containsAssetReferenceSymbol ? qsTr ( "Symbol already exists" ) : 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-09-01 09:27:44 +00:00
root . token . symbol = upperSymbol
2023-06-27 08:15:49 +00:00
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-09-12 09:26:25 +00:00
StatusBaseText {
text: qsTr ( "Network" )
color: Theme . palette . directColor1
font.pixelSize: Theme . primaryTextFontSize
}
Rectangle {
Layout.preferredHeight: 44
Layout.fillWidth: true
radius: 8
color: "transparent"
border.color: Theme . palette . directColor7
RowLayout {
id: networkRow
anchors.verticalCenter: parent . verticalCenter
spacing: 16
StatusSmartIdenticon {
Layout.alignment: Qt . AlignVCenter
Layout.leftMargin: Style . current . padding
asset.height: 24
asset.width: asset . height
asset.isImage: true
asset.name: Style . svg ( token . chainIcon )
active: true
visible: active
}
2023-06-13 13:55:07 +00:00
2023-09-12 09:26:25 +00:00
StatusBaseText {
Layout.alignment: Qt . AlignVCenter
Layout.fillWidth: true
Layout.rightMargin: Style . current . padding
font.pixelSize: 13
font.weight: Font . Medium
elide: Text . ElideRight
lineHeight: 24
lineHeightMode: Text . FixedHeight
verticalAlignment: Text . AlignVCenter
text: token . chainName
color: Theme . palette . baseColor1
visible: ! ! text
}
}
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: 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-09-01 09:27:44 +00:00
checked: root . token . infiniteSupply
2023-06-01 10:38:56 +00:00
onCheckedChanged: {
if ( ! checked ) supplyInput . forceActiveFocus ( )
2023-05-11 15:51:50 +00:00
2023-09-01 09:27:44 +00:00
root . token . infiniteSupply = checked
2023-06-01 10:38:56 +00:00
}
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-09-01 09:27:44 +00:00
text: SQUtils . AmountsArithmetic . toNumber ( root . token . supply ,
2023-09-12 09:26:25 +00:00
root . token . multiplierIndex )
2023-08-10 12:23:59 +00:00
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)" ) :
2023-09-12 09:26:25 +00:00
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-08-10 12:23:59 +00:00
const supplyNumber = parseInt ( text )
if ( Number . isNaN ( supplyNumber ) || Object . values ( errors ) . length )
2023-06-22 08:38:38 +00:00
return
2023-08-10 12:23:59 +00:00
token . supply = SQUtils . AmountsArithmetic . fromNumber (
2023-09-01 09:27:44 +00:00
supplyNumber , root . token . multiplierIndex ) . toFixed ( 0 )
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-09-01 09:27:44 +00:00
checked: ! root . token . transferable
2023-06-01 10:38:56 +00:00
2023-09-01 09:27:44 +00:00
onCheckedChanged: root . token . 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-09-01 09:27:44 +00:00
checked: ! ! root . token ? root.token.remotelyDestruct : true
onCheckedChanged: root . token . 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-09-01 09:27:44 +00:00
text: root . token . 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)" ) :
2023-09-12 09:26:25 +00:00
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-09-01 09:27:44 +00:00
onTextChanged: root . token . 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 !== ""
}
2023-08-09 12:31:58 +00:00
accountsSelector.model: root . accounts
2023-07-27 20:29:31 +00:00
// account can be changed also on preview page and it should be
// reflected in the form after navigating back
Connections {
2023-09-01 09:27:44 +00:00
target: root . token
2023-07-27 20:29:31 +00:00
function onAccountAddressChanged ( ) {
const idx = SQUtils . ModelUtils . indexOf (
2023-09-12 09:26:25 +00:00
feesBox . accountsSelector . model , "address" ,
root . token . accountAddress )
2023-07-27 20:29:31 +00:00
feesBox . accountsSelector . currentIndex = idx
}
}
accountsSelector.onCurrentIndexChanged: {
if ( accountsSelector . currentIndex < 0 )
return
const item = SQUtils . ModelUtils . get (
accountsSelector . model , accountsSelector . currentIndex )
2023-09-01 09:27:44 +00:00
root . token . accountAddress = item . address
root . token . accountName = item . name
2023-07-27 20:29:31 +00:00
}
}
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_
}
}
2023-02-22 17:10:46 +00:00
}