mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 22:21:16 +00:00
Add portable Basecamp packaging and an inspector-driven end-to-end flow.
1816 lines
84 KiB
QML
1816 lines
84 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls.Basic
|
|
import QtQuick.Layouts
|
|
|
|
Item {
|
|
id: root
|
|
|
|
objectName: "tokenCreatePage"
|
|
|
|
property var store: null
|
|
property var backend: null
|
|
property var runtime: null
|
|
property int tokenKind: 0
|
|
property int authorityMode: 0
|
|
property bool metadataEnabled: false
|
|
property int metadataStandard: 0
|
|
property int step: 0
|
|
property bool prepared: false
|
|
property string preparedMessage: ""
|
|
property string errorMessage: ""
|
|
property bool submitting: false
|
|
property bool accountBusy: false
|
|
|
|
signal requestInspect()
|
|
|
|
onVisibleChanged: {
|
|
if (visible) {
|
|
step = 0;
|
|
prepared = false;
|
|
preparedMessage = "";
|
|
errorMessage = "";
|
|
submitting = false;
|
|
scroll.contentY = 0;
|
|
}
|
|
}
|
|
|
|
readonly property bool isFungible: tokenKind === 0
|
|
readonly property bool requiresMetadata: !isFungible
|
|
readonly property bool hasMetadata: requiresMetadata || metadataEnabled
|
|
readonly property string maximumU128: "340282366920938463463374607431768211455"
|
|
readonly property string instructionName: !isFungible ? "createNonFungible" : hasMetadata ? "createFungibleWithMetadata" : "createFungible"
|
|
readonly property string supplyLabel: isFungible ? qsTr("Initial raw supply") : qsTr("Printable supply")
|
|
readonly property string supplyValue: supplyField.text.length > 0 ? supplyField.text : qsTr("Not set")
|
|
readonly property string authorityValue: !isFungible ? qsTr("Master holding controls printing") : authorityMode === 0 ? qsTr("Fixed supply") : authorityMode === 1 ? qsTr("Definition account") : externalAuthorityField.text.length > 0 ? externalAuthorityField.text : qsTr("External account required")
|
|
readonly property bool validSupply: isUnsignedU128(supplyField.text)
|
|
readonly property bool validDefinitionTarget: isAccountId(definitionTargetField.text)
|
|
readonly property bool validHoldingTarget: isAccountId(holdingTargetField.text)
|
|
readonly property bool validMetadataTarget: !hasMetadata || isAccountId(metadataTargetField.text)
|
|
readonly property bool validExternalAuthority: !isFungible || authorityMode !== 2 || (isAccountId(externalAuthorityField.text) && externalAuthorityField.text !== "11111111111111111111111111111111")
|
|
readonly property bool canContinue: validSupply && validExternalAuthority
|
|
readonly property bool canPrepare: canContinue && validDefinitionTarget && validHoldingTarget && validMetadataTarget
|
|
readonly property bool canSubmit: canPrepare && root.backend !== null && root.backend.isWalletOpen && !root.submitting
|
|
readonly property int validTargetCount: (validDefinitionTarget ? 1 : 0) + (validHoldingTarget ? 1 : 0) + (hasMetadata && validMetadataTarget ? 1 : 0)
|
|
readonly property int targetCount: hasMetadata ? 3 : 2
|
|
|
|
function isUnsignedU128(value) {
|
|
if (!/^[0-9]+$/.test(value))
|
|
return false;
|
|
|
|
var normalized = value.replace(/^0+(?=\d)/, "");
|
|
if (normalized.length < maximumU128.length)
|
|
return true;
|
|
if (normalized.length > maximumU128.length)
|
|
return false;
|
|
return normalized <= maximumU128;
|
|
}
|
|
|
|
function isAccountId(value) {
|
|
return /^[0-9a-fA-F]{64}$/.test(value) || /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
|
|
}
|
|
|
|
function clearDraftState() {
|
|
prepared = false;
|
|
preparedMessage = "";
|
|
errorMessage = "";
|
|
}
|
|
|
|
function watch(result, success, failure) {
|
|
if (root.runtime && root.runtime.watch)
|
|
root.runtime.watch(result, success, failure);
|
|
else if (failure)
|
|
failure(qsTr("Runtime is not ready."));
|
|
}
|
|
|
|
function createTargetAccounts() {
|
|
if (!root.backend || !root.backend.isWalletOpen || root.accountBusy)
|
|
return;
|
|
|
|
var fields = [definitionTargetField, holdingTargetField];
|
|
if (root.hasMetadata)
|
|
fields.push(metadataTargetField);
|
|
|
|
var pendingFields = [];
|
|
for (var index = 0; index < fields.length; ++index) {
|
|
if (!fields[index].text)
|
|
pendingFields.push(fields[index]);
|
|
}
|
|
|
|
if (pendingFields.length === 0)
|
|
return;
|
|
|
|
root.accountBusy = true;
|
|
function createNext(fieldIndex) {
|
|
if (fieldIndex >= pendingFields.length) {
|
|
root.accountBusy = false;
|
|
root.clearDraftState();
|
|
return;
|
|
}
|
|
root.watch(root.backend.createAccountPublic(), function(accountId) {
|
|
if (!accountId) {
|
|
root.accountBusy = false;
|
|
root.errorMessage = qsTr("Could not create a public wallet account.");
|
|
return;
|
|
}
|
|
pendingFields[fieldIndex].text = String(accountId);
|
|
createNext(fieldIndex + 1);
|
|
}, function(error) {
|
|
root.accountBusy = false;
|
|
root.errorMessage = qsTr("Could not create a public wallet account: %1").arg(error);
|
|
});
|
|
}
|
|
createNext(0);
|
|
}
|
|
|
|
function selectTokenKind(kind) {
|
|
tokenKind = kind;
|
|
if (requiresMetadata)
|
|
metadataEnabled = true;
|
|
clearDraftState();
|
|
}
|
|
|
|
function setPattern(pattern) {
|
|
clearDraftState();
|
|
step = 0;
|
|
externalAuthorityField.text = "";
|
|
definitionTargetField.text = "";
|
|
holdingTargetField.text = "";
|
|
metadataTargetField.text = "";
|
|
metadataUriField.text = "";
|
|
creatorsField.text = "";
|
|
|
|
if (pattern === "fixed") {
|
|
tokenKind = 0;
|
|
authorityMode = 0;
|
|
metadataEnabled = false;
|
|
metadataStandard = 0;
|
|
nameField.text = qsTr("Fixed supply token");
|
|
supplyField.text = "7654321";
|
|
return;
|
|
}
|
|
|
|
if (pattern === "metadata") {
|
|
tokenKind = 0;
|
|
authorityMode = 2;
|
|
metadataEnabled = true;
|
|
metadataStandard = 1;
|
|
nameField.text = qsTr("Metadata token");
|
|
supplyField.text = "10000000000000000000000000";
|
|
metadataUriField.text = "data:application/json;base64,...";
|
|
creatorsField.text = qsTr("Creator or authority label");
|
|
return;
|
|
}
|
|
|
|
tokenKind = 1;
|
|
authorityMode = 0;
|
|
metadataEnabled = true;
|
|
metadataStandard = 1;
|
|
nameField.text = qsTr("NFT collection");
|
|
supplyField.text = "64";
|
|
metadataUriField.text = "data:application/json;base64,...";
|
|
creatorsField.text = qsTr("Master-holding creator");
|
|
}
|
|
|
|
function prepareDefinition() {
|
|
if (!canSubmit)
|
|
return;
|
|
|
|
var mintAuthority = !isFungible ? "" : authorityMode === 0 ? "none" : authorityMode === 1 ? "self" : externalAuthorityField.text;
|
|
var metadataStandardValue = metadataStandard === 0 ? "simple" : "expanded";
|
|
var pending;
|
|
root.submitting = true;
|
|
root.errorMessage = "";
|
|
root.preparedMessage = qsTr("Submitting to the Token Program…");
|
|
|
|
if (!isFungible && root.backend) {
|
|
pending = root.backend.createNonFungible(
|
|
definitionTargetField.text,
|
|
holdingTargetField.text,
|
|
metadataTargetField.text,
|
|
nameField.text,
|
|
supplyField.text,
|
|
metadataStandardValue,
|
|
metadataUriField.text,
|
|
creatorsField.text);
|
|
} else if (hasMetadata && root.backend) {
|
|
pending = root.backend.createFungibleWithMetadata(
|
|
definitionTargetField.text,
|
|
holdingTargetField.text,
|
|
metadataTargetField.text,
|
|
nameField.text,
|
|
supplyField.text,
|
|
mintAuthority,
|
|
metadataStandardValue,
|
|
metadataUriField.text,
|
|
creatorsField.text);
|
|
} else if (root.backend) {
|
|
pending = root.backend.createFungible(
|
|
definitionTargetField.text,
|
|
holdingTargetField.text,
|
|
nameField.text,
|
|
supplyField.text,
|
|
mintAuthority);
|
|
}
|
|
|
|
root.watch(pending, function(result) {
|
|
root.submitting = false;
|
|
if (!result || result.status !== "ok") {
|
|
root.prepared = false;
|
|
root.errorMessage = qsTr("Token Program rejected the request: %1").arg(result && result.error ? result.error : qsTr("unknown_error"));
|
|
return;
|
|
}
|
|
|
|
var transactionId = String(result.transactionId || "");
|
|
var definitionType = isFungible ? "fungible" : "nonFungible";
|
|
var draft = {
|
|
id: definitionTargetField.text,
|
|
name: nameField.text.length > 0 ? nameField.text : qsTr("Untitled definition"),
|
|
type: definitionType,
|
|
definitionId: definitionTargetField.text,
|
|
holdingId: holdingTargetField.text,
|
|
metadataId: hasMetadata ? metadataTargetField.text : "",
|
|
rawSupply: supplyField.text,
|
|
displaySupply: supplyField.text,
|
|
inferredDecimals: "",
|
|
authorityMode: isFungible ? (authorityMode === 0 ? "fixed" : authorityMode === 1 ? "self" : "external") : "masterHolding",
|
|
authority: isFungible && authorityMode === 2 ? externalAuthorityField.text : authorityMode === 1 ? definitionTargetField.text : "",
|
|
authorityLabel: isFungible && authorityMode === 2 ? qsTr("External authority account") : "",
|
|
metadataStandard: hasMetadata ? (metadataStandard === 0 ? "Simple" : "Expanded") : "",
|
|
metadataUri: hasMetadata ? metadataUriField.text : "",
|
|
creators: hasMetadata ? creatorsField.text : "",
|
|
description: qsTr("Submitted to the Token Program."),
|
|
source: "pending",
|
|
instruction: instructionName,
|
|
printableCopies: !isFungible ? supplyField.text : "",
|
|
masterHolding: !isFungible ? holdingTargetField.text : "",
|
|
transactionId: transactionId,
|
|
definition: {
|
|
id: definitionTargetField.text,
|
|
hex: definitionTargetField.text,
|
|
name: nameField.text,
|
|
type: definitionType,
|
|
totalSupplyRaw: isFungible ? supplyField.text : undefined,
|
|
printableSupply: !isFungible ? supplyField.text : undefined,
|
|
mintAuthority: isFungible && authorityMode === 2 ? externalAuthorityField.text : isFungible && authorityMode === 1 ? definitionTargetField.text : undefined,
|
|
metadataId: hasMetadata ? metadataTargetField.text : undefined
|
|
},
|
|
holding: {
|
|
id: holdingTargetField.text,
|
|
wallet: "connected wallet",
|
|
role: !isFungible ? "nftMaster" : "fungible",
|
|
rawBalance: isFungible ? supplyField.text : undefined,
|
|
printBalance: !isFungible ? supplyField.text : undefined
|
|
},
|
|
holdings: [{
|
|
id: holdingTargetField.text,
|
|
wallet: "connected wallet",
|
|
role: !isFungible ? "nftMaster" : "fungible",
|
|
rawBalance: isFungible ? supplyField.text : undefined,
|
|
printBalance: !isFungible ? supplyField.text : undefined
|
|
}]
|
|
};
|
|
|
|
if (hasMetadata) {
|
|
draft.metadata = {
|
|
id: metadataTargetField.text,
|
|
standard: metadataStandard === 0 ? "Simple" : "Expanded",
|
|
uri: metadataUriField.text,
|
|
creators: creatorsField.text
|
|
};
|
|
}
|
|
|
|
if (store && store.addDraft)
|
|
store.addDraft(draft);
|
|
|
|
root.prepared = true;
|
|
root.preparedMessage = transactionId.length > 0
|
|
? qsTr("Transaction submitted · %1").arg(transactionId)
|
|
: qsTr("Transaction submitted.");
|
|
}, function(error) {
|
|
root.submitting = false;
|
|
root.prepared = false;
|
|
root.errorMessage = qsTr("Token Program request failed: %1").arg(error);
|
|
});
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: "#151515"
|
|
}
|
|
|
|
Menu {
|
|
id: examplesMenu
|
|
|
|
objectName: "tokenExamplesMenu"
|
|
|
|
MenuItem {
|
|
objectName: "tokenFixedTemplate"
|
|
text: qsTr("Fixed supply")
|
|
onTriggered: root.setPattern("fixed")
|
|
}
|
|
|
|
MenuItem {
|
|
objectName: "tokenMetadataTemplate"
|
|
text: qsTr("Metadata-backed fungible")
|
|
onTriggered: root.setPattern("metadata")
|
|
}
|
|
|
|
MenuItem {
|
|
objectName: "tokenNftTemplate"
|
|
text: qsTr("NFT collection")
|
|
onTriggered: root.setPattern("nft")
|
|
}
|
|
}
|
|
|
|
Flickable {
|
|
id: scroll
|
|
|
|
anchors.fill: parent
|
|
clip: true
|
|
contentWidth: width
|
|
contentHeight: contentColumn.implicitHeight + 32
|
|
flickableDirection: Flickable.VerticalFlick
|
|
|
|
ScrollBar.vertical: ScrollBar {
|
|
id: verticalScrollBar
|
|
|
|
parent: scroll.parent
|
|
anchors.top: scroll.top
|
|
anchors.right: scroll.right
|
|
anchors.bottom: scroll.bottom
|
|
anchors.topMargin: 4
|
|
anchors.rightMargin: 4
|
|
anchors.bottomMargin: 4
|
|
policy: scroll.contentHeight > scroll.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
|
active: true
|
|
visible: policy === ScrollBar.AlwaysOn
|
|
width: 12
|
|
z: 10
|
|
|
|
background: Rectangle {
|
|
radius: 6
|
|
color: "#292929"
|
|
}
|
|
|
|
contentItem: Rectangle {
|
|
implicitWidth: 8
|
|
implicitHeight: 32
|
|
radius: 4
|
|
color: verticalScrollBar.pressed ? "#F26A21" : "#9A8C81"
|
|
opacity: 1
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: contentColumn
|
|
|
|
width: Math.max(240, Math.min(scroll.width - 32, 1120))
|
|
x: Math.max(16, (scroll.width - width) / 2)
|
|
y: 16
|
|
spacing: 12
|
|
|
|
ColumnLayout {
|
|
id: pageHeader
|
|
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
spacing: 3
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 28
|
|
font.weight: Font.DemiBold
|
|
wrapMode: Text.Wrap
|
|
text: qsTr("Create definition")
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
color: "#A9A098"
|
|
font.pixelSize: 14
|
|
wrapMode: Text.Wrap
|
|
text: qsTr("Configure the token, confirm its account targets, then create the definition.")
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
id: progressRow
|
|
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
spacing: 5
|
|
|
|
Repeater {
|
|
model: [qsTr("Configure"), qsTr("Account targets"), qsTr("Review")]
|
|
|
|
delegate: RowLayout {
|
|
id: stepItem
|
|
|
|
required property int index
|
|
required property var modelData
|
|
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
spacing: 5
|
|
|
|
Rectangle {
|
|
Layout.preferredWidth: 24
|
|
Layout.preferredHeight: 24
|
|
radius: 12
|
|
color: root.step === stepItem.index ? "#F26A21" : root.step > stepItem.index ? "#183222" : "#202020"
|
|
border.color: root.step === stepItem.index ? "#F26A21" : root.step > stepItem.index ? "#39C06A" : "#343434"
|
|
border.width: 1
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
color: root.step === stepItem.index ? "#151515" : root.step > stepItem.index ? "#78C88D" : "#A9A098"
|
|
font.pixelSize: 12
|
|
font.weight: Font.DemiBold
|
|
text: (stepItem.index + 1).toString()
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
color: root.step === stepItem.index ? "#E7E1D8" : "#A9A098"
|
|
font.pixelSize: 13
|
|
font.weight: root.step === stepItem.index ? Font.DemiBold : Font.Normal
|
|
horizontalAlignment: Text.AlignLeft
|
|
wrapMode: Text.Wrap
|
|
text: stepItem.modelData
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
Layout.preferredHeight: 1
|
|
visible: stepItem.index < 2
|
|
color: root.step > stepItem.index ? "#39C06A" : "#343434"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GridLayout {
|
|
id: stage
|
|
|
|
readonly property int columnCount: contentColumn.width >= 860 ? 2 : 1
|
|
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
columns: columnCount
|
|
columnSpacing: 12
|
|
rowSpacing: 12
|
|
|
|
Rectangle {
|
|
id: mainPanel
|
|
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
Layout.preferredWidth: stage.columnCount === 2 ? 640 : 0
|
|
Layout.maximumWidth: stage.columnCount === 2 ? 680 : 1120
|
|
implicitHeight: editor.implicitHeight + 32
|
|
radius: 16
|
|
color: "#1B1B1B"
|
|
border.color: "#303030"
|
|
border.width: 1
|
|
|
|
ColumnLayout {
|
|
id: editor
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 14
|
|
spacing: 10
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 3
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 20
|
|
font.weight: Font.DemiBold
|
|
text: root.step === 0 ? qsTr("Token settings") : root.step === 1 ? qsTr("Account targets") : qsTr("Review definition")
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#A9A098"
|
|
font.pixelSize: 13
|
|
wrapMode: Text.Wrap
|
|
text: root.step === 0 ? qsTr("Choose the fields that define this token.") : root.step === 1 ? qsTr("These accounts are created with the token definition.") : qsTr("Review the definition before creating it.")
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: configureView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 10
|
|
visible: root.step === 0
|
|
|
|
Rectangle {
|
|
id: kindTabs
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Layout.minimumHeight: 44
|
|
Layout.maximumHeight: 44
|
|
radius: 8
|
|
color: "#101010"
|
|
border.color: "#343434"
|
|
border.width: 1
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
Button {
|
|
id: fungibleTab
|
|
|
|
objectName: "tokenFungibleTab"
|
|
|
|
property bool selected: root.tokenKind === 0
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
text: qsTr("Fungible")
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Create fungible token definition")
|
|
onClicked: root.selectTokenKind(0)
|
|
|
|
contentItem: Text {
|
|
color: fungibleTab.selected ? "#F2D8C7" : "#A9A098"
|
|
font.pixelSize: 14
|
|
font.weight: fungibleTab.selected ? Font.DemiBold : Font.Normal
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: fungibleTab.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: fungibleTab.selected ? "#211914" : "transparent"
|
|
border.color: fungibleTab.activeFocus ? "#FFB26B" : fungibleTab.selected ? "#F26A21" : "transparent"
|
|
border.width: fungibleTab.activeFocus ? 2 : fungibleTab.selected ? 1 : 0
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: nonFungibleTab
|
|
|
|
objectName: "tokenNonFungibleTab"
|
|
|
|
property bool selected: root.tokenKind === 1
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
text: qsTr("Non-fungible")
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Create non-fungible token definition")
|
|
onClicked: root.selectTokenKind(1)
|
|
|
|
contentItem: Text {
|
|
color: nonFungibleTab.selected ? "#F2D8C7" : "#A9A098"
|
|
font.pixelSize: 14
|
|
font.weight: nonFungibleTab.selected ? Font.DemiBold : Font.Normal
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: nonFungibleTab.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: nonFungibleTab.selected ? "#211914" : "transparent"
|
|
border.color: nonFungibleTab.activeFocus ? "#FFB26B" : nonFungibleTab.selected ? "#F26A21" : "transparent"
|
|
border.width: nonFungibleTab.activeFocus ? 2 : nonFungibleTab.selected ? 1 : 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#A9A098"
|
|
font.pixelSize: 13
|
|
wrapMode: Text.Wrap
|
|
text: root.isFungible ? qsTr("One initial holding receives the full raw supply.") : qsTr("Metadata is required. The initial master holding controls printing.")
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: "#303030"
|
|
}
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 16
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("Identity")
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Name")
|
|
}
|
|
|
|
TextField {
|
|
id: nameField
|
|
|
|
objectName: "tokenNameField"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Token definition name")
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 15
|
|
placeholderText: qsTr("Name stored on the definition")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
text: qsTr("New token")
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: nameField.activeFocus ? "#F26A21" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: root.supplyLabel
|
|
}
|
|
|
|
TextField {
|
|
id: supplyField
|
|
|
|
objectName: "tokenSupplyField"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: root.supplyLabel
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 15
|
|
inputMethodHints: Qt.ImhDigitsOnly
|
|
placeholderText: qsTr("Unsigned 128-bit integer")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
text: "1000000"
|
|
validator: RegularExpressionValidator {
|
|
regularExpression: /^[0-9]*$/
|
|
}
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: supplyField.activeFocus ? "#F26A21" : supplyField.text.length > 0 && !root.validSupply ? "#D85F4B" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: root.validSupply ? "#A9A098" : "#F08A76"
|
|
font.pixelSize: 12
|
|
wrapMode: Text.Wrap
|
|
text: root.validSupply ? qsTr("Raw u128 value. Display decimals are inferred by clients, not stored here.") : qsTr("Enter an unsigned 128-bit integer between 0 and 340282366920938463463374607431768211455.")
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
visible: root.isFungible
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 8
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: "#303030"
|
|
}
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 16
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("Mint authority")
|
|
}
|
|
|
|
ComboBox {
|
|
id: authoritySelector
|
|
|
|
objectName: "tokenAuthoritySelector"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Mint authority policy")
|
|
currentIndex: root.authorityMode
|
|
model: [qsTr("Fixed supply — no future minting"), qsTr("Self authority — definition account mints"), qsTr("External authority — another account mints")]
|
|
|
|
onActivated: {
|
|
root.authorityMode = currentIndex;
|
|
root.clearDraftState();
|
|
}
|
|
|
|
contentItem: Text {
|
|
leftPadding: 12
|
|
rightPadding: authoritySelector.indicator.width + authoritySelector.spacing
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 14
|
|
text: authoritySelector.currentText
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: authoritySelector.activeFocus ? "#F26A21" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
TextField {
|
|
id: externalAuthorityField
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: root.authorityMode === 2 ? 44 : 0
|
|
Accessible.name: qsTr("External mint authority account ID")
|
|
clip: true
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 14
|
|
placeholderText: qsTr("External authority account ID")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
visible: root.authorityMode === 2
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: externalAuthorityField.activeFocus ? "#F26A21" : externalAuthorityField.text.length > 0 && !root.validExternalAuthority ? "#D85F4B" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: "#303030"
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 10
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 3
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 16
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("Metadata")
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: root.requiresMetadata ? qsTr("Required for non-fungible definitions") : qsTr("Optional for fungible definitions")
|
|
}
|
|
}
|
|
|
|
CheckBox {
|
|
id: metadataCheckBox
|
|
|
|
objectName: "tokenMetadataCheckBox"
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
Accessible.name: qsTr("Include metadata account")
|
|
checked: root.hasMetadata
|
|
enabled: !root.requiresMetadata
|
|
text: qsTr("Include")
|
|
|
|
onToggled: {
|
|
root.metadataEnabled = checked;
|
|
root.clearDraftState();
|
|
}
|
|
|
|
contentItem: Text {
|
|
leftPadding: metadataCheckBox.indicator.width + metadataCheckBox.spacing
|
|
color: metadataCheckBox.enabled ? "#B8ADA3" : "#6D6761"
|
|
font.pixelSize: 12
|
|
text: metadataCheckBox.text
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
indicator: Rectangle {
|
|
implicitWidth: 18
|
|
implicitHeight: 18
|
|
x: metadataCheckBox.leftPadding
|
|
y: metadataCheckBox.topPadding + (metadataCheckBox.availableHeight - height) / 2
|
|
radius: 4
|
|
color: metadataCheckBox.checked ? "#F26A21" : "#101010"
|
|
border.color: metadataCheckBox.activeFocus ? "#FFB26B" : metadataCheckBox.checked ? "#F26A21" : "#8E8780"
|
|
border.width: metadataCheckBox.activeFocus ? 2 : 1
|
|
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: 6
|
|
height: 6
|
|
radius: 3
|
|
color: "#151515"
|
|
visible: metadataCheckBox.checked
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
visible: root.hasMetadata
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 8
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
wrapMode: Text.Wrap
|
|
text: qsTr("Only the standard, URI, creators, definition link, and primary-sale date are stored. URI content is not validated by the program.")
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Standard")
|
|
}
|
|
|
|
ComboBox {
|
|
id: metadataStandardSelector
|
|
|
|
Layout.preferredWidth: 150
|
|
Layout.preferredHeight: 36
|
|
Accessible.name: qsTr("Metadata standard")
|
|
currentIndex: root.metadataStandard
|
|
model: [qsTr("Simple"), qsTr("Expanded")]
|
|
|
|
onActivated: {
|
|
root.metadataStandard = currentIndex;
|
|
root.clearDraftState();
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: metadataStandardSelector.activeFocus ? "#F26A21" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("URI")
|
|
}
|
|
|
|
TextField {
|
|
id: metadataUriField
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Metadata URI")
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 14
|
|
placeholderText: qsTr("data:application/json;base64,... or external URI")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: metadataUriField.activeFocus ? "#F26A21" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Creators")
|
|
}
|
|
|
|
TextField {
|
|
id: creatorsField
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Metadata creators string")
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 14
|
|
placeholderText: qsTr("Creator account or attribution string")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: creatorsField.activeFocus ? "#F26A21" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Button {
|
|
id: examplesButton
|
|
|
|
objectName: "tokenExamplesButton"
|
|
|
|
Layout.preferredHeight: 36
|
|
text: qsTr("Use template")
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Choose a token definition template")
|
|
onClicked: examplesMenu.popup(examplesButton, Qt.point(0, examplesButton.height))
|
|
|
|
contentItem: Text {
|
|
color: "#B8ADA3"
|
|
font.pixelSize: 12
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: examplesButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: examplesButton.activeFocus ? "#FFB26B" : "#343434"
|
|
border.width: examplesButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: continueButton
|
|
|
|
objectName: "tokenContinueButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: visible ? 46 : 0
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Continue to account targets")
|
|
enabled: root.canContinue
|
|
text: root.canContinue ? qsTr("Continue to account targets") : qsTr("Complete required fields")
|
|
visible: stage.columnCount === 1
|
|
onClicked: root.step = 1
|
|
|
|
contentItem: Text {
|
|
color: continueButton.enabled ? "#FFFFFF" : "#8E8780"
|
|
font.pixelSize: 15
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: continueButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: continueButton.enabled ? "#F26A21" : "#282522"
|
|
border.color: continueButton.activeFocus ? "#FFB26B" : continueButton.enabled ? "#F26A21" : "#3C3833"
|
|
border.width: continueButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: accountsView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 12
|
|
visible: root.step === 1
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
implicitHeight: accountList.implicitHeight + 24
|
|
radius: 10
|
|
color: "#101010"
|
|
border.color: "#343434"
|
|
border.width: 1
|
|
|
|
ColumnLayout {
|
|
id: accountList
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 12
|
|
spacing: 6
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 15
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("What this definition creates")
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
wrapMode: Text.Wrap
|
|
text: root.hasMetadata ? qsTr("A definition account, an initial holding, and a metadata account.") : qsTr("A definition account and an initial holding.")
|
|
}
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: createAccountsButton
|
|
|
|
objectName: "tokenCreateAccountsButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 38
|
|
enabled: root.backend !== null && root.backend.isWalletOpen && !root.accountBusy
|
|
text: root.accountBusy ? qsTr("Creating fresh accounts…") : qsTr("Create fresh wallet accounts")
|
|
Accessible.name: qsTr("Create fresh wallet accounts for this definition")
|
|
onClicked: root.createTargetAccounts()
|
|
|
|
contentItem: Text {
|
|
color: parent.enabled ? "#F2D8C7" : "#8E8780"
|
|
font.pixelSize: 13
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: createAccountsButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: parent.enabled ? "#211914" : "#282522"
|
|
border.color: parent.enabled ? "#49301F" : "#3C3833"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Definition target account")
|
|
}
|
|
|
|
TextField {
|
|
id: definitionTargetField
|
|
|
|
objectName: "tokenDefinitionTargetField"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Definition target account ID")
|
|
clip: true
|
|
color: "#E7E1D8"
|
|
font.family: "monospace"
|
|
font.pixelSize: 13
|
|
placeholderText: qsTr("New authorized account ID")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: definitionTargetField.activeFocus ? "#F26A21" : definitionTargetField.text.length > 0 && !root.validDefinitionTarget ? "#D85F4B" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: definitionTargetField.text.length === 0 || root.validDefinitionTarget ? "#A9A098" : "#F08A76"
|
|
font.pixelSize: 12
|
|
text: definitionTargetField.text.length === 0 || root.validDefinitionTarget ? qsTr("Fresh public wallet account; accepts base58 or 64-character hex.") : qsTr("Enter a base58 account ID or 64-character hex account ID.")
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: root.isFungible ? qsTr("Initial fungible holding target") : qsTr("NFT master holding target")
|
|
}
|
|
|
|
TextField {
|
|
id: holdingTargetField
|
|
|
|
objectName: "tokenHoldingTargetField"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: root.isFungible ? qsTr("Initial fungible holding target account ID") : qsTr("NFT master holding target account ID")
|
|
clip: true
|
|
color: "#E7E1D8"
|
|
font.family: "monospace"
|
|
font.pixelSize: 13
|
|
placeholderText: qsTr("New authorized account ID")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: holdingTargetField.activeFocus ? "#F26A21" : holdingTargetField.text.length > 0 && !root.validHoldingTarget ? "#D85F4B" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: holdingTargetField.text.length === 0 || root.validHoldingTarget ? "#A9A098" : "#F08A76"
|
|
font.pixelSize: 12
|
|
text: holdingTargetField.text.length === 0 || root.validHoldingTarget ? qsTr("Fresh public wallet account; receives the initial state.") : qsTr("Enter a base58 account ID or 64-character hex account ID.")
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
visible: root.hasMetadata
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 7
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Metadata target account")
|
|
}
|
|
|
|
TextField {
|
|
id: metadataTargetField
|
|
|
|
objectName: "tokenMetadataTargetField"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
Accessible.name: qsTr("Metadata target account ID")
|
|
clip: true
|
|
color: "#E7E1D8"
|
|
font.family: "monospace"
|
|
font.pixelSize: 13
|
|
placeholderText: qsTr("New authorized account ID")
|
|
placeholderTextColor: "#8E8780"
|
|
selectByMouse: true
|
|
|
|
onTextEdited: root.clearDraftState()
|
|
|
|
background: Rectangle {
|
|
radius: 7
|
|
color: "#101010"
|
|
border.color: metadataTargetField.activeFocus ? "#F26A21" : metadataTargetField.text.length > 0 && !root.validMetadataTarget ? "#D85F4B" : "#343434"
|
|
border.width: 1
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: metadataTargetField.text.length === 0 || root.validMetadataTarget ? "#A9A098" : "#F08A76"
|
|
font.pixelSize: 12
|
|
text: metadataTargetField.text.length === 0 || root.validMetadataTarget ? qsTr("Fresh public wallet account; stores the metadata record.") : qsTr("Enter a base58 account ID or 64-character hex account ID.")
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Button {
|
|
id: backToConfigureButton
|
|
|
|
objectName: "tokenBackToConfigureButton"
|
|
|
|
Layout.preferredWidth: 112
|
|
Layout.preferredHeight: 46
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Back to token settings")
|
|
text: qsTr("Back")
|
|
onClicked: root.step = 0
|
|
|
|
contentItem: Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 14
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: backToConfigureButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: "#101010"
|
|
border.color: backToConfigureButton.activeFocus ? "#FFB26B" : "#343434"
|
|
border.width: backToConfigureButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: reviewButton
|
|
|
|
objectName: "tokenReviewButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 46
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Review token definition")
|
|
enabled: root.canPrepare
|
|
text: root.canPrepare ? qsTr("Review definition") : qsTr("Complete targets")
|
|
onClicked: root.step = 2
|
|
|
|
contentItem: Text {
|
|
color: reviewButton.enabled ? "#FFFFFF" : "#8E8780"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 15
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: reviewButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: reviewButton.enabled ? "#F26A21" : "#282522"
|
|
border.color: reviewButton.activeFocus ? "#FFB26B" : reviewButton.enabled ? "#F26A21" : "#3C3833"
|
|
border.width: reviewButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: reviewView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
|
spacing: 12
|
|
visible: root.step === 2
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
implicitHeight: reviewContent.implicitHeight + 24
|
|
radius: 10
|
|
color: "#101010"
|
|
border.color: "#343434"
|
|
border.width: 1
|
|
|
|
ColumnLayout {
|
|
id: reviewContent
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 12
|
|
spacing: 9
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Rectangle {
|
|
Layout.preferredWidth: 78
|
|
Layout.preferredHeight: 28
|
|
radius: 14
|
|
color: root.isFungible ? "#211914" : "#182534"
|
|
border.color: root.isFungible ? "#6A4329" : "#40607A"
|
|
border.width: 1
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
color: root.isFungible ? "#F2D8C7" : "#BFD8F4"
|
|
font.pixelSize: 12
|
|
font.weight: Font.DemiBold
|
|
text: root.isFungible ? qsTr("Fungible") : qsTr("NFT")
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 18
|
|
font.weight: Font.DemiBold
|
|
text: nameField.text.length > 0 ? nameField.text : qsTr("Untitled definition")
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: "#303030"
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 13
|
|
text: root.supplyLabel
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
color: root.validSupply ? "#E7E1D8" : "#F08A76"
|
|
font.pixelSize: 14
|
|
font.weight: Font.Medium
|
|
text: root.supplyValue
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 13
|
|
text: root.isFungible ? qsTr("Mint authority") : qsTr("Printing")
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
Layout.maximumWidth: 280
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 13
|
|
text: root.authorityValue
|
|
horizontalAlignment: Text.AlignRight
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 13
|
|
text: qsTr("Metadata")
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 13
|
|
text: root.hasMetadata ? root.metadataStandard === 0 ? qsTr("Simple") : qsTr("Expanded") : qsTr("None")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Account targets")
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 6
|
|
|
|
Repeater {
|
|
model: [
|
|
{ label: qsTr("Definition"), value: definitionTargetField.text, ok: root.validDefinitionTarget, metadata: false },
|
|
{ label: root.isFungible ? qsTr("Initial holding") : qsTr("Master holding"), value: holdingTargetField.text, ok: root.validHoldingTarget, metadata: false },
|
|
{ label: qsTr("Metadata"), value: metadataTargetField.text, ok: root.validMetadataTarget, metadata: true }
|
|
]
|
|
|
|
delegate: RowLayout {
|
|
id: reviewTargetRow
|
|
|
|
required property var modelData
|
|
|
|
Layout.fillWidth: true
|
|
visible: !reviewTargetRow.modelData.metadata || root.hasMetadata
|
|
spacing: 8
|
|
|
|
Text {
|
|
Layout.preferredWidth: 112
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: reviewTargetRow.modelData.label
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideMiddle
|
|
font.family: "monospace"
|
|
font.pixelSize: 12
|
|
text: reviewTargetRow.modelData.value.length > 0 ? reviewTargetRow.modelData.value : qsTr("Missing")
|
|
}
|
|
|
|
Text {
|
|
color: reviewTargetRow.modelData.value.length > 0 && reviewTargetRow.modelData.ok ? "#78C88D" : "#F08A76"
|
|
font.pixelSize: 12
|
|
text: reviewTargetRow.modelData.value.length > 0 && reviewTargetRow.modelData.ok ? qsTr("Ready") : qsTr("Needs input")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
implicitHeight: reviewNotice.implicitHeight + 20
|
|
radius: 8
|
|
color: "#211914"
|
|
border.color: "#49301F"
|
|
border.width: 1
|
|
|
|
Text {
|
|
id: reviewNotice
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 10
|
|
color: "#F2D8C7"
|
|
font.pixelSize: 12
|
|
wrapMode: Text.Wrap
|
|
text: root.hasMetadata ? qsTr("Metadata-backed definitions use typed serialization.") : qsTr("Fungible definitions use the standard Token Program fields.")
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
visible: root.prepared || root.errorMessage.length > 0
|
|
color: root.errorMessage.length > 0 ? "#F08A76" : "#78C88D"
|
|
font.pixelSize: 13
|
|
wrapMode: Text.Wrap
|
|
text: root.errorMessage.length > 0 ? root.errorMessage : root.preparedMessage
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Button {
|
|
id: backToAccountsButton
|
|
|
|
objectName: "tokenBackToAccountsButton"
|
|
|
|
Layout.preferredWidth: 112
|
|
Layout.preferredHeight: 46
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Back to account targets")
|
|
enabled: !root.prepared && !root.submitting
|
|
text: qsTr("Back")
|
|
onClicked: root.step = 1
|
|
|
|
contentItem: Text {
|
|
color: backToAccountsButton.enabled ? "#E7E1D8" : "#8E8780"
|
|
font.pixelSize: 14
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: backToAccountsButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: "#101010"
|
|
border.color: backToAccountsButton.activeFocus ? "#FFB26B" : "#343434"
|
|
border.width: backToAccountsButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: prepareButton
|
|
|
|
objectName: "tokenPrepareButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 46
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Create token definition")
|
|
enabled: root.canSubmit
|
|
text: root.submitting ? qsTr("Submitting…") : root.prepared ? qsTr("Transaction submitted") : !root.backend || !root.backend.isWalletOpen ? qsTr("Connect wallet to create") : root.canPrepare ? qsTr("Create token definition") : qsTr("Complete required fields")
|
|
onClicked: root.prepareDefinition()
|
|
|
|
contentItem: Text {
|
|
color: prepareButton.enabled ? "#FFFFFF" : root.prepared ? "#78C88D" : "#8E8780"
|
|
font.pixelSize: 15
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: prepareButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: prepareButton.enabled ? "#F26A21" : root.prepared ? "#183222" : "#282522"
|
|
border.color: prepareButton.activeFocus ? "#FFB26B" : prepareButton.enabled ? "#F26A21" : root.prepared ? "#39C06A" : "#3C3833"
|
|
border.width: prepareButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: inspectButton
|
|
|
|
objectName: "tokenInspectButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 42
|
|
visible: root.prepared
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Inspect token definition")
|
|
text: qsTr("Open in Inspect")
|
|
onClicked: root.requestInspect()
|
|
|
|
contentItem: Text {
|
|
color: "#F2D8C7"
|
|
font.pixelSize: 14
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: inspectButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: "#211914"
|
|
border.color: inspectButton.activeFocus ? "#FFB26B" : "#49301F"
|
|
border.width: inspectButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: summaryPanel
|
|
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.fillWidth: true
|
|
Layout.minimumWidth: 0
|
|
Layout.preferredWidth: stage.columnCount === 2 ? 300 : 0
|
|
Layout.maximumWidth: 1120
|
|
implicitHeight: summaryContent.implicitHeight + 32
|
|
radius: 16
|
|
color: "#181818"
|
|
border.color: "#303030"
|
|
border.width: 1
|
|
|
|
ColumnLayout {
|
|
id: summaryContent
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 16
|
|
spacing: 12
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 18
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("Definition summary")
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
implicitHeight: summaryHeadline.implicitHeight + 20
|
|
radius: 10
|
|
color: root.isFungible ? "#211914" : "#182534"
|
|
border.color: root.isFungible ? "#49301F" : "#31435D"
|
|
border.width: 1
|
|
|
|
ColumnLayout {
|
|
id: summaryHeadline
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: 10
|
|
spacing: 5
|
|
|
|
Text {
|
|
color: root.isFungible ? "#F2D8C7" : "#BFD8F4"
|
|
font.pixelSize: 12
|
|
font.weight: Font.DemiBold
|
|
text: root.isFungible ? qsTr("Fungible") : qsTr("Non-fungible")
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 18
|
|
font.weight: Font.DemiBold
|
|
text: nameField.text.length > 0 ? nameField.text : qsTr("Untitled definition")
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: root.supplyLabel
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
color: root.validSupply ? "#E7E1D8" : "#F08A76"
|
|
font.pixelSize: 13
|
|
font.weight: Font.Medium
|
|
text: root.supplyValue
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: root.isFungible ? qsTr("Authority") : qsTr("Printing")
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
Layout.maximumWidth: 170
|
|
color: "#E7E1D8"
|
|
elide: Text.ElideRight
|
|
font.pixelSize: 12
|
|
text: root.authorityValue
|
|
horizontalAlignment: Text.AlignRight
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
|
|
Text {
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
text: qsTr("Metadata")
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 12
|
|
text: root.hasMetadata ? root.metadataStandard === 0 ? qsTr("Simple") : qsTr("Expanded") : qsTr("Not included")
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 1
|
|
color: "#303030"
|
|
}
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 14
|
|
font.weight: Font.DemiBold
|
|
text: qsTr("Readiness")
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Rectangle {
|
|
Layout.preferredWidth: 28
|
|
Layout.preferredHeight: 28
|
|
radius: 14
|
|
color: root.validTargetCount === root.targetCount ? "#183222" : "#332521"
|
|
border.color: root.validTargetCount === root.targetCount ? "#39C06A" : "#D85F4B"
|
|
border.width: 1
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
color: root.validTargetCount === root.targetCount ? "#78C88D" : "#F08A76"
|
|
font.pixelSize: 12
|
|
font.weight: Font.DemiBold
|
|
text: root.validTargetCount === root.targetCount ? qsTr("OK") : qsTr("!")
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 2
|
|
|
|
Text {
|
|
color: "#E7E1D8"
|
|
font.pixelSize: 13
|
|
font.weight: Font.Medium
|
|
text: root.validTargetCount === root.targetCount ? qsTr("All targets look valid") : qsTr("%1 of %2 targets ready").arg(root.validTargetCount).arg(root.targetCount)
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
color: "#A9A098"
|
|
font.pixelSize: 12
|
|
wrapMode: Text.Wrap
|
|
text: root.step === 0 ? qsTr("Account targets appear next.") : root.step === 1 ? qsTr("Review unlocks when every target is valid.") : root.prepared ? qsTr("Transaction submitted to the Token Program.") : qsTr("Ready for final review.")
|
|
}
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: summaryContinueButton
|
|
|
|
objectName: "tokenSummaryContinueButton"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: visible ? 46 : 0
|
|
visible: root.step === 0 && stage.columnCount === 2
|
|
activeFocusOnTab: true
|
|
Accessible.name: qsTr("Continue to account targets")
|
|
enabled: root.canContinue
|
|
text: root.canContinue ? qsTr("Continue to account targets") : qsTr("Complete required fields")
|
|
onClicked: root.step = 1
|
|
|
|
contentItem: Text {
|
|
color: summaryContinueButton.enabled ? "#FFFFFF" : "#8E8780"
|
|
font.pixelSize: 14
|
|
font.weight: Font.DemiBold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: summaryContinueButton.text
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 8
|
|
color: summaryContinueButton.enabled ? "#F26A21" : "#282522"
|
|
border.color: summaryContinueButton.activeFocus ? "#FFB26B" : summaryContinueButton.enabled ? "#F26A21" : "#3C3833"
|
|
border.width: summaryContinueButton.activeFocus ? 2 : 1
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|