557 lines
24 KiB
QML
557 lines
24 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Layouts 1.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Dialogs 1.3
|
|
import QtGraphicalEffects 1.15
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
import utils 1.0
|
|
import shared.panels 1.0
|
|
import shared.popups 1.0
|
|
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
import StatusQ.Layout 0.1
|
|
import StatusQ.Components 0.1
|
|
import StatusQ.Controls 0.1
|
|
import StatusQ.Controls.Validators 0.1
|
|
|
|
import AppLayouts.Chat.stores 1.0
|
|
import AppLayouts.Chat.controls.community 1.0
|
|
|
|
import shared.stores 1.0
|
|
import shared.views.chat 1.0
|
|
|
|
import "../panels/communities"
|
|
import "../popups/community"
|
|
import "../layouts"
|
|
|
|
StatusSectionLayout {
|
|
id: root
|
|
|
|
notificationCount: activityCenterStore.unreadNotificationsCount
|
|
hasUnseenNotifications: activityCenterStore.hasUnseenNotifications
|
|
onNotificationButtonClicked: Global.openActivityCenterPopup()
|
|
// TODO: get this model from backend?
|
|
property var settingsMenuModel: [{id: Constants.CommunitySettingsSections.Overview, name: qsTr("Overview"), icon: "show", enabled: true},
|
|
{id: Constants.CommunitySettingsSections.Members, name: qsTr("Members"), icon: "group-chat", enabled: true, },
|
|
{id: Constants.CommunitySettingsSections.Permissions, name: qsTr("Permissions"), icon: "objects", enabled: true},
|
|
{id: Constants.CommunitySettingsSections.MintTokens, name: qsTr("Mint Tokens"), icon: "token", enabled: true},
|
|
{id: Constants.CommunitySettingsSections.Airdrops, name: qsTr("Airdrops"), icon: "airdrop", enabled: true}]
|
|
// TODO: Next community settings options:
|
|
// {name: qsTr("Token sales"), icon: "token-sale"},
|
|
// {name: qsTr("Subscriptions"), icon: "subscription"},
|
|
property var rootStore
|
|
property var chatCommunitySectionModule
|
|
property var community
|
|
property bool hasAddedContacts: false
|
|
property var transactionStore: TransactionStore {}
|
|
|
|
readonly property string filteredSelectedTags: {
|
|
var tagsArray = []
|
|
if (community && community.tags) {
|
|
try {
|
|
const json = JSON.parse(community.tags)
|
|
if (!!json) {
|
|
tagsArray = json.map(tag => {
|
|
return tag.name
|
|
})
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.warn("Error parsing community tags: ", community.tags, " error: ", e.message)
|
|
}
|
|
}
|
|
return JSON.stringify(tagsArray);
|
|
}
|
|
|
|
signal backToCommunityClicked
|
|
|
|
//navigate to a specific section and subsection
|
|
function goTo(section: int, subSection: int) {
|
|
d.goTo(section, subSection)
|
|
}
|
|
|
|
onBackButtonClicked: {
|
|
centerPanelContentLoader.item.children[d.currentIndex].navigateBack()
|
|
}
|
|
|
|
leftPanel: Item {
|
|
anchors.fill: parent
|
|
|
|
ColumnLayout {
|
|
anchors {
|
|
top: parent.top
|
|
bottom: backToCommunityButton.top
|
|
bottomMargin: 12
|
|
topMargin: Style.current.smallPadding
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
width: parent.width
|
|
spacing: 32
|
|
clip: true
|
|
|
|
StatusChatInfoButton {
|
|
id: communityHeader
|
|
|
|
title: community.name
|
|
subTitle: qsTr("%n member(s)", "", community.members.count || 0)
|
|
asset.name: community.image
|
|
asset.color: community.color
|
|
asset.isImage: true
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: Style.current.halfPadding
|
|
Layout.rightMargin: Style.current.halfPadding
|
|
type: StatusChatInfoButton.Type.OneToOneChat
|
|
hoverEnabled: false
|
|
}
|
|
|
|
StatusListView {
|
|
id: listView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
Layout.leftMargin: Style.current.padding
|
|
Layout.rightMargin: Style.current.padding
|
|
model: root.settingsMenuModel
|
|
spacing: 8
|
|
|
|
delegate: StatusNavigationListItem {
|
|
objectName: "CommunitySettingsView_NavigationListItem_" + modelData.name
|
|
width: listView.width
|
|
title: modelData.name
|
|
asset.name: modelData.icon
|
|
asset.height: 24
|
|
asset.width: 24
|
|
selected: d.currentIndex === index
|
|
onClicked: d.currentIndex = index
|
|
visible: modelData.enabled
|
|
height: modelData.enabled ? implicitHeight : 0
|
|
}
|
|
}
|
|
}
|
|
|
|
StatusBaseText {
|
|
id: backToCommunityButton
|
|
objectName: "communitySettingsBackToCommunityButton"
|
|
anchors {
|
|
bottom: parent.bottom
|
|
bottomMargin: 16
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
text: "<- " + qsTr("Back to community")
|
|
color: Theme.palette.baseColor1
|
|
font.pixelSize: 15
|
|
font.underline: true
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.backToCommunityClicked()
|
|
hoverEnabled: true
|
|
}
|
|
}
|
|
}
|
|
|
|
centerPanel: Loader {
|
|
id: centerPanelContentLoader
|
|
anchors.fill: parent
|
|
active: root.community
|
|
sourceComponent: StackLayout {
|
|
id: stackLayout
|
|
currentIndex: d.currentIndex
|
|
|
|
CommunityOverviewSettingsPanel {
|
|
communityId: root.community.id
|
|
name: root.community.name
|
|
description: root.community.description
|
|
introMessage: root.community.introMessage
|
|
outroMessage: root.community.outroMessage
|
|
logoImageData: root.community.image
|
|
bannerImageData: root.community.bannerImageData
|
|
color: root.community.color
|
|
tags: root.rootStore.communityTags
|
|
selectedTags: root.filteredSelectedTags
|
|
archiveSupportEnabled: root.community.historyArchiveSupportEnabled
|
|
requestToJoinEnabled: root.community.access === Constants.communityChatOnRequestAccess
|
|
pinMessagesEnabled: root.community.pinMessageAllMembersEnabled
|
|
editable: root.community.amISectionAdmin
|
|
|
|
onEdited: {
|
|
const error = root.chatCommunitySectionModule.editCommunity(
|
|
StatusQUtils.Utils.filterXSS(item.name),
|
|
StatusQUtils.Utils.filterXSS(item.description),
|
|
StatusQUtils.Utils.filterXSS(item.introMessage),
|
|
StatusQUtils.Utils.filterXSS(item.outroMessage),
|
|
item.options.requestToJoinEnabled ? Constants.communityChatOnRequestAccess : Constants.communityChatPublicAccess,
|
|
item.color.toString().toUpperCase(),
|
|
item.selectedTags,
|
|
Utils.getImageAndCropInfoJson(item.logoImagePath, item.logoCropRect),
|
|
Utils.getImageAndCropInfoJson(item.bannerPath, item.bannerCropRect),
|
|
item.options.archiveSupportEnabled,
|
|
item.options.pinMessagesEnabled
|
|
)
|
|
if (error) {
|
|
errorDialog.text = error.error
|
|
errorDialog.open()
|
|
}
|
|
}
|
|
|
|
onInviteNewPeopleClicked: {
|
|
Global.openInviteFriendsToCommunityPopup(root.community,
|
|
root.chatCommunitySectionModule,
|
|
null)
|
|
}
|
|
|
|
onAirdropTokensClicked: root.goTo(Constants.CommunitySettingsSections.Airdrops)
|
|
onBackUpClicked: {
|
|
Global.openPopup(transferOwnershipPopup, {
|
|
privateKey: root.chatCommunitySectionModule.exportCommunity(root.community.id),
|
|
})
|
|
}
|
|
onPreviousPageNameChanged: root.backButtonName = previousPageName
|
|
}
|
|
|
|
CommunityMembersSettingsPanel {
|
|
membersModel: root.community.members
|
|
bannedMembersModel: root.community.bannedMembers
|
|
pendingMemberRequestsModel: root.community.pendingMemberRequests
|
|
declinedMemberRequestsModel: root.community.declinedMemberRequests
|
|
editable: root.community.amISectionAdmin
|
|
communityName: root.community.name
|
|
|
|
onKickUserClicked: root.rootStore.removeUserFromCommunity(id)
|
|
onBanUserClicked: root.rootStore.banUserFromCommunity(id)
|
|
onUnbanUserClicked: root.rootStore.unbanUserFromCommunity(id)
|
|
onAcceptRequestToJoin: root.rootStore.acceptRequestToJoinCommunity(id, root.community.id)
|
|
onDeclineRequestToJoin: root.rootStore.declineRequestToJoinCommunity(id, root.community.id)
|
|
}
|
|
|
|
CommunityPermissionsSettingsPanel {
|
|
readonly property PermissionsStore permissionsStore:
|
|
rootStore.permissionsStore
|
|
|
|
permissionsModel: permissionsStore.permissionsModel
|
|
|
|
// temporary solution to provide icons for assets, similar
|
|
// method is used in wallet (constructing filename from asset's
|
|
// symbol) and is intended to be replaced by more robust
|
|
// solution soon.
|
|
|
|
assetsModel: rootStore.assetsModel
|
|
collectiblesModel: rootStore.collectiblesModel
|
|
channelsModel: rootStore.chatCommunitySectionModule.model
|
|
|
|
communityDetails: QtObject {
|
|
readonly property var _activeSection:
|
|
rootStore.mainModuleInst.activeSection
|
|
|
|
readonly property string name: _activeSection.name
|
|
readonly property string image: _activeSection.image
|
|
readonly property string color: _activeSection.color
|
|
}
|
|
|
|
onCreatePermissionRequested:
|
|
permissionsStore.createPermission(holdings, permissionType,
|
|
isPrivate, channels)
|
|
|
|
onUpdatePermissionRequested:
|
|
permissionsStore.editPermission(
|
|
key, holdings, permissionType, channels, isPrivate)
|
|
|
|
onRemovePermissionRequested:
|
|
permissionsStore.removePermission(key)
|
|
|
|
onPreviousPageNameChanged: root.backButtonName = previousPageName
|
|
|
|
onNavigateToMintTokenSettings: root.goTo(Constants.CommunitySettingsSections.MintTokens)
|
|
}
|
|
|
|
CommunityMintTokensSettingsPanel {
|
|
id: mintPanel
|
|
|
|
readonly property CommunityTokensStore communityTokensStore:
|
|
rootStore.communityTokensStore
|
|
|
|
function setFeesInfo(ethCurrency, fiatCurrency, errorCode) {
|
|
if (errorCode === Constants.ComputeFeeErrorCode.Success || errorCode === Constants.ComputeFeeErrorCode.Balance) {
|
|
let valueStr = LocaleUtils.currencyAmountToLocaleString(ethCurrency) + "(" + LocaleUtils.currencyAmountToLocaleString(fiatCurrency) + ")"
|
|
mintPanel.feeText = valueStr
|
|
if (errorCode === Constants.ComputeFeeErrorCode.Balance) {
|
|
mintPanel.errorText = qsTr("Not enough funds to make transaction")
|
|
}
|
|
mintPanel.isFeeLoading = false
|
|
return
|
|
} else if (errorCode === Constants.ComputeFeeErrorCode.Infura) {
|
|
mintPanel.errorText = qsTr("Infura error")
|
|
mintPanel.isFeeLoading = true
|
|
return
|
|
}
|
|
|
|
mintPanel.errorText = qsTr("Unknown error")
|
|
mintPanel.isFeeLoading = true
|
|
}
|
|
|
|
communityName: root.community.name
|
|
tokensModel: root.community.communityTokens
|
|
layer1Networks: communityTokensStore.layer1Networks
|
|
layer2Networks: communityTokensStore.layer2Networks
|
|
testNetworks: communityTokensStore.testNetworks
|
|
enabledNetworks: communityTokensStore.enabledNetworks
|
|
allNetworks: communityTokensStore.allNetworks
|
|
accounts: root.rootStore.accounts
|
|
|
|
onPreviousPageNameChanged: root.backButtonName = previousPageName
|
|
onSignMintTransactionOpened: communityTokensStore.computeDeployFee(chainId, accountAddress)
|
|
onMintCollectible: {
|
|
communityTokensStore.deployCollectible(root.community.id,
|
|
accountAddress,
|
|
name,
|
|
symbol,
|
|
description,
|
|
supply,
|
|
infiniteSupply,
|
|
transferable,
|
|
selfDestruct,
|
|
chainId,
|
|
artworkSource,
|
|
accountName,
|
|
artworkCropRect)
|
|
}
|
|
onMintAsset: {
|
|
communityTokensStore.deployAsset(root.community.id,
|
|
accountAddress,
|
|
name,
|
|
symbol,
|
|
description,
|
|
supply,
|
|
infiniteSupply,
|
|
decimals,
|
|
chainId,
|
|
artworkSource,
|
|
accountName,
|
|
artworkCropRect)
|
|
}
|
|
onSignSelfDestructTransactionOpened: communityTokensStore.computeSelfDestructFee(selfDestructTokensList, tokenKey)
|
|
onRemoteSelfDestructCollectibles: {
|
|
communityTokensStore.remoteSelfDestructCollectibles(root.community.id,
|
|
selfDestructTokensList,
|
|
tokenKey)
|
|
}
|
|
onSignBurnTransactionOpened: communityTokensStore.computeBurnFee(chainId)
|
|
onBurnCollectibles: communityTokensStore.burnCollectibles(tokenKey, amount)
|
|
onAirdropCollectible: root.goTo(Constants.CommunitySettingsSections.Airdrops)
|
|
onDeleteToken: communityTokensStore.deleteToken(root.community.id, tokenKey)
|
|
onRetryMintToken: communityTokensStore.retryMintToken(root.community.id, tokenKey)
|
|
|
|
Connections {
|
|
target: rootStore.communityTokensStore
|
|
function onDeployFeeUpdated(ethCurrency, fiatCurrency, errorCode) {
|
|
mintPanel.setFeesInfo(ethCurrency, fiatCurrency, errorCode)
|
|
}
|
|
|
|
function onSelfDestructFeeUpdated(ethCurrency, fiatCurrency, errorCode) {
|
|
mintPanel.setFeesInfo(ethCurrency, fiatCurrency, errorCode)
|
|
}
|
|
|
|
function onRemoteDestructStateChanged(communityId, tokenName, status, url) {
|
|
if (root.community.id !== communityId) {
|
|
return
|
|
}
|
|
|
|
let title = ""
|
|
let loading = false
|
|
let type = Constants.ephemeralNotificationType.normal
|
|
switch (status) {
|
|
case Constants.ContractTransactionStatus.InProgress:
|
|
title = qsTr("Remotely destroying tokens...")
|
|
loading = true
|
|
break
|
|
case Constants.ContractTransactionStatus.Completed:
|
|
title = qsTr("%1 tokens destroyed").arg(tokenName)
|
|
type = Constants.ephemeralNotificationType.success
|
|
break
|
|
case Constants.ContractTransactionStatus.Failed:
|
|
title = qsTr("%1 tokens destruction failed").arg(tokenName)
|
|
break
|
|
default:
|
|
console.warn("Unknown destruction state: "+status)
|
|
return
|
|
}
|
|
Global.displayToastMessage(title,
|
|
qsTr("View on etherscan"),
|
|
"",
|
|
loading,
|
|
type,
|
|
url)
|
|
}
|
|
|
|
function onDeploymentStateChanged(communityId, status, url) {
|
|
if (root.community.id !== communityId) {
|
|
return
|
|
}
|
|
|
|
let title = ""
|
|
let loading = false
|
|
let type = Constants.ephemeralNotificationType.normal
|
|
switch (status) {
|
|
case Constants.ContractTransactionStatus.InProgress:
|
|
title = qsTr("Token is being minted...")
|
|
loading = true
|
|
break
|
|
case Constants.ContractTransactionStatus.Completed:
|
|
title = qsTr("Token minting finished")
|
|
type = Constants.ephemeralNotificationType.success
|
|
break
|
|
case Constants.ContractTransactionStatus.Failed:
|
|
title = qsTr("Token minting failed")
|
|
break
|
|
default:
|
|
console.warn("Unknown deploy state: "+status)
|
|
return
|
|
}
|
|
Global.displayToastMessage(title,
|
|
qsTr("View on etherscan"),
|
|
"",
|
|
loading,
|
|
type,
|
|
url)
|
|
}
|
|
}
|
|
}
|
|
|
|
CommunityAirdropsSettingsPanel {
|
|
id: airdropPanel
|
|
|
|
readonly property CommunityTokensStore communityTokensStore:
|
|
rootStore.communityTokensStore
|
|
|
|
assetsModel: ListModel {}
|
|
|
|
readonly property var communityTokens: root.community.communityTokens
|
|
|
|
Loader {
|
|
id: modelLoader
|
|
active: airdropPanel.communityTokens
|
|
|
|
sourceComponent: SortFilterProxyModel {
|
|
|
|
sourceModel: airdropPanel.communityTokens
|
|
|
|
proxyRoles: [
|
|
ExpressionRole {
|
|
name: "category"
|
|
|
|
// Singleton cannot be used directly in the epression
|
|
readonly property int category: TokenCategories.Category.Own
|
|
expression: category
|
|
},
|
|
ExpressionRole {
|
|
name: "iconSource"
|
|
expression: model.image
|
|
},
|
|
ExpressionRole {
|
|
name: "key"
|
|
expression: model.symbol
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
collectiblesModel: modelLoader.item
|
|
|
|
membersModel: {
|
|
const chatContentModule = root.rootStore.currentChatContentModule()
|
|
if (!chatContentModule || !chatContentModule.usersModule) {
|
|
// New communities have no chats, so no chatContentModule
|
|
return null
|
|
}
|
|
return chatContentModule.usersModule.model
|
|
}
|
|
|
|
onPreviousPageNameChanged: root.backButtonName = previousPageName
|
|
onAirdropClicked: communityTokensStore.airdrop(root.community.id, airdropTokens, addresses)
|
|
onNavigateToMintTokenSettings: root.goTo(Constants.CommunitySettingsSections.MintTokens)
|
|
|
|
Connections {
|
|
target: mintPanel
|
|
|
|
function onAirdropCollectible(key) {
|
|
// Here it is forced a navigation to the new airdrop form, like if it was clicked the header button
|
|
airdropPanel.primaryHeaderButtonClicked()
|
|
|
|
// Force a token selection to be airdroped with default amount 1
|
|
airdropPanel.selectCollectible(key, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
onCurrentIndexChanged: root.backButtonName = centerPanelContentLoader.item.children[d.currentIndex].previousPageName
|
|
}
|
|
}
|
|
|
|
onSettingsMenuModelChanged: d.currentIndex = 0
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
property int currentIndex: 0
|
|
readonly property var currentItem: centerPanelContentLoader.item && centerPanelContentLoader.item.children[d.currentIndex]
|
|
? centerPanelContentLoader.item.children[d.currentIndex]
|
|
: null
|
|
|
|
function goTo(section: int, subSection: int) {
|
|
//find and enable section
|
|
const matchingIndex = listView.model.findIndex((modelItem, index) => modelItem.id === section && modelItem.enabled)
|
|
if(matchingIndex !== -1) {
|
|
d.currentIndex = matchingIndex
|
|
//find and enable subsection if subSection navigation is available
|
|
if(d.currentItem && d.currentItem.goTo) {
|
|
d.currentItem.goTo(subSection)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MessageDialog {
|
|
id: errorDialog
|
|
title: qsTr("Error editing the community")
|
|
icon: StandardIcon.Critical
|
|
standardButtons: StandardButton.Ok
|
|
}
|
|
|
|
Component {
|
|
id: transferOwnershipPopup
|
|
TransferOwnershipPopup {
|
|
anchors.centerIn: parent
|
|
store: root.rootStore
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: noPermissionsPopupCmp
|
|
NoPermissionsToJoinPopup {
|
|
onRejectButtonClicked: {
|
|
root.rootStore.declineRequestToJoinCommunity(requestId, communityId)
|
|
close()
|
|
}
|
|
onClosed: destroy()
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: root.chatCommunitySectionModule
|
|
function onOpenNoPermissionsToJoinPopup(communityName: string, userName: string, communityId: string, requestId: string) {
|
|
Global.openPopup(noPermissionsPopupCmp, {
|
|
communityName: communityName,
|
|
userName: userName,
|
|
communityId: communityId,
|
|
requestId: requestId
|
|
})
|
|
}
|
|
|
|
}
|
|
}
|