status-desktop/ui/app/AppLayouts/Communities/panels/SharedAddressesPanel.qml

347 lines
13 KiB
QML

import QtQuick 2.15
import QtQml.Models 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import SortFilterProxyModel 0.2
import AppLayouts.Profile.controls 1.0
import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.views 1.0
import AppLayouts.Communities.helpers 1.0
import utils 1.0
Control {
id: root
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
required property int /*PermissionTypes.Type*/ eligibleToJoinAs
property bool requirementsCheckPending
property bool checkingPermissionToJoinInProgress
property bool joinPermissionsCheckCompletedWithoutErrors
required property string communityId
required property string communityName
required property string communityIcon
required property var walletAssetsModel
required property var walletCollectiblesModel
required property var walletAccountsModel // name, address, emoji, colorId
required property var permissionsModel // id, key, permissionType, holdingsListModel, channelsListModel, isPrivate, tokenCriteriaMet
required property var assetsModel
required property var collectiblesModel
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 rightButtons: root.isEditMode? [d.cancelButton, d.saveButton] : [d.shareAddressesButton]
property var getCurrencyAmount: function (balance, symbol){}
signal toggleAddressSelection(string keyUid, string address)
signal airdropAddressSelected (string address)
signal shareSelectedAddressesClicked()
signal close()
padding: 0
spacing: Theme.padding
QtObject {
id: d
// internal logic
readonly property bool hasPermissions: permissionsView.hasAnyVisiblePermission
readonly property int selectedSharedAddressesCount: root.selectedSharedAddressesMap.size
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
}
property var tokenCountMap: new Map()
function getTokenCount(address) {
if (d.tokenCountMap.has(address))
return d.tokenCountMap.get(address)
return 0
}
// warning states
readonly property bool lostCommunityPermission: root.isEditMode && permissionsView.lostPermissionToJoin
readonly property bool lostChannelPermissions: root.isEditMode && permissionsView.lostChannelPermissions
readonly property var cancelButton: StatusFlatButton {
visible: root.isEditMode
borderColor: Theme.palette.baseColor2
text: qsTr("Cancel")
onClicked: root.close()
}
readonly property string tooltipText: {
if (root.requirementsCheckPending || root.checkingPermissionToJoinInProgress)
return qsTr("Requirements check pending")
if (!root.joinPermissionsCheckCompletedWithoutErrors)
return qsTr("Checking permissions failed")
return ""
}
readonly property var saveButton: StatusButton {
visible: root.isEditMode
interactive: d.dirty && !root.requirementsCheckPending && root.joinPermissionsCheckCompletedWithoutErrors
loading: root.requirementsCheckPending
type: d.lostCommunityPermission || d.lostChannelPermissions ? StatusBaseButton.Type.Danger : StatusBaseButton.Type.Normal
tooltip.text: {
if (interactive)
return ""
return d.tooltipText
}
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"
}
return ""
}
onClicked: {
root.shareSelectedAddressesClicked()
}
}
readonly property var shareAddressesButton: StatusButton {
visible: !root.isEditMode
interactive: !root.checkingPermissionToJoinInProgress && root.eligibleToJoinAs !== PermissionTypes.Type.None && root.joinPermissionsCheckCompletedWithoutErrors
loading: root.checkingPermissionToJoinInProgress
tooltip.text: {
if (interactive)
return ""
return d.tooltipText
}
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 {
anchors.fill: parent
spacing: 0
// warning panel
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 32
color: Theme.palette.dangerColor1
visible: d.lostCommunityPermission || d.lostChannelPermissions
StatusBaseText {
width: parent.width
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Qt.AlignHCenter
elide: Text.ElideRight
text: d.lostCommunityPermission ? qsTr("Selected addresses have insufficient tokens to maintain %1 membership").arg(root.communityName) :
d.lostChannelPermissions ? qsTr("By deselecting these addresses, you will lose channel permissions") :
""
font.pixelSize: Theme.additionalTextSize
font.weight: Font.Medium
color: Theme.palette.indirectColor1
}
}
// addresses
SharedAddressesAccountSelector {
id: accountSelector
hasPermissions: d.hasPermissions
Layout.fillWidth: true
Layout.preferredHeight: contentHeight + topMargin + bottomMargin
Layout.maximumHeight: hasPermissions ? permissionsView.implicitHeight > root.availableHeight / 2 ? root.availableHeight / 2 : root.availableHeight : -1
Layout.fillHeight: !hasPermissions
implicitHeight: contentHeight
uniquePermissionAssetsKeys:
PermissionsHelpers.getUniquePermissionTokenKeys(
root.permissionsModel, Constants.TokenType.ERC20)
uniquePermissionCollectiblesKeys:
PermissionsHelpers.getUniquePermissionTokenKeys(
root.permissionsModel, Constants.TokenType.ERC721)
model: SortFilterProxyModel {
sourceModel: root.walletAccountsModel
proxyRoles: FastExpressionRole {
name: "tokenCount"
expression: {
d.tokenCountMap
return d.getTokenCount(model.address.toLowerCase())
}
expectedRoles: ["address"]
}
sorters: [
// FIXME add sort token-relevant accounts first; https://github.com/status-im/status-desktop/issues/14192
RoleSorter {
roleName: "tokenCount"
sortOrder: Qt.DescendingOrder
},
RoleSorter {
roleName: "name"
}
]
}
walletAssetsModel: root.walletAssetsModel
walletCollectiblesModel: root.walletCollectiblesModel
communityId: root.communityId
communityCollectiblesModel: root.collectiblesModel
selectedSharedAddressesMap: root.selectedSharedAddressesMap
onToggleAddressSelection: {
root.toggleAddressSelection(keyUid, address)
}
onAirdropAddressSelected: {
root.airdropAddressSelected(address)
}
getCurrencyAmount: function (balance, symbol) {
return root.getCurrencyAmount(balance, symbol)
}
Component.onCompleted: {
const tmpTokenCountMap = new Map()
for (let i = 0; i < accountSelector.count; i++) {
const item = accountSelector.itemAtIndex(i)
if (!!item) {
tmpTokenCountMap.set(item.address.toLowerCase(), item.tokenCount)
}
}
d.tokenCountMap = tmpTokenCountMap
}
}
// divider with top rounded corners + drop shadow
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Theme.padding * 2
color: permissionsView.color
radius: Theme.padding
border.width: 1
border.color: Theme.palette.baseColor3
visible: permissionsView.hasAnyVisiblePermission
layer.enabled: true
layer.effect: DropShadow {
horizontalOffset: 0
verticalOffset: -9
radius: 14
samples: 29
color: Qt.rgba(0, 0, 0, 0.04)
}
}
// permissions
SharedAddressesPermissionsPanel {
id: permissionsView
isEditMode: root.isEditMode
isDirty: d.dirty
permissionsModel: root.permissionsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
requirementsCheckPending: root.requirementsCheckPending
checkingPermissionToJoinInProgress: root.checkingPermissionToJoinInProgress
joinPermissionsCheckCompletedWithoutErrors: root.joinPermissionsCheckCompletedWithoutErrors
communityName: root.communityName
communityIcon: root.communityIcon
eligibleToJoinAs: root.eligibleToJoinAs
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: -Theme.padding // compensate for the half-rounded divider above
visible: permissionsView.hasAnyVisiblePermission
}
}
}