fix(communities): deltas between designs and build for join token gated community flow
This commit: - improves selection of addresses to reveal - keeps the selection state for the popup lifetime - brings higher granularity in terms of signed requests by keypairs - meets new requirements from the latest related Figma - merges edit shared addresses feature and request to join community features into a single component, cause the flow is logically the same, with the only difference that when editing revealed addresses we don't show the community intro screen Fixes at least points 3 and 4 from #13988
This commit is contained in:
parent
3b16f203b5
commit
665d1cb949
|
@ -224,7 +224,7 @@ method prepareKeypairsForSigning*(self: AccessInterface, communityId: string, en
|
|||
airdropAddress: string, editMode: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method signSharedAddressesForAllNonKeycardKeypairs*(self: AccessInterface) {.base.} =
|
||||
method signProfileKeypairAndAllNonKeycardKeypairs*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method signSharedAddressesForKeypair*(self: AccessInterface, keyUid: string, pin: string) {.base.} =
|
||||
|
|
|
@ -675,7 +675,7 @@ method shareCommunityChannelUrlWithChatKey*(self: Module, communityId: string, c
|
|||
method shareCommunityChannelUrlWithData*(self: Module, communityId: string, chatId: string): string =
|
||||
return self.controller.shareCommunityChannelUrlWithData(communityId, chatId)
|
||||
|
||||
proc signRevealedAddressesThatBelongToRegularKeypairs(self: Module): bool =
|
||||
proc signRevealedAddressesForNonKeycardKeypairs(self: Module): bool =
|
||||
var signingParams: seq[SignParamsDto]
|
||||
for address, details in self.joiningCommunityDetails.addressesToShare.pairs:
|
||||
if details.signature.len > 0:
|
||||
|
@ -702,8 +702,24 @@ proc signRevealedAddressesThatBelongToRegularKeypairs(self: Module): bool =
|
|||
let signatures = self.controller.signCommunityRequests(self.joiningCommunityDetails.communityId, signingParams)
|
||||
for i in 0 ..< len(signingParams):
|
||||
self.joiningCommunityDetails.addressesToShare[signingParams[i].address].signature = signatures[i]
|
||||
self.view.keypairsSigningModel().setOwnershipVerified(self.joiningCommunityDetails.addressesToShare[signingParams[i].address].keyUid, true)
|
||||
return true
|
||||
|
||||
proc signRevealedAddressesForNonKeycardKeypairsAndEmitSignal(self: Module) =
|
||||
if self.signRevealedAddressesForNonKeycardKeypairs() and self.joiningCommunityDetails.allSigned():
|
||||
self.view.sendAllSharedAddressesSignedSignal()
|
||||
|
||||
proc anyProfileKeyPairAddressSelectedToBeRevealed(self: Module): bool =
|
||||
let profileKeypair = self.controller.getKeypairByKeyUid(singletonInstance.userProfile.getKeyUid())
|
||||
if profileKeypair.isNil:
|
||||
error "profile keypair not found"
|
||||
return false
|
||||
for acc in profileKeypair.accounts:
|
||||
for addrToReveal in self.joiningCommunityDetails.addressesToShare.keys:
|
||||
if cmpIgnoreCase(addrToReveal, acc.address) == 0:
|
||||
return true
|
||||
return false
|
||||
|
||||
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) =
|
||||
if password == "" and pin == "":
|
||||
info "unsuccesful authentication"
|
||||
|
@ -712,8 +728,16 @@ method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid:
|
|||
|
||||
self.joiningCommunityDetails.profilePassword = password
|
||||
self.joiningCommunityDetails.profilePin = pin
|
||||
if self.signRevealedAddressesThatBelongToRegularKeypairs():
|
||||
self.view.sendSharedAddressesForAllNonKeycardKeypairsSignedSignal()
|
||||
|
||||
# If any profile keypair address selected to be revealed and if the profile is a keycard user, we need to sign the request
|
||||
# for revealed profile addresses first, then using pubic encryption key to sign other non keycard key pairs.
|
||||
# If the profile is not a keycard user, we sign the request for it calling `signRevealedAddressesForNonKeycardKeypairs` function.
|
||||
if keyUid == singletonInstance.userProfile.getKeyUid() and
|
||||
singletonInstance.userProfile.getIsKeycardUser() and
|
||||
self.anyProfileKeyPairAddressSelectedToBeRevealed():
|
||||
self.signSharedAddressesForKeypair(keyUid, pin)
|
||||
return
|
||||
self.signRevealedAddressesForNonKeycardKeypairsAndEmitSignal()
|
||||
|
||||
method onDataSigned*(self: Module, keyUid: string, path: string, r: string, s: string, v: string, pin: string) =
|
||||
if keyUid.len == 0 or path.len == 0 or r.len == 0 or s.len == 0 or v.len == 0 or pin.len == 0:
|
||||
|
@ -727,6 +751,11 @@ method onDataSigned*(self: Module, keyUid: string, path: string, r: string, s: s
|
|||
break
|
||||
self.signSharedAddressesForKeypair(keyUid, pin)
|
||||
|
||||
# Only if the signed request is for the profile revealed addresses, we need to try to sign other revealed addresses
|
||||
# for non profile key pairs. If they are already signed or moved to keycard we skip them (handled in signRevealedAddressesForNonKeycardKeypairsAndEmitSignal)
|
||||
if keyUid == singletonInstance.userProfile.getKeyUid():
|
||||
self.signRevealedAddressesForNonKeycardKeypairsAndEmitSignal()
|
||||
|
||||
method prepareKeypairsForSigning*(self: Module, communityId, ensName: string, addresses: string,
|
||||
airdropAddress: string, editMode: bool) =
|
||||
var addressesToShare: seq[string]
|
||||
|
@ -778,7 +807,7 @@ method prepareKeypairsForSigning*(self: Module, communityId, ensName: string, ad
|
|||
)
|
||||
self.joiningCommunityDetails.addressesToShare[param.address] = details
|
||||
|
||||
method signSharedAddressesForAllNonKeycardKeypairs*(self: Module) =
|
||||
method signProfileKeypairAndAllNonKeycardKeypairs*(self: Module) =
|
||||
self.controller.authenticate()
|
||||
|
||||
# if pin is provided we're signing on a keycard silently
|
||||
|
@ -796,6 +825,8 @@ method signSharedAddressesForKeypair*(self: Module, keyUid: string, pin: string)
|
|||
self.controller.runSigningOnKeycard(keyUid, details.path, details.messageToBeSigned, pin)
|
||||
return
|
||||
self.view.keypairsSigningModel().setOwnershipVerified(keyUid, true)
|
||||
if self.joiningCommunityDetails.allSigned():
|
||||
self.view.sendAllSharedAddressesSignedSignal()
|
||||
|
||||
method joinCommunityOrEditSharedAddresses*(self: Module) =
|
||||
if not self.joiningCommunityDetails.allSigned():
|
||||
|
|
|
@ -339,8 +339,8 @@ QtObject:
|
|||
proc prepareTokenModelForCommunity(self: View, communityId: string) {.slot.} =
|
||||
self.delegate.prepareTokenModelForCommunity(communityId)
|
||||
|
||||
proc signSharedAddressesForAllNonKeycardKeypairs*(self: View) {.slot.} =
|
||||
self.delegate.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
proc signProfileKeypairAndAllNonKeycardKeypairs*(self: View) {.slot.} =
|
||||
self.delegate.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
|
||||
proc signSharedAddressesForKeypair*(self: View, keyUid: string) {.slot.} =
|
||||
self.delegate.signSharedAddressesForKeypair(keyUid, pin = "")
|
||||
|
@ -815,9 +815,9 @@ QtObject:
|
|||
self.keypairsSigningModel.setItems(items)
|
||||
self.keypairsSigningModelChanged()
|
||||
|
||||
proc sharedAddressesForAllNonKeycardKeypairsSigned(self: View) {.signal.}
|
||||
proc sendSharedAddressesForAllNonKeycardKeypairsSignedSignal*(self: View) =
|
||||
self.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
proc allSharedAddressesSigned*(self: View) {.signal.}
|
||||
proc sendAllSharedAddressesSignedSignal*(self: View) =
|
||||
self.allSharedAddressesSigned()
|
||||
|
||||
proc promoteSelfToControlNode*(self: View, communityId: string) {.slot.} =
|
||||
self.delegate.promoteSelfToControlNode(communityId)
|
||||
|
|
|
@ -120,3 +120,6 @@ method getRpcStats*(self: AccessInterface): string {.base.} =
|
|||
|
||||
method resetRpcStats*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method canProfileProveOwnershipOfProvidedAddresses*(self: AccessInterface, addresses: string): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, chronicles, sequtils, strutils, sugar
|
||||
import NimQml, json, chronicles, sequtils, strutils, sugar
|
||||
|
||||
import ./controller, ./view, ./filter
|
||||
import ./io_interface as io_interface
|
||||
|
@ -471,3 +471,21 @@ method getRpcStats*(self: Module): string =
|
|||
|
||||
method resetRpcStats*(self: Module) =
|
||||
self.view.resetRpcStats()
|
||||
|
||||
method canProfileProveOwnershipOfProvidedAddresses*(self: Module, addresses: string): bool =
|
||||
var addressesForProvingOwnership: seq[string]
|
||||
try:
|
||||
addressesForProvingOwnership = map(parseJson(addresses).getElems(), proc(x:JsonNode):string = x.getStr())
|
||||
except Exception as e:
|
||||
error "Failed to parse addresses for proving ownership: ", msg=e.msg
|
||||
return false
|
||||
|
||||
for address in addressesForProvingOwnership:
|
||||
let keypair = self.controller.getKeypairByAccountAddress(address)
|
||||
if keypair.isNil:
|
||||
return false
|
||||
if keypair.keyUid == singletonInstance.userProfile.getKeyUid():
|
||||
continue
|
||||
if keypair.migratedToKeycard():
|
||||
return false
|
||||
return true
|
|
@ -10,6 +10,7 @@ QtObject:
|
|||
canSend: bool
|
||||
|
||||
proc setup*(self: AccountItem,
|
||||
keyUid: string,
|
||||
name: string,
|
||||
address: string,
|
||||
colorId: string,
|
||||
|
@ -29,7 +30,7 @@ QtObject:
|
|||
emoji,
|
||||
walletType,
|
||||
path = "",
|
||||
keyUid = "",
|
||||
keyUid = keyUid,
|
||||
keycardAccount = false,
|
||||
position,
|
||||
operability = wa_dto.AccountFullyOperable,
|
||||
|
@ -43,6 +44,7 @@ QtObject:
|
|||
self.QObject.delete
|
||||
|
||||
proc newAccountItem*(
|
||||
keyUid: string = "",
|
||||
name: string = "",
|
||||
address: string = "",
|
||||
colorId: string = "",
|
||||
|
@ -56,7 +58,7 @@ QtObject:
|
|||
canSend: bool = true,
|
||||
): AccountItem =
|
||||
new(result, delete)
|
||||
result.setup(name, address, colorId, emoji, walletType, currencyBalance, position, areTestNetworksEnabled, prodPreferredChainIds, testPreferredChainIds, canSend)
|
||||
result.setup(keyUid, name, address, colorId, emoji, walletType, currencyBalance, position, areTestNetworksEnabled, prodPreferredChainIds, testPreferredChainIds, canSend)
|
||||
|
||||
proc `$`*(self: AccountItem): string =
|
||||
result = "WalletSection-Send-Item("
|
||||
|
|
|
@ -5,13 +5,14 @@ import ../../../shared_models/currency_amount
|
|||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
||||
Name = UserRole + 1,
|
||||
Address,
|
||||
ColorId,
|
||||
WalletType,
|
||||
Emoji,
|
||||
CurrencyBalance,
|
||||
Position,
|
||||
KeyUid = UserRole + 1
|
||||
Name
|
||||
Address
|
||||
ColorId
|
||||
WalletType
|
||||
Emoji
|
||||
CurrencyBalance
|
||||
Position
|
||||
PreferredSharingChainIds
|
||||
|
||||
QtObject:
|
||||
|
@ -48,6 +49,7 @@ QtObject:
|
|||
|
||||
method roleNames(self: AccountsModel): Table[int, string] =
|
||||
{
|
||||
ModelRole.KeyUid.int: "keyUid",
|
||||
ModelRole.Name.int:"name",
|
||||
ModelRole.Address.int:"address",
|
||||
ModelRole.ColorId.int:"colorId",
|
||||
|
@ -75,6 +77,8 @@ QtObject:
|
|||
let enumRole = role.ModelRole
|
||||
|
||||
case enumRole:
|
||||
of ModelRole.KeyUid:
|
||||
result = newQVariant(item.keyUid())
|
||||
of ModelRole.Name:
|
||||
result = newQVariant(item.name())
|
||||
of ModelRole.Address:
|
||||
|
|
|
@ -7,7 +7,7 @@ import ./io_interface
|
|||
import ../../shared_models/currency_amount
|
||||
import ./wallet_connect/controller as wcc
|
||||
|
||||
type
|
||||
type
|
||||
ActivityControllerArray* = array[2, activityc.Controller]
|
||||
|
||||
QtObject:
|
||||
|
@ -36,11 +36,11 @@ QtObject:
|
|||
proc delete*(self: View) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface,
|
||||
activityController: activityc.Controller,
|
||||
tmpActivityControllers: ActivityControllerArray,
|
||||
activityDetailsController: activity_detailsc.Controller,
|
||||
collectibleDetailsController: collectible_detailsc.Controller,
|
||||
proc newView*(delegate: io_interface.AccessInterface,
|
||||
activityController: activityc.Controller,
|
||||
tmpActivityControllers: ActivityControllerArray,
|
||||
activityDetailsController: activity_detailsc.Controller,
|
||||
collectibleDetailsController: collectible_detailsc.Controller,
|
||||
wcController: wcc.Controller): View =
|
||||
new(result, delete)
|
||||
result.delegate = delegate
|
||||
|
@ -259,3 +259,6 @@ QtObject:
|
|||
return self.delegate.getRpcStats()
|
||||
proc resetRpcStats*(self: View) {.slot.} =
|
||||
self.delegate.resetRpcStats()
|
||||
|
||||
proc canProfileProveOwnershipOfProvidedAddresses*(self: View, addresses: string): bool {.slot.} =
|
||||
return self.delegate.canProfileProveOwnershipOfProvidedAddresses(addresses)
|
|
@ -60,6 +60,7 @@ proc walletAccountToWalletAccountsItem*(w: WalletAccountDto, keycardAccount: boo
|
|||
proc walletAccountToWalletSendAccountItem*(w: WalletAccountDto, chainIds: seq[int], enabledChainIds: seq[int],
|
||||
currencyBalance: float64, currencyFormat: CurrencyFormatDto, areTestNetworksEnabled: bool): wallet_send_account_item.AccountItem =
|
||||
return wallet_send_account_item.newAccountItem(
|
||||
w.keyUid,
|
||||
w.name,
|
||||
w.address,
|
||||
w.colorId,
|
||||
|
|
|
@ -122,9 +122,9 @@ StackLayout {
|
|||
Global.openPopup(communityIntroDialogPopup, {
|
||||
communityId: joinCommunityView.communityId,
|
||||
isInvitationPending: joinCommunityView.isInvitationPending,
|
||||
name: communityData.name,
|
||||
communityName: communityData.name,
|
||||
introMessage: communityData.introMessage,
|
||||
imageSrc: communityData.image,
|
||||
communityIcon: communityData.image,
|
||||
accessType: communityData.access
|
||||
})
|
||||
}
|
||||
|
@ -190,9 +190,9 @@ StackLayout {
|
|||
Global.openPopup(communityIntroDialogPopup, {
|
||||
communityId: chatView.communityId,
|
||||
isInvitationPending: root.rootStore.isMyCommunityRequestPending(chatView.communityId),
|
||||
name: root.sectionItemModel.name,
|
||||
communityName: root.sectionItemModel.name,
|
||||
introMessage: root.sectionItemModel.introMessage,
|
||||
imageSrc: root.sectionItemModel.image,
|
||||
communityIcon: root.sectionItemModel.image,
|
||||
accessType: root.sectionItemModel.access
|
||||
})
|
||||
}
|
||||
|
@ -205,8 +205,8 @@ StackLayout {
|
|||
|
||||
Loader {
|
||||
id: communitySettingsLoader
|
||||
active: root.rootStore.chatCommunitySectionModule.isCommunity() &&
|
||||
root.isPrivilegedUser &&
|
||||
active: root.rootStore.chatCommunitySectionModule.isCommunity() &&
|
||||
root.isPrivilegedUser &&
|
||||
(root.currentIndex === 1 || !!communitySettingsLoader.item) // lazy load and preserve state after loading
|
||||
asynchronous: false // It's false on purpose. We want to load the component synchronously
|
||||
sourceComponent: CommunitySettingsView {
|
||||
|
@ -272,8 +272,9 @@ StackLayout {
|
|||
|
||||
property string communityId
|
||||
|
||||
loginType: root.rootStore.loginType
|
||||
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
|
||||
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
|
||||
|
||||
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
|
||||
requirementsCheckPending: root.rootStore.requirementsCheckPending
|
||||
permissionsModel: {
|
||||
|
@ -293,8 +294,8 @@ StackLayout {
|
|||
communityIntroDialog.keypairSigningModel = root.rootStore.communitiesModuleInst.keypairsSigningModel
|
||||
}
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.rootStore.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.rootStore.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -321,9 +322,21 @@ StackLayout {
|
|||
Connections {
|
||||
target: root.rootStore.communitiesModuleInst
|
||||
|
||||
function onSharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
function onAllSharedAddressesSigned() {
|
||||
if (communityIntroDialog.profileProvesOwnershipOfSelectedAddresses) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (communityIntroDialog.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!!communityIntroDialog.replaceItem) {
|
||||
communityIntroDialog.replaceLoader.item.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
communityIntroDialog.replaceLoader.item.allSigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -403,8 +403,8 @@ QtObject {
|
|||
communitiesModuleInst.prepareKeypairsForSigning(communityId, ensName, JSON.stringify(addressesToShare), airdropAddress, editMode)
|
||||
}
|
||||
|
||||
function signSharedAddressesForAllNonKeycardKeypairs() {
|
||||
communitiesModuleInst.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
function signProfileKeypairAndAllNonKeycardKeypairs() {
|
||||
communitiesModuleInst.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
function signSharedAddressesForKeypair(keyUid) {
|
||||
|
|
|
@ -15,17 +15,16 @@ import utils 1.0
|
|||
StatusListView {
|
||||
id: root
|
||||
|
||||
required property var selectedSharedAddressesMap // Map[address, [selected, isAirdrop]]
|
||||
|
||||
property var walletAssetsModel
|
||||
property bool hasPermissions
|
||||
property var uniquePermissionTokenKeys
|
||||
|
||||
// read/write properties
|
||||
property string selectedAirdropAddress
|
||||
property var selectedSharedAddresses: []
|
||||
|
||||
property var getCurrencyAmount: function (balance, symbol){}
|
||||
|
||||
signal addressesChanged()
|
||||
signal toggleAddressSelection(string keyUid, string address)
|
||||
signal airdropAddressSelected (string address)
|
||||
|
||||
leftMargin: d.absLeftMargin
|
||||
topMargin: Style.current.padding
|
||||
|
@ -35,6 +34,8 @@ StatusListView {
|
|||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int selectedSharedAddressesCount: root.selectedSharedAddressesMap.size
|
||||
|
||||
// UI
|
||||
readonly property int absLeftMargin: 12
|
||||
|
||||
|
@ -46,10 +47,6 @@ StatusListView {
|
|||
exclusive: false
|
||||
}
|
||||
|
||||
function selectFirstAvailableAirdropAddress() {
|
||||
root.selectedAirdropAddress = ModelUtils.modelToFlatArray(root.model, "address").find(address => selectedSharedAddresses.includes(address))
|
||||
}
|
||||
|
||||
function getTotalBalance(balances, decimals, symbol) {
|
||||
let totalBalance = 0
|
||||
for(let i=0; i<balances.count; i++) {
|
||||
|
@ -143,12 +140,20 @@ StatusListView {
|
|||
icon.color: hovered ? Theme.palette.primaryColor3 :
|
||||
checked ? Theme.palette.primaryColor1 : disabledTextColor
|
||||
checkable: true
|
||||
checked: listItem.address === root.selectedAirdropAddress.toLowerCase()
|
||||
enabled: shareAddressCheckbox.checked && root.selectedSharedAddresses.length > 1 // last cannot be unchecked
|
||||
checked: {
|
||||
let obj = root.selectedSharedAddressesMap.get(listItem.address)
|
||||
if (!!obj) {
|
||||
return obj.isAirdrop
|
||||
}
|
||||
return false
|
||||
}
|
||||
enabled: shareAddressCheckbox.checked && d.selectedSharedAddressesCount > 1 // last cannot be unchecked
|
||||
visible: shareAddressCheckbox.checked
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onCheckedChanged: if (checked) root.selectedAirdropAddress = listItem.address
|
||||
onToggled: root.addressesChanged()
|
||||
|
||||
onToggled: {
|
||||
root.airdropAddressSelected(listItem.address)
|
||||
}
|
||||
|
||||
StatusToolTip {
|
||||
text: qsTr("Use this address for any Community airdrops")
|
||||
|
@ -161,25 +166,11 @@ StatusListView {
|
|||
ButtonGroup.group: d.addressesGroup
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checkable: true
|
||||
checked: root.selectedSharedAddresses.some((address) => address.toLowerCase() === listItem.address )
|
||||
enabled: !(root.selectedSharedAddresses.length === 1 && checked) // last cannot be unchecked
|
||||
checked: root.selectedSharedAddressesMap.has(listItem.address)
|
||||
enabled: !(d.selectedSharedAddressesCount === 1 && checked) // last cannot be unchecked
|
||||
|
||||
onToggled: {
|
||||
// handle selected addresses
|
||||
const index = root.selectedSharedAddresses.findIndex((address) => address.toLowerCase() === listItem.address)
|
||||
const selectedSharedAddressesCopy = Object.assign([], root.selectedSharedAddresses) // deep copy
|
||||
if (index === -1) {
|
||||
selectedSharedAddressesCopy.push(listItem.address)
|
||||
} else {
|
||||
selectedSharedAddressesCopy.splice(index, 1)
|
||||
}
|
||||
root.selectedSharedAddresses = selectedSharedAddressesCopy
|
||||
|
||||
// switch to next available airdrop address when unchecking
|
||||
if (!checked && listItem.address === root.selectedAirdropAddress.toLowerCase()) {
|
||||
d.selectFirstAvailableAirdropAddress()
|
||||
}
|
||||
|
||||
root.addressesChanged()
|
||||
root.toggleAddressSelection(model.keyUid, listItem.address)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -24,13 +24,18 @@ import shared.panels 1.0
|
|||
Control {
|
||||
id: root
|
||||
|
||||
property bool isEditMode
|
||||
required property string componentUid
|
||||
required property bool isEditMode
|
||||
required property var selectedSharedAddressesMap // Map[address, [keyUid, selected, isAirdrop]
|
||||
property var currentSharedAddressesMap // Map[address, [keyUid, selected, isAirdrop]
|
||||
required property int totalNumOfAddressesForSharing
|
||||
required property bool profileProvesOwnershipOfSelectedAddresses
|
||||
required property bool allAddressesToRevealBelongToSingleNonProfileKeypair
|
||||
|
||||
property bool requirementsCheckPending: false
|
||||
|
||||
required property string communityName
|
||||
required property string communityIcon
|
||||
property int loginType: Constants.LoginType.Password
|
||||
|
||||
required property var walletAssetsModel
|
||||
required property var walletAccountsModel // name, address, emoji, colorId, assets
|
||||
|
@ -38,52 +43,16 @@ Control {
|
|||
required property var assetsModel
|
||||
required property var collectiblesModel
|
||||
|
||||
readonly property string title: isEditMode ? qsTr("Edit which addresses you share with %1").arg(communityName)
|
||||
: qsTr("Select addresses to share with %1").arg(communityName)
|
||||
readonly property string title: isEditMode ? qsTr("Edit which addresses you share with %1").arg(root.communityName)
|
||||
: qsTr("Select addresses to share with %1").arg(root.communityName)
|
||||
|
||||
readonly property var buttons: ObjectModel {
|
||||
StatusFlatButton {
|
||||
visible: root.isEditMode
|
||||
borderColor: Theme.palette.baseColor2
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.close()
|
||||
}
|
||||
StatusButton {
|
||||
enabled: d.dirty
|
||||
type: d.lostCommunityPermission || d.lostChannelPermissions ? StatusBaseButton.Type.Danger : StatusBaseButton.Type.Normal
|
||||
visible: root.isEditMode
|
||||
icon.name: type === StatusBaseButton.Type.Normal && d.selectedAddressesDirty?
|
||||
!root.isEditMode? Constants.authenticationIconByType[root.loginType] : ""
|
||||
: ""
|
||||
text: d.lostCommunityPermission ? qsTr("Save changes & leave %1").arg(root.communityName) :
|
||||
d.lostChannelPermissions ? qsTr("Save changes & update my permissions")
|
||||
: qsTr("Prove ownership")
|
||||
onClicked: {
|
||||
root.prepareForSigning(root.selectedAirdropAddress, root.selectedSharedAddresses)
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
visible: !root.isEditMode
|
||||
text: qsTr("Share selected addresses to join")
|
||||
onClicked: {
|
||||
root.shareSelectedAddressesClicked(root.selectedAirdropAddress, root.selectedSharedAddresses)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
// NB no more buttons after this, see property `rightButtons` below
|
||||
}
|
||||
|
||||
readonly property var rightButtons: [buttons.get(buttons.count-1)] // "magically" used by CommunityIntroDialog StatusStackModal impl
|
||||
|
||||
property var selectedSharedAddresses: []
|
||||
property string selectedAirdropAddress
|
||||
readonly property var rightButtons: root.isEditMode? [d.cancelButton, d.saveButton] : [d.shareAddressesButton]
|
||||
|
||||
property var getCurrencyAmount: function (balance, symbol){}
|
||||
|
||||
signal sharedAddressesChanged(string airdropAddress, var sharedAddresses)
|
||||
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
signal prepareForSigning(string airdropAddress, var sharedAddresses)
|
||||
|
||||
signal toggleAddressSelection(string keyUid, string address)
|
||||
signal airdropAddressSelected (string address)
|
||||
signal shareSelectedAddressesClicked()
|
||||
signal close()
|
||||
|
||||
padding: 0
|
||||
|
@ -94,69 +63,108 @@ Control {
|
|||
|
||||
// internal logic
|
||||
readonly property bool hasPermissions: root.permissionsModel && root.permissionsModel.count
|
||||
readonly property int selectedSharedAddressesCount: root.selectedSharedAddressesMap.size
|
||||
|
||||
// initial state (not bindings, we want a static snapshot of the initial state)
|
||||
property var initialSelectedSharedAddresses: []
|
||||
property string initialSelectedAirdropAddress
|
||||
|
||||
// dirty state handling
|
||||
readonly property bool selectedAddressesDirty: !SQInternal.ModelUtils.isSameArray(d.initialSelectedSharedAddresses, root.selectedSharedAddresses)
|
||||
readonly property bool selectedAirdropAddressDirty: root.selectedAirdropAddress !== d.initialSelectedAirdropAddress
|
||||
readonly property bool dirty: selectedAddressesDirty || selectedAirdropAddressDirty
|
||||
readonly property bool dirty: {
|
||||
if (root.currentSharedAddressesMap.size !== root.selectedSharedAddressesMap.size) {
|
||||
return true
|
||||
}
|
||||
for (const [key, value] of root.currentSharedAddressesMap) {
|
||||
const obj = root.selectedSharedAddressesMap.get(key)
|
||||
if (!obj || value.selected !== obj.selected || value.isAirdrop !== obj.isAirdrop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// warning states
|
||||
readonly property bool lostCommunityPermission: root.isEditMode && permissionsView.lostPermissionToJoin
|
||||
readonly property bool lostChannelPermissions: root.isEditMode && permissionsView.lostChannelPermissions
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// initialize the state
|
||||
d.initialSelectedSharedAddresses = root.selectedSharedAddresses.length ? root.selectedSharedAddresses
|
||||
: filteredAccountsModel.count ? ModelUtils.modelToFlatArray(filteredAccountsModel, "address")
|
||||
: []
|
||||
d.initialSelectedAirdropAddress = !!root.selectedAirdropAddress ? root.selectedAirdropAddress
|
||||
: d.initialSelectedSharedAddresses.length ? d.initialSelectedSharedAddresses[0] : ""
|
||||
root.selectedSharedAddresses = accountSelector.selectedSharedAddresses
|
||||
root.selectedAirdropAddress = accountSelector.selectedAirdropAddress
|
||||
}
|
||||
|
||||
function setOldSharedAddresses(oldSharedAddresses) {
|
||||
d.initialSelectedSharedAddresses = oldSharedAddresses
|
||||
accountSelector.selectedSharedAddresses = Qt.binding(() => d.initialSelectedSharedAddresses)
|
||||
accountSelector.applyChange()
|
||||
}
|
||||
|
||||
function setOldAirdropAddress(oldAirdropAddress) {
|
||||
d.initialSelectedAirdropAddress = oldAirdropAddress
|
||||
accountSelector.selectedAirdropAddress = Qt.binding(() => d.initialSelectedAirdropAddress)
|
||||
accountSelector.applyChange()
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: filteredAccountsModel
|
||||
sourceModel: root.walletAccountsModel
|
||||
filters: ValueFilter {
|
||||
roleName: "walletType"
|
||||
value: Constants.watchWalletType
|
||||
inverted: true
|
||||
readonly property var cancelButton: StatusFlatButton {
|
||||
visible: root.isEditMode
|
||||
borderColor: Theme.palette.baseColor2
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.close()
|
||||
}
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
function isGenerated(modelData) {
|
||||
return modelData.walletType === Constants.generatedWalletType
|
||||
|
||||
readonly property var saveButton: StatusButton {
|
||||
enabled: d.dirty
|
||||
type: d.lostCommunityPermission || d.lostChannelPermissions ? StatusBaseButton.Type.Danger : StatusBaseButton.Type.Normal
|
||||
visible: root.isEditMode
|
||||
|
||||
text: {
|
||||
if (d.lostCommunityPermission) {
|
||||
return qsTr("Save changes & leave %1").arg(root.communityName)
|
||||
}
|
||||
if (d.lostChannelPermissions) {
|
||||
return qsTr("Save changes & update my permissions")
|
||||
}
|
||||
if (d.selectedSharedAddressesCount === root.totalNumOfAddressesForSharing) {
|
||||
return qsTr("Reveal all addresses")
|
||||
}
|
||||
return qsTr("Reveal %n address(s)", "", d.selectedSharedAddressesCount)
|
||||
}
|
||||
|
||||
icon.name: {
|
||||
if (!d.lostCommunityPermission
|
||||
&& !d.lostChannelPermissions
|
||||
&& root.profileProvesOwnershipOfSelectedAddresses) {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
expression: {
|
||||
return isGenerated(modelLeft)
|
||||
}
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "position"
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "name"
|
||||
return ""
|
||||
}
|
||||
]
|
||||
|
||||
onClicked: {
|
||||
root.shareSelectedAddressesClicked()
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var shareAddressesButton: StatusButton {
|
||||
visible: !root.isEditMode
|
||||
text: {
|
||||
if (d.selectedSharedAddressesCount === root.totalNumOfAddressesForSharing) {
|
||||
return qsTr("Share all addresses to join")
|
||||
}
|
||||
return qsTr("Share %n address(s) to join", "", d.selectedSharedAddressesCount)
|
||||
}
|
||||
|
||||
icon.name: {
|
||||
if (root.profileProvesOwnershipOfSelectedAddresses) {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.shareSelectedAddressesClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
|
@ -184,14 +192,16 @@ Control {
|
|||
Layout.fillHeight: !hasPermissions
|
||||
model: root.walletAccountsModel
|
||||
walletAssetsModel: root.walletAssetsModel
|
||||
selectedSharedAddresses: d.initialSelectedSharedAddresses
|
||||
selectedAirdropAddress: d.initialSelectedAirdropAddress
|
||||
onAddressesChanged: accountSelector.applyChange()
|
||||
function applyChange() {
|
||||
root.selectedSharedAddresses = selectedSharedAddresses
|
||||
root.selectedAirdropAddress = selectedAirdropAddress
|
||||
root.sharedAddressesChanged(selectedAirdropAddress, selectedSharedAddresses)
|
||||
selectedSharedAddressesMap: root.selectedSharedAddressesMap
|
||||
|
||||
onToggleAddressSelection: {
|
||||
root.toggleAddressSelection(keyUid, address)
|
||||
}
|
||||
|
||||
onAirdropAddressSelected: {
|
||||
root.airdropAddressSelected(address)
|
||||
}
|
||||
|
||||
getCurrencyAmount: function (balance, symbol){
|
||||
return root.getCurrencyAmount(balance, symbol)
|
||||
}
|
||||
|
|
|
@ -13,46 +13,67 @@ import SortFilterProxyModel 0.2
|
|||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property string componentUid
|
||||
required property bool isEditMode
|
||||
property var keypairSigningModel
|
||||
|
||||
readonly property string title: qsTr("Prove ownership of keypairs")
|
||||
required property var selectedSharedAddressesMap // Map[address, [keyUid, selected, isAirdrop]
|
||||
required property int totalNumOfAddressesForSharing
|
||||
|
||||
required property string communityName
|
||||
readonly property string title: root.isEditMode?
|
||||
qsTr("Save addresses you share with %1").arg(root.communityName)
|
||||
: qsTr("Request to join %1").arg(root.communityName)
|
||||
readonly property var rightButtons: [d.rightBtn]
|
||||
readonly property bool allSigned: regularKeypairs.visible == d.sharedAddressesForAllNonKeycardKeypairsSigned &&
|
||||
keycardKeypairs.visible == d.allKeycardKeypairsSigned
|
||||
|
||||
signal joinCommunity()
|
||||
signal signSharedAddressesForAllNonKeycardKeypairs()
|
||||
signal signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
signal signSharedAddressesForKeypair(string keyUid)
|
||||
|
||||
function sharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
d.sharedAddressesForAllNonKeycardKeypairsSigned = true
|
||||
function allSigned() {
|
||||
d.allSigned = true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property bool sharedAddressesForAllNonKeycardKeypairsSigned: false
|
||||
property bool allKeycardKeypairsSigned: false
|
||||
readonly property int selectedSharedAddressesCount: root.selectedSharedAddressesMap.size
|
||||
|
||||
property bool allSigned: false
|
||||
|
||||
readonly property bool anyOfSelectedAddressesToRevealBelongToProfileKeypair: {
|
||||
for (const [key, value] of root.selectedSharedAddressesMap) {
|
||||
if (value.keyUid === userProfile.keyUid) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
readonly property bool thereAreMoreThanOneNonProfileRegularKeypairs: nonProfileRegularKeypairs.count > 1
|
||||
|
||||
readonly property bool allNonProfileRegularKeypairsSigned: {
|
||||
for (let i = 0; i < nonProfileRegularKeypairs.model.count; ++i) {
|
||||
const item = nonProfileRegularKeypairs.model.get(i)
|
||||
if (!!item && !item.keyPair.ownershipVerified) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
readonly property var rightBtn: StatusButton {
|
||||
enabled: root.allSigned
|
||||
text: qsTr("Share your addresses to join")
|
||||
enabled: d.allSigned
|
||||
text: {
|
||||
if (d.selectedSharedAddressesCount === root.totalNumOfAddressesForSharing) {
|
||||
return qsTr("Share all addresses to join")
|
||||
}
|
||||
return qsTr("Share %n address(s) to join", "", d.selectedSharedAddressesCount)
|
||||
}
|
||||
onClicked: {
|
||||
root.joinCommunity()
|
||||
}
|
||||
}
|
||||
|
||||
function reEvaluateSignedKeypairs() {
|
||||
let allKeypairsSigned = true
|
||||
for(var i = 0; i< keycardKeypairs.model.count; i++) {
|
||||
if(!keycardKeypairs.model.get(i).keyPair.ownershipVerified) {
|
||||
allKeypairsSigned = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
d.allKeycardKeypairsSigned = allKeypairsSigned
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -61,43 +82,41 @@ ColumnLayout {
|
|||
|
||||
spacing: Style.current.padding
|
||||
|
||||
StatusBaseText {
|
||||
Layout.preferredWidth: parent.width
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Constants.keycard.general.fontSize2
|
||||
text: qsTr("To share %n address(s) with <b>%1</b>, authenticate the associated keypairs...", "", d.selectedSharedAddressesCount).arg(root.communityName)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: regularKeypairs.visible
|
||||
visible: nonKeycardProfileKeypair.visible
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Keypairs we need an authentication for")
|
||||
text: qsTr("Stored on device")
|
||||
font.pixelSize: Constants.keycard.general.fontSize2
|
||||
color: Theme.palette.baseColor1
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: d.sharedAddressesForAllNonKeycardKeypairsSigned? qsTr("Authenticated") : qsTr("Authenticate")
|
||||
enabled: !d.sharedAddressesForAllNonKeycardKeypairsSigned
|
||||
icon.name: userProfile.usingBiometricLogin? "touch-id" : "password"
|
||||
|
||||
onClicked: {
|
||||
root.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
id: regularKeypairs
|
||||
id: nonKeycardProfileKeypair
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: regularKeypairs.contentHeight
|
||||
visible: regularKeypairs.model.count > 0
|
||||
Layout.preferredHeight: nonKeycardProfileKeypair.contentHeight
|
||||
visible: nonKeycardProfileKeypair.model.count > 0
|
||||
spacing: Style.current.padding
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: root.keypairSigningModel
|
||||
filters: ExpressionFilter {
|
||||
expression: !model.keyPair.migratedToKeycard
|
||||
expression: model.keyPair.keyUid === userProfile.keyUid && !userProfile.isKeycardUser
|
||||
}
|
||||
}
|
||||
delegate: KeyPairItem {
|
||||
id: kpOnDeviceDelegate
|
||||
width: ListView.view.width
|
||||
sensor.hoverEnabled: false
|
||||
additionalInfoForProfileKeypair: ""
|
||||
|
@ -109,22 +128,80 @@ ColumnLayout {
|
|||
keyPairImage: model.keyPair.image
|
||||
keyPairDerivedFrom: model.keyPair.derivedFrom
|
||||
keyPairAccounts: model.keyPair.accounts
|
||||
|
||||
components: [
|
||||
StatusButton {
|
||||
text: qsTr("Authenticate")
|
||||
visible: !model.keyPair.ownershipVerified
|
||||
icon.name: {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
},
|
||||
StatusButton {
|
||||
text: qsTr("Authenticated")
|
||||
visible: model.keyPair.ownershipVerified
|
||||
enabled: false
|
||||
normalColor: "transparent"
|
||||
disabledColor: "transparent"
|
||||
disabledTextColor: Theme.palette.successColor1
|
||||
icon.name: "checkmark-circle"
|
||||
}
|
||||
]
|
||||
|
||||
SequentialAnimation {
|
||||
running: model.keyPair.ownershipVerified
|
||||
PropertyAnimation {
|
||||
target: kpOnDeviceDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.successColor3
|
||||
duration: 500
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: kpOnDeviceDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.baseColor2
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: regularKeypairs.visible && keycardKeypairs.visible
|
||||
visible: nonKeycardProfileKeypair.visible
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.current.xlPadding
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: keycardKeypairs.visible
|
||||
text: qsTr("Keypairs that need to be singed using appropriate Keycard")
|
||||
font.pixelSize: Constants.keycard.general.fontSize2
|
||||
color: Theme.palette.baseColor1
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("Stored on keycard")
|
||||
font.pixelSize: Constants.keycard.general.fontSize2
|
||||
color: Theme.palette.baseColor1
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
color: Theme.palette.baseColor1
|
||||
icon: "keycard"
|
||||
}
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
|
@ -140,6 +217,7 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
delegate: KeyPairItem {
|
||||
id: kpOnKeycardDelegate
|
||||
width: ListView.view.width
|
||||
sensor.hoverEnabled: !model.keyPair.ownershipVerified
|
||||
additionalInfoForProfileKeypair: ""
|
||||
|
@ -153,28 +231,173 @@ ColumnLayout {
|
|||
keyPairAccounts: model.keyPair.accounts
|
||||
|
||||
components: [
|
||||
StatusBaseText {
|
||||
font.weight: Font.Medium
|
||||
font.underline: mouseArea.containsMouse
|
||||
font.pixelSize: Theme.primaryTextFontSize
|
||||
color: model.keyPair.ownershipVerified? Theme.palette.baseColor1 : Theme.palette.primaryColor1
|
||||
text: model.keyPair.ownershipVerified? qsTr("Signed") : qsTr("Sign")
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: !model.keyPair.ownershipVerified
|
||||
enabled: !model.keyPair.ownershipVerified
|
||||
onEnabledChanged: {
|
||||
d.reEvaluateSignedKeypairs()
|
||||
}
|
||||
onClicked: {
|
||||
root.signSharedAddressesForKeypair(model.keyPair.keyUid)
|
||||
StatusButton {
|
||||
text: qsTr("Authenticate")
|
||||
visible: !model.keyPair.ownershipVerified
|
||||
icon.name: "keycard"
|
||||
|
||||
onClicked: {
|
||||
if (model.keyPair.keyUid === userProfile.keyUid) {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
return
|
||||
}
|
||||
root.signSharedAddressesForKeypair(model.keyPair.keyUid)
|
||||
}
|
||||
},
|
||||
StatusButton {
|
||||
text: qsTr("Authenticated")
|
||||
visible: model.keyPair.ownershipVerified
|
||||
enabled: false
|
||||
normalColor: "transparent"
|
||||
disabledColor: "transparent"
|
||||
disabledTextColor: Theme.palette.successColor1
|
||||
icon.name: "checkmark-circle"
|
||||
}
|
||||
]
|
||||
|
||||
SequentialAnimation {
|
||||
running: model.keyPair.ownershipVerified
|
||||
PropertyAnimation {
|
||||
target: kpOnKeycardDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.successColor3
|
||||
duration: 500
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: kpOnKeycardDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.baseColor2
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: keycardKeypairs.visible
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.current.xlPadding
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
visible: nonProfileRegularKeypairs.visible
|
||||
|
||||
StatusBaseText {
|
||||
Layout.preferredWidth: !d.anyOfSelectedAddressesToRevealBelongToProfileKeypair &&
|
||||
d.thereAreMoreThanOneNonProfileRegularKeypairs?
|
||||
370
|
||||
: -1
|
||||
Layout.fillWidth: true
|
||||
text: !d.anyOfSelectedAddressesToRevealBelongToProfileKeypair &&
|
||||
d.thereAreMoreThanOneNonProfileRegularKeypairs?
|
||||
qsTr("Authenticate via “%1” keypair").arg(userProfile.name)
|
||||
: qsTr("The following keypairs will be authenticated via “%1” keypair").arg(userProfile.name)
|
||||
font.pixelSize: Constants.keycard.general.fontSize2
|
||||
color: Theme.palette.baseColor1
|
||||
wrapMode: Text.WrapAnywhere
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("Authenticate")
|
||||
visible: !d.anyOfSelectedAddressesToRevealBelongToProfileKeypair
|
||||
&& d.thereAreMoreThanOneNonProfileRegularKeypairs
|
||||
&& !d.allNonProfileRegularKeypairsSigned
|
||||
icon.name: {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
id: nonProfileRegularKeypairs
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: nonProfileRegularKeypairs.contentHeight
|
||||
visible: nonProfileRegularKeypairs.model.count > 0
|
||||
spacing: Style.current.padding
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: root.keypairSigningModel
|
||||
filters: ExpressionFilter {
|
||||
expression: !model.keyPair.migratedToKeycard && model.keyPair.keyUid !== userProfile.keyUid
|
||||
}
|
||||
}
|
||||
delegate: KeyPairItem {
|
||||
id: dependantKpOnDeviceDelegate
|
||||
width: ListView.view.width
|
||||
sensor.hoverEnabled: false
|
||||
additionalInfoForProfileKeypair: ""
|
||||
|
||||
keyPairType: model.keyPair.pairType
|
||||
keyPairKeyUid: model.keyPair.keyUid
|
||||
keyPairName: model.keyPair.name
|
||||
keyPairIcon: model.keyPair.icon
|
||||
keyPairImage: model.keyPair.image
|
||||
keyPairDerivedFrom: model.keyPair.derivedFrom
|
||||
keyPairAccounts: model.keyPair.accounts
|
||||
|
||||
components: [
|
||||
StatusButton {
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("Authenticate")
|
||||
visible: !d.anyOfSelectedAddressesToRevealBelongToProfileKeypair
|
||||
&& !d.thereAreMoreThanOneNonProfileRegularKeypairs
|
||||
&& !model.keyPair.ownershipVerified
|
||||
icon.name: {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
},
|
||||
StatusButton {
|
||||
text: qsTr("Authenticated")
|
||||
visible: model.keyPair.ownershipVerified
|
||||
enabled: false
|
||||
normalColor: "transparent"
|
||||
disabledColor: "transparent"
|
||||
disabledTextColor: Theme.palette.successColor1
|
||||
icon.name: "checkmark-circle"
|
||||
}
|
||||
]
|
||||
|
||||
SequentialAnimation {
|
||||
running: model.keyPair.ownershipVerified
|
||||
PropertyAnimation {
|
||||
target: dependantKpOnDeviceDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.successColor3
|
||||
duration: 500
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: dependantKpOnDeviceDelegate
|
||||
property: "color"
|
||||
to: Theme.palette.baseColor2
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ import AppLayouts.Communities.panels 1.0
|
|||
|
||||
import utils 1.0
|
||||
|
||||
/****************************************************
|
||||
This file is not in use any more.
|
||||
|
||||
TODO: remove it
|
||||
****************************************************/
|
||||
|
||||
StatusDialog {
|
||||
id: root
|
||||
|
||||
|
@ -35,7 +41,7 @@ StatusDialog {
|
|||
|
||||
signal prepareForSigning(string airdropAddress, var sharedAddresses)
|
||||
signal editRevealedAddresses()
|
||||
signal signSharedAddressesForAllNonKeycardKeypairs()
|
||||
signal signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
signal signSharedAddressesForKeypair(string keyUid)
|
||||
|
||||
function setOldSharedAddresses(oldSharedAddresses) {
|
||||
|
@ -71,6 +77,8 @@ StatusDialog {
|
|||
property var oldSharedAddresses
|
||||
property string oldAirdropAddress
|
||||
|
||||
property int selectedSharedAddressesCount
|
||||
|
||||
property var selectAddressesPanelButtons: ObjectModel {}
|
||||
readonly property var signingPanelButtons: ObjectModel {
|
||||
StatusFlatButton {
|
||||
|
@ -136,6 +144,7 @@ StatusDialog {
|
|||
d.displaySigningPanel = true
|
||||
}
|
||||
onSharedAddressesChanged: {
|
||||
d.selectedSharedAddressesCount = sharedAddresses.length
|
||||
root.sharedAddressesChanged(airdropAddress, sharedAddresses)
|
||||
}
|
||||
onClose: root.close()
|
||||
|
@ -147,12 +156,16 @@ StatusDialog {
|
|||
|
||||
Component {
|
||||
id: sharedAddressesSigningPanelComponent
|
||||
SharedAddressesSigningPanel {
|
||||
SharedAddressesSigningPanel {
|
||||
|
||||
totalNumOfAddressesForSharing: root.walletAccountsModel.count
|
||||
numOfSelectedAddressesForSharing: d.selectedSharedAddressesCount
|
||||
|
||||
communityName: root.communityName
|
||||
keypairSigningModel: root.keypairSigningModel
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
|
|
@ -305,7 +305,7 @@ Item {
|
|||
|
||||
chatListPopupMenu: ChatContextMenuView {
|
||||
id: chatContextMenuView
|
||||
showDebugOptions: root.store.isDebugEnabledfir
|
||||
showDebugOptions: root.store.isDebugEnabledfir
|
||||
|
||||
// TODO pass the chatModel in its entirety instead of fetching the JSOn using just the id
|
||||
openHandler: function (id) {
|
||||
|
@ -536,12 +536,14 @@ Item {
|
|||
|
||||
isInvitationPending: d.invitationPending
|
||||
requirementsCheckPending: root.store.requirementsCheckPending
|
||||
name: communityData.name
|
||||
communityName: communityData.name
|
||||
introMessage: communityData.introMessage
|
||||
imageSrc: communityData.image
|
||||
communityIcon: communityData.image
|
||||
accessType: communityData.access
|
||||
loginType: root.store.loginType
|
||||
|
||||
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
|
||||
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
|
||||
|
||||
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
|
||||
permissionsModel: {
|
||||
root.store.prepareTokenModelForCommunity(communityData.id)
|
||||
|
@ -560,8 +562,8 @@ Item {
|
|||
communityIntroDialog.keypairSigningModel = root.store.communitiesModuleInst.keypairsSigningModel
|
||||
}
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.store.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.store.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -589,9 +591,21 @@ Item {
|
|||
Connections {
|
||||
target: root.store.communitiesModuleInst
|
||||
|
||||
function onSharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
function onAllSharedAddressesSigned() {
|
||||
if (communityIntroDialog.profileProvesOwnershipOfSelectedAddresses) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (communityIntroDialog.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!!communityIntroDialog.replaceItem) {
|
||||
communityIntroDialog.replaceLoader.item.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
communityIntroDialog.replaceLoader.item.allSigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -628,7 +642,7 @@ Item {
|
|||
|
||||
property int channelPosition: -1
|
||||
property var deleteChatConfirmationDialog
|
||||
|
||||
|
||||
onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor,
|
||||
chCategoryId, hideIfPermissionsNotMet) {
|
||||
root.store.createCommunityChannel(chName, chDescription, chEmoji, chColor,
|
||||
|
@ -649,7 +663,7 @@ Item {
|
|||
|
||||
onAddPermissions: function (permissions) {
|
||||
for (var i = 0; i < permissions.length; i++) {
|
||||
root.store.permissionsStore.createPermission(permissions[i].holdingsListModel,
|
||||
root.store.permissionsStore.createPermission(permissions[i].holdingsListModel,
|
||||
permissions[i].permissionType,
|
||||
permissions[i].isPrivate,
|
||||
permissions[i].channelsListModel)
|
||||
|
|
|
@ -211,9 +211,9 @@ SettingsContentBase {
|
|||
Global.openPopup(communityIntroDialogPopup, {
|
||||
communityId: communityId,
|
||||
isInvitationPending: root.rootStore.isMyCommunityRequestPending(communityId),
|
||||
name: name,
|
||||
communityName: name,
|
||||
introMessage: introMessage,
|
||||
imageSrc: imageSrc,
|
||||
communityIcon: imageSrc,
|
||||
accessType: accessType
|
||||
})
|
||||
}
|
||||
|
@ -236,8 +236,9 @@ SettingsContentBase {
|
|||
}
|
||||
}
|
||||
|
||||
loginType: chatStore.loginType
|
||||
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
|
||||
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
|
||||
|
||||
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
|
||||
requirementsCheckPending: root.rootStore.requirementsCheckPending
|
||||
permissionsModel: {
|
||||
|
@ -257,8 +258,8 @@ SettingsContentBase {
|
|||
communityIntroDialog.keypairSigningModel = chatStore.communitiesModuleInst.keypairsSigningModel
|
||||
}
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
chatStore.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
chatStore.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -280,9 +281,21 @@ SettingsContentBase {
|
|||
Connections {
|
||||
target: chatStore.communitiesModuleInst
|
||||
|
||||
function onSharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
function onAllSharedAddressesSigned() {
|
||||
if (communityIntroDialog.profileProvesOwnershipOfSelectedAddresses) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (communityIntroDialog.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!!communityIntroDialog.replaceItem) {
|
||||
communityIntroDialog.replaceLoader.item.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
communityIntroDialog.replaceLoader.item.allSigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,6 +209,10 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
function canProfileProveOwnershipOfProvidedAddresses(addresses) {
|
||||
return walletSection.canProfileProveOwnershipOfProvidedAddresses(JSON.stringify(addresses))
|
||||
}
|
||||
|
||||
function setHideSignPhraseModal(value) {
|
||||
localAccountSensitiveSettings.hideSignPhraseModal = value;
|
||||
}
|
||||
|
|
|
@ -241,8 +241,8 @@ QtObject {
|
|||
communitiesModuleInst.prepareKeypairsForSigning(communityId, ensName, JSON.stringify(addressesToShare), airdropAddress, editMode)
|
||||
}
|
||||
|
||||
function signSharedAddressesForAllNonKeycardKeypairs() {
|
||||
communitiesModuleInst.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
function signProfileKeypairAndAllNonKeycardKeypairs() {
|
||||
communitiesModuleInst.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
function signSharedAddressesForKeypair(keyUid) {
|
||||
|
|
|
@ -263,9 +263,9 @@ QtObject {
|
|||
imageSrc, accessType, isInvitationPending) {
|
||||
openPopup(communityIntroDialogPopup,
|
||||
{communityId: communityId,
|
||||
name: name,
|
||||
communityName: name,
|
||||
introMessage: introMessage,
|
||||
imageSrc: imageSrc,
|
||||
communityIcon: imageSrc,
|
||||
accessType: accessType,
|
||||
isInvitationPending: isInvitationPending
|
||||
})
|
||||
|
@ -275,9 +275,9 @@ QtObject {
|
|||
openPopup(communityIntroDialogPopup,
|
||||
{communityId: communityId,
|
||||
stackTitle: qsTr("Share addresses with %1's owner").arg(name),
|
||||
name: name,
|
||||
communityName: name,
|
||||
introMessage: qsTr("Share addresses to rejoin %1").arg(name),
|
||||
imageSrc: imageSrc,
|
||||
communityIcon: imageSrc,
|
||||
accessType: Constants.communityChatOnRequestAccess,
|
||||
isInvitationPending: false
|
||||
})
|
||||
|
@ -354,7 +354,7 @@ QtObject {
|
|||
tokenImage: tokenImage
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function openConfirmHideAssetPopup(assetSymbol, assetName, assetImage, isCommunityToken) {
|
||||
openPopup(confirmHideAssetPopup, { assetSymbol, assetName, assetImage, isCommunityToken })
|
||||
}
|
||||
|
@ -682,9 +682,12 @@ QtObject {
|
|||
CommunityIntroDialog {
|
||||
id: communityIntroDialog
|
||||
property string communityId
|
||||
loginType: root.rootStore.loginType
|
||||
|
||||
requirementsCheckPending: root.rootStore.requirementsCheckPending
|
||||
|
||||
walletAccountsModel: root.rootStore.walletAccountsModel
|
||||
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
|
||||
|
||||
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
|
||||
permissionsModel: {
|
||||
root.rootStore.prepareTokenModelForCommunity(communityIntroDialog.communityId)
|
||||
|
@ -703,8 +706,8 @@ QtObject {
|
|||
communityIntroDialog.keypairSigningModel = root.rootStore.communitiesModuleInst.keypairsSigningModel
|
||||
}
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.rootStore.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.rootStore.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -739,9 +742,21 @@ QtObject {
|
|||
Connections {
|
||||
target: root.rootStore.communitiesModuleInst
|
||||
|
||||
function onSharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
function onAllSharedAddressesSigned() {
|
||||
if (communityIntroDialog.profileProvesOwnershipOfSelectedAddresses) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (communityIntroDialog.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
communityIntroDialog.joinCommunity()
|
||||
communityIntroDialog.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!!communityIntroDialog.replaceItem) {
|
||||
communityIntroDialog.replaceLoader.item.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
communityIntroDialog.replaceLoader.item.allSigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -886,24 +901,9 @@ QtObject {
|
|||
|
||||
Component {
|
||||
id: editSharedAddressesPopupComponent
|
||||
SharedAddressesPopup {
|
||||
CommunityIntroDialog {
|
||||
id: editSharedAddressesPopup
|
||||
|
||||
readonly property var oldSharedAddresses: root.rootStore.myRevealedAddressesForCurrentCommunity
|
||||
readonly property string oldAirdropAddress: root.rootStore.myRevealedAirdropAddressForCurrentCommunity
|
||||
|
||||
onOldSharedAddressesChanged: {
|
||||
editSharedAddressesPopup.setOldSharedAddresses(
|
||||
editSharedAddressesPopup.oldSharedAddresses
|
||||
)
|
||||
}
|
||||
|
||||
onOldAirdropAddressChanged: {
|
||||
editSharedAddressesPopup.setOldAirdropAddress(
|
||||
editSharedAddressesPopup.oldAirdropAddress
|
||||
)
|
||||
}
|
||||
|
||||
property string communityId
|
||||
|
||||
readonly property var chatStore: ChatStore.RootStore {
|
||||
|
@ -914,10 +914,17 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
isEditMode: true
|
||||
|
||||
currentSharedAddresses: root.rootStore.myRevealedAddressesForCurrentCommunity
|
||||
currentAirdropAddress: root.rootStore.myRevealedAirdropAddressForCurrentCommunity
|
||||
|
||||
communityName: chatStore.sectionDetails.name
|
||||
communityIcon: chatStore.sectionDetails.image
|
||||
requirementsCheckPending: root.rootStore.requirementsCheckPending
|
||||
loginType: chatStore.loginType
|
||||
|
||||
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
|
||||
|
||||
walletAccountsModel: root.rootStore.walletAccountsModel
|
||||
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
|
||||
permissionsModel: {
|
||||
|
@ -927,16 +934,22 @@ QtObject {
|
|||
assetsModel: chatStore.assetsModel
|
||||
collectiblesModel: chatStore.collectiblesModel
|
||||
|
||||
onSharedAddressesChanged: root.rootStore.updatePermissionsModel(
|
||||
editSharedAddressesPopup.communityId, sharedAddresses)
|
||||
getCurrencyAmount: function (balance, symbol) {
|
||||
return root.currencyStore.getCurrencyAmount(balance, symbol)
|
||||
}
|
||||
|
||||
onSharedAddressesUpdated: {
|
||||
root.rootStore.updatePermissionsModel(editSharedAddressesPopup.communityId, sharedAddresses)
|
||||
}
|
||||
|
||||
onPrepareForSigning: {
|
||||
root.rootStore.prepareKeypairsForSigning(editSharedAddressesPopup.communityId, "", sharedAddresses, airdropAddress, true)
|
||||
|
||||
editSharedAddressesPopup.keypairSigningModel = root.rootStore.communitiesModuleInst.keypairsSigningModel
|
||||
}
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.rootStore.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.rootStore.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -952,13 +965,23 @@ QtObject {
|
|||
Connections {
|
||||
target: root.rootStore.communitiesModuleInst
|
||||
|
||||
function onSharedAddressesForAllNonKeycardKeypairsSigned() {
|
||||
editSharedAddressesPopup.sharedAddressesForAllNonKeycardKeypairsSigned()
|
||||
}
|
||||
}
|
||||
function onAllSharedAddressesSigned() {
|
||||
if (editSharedAddressesPopup.profileProvesOwnershipOfSelectedAddresses) {
|
||||
editSharedAddressesPopup.editRevealedAddresses()
|
||||
editSharedAddressesPopup.close()
|
||||
return
|
||||
}
|
||||
|
||||
getCurrencyAmount: function (balance, symbol) {
|
||||
return root.currencyStore.getCurrencyAmount(balance, symbol)
|
||||
if (editSharedAddressesPopup.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
editSharedAddressesPopup.editRevealedAddresses()
|
||||
editSharedAddressesPopup.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!!editSharedAddressesPopup.replaceItem) {
|
||||
editSharedAddressesPopup.replaceLoader.item.allSigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -18,48 +18,98 @@ import SortFilterProxyModel 0.2
|
|||
StatusStackModal {
|
||||
id: root
|
||||
|
||||
property string name
|
||||
property bool isEditMode: false
|
||||
|
||||
required property string communityName
|
||||
required property string communityIcon
|
||||
required property bool requirementsCheckPending
|
||||
|
||||
property string introMessage
|
||||
property int accessType
|
||||
property url imageSrc
|
||||
|
||||
property bool isInvitationPending: false
|
||||
property int loginType: Constants.LoginType.Password
|
||||
|
||||
required property var walletAccountsModel // name, address, emoji, colorId
|
||||
property var walletAssetsModel
|
||||
required property var walletAssetsModel
|
||||
required property var permissionsModel // id, key, permissionType, holdingsListModel, channelsListModel, isPrivate, tokenCriteriaMet
|
||||
required property var assetsModel
|
||||
required property var collectiblesModel
|
||||
|
||||
required property bool requirementsCheckPending
|
||||
|
||||
property var keypairSigningModel
|
||||
|
||||
property var currentSharedAddresses: []
|
||||
onCurrentSharedAddressesChanged: d.reEvaluateModels()
|
||||
property string currentAirdropAddress: ""
|
||||
onCurrentAirdropAddressChanged: d.reEvaluateModels()
|
||||
|
||||
property var getCurrencyAmount: function (balance, symbol){}
|
||||
|
||||
property var canProfileProveOwnershipOfProvidedAddressesFn: function(addresses) { return false }
|
||||
|
||||
readonly property bool profileProvesOwnershipOfSelectedAddresses: {
|
||||
d.selectedSharedAddressesMap // needed for binding
|
||||
const obj = d.getSelectedAddresses()
|
||||
return root.canProfileProveOwnershipOfProvidedAddressesFn(obj.addresses)
|
||||
}
|
||||
|
||||
readonly property bool allAddressesToRevealBelongToSingleNonProfileKeypair: {
|
||||
const keyUids = new Set()
|
||||
for (const [key, value] of d.selectedSharedAddressesMap) {
|
||||
keyUids.add(value.keyUid)
|
||||
}
|
||||
return keyUids.size === 1 && !keyUids.has(userProfile.keyUid)
|
||||
}
|
||||
|
||||
signal prepareForSigning(string airdropAddress, var sharedAddresses)
|
||||
signal joinCommunity()
|
||||
signal signSharedAddressesForAllNonKeycardKeypairs()
|
||||
signal editRevealedAddresses()
|
||||
signal signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
signal signSharedAddressesForKeypair(string keyUid)
|
||||
signal cancelMembershipRequest()
|
||||
signal sharedAddressesUpdated(var sharedAddresses)
|
||||
|
||||
width: 640 // by design
|
||||
padding: 0
|
||||
stackTitle: root.accessType === Constants.communityChatOnRequestAccess ? qsTr("Request to join %1").arg(name) : qsTr("Welcome to %1").arg(name)
|
||||
stackTitle: root.accessType === Constants.communityChatOnRequestAccess ?
|
||||
qsTr("Request to join %1").arg(root.communityName)
|
||||
: qsTr("Welcome to %1").arg(root.communityName)
|
||||
|
||||
rightButtons: [d.shareButton, finishButton]
|
||||
|
||||
finishButton: StatusButton {
|
||||
text: root.isInvitationPending ?
|
||||
qsTr("Cancel Membership Request")
|
||||
: root.accessType === Constants.communityChatOnRequestAccess?
|
||||
qsTr("Prove ownership")
|
||||
: qsTr("Join %1").arg(root.name)
|
||||
|
||||
text: {
|
||||
if (root.isInvitationPending) {
|
||||
return qsTr("Cancel Membership Request")
|
||||
} else if (root.accessType === Constants.communityChatOnRequestAccess) {
|
||||
if (d.selectedSharedAddressesCount === d.totalNumOfAddressesForSharing) {
|
||||
return qsTr("Share all addresses to join")
|
||||
}
|
||||
return qsTr("Share %n address(s) to join", "", d.selectedSharedAddressesCount)
|
||||
}
|
||||
return qsTr("Join %1").arg(root.communityName)
|
||||
}
|
||||
type: root.isInvitationPending ? StatusBaseButton.Type.Danger
|
||||
: StatusBaseButton.Type.Normal
|
||||
|
||||
icon.name: {
|
||||
if (root.profileProvesOwnershipOfSelectedAddresses) {
|
||||
if (userProfile.usingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
||||
if (userProfile.isKeycardUser) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return "password"
|
||||
}
|
||||
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
return "keycard"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.isInvitationPending) {
|
||||
root.cancelMembershipRequest()
|
||||
|
@ -67,15 +117,48 @@ StatusStackModal {
|
|||
return
|
||||
}
|
||||
|
||||
root.prepareForSigning(d.selectedAirdropAddress, d.selectedSharedAddresses)
|
||||
root.replace(sharedAddressesSigningPanelComponent)
|
||||
d.proceedToSigningOrSubmitRequest(d.communityIntroUid)
|
||||
}
|
||||
}
|
||||
|
||||
backButton: StatusBackButton {
|
||||
visible: !!root.replaceLoader.item
|
||||
&& !(root.replaceLoader.item.componentUid === d.shareAddressesUid && root.isEditMode)
|
||||
|
||||
onClicked: {
|
||||
if (d.backActionGoesTo === d.communityIntroUid) {
|
||||
if (root.replaceItem) {
|
||||
root.replaceItem = undefined
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (d.backActionGoesTo === d.shareAddressesUid) {
|
||||
d.backActionGoesTo = d.communityIntroUid
|
||||
root.replace(sharedAddressesPanelComponent)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Layout.minimumWidth: implicitWidth
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property var tempAddressesModel: SortFilterProxyModel {
|
||||
readonly property string communityIntroUid: "community-intro"
|
||||
readonly property string shareAddressesUid: "shared-addresses"
|
||||
readonly property string signingPanelUid: "signing-panel"
|
||||
property string backActionGoesTo: d.communityIntroUid
|
||||
|
||||
readonly property int totalNumOfAddressesForSharing: root.walletAccountsModel.count
|
||||
|
||||
property var currentSharedAddressesMap: new Map() // Map[address, [keyUid, selected, isAirdrop]] - used in edit mode only
|
||||
property var selectedSharedAddressesMap: new Map() // Map[address, [keyUid, selected, isAirdrop]]
|
||||
readonly property int selectedSharedAddressesCount: d.selectedSharedAddressesMap.size
|
||||
|
||||
property var initialAddressesModel: SortFilterProxyModel {
|
||||
sourceModel: root.walletAccountsModel
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
|
@ -96,58 +179,188 @@ StatusStackModal {
|
|||
]
|
||||
}
|
||||
|
||||
// all non-watched addresses by default, unless selected otherwise below in SharedAddressesPanel
|
||||
property var selectedSharedAddresses: tempAddressesModel.count ? ModelUtils.modelToFlatArray(tempAddressesModel, "address") : []
|
||||
property string selectedAirdropAddress: selectedSharedAddresses.length ? selectedSharedAddresses[0] : ""
|
||||
function proceedToSigningOrSubmitRequest(uidOfComponentThisFunctionIsCalledFrom) {
|
||||
const selected = d.getSelectedAddresses()
|
||||
root.prepareForSigning(selected.airdropAddress, selected.addresses)
|
||||
if (root.profileProvesOwnershipOfSelectedAddresses) {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
return
|
||||
}
|
||||
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
|
||||
if (d.selectedSharedAddressesMap.size === 0) {
|
||||
console.error("selected shared addresses must not be empty")
|
||||
return
|
||||
}
|
||||
const keyUid = d.selectedSharedAddressesMap.values()[0].keyUid
|
||||
root.signSharedAddressesForKeypair(keyUid)
|
||||
return
|
||||
}
|
||||
|
||||
d.backActionGoesTo = uidOfComponentThisFunctionIsCalledFrom
|
||||
root.replace(sharedAddressesSigningPanelComponent)
|
||||
}
|
||||
|
||||
// This function deletes/adds it the address from/to the map.
|
||||
function toggleAddressSelection(keyUid, address) {
|
||||
const tmpMap = d.selectedSharedAddressesMap
|
||||
|
||||
const lAddress = address.toLowerCase()
|
||||
const obj = tmpMap.get(lAddress)
|
||||
if (!!obj) {
|
||||
if (tmpMap.size === 1) {
|
||||
console.error("cannot remove the last selected address")
|
||||
}
|
||||
tmpMap.delete(lAddress)
|
||||
if (obj.isAirdrop) {
|
||||
d.selectAirdropAddressForTheFirstSelectedAddress()
|
||||
}
|
||||
} else {
|
||||
tmpMap.set(lAddress, {keyUid: keyUid, selected: true, isAirdrop: false})
|
||||
}
|
||||
|
||||
d.selectedSharedAddressesMap = tmpMap
|
||||
}
|
||||
|
||||
// This function selects new airdrop address, invalidating old airdrop address selection.
|
||||
function selectAirdropAddressForTheFirstSelectedAddress() {
|
||||
const tmpMap = d.selectedSharedAddressesMap
|
||||
|
||||
// clear previous airdrop address
|
||||
for (const [key, value] of tmpMap) {
|
||||
if (!value.isAirdrop) {
|
||||
d.selectedSharedAddressesMap.set(key, {keyUid: value.keyUid, selected: value.selected, isAirdrop: true})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
d.selectedSharedAddressesMap = tmpMap
|
||||
}
|
||||
|
||||
// This function selects new airdrop address, invalidating old airdrop address selection.
|
||||
function selectAirdropAddress(address) {
|
||||
const tmpMap = d.selectedSharedAddressesMap
|
||||
|
||||
// clear previous airdrop address
|
||||
for (const [key, value] of tmpMap) {
|
||||
if (value.isAirdrop) {
|
||||
tmpMap.set(key, {keyUid: value.keyUid, selected: value.selected, isAirdrop: false})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// set new airdrop address
|
||||
const lAddress = address.toLowerCase()
|
||||
const obj = tmpMap.get(lAddress)
|
||||
if (!obj) {
|
||||
console.error("cannot set airdrop address for unselected address")
|
||||
return
|
||||
}
|
||||
obj.isAirdrop = true
|
||||
tmpMap.set(lAddress, obj)
|
||||
|
||||
d.selectedSharedAddressesMap = tmpMap
|
||||
}
|
||||
|
||||
// Returns an object containing all selected addresses and selected airdrop address.s
|
||||
function getSelectedAddresses() {
|
||||
const result = {addresses: [], airdropAddress: ""}
|
||||
for (const [key, value] of d.selectedSharedAddressesMap) {
|
||||
if (value.selected) {
|
||||
result.addresses.push(key)
|
||||
}
|
||||
if (value.isAirdrop) {
|
||||
result.airdropAddress = key
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function reEvaluateModels() {
|
||||
const tmpSharedAddressesMap = new Map()
|
||||
const tmpCurrentSharedAddressesMap = new Map()
|
||||
for (let i=0; i < d.initialAddressesModel.count; ++i){
|
||||
const obj = d.initialAddressesModel.get(i)
|
||||
if (!!obj) {
|
||||
let isAirdrop = i === 0
|
||||
if (root.isEditMode) {
|
||||
if (root.currentSharedAddresses.indexOf(obj.address) === -1) {
|
||||
continue
|
||||
}
|
||||
isAirdrop = obj.address.toLowerCase() === root.currentAirdropAddress.toLowerCase()
|
||||
}
|
||||
tmpSharedAddressesMap.set(obj.address, {keyUid: obj.keyUid, selected: true, isAirdrop: isAirdrop})
|
||||
tmpCurrentSharedAddressesMap.set(obj.address, {keyUid: obj.keyUid, selected: true, isAirdrop: isAirdrop})
|
||||
}
|
||||
}
|
||||
|
||||
d.selectedSharedAddressesMap = tmpSharedAddressesMap
|
||||
if (root.isEditMode) {
|
||||
d.currentSharedAddressesMap = new Map(tmpCurrentSharedAddressesMap)
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var shareButton: StatusFlatButton {
|
||||
height: finishButton.height
|
||||
visible: !root.isInvitationPending && !root.replaceItem
|
||||
borderColor: Theme.palette.baseColor2
|
||||
borderColor: "transparent"
|
||||
text: qsTr("Select addresses to share")
|
||||
onClicked: root.replace(sharedAddressesPanelComponent)
|
||||
onClicked: {
|
||||
d.backActionGoesTo = d.communityIntroUid
|
||||
root.replace(sharedAddressesPanelComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.reEvaluateModels()
|
||||
|
||||
if (root.isEditMode) {
|
||||
d.backActionGoesTo = d.shareAddressesUid
|
||||
root.replace(sharedAddressesPanelComponent)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sharedAddressesPanelComponent
|
||||
SharedAddressesPanel {
|
||||
communityName: root.name
|
||||
communityIcon: root.imageSrc
|
||||
loginType: root.loginType
|
||||
componentUid: d.shareAddressesUid
|
||||
isEditMode: root.isEditMode
|
||||
communityName: root.communityName
|
||||
communityIcon: root.communityIcon
|
||||
requirementsCheckPending: root.requirementsCheckPending
|
||||
walletAccountsModel: SortFilterProxyModel {
|
||||
sourceModel: root.walletAccountsModel
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
function isGenerated(modelData) {
|
||||
return modelData.walletType === Constants.generatedWalletType
|
||||
}
|
||||
|
||||
expression: {
|
||||
return isGenerated(modelLeft)
|
||||
}
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "position"
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "name"
|
||||
}
|
||||
]
|
||||
}
|
||||
walletAccountsModel: d.initialAddressesModel
|
||||
selectedSharedAddressesMap: d.selectedSharedAddressesMap
|
||||
currentSharedAddressesMap: d.currentSharedAddressesMap
|
||||
|
||||
totalNumOfAddressesForSharing: d.totalNumOfAddressesForSharing
|
||||
profileProvesOwnershipOfSelectedAddresses: root.profileProvesOwnershipOfSelectedAddresses
|
||||
allAddressesToRevealBelongToSingleNonProfileKeypair: root.allAddressesToRevealBelongToSingleNonProfileKeypair
|
||||
|
||||
walletAssetsModel: root.walletAssetsModel
|
||||
permissionsModel: root.permissionsModel
|
||||
assetsModel: root.assetsModel
|
||||
collectiblesModel: root.collectiblesModel
|
||||
|
||||
onClose: {
|
||||
root.close()
|
||||
}
|
||||
|
||||
onToggleAddressSelection: {
|
||||
d.toggleAddressSelection(keyUid, address)
|
||||
|
||||
const obj = d.getSelectedAddresses()
|
||||
root.sharedAddressesUpdated(obj.addresses)
|
||||
}
|
||||
|
||||
onAirdropAddressSelected: {
|
||||
d.selectAirdropAddress(address)
|
||||
}
|
||||
|
||||
onShareSelectedAddressesClicked: {
|
||||
d.selectedAirdropAddress = airdropAddress
|
||||
d.selectedSharedAddresses = sharedAddresses
|
||||
root.replaceItem = undefined // go back, unload us
|
||||
}
|
||||
onSharedAddressesChanged: {
|
||||
root.sharedAddressesUpdated(sharedAddresses)
|
||||
d.proceedToSigningOrSubmitRequest(d.shareAddressesUid)
|
||||
}
|
||||
|
||||
getCurrencyAmount: function (balance, symbol){
|
||||
return root.getCurrencyAmount(balance, symbol)
|
||||
}
|
||||
|
@ -158,10 +371,16 @@ StatusStackModal {
|
|||
id: sharedAddressesSigningPanelComponent
|
||||
SharedAddressesSigningPanel {
|
||||
|
||||
componentUid: d.signingPanelUid
|
||||
isEditMode: root.isEditMode
|
||||
totalNumOfAddressesForSharing: d.totalNumOfAddressesForSharing
|
||||
selectedSharedAddressesMap: d.selectedSharedAddressesMap
|
||||
|
||||
communityName: root.communityName
|
||||
keypairSigningModel: root.keypairSigningModel
|
||||
|
||||
onSignSharedAddressesForAllNonKeycardKeypairs: {
|
||||
root.signSharedAddressesForAllNonKeycardKeypairs()
|
||||
onSignProfileKeypairAndAllNonKeycardKeypairs: {
|
||||
root.signProfileKeypairAndAllNonKeycardKeypairs()
|
||||
}
|
||||
|
||||
onSignSharedAddressesForKeypair: {
|
||||
|
@ -169,7 +388,11 @@ StatusStackModal {
|
|||
}
|
||||
|
||||
onJoinCommunity: {
|
||||
root.joinCommunity()
|
||||
if (root.isEditMode) {
|
||||
root.editRevealedAddresses()
|
||||
} else {
|
||||
root.joinCommunity()
|
||||
}
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
@ -191,12 +414,12 @@ StatusStackModal {
|
|||
visible: ((image.status == Image.Loading) ||
|
||||
(image.status == Image.Ready)) &&
|
||||
!image.isError
|
||||
image.source: root.imageSrc
|
||||
image.source: root.communityIcon
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: root.introMessage || qsTr("Community <b>%1</b> has no intro message...").arg(root.name)
|
||||
text: root.introMessage || qsTr("Community <b>%1</b> has no intro message...").arg(root.communityName)
|
||||
color: Theme.palette.directColor1
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue