feat(@desktop/permissions): use non community NFTs in permissions

Issue #15077
This commit is contained in:
Michal Iskierko 2024-06-24 16:51:18 +02:00 committed by Michał Iskierko
parent 62092e5083
commit 1bc979290f
19 changed files with 166 additions and 57 deletions

View File

@ -21,6 +21,8 @@ import ../../../core/signals/types
import ../../../core/eventemitter
import ../../../core/unique_event_emitter
import backend/collectibles_types
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
@ -730,9 +732,11 @@ proc getTokenDecimals*(self: Controller, symbol: string): int =
return asset.decimals
return 0
proc getContractAddressesForToken*(self: Controller, symbol: string): Table[int, string] =
# find addresses by tokenKey from UI
# tokenKey can be: symbol for ERC20, or chain+address[+tokenId] for ERC721
proc getContractAddressesForToken*(self: Controller, tokenKey: string): Table[int, string] =
var contractAddresses = initTable[int, string]()
let token = self.tokenService.findTokenBySymbol(symbol)
let token = self.tokenService.findTokenBySymbol(tokenKey)
if token != nil:
for addrPerChain in token.addressPerChainId:
# depending on areTestNetworksEnabled (in getNetworkByChainId), contractAddresses will
@ -741,9 +745,15 @@ proc getContractAddressesForToken*(self: Controller, symbol: string): Table[int,
if network == nil:
continue
contractAddresses[addrPerChain.chainId] = addrPerChain.address
let communityToken = self.communityService.getCommunityTokenBySymbol(self.getMySectionId(), symbol)
let communityToken = self.communityService.getCommunityTokenBySymbol(self.getMySectionId(), tokenKey)
if communityToken.address != "":
contractAddresses[communityToken.chainId] = communityToken.address
if contractAddresses.len == 0:
try:
let chainAndAddress = toContractID(tokenKey)
contractAddresses[chainAndAddress.chainID] = chainAndAddress.address
except Exception:
discard
return contractAddresses
proc getCommunityTokenList*(self: Controller): seq[CommunityTokenDto] =

View File

@ -1010,7 +1010,8 @@ proc updateTokenPermissionModel*(self: Module, permissions: Table[string, CheckP
tokenCriteriaItem.amount,
tokenCriteriaItem.`type`,
tokenCriteriaItem.ensPattern,
criteriaResult.criteria[index]
criteriaResult.criteria[index],
tokenCriteriaItem.addresses
)
if criteriaResult.criteria[index] == false:
@ -1530,11 +1531,12 @@ method createOrEditCommunityTokenPermission*(self: Module, communityId: string,
let tokenCriteriaJsonObj = tokenCriteriaJson.parseJson
for tokenCriteria in tokenCriteriaJsonObj:
let tokenKey = tokenCriteria{"key"}.getStr()
var tokenCriteriaDto = tokenCriteria.toTokenCriteriaDto
if tokenCriteriaDto.`type` == TokenType.ERC20:
tokenCriteriaDto.decimals = self.controller.getTokenDecimals(tokenCriteriaDto.symbol)
tokenCriteriaDto.decimals = self.controller.getTokenDecimals(tokenKey)
let contractAddresses = self.controller.getContractAddressesForToken(tokenCriteriaDto.symbol)
let contractAddresses = self.controller.getContractAddressesForToken(tokenKey)
if contractAddresses.len == 0 and tokenCriteriaDto.`type` != TokenType.ENS:
if permissionId == "":
self.onCommunityTokenPermissionCreationFailed(communityId)

View File

@ -433,6 +433,12 @@ proc getKeypairByKeyUid*(self: Controller, keyUid: string): KeypairDto =
proc getKeypairs*(self: Controller): seq[KeypairDto] =
return self.walletAccountService.getKeypairs()
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.getWalletAccounts()
proc getEnabledChainIds*(self: Controller): seq[int] =
return self.walletAccountService.getEnabledChainIds()
proc disconnectKeycardReponseSignal(self: Controller) =
self.events.disconnect(self.connectionKeycardResponse)

View File

@ -549,8 +549,9 @@ method requestCancelDiscordChannelImport*(self: Module, discordChannelId: string
proc createCommunityTokenItem(self: Module, token: CommunityTokensMetadataDto, communityId: string, supply: string,
infiniteSupply: bool, privilegesLevel: int): TokenListItem =
let communityTokenDecimals = if token.tokenType == TokenType.ERC20: 18 else: 0
let key = if token.tokenType == TokenType.ERC721: token.getContractIdFromFirstAddress() else: token.symbol
result = initTokenListItem(
key = token.symbol,
key = key,
name = token.name,
symbol = token.symbol,
color = "", # community tokens don't have `color`
@ -911,7 +912,8 @@ proc applyPermissionResponse*(self: Module, communityId: string, permissions: Ta
tokenCriteriaItem.amount,
tokenCriteriaItem.`type`,
tokenCriteriaItem.ensPattern,
criteriaResult.criteria[index]
criteriaResult.criteria[index],
tokenCriteriaItem.addresses
)
if criteriaResult.criteria[index] == false:

View File

@ -64,7 +64,7 @@ QtObject:
proc hasCollectibleData(self: CollectiblesEntry): bool =
return self.data != nil and isSome(self.data.collectibleData)
proc getCollectibleData(self: CollectiblesEntry): backend.CollectibleData =
proc getCollectibleData*(self: CollectiblesEntry): backend.CollectibleData =
return self.data.collectibleData.get()
proc hasCollectionData(self: CollectiblesEntry): bool =

View File

@ -18,6 +18,7 @@ type
CollectionUid
CollectionName
CollectionSlug
CollectionImageUrl
IsLoading
Ownership
# Community-related roles
@ -139,6 +140,7 @@ QtObject:
CollectibleRole.CollectionUid.int:"collectionUid",
CollectibleRole.CollectionName.int:"collectionName",
CollectibleRole.CollectionSlug.int:"collectionSlug",
CollectibleRole.CollectionImageUrl.int:"collectionImageUrl",
CollectibleRole.IsLoading.int:"isLoading",
CollectibleRole.Ownership.int:"ownership",
CollectibleRole.CommunityId.int:"communityId",
@ -153,9 +155,7 @@ QtObject:
if (index.row < 0 or index.row >= self.getCount()):
return
let enumRole = role.CollectibleRole
if index.row < self.items.len:
let item = self.items[index.row]
case enumRole:
@ -183,6 +183,8 @@ QtObject:
result = newQVariant(item.getCollectionName())
of CollectibleRole.CollectionSlug:
result = newQVariant(item.getCollectionSlug())
of CollectibleRole.CollectionImageUrl:
result = newQVariant(item.getCollectionImageURL())
of CollectibleRole.IsLoading:
result = newQVariant(false)
of CollectibleRole.Ownership:
@ -195,6 +197,10 @@ QtObject:
result = newQVariant(item.getTokenType())
of CollectibleRole.Soulbound:
result = newQVariant(item.getSoulbound())
else:
result = newQVariant()
else:
result = newQVariant()
proc resetCollectibleItems(self: Model, newItems: seq[CollectiblesEntry] = @[]) =
self.beginResetModel()

View File

@ -1,4 +1,5 @@
import stew/shims/strformat
import stew/shims/strformat, tables
import backend/collectibles_types
type
TokenCriteriaItem* = object
@ -8,6 +9,7 @@ type
`type`*: int
ensPattern*: string
criteriaMet*: bool
addresses*: Table[int, string]
proc initTokenCriteriaItem*(
symbol: string,
@ -15,7 +17,8 @@ proc initTokenCriteriaItem*(
amount: string,
`type`: int,
ensPattern: string,
criteriaMet: bool
criteriaMet: bool,
addresses: Table[int, string]
): TokenCriteriaItem =
result.symbol = symbol
result.name = name
@ -23,6 +26,7 @@ proc initTokenCriteriaItem*(
result.ensPattern = ensPattern
result.amount = amount
result.criteriaMet = criteriaMet
result.addresses = addresses
proc `$`*(self: TokenCriteriaItem): string =
result = fmt"""TokenCriteriaItem(
@ -31,7 +35,8 @@ proc `$`*(self: TokenCriteriaItem): string =
amount: {self.amount},
type: {self.type},
ensPattern: {self.ensPattern},
criteriaMet: {self.criteriaMet}
criteriaMet: {self.criteriaMet},
addresses: {self.addresses}
]"""
proc getType*(self: TokenCriteriaItem): int =
@ -51,3 +56,12 @@ proc getEnsPattern*(self: TokenCriteriaItem): string =
proc getCriteriaMet*(self: TokenCriteriaItem): bool =
return self.criteriaMet
proc getAddresses*(self: TokenCriteriaItem): Table[int, string] =
return self.addresses
proc getContractIdFromFirstAddress*(self: TokenCriteriaItem): string =
for chainID, address in self.addresses:
let contractId = ContractID(chainID: chainID, address: address)
return contractId.toString()
return ""

View File

@ -61,6 +61,8 @@ QtObject:
of ModelRole.Key:
if item.getType() == ord(TokenType.ENS):
result = newQVariant(item.getEnsPattern())
elif item.getType() == ord(TokenType.ERC721):
result = newQVariant(item.getContractIdFromFirstAddress())
else:
result = newQVariant(item.getSymbol())
of ModelRole.Type:

View File

@ -80,7 +80,8 @@ proc buildTokenPermissionItem*(tokenPermission: CommunityTokenPermissionDto, cha
tc.amountInWei,
tc.`type`.int,
tc.ensPattern,
false # tokenCriteriaMet will be updated by a call to checkPermissionsToJoin
false, # tokenCriteriaMet will be updated by a call to checkPermissionsToJoin
tc.contractAddresses
)
tokenCriteriaItems.add(tokenCriteriaItem)

View File

@ -138,7 +138,6 @@ QtObject:
else:
offset = self.tempItems.len
self.fetchFromStart = false
let response = backend_collectibles.getOwnedCollectiblesAsync(self.requestId, self.chainIds, self.addresses, self.filter, offset, FETCH_BATCH_COUNT_DEFAULT, self.dataType, self.fetchCriteria)
if response.error != nil:
self.model.setIsFetching(false)

View File

@ -10,6 +10,8 @@ import ../../chat/dto/chat
import ../../shared_urls/dto/url_data
import ../../../../app_service/common/types
import backend/collectibles_types
type RequestToJoinType* {.pure.}= enum
Pending = 1,
Declined = 2,
@ -291,7 +293,11 @@ proc toTokenCriteriaDto*(jsonObj: JsonNode): TokenCriteriaDto =
if result.`type` == TokenType.ENS:
discard jsonObj.getProp("key", result.ensPattern)
else:
discard jsonObj.getProp("key", result.symbol)
var tmpSymbol = ""
discard jsonObj.getProp("key", tmpSymbol)
if not isContractID(tmpSymbol):
# overwrite only if key does not contain contractID
result.symbol = tmpSymbol
proc toCommunityTokenPermissionDto*(jsonObj: JsonNode): CommunityTokenPermissionDto =
result = CommunityTokenPermissionDto()
@ -652,3 +658,9 @@ proc findOwner*(self: CommunityDto): ChatMember =
if member.role == MemberRole.Owner:
return member
raise newException(ValueError, "No owner found in members list")
proc getContractIdFromFirstAddress*(self: CommunityTokensMetadataDto): string =
for chainID, address in self.addresses:
let contractId = ContractID(chainID: chainID, address: address)
return contractId.toString()
return ""

View File

@ -151,6 +151,13 @@ proc toContractID*(t: string): ContractID =
var parts = t.split("+")
return ContractID(chainID: parts[0].parseInt(), address: parts[1])
proc isContractID*(t: string): bool =
try:
discard toContractID(t)
return true
except Exception:
return false
# CollectibleUniqueID
proc `$`*(self: CollectibleUniqueID): string =
return fmt"""CollectibleUniqueID(

View File

@ -49,39 +49,56 @@ QtObject {
}
}
readonly property var tmp: SortFilterProxyModel {
id: tmpSfpm
readonly property var communityCollectiblesModelWithCollectionRoles: SortFilterProxyModel {
sourceModel: communitiesModuleInst.collectiblesModel
proxyRoles: ExpressionRole {
function collectibleIcon(icon) {
return !!icon ? icon : Style.png("tokens/DEFAULT-TOKEN")
}
proxyRoles: [
ExpressionRole {
function collectibleIcon(icon) {
return !!icon ? icon : Style.png("tokens/DEFAULT-TOKEN")
}
name: "iconSource"
expression: collectibleIcon(model.icon)
}
},
ExpressionRole {
name: "collectionUid"
expression: model.key
},
ExpressionRole {
function collectibleIcon(icon) {
return !!icon ? icon : Style.png("tokens/DEFAULT-TOKEN")
}
name: "collectionImageUrl"
expression: collectibleIcon(model.icon)
}
]
}
readonly property var tmp2: ObjectProxyModel {
readonly property var walletCollectiblesModel: ObjectProxyModel {
sourceModel: WalletStore.RootStore.collectiblesStore.allCollectiblesModel
delegate: QtObject {
readonly property string key: model.symbol
readonly property string key: model.symbol ?? ""
readonly property string shortName: model.collectionName ? model.collectionName : model.collectionUid ? model.collectionUid : ""
readonly property string symbol: shortName
readonly property int category: 1
readonly property string name: shortName
readonly property int category: 1 // Own
}
exposedRoles: ["key", "symbol", "shortName"]
exposedRoles: ["key", "symbol", "shortName", "name", "category"]
expectedRoles: ["symbol", "collectionName", "collectionUid"]
}
readonly property var tmp3: SortFilterProxyModel {
id: tmpSfpm2
readonly property var walletCollectiblesGroupingModel: GroupingModel {
sourceModel: walletCollectiblesModel
sourceModel: tmp2
groupingRoleName: "collectionUid"
submodelRoleName: "subnames"
}
readonly property var walletNonCommunityCollectiblesModel: SortFilterProxyModel {
sourceModel: walletCollectiblesGroupingModel
filters: ValueFilter {
roleName: "communityId"
@ -89,10 +106,8 @@ QtObject {
}
}
property var r: RolesRenamingModel {
id: renaming
sourceModel: tmpSfpm2
property var walletCollectiblesWithIconSourceModel: RolesRenamingModel {
sourceModel: walletNonCommunityCollectiblesModel
mapping: RoleRename {
from: "mediaUrl"
@ -101,13 +116,12 @@ QtObject {
}
property var collectiblesModel: ConcatModel {
sources: [
SourceModel {
model: tmpSfpm
model: communityCollectiblesModelWithCollectionRoles
},
SourceModel {
model: renaming
model: walletCollectiblesWithIconSourceModel
}
]
}

View File

@ -127,12 +127,20 @@ Item {
AnyOf {
enabled: root.showAllTokensMode
/*
// this category is not used
ValueFilter {
roleName: "category"
value: TokenCategories.Category.Own
}*/
}
ValueFilter {
roleName: "category"
value: TokenCategories.Category.General
}
ValueFilter {
roleName: "category"
value: TokenCategories.Category.Community
}
AllOf {
ValueFilter {

View File

@ -95,7 +95,7 @@ StatusListView {
name: model.name
shortName: model.shortName ?? ""
iconSource: model.iconSource ?? ""
iconSource: model.iconSource ? model.iconSource : Style.png("tokens/DEFAULT-TOKEN")
showSubItemsIcon: !!model.subItems && model.subItems.count > 0
selected: root.checkedKeys.includes(model.key)
amount: {

View File

@ -12,7 +12,20 @@ import utils 1.0
QtObject {
function getTokenByKey(model, key) {
return Internal.PermissionUtils.getTokenByKey(model, key)
var item
// key format:
// chainId+address[+tokenId] - ERC721
// symbol - ERC20
// collectionUid model role keeps chainId+address for every ERC721
// key model role keeps: symbol for ERC20, chainId+address for community ERC721 tokens, chainId+address+tokenId for ERC721 tokens from wallet
let collectionUid = PermissionsHelpers.getCollectionUidFromKey(key)
if(collectionUid !== "") {
item = ModelUtils.getByKey(model, "collectionUid", collectionUid)
} else {
item = Internal.PermissionUtils.getTokenByKey(model, key)
}
return item
}
function getTokenNameByKey(model, key) {
@ -31,9 +44,10 @@ QtObject {
function getTokenIconByKey(model, key) {
const item = getTokenByKey(model, key)
const defaultIcon = Style.png("tokens/DEFAULT-TOKEN")
if (item)
return item.iconSource ?? ""
return ""
return item.iconSource ? item.iconSource : defaultIcon
return defaultIcon
}
function getTokenDecimalsByKey(model, key) {
@ -113,4 +127,14 @@ QtObject {
else
return tMasterTokenSymbolTag + shortName.toUpperCase()
}
function getCollectionUidFromKey(key) {
const parts = key.split('+');
if(parts.length === 2)
return key
else if(parts.length === 3)
return parts[0]+"+"+parts[1]
else
return ""
}
}

View File

@ -273,8 +273,7 @@ StackView {
const holdings = dirtyValues.holdingsRequired ?
ModelUtils.modelToArray(
dirtyValues.selectedHoldingsModel,
["key", "type", "amount"]) : []
["key", "type", "amount", "symbol"]) : []
const channels = root.showChannelSelector ?
ModelUtils.modelToArray(
dirtyValues.selectedChannelsModel, ["key"]) :
@ -295,9 +294,8 @@ StackView {
const holdings = dirtyValues.holdingsRequired ?
ModelUtils.modelToArray(
dirtyValues.selectedHoldingsModel,
["key", "type", "amount"])
["key", "type", "amount", "symbol"])
: []
const channels = ModelUtils.modelToArray(
dirtyValues.selectedChannelsModel, ["key"])

View File

@ -245,9 +245,10 @@ StatusScrollView {
function addItem(type, item, amount) {
const key = item.key
const symbol = item.symbol
d.dirtyValues.selectedHoldingsModel.append(
{ type, key, amount })
{ type, key, amount, symbol })
}
function prepareUpdateIndex(key) {
@ -310,7 +311,7 @@ StatusScrollView {
d.dirtyValues.selectedHoldingsModel.set(
itemIndex,
{ type: Constants.TokenType.ERC721, key, amount: String(amount) })
{ type: Constants.TokenType.ERC721, key, amount: String(amount), symbol: modelItem.symbol })
dropdown.close()
}

View File

@ -37,15 +37,18 @@ SortFilterProxyModel {
return item.decimals
}
function getText(type, key, amount) {
function getText(type, key, amount, defaultText) {
const model = type === Constants.TokenType.ERC20
? assetsModel
: collectiblesModel
const item = PermissionsHelpers.getTokenByKey(model, key)
const name = getName(type, item, key)
let item = PermissionsHelpers.getTokenByKey(model, key)
let name = getName(type, item, key)
const decimals = getDecimals(type, item)
if (name === "")
name = defaultText
return PermissionsHelpers.setHoldingsTextFormat(
type, name, amount, decimals)
}
@ -55,9 +58,9 @@ SortFilterProxyModel {
expression: {
_assetsChanges.revision
_collectiblesChanges.revision
return getText(model.type, model.key, model.amount)
return getText(model.type, model.key, model.amount, model.symbol)
}
expectedRoles: ["type", "key", "amount"]
expectedRoles: ["type", "key", "amount", "symbol", "shortName"]
},
FastExpressionRole {
name: "imageSource"