feat: New design flows to integrate Revealing addresses...
... when joining Community functionality Closes #11138
This commit is contained in:
parent
fe94bd0c69
commit
02e40adfca
|
@ -249,7 +249,7 @@ QtObject:
|
||||||
let addressesArray = map(parseJson(addressesToShare).getElems(), proc(x:JsonNode):string = x.getStr())
|
let addressesArray = map(parseJson(addressesToShare).getElems(), proc(x:JsonNode):string = x.getStr())
|
||||||
self.delegate.requestToJoinCommunityWithAuthentication(ensName, addressesArray, airdropAddress)
|
self.delegate.requestToJoinCommunityWithAuthentication(ensName, addressesArray, airdropAddress)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
echo "Error requesting to join community with authetication and shared addresses: ", e.msg
|
echo "Error requesting to join community with authentication and shared addresses: ", e.msg
|
||||||
|
|
||||||
proc joinGroupChatFromInvitation*(self: View, groupName: string, chatId: string, adminPK: string) {.slot.} =
|
proc joinGroupChatFromInvitation*(self: View, groupName: string, chatId: string, adminPK: string) {.slot.} =
|
||||||
self.delegate.joinGroupChatFromInvitation(groupName, chatId, adminPK)
|
self.delegate.joinGroupChatFromInvitation(groupName, chatId, adminPK)
|
||||||
|
@ -421,4 +421,4 @@ QtObject:
|
||||||
read = getAllTokenRequirementsMet
|
read = getAllTokenRequirementsMet
|
||||||
notify = allTokenRequirementsMetChanged
|
notify = allTokenRequirementsMetChanged
|
||||||
|
|
||||||
proc userAuthenticationCanceled*(self: View) {.signal.}
|
proc userAuthenticationCanceled*(self: View) {.signal.}
|
||||||
|
|
|
@ -57,7 +57,6 @@ QtObject:
|
||||||
let enumRole = role.ModelRole
|
let enumRole = role.ModelRole
|
||||||
case enumRole:
|
case enumRole:
|
||||||
of ModelRole.Key:
|
of ModelRole.Key:
|
||||||
|
|
||||||
if item.getType() == ord(TokenType.ENS):
|
if item.getType() == ord(TokenType.ENS):
|
||||||
result = newQVariant(item.getEnsPattern())
|
result = newQVariant(item.getEnsPattern())
|
||||||
else:
|
else:
|
||||||
|
@ -69,7 +68,7 @@ QtObject:
|
||||||
of ModelRole.ShortName:
|
of ModelRole.ShortName:
|
||||||
result = newQVariant(item.getSymbol())
|
result = newQVariant(item.getSymbol())
|
||||||
of ModelRole.Name:
|
of ModelRole.Name:
|
||||||
result = newQVariant(item.getSymbol())
|
result = newQVariant(item.getName())
|
||||||
of ModelRole.Amount:
|
of ModelRole.Amount:
|
||||||
result = newQVariant(item.getAmount())
|
result = newQVariant(item.getAmount())
|
||||||
of ModelRole.CriteriaMet:
|
of ModelRole.CriteriaMet:
|
||||||
|
|
|
@ -162,7 +162,7 @@ QtObject:
|
||||||
of "description": result = $item.getDescription()
|
of "description": result = $item.getDescription()
|
||||||
of "assetWebsiteUrl": result = $item.getAssetWebsiteUrl()
|
of "assetWebsiteUrl": result = $item.getAssetWebsiteUrl()
|
||||||
of "builtOn": result = $item.getBuiltOn()
|
of "builtOn": result = $item.getBuiltOn()
|
||||||
of "Address": result = $item.getAddress()
|
of "address": result = $item.getAddress()
|
||||||
of "marketCap": result = $item.getMarketCap()
|
of "marketCap": result = $item.getMarketCap()
|
||||||
of "highDay": result = $item.getHighDay()
|
of "highDay": result = $item.getHighDay()
|
||||||
of "lowDay": result = $item.getLowDay()
|
of "lowDay": result = $item.getLowDay()
|
||||||
|
|
|
@ -89,7 +89,7 @@ proc buildTokenPermissionItem*(tokenPermission: CommunityTokenPermissionDto): To
|
||||||
tokenCriteriaItems,
|
tokenCriteriaItems,
|
||||||
tokenPermissionChatListItems,
|
tokenPermissionChatListItems,
|
||||||
tokenPermission.isPrivate,
|
tokenPermission.isPrivate,
|
||||||
false # allTokenCriteriaMet will be update by a call to checkPermissinosToJoin
|
false # allTokenCriteriaMet will be updated by a call to checkPermissionsToJoin
|
||||||
)
|
)
|
||||||
|
|
||||||
return tokenPermissionItem
|
return tokenPermissionItem
|
||||||
|
|
|
@ -253,6 +253,10 @@ ListModel {
|
||||||
title: "RemotelyDestructPopup"
|
title: "RemotelyDestructPopup"
|
||||||
section: "Popups"
|
section: "Popups"
|
||||||
}
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "SharedAddressesPopup"
|
||||||
|
section: "Popups"
|
||||||
|
}
|
||||||
ListElement {
|
ListElement {
|
||||||
title: "AlertPopup"
|
title: "AlertPopup"
|
||||||
section: "Popups"
|
section: "Popups"
|
||||||
|
|
|
@ -242,6 +242,13 @@
|
||||||
],
|
],
|
||||||
"EditOwnerTokenView": [
|
"EditOwnerTokenView": [
|
||||||
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?type=design&node-id=34794-590207&mode=design&t=ZnwK9yenS5oSgwws-0"
|
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?type=design&node-id=34794-590207&mode=design&t=ZnwK9yenS5oSgwws-0"
|
||||||
|
],
|
||||||
|
"CommunityIntroDialog": [
|
||||||
|
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=31461%3A563897&mode=dev"
|
||||||
|
],
|
||||||
|
"SharedAddressesPopup": [
|
||||||
|
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=31461%3A564367&mode=dev",
|
||||||
|
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=31461%3A563905&mode=dev",
|
||||||
|
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=31461%3A579875&mode=dev"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,16 @@ SplitView {
|
||||||
5. 🚗 consectetur adipiscing elit
|
5. 🚗 consectetur adipiscing elit
|
||||||
|
|
||||||
Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.".arg(dialog.name)
|
Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.".arg(dialog.name)
|
||||||
|
loginType: ctrlLoginType.currentIndex
|
||||||
|
|
||||||
onJoined: logs.logEvent("CommunityIntroDialog::onJoined()")
|
walletAccountsModel: WalletAccountsModel {}
|
||||||
|
permissionsModel: dialog.accessType === Constants.communityChatOnRequestAccess ? PermissionsModel.complexPermissionsModel
|
||||||
|
: null
|
||||||
|
assetsModel: AssetsModel {}
|
||||||
|
collectiblesModel: CollectiblesModel {}
|
||||||
|
|
||||||
|
onJoined: logs.logEvent("CommunityIntroDialog::onJoined", ["airdropAddress", "sharedAddresses"], arguments)
|
||||||
onCancelMembershipRequest: logs.logEvent("CommunityIntroDialog::onCancelMembershipRequest()")
|
onCancelMembershipRequest: logs.logEvent("CommunityIntroDialog::onCancelMembershipRequest()")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +152,20 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: dialog.accessType == Constants.communityChatOnRequestAccess && !dialog.isInvitationPending
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Login type"
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: ctrlLoginType
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ["Password","Biometrics","Keycard"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
import StatusQ.Core.Utils 0.1
|
import StatusQ.Core.Utils 0.1
|
||||||
|
|
||||||
|
import AppLayouts.Communities.controls 1.0
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ SplitView {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
property string name: "Uniswap"
|
property string name: "Uniswap"
|
||||||
property string channelName: "#vip"
|
property string channelName: "vip"
|
||||||
property bool joinCommunity: true // Otherwise, enter channel
|
property bool joinCommunity: true // Otherwise, enter channel
|
||||||
property bool requirementsMet: true
|
property bool requirementsMet: true
|
||||||
property bool isInvitationPending: false
|
property bool isInvitationPending: false
|
||||||
|
@ -40,8 +40,6 @@ SplitView {
|
||||||
orientation: Qt.Vertical
|
orientation: Qt.Vertical
|
||||||
SplitView.fillWidth: true
|
SplitView.fillWidth: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
SplitView.fillWidth: true
|
SplitView.fillWidth: true
|
||||||
SplitView.fillHeight: true
|
SplitView.fillHeight: true
|
||||||
|
|
|
@ -59,6 +59,7 @@ SplitView {
|
||||||
readonly property string image: ModelsData.icons.socks
|
readonly property string image: ModelsData.icons.socks
|
||||||
readonly property string color: "red"
|
readonly property string color: "red"
|
||||||
readonly property bool owner: isOwnerCheckBox.checked
|
readonly property bool owner: isOwnerCheckBox.checked
|
||||||
|
readonly property bool admin: isAdminCheckBox.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(method, index) {
|
function log(method, index) {
|
||||||
|
@ -94,6 +95,12 @@ SplitView {
|
||||||
text: "Is owner"
|
text: "Is owner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: isAdminCheckBox
|
||||||
|
|
||||||
|
text: "Is admin"
|
||||||
|
}
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: emptyModelCheckBox
|
id: emptyModelCheckBox
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import Storybook 1.0
|
||||||
|
import Models 1.0
|
||||||
|
|
||||||
|
import AppLayouts.Communities.popups 1.0
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
id: root
|
||||||
|
Logs { id: logs }
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: emptyModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: dlgComponent
|
||||||
|
SharedAddressesPopup {
|
||||||
|
//anchors.centerIn: parent
|
||||||
|
isEditMode: ctrlEditMode.checked
|
||||||
|
communityName: "Decentraland"
|
||||||
|
communityIcon: ModelsData.assets.uni
|
||||||
|
loginType: ctrlLoginType.currentIndex
|
||||||
|
walletAccountsModel: WalletAccountsModel {}
|
||||||
|
permissionsModel: {
|
||||||
|
if (ctrlPermissions.checked && ctrlTokenGatedChannels.checked)
|
||||||
|
return PermissionsModel.complexPermissionsModel
|
||||||
|
if (ctrlPermissions.checked)
|
||||||
|
return PermissionsModel.permissionsModel
|
||||||
|
if (ctrlTokenGatedChannels.checked)
|
||||||
|
return PermissionsModel.channelsOnlyPermissionsModel
|
||||||
|
|
||||||
|
return emptyModel
|
||||||
|
}
|
||||||
|
|
||||||
|
assetsModel: AssetsModel {}
|
||||||
|
collectiblesModel: CollectiblesModel {}
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
onShareSelectedAddressesClicked: logs.logEvent("::shareSelectedAddressesClicked", ["airdropAddress", "sharedAddresses"], arguments)
|
||||||
|
onClosed: destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var dialog
|
||||||
|
|
||||||
|
function createAndOpenDialog() {
|
||||||
|
dialog = dlgComponent.createObject(root)
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: createAndOpenDialog()
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
id: pane
|
||||||
|
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
SplitView.fillHeight: true
|
||||||
|
|
||||||
|
PopupBackground {
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Reopen"
|
||||||
|
|
||||||
|
onClicked: createAndOpenDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogsAndControlsPanel {
|
||||||
|
id: logsAndControlsPanel
|
||||||
|
|
||||||
|
SplitView.minimumHeight: 100
|
||||||
|
SplitView.preferredHeight: 150
|
||||||
|
|
||||||
|
logsView.logText: logs.logText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
SplitView.minimumWidth: 300
|
||||||
|
SplitView.preferredWidth: 300
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: ctrlPermissions
|
||||||
|
text: "With permissions"
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: ctrlTokenGatedChannels
|
||||||
|
text: "With token gated channels"
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: ctrlEditMode
|
||||||
|
text: "Edit mode"
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: ctrlEditMode.checked
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Login type"
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: ctrlLoginType
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ["Password","Biometrics","Keycard"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: "lightgray"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Info"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Shared addresses: %1".arg(!!dialog ? dialog.selectedSharedAddresses.join(";") : "")
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Airdrop address: %1".arg(!!dialog ? dialog.selectedAirdropAddress : "")
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,8 +65,8 @@ SplitView {
|
||||||
errorText: errorTextField.text
|
errorText: errorTextField.text
|
||||||
totalFeeText: "0.01 ETH ($265.43)"
|
totalFeeText: "0.01 ETH ($265.43)"
|
||||||
|
|
||||||
onSignTransactionClicked: logs.logEvent("SignTokenTransactionsPopup::onSignTransactionClicked")
|
onSignTransactionClicked: logs.logEvent("SignMultiTokenTransactionsPopup::onSignTransactionClicked")
|
||||||
onCancelClicked: logs.logEvent("SignTokenTransactionsPopup::onCancelClicked")
|
onCancelClicked: logs.logEvent("SignMultiTokenTransactionsPopup::onCancelClicked")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ SplitView {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: "Network name"
|
text: "Network fee"
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
|
|
|
@ -50,7 +50,7 @@ SplitView {
|
||||||
id: editor
|
id: editor
|
||||||
|
|
||||||
isOnlyChannelPanelEditor: true
|
isOnlyChannelPanelEditor: true
|
||||||
channelName: "#vip"
|
channelName: "vip"
|
||||||
joinCommunity: false
|
joinCommunity: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ ListModel {
|
||||||
iconSource: ModelsData.assets.snt,
|
iconSource: ModelsData.assets.snt,
|
||||||
name: "snt",
|
name: "snt",
|
||||||
shortName: "snt",
|
shortName: "snt",
|
||||||
symbol: "snt",
|
symbol: "SNT",
|
||||||
category: TokenCategories.Category.General,
|
category: TokenCategories.Category.General,
|
||||||
communityId: ""
|
communityId: ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ ListModel {
|
||||||
key: "Anniversary",
|
key: "Anniversary",
|
||||||
iconSource: ModelsData.collectibles.anniversary,
|
iconSource: ModelsData.collectibles.anniversary,
|
||||||
name: "Anniversary",
|
name: "Anniversary",
|
||||||
symbol: "Anniversary",
|
symbol: "ANN",
|
||||||
category: TokenCategories.Category.Community,
|
category: TokenCategories.Category.Community,
|
||||||
imageUrl: ModelsData.collectibles.anniversary,
|
imageUrl: ModelsData.collectibles.anniversary,
|
||||||
id: 1767698,
|
id: 1767698,
|
||||||
|
@ -18,7 +18,7 @@ ListModel {
|
||||||
key: "Anniversary2",
|
key: "Anniversary2",
|
||||||
iconSource: ModelsData.collectibles.anniversary,
|
iconSource: ModelsData.collectibles.anniversary,
|
||||||
name: "Anniversary2",
|
name: "Anniversary2",
|
||||||
symbol: "Anniversary2",
|
symbol: "ANN2",
|
||||||
category: TokenCategories.Category.Community,
|
category: TokenCategories.Category.Community,
|
||||||
imageUrl: ModelsData.collectibles.anniversary,
|
imageUrl: ModelsData.collectibles.anniversary,
|
||||||
id: 1767699,
|
id: 1767699,
|
||||||
|
@ -28,7 +28,7 @@ ListModel {
|
||||||
key: "CryptoKitties",
|
key: "CryptoKitties",
|
||||||
iconSource: ModelsData.collectibles.cryptoKitties,
|
iconSource: ModelsData.collectibles.cryptoKitties,
|
||||||
name: "CryptoKitties",
|
name: "CryptoKitties",
|
||||||
symbol: "CryptoKitties",
|
symbol: "CK",
|
||||||
category: TokenCategories.Category.Own,
|
category: TokenCategories.Category.Own,
|
||||||
subItems: [
|
subItems: [
|
||||||
{
|
{
|
||||||
|
@ -82,7 +82,7 @@ ListModel {
|
||||||
key: "SuperRare",
|
key: "SuperRare",
|
||||||
iconSource: ModelsData.collectibles.superRare,
|
iconSource: ModelsData.collectibles.superRare,
|
||||||
name: "SuperRare",
|
name: "SuperRare",
|
||||||
symbol: "SuperRare",
|
symbol: "SR",
|
||||||
category: TokenCategories.Category.Own,
|
category: TokenCategories.Category.Own,
|
||||||
imageUrl: ModelsData.collectibles.superRare,
|
imageUrl: ModelsData.collectibles.superRare,
|
||||||
id: 1767701,
|
id: 1767701,
|
||||||
|
@ -92,7 +92,7 @@ ListModel {
|
||||||
key: "Custom",
|
key: "Custom",
|
||||||
iconSource: ModelsData.collectibles.custom,
|
iconSource: ModelsData.collectibles.custom,
|
||||||
name: "Custom Collectible",
|
name: "Custom Collectible",
|
||||||
symbol: "Custom",
|
symbol: "CUS",
|
||||||
category: TokenCategories.Category.General,
|
category: TokenCategories.Category.General,
|
||||||
imageUrl: ModelsData.collectibles.custom,
|
imageUrl: ModelsData.collectibles.custom,
|
||||||
id: 1767764,
|
id: 1767764,
|
||||||
|
|
|
@ -14,13 +14,15 @@ QtObject {
|
||||||
holdingsListModel: root.createHoldingsModel1(),
|
holdingsListModel: root.createHoldingsModel1(),
|
||||||
channelsListModel: root.createChannelsModel1(),
|
channelsListModel: root.createChannelsModel1(),
|
||||||
permissionType: PermissionTypes.Type.Admin,
|
permissionType: PermissionTypes.Type.Admin,
|
||||||
isPrivate: true
|
isPrivate: true,
|
||||||
|
tokenCriteriaMet: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
holdingsListModel: root.createHoldingsModel2(),
|
holdingsListModel: root.createHoldingsModel2(),
|
||||||
channelsListModel: root.createChannelsModel2(),
|
channelsListModel: root.createChannelsModel2(),
|
||||||
permissionType: PermissionTypes.Type.Member,
|
permissionType: PermissionTypes.Type.Member,
|
||||||
isPrivate: false
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ QtObject {
|
||||||
holdingsListModel: root.createHoldingsModel4(),
|
holdingsListModel: root.createHoldingsModel4(),
|
||||||
channelsListModel: root.createChannelsModel1(),
|
channelsListModel: root.createChannelsModel1(),
|
||||||
permissionType: PermissionTypes.Type.Admin,
|
permissionType: PermissionTypes.Type.Admin,
|
||||||
isPrivate: true,
|
isPrivate: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -138,6 +140,124 @@ QtObject {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
readonly property var complexPermissionsModelData: [
|
||||||
|
{
|
||||||
|
id: "admin1",
|
||||||
|
holdingsListModel: root.createHoldingsModel2b(),
|
||||||
|
channelsListModel: root.createChannelsModel2(),
|
||||||
|
permissionType: PermissionTypes.Type.Admin,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "admin2",
|
||||||
|
holdingsListModel: root.createHoldingsModel3(),
|
||||||
|
channelsListModel: root.createChannelsModel2(),
|
||||||
|
permissionType: PermissionTypes.Type.Admin,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "member1",
|
||||||
|
holdingsListModel: root.createHoldingsModel2(),
|
||||||
|
channelsListModel: root.createChannelsModel2(),
|
||||||
|
permissionType: PermissionTypes.Type.Member,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "member2",
|
||||||
|
holdingsListModel: root.createHoldingsModel3(),
|
||||||
|
channelsListModel: root.createChannelsModel2(),
|
||||||
|
permissionType: PermissionTypes.Type.Member,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly property var channelsOnlyPermissionsModelData: [
|
||||||
|
{
|
||||||
|
id: "read1a",
|
||||||
|
holdingsListModel: root.createHoldingsModel1b(),
|
||||||
|
channelsListModel: root.createChannelsModel1(),
|
||||||
|
permissionType: PermissionTypes.Type.Read,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "read1b",
|
||||||
|
holdingsListModel: root.createHoldingsModel1(),
|
||||||
|
channelsListModel: root.createChannelsModel1(),
|
||||||
|
permissionType: PermissionTypes.Type.Read,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "read1c",
|
||||||
|
holdingsListModel: root.createHoldingsModel3(),
|
||||||
|
channelsListModel: root.createChannelsModel1(),
|
||||||
|
permissionType: PermissionTypes.Type.Read,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "read2a",
|
||||||
|
holdingsListModel: root.createHoldingsModel2(),
|
||||||
|
channelsListModel: root.createChannelsModel3(),
|
||||||
|
permissionType: PermissionTypes.Type.Read,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "read2b",
|
||||||
|
holdingsListModel: root.createHoldingsModel5(),
|
||||||
|
channelsListModel: root.createChannelsModel3(),
|
||||||
|
permissionType: PermissionTypes.Type.Read,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "viewAndPost1a",
|
||||||
|
holdingsListModel: root.createHoldingsModel3(),
|
||||||
|
channelsListModel: root.createChannelsModel1(),
|
||||||
|
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "viewAndPost1b",
|
||||||
|
holdingsListModel: root.createHoldingsModel2b(),
|
||||||
|
channelsListModel: root.createChannelsModel1(),
|
||||||
|
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "viewAndPost2a",
|
||||||
|
holdingsListModel: root.createHoldingsModel3(),
|
||||||
|
channelsListModel: root.createChannelsModel3(),
|
||||||
|
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "viewAndPost2b",
|
||||||
|
holdingsListModel: root.createHoldingsModel5(),
|
||||||
|
channelsListModel: root.createChannelsModel3(),
|
||||||
|
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "viewAndPost2c",
|
||||||
|
holdingsListModel: root.createHoldingsModel1(),
|
||||||
|
channelsListModel: root.createChannelsModel3(),
|
||||||
|
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||||
|
isPrivate: false,
|
||||||
|
tokenCriteriaMet: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
readonly property ListModel permissionsModel: ListModel {
|
readonly property ListModel permissionsModel: ListModel {
|
||||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||||
model: root.permissionsModel
|
model: root.permissionsModel
|
||||||
|
@ -215,6 +335,29 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property var complexPermissionsModel: ListModel {
|
||||||
|
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||||
|
model: root.complexPermissionsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
append(complexPermissionsModelData)
|
||||||
|
append(channelsOnlyPermissionsModelData)
|
||||||
|
guard.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var channelsOnlyPermissionsModel: ListModel {
|
||||||
|
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||||
|
model: root.channelsOnlyPermissionsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
append(channelsOnlyPermissionsModelData)
|
||||||
|
guard.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createHoldingsModel1() {
|
function createHoldingsModel1() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -230,7 +373,7 @@ QtObject {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: HoldingTypes.Type.Ens,
|
type: HoldingTypes.Type.Ens,
|
||||||
key: "Ens",
|
key: "*.eth",
|
||||||
amount: 1,
|
amount: 1,
|
||||||
available: true
|
available: true
|
||||||
}
|
}
|
||||||
|
@ -249,7 +392,24 @@ QtObject {
|
||||||
type: HoldingTypes.Type.Asset,
|
type: HoldingTypes.Type.Asset,
|
||||||
key: "Dai",
|
key: "Dai",
|
||||||
amount: 11,
|
amount: 11,
|
||||||
available: false
|
available: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function createHoldingsModel2b() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: HoldingTypes.Type.Collectible,
|
||||||
|
key: "Anniversary2",
|
||||||
|
amount: 1,
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: HoldingTypes.Type.Asset,
|
||||||
|
key: "snt",
|
||||||
|
amount: 666,
|
||||||
|
available: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -293,7 +453,7 @@ QtObject {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: HoldingTypes.Type.Ens,
|
type: HoldingTypes.Type.Ens,
|
||||||
key: "ENS",
|
key: "foo.bar.eth",
|
||||||
amount: 1,
|
amount: 1,
|
||||||
available: false
|
available: false
|
||||||
},
|
},
|
||||||
|
@ -317,7 +477,7 @@ QtObject {
|
||||||
{
|
{
|
||||||
type: HoldingTypes.Type.Asset,
|
type: HoldingTypes.Type.Asset,
|
||||||
key: "zrx",
|
key: "zrx",
|
||||||
amount: 1,
|
amount: 10,
|
||||||
available: false
|
available: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -355,4 +515,8 @@ QtObject {
|
||||||
function createChannelsModel2() {
|
function createChannelsModel2() {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createChannelsModel3() {
|
||||||
|
return [{ key: "_vip" } ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,122 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
ListElement { name: "Test account"; emoji: "😋"; colorId: "primary"; address: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"; walletType: "" }
|
readonly property var data: [
|
||||||
ListElement { name: "Another account - generated"; emoji: "🚗"; colorId: "army"; address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8888"; walletType: "generated" }
|
{
|
||||||
ListElement { name: "Another account - seed"; emoji: "🎨"; colorId: "army"; address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8888"; walletType: "seed" }
|
name: "helloworld",
|
||||||
ListElement { name: "Another account - watch"; emoji: "🔗"; colorId: "army"; address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8888"; walletType: "watch" }
|
emoji: "😋",
|
||||||
ListElement { name: "Another account - key"; emoji: "💼"; colorId: "army"; address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8888"; walletType: "key" }
|
colorId: "primary",
|
||||||
|
address: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
|
||||||
|
walletType: "",
|
||||||
|
position: 0,
|
||||||
|
assets: [
|
||||||
|
{
|
||||||
|
symbol: "socks",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 2,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 15.0,
|
||||||
|
symbol: "SOX"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol: "snt",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 2,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 670.2345,
|
||||||
|
symbol: "SNT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol: "zrx",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 4,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 7.456000,
|
||||||
|
symbol: "ZRX"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hot wallet (generated)",
|
||||||
|
emoji: "🚗",
|
||||||
|
colorId: "army",
|
||||||
|
address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
|
||||||
|
walletType: Constants.generatedWalletType,
|
||||||
|
position: 3,
|
||||||
|
assets: [
|
||||||
|
{
|
||||||
|
symbol: "deadbeef",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 1,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 1,
|
||||||
|
symbol: "DBF"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Family (seed)",
|
||||||
|
emoji: "🎨", colorId: "magenta",
|
||||||
|
address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8882",
|
||||||
|
walletType: Constants.seedWalletType,
|
||||||
|
position: 1,
|
||||||
|
assets: [
|
||||||
|
{
|
||||||
|
symbol: "Aave",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 6,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 42,
|
||||||
|
symbol: "AAVE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol: "dai",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 2,
|
||||||
|
stripTrailingZeroes: true,
|
||||||
|
amount: 120.123,
|
||||||
|
symbol: "DAI"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Tag Heuer (watch)",
|
||||||
|
emoji: "⌚",
|
||||||
|
colorId: "copper",
|
||||||
|
address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8883",
|
||||||
|
walletType: Constants.watchWalletType,
|
||||||
|
position: 2,
|
||||||
|
assets: [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Fab (key)",
|
||||||
|
emoji: "⌚",
|
||||||
|
colorId: Constants.walletAccountColors.camel,
|
||||||
|
address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8884",
|
||||||
|
walletType: Constants.keyWalletType,
|
||||||
|
position: 4,
|
||||||
|
assets: [
|
||||||
|
{
|
||||||
|
symbol: "socks",
|
||||||
|
enabledNetworkBalance: {
|
||||||
|
displayDecimals: 2,
|
||||||
|
stripTrailingZeroes: false,
|
||||||
|
amount: 3.5,
|
||||||
|
symbol: "SOX"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Component.onCompleted: append(data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,11 +91,13 @@ add_library(StatusQ SHARED
|
||||||
src/plugin.cpp
|
src/plugin.cpp
|
||||||
include/StatusQ/QClipboardProxy.h
|
include/StatusQ/QClipboardProxy.h
|
||||||
include/StatusQ/modelutilsinternal.h
|
include/StatusQ/modelutilsinternal.h
|
||||||
|
include/StatusQ/permissionutilsinternal.h
|
||||||
include/StatusQ/rxvalidator.h
|
include/StatusQ/rxvalidator.h
|
||||||
include/StatusQ/statussyntaxhighlighter.h
|
include/StatusQ/statussyntaxhighlighter.h
|
||||||
include/StatusQ/statuswindow.h
|
include/StatusQ/statuswindow.h
|
||||||
src/QClipboardProxy.cpp
|
src/QClipboardProxy.cpp
|
||||||
src/modelutilsinternal.cpp
|
src/modelutilsinternal.cpp
|
||||||
|
src/permissionutilsinternal.cpp
|
||||||
src/rxvalidator.cpp
|
src/rxvalidator.cpp
|
||||||
src/statussyntaxhighlighter.cpp
|
src/statussyntaxhighlighter.cpp
|
||||||
src/statuswindow.cpp
|
src/statuswindow.cpp
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
|
class PermissionUtilsInternal : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PermissionUtilsInternal(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
//!< traverse the permissions @p model, and look for unique token keys recursively under holdingsListModel->key
|
||||||
|
Q_INVOKABLE QStringList getUniquePermissionTokenKeys(QAbstractItemModel *model) const;
|
||||||
|
|
||||||
|
//!< traverse the permissions @p model, and look for unique channel keys recursively under channelsListModel->key; filtering out @permissionTypes ([PermissionTypes.Type.FOO])
|
||||||
|
Q_INVOKABLE QStringList getUniquePermissionChannels(QAbstractItemModel *model, const QList<int> &permissionTypes = {}) const;
|
||||||
|
};
|
|
@ -359,8 +359,8 @@ Rectangle {
|
||||||
id: tagsScrollView
|
id: tagsScrollView
|
||||||
visible: tagsRepeater.count > 0
|
visible: tagsRepeater.count > 0
|
||||||
anchors.top: statusListItemTertiaryTitle.bottom
|
anchors.top: statusListItemTertiaryTitle.bottom
|
||||||
anchors.topMargin: visible ? 8 : 0
|
anchors.topMargin: visible ? 2 : 0
|
||||||
width: Math.min(statusListItemTagsSlotInline.width, statusListItemTagsSlotInline.availableWidth)
|
width: Math.min(statusListItemTagsSlotInline.width, statusListItemTagsSlotInline.availableWidth, parent.width)
|
||||||
height: visible ? contentHeight : 0
|
height: visible ? contentHeight : 0
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ Rectangle {
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.top: tagsScrollView.bottom
|
anchors.top: tagsScrollView.bottom
|
||||||
anchors.topMargin: visible ? 8 : 0
|
anchors.topMargin: visible ? 4 : 0
|
||||||
width: parent.width
|
width: parent.width
|
||||||
visible: !!root.beneathTagsIcon || !!root.beneathTagsTitle
|
visible: !!root.beneathTagsIcon || !!root.beneathTagsTitle
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
|
@ -2,4 +2,3 @@ module StatusQ.Components.private
|
||||||
|
|
||||||
StatusImageMessage 0.1 statusMessage/StatusImageMessage.qml
|
StatusImageMessage 0.1 statusMessage/StatusImageMessage.qml
|
||||||
StatusMessageImageAlbum 0.1 statusMessage/StatusMessageImageAlbum.qml
|
StatusMessageImageAlbum 0.1 statusMessage/StatusMessageImageAlbum.qml
|
||||||
StatusBaseDateInput 0.1 dateInput/StatusBaseDateInput.qml
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ CheckBox {
|
||||||
: root.indicator.width) : 0
|
: root.indicator.width) : 0
|
||||||
rightPadding: !root.leftSide? (!!root.text ? root.indicator.width + root.spacing
|
rightPadding: !root.leftSide? (!!root.text ? root.indicator.width + root.spacing
|
||||||
: root.indicator.width) : 0
|
: root.indicator.width) : 0
|
||||||
|
visible: !!text
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
|
|
|
@ -21,6 +21,7 @@ RadioButton {
|
||||||
Large
|
Large
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1.0 : 0.3
|
||||||
font.family: Theme.palette.baseFont.name
|
font.family: Theme.palette.baseFont.name
|
||||||
|
|
||||||
indicator: Rectangle {
|
indicator: Rectangle {
|
||||||
|
@ -47,5 +48,6 @@ RadioButton {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
leftPadding: root.indicator && !root.mirrored ? root.indicator.width + root.spacing : 0
|
leftPadding: root.indicator && !root.mirrored ? root.indicator.width + root.spacing : 0
|
||||||
rightPadding: root.indicator && root.mirrored ? root.indicator.width + root.spacing : 0
|
rightPadding: root.indicator && root.mirrored ? root.indicator.width + root.spacing : 0
|
||||||
|
visible: !!text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ QtObject {
|
||||||
return num.toString().split('.')[1].length
|
return num.toString().split('.')[1].length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function stripTrailingZeroes(numStr, locale) {
|
function stripTrailingZeroes(numStr, locale) {
|
||||||
let regEx = locale.decimalPoint == "." ? /(\.[0-9]*[1-9])0+$|\.0*$/ : /(\,[0-9]*[1-9])0+$|\,0*$/
|
let regEx = locale.decimalPoint == "." ? /(\.[0-9]*[1-9])0+$|\.0*$/ : /(\,[0-9]*[1-9])0+$|\,0*$/
|
||||||
return numStr.replace(regEx, '$1')
|
return numStr.replace(regEx, '$1')
|
||||||
|
@ -101,8 +102,6 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function currencyAmountToLocaleString(currencyAmount, options = null, locale = null) {
|
function currencyAmountToLocaleString(currencyAmount, options = null, locale = null) {
|
||||||
locale = locale || Qt.locale()
|
|
||||||
|
|
||||||
if (!currencyAmount) {
|
if (!currencyAmount) {
|
||||||
return qsTr("N/A")
|
return qsTr("N/A")
|
||||||
}
|
}
|
||||||
|
@ -114,6 +113,8 @@ QtObject {
|
||||||
if (typeof currencyAmount.amount === "undefined")
|
if (typeof currencyAmount.amount === "undefined")
|
||||||
return qsTr("N/A")
|
return qsTr("N/A")
|
||||||
|
|
||||||
|
locale = locale || Qt.locale()
|
||||||
|
|
||||||
// Parse options
|
// Parse options
|
||||||
var optNoSymbol = false
|
var optNoSymbol = false
|
||||||
var optRawAmount = false
|
var optRawAmount = false
|
||||||
|
@ -141,7 +142,7 @@ QtObject {
|
||||||
if (currencyAmount.amount > 0 && currencyAmount.amount < minAmount && !optRawAmount)
|
if (currencyAmount.amount > 0 && currencyAmount.amount < minAmount && !optRawAmount)
|
||||||
{
|
{
|
||||||
// Handle amounts smaller than resolution
|
// Handle amounts smaller than resolution
|
||||||
amountStr = "<%1".arg(numberToLocaleString(minAmount, displayDecimals, locale))
|
amountStr = "<%1".arg(numberToLocaleString(minAmount, optDisplayDecimals, locale))
|
||||||
} else {
|
} else {
|
||||||
var amount
|
var amount
|
||||||
var displayDecimals
|
var displayDecimals
|
||||||
|
@ -158,11 +159,10 @@ QtObject {
|
||||||
// For normal numbers, we show the whole integral part and as many decimal places not
|
// For normal numbers, we show the whole integral part and as many decimal places not
|
||||||
// not to exceed the maximum
|
// not to exceed the maximum
|
||||||
amount = currencyAmount.amount
|
amount = currencyAmount.amount
|
||||||
// For numbers over 1M , dont show decimal places
|
// For numbers over 1M , dont show decimal places
|
||||||
if(numIntegerDigits > maxDigitsToShowDecimal) {
|
if(numIntegerDigits > maxDigitsToShowDecimal) {
|
||||||
displayDecimals = 0
|
displayDecimals = 0
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
displayDecimals = Math.min(optDisplayDecimals, Math.max(0, maxDigits - numIntegerDigits))
|
displayDecimals = Math.min(optDisplayDecimals, Math.max(0, maxDigits - numIntegerDigits))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,8 @@ Image {
|
||||||
if(icon.startsWith("data:image/") || icon.startsWith("https://") || icon.startsWith("qrc:/") || icon.startsWith("file:/")) {
|
if(icon.startsWith("data:image/") || icon.startsWith("https://") || icon.startsWith("qrc:/") || icon.startsWith("file:/")) {
|
||||||
//raw image data
|
//raw image data
|
||||||
source = icon
|
source = icon
|
||||||
objectName = "custom-icon"
|
objectName = "custom-icon"
|
||||||
}
|
} else if (icon !== "") {
|
||||||
else if (icon !== "") {
|
|
||||||
source = "../../assets/img/icons/" + icon+ ".svg";
|
source = "../../assets/img/icons/" + icon+ ".svg";
|
||||||
objectName = icon + "-icon"
|
objectName = icon + "-icon"
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ QtObject {
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function modelToFlatArray(model, role) {
|
||||||
|
return modelToArray(model, [role]).map(entry => entry[role])
|
||||||
|
}
|
||||||
|
|
||||||
function indexOf(model, role, key) {
|
function indexOf(model, role, key) {
|
||||||
const count = model.rowCount()
|
const count = model.rowCount()
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ QtObject {
|
||||||
case OperatorsUtils.Operators.None:
|
case OperatorsUtils.Operators.None:
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.15
|
||||||
import QtQml.Models 2.14
|
import QtQml.Models 2.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
@ -28,6 +29,14 @@ Dialog {
|
||||||
margins: 64
|
margins: 64
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
// workaround for https://bugreports.qt.io/browse/QTBUG-87804
|
||||||
|
Binding on margins {
|
||||||
|
id: workaroundBinding
|
||||||
|
|
||||||
|
when: false
|
||||||
|
restoreMode: Binding.RestoreBindingOrValue
|
||||||
|
}
|
||||||
|
|
||||||
standardButtons: Dialog.Cancel | Dialog.Ok
|
standardButtons: Dialog.Cancel | Dialog.Ok
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
Overlay.modal: Rectangle {
|
||||||
|
|
|
@ -23,7 +23,7 @@ StatusModal {
|
||||||
visible: replaceItem || stackLayout.currentIndex > 0
|
visible: replaceItem || stackLayout.currentIndex > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (replaceItem) {
|
if (replaceItem) {
|
||||||
replaceItem = null;
|
replaceItem = undefined; // unload the replaceItem
|
||||||
} else {
|
} else {
|
||||||
let prevAction = stackLayout.currentItem.prevAction
|
let prevAction = stackLayout.currentItem.prevAction
|
||||||
stackLayout.currentIndex--;
|
stackLayout.currentIndex--;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "StatusQ/modelutilsinternal.h"
|
#include "StatusQ/modelutilsinternal.h"
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
ModelUtilsInternal::ModelUtilsInternal(QObject* parent)
|
ModelUtilsInternal::ModelUtilsInternal(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -16,7 +17,6 @@ QStringList ModelUtilsInternal::roleNames(QAbstractItemModel *model) const
|
||||||
return {roles.cbegin(), roles.cend()};
|
return {roles.cbegin(), roles.cend()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int ModelUtilsInternal::roleByName(QAbstractItemModel* model,
|
int ModelUtilsInternal::roleByName(QAbstractItemModel* model,
|
||||||
const QString &roleName) const
|
const QString &roleName) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
#include "StatusQ/permissionutilsinternal.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int roleByName(QAbstractItemModel* model, const QString &roleName)
|
||||||
|
{
|
||||||
|
if (!model)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return model->roleNames().key(roleName.toUtf8(), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionUtilsInternal::PermissionUtilsInternal(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PermissionUtilsInternal::getUniquePermissionTokenKeys(QAbstractItemModel* model) const
|
||||||
|
{
|
||||||
|
if (!model)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto role = roleByName(model, QStringLiteral("holdingsListModel"));
|
||||||
|
if (role == -1) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Requested roleName 'holdingsListModel' not found!";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<QString> result; // unique, sorted by default
|
||||||
|
|
||||||
|
const auto permissionsCount = model->rowCount();
|
||||||
|
for (int i = 0; i < permissionsCount; i++) {
|
||||||
|
const auto isPrivate = model->data(model->index(i, 0), roleByName(model, QStringLiteral("isPrivate"))).toBool();
|
||||||
|
if (isPrivate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto holdings = model->data(model->index(i, 0), role);
|
||||||
|
if (holdings.isValid() && !holdings.isNull()) {
|
||||||
|
const auto holdingItems = holdings.value<QAbstractItemModel*>();
|
||||||
|
if (!holdingItems) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Unable to cast 'holdingsListModel' to QAbstractItemModel *!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto holdingItemsCount = holdingItems->rowCount();
|
||||||
|
for (int j = 0; j < holdingItemsCount; j++) {
|
||||||
|
const auto keyRole = roleByName(holdingItems, QStringLiteral("key"));
|
||||||
|
if (keyRole == -1) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Requested roleName 'key' not found!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.insert(holdingItems->data(holdingItems->index(j, 0), keyRole).toString().toUpper());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result.cbegin(), result.cend()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO return a QVariantMap (https://github.com/status-im/status-desktop/issues/11481) with key->channelName
|
||||||
|
QStringList PermissionUtilsInternal::getUniquePermissionChannels(QAbstractItemModel* model, const QList<int> &permissionTypes) const
|
||||||
|
{
|
||||||
|
if (!model)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto role = roleByName(model, QStringLiteral("channelsListModel"));
|
||||||
|
if (role == -1) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Requested roleName 'channelsListModel' not found!";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto permissionTypeRole = roleByName(model, QStringLiteral("permissionType"));
|
||||||
|
|
||||||
|
std::set<QString> result; // unique, sorted by default
|
||||||
|
|
||||||
|
const auto permissionsCount = model->rowCount();
|
||||||
|
for (int i = 0; i < permissionsCount; i++) {
|
||||||
|
if (!permissionTypes.isEmpty()) {
|
||||||
|
const auto permissionType = model->data(model->index(i, 0), permissionTypeRole).toInt();
|
||||||
|
if (!permissionTypes.contains(permissionType))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto channels = model->data(model->index(i, 0), role);
|
||||||
|
if (channels.isValid() && !channels.isNull()) {
|
||||||
|
const auto channelItems = channels.value<QAbstractItemModel *>();
|
||||||
|
if (!channelItems) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Unable to cast 'channelsListModel' to QAbstractItemModel *!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto channelItemsCount = channelItems->rowCount();
|
||||||
|
for (int j = 0; j < channelItemsCount; j++) {
|
||||||
|
const auto keyRole = roleByName(channelItems, QStringLiteral("key"));
|
||||||
|
if (keyRole == -1) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Requested roleName 'key' not found!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.insert(channelItems->data(channelItems->index(j, 0), keyRole).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result.cbegin(), result.cend()};
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "StatusQ/QClipboardProxy.h"
|
#include "StatusQ/QClipboardProxy.h"
|
||||||
#include "StatusQ/modelutilsinternal.h"
|
#include "StatusQ/modelutilsinternal.h"
|
||||||
|
#include "StatusQ/permissionutilsinternal.h"
|
||||||
#include "StatusQ/rxvalidator.h"
|
#include "StatusQ/rxvalidator.h"
|
||||||
#include "StatusQ/statussyntaxhighlighter.h"
|
#include "StatusQ/statussyntaxhighlighter.h"
|
||||||
#include "StatusQ/statuswindow.h"
|
#include "StatusQ/statuswindow.h"
|
||||||
|
@ -26,6 +27,10 @@ public:
|
||||||
qmlRegisterSingletonType<ModelUtilsInternal>(
|
qmlRegisterSingletonType<ModelUtilsInternal>(
|
||||||
"StatusQ.Internal", 0, 1, "ModelUtils", &ModelUtilsInternal::qmlInstance);
|
"StatusQ.Internal", 0, 1, "ModelUtils", &ModelUtilsInternal::qmlInstance);
|
||||||
|
|
||||||
|
qmlRegisterSingletonType<PermissionUtilsInternal>("StatusQ.Internal", 0, 1, "PermissionUtils", [](QQmlEngine *, QJSEngine *) {
|
||||||
|
return new PermissionUtilsInternal;
|
||||||
|
});
|
||||||
|
|
||||||
QZXing::registerQMLTypes();
|
QZXing::registerQMLTypes();
|
||||||
qqsfpm::registerTypes();
|
qqsfpm::registerTypes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import "stores"
|
||||||
import AppLayouts.Communities.popups 1.0
|
import AppLayouts.Communities.popups 1.0
|
||||||
|
|
||||||
import AppLayouts.Chat.stores 1.0
|
import AppLayouts.Chat.stores 1.0
|
||||||
|
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: root
|
id: root
|
||||||
|
@ -176,8 +177,14 @@ StackLayout {
|
||||||
|
|
||||||
property string communityId
|
property string communityId
|
||||||
|
|
||||||
|
loginType: root.rootStore.loginType
|
||||||
|
walletAccountsModel: WalletStore.RootStore.receiveAccounts
|
||||||
|
permissionsModel: root.permissionsStore.permissionsModel
|
||||||
|
assetsModel: root.rootStore.assetsModel
|
||||||
|
collectiblesModel: root.rootStore.collectiblesModel
|
||||||
|
|
||||||
onJoined: {
|
onJoined: {
|
||||||
root.rootStore.requestToJoinCommunityWithAuthentication(root.rootStore.userProfileInst.name)
|
root.rootStore.requestToJoinCommunityWithAuthentication(root.rootStore.userProfileInst.name, sharedAddresses, airdropAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancelMembershipRequest: {
|
onCancelMembershipRequest: {
|
||||||
|
|
|
@ -378,8 +378,8 @@ QtObject {
|
||||||
return communitiesModuleInst.spectateCommunity(id, ensName)
|
return communitiesModuleInst.spectateCommunity(id, ensName)
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestToJoinCommunityWithAuthentication(ensName) {
|
function requestToJoinCommunityWithAuthentication(ensName, addressesToShare = [], airdropAddress = "") {
|
||||||
chatCommunitySectionModule.requestToJoinCommunityWithAuthentication(ensName)
|
chatCommunitySectionModule.requestToJoinCommunityWithAuthenticationWithSharedAddresses(ensName, JSON.stringify(addressesToShare), airdropAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
function userCanJoin(id) {
|
function userCanJoin(id) {
|
||||||
|
@ -475,7 +475,7 @@ QtObject {
|
||||||
const userCanJoin = userCanJoin(result.communityId)
|
const userCanJoin = userCanJoin(result.communityId)
|
||||||
// TODO find what to do when you can't join
|
// TODO find what to do when you can't join
|
||||||
if (userCanJoin) {
|
if (userCanJoin) {
|
||||||
requestToJoinCommunityWithAuthentication(userProfileInst.preferredName)
|
requestToJoinCommunityWithAuthentication(userProfileInst.preferredName) // FIXME what addresses to share?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -608,9 +608,9 @@ QtObject {
|
||||||
|
|
||||||
if(userProfileInst.usingBiometricLogin)
|
if(userProfileInst.usingBiometricLogin)
|
||||||
return Constants.LoginType.Biometrics
|
return Constants.LoginType.Biometrics
|
||||||
else if(userProfileInst.isKeycardUser)
|
if(userProfileInst.isKeycardUser)
|
||||||
return Constants.LoginType.Keycard
|
return Constants.LoginType.Keycard
|
||||||
else return Constants.LoginType.Password
|
return Constants.LoginType.Password
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property Connections communitiesModuleConnections: Connections {
|
readonly property Connections communitiesModuleConnections: Connections {
|
||||||
|
|
|
@ -26,7 +26,7 @@ StatusSectionLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var contactsStore
|
property var contactsStore
|
||||||
property bool hasAddedContacts: root.contactsStore.myContactsModel.count > 0
|
property bool hasAddedContacts: contactsStore.myContactsModel.count > 0
|
||||||
|
|
||||||
property RootStore rootStore
|
property RootStore rootStore
|
||||||
property var createChatPropertiesStore
|
property var createChatPropertiesStore
|
||||||
|
@ -36,7 +36,7 @@ StatusSectionLayout {
|
||||||
property var stickersPopup
|
property var stickersPopup
|
||||||
property bool stickersLoaded: false
|
property bool stickersLoaded: false
|
||||||
|
|
||||||
readonly property var chatContentModule: root.rootStore.currentChatContentModule() || null
|
readonly property var chatContentModule: rootStore.currentChatContentModule() || null
|
||||||
readonly property bool viewOnlyPermissionsSatisfied: chatContentModule.viewOnlyPermissionsSatisfied
|
readonly property bool viewOnlyPermissionsSatisfied: chatContentModule.viewOnlyPermissionsSatisfied
|
||||||
readonly property bool viewAndPostPermissionsSatisfied: chatContentModule.viewAndPostPermissionsSatisfied
|
readonly property bool viewAndPostPermissionsSatisfied: chatContentModule.viewAndPostPermissionsSatisfied
|
||||||
property bool hasViewOnlyPermissions: false
|
property bool hasViewOnlyPermissions: false
|
||||||
|
|
|
@ -6,7 +6,7 @@ import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
enum Type {
|
enum Type {
|
||||||
None, Admin, Member, Read, ViewAndPost
|
None, Admin, Member, Read, ViewAndPost, Moderator
|
||||||
}
|
}
|
||||||
|
|
||||||
function getName(type) {
|
function getName(type) {
|
||||||
|
@ -15,12 +15,12 @@ QtObject {
|
||||||
return qsTr("Become admin")
|
return qsTr("Become admin")
|
||||||
case PermissionTypes.Type.Member:
|
case PermissionTypes.Type.Member:
|
||||||
return qsTr("Become member")
|
return qsTr("Become member")
|
||||||
case PermissionTypes.Type.Moderator:
|
|
||||||
return qsTr("Moderate")
|
|
||||||
case PermissionTypes.Type.ViewAndPost:
|
|
||||||
return qsTr("View and post")
|
|
||||||
case PermissionTypes.Type.Read:
|
case PermissionTypes.Type.Read:
|
||||||
return qsTr("View only")
|
return qsTr("View only")
|
||||||
|
case PermissionTypes.Type.ViewAndPost:
|
||||||
|
return qsTr("View and post")
|
||||||
|
case PermissionTypes.Type.Moderator:
|
||||||
|
return qsTr("Moderate")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
@ -32,12 +32,12 @@ QtObject {
|
||||||
return "admin"
|
return "admin"
|
||||||
case PermissionTypes.Type.Member:
|
case PermissionTypes.Type.Member:
|
||||||
return "in-contacts"
|
return "in-contacts"
|
||||||
case PermissionTypes.Type.Moderator:
|
|
||||||
return "arbitrator"
|
|
||||||
case PermissionTypes.Type.ViewAndPost:
|
case PermissionTypes.Type.ViewAndPost:
|
||||||
return "edit"
|
return "edit"
|
||||||
case PermissionTypes.Type.Read:
|
case PermissionTypes.Type.Read:
|
||||||
return "show"
|
return "show"
|
||||||
|
case PermissionTypes.Type.Moderator:
|
||||||
|
return "arbitrator"
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -4,6 +4,7 @@ import QtQml 2.14
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Utils 0.1
|
import StatusQ.Core.Utils 0.1
|
||||||
|
import StatusQ.Internal 0.1 as Internal
|
||||||
|
|
||||||
import AppLayouts.Communities.controls 1.0
|
import AppLayouts.Communities.controls 1.0
|
||||||
|
|
||||||
|
@ -63,6 +64,15 @@ QtObject {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUniquePermissionTokenKeys(model) {
|
||||||
|
return Internal.PermissionUtils.getUniquePermissionTokenKeys(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUniquePermissionChannels(model, permissionsTypesArray = []) {
|
||||||
|
// TODO return a QVariantMap (https://github.com/status-im/status-desktop/issues/11481)
|
||||||
|
return Internal.PermissionUtils.getUniquePermissionChannels(model, permissionsTypesArray)
|
||||||
|
}
|
||||||
|
|
||||||
function setHoldingsTextFormat(type, name, amount) {
|
function setHoldingsTextFormat(type, name, amount) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case HoldingTypes.Type.Asset:
|
case HoldingTypes.Type.Asset:
|
||||||
|
|
|
@ -27,10 +27,6 @@ Control {
|
||||||
|
|
||||||
// By design values:
|
// By design values:
|
||||||
readonly property int defaultHoldingsSpacing: 8
|
readonly property int defaultHoldingsSpacing: 8
|
||||||
|
|
||||||
function holdingsTextFormat(name, amount) {
|
|
||||||
return PermissionsHelpers.setHoldingsTextFormat(HoldingTypes.Type.Asset, name, amount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
|
@ -83,7 +79,7 @@ Control {
|
||||||
asset.color: asset.isImage ? "transparent" : titleText.color
|
asset.color: asset.isImage ? "transparent" : titleText.color
|
||||||
closeButtonVisible: false
|
closeButtonVisible: false
|
||||||
titleText.color: model.available ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
|
titleText.color: model.available ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
|
||||||
bgColor: model.available ? Theme.palette.primaryColor2 :Theme.palette.dangerColor2
|
bgColor: model.available ? Theme.palette.primaryColor2 : Theme.palette.dangerColor2
|
||||||
titleText.font.pixelSize: 15
|
titleText.font.pixelSize: 15
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,15 +92,23 @@ Control {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property var moderatePermissionsModel: SortFilterProxyModel {
|
||||||
|
sourceModel: root.moderateHoldingsModel
|
||||||
|
filters: [
|
||||||
|
ExpressionFilter {
|
||||||
|
expression: d.filterPermissions(model)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 35 // default by design
|
padding: 35 // default by design
|
||||||
spacing: 32 // default by design
|
spacing: 32 // default by design
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
id: column
|
id: column
|
||||||
|
|
||||||
spacing: root.spacing
|
spacing: root.spacing
|
||||||
|
|
||||||
component CustomHoldingsListPanel: HoldingsListPanel {
|
component CustomHoldingsListPanel: HoldingsListPanel {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
@ -123,25 +131,23 @@ Control {
|
||||||
CustomHoldingsListPanel {
|
CustomHoldingsListPanel {
|
||||||
visible: !root.joinCommunity && d.viewOnlyPermissionsModel.count > 0
|
visible: !root.joinCommunity && d.viewOnlyPermissionsModel.count > 0
|
||||||
introText: root.requiresRequest ?
|
introText: root.requiresRequest ?
|
||||||
qsTr("To view the #<b>%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
|
qsTr("To view the <b>#%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
|
||||||
qsTr("To view the #<b>%1</b> channel you need to hold").arg(root.channelName)
|
qsTr("To view the <b>#%1</b> channel you need to hold").arg(root.channelName)
|
||||||
model: d.viewOnlyPermissionsModel
|
model: d.viewOnlyPermissionsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomHoldingsListPanel {
|
CustomHoldingsListPanel {
|
||||||
visible: !root.joinCommunity && d.viewAndPostPermissionsModel.count > 0
|
visible: !root.joinCommunity && d.viewAndPostPermissionsModel.count > 0
|
||||||
introText: root.requiresRequest ?
|
introText: root.requiresRequest ?
|
||||||
qsTr("To view and post in the #<b>%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
|
qsTr("To view and post in the <b>#%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
|
||||||
qsTr("To view and post in the #<b>%1</b> channel you need to hold").arg(root.channelName)
|
qsTr("To view and post in the <b>#%1</b> channel you need to hold").arg(root.channelName)
|
||||||
model: d.viewAndPostPermissionsModel
|
model: d.viewAndPostPermissionsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
HoldingsListPanel {
|
CustomHoldingsListPanel {
|
||||||
Layout.fillWidth: true
|
visible: !root.joinCommunity && d.moderatePermissionsModel.count > 0
|
||||||
spacing: root.spacing
|
introText: qsTr("To moderate in the <b>#%1</b> channel you need to hold").arg(root.channelName)
|
||||||
visible: !root.joinCommunity && !!d.moderateHoldings
|
model: d.moderatePermissionsModel
|
||||||
introText: qsTr("To moderate in the <b>%1</b> channel you need to hold").arg(root.channelName)
|
|
||||||
model: d.moderateHoldingsModel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusButton {
|
StatusButton {
|
||||||
|
@ -150,7 +156,7 @@ Control {
|
||||||
text: root.isInvitationPending ? d.getInvitationPendingText() : d.getRevealAddressText()
|
text: root.isInvitationPending ? d.getInvitationPendingText() : d.getRevealAddressText()
|
||||||
icon.name: root.isInvitationPending ? "" : Constants.authenticationIconByType[root.loginType]
|
icon.name: root.isInvitationPending ? "" : Constants.authenticationIconByType[root.loginType]
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
enabled: root.requirementsMet || d.communityPermissionsModel.count == 0
|
enabled: root.requirementsMet || d.communityPermissionsModel.count === 0
|
||||||
onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.revealAddressClicked()
|
onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.revealAddressClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,4 +169,3 @@ Control {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
|
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 utils 1.0
|
||||||
|
|
||||||
|
StatusListView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool hasPermissions
|
||||||
|
property var uniquePermissionTokenKeys
|
||||||
|
|
||||||
|
// read/write properties
|
||||||
|
property string selectedAirdropAddress: selectedSharedAddresses.length ? selectedSharedAddresses[0] : ""
|
||||||
|
property var selectedSharedAddresses: count ? ModelUtils.modelToFlatArray(model, "address") : []
|
||||||
|
|
||||||
|
leftMargin: d.absLeftMargin
|
||||||
|
topMargin: Style.current.padding
|
||||||
|
rightMargin: Style.current.padding
|
||||||
|
bottomMargin: Style.current.padding
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
// UI
|
||||||
|
readonly property int absLeftMargin: 12
|
||||||
|
|
||||||
|
readonly property ButtonGroup airdropGroup: ButtonGroup {
|
||||||
|
exclusive: true
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property ButtonGroup addressesGroup: ButtonGroup {
|
||||||
|
exclusive: false
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFirstAvailableAirdropAddress() {
|
||||||
|
root.selectedAirdropAddress = ModelUtils.modelToFlatArray(root.model, "address").find(address => selectedSharedAddresses.includes(address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
delegate: StatusListItem {
|
||||||
|
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
||||||
|
statusListItemTitle.font.weight: Font.Medium
|
||||||
|
title: model.name
|
||||||
|
tertiaryTitle: !walletAccountAssetsModel.count && root.hasPermissions ? qsTr("No relevant tokens") : ""
|
||||||
|
|
||||||
|
tagsModel: SortFilterProxyModel {
|
||||||
|
id: walletAccountAssetsModel
|
||||||
|
sourceModel: model.assets
|
||||||
|
|
||||||
|
function filterPredicate(modelData) {
|
||||||
|
return root.uniquePermissionTokenKeys.includes(modelData.symbol.toUpperCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
filters: ExpressionFilter {
|
||||||
|
expression: walletAccountAssetsModel.filterPredicate(model)
|
||||||
|
}
|
||||||
|
sorters: ExpressionSorter {
|
||||||
|
expression: {
|
||||||
|
return modelLeft.enabledNetworkBalance.amount > modelRight.enabledNetworkBalance.amount // descending, biggest first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusListItemInlineTagsSlot.spacing: Style.current.padding
|
||||||
|
tagsDelegate: Row {
|
||||||
|
spacing: 4
|
||||||
|
StatusRoundedImage {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
image.source: Constants.tokenIcon(model.symbol.toUpperCase())
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: Theme.tertiaryTextFontSize
|
||||||
|
text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asset.color: !!model.colorId ? Utils.getColorForId(model.colorId): ""
|
||||||
|
asset.emoji: model.emoji
|
||||||
|
asset.name: !model.emoji ? "filled-account": ""
|
||||||
|
asset.letterSize: 14
|
||||||
|
asset.isLetterIdenticon: !!model.emoji
|
||||||
|
asset.isImage: asset.isLetterIdenticon
|
||||||
|
|
||||||
|
components: [
|
||||||
|
StatusFlatButton {
|
||||||
|
ButtonGroup.group: d.airdropGroup
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
icon.name: "airdrop"
|
||||||
|
icon.color: hovered ? Theme.palette.primaryColor3 :
|
||||||
|
checked ? Theme.palette.primaryColor1 : disabledTextColor
|
||||||
|
checkable: true
|
||||||
|
checked: model.address === root.selectedAirdropAddress
|
||||||
|
enabled: shareAddressCheckbox.checked && root.selectedSharedAddresses.length > 1 // last cannot be unchecked
|
||||||
|
visible: shareAddressCheckbox.checked
|
||||||
|
opacity: enabled ? 1.0 : 0.3
|
||||||
|
onCheckedChanged: if (checked) root.selectedAirdropAddress = model.address
|
||||||
|
|
||||||
|
StatusToolTip {
|
||||||
|
text: qsTr("Use this address for any Community airdrops")
|
||||||
|
visible: parent.hovered
|
||||||
|
delay: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StatusCheckBox {
|
||||||
|
id: shareAddressCheckbox
|
||||||
|
ButtonGroup.group: d.addressesGroup
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
checkable: true
|
||||||
|
checked: root.selectedSharedAddresses.includes(model.address)
|
||||||
|
enabled: !(root.selectedSharedAddresses.length === 1 && checked) // last cannot be unchecked
|
||||||
|
onToggled: {
|
||||||
|
// handle selected addresses
|
||||||
|
const index = root.selectedSharedAddresses.indexOf(model.address)
|
||||||
|
const selectedSharedAddressesCopy = Object.assign([], root.selectedSharedAddresses) // deep copy
|
||||||
|
if (index === -1) {
|
||||||
|
selectedSharedAddressesCopy.push(model.address)
|
||||||
|
} else {
|
||||||
|
selectedSharedAddressesCopy.splice(index, 1)
|
||||||
|
}
|
||||||
|
root.selectedSharedAddresses = selectedSharedAddressesCopy
|
||||||
|
|
||||||
|
// switch to next available airdrop address when unchecking
|
||||||
|
if (!checked && model.address === root.selectedAirdropAddress) {
|
||||||
|
d.selectFirstAvailableAirdropAddress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
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.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
|
||||||
|
|
||||||
|
property bool isEditMode
|
||||||
|
|
||||||
|
required property string communityName
|
||||||
|
required property string communityIcon
|
||||||
|
property int loginType: Constants.LoginType.Password
|
||||||
|
|
||||||
|
required property var walletAccountsModel // name, address, emoji, colorId, assets
|
||||||
|
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(communityName)
|
||||||
|
: qsTr("Select addresses to share with %1").arg(communityName)
|
||||||
|
|
||||||
|
readonly property var buttons: ObjectModel {
|
||||||
|
StatusFlatButton {
|
||||||
|
visible: root.isEditMode
|
||||||
|
borderColor: Theme.palette.baseColor2
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
StatusButton {
|
||||||
|
enabled: root.selectedSharedAddresses.length && root.selectedAirdropAddress
|
||||||
|
visible: root.isEditMode
|
||||||
|
icon.name: Constants.authenticationIconByType[root.loginType]
|
||||||
|
text: qsTr("Save changes")
|
||||||
|
onClicked: {
|
||||||
|
// TODO connect to backend
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusButton {
|
||||||
|
visible: !root.isEditMode
|
||||||
|
enabled: root.selectedAirdropAddress && root.selectedSharedAddresses.length
|
||||||
|
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
|
||||||
|
|
||||||
|
readonly property string selectedAirdropAddress: accountSelector.selectedAirdropAddress
|
||||||
|
readonly property var selectedSharedAddresses: accountSelector.selectedSharedAddresses
|
||||||
|
|
||||||
|
signal close()
|
||||||
|
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||||
|
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
// internal logic
|
||||||
|
readonly property bool hasPermissions: root.permissionsModel && root.permissionsModel.count
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
// addresses
|
||||||
|
SharedAddressesAccountSelector {
|
||||||
|
id: accountSelector
|
||||||
|
hasPermissions: d.hasPermissions
|
||||||
|
uniquePermissionTokenKeys: PermissionsHelpers.getUniquePermissionTokenKeys(root.permissionsModel)
|
||||||
|
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
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
sourceModel: root.walletAccountsModel
|
||||||
|
filters: ValueFilter {
|
||||||
|
roleName: "walletType"
|
||||||
|
value: Constants.watchWalletType
|
||||||
|
inverted: true
|
||||||
|
}
|
||||||
|
sorters: [
|
||||||
|
ExpressionSorter {
|
||||||
|
function isGenerated(modelData) {
|
||||||
|
return modelData.walletType === Constants.generatedWalletType
|
||||||
|
}
|
||||||
|
|
||||||
|
expression: {
|
||||||
|
return isGenerated(modelLeft)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RoleSorter {
|
||||||
|
roleName: "position"
|
||||||
|
},
|
||||||
|
RoleSorter {
|
||||||
|
roleName: "name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// divider with top rounded corners + drop shadow
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Style.current.padding * 2
|
||||||
|
color: Theme.palette.baseColor2
|
||||||
|
radius: Style.current.padding
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor3
|
||||||
|
visible: d.hasPermissions
|
||||||
|
|
||||||
|
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
|
||||||
|
permissionsModel: root.permissionsModel
|
||||||
|
assetsModel: root.assetsModel
|
||||||
|
collectiblesModel: root.collectiblesModel
|
||||||
|
communityName: root.communityName
|
||||||
|
communityIcon: root.communityIcon
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: -Style.current.padding // compensate for the half-rounded divider above
|
||||||
|
visible: d.hasPermissions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,437 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import AppLayouts.Communities.controls 1.0
|
||||||
|
import AppLayouts.Communities.views 1.0
|
||||||
|
import AppLayouts.Communities.helpers 1.0
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var permissionsModel
|
||||||
|
property var assetsModel
|
||||||
|
property var collectiblesModel
|
||||||
|
property string communityName
|
||||||
|
property string communityIcon
|
||||||
|
|
||||||
|
implicitHeight: permissionsScrollView.contentHeight - permissionsScrollView.anchors.topMargin
|
||||||
|
color: Theme.palette.baseColor2
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
// UI
|
||||||
|
readonly property int absLeftMargin: 12
|
||||||
|
readonly property color tableBorderColor: Theme.palette.directColor7
|
||||||
|
|
||||||
|
// internal logic
|
||||||
|
readonly property var uniquePermissionChannels:
|
||||||
|
root.permissionsModel && root.permissionsModel.count ?
|
||||||
|
PermissionsHelpers.getUniquePermissionChannels(root.permissionsModel, [PermissionTypes.Type.Read, PermissionTypes.Type.ViewAndPost])
|
||||||
|
: []
|
||||||
|
|
||||||
|
// models
|
||||||
|
readonly property var adminPermissionsModel: SortFilterProxyModel {
|
||||||
|
id: adminPermissionsModel
|
||||||
|
sourceModel: root.permissionsModel
|
||||||
|
function filterPredicate(modelData) {
|
||||||
|
return (modelData.permissionType === Constants.permissionType.admin) &&
|
||||||
|
(modelData.tokenCriteriaMet && !modelData.isPrivate) // admin privs are hidden if criteria not met
|
||||||
|
}
|
||||||
|
filters: ExpressionFilter {
|
||||||
|
expression: adminPermissionsModel.filterPredicate(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readonly property var joinPermissionsModel: SortFilterProxyModel {
|
||||||
|
id: joinPermissionsModel
|
||||||
|
sourceModel: root.permissionsModel
|
||||||
|
function filterPredicate(modelData) {
|
||||||
|
return (modelData.permissionType === Constants.permissionType.member) &&
|
||||||
|
(modelData.tokenCriteriaMet || !modelData.isPrivate)
|
||||||
|
}
|
||||||
|
filters: ExpressionFilter {
|
||||||
|
expression: joinPermissionsModel.filterPredicate(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusScrollView {
|
||||||
|
id: permissionsScrollView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: -Style.current.padding
|
||||||
|
contentWidth: availableWidth
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
|
||||||
|
// header
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
spacing: Style.current.padding
|
||||||
|
StatusRoundedImage {
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
Layout.leftMargin: d.absLeftMargin
|
||||||
|
image.source: root.communityIcon
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Permissions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// permission types
|
||||||
|
PermissionPanel {
|
||||||
|
permissionType: PermissionTypes.Type.Member
|
||||||
|
permissionsModel: d.joinPermissionsModel
|
||||||
|
}
|
||||||
|
PermissionPanel {
|
||||||
|
permissionType: PermissionTypes.Type.Admin
|
||||||
|
permissionsModel: d.adminPermissionsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater { // channel repeater
|
||||||
|
model: d.uniquePermissionChannels // TODO get channelName in addition (https://github.com/status-im/status-desktop/issues/11481)
|
||||||
|
delegate: ChannelPermissionPanel {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component PanelBg: Rectangle {
|
||||||
|
color: Theme.palette.statusListItem.backgroundColor
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor2
|
||||||
|
radius: Style.current.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
component PanelIcon: StatusRoundIcon {
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
asset.name: {
|
||||||
|
switch (permissionType) {
|
||||||
|
case PermissionTypes.Type.Admin:
|
||||||
|
return "admin"
|
||||||
|
case PermissionTypes.Type.Member:
|
||||||
|
return "communities"
|
||||||
|
default:
|
||||||
|
return "channel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
radius: height/2
|
||||||
|
}
|
||||||
|
|
||||||
|
component PanelHeading: StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: {
|
||||||
|
switch (permissionType) {
|
||||||
|
case PermissionTypes.Type.Admin:
|
||||||
|
return qsTr("Become an admin")
|
||||||
|
case PermissionTypes.Type.Member:
|
||||||
|
return qsTr("Join %1").arg(root.communityName)
|
||||||
|
default:
|
||||||
|
return modelData // TODO display channel name https://github.com/status-im/status-desktop/issues/11481
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SinglePermissionFlow: Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
Repeater {
|
||||||
|
model: HoldingsSelectionModel {
|
||||||
|
sourceModel: model.holdingsListModel
|
||||||
|
assetsModel: root.assetsModel
|
||||||
|
collectiblesModel: root.collectiblesModel
|
||||||
|
}
|
||||||
|
delegate: Row {
|
||||||
|
spacing: 4
|
||||||
|
StatusRoundedImage {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
image.source: model.imageSource
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: Theme.tertiaryTextFontSize
|
||||||
|
text: model.text
|
||||||
|
color: model.available ? Theme.palette.successColor1 : Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component PermissionPanel: Control {
|
||||||
|
id: permissionPanel
|
||||||
|
property int permissionType: PermissionTypes.Type.None
|
||||||
|
property var permissionsModel
|
||||||
|
|
||||||
|
readonly property bool tokenCriteriaMet: overallPermissionRow.tokenCriteriaMet
|
||||||
|
|
||||||
|
visible: permissionsModel.count
|
||||||
|
Layout.fillWidth: true
|
||||||
|
padding: d.absLeftMargin
|
||||||
|
background: PanelBg {}
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: Style.current.padding
|
||||||
|
PanelIcon {}
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
PanelHeading {}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: grid.implicitHeight + grid.anchors.margins*2
|
||||||
|
border.width: 1
|
||||||
|
border.color: d.tableBorderColor
|
||||||
|
radius: Style.current.radius
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: grid
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.current.halfPadding
|
||||||
|
rowSpacing: Style.current.halfPadding
|
||||||
|
columnSpacing: Style.current.halfPadding
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: permissionsRepeater
|
||||||
|
|
||||||
|
property int revision
|
||||||
|
|
||||||
|
model: permissionPanel.permissionsModel
|
||||||
|
delegate: Column {
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.row: index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
|
||||||
|
readonly property bool tokenCriteriaMet: model.tokenCriteriaMet ?? false
|
||||||
|
onTokenCriteriaMetChanged: permissionsRepeater.revision++
|
||||||
|
|
||||||
|
SinglePermissionFlow {}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: parent.width + grid.anchors.margins*2
|
||||||
|
height: 1
|
||||||
|
color: d.tableBorderColor
|
||||||
|
visible: index < permissionsRepeater.count - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: overallPermissionRow
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.rowSpan: permissionsRepeater.count || 1
|
||||||
|
Layout.preferredWidth: 110
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
readonly property bool tokenCriteriaMet: {
|
||||||
|
permissionsRepeater.revision // NB no let/const here b/c of https://bugreports.qt.io/browse/QTBUG-91917
|
||||||
|
for (var i = 0; i < permissionsRepeater.count; i++) {
|
||||||
|
var permissionItem = permissionsRepeater.itemAt(i);
|
||||||
|
if (permissionItem.tokenCriteriaMet)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: 1
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: -Style.current.halfPadding
|
||||||
|
Layout.bottomMargin: -Style.current.halfPadding
|
||||||
|
color: d.tableBorderColor
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
StatusIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
icon: overallPermissionRow.tokenCriteriaMet ? "tiny/checkmark" : "tiny/secure"
|
||||||
|
color: overallPermissionRow.tokenCriteriaMet ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: Theme.tertiaryTextFontSize
|
||||||
|
text: {
|
||||||
|
switch (permissionPanel.permissionType) {
|
||||||
|
case PermissionTypes.Type.Admin:
|
||||||
|
return qsTr("Admin")
|
||||||
|
case PermissionTypes.Type.Member:
|
||||||
|
return qsTr("Join")
|
||||||
|
case PermissionTypes.Type.Read:
|
||||||
|
return qsTr("View only")
|
||||||
|
case PermissionTypes.Type.ViewAndPost:
|
||||||
|
return qsTr("View & post")
|
||||||
|
default:
|
||||||
|
return "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color: overallPermissionRow.tokenCriteriaMet ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component ChannelPermissionPanel: Control {
|
||||||
|
id: channelPermsPanel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 10
|
||||||
|
padding: d.absLeftMargin
|
||||||
|
background: PanelBg {}
|
||||||
|
|
||||||
|
readonly property string channelKey: modelData
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: Style.current.padding
|
||||||
|
PanelIcon {}
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: Style.current.smallPadding
|
||||||
|
PanelHeading {}
|
||||||
|
Repeater { // permissions repeater
|
||||||
|
model: [PermissionTypes.Type.Read, PermissionTypes.Type.ViewAndPost]
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: channelPermsSubPanel
|
||||||
|
|
||||||
|
readonly property int permissionType: modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: grid2.implicitHeight + grid2.anchors.margins*2
|
||||||
|
border.width: 1
|
||||||
|
border.color: d.tableBorderColor
|
||||||
|
radius: Style.current.radius
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: grid2
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.current.halfPadding
|
||||||
|
rowSpacing: Style.current.halfPadding
|
||||||
|
columnSpacing: Style.current.halfPadding
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: permissionsRepeater2
|
||||||
|
|
||||||
|
property int revision
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: channelPermissionsModel
|
||||||
|
sourceModel: root.permissionsModel
|
||||||
|
function filterPredicate(modelData) {
|
||||||
|
return modelData.permissionType === channelPermsSubPanel.permissionType &&
|
||||||
|
!modelData.isPrivate &&
|
||||||
|
ModelUtils.contains(modelData.channelsListModel, "key", channelPermsPanel.channelKey) // filter and group by channel "key"
|
||||||
|
}
|
||||||
|
filters: ExpressionFilter {
|
||||||
|
expression: channelPermissionsModel.filterPredicate(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: Column {
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.row: index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
|
||||||
|
readonly property bool tokenCriteriaMet: model.tokenCriteriaMet ?? false
|
||||||
|
onTokenCriteriaMetChanged: permissionsRepeater2.revision++
|
||||||
|
|
||||||
|
SinglePermissionFlow {}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: parent.width + grid2.anchors.margins*2
|
||||||
|
height: 1
|
||||||
|
color: d.tableBorderColor
|
||||||
|
visible: index < permissionsRepeater2.count - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: overallPermissionRow2
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.rowSpan: channelPermissionsModel.count || 1
|
||||||
|
Layout.preferredWidth: 110
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
readonly property bool tokenCriteriaMet: {
|
||||||
|
permissionsRepeater2.revision
|
||||||
|
for (let i = 0; i < permissionsRepeater2.count; i++) {
|
||||||
|
const permissionItem = permissionsRepeater2.itemAt(i);
|
||||||
|
if (permissionItem.tokenCriteriaMet)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: 1
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: -Style.current.halfPadding
|
||||||
|
Layout.bottomMargin: -Style.current.halfPadding
|
||||||
|
color: d.tableBorderColor
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
StatusIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
icon: overallPermissionRow2.tokenCriteriaMet ? "tiny/checkmark" : "tiny/secure"
|
||||||
|
color: overallPermissionRow2.tokenCriteriaMet ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: Theme.tertiaryTextFontSize
|
||||||
|
text: {
|
||||||
|
switch (channelPermsSubPanel.permissionType) {
|
||||||
|
case PermissionTypes.Type.Read:
|
||||||
|
return qsTr("View only")
|
||||||
|
case PermissionTypes.Type.ViewAndPost:
|
||||||
|
return qsTr("View & post")
|
||||||
|
case PermissionTypes.Type.Moderator:
|
||||||
|
return qsTr("Moderate")
|
||||||
|
default:
|
||||||
|
return "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color: overallPermissionRow2.tokenCriteriaMet ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ PrivilegedTokenArtworkPanel 1.0 PrivilegedTokenArtworkPanel.qml
|
||||||
ProfilePopupInviteFriendsPanel 1.0 ProfilePopupInviteFriendsPanel.qml
|
ProfilePopupInviteFriendsPanel 1.0 ProfilePopupInviteFriendsPanel.qml
|
||||||
ProfilePopupInviteMessagePanel 1.0 ProfilePopupInviteMessagePanel.qml
|
ProfilePopupInviteMessagePanel 1.0 ProfilePopupInviteMessagePanel.qml
|
||||||
ProfilePopupOverviewPanel 1.0 ProfilePopupOverviewPanel.qml
|
ProfilePopupOverviewPanel 1.0 ProfilePopupOverviewPanel.qml
|
||||||
|
SharedAddressesPanel 1.0 SharedAddressesPanel.qml
|
||||||
SortableTokenHoldersList 1.0 SortableTokenHoldersList.qml
|
SortableTokenHoldersList 1.0 SortableTokenHoldersList.qml
|
||||||
SortableTokenHoldersPanel 1.0 SortableTokenHoldersPanel.qml
|
SortableTokenHoldersPanel 1.0 SortableTokenHoldersPanel.qml
|
||||||
TagsPanel 1.0 TagsPanel.qml
|
TagsPanel 1.0 TagsPanel.qml
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
import AppLayouts.Communities.panels 1.0
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
StatusDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool isEditMode
|
||||||
|
|
||||||
|
required property string communityName
|
||||||
|
required property string communityIcon
|
||||||
|
property int loginType: Constants.LoginType.Password
|
||||||
|
|
||||||
|
required property var walletAccountsModel // name, address, emoji, colorId, assets
|
||||||
|
required property var permissionsModel // id, key, permissionType, holdingsListModel, channelsListModel, isPrivate, tokenCriteriaMet
|
||||||
|
required property var assetsModel
|
||||||
|
required property var collectiblesModel
|
||||||
|
|
||||||
|
readonly property string selectedAirdropAddress: panel.selectedAirdropAddress
|
||||||
|
readonly property var selectedSharedAddresses: panel.selectedSharedAddresses
|
||||||
|
|
||||||
|
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||||
|
|
||||||
|
title: panel.title
|
||||||
|
implicitWidth: 640 // by design
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
contentItem: SharedAddressesPanel {
|
||||||
|
id: panel
|
||||||
|
isEditMode: root.isEditMode
|
||||||
|
communityName: root.communityName
|
||||||
|
communityIcon: root.communityIcon
|
||||||
|
loginType: root.loginType
|
||||||
|
walletAccountsModel: root.walletAccountsModel
|
||||||
|
permissionsModel: root.permissionsModel
|
||||||
|
assetsModel: root.assetsModel
|
||||||
|
collectiblesModel: root.collectiblesModel
|
||||||
|
onShareSelectedAddressesClicked: root.shareSelectedAddressesClicked(airdropAddress, sharedAddresses)
|
||||||
|
onClose: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: StatusDialogFooter {
|
||||||
|
spacing: Style.current.padding
|
||||||
|
rightButtons: panel.buttons
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,3 +16,4 @@ RemotelyDestructPopup 1.0 RemotelyDestructPopup.qml
|
||||||
SignMultiTokenTransactionsPopup 1.0 SignMultiTokenTransactionsPopup.qml
|
SignMultiTokenTransactionsPopup 1.0 SignMultiTokenTransactionsPopup.qml
|
||||||
SignTokenTransactionsPopup 1.0 SignTokenTransactionsPopup.qml
|
SignTokenTransactionsPopup 1.0 SignTokenTransactionsPopup.qml
|
||||||
TransferOwnershipPopup 1.0 TransferOwnershipPopup.qml
|
TransferOwnershipPopup 1.0 TransferOwnershipPopup.qml
|
||||||
|
SharedAddressesPopup 1.0 SharedAddressesPopup.qml
|
||||||
|
|
|
@ -20,6 +20,7 @@ import shared.views.chat 1.0
|
||||||
|
|
||||||
import AppLayouts.Communities.popups 1.0
|
import AppLayouts.Communities.popups 1.0
|
||||||
import AppLayouts.Communities.panels 1.0
|
import AppLayouts.Communities.panels 1.0
|
||||||
|
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||||
|
|
||||||
// FIXME: Rework me to use ColumnLayout instead of anchors!!
|
// FIXME: Rework me to use ColumnLayout instead of anchors!!
|
||||||
Item {
|
Item {
|
||||||
|
@ -126,10 +127,15 @@ Item {
|
||||||
introMessage: communityData.introMessage
|
introMessage: communityData.introMessage
|
||||||
imageSrc: communityData.image
|
imageSrc: communityData.image
|
||||||
accessType: communityData.access
|
accessType: communityData.access
|
||||||
|
loginType: root.store.loginType
|
||||||
|
walletAccountsModel: WalletStore.RootStore.receiveAccounts
|
||||||
|
permissionsModel: root.store.permissionsStore.permissionsModel
|
||||||
|
assetsModel: root.store.assetsModel
|
||||||
|
collectiblesModel: root.store.collectiblesModel
|
||||||
|
|
||||||
onJoined: {
|
onJoined: {
|
||||||
joinCommunityButton.loading = true
|
joinCommunityButton.loading = true
|
||||||
root.store.requestToJoinCommunityWithAuthentication(root.store.userProfileInst.name)
|
root.store.requestToJoinCommunityWithAuthentication(root.store.userProfileInst.name, sharedAddresses, airdropAddress)
|
||||||
}
|
}
|
||||||
onCancelMembershipRequest: {
|
onCancelMembershipRequest: {
|
||||||
root.store.cancelPendingRequest(communityData.id)
|
root.store.cancelPendingRequest(communityData.id)
|
||||||
|
|
|
@ -18,6 +18,8 @@ import SortFilterProxyModel 0.2
|
||||||
import "../panels"
|
import "../panels"
|
||||||
import AppLayouts.Communities.popups 1.0
|
import AppLayouts.Communities.popups 1.0
|
||||||
import AppLayouts.Communities.panels 1.0
|
import AppLayouts.Communities.panels 1.0
|
||||||
|
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||||
|
import AppLayouts.Chat.stores 1.0 as ChatStore
|
||||||
|
|
||||||
SettingsContentBase {
|
SettingsContentBase {
|
||||||
id: root
|
id: root
|
||||||
|
@ -208,22 +210,23 @@ SettingsContentBase {
|
||||||
|
|
||||||
property string communityId
|
property string communityId
|
||||||
|
|
||||||
readonly property var chatCommunitySectionModule: {
|
readonly property var chatStore: ChatStore.RootStore {
|
||||||
root.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(communityIntroDialog.communityId)
|
chatCommunitySectionModule: {
|
||||||
return root.rootStore.mainModuleInst.getCommunitySectionModule()
|
root.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(communityIntroDialog.communityId)
|
||||||
|
return root.rootStore.mainModuleInst.getCommunitySectionModule()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onJoined: {
|
loginType: chatStore.loginType
|
||||||
chatCommunitySectionModule.requestToJoinCommunityWithAuthentication(root.rootStore.userProfileInst.name)
|
walletAccountsModel: WalletStore.RootStore.receiveAccounts
|
||||||
}
|
permissionsModel: chatStore.permissionsStore.permissionsModel
|
||||||
|
assetsModel: chatStore.assetsModel
|
||||||
|
collectiblesModel: chatStore.collectiblesModel
|
||||||
|
|
||||||
onCancelMembershipRequest: {
|
onJoined: chatStore.requestToJoinCommunityWithAuthentication(root.rootStore.userProfileInst.name, JSON.stringify(sharedAddresses), airdropAddress)
|
||||||
root.rootStore.cancelPendingRequest(communityIntroDialog.communityId)
|
onCancelMembershipRequest: root.rootStore.cancelPendingRequest(communityIntroDialog.communityId)
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: {
|
onClosed: destroy()
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.15
|
||||||
import QtQml.Models 2.14
|
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
|
@ -9,9 +8,14 @@ import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups.Dialog 0.1
|
import StatusQ.Popups 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
|
||||||
StatusDialog {
|
import AppLayouts.Communities.panels 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
StatusStackModal {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string name
|
property string name
|
||||||
|
@ -19,74 +23,131 @@ StatusDialog {
|
||||||
property int accessType
|
property int accessType
|
||||||
property url imageSrc
|
property url imageSrc
|
||||||
property bool isInvitationPending: false
|
property bool isInvitationPending: false
|
||||||
|
property int loginType: Constants.LoginType.Password
|
||||||
|
|
||||||
signal joined
|
required property var walletAccountsModel // name, address, emoji, colorId
|
||||||
signal cancelMembershipRequest
|
required property var permissionsModel // id, key, permissionType, holdingsListModel, channelsListModel, isPrivate, tokenCriteriaMet
|
||||||
|
required property var assetsModel
|
||||||
|
required property var collectiblesModel
|
||||||
|
|
||||||
|
signal joined(string airdropAddress, var sharedAddresses)
|
||||||
|
signal cancelMembershipRequest()
|
||||||
|
|
||||||
|
width: 640 // by design
|
||||||
padding: 0
|
padding: 0
|
||||||
title: qsTr("Welcome to %1").arg(name)
|
stackTitle: root.accessType === Constants.communityChatOnRequestAccess ? qsTr("Request to join %1").arg(name) : qsTr("Welcome to %1").arg(name)
|
||||||
|
|
||||||
footer: StatusDialogFooter {
|
rightButtons: [d.shareButton, finishButton]
|
||||||
rightButtons: ObjectModel {
|
|
||||||
StatusButton {
|
finishButton: StatusButton {
|
||||||
text: root.isInvitationPending ? qsTr("Cancel Membership Request")
|
text: root.isInvitationPending ? qsTr("Cancel Membership Request")
|
||||||
: (root.accessType === Constants.communityChatOnRequestAccess
|
: (root.accessType === Constants.communityChatOnRequestAccess
|
||||||
? qsTr("Request to join %1").arg(root.name)
|
? qsTr("Share your addresses to join")
|
||||||
: qsTr("Join %1").arg(root.name) )
|
: qsTr("Join %1").arg(root.name) )
|
||||||
type: root.isInvitationPending ? StatusBaseButton.Type.Danger
|
type: root.isInvitationPending ? StatusBaseButton.Type.Danger
|
||||||
: StatusBaseButton.Type.Normal
|
: StatusBaseButton.Type.Normal
|
||||||
enabled: checkBox.checked || root.isInvitationPending
|
icon.name: root.accessType === Constants.communityChatOnRequestAccess && !root.isInvitationPending ? Constants.authenticationIconByType[root.loginType] : ""
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (root.isInvitationPending) {
|
if (root.isInvitationPending) {
|
||||||
root.cancelMembershipRequest()
|
root.cancelMembershipRequest()
|
||||||
} else {
|
} else {
|
||||||
root.joined()
|
root.joined(d.selectedAirdropAddress, d.selectedSharedAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property var tempAddressesModel: SortFilterProxyModel {
|
||||||
|
sourceModel: root.walletAccountsModel
|
||||||
|
filters: [
|
||||||
|
ValueFilter {
|
||||||
|
roleName: "walletType"
|
||||||
|
value: Constants.watchWalletType
|
||||||
|
inverted: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
sorters: [
|
||||||
|
ExpressionSorter {
|
||||||
|
function isGenerated(modelData) {
|
||||||
|
return modelData.walletType === Constants.generatedWalletType
|
||||||
}
|
}
|
||||||
|
|
||||||
root.close()
|
expression: {
|
||||||
|
return isGenerated(modelLeft)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RoleSorter {
|
||||||
|
roleName: "position"
|
||||||
|
},
|
||||||
|
RoleSorter {
|
||||||
|
roleName: "name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] : ""
|
||||||
|
|
||||||
|
readonly property var shareButton: StatusFlatButton {
|
||||||
|
height: finishButton.height
|
||||||
|
visible: !root.isInvitationPending && !root.replaceItem
|
||||||
|
borderColor: Theme.palette.baseColor2
|
||||||
|
text: qsTr("Select addresses to share")
|
||||||
|
onClicked: root.replace(sharedAddressesPanelComponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: sharedAddressesPanelComponent
|
||||||
|
SharedAddressesPanel {
|
||||||
|
communityName: root.name
|
||||||
|
communityIcon: root.imageSrc
|
||||||
|
loginType: root.loginType
|
||||||
|
walletAccountsModel: root.walletAccountsModel
|
||||||
|
permissionsModel: root.permissionsModel
|
||||||
|
assetsModel: root.assetsModel
|
||||||
|
collectiblesModel: root.collectiblesModel
|
||||||
|
onShareSelectedAddressesClicked: {
|
||||||
|
d.selectedAirdropAddress = airdropAddress
|
||||||
|
d.selectedSharedAddresses = sharedAddresses
|
||||||
|
root.replaceItem = undefined // go back, unload us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stackItems: [
|
||||||
|
StatusScrollView {
|
||||||
|
id: scrollView
|
||||||
|
contentWidth: availableWidth
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 24
|
||||||
|
width: scrollView.availableWidth
|
||||||
|
|
||||||
|
StatusRoundedImage {
|
||||||
|
id: roundImage
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.preferredHeight: 64
|
||||||
|
Layout.preferredWidth: Layout.preferredHeight
|
||||||
|
visible: image.status == Image.Loading || image.status == Image.Ready
|
||||||
|
image.source: root.imageSrc
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: introText
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.introMessage || qsTr("Community <b>%1</b> has no intro message...").arg(root.name)
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
|
|
||||||
StatusScrollView {
|
|
||||||
id: scrollView
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitWidth: 640 // by design
|
|
||||||
contentWidth: availableWidth
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: columnContent
|
|
||||||
|
|
||||||
spacing: 24
|
|
||||||
width: scrollView.availableWidth
|
|
||||||
|
|
||||||
StatusRoundedImage {
|
|
||||||
id: roundImage
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
Layout.preferredHeight: 64
|
|
||||||
Layout.preferredWidth: Layout.preferredHeight
|
|
||||||
visible: image.status == Image.Loading || image.status == Image.Ready
|
|
||||||
image.source: root.imageSrc
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusBaseText {
|
|
||||||
id: introText
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.introMessage !== "" ? root.introMessage : qsTr("Community <b>%1</b> has no intro message...").arg(root.name)
|
|
||||||
color: Theme.palette.directColor1
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusCheckBox {
|
|
||||||
id: checkBox
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
visible: !root.isInvitationPending
|
|
||||||
text: qsTr("I agree with the above")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ QtObject {
|
||||||
return (modelData.permissionType == Constants.permissionType.viewAndPost) &&
|
return (modelData.permissionType == Constants.permissionType.viewAndPost) &&
|
||||||
root.permissionsModel.belongsToChat(modelData.id, root.activeChannelId) &&
|
root.permissionsModel.belongsToChat(modelData.id, root.activeChannelId) &&
|
||||||
(modelData.tokenCriteriaMet || !modelData.isPrivate)
|
(modelData.tokenCriteriaMet || !modelData.isPrivate)
|
||||||
|
|
||||||
}
|
}
|
||||||
filters: [
|
filters: [
|
||||||
ExpressionFilter {
|
ExpressionFilter {
|
||||||
|
|
|
@ -987,7 +987,8 @@ QtObject {
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
ERC20 = 1, // Asset
|
ERC20 = 1, // Asset
|
||||||
ERC721 = 2 // Collectible
|
ERC721 = 2, // Collectible
|
||||||
|
ENS = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirrors src/backend/activity.nim ActivityStatus
|
// Mirrors src/backend/activity.nim ActivityStatus
|
||||||
|
|
|
@ -24,7 +24,7 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startsWith0x(value) {
|
function startsWith0x(value) {
|
||||||
return value.startsWith('0x')
|
return !!value && value.startsWith('0x')
|
||||||
}
|
}
|
||||||
|
|
||||||
function isChatKey(value) {
|
function isChatKey(value) {
|
||||||
|
@ -248,8 +248,8 @@ QtObject {
|
||||||
* Returns text in the format "✓ 12 words" for seed phrases input boxes
|
* Returns text in the format "✓ 12 words" for seed phrases input boxes
|
||||||
*/
|
*/
|
||||||
function seedPhraseWordCountText(text) {
|
function seedPhraseWordCountText(text) {
|
||||||
let wordCount = countWords(text);
|
const wordCount = countWords(text);
|
||||||
return getTick(wordCount) + wordCount.toString() + " " + qsTr("words")
|
return getTick(wordCount) + qsTr("%n word(s)", "", wordCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function uuid() {
|
function uuid() {
|
||||||
|
@ -288,7 +288,7 @@ QtObject {
|
||||||
} else if (!/^\d+$/.test(firstPINField.pinInput)) {
|
} else if (!/^\d+$/.test(firstPINField.pinInput)) {
|
||||||
return [false, qsTr("The PIN must contain only digits")];
|
return [false, qsTr("The PIN must contain only digits")];
|
||||||
} else if (firstPINField.pinInput.length != Constants.keycard.general.keycardPinLength) {
|
} else if (firstPINField.pinInput.length != Constants.keycard.general.keycardPinLength) {
|
||||||
return [false, qsTr("The PIN must be exactly %1 digits").arg(Constants.keycard.general.keycardPinLength)];
|
return [false, qsTr("The PIN must be exactly %n digit(s)", "", Constants.keycard.general.keycardPinLength)];
|
||||||
}
|
}
|
||||||
return [true, ""];
|
return [true, ""];
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ QtObject {
|
||||||
if (repeatPINField.pinInput === "") {
|
if (repeatPINField.pinInput === "") {
|
||||||
return [false, qsTr("You need to repeat your PIN")];
|
return [false, qsTr("You need to repeat your PIN")];
|
||||||
} else if (repeatPINField.pinInput !== firstPINField.pinInput) {
|
} else if (repeatPINField.pinInput !== firstPINField.pinInput) {
|
||||||
return [false, qsTr("PIN don't match")];
|
return [false, qsTr("PINs don't match")];
|
||||||
}
|
}
|
||||||
return [true, ""];
|
return [true, ""];
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(validation & Utils.Validate.TextLength && str.length > limit) {
|
if(validation & Utils.Validate.TextLength && str.length > limit) {
|
||||||
errMsg = qsTr("The %1 cannot exceed %2 characters").arg(fieldName, limit)
|
errMsg = qsTr("The %1 cannot exceed %n character(s)", "", limit).arg(fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(validation & Utils.Validate.TextHexColor && !isHexColor(str)) {
|
if(validation & Utils.Validate.TextHexColor && !isHexColor(str)) {
|
||||||
|
@ -392,7 +392,7 @@ QtObject {
|
||||||
if (errors.minLength) {
|
if (errors.minLength) {
|
||||||
return errors.minLength.min === 1 ?
|
return errors.minLength.min === 1 ?
|
||||||
qsTr("You need to enter a %1").arg(fieldName) :
|
qsTr("You need to enter a %1").arg(fieldName) :
|
||||||
qsTr("Value has to be at least %1 characters long").arg(errors.minLength.min)
|
qsTr("Value has to be at least %n character(s) long", "", errors.minLength.min)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
@ -657,7 +657,7 @@ QtObject {
|
||||||
// special handling because on an index attached to the constant
|
// special handling because on an index attached to the constant
|
||||||
if (key.startsWith(Constants.appTranslatableConstants.keycardAccountNameOfUnknownWalletAccount)) {
|
if (key.startsWith(Constants.appTranslatableConstants.keycardAccountNameOfUnknownWalletAccount)) {
|
||||||
let num = key.substring(Constants.appTranslatableConstants.keycardAccountNameOfUnknownWalletAccount.length)
|
let num = key.substring(Constants.appTranslatableConstants.keycardAccountNameOfUnknownWalletAccount.length)
|
||||||
return "%1%2".arg(qsTr("acc")).arg(num) //short name of an unknown (removed) wallet account
|
return "%1%2".arg(qsTr("acc", "short for account")).arg(num) //short name of an unknown (removed) wallet account
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6a471f1bef1288407751400d93b12b7fa911474d
|
Subproject commit 70b76297fd074b4adda5e659d260c8cc18e51ef9
|
Loading…
Reference in New Issue