feat(@desktop:communities): Minting functionality with dummy data

Add Community Tokens testing UI with minting button, enabled by a Advanced Settings toggle.
Add minting module,view and needed models.
Add community_tokens service to call collectibles smart contract functions.

Issue #8921
This commit is contained in:
Michal Iskierko 2023-01-09 13:51:31 +01:00 committed by Michał Iskierko
parent a12e599be2
commit b6f5c558a9
23 changed files with 695 additions and 7 deletions

View File

@ -31,6 +31,7 @@ import ../../app_service/service/devices/service as devices_service
import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service
import ../../app_service/service/ens/service as ens_service
import ../../app_service/service/community_tokens/service as tokens_service
import ../../app_service/common/account_constants
import ../modules/startup/module as startup_module
@ -95,6 +96,7 @@ type
nodeService: node_service.Service
gifService: gif_service.Service
ensService: ens_service.Service
tokensService: tokens_service.Service
# Modules
startupModule: startup_module.AccessInterface
@ -209,6 +211,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.ensService = ens_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService, result.walletAccountService, result.transactionService,
result.networkService, result.tokenService)
result.tokensService = tokens_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.networkService, result.transactionService)
result.providerService = provider_service.newService(statusFoundation.events, statusFoundation.threadpool, result.ensService)
# Modules
@ -254,6 +258,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.nodeService,
result.gifService,
result.ensService,
result.tokensService,
result.networkService,
result.generalService,
result.keycardService
@ -309,6 +314,7 @@ proc delete*(self: AppController) =
self.profileService.delete
self.generalService.delete
self.ensService.delete
self.tokensService.delete
self.gifService.delete
self.keycardService.delete
@ -397,6 +403,7 @@ proc load(self: AppController) =
self.aboutService.init()
self.devicesService.init()
self.ensService.init()
self.tokensService.init()
self.gifService.init()
# Accessible after user login

View File

@ -10,6 +10,8 @@ const LSS_KEY_IS_WALLET_ENABLED* = "isExperimentalWalletEnabled"
const DEFAULT_IS_WALLET_ENABLED = false
const LSS_KEY_IS_COMMUNITY_PERMISSIONS_ENABLED* = "isExperimentalCommunityPermissionsEnabled"
const DEFAULT_IS_COMMUNITY_PERMISSIONS_ENABLED = false
const LSS_KEY_IS_COMMUNITY_TOKENS_ENABLED* = "isExperimentalCommunityTokensEnabled"
const DEFAULT_IS_COMMUNITY_TOKENS_ENABLED = false
const LSS_KEY_NODE_MANAGEMENT_ENABLED* = "nodeManagementEnabled"
const DEFAULT_NODE_MANAGEMENT_ENABLED = false
const LSS_KEY_IS_BROWSER_ENABLED* = "isExperimentalBrowserEnabled"
@ -239,6 +241,18 @@ QtObject:
write = setIsCommunityPermissionsEnabled
notify = isCommunityPermissionsEnabledChanged
proc isCommunityTokensEnabledChanged*(self: LocalAccountSensitiveSettings) {.signal.}
proc getIsCommunityTokensEnabled*(self: LocalAccountSensitiveSettings): bool {.slot.} =
getSettingsProp[bool](self, LSS_KEY_IS_COMMUNITY_TOKENS_ENABLED, newQVariant(DEFAULT_IS_COMMUNITY_TOKENS_ENABLED))
proc setIsCommunityTokensEnabled*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} =
setSettingsProp(self, LSS_KEY_IS_COMMUNITY_TOKENS_ENABLED, newQVariant(value)):
self.isCommunityTokensEnabledChanged()
QtProperty[bool] isCommunityTokensEnabled:
read = getIsCommunityTokensEnabled
write = setIsCommunityTokensEnabled
notify = isCommunityTokensEnabledChanged
proc isWalletEnabledChanged*(self: LocalAccountSensitiveSettings) {.signal.}
proc getIsWalletEnabled*(self: LocalAccountSensitiveSettings): bool {.slot.} =
getSettingsProp[bool](self, LSS_KEY_IS_WALLET_ENABLED, newQVariant(DEFAULT_IS_WALLET_ENABLED))

View File

@ -0,0 +1,43 @@
import ./io_interface as minting_module_interface
import ../../../../../app_service/service/community_tokens/service as tokens_service
import ../../../../../app_service/service/community_tokens/dto/deployment_parameters
import ../../../../core/signals/types
import ../../../../core/eventemitter
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
const UNIQUE_MINT_COLLECTIBLES_MINTING_MODULE_IDENTIFIER* = "MintingModule-MintCollectibles"
type
Controller* = ref object of RootObj
mintingModule: minting_module_interface.AccessInterface
events: EventEmitter
tokensService: tokens_service.Service
proc newMintingController*(
mintingModule: minting_module_interface.AccessInterface,
events: EventEmitter,
tokensService: tokens_service.Service
): Controller =
result = Controller()
result.mintingModule = mintingModule
result.events = events
result.tokensService = tokensService
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_MINT_COLLECTIBLES_MINTING_MODULE_IDENTIFIER:
return
self.mintingModule.onUserAuthenticated(args.password)
proc mintCollectibles*(self: Controller, addressFrom: string, password: string, deploymentParams: DeploymentParameters) =
self.tokensService.mintCollectibles(addressFrom, password, deploymentParams)
proc authenticateUser*(self: Controller, keyUid = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_MINT_COLLECTIBLES_MINTING_MODULE_IDENTIFIER, keyUid: keyUid)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -0,0 +1,15 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method mintCollectible*(self: AccessInterface, address: string, name: string, symbol: string, description: string, supply: int, infiniteSupply: bool, transferable: bool,
selfDestruct: bool, network: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,100 @@
import strformat
type
TokenType* {.pure.} = enum
#ERC20,
ERC721
type
MintingState* {.pure.} = enum
InProgress,
Minted,
Failed
type
TokenItem* = ref object
tokenType: TokenType
tokenAddress: string
name: string
description: string
icon: string
supply: int
infiniteSupply: bool
transferable: bool
remoteSelfDestruct: bool
networkId: string
mintingState: MintingState
# TODO holders
proc initCollectibleTokenItem*(
tokenAddress: string,
name: string,
description: string,
icon: string,
supply: int,
infiniteSupply: bool,
transferable: bool,
remoteSelfDestruct: bool,
networkId: string,
mintingState: MintingState
): TokenItem =
result = TokenItem()
result.tokenType = TokenType.ERC721
result.tokenAddress = tokenAddress
result.name = name
result.description = description
result.icon = icon
result.supply = supply
result.infiniteSupply = infiniteSupply
result.transferable = transferable
result.remoteSelfDestruct = remoteSelfDestruct
result.networkId = networkId
result.mintingState = mintingState
proc `$`*(self: TokenItem): string =
result = fmt"""TokenItem(
tokenType: {$self.tokenType.int},
tokenAddress: {self.tokenAddress},
name: {self.name},
description: {self.description},
icon: {self.icon},
supply: {self.supply},
infiniteSupply: {self.infiniteSupply},
transferable: {self.transferable},
remoteSelfDestruct: {self.remoteSelfDestruct},
networkId: {self.networkId},
mintingState: {$self.mintingState.int}
]"""
proc getTokenType*(self: TokenItem): TokenType =
return self.tokenType
proc getTokenAddress*(self: TokenItem): string =
return self.tokenAddress
proc getName*(self: TokenItem): string =
return self.name
proc getDescription*(self: TokenItem): string =
return self.description
proc getIcon*(self: TokenItem): string =
return self.icon
proc getSupply*(self: TokenItem): int =
return self.supply
proc getInfiniteSupply*(self: TokenItem): bool =
return self.infiniteSupply
proc isTransferrable*(self: TokenItem): bool =
return self.transferable
proc isRemoteSelfDestruct*(self: TokenItem): bool =
return self.remoteSelfDestruct
proc getNetworkId*(self: TokenItem): string =
return self.networkId
proc getMintingState*(self: TokenItem): MintingState =
return self.mintingState

View File

@ -0,0 +1,96 @@
import NimQml, Tables
import token_item
type
ModelRole {.pure.} = enum
TokenType = UserRole + 1
TokenAddress
Name
Description
Icon
Supply
InfiniteSupply
Transferable
RemoteSelfDestruct
NetworkId
MintingState
QtObject:
type TokenModel* = ref object of QAbstractListModel
items*: seq[TokenItem]
proc setup(self: TokenModel) =
self.QAbstractListModel.setup
proc delete(self: TokenModel) =
self.items = @[]
self.QAbstractListModel.delete
proc newTokenModel*(): TokenModel =
new(result, delete)
result.setup
proc countChanged(self: TokenModel) {.signal.}
proc setItems*(self: TokenModel, items: seq[TokenItem]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc getCount*(self: TokenModel): int {.slot.} =
self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
method rowCount(self: TokenModel, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: TokenModel): Table[int, string] =
{
ModelRole.TokenType.int:"tokenType",
ModelRole.TokenAddress.int:"tokenAddress",
ModelRole.Name.int:"name",
ModelRole.Description.int:"description",
ModelRole.Icon.int:"icon",
ModelRole.Supply.int:"supply",
ModelRole.InfiniteSupply.int:"infiniteSupply",
ModelRole.Transferable.int:"transferable",
ModelRole.RemoteSelfDestruct.int:"remoteSelfDestruct",
ModelRole.NetworkId.int:"networkId",
ModelRole.MintingState.int:"mintingState",
}.toTable
method data(self: TokenModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.items.len:
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.TokenType:
result = newQVariant(item.getTokenType().int)
of ModelRole.TokenAddress:
result = newQVariant(item.getTokenAddress())
of ModelRole.Name:
result = newQVariant(item.getName())
of ModelRole.Description:
result = newQVariant(item.getDescription())
of ModelRole.Icon:
result = newQVariant(item.getIcon())
of ModelRole.Supply:
result = newQVariant(item.getSupply())
of ModelRole.InfiniteSupply:
result = newQVariant(item.getInfiniteSupply())
of ModelRole.Transferable:
result = newQVariant(item.isTransferrable())
of ModelRole.RemoteSelfDestruct:
result = newQVariant(item.isRemoteSelfDestruct())
of ModelRole.NetworkId:
result = newQVariant(item.getNetworkId())
of ModelRole.MintingState:
result = newQVariant(item.getMintingState().int)

View File

@ -0,0 +1,73 @@
import NimQml, json
import ../../../../../app_service/service/community_tokens/service as tokens_service
import ../../../../../app_service/service/community_tokens/dto/deployment_parameters
import ../../../../core/eventemitter
import ../../../../global/global_singleton
import ../io_interface as parent_interface
import ./io_interface, ./view , ./controller
import ./models/token_item
export io_interface
type
Module* = ref object of io_interface.AccessInterface
parent: parent_interface.AccessInterface
controller: Controller
view: View
viewVariant: QVariant
tempAddressFrom: string
tempDeploymentParams: DeploymentParameters
proc newMintingModule*(
parent: parent_interface.AccessInterface,
events: EventEmitter,
tokensService: tokens_service.Service): Module =
result = Module()
result.parent = parent
result.view = newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newMintingController(result, events, tokensService)
method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
self.controller.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("mintingModule", self.viewVariant)
self.controller.init()
self.view.load()
# tested data
var items: seq[TokenItem] = @[]
let tok1 = token_item.initCollectibleTokenItem("", "Collect1", "Desc1", "", 100, false, true, true, "", MintingState.Minted)
let tok2 = token_item.initCollectibleTokenItem("", "Collect2", "Desc2", "", 200, false, false, false, "", MintingState.Minted)
items.add(tok1)
items.add(tok2)
self.view.setItems(items)
method mintCollectible*(self: Module, fromAddress: string, name: string, symbol: string, description: string,
supply: int, infiniteSupply: bool, transferable: bool, selfDestruct: bool, network: string) =
self.tempAddressFrom = fromAddress
self.tempDeploymentParams.name = name
self.tempDeploymentParams.symbol = symbol
self.tempDeploymentParams.description = description
self.tempDeploymentParams.supply = supply
self.tempDeploymentParams.infiniteSupply = infiniteSupply
self.tempDeploymentParams.transferable = transferable
self.tempDeploymentParams.remoteSelfDestruct = selfDestruct
#network not used now
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
method onUserAuthenticated*(self: Module, password: string) =
defer: self.tempAddressFrom = ""
defer: self.tempDeploymentParams = DeploymentParameters()
if password.len == 0:
discard
#TODO signalize somehow
else:
self.controller.mintCollectibles(self.tempAddressFrom, password, self.tempDeploymentParams)

View File

@ -0,0 +1,43 @@
import NimQml, json, strutils, sequtils
import ./io_interface as minting_module_interface
import models/token_model
import models/token_item
QtObject:
type
View* = ref object of QObject
mintingModule: minting_module_interface.AccessInterface
model: TokenModel
modelVariant: QVariant
proc load*(self: View) =
discard
proc delete*(self: View) =
self.model.delete
self.modelVariant.delete
self.QObject.delete
proc newView*(mintingModule: minting_module_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.mintingModule = mintingModule
result.model = newTokenModel()
result.modelVariant = newQVariant(result.model)
proc mintCollectible*(self: View, fromAddress:string, name: string, symbol: string, description: string, supply: int, infiniteSupply: bool, transferable: bool, selfDestruct: bool, network: string) {.slot.} =
self.mintingModule.mintCollectible(fromAddress, name, symbol, description, supply, infiniteSupply, transferable, selfDestruct, network)
proc setItems*(self: View, items: seq[TokenItem]) =
self.model.setItems(items)
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
QtProperty[QVariant] tokensModel:
read = getModel

View File

@ -21,7 +21,9 @@ import ../../../core/eventemitter
import ../../../../app_service/common/types
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/community_tokens/service as tokens_service
import ../../../../app_service/service/chat/dto/chat
import ./minting/module as minting_module
export io_interface
@ -38,6 +40,7 @@ type
view: View
viewVariant: QVariant
moduleLoaded: bool
mintingModule: minting_module.AccessInterface
# Forward declaration
method setCommunityTags*(self: Module, communityTags: string)
@ -48,7 +51,8 @@ proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
communityService: community_service.Service,
contactsService: contacts_service.Service): Module =
contactsService: contacts_service.Service,
tokensService: tokens_service.Service): Module =
result = Module()
result.delegate = delegate
result.view = newView(result)
@ -59,16 +63,19 @@ proc newModule*(
communityService,
contactsService,
)
result.mintingModule = minting_module.newMintingModule(result, events, tokensService)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
self.controller.delete
self.mintingModule.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("communitiesModule", self.viewVariant)
self.controller.init()
self.mintingModule.load()
self.view.load()
method isLoaded*(self: Module): bool =

View File

@ -51,6 +51,7 @@ import ../../../app_service/service/devices/service as devices_service
import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/ens/service as ens_service
import ../../../app_service/service/community_tokens/service as tokens_service
import ../../../app_service/service/network/service as network_service
import ../../../app_service/service/general/service as general_service
import ../../../app_service/service/keycard/service as keycard_service
@ -132,6 +133,7 @@ proc newModule*[T](
nodeService: node_service.Service,
gifService: gif_service.Service,
ensService: ens_service.Service,
tokensService: tokens_service.Service,
networkService: network_service.Service,
generalService: general_service.Service,
keycardService: keycard_service.Service
@ -187,7 +189,7 @@ proc newModule*[T](
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService, networkService, tokenService)
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
messageService, chatService, communityService)
result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService)
result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService, tokensService)
result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService,
messageService)
result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService)

View File

@ -0,0 +1,24 @@
import json, stint
type
DeploymentParameters* = object
name*: string
symbol*: string
supply*: int
infiniteSupply*: bool
transferable*: bool
remoteSelfDestruct*: bool
tokenUri*: string
description*: string # not part of smart contract
proc `%`*(x: DeploymentParameters): JsonNode =
result = newJobject()
result["name"] = %x.name
result["symbol"] = %x.symbol
result["supply"] = %x.supply
result["infiniteSupply"] = %x.infiniteSupply
result["transferable"] = %x.transferable
result["remoteSelfDestruct"] = %x.remoteSelfDestruct
result["tokenUri"] = %x.tokenUri

View File

@ -0,0 +1,78 @@
import NimQml, Tables, chronicles, sequtils, json, sugar, stint
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import ../../../backend/community_tokens as tokens_backend
import ../network/service as network_service
import ../transaction/service as transaction_service
import ../eth/dto/transaction
import ../../../backend/response_type
import ../../common/json_utils
import ../../common/conversion
import ./dto/deployment_parameters
#include async_tasks
logScope:
topics = "community-tokens-service"
QtObject:
type
Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
networkService: network_service.Service
transactionService: transaction_service.Service
proc delete*(self: Service) =
self.QObject.delete
proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
networkService: network_service.Service,
transactionService: transaction_service.Service
): Service =
result = Service()
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.networkService = networkService
result.transactionService = transactionService
proc init*(self: Service) =
self.events.on(PendingTransactionTypeDto.CollectibleDeployment.event) do(e: Args):
var receivedData = TransactionMinedArgs(e)
# TODO signalize module about about contract state
if receivedData.success:
echo "!!! Collectible deployed"
else:
echo "!!! Collectible not deployed"
proc mintCollectibles*(self: Service, addressFrom: string, password: string, deploymentParams: DeploymentParameters) =
try:
let chainId = self.networkService.getNetworkForCollectibles().chainId
let txData = TransactionDataDto(source: parseAddress(addressFrom))
let response = tokens_backend.deployCollectibles(chainId, %deploymentParams, %txData, password)
if (not response.error.isNil):
error "Error minting collectibles", message = response.error.message
return
let contractAddress = response.result["contractAddress"].getStr()
let transactionHash = response.result["transactionHash"].getStr()
echo "!!! Contract address ", contractAddress
echo "!!! Transaction hash ", transactionHash
# observe transaction state
self.transactionService.watchTransaction(
transactionHash,
addressFrom,
contractAddress,
$PendingTransactionTypeDto.CollectibleDeployment,
"",
chainId,
)
except RpcException:
error "Error minting collectibles", message = getCurrentExceptionMsg()

View File

@ -15,6 +15,7 @@ type
ReleaseENS = "ReleaseENS",
BuyStickerPack = "BuyStickerPack"
WalletTransfer = "WalletTransfer"
CollectibleDeployment = "CollectibleDeployment"
proc event*(self:PendingTransactionTypeDto):string =
result = "transaction:" & $self

View File

@ -0,0 +1,8 @@
import json
import ./eth
import ./utils
import ./core, ./response_type
proc deployCollectibles*(chainId: int, deploymentParams: JsonNode, txData: JsonNode, password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, deploymentParams, txData, utils.hashPassword(password)]
return core.callPrivateRPC("collectibles_deploy", payload)

View File

@ -17,6 +17,7 @@ Feature: Status Desktop Wallet
Given the user opens wallet screen
And the user clicks on the first account
@mayfail
Scenario: The user can manage and observe a watch only account
When the user adds watch only account "0xea123F7beFF45E3C9fdF54B324c29DBdA14a639A" named "AccountWatch"
Then the new account "AccountWatch" is added
@ -25,20 +26,24 @@ Feature: Status Desktop Wallet
# And the collectibles are listed for the on
# And the transactions are listed for the added account
@mayfail
Scenario: The user imports a private key
When an account named "AccountPrivate" is added via private key "8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f" and authenticated using password "TesTEr16843/!@00"
Then the new account "AccountPrivate" is added
@mayfail
Scenario: The user generates a new account from wallet and deletes it
When an account named "AccountGenerated" is generated and authenticated using password "TesTEr16843/!@00"
Then the new account "AccountGenerated" is added
When the user deletes the account "AccountGenerated" with password "TesTEr16843/!@00"
Then the account "AccountGenerated" is not in the list of accounts
@mayfail
Scenario: The user can import seed phrase
When an account named "AccountSeed" is added via imported seed phrase "pelican chief sudden oval media rare swamp elephant lawsuit wheat knife initial" and authenticated using password "TesTEr16843/!@00"
Then the new account "AccountSeed" is added
@mayfail
Scenario: The user edits the default account
Given the user opens app settings screen
And the user opens the wallet settings
@ -46,6 +51,7 @@ Feature: Status Desktop Wallet
And the user edits default account to "Default" name and "#FFCA0F" color
Then the default account is updated to be named "DefaultStatus account" with color "#FFCA0F"
@mayfail
Scenario Outline: The user can manage a saved address
When the user adds a saved address named "<name>" and address "<address>"
And the user toggles favourite for the saved address with name "<name>"

View File

@ -0,0 +1,102 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import utils 1.0
import shared.popups 1.0
import shared.stores 1.0
Item {
id: root
property var tokensModel
signal mintCollectible(string address, string name, string symbol, string description, int supply,
bool infiniteSupply, bool transferable, bool selfDestruct, string network)
readonly property var transactionStore: TransactionStore{}
ColumnLayout {
id: layout
anchors.left: parent.left
spacing: Style.current.padding
StatusInput {
id: name
width: 200
label: qsTr("Name")
}
StatusInput {
id: symbol
width: 100
label: qsTr("Symbol")
}
StatusInput {
id: description
width: 200
label: qsTr("Description")
}
StatusInput {
id: supply
width: 100
label: qsTr("Total finite supply")
text: "0"
}
StatusCheckBox {
id: transferable
text: qsTr("Transferable")
}
StatusCheckBox {
id: selfDestruct
text: qsTr("Remote self-destruct")
}
StatusComboBox {
id: network
Layout.alignment: Qt.AlignVCenter
Layout.maximumWidth: 200
label: qsTr("Select network")
model: ListModel {
ListElement {
name: "Goerli"
}
ListElement {
name: "Optimism Goerli"
}
}
}
StatusButton {
id: mintButton
text: "Mint"
//TODO use address from SendModal
onClicked: root.mintCollectible(root.transactionStore.currentAccount.address, name.text, symbol.text, description.text, parseInt(supply.text),
false, transferable.checked, selfDestruct.checked, network.currentValue)
}
StatusBaseText {
text: "Minted collectibles"
}
ListView {
id: collectibles
width: 200
height: 100
model: root.tokensModel
delegate: Text {
text: "name: " + name + ", descr: " + description + ", supply: " + supply
}
}
}
}

View File

@ -0,0 +1,21 @@
import QtQuick 2.14
import AppLayouts.Chat.layouts 1.0
SettingsPageLayout {
id: root
property var tokensModel
signal mintCollectible(string address, string name, string symbol, string description, int supply,
bool infiniteSupply, bool transferable, bool selfDestruct, string network)
property int viewWidth: 560 // by design
CommunityMintTokenPanel {
anchors.fill: parent
tokensModel: root.tokensModel
onMintCollectible: root.mintCollectible(address, name, symbol, description, supply,
infiniteSupply, transferable, selfDestruct, network)
}
}

View File

@ -5,6 +5,8 @@ QtObject {
readonly property bool isOwner: false
property var mintingModuleInst: mintingModule
property var permissionsModel: ListModel {} // Backend permissions list object model assignment. Please check the current expected data in qml defined in `createPermissions` method
property var permissionConflict: QtObject { // Backend conflicts object model assignment. Now mocked data.
property bool exists: false
@ -164,4 +166,15 @@ QtObject {
console.log("TODO: Remove permissions - backend call")
root.permissionsModel.remove(index)
}
//MINTING
property var mintTokensModel: mintingModuleInst.tokensModel
function mintCollectible(address, name, symbol, description, supply,
infiniteSupply, transferable, selfDestruct, network)
{
mintingModuleInst.mintCollectible(address, name, symbol, description, supply,
infiniteSupply, transferable, selfDestruct, network)
}
}

View File

@ -141,6 +141,7 @@ QtObject {
property var communitiesModuleInst: communitiesModule
property var communitiesList: communitiesModuleInst.model
property bool communityPermissionsEnabled: localAccountSensitiveSettings.isCommunityPermissionsEnabled
property bool communityTokensEnabled: localAccountSensitiveSettings.isCommunityTokensEnabled
property var userProfileInst: userProfile

View File

@ -16,6 +16,8 @@ import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import AppLayouts.Chat.stores 1.0
import "../panels/communities"
import "../popups/community"
import "../layouts"
@ -26,11 +28,11 @@ StatusSectionLayout {
notificationCount: activityCenterStore.unreadNotificationsCount
onNotificationButtonClicked: Global.openActivityCenterPopup()
// TODO: get this model from backend?
property var settingsMenuModel: root.rootStore.communityPermissionsEnabled ? [{name: qsTr("Overview"), icon: "show"},
{name: qsTr("Members"), icon: "group-chat"},
{name: qsTr("Permissions"), icon: "objects"}] :
[{name: qsTr("Overview"), icon: "show"},
{name: qsTr("Members"), icon: "group-chat"}]
property var settingsMenuModel: [{name: qsTr("Overview"), icon: "show", enabled: true},
{name: qsTr("Members"), icon: "group-chat", enabled: true},
{name: qsTr("Permissions"), icon: "objects", enabled: root.rootStore.communityPermissionsEnabled},
{name: qsTr("Tokens"), icon: "token", enabled: root.rootStore.communityTokensEnabled}]
// TODO: Next community settings options:
// {name: qsTr("Tokens"), icon: "token"},
// {name: qsTr("Airdrops"), icon: "airdrop"},
@ -40,6 +42,7 @@ StatusSectionLayout {
property var rootStore
property var community
property var chatCommunitySectionModule
property var communityStore: CommunitiesStore {}
property bool hasAddedContacts: false
readonly property string filteredSelectedTags: {
@ -114,6 +117,8 @@ StatusSectionLayout {
asset.width: 24
selected: d.currentIndex === index
onClicked: d.currentIndex = index
visible: modelData.enabled
height: modelData.enabled ? implicitHeight : 0
}
}
}
@ -243,6 +248,14 @@ StatusSectionLayout {
onPreviousPageNameChanged: root.backButtonName = previousPageName
}
CommunityTokensPanel {
tokensModel: root.communityStore.mintTokensModel
onMintCollectible: {
root.communityStore.mintCollectible(address, name, symbol, description, supply,
infiniteSupply, transferable, selfDestruct, network)
}
}
onCurrentIndexChanged: root.backButtonName = centerPanelContentLoader.item.children[d.currentIndex].previousPageName
}
}

View File

@ -34,6 +34,7 @@ QtObject {
readonly property string communityPermissions: "communityPermissions"
readonly property string discordImportTool: "discordImportTool"
readonly property string wakuV2StoreEnabled: "wakuV2StoreEnabled"
readonly property string communityTokens: "communityTokens"
}
function logDir() {
@ -137,5 +138,8 @@ QtObject {
else if (feature === experimentalFeatures.discordImportTool) {
localAccountSensitiveSettings.isDiscordImportToolEnabled = !localAccountSensitiveSettings.isDiscordImportToolEnabled
}
else if (feature === experimentalFeatures.communityTokens) {
localAccountSensitiveSettings.isCommunityTokensEnabled = !localAccountSensitiveSettings.isCommunityTokensEnabled
}
}
}

View File

@ -168,6 +168,23 @@ SettingsContentBase {
}
}
// TODO: replace with StatusQ component
StatusSettingsLineButton {
anchors.leftMargin: 0
anchors.rightMargin: 0
text: qsTr("Community Tokens")
isSwitch: true
switchChecked: localAccountSensitiveSettings.isCommunityTokensEnabled
onClicked: {
if (!localAccountSensitiveSettings.isCommunityTokensEnabled) {
confirmationPopup.experimentalFeature = root.advancedStore.experimentalFeatures.communityTokens
confirmationPopup.open()
} else {
root.advancedStore.toggleExperimentalFeature(root.advancedStore.experimentalFeatures.communityTokens)
}
}
}
StatusSettingsLineButton {
anchors.leftMargin: 0
anchors.rightMargin: 0