feat(ChannelPermissions): Add permissions section in create/edit channel popup

Changes:
1. Make PermissionsView/EditPermissionsView configurable to support channel permissions config
2. Adding channel permissions support in the create/edit channel popup
3. Connect the channel permissions to backend
4. Cleaning unneeded emojiPopup
This commit is contained in:
Alex Jbanca 2024-02-06 11:31:36 +02:00 committed by Alex Jbanca
parent 56d67f5ba2
commit cf82772aed
11 changed files with 887 additions and 479 deletions

View File

@ -27,6 +27,14 @@ Item {
property int padding: Style.current.halfPadding property int padding: Style.current.halfPadding
signal searchButtonClicked() signal searchButtonClicked()
signal displayEditChannelPopup(string chatId,
string chatName,
string chatDescription,
string chatEmoji,
string chatColor,
string chatCategoryId,
int channelPosition,
var deleteDialog)
function addRemoveGroupMember() { function addRemoveGroupMember() {
root.state = d.stateMembersSelectorContent root.state = d.stateMembersSelectorContent
@ -140,7 +148,6 @@ Item {
ChatContextMenuView { ChatContextMenuView {
id: contextMenu id: contextMenu
objectName: "moreOptionsContextMenu" objectName: "moreOptionsContextMenu"
emojiPopup: root.emojiPopup
showDebugOptions: root.rootStore.isDebugEnabled showDebugOptions: root.rootStore.isDebugEnabled
openHandler: function () { openHandler: function () {
if(!chatContentModule) { if(!chatContentModule) {
@ -218,17 +225,11 @@ Item {
onDisplayProfilePopup: { onDisplayProfilePopup: {
Global.openProfilePopup(publicKey) Global.openProfilePopup(publicKey)
} }
onDisplayEditChannelPopup: {
onEditCommunityChannel: { root.displayEditChannelPopup(chatId, chatName, chatDescription,
root.rootStore.editCommunityChannel( chatEmoji, chatColor,
chatId, chatCategoryId, channelPosition,
newName, contextMenu.deleteChatConfirmationDialog);
newDescription,
newEmoji,
newColor,
newCategory,
channelPosition // TODO change this to the signal once it is modifiable
)
} }
onAddRemoveGroupMember: { onAddRemoveGroupMember: {
root.addRemoveGroupMember() root.addRemoveGroupMember()

View File

@ -166,6 +166,19 @@ StatusSectionLayout {
rootStore: root.rootStore rootStore: root.rootStore
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
onSearchButtonClicked: root.openAppSearch() onSearchButtonClicked: root.openAppSearch()
onDisplayEditChannelPopup: {
Global.openPopup(contactColumnLoader.item.createChannelPopup, {
isEdit: true,
chatId: chatId,
channelName: chatName,
channelDescription: chatDescription,
channelEmoji: chatEmoji,
channelColor: chatColor,
categoryId: chatCategoryId,
channelPosition: channelPosition,
deleteChatConfirmationDialog: deleteDialog
});
}
} }
} }

View File

@ -141,7 +141,6 @@ Item {
popupMenu: ChatContextMenuView { popupMenu: ChatContextMenuView {
id: chatContextMenuView id: chatContextMenuView
emojiPopup: root.emojiPopup
showDebugOptions: root.store.isDebugEnabled showDebugOptions: root.store.isDebugEnabled
openHandler: function (id) { openHandler: function (id) {

View File

@ -58,23 +58,9 @@ QtObject {
d.editPermission(key, permissionType, holdings, channels, isPrivate) d.editPermission(key, permissionType, holdings, channels, isPrivate)
} }
// Function duplicating a permission. The new permission will be have a different id and key
function duplicatePermission(index) {
const permission = channelPermissionsModel.get(index)
if (!permission)
return
permission.id = Utils.uuid()
permission.key = Utils.uuid()
permission.holdingsListModel = d.newHoldingsModel(StatusQUtils.ModelUtils.modelToArray(permission.holdingsListModel))
permission.channelsListModel = d.newChannelsModel(StatusQUtils.ModelUtils.modelToArray(permission.channelsListModel))
channelPermissionsModel.append(permission)
}
// Function removing a permission by index // Function removing a permission by index
function removePermission(index) { function removePermission(index) {
channelPermissionsModel.remove(index, 1); return channelPermissionsModel.remove(index, 1);
} }

View File

@ -5,9 +5,11 @@ import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.layouts 1.0 import AppLayouts.Communities.layouts 1.0
import AppLayouts.Communities.views 1.0 import AppLayouts.Communities.views 1.0
import StatusQ.Core 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1 import StatusQ.Core.Utils 0.1
import utils 1.0
import shared.popups 1.0 import shared.popups 1.0
StackView { StackView {
@ -17,6 +19,8 @@ StackView {
required property var assetsModel required property var assetsModel
required property var collectiblesModel required property var collectiblesModel
required property var channelsModel required property var channelsModel
property bool showChannelSelector: true
property alias initialPage: initialItem
// id, name, image, color, owner properties expected // id, name, image, color, owner properties expected
required property var communityDetails required property var communityDetails
@ -38,8 +42,13 @@ StackView {
pop(StackView.Immediate) pop(StackView.Immediate)
} }
function pushEditView(properties) {
root.push(newPermissionView, properties, StackView.Immediate);
}
// Community Permissions possible view contents: // Community Permissions possible view contents:
initialItem: SettingsPage { initialItem: SettingsPage {
id: initialItem
implicitWidth: 0 implicitWidth: 0
title: qsTr("Permissions") title: qsTr("Permissions")
@ -52,7 +61,12 @@ StackView {
onClicked: root.push(newPermissionView, StackView.Immediate) onClicked: root.push(newPermissionView, StackView.Immediate)
} }
contentItem: PermissionsView { contentItem: StatusScrollView {
contentHeight: (permissionsView.height + topPadding)
topPadding: permissionsView.topPadding
padding: 0
PermissionsView {
id: permissionsView
permissionsModel: root.permissionsModel permissionsModel: root.permissionsModel
assetsModel: root.assetsModel assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel collectiblesModel: root.collectiblesModel
@ -72,7 +86,7 @@ StackView {
isPrivateToEditValue: item.isPrivate isPrivateToEditValue: item.isPrivate
} }
root.push(newPermissionView, properties, StackView.Immediate) root.pushEditView(properties);
} }
onDuplicatePermissionRequested: { onDuplicatePermissionRequested: {
@ -85,7 +99,7 @@ StackView {
isPrivateToEditValue: item.isPrivate isPrivateToEditValue: item.isPrivate
} }
root.push(newPermissionView, properties, StackView.Immediate) root.pushEditView(properties);
} }
onRemovePermissionRequested: { onRemovePermissionRequested: {
@ -94,26 +108,40 @@ StackView {
} }
} }
} }
}
Component { Component {
id: newPermissionView id: newPermissionView
SettingsPage { SettingsPage {
id: newPermissionViewPage id: newPermissionViewPage
implicitWidth: 0 implicitWidth: 0
title: isEditState ? qsTr("Edit permission") : qsTr("New permission") title: isEditState ? qsTr("Edit permission") : qsTr("New permission")
property alias isDirty: editPermissionView.dirty
property alias isFullyFilled: editPermissionView.isFullyFilled
property alias isPrivateToEditValue: editPermissionView.isPrivate
property alias permissionTypeToEdit: editPermissionView.permissionType
property alias holdingsToEditModel: editPermissionView.selectedHoldingsModel property alias holdingsToEditModel: editPermissionView.selectedHoldingsModel
property alias channelsToEditModel: editPermissionView.selectedChannelsModel property alias channelsToEditModel: editPermissionView.selectedChannelsModel
property alias permissionTypeToEdit: editPermissionView.permissionType
property alias isPrivateToEditValue: editPermissionView.isPrivate property bool holdingsRequired: editPermissionView.dirtyValues.holdingsRequired
property string permissionKeyToEdit property string permissionKeyToEdit
readonly property bool isEditState: !!permissionKeyToEdit readonly property bool isEditState: !!permissionKeyToEdit
readonly property alias toast: settingsDirtyToastMessage readonly property alias toast: settingsDirtyToastMessage
function resetChanges() {
editPermissionView.resetChanges();
}
function updatePermission() {
editPermissionView.saveChanges();
}
function createPermission() {
editPermissionView.createPermissionClicked();
}
contentItem: EditPermissionView { contentItem: EditPermissionView {
id: editPermissionView id: editPermissionView
@ -123,7 +151,7 @@ StackView {
collectiblesModel: root.collectiblesModel collectiblesModel: root.collectiblesModel
channelsModel: root.channelsModel channelsModel: root.channelsModel
communityDetails: root.communityDetails communityDetails: root.communityDetails
showChannelSelector: root.showChannelSelector
isEditState: newPermissionViewPage.isEditState isEditState: newPermissionViewPage.isEditState
holdingsRequired: selectedHoldingsModel holdingsRequired: selectedHoldingsModel
? selectedHoldingsModel.count > 0 : false ? selectedHoldingsModel.count > 0 : false
@ -191,15 +219,19 @@ StackView {
dirtyValues.selectedHoldingsModel, dirtyValues.selectedHoldingsModel,
["key", "type", "amount"]) : [] ["key", "type", "amount"]) : []
const channels = ModelUtils.modelToArray( const channels = root.showChannelSelector ?
dirtyValues.selectedChannelsModel, ["key"]) ModelUtils.modelToArray(
dirtyValues.selectedChannelsModel, ["key"]) :
ModelUtils.modelToArray(selectedChannelsModel, ["key"])
root.createPermissionRequested( root.createPermissionRequested(
dirtyValues.permissionType, holdings, channels, dirtyValues.permissionType, holdings, channels,
dirtyValues.isPrivate) dirtyValues.isPrivate)
if (root.showChannelSelector) {
root.pop(StackView.Immediate) root.pop(StackView.Immediate)
} }
}
onNavigateToMintTokenSettings: root.navigateToMintTokenSettings(isAssetType) onNavigateToMintTokenSettings: root.navigateToMintTokenSettings(isAssetType)
@ -261,7 +293,8 @@ StackView {
// delay to avoid toast blinking on entry // delay to avoid toast blinking on entry
settingsDirtyToastMessage.active = Qt.binding( settingsDirtyToastMessage.active = Qt.binding(
() => editPermissionView.isEditState && () => editPermissionView.isEditState &&
editPermissionView.dirty) editPermissionView.dirty &&
root.showChannelSelector)
} }
} }
} }

View File

@ -2,11 +2,14 @@ import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import QtQml 2.15
import QtQml.Models 2.15 import QtQml.Models 2.15
import utils 1.0 import utils 1.0
import shared.panels 1.0 import shared.panels 1.0
import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils import StatusQ.Core.Utils 0.1 as StatusQUtils
@ -15,8 +18,10 @@ import StatusQ.Controls.Validators 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
import AppLayouts.Communities.controls 1.0 import AppLayouts.Communities.views 1.0
import AppLayouts.Communities.panels 1.0 import AppLayouts.Communities.panels 1.0
import AppLayouts.Communities.models 1.0
import AppLayouts.Communities.controls 1.0
StatusStackModal { StatusStackModal {
id: root id: root
@ -26,9 +31,12 @@ StatusStackModal {
property bool isDiscordImport // creating new or importing from discord? property bool isDiscordImport // creating new or importing from discord?
property bool isEdit: false property bool isEdit: false
property bool isDeleteable: false property bool isDeleteable: false
property bool viewOnlyCanAddReaction
property bool hideIfPermissionsNotMet
property string communityId: "" property string communityId: ""
property string chatId: "" property string chatId: "_newChannel"
property string categoryId: "" property string categoryId: ""
property string channelName: "" property string channelName: ""
property string channelDescription: "" property string channelDescription: ""
@ -39,20 +47,90 @@ StatusStackModal {
readonly property int communityColorValidator: Utils.Validate.NoEmpty readonly property int communityColorValidator: Utils.Validate.NoEmpty
| Utils.Validate.TextHexColor | Utils.Validate.TextHexColor
property var activeCommunity
required property var assetsModel
required property var collectiblesModel
required property var permissionsModel
required property var channelsModel
readonly property int maxChannelNameLength: 24 readonly property int maxChannelNameLength: 24
readonly property int maxChannelDescLength: 140 readonly property int maxChannelDescLength: 140
// channel signals
signal createCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId) signal createCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId)
signal editCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId) signal editCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId)
signal deleteCommunityChannel() signal deleteCommunityChannel()
// Permissions signals:
// permissions arg is a list of objects with the following properties:
// - key: string
// - id: string
// - permissionType: string
// - holdings: list of objects with the following properties:
// - key: string
// - type: string
// - amount: string
// - channels: list of objects with the following properties:
// - key: string
// - isPrivate: bool
signal addPermissions(var permissions)
signal removePermissions(var permissions)
signal editPermissions(var permissions)
signal setViewOnlyCanAddReaction(bool checked)
signal setHideIfPermissionsNotMet(bool checked)
width: 640 width: 640
leftPadding: 0
rightPadding: 0
currentIndex: d.currentPage
enum CurrentPage {
ChannelDetails, //0
ColorPicker, //1
ChannelPermissions, //2
DiscordImportUploadFile, //3
DiscordImportUploadStart //4
}
QtObject { QtObject {
id: d id: d
property int currentPage: CreateChannelPopup.CurrentPage.ChannelDetails
readonly property QtObject communityDetails: QtObject {
readonly property string id: root.activeCommunity.id
readonly property string name: root.activeCommunity.name
readonly property string image: root.activeCommunity.image
readonly property string color: root.activeCommunity.color
readonly property bool owner: root.activeCommunity.memberRole === Constants.memberRole.owner
readonly property bool admin: root.activeCommunity.memberRole === Constants.memberRole.admin
readonly property bool tokenMaster: root.activeCommunity.memberRole === Constants.memberRole.tokenMaster
}
readonly property ChannelPermissionsModelEditor channelEditModel: ChannelPermissionsModelEditor {
channelId: root.chatId
name: nameInput.input.text
emoji: nameInput.input.asset.emoji
color: colorPanel.color.toString().toUpperCase()
channelsModel: root.channelsModel
permissionsModel: root.permissionsModel
newChannelMode: !root.isEdit
property Connections rootConnection: Connections {
target: root
function onClosed() {
d.channelEditModel.reset()
}
}
}
property bool viewOnlyCanAddReaction: root.viewOnlyCanAddReaction
property bool hideIfPermissionsNotMet: root.hideIfPermissionsNotMet
property bool colorPickerOpened: false
function isFormValid() { function isFormValid() {
return nameInput.valid && descriptionTextArea.valid && return nameInput.valid && descriptionTextArea.valid &&
Utils.validateAndReturnError(colorDialog.color.toString().toUpperCase(), communityColorValidator) === "" Utils.validateAndReturnError(colorPanel.color.toString().toUpperCase(), communityColorValidator) === ""
} }
function openEmojiPopup(leftSide = false) { function openEmojiPopup(leftSide = false) {
@ -70,7 +148,7 @@ StatusStackModal {
categoryId: root.categoryId, categoryId: root.categoryId,
name: StatusQUtils.Utils.filterXSS(nameInput.input.text), name: StatusQUtils.Utils.filterXSS(nameInput.input.text),
description: StatusQUtils.Utils.filterXSS(descriptionTextArea.text), description: StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
color: colorDialog.color.toString().toUpperCase(), color: colorPanel.color.toString().toUpperCase(),
emoji: StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji), emoji: StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji),
options: { options: {
// TODO // TODO
@ -85,66 +163,99 @@ StatusStackModal {
creatingError.open() creatingError.open()
} }
} }
}
stackTitle: isDiscordImport ? qsTr("New Channel With Imported Chat History") : function saveAndClose() {
isEdit ? qsTr("Edit #%1").arg(root.channelName)
: qsTr("New channel")
nextButton: StatusButton {
objectName: "createChannelNextBtn"
font.weight: Font.Medium
text: typeof currentItem.nextButtonText !== "undefined" ? currentItem.nextButtonText : qsTr("Import chat history")
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
loading: root.communitiesStore.discordDataExtractionInProgress
onClicked: {
const nextAction = currentItem.nextAction
if (typeof(nextAction) == "function") {
return nextAction()
}
root.currentIndex++
}
}
finishButton: StatusButton {
objectName: "createOrEditCommunityChannelBtn"
font.weight: Font.Medium
text: isDiscordImport ? qsTr("Import chat history") : isEdit ? qsTr("Save changes") : qsTr("Create channel")
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
onClicked: {
let nextAction = currentItem.nextAction
if (typeof (nextAction) == "function") {
return nextAction()
}
if (!root.isDiscordImport) {
if (!d.isFormValid()) {
scrollView.scrollBackUp()
return
}
let emoji = StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji) let emoji = StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji)
if (!isEdit) { if (!isEdit) {
root.createCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text), root.createCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
StatusQUtils.Utils.filterXSS(descriptionTextArea.text), StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
emoji, emoji,
colorDialog.color.toString().toUpperCase(), colorPanel.color.toString().toUpperCase(),
root.categoryId) root.categoryId)
} else { } else {
root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text), root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
StatusQUtils.Utils.filterXSS(descriptionTextArea.text), StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
emoji, emoji,
colorDialog.color.toString().toUpperCase(), colorPanel.color.toString().toUpperCase(),
root.categoryId) root.categoryId)
} }
if (d.channelEditModel.dirtyPermissions) {
var newPermissions = d.channelEditModel.getAddedPermissions();
if (newPermissions.length > 0) {
root.addPermissions(newPermissions);
}
var editedPermissions = d.channelEditModel.getEditedPermissions();
if (editedPermissions.length > 0) {
root.editPermissions(editedPermissions);
}
var removedPermissions = d.channelEditModel.getRemovedPermissions();
if (removedPermissions.length > 0) {
root.removePermissions(removedPermissions);
}
}
if (root.viewOnlyCanAddReaction !== d.viewOnlyCanAddReaction) {
root.setViewOnlyCanAddReaction(d.viewOnlyCanAddReaction);
}
if (root.hideIfPermissionsNotMet !== d.hideIfPermissionsNotMet) {
root.setHideIfPermissionsNotMet(d.hideIfPermissionsNotMet);
}
// TODO Open the channel once we have designs for it // TODO Open the channel once we have designs for it
root.close() root.close()
} }
} }
stackTitle: isDiscordImport ? qsTr("New Channel With Imported Chat History") :
!!currentItem.stackTitleText ? currentItem.stackTitleText :
(isEdit ? qsTr("Edit #%1").arg(root.channelName) : qsTr("New channel"))
nextButton: StatusButton {
objectName: "createOrEditCommunityChannelBtn"
font.weight: Font.Medium
height: 44
visible: !d.colorPickerOpened
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
text: !!currentItem.nextButtonText ? currentItem.nextButtonText :
d.colorPickerOpened ? qsTr("Set channel color") : (
isDiscordImport ? qsTr("Import chat history") :
isEdit ? qsTr("Save changes") : qsTr("Create channel"))
loading: root.communitiesStore.discordDataExtractionInProgress
onClicked: {
let nextAction = currentItem.nextAction
if (typeof (nextAction) == "function") {
return nextAction()
}
}
}
finishButton: StatusButton {
objectName: "createChannelNextBtn"
font.weight: Font.Medium
height: 44
text: (typeof currentItem.nextButtonText !== "undefined") ? currentItem.nextButtonText :
qsTr("Import chat history")
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
onClicked: {
const nextAction = currentItem.nextAction
if (typeof(nextAction) == "function") {
return nextAction()
}
}
}
//TODO
onCurrentIndexChanged: {
d.colorPickerOpened = false;
} }
readonly property StatusButton clearFilesButton: StatusButton { readonly property StatusButton clearFilesButton: StatusButton {
font.weight: Font.Medium font.weight: Font.Medium
text: qsTr("Clear all") text: qsTr("Clear all")
height: 44
type: StatusBaseButton.Type.Danger type: StatusBaseButton.Type.Danger
visible: typeof currentItem.isFileListView !== "undefined" && currentItem.isFileListView visible: typeof currentItem.isFileListView !== "undefined" && currentItem.isFileListView
enabled: !fileListView.fileListModelEmpty && !root.communitiesStore.discordDataExtractionInProgress enabled: !fileListView.fileListModelEmpty && !root.communitiesStore.discordDataExtractionInProgress
@ -153,12 +264,32 @@ StatusStackModal {
readonly property StatusButton deleteChannelButton: StatusButton { readonly property StatusButton deleteChannelButton: StatusButton {
objectName: "deleteCommunityChannelBtn" objectName: "deleteCommunityChannelBtn"
visible: isEdit && isDeleteable && !isDiscordImport && typeof(replaceItem) === "undefined" height: 44
text: qsTr("Delete channel") visible: isEdit && isDeleteable && !isDiscordImport && (d.currentPage === CreateChannelPopup.CurrentPage.ChannelDetails) ||
!!currentItem.deleteButtonText
text: (d.currentPage === CreateChannelPopup.CurrentPage.ChannelPermissions) ? currentItem.deleteButtonText : qsTr("Delete channel")
enabled: (d.currentPage === CreateChannelPopup.CurrentPage.ChannelPermissions) ? currentItem.deleteButtonEnabled : true
type: StatusBaseButton.Type.Danger type: StatusBaseButton.Type.Danger
onClicked: root.deleteCommunityChannel() onClicked: {
const nextAction = currentItem.nextDeleteAction
if (typeof(nextAction) == "function") {
return nextAction()
} else {
root.deleteCommunityChannel();
}
}
} }
property Item backButton: StatusBackButton {
visible: d.currentPage !== CreateChannelPopup.CurrentPage.ChannelDetails
onClicked: {
d.currentPage = (d.currentPage === CreateChannelPopup.CurrentPage.DiscordImportUploadStart) ?
CreateChannelPopup.CurrentPage.DiscordImportUploadFile : CreateChannelPopup.CurrentPage.ChannelDetails
}
}
leftButtons: [ backButton ]
rightButtons: [clearFilesButton, deleteChannelButton, nextButton, finishButton] rightButtons: [clearFilesButton, deleteChannelButton, nextButton, finishButton]
onAboutToShow: { onAboutToShow: {
@ -179,7 +310,7 @@ StatusStackModal {
if (root.channelEmoji) { if (root.channelEmoji) {
nameInput.input.asset.emoji = root.channelEmoji nameInput.input.asset.emoji = root.channelEmoji
} }
colorDialog.color = root.channelColor colorPanel.color = root.channelColor
} else { } else {
nameInput.input.asset.isLetterIdenticon = true; nameInput.input.asset.isLetterIdenticon = true;
} }
@ -188,10 +319,8 @@ StatusStackModal {
} }
readonly property list<Item> discordPages: [ readonly property list<Item> discordPages: [
ColumnLayout { Item {
id: fileListView id: fileListViewItem
spacing: 24
readonly property bool isFileListView: true readonly property bool isFileListView: true
readonly property var fileListModel: root.communitiesStore.discordFileList readonly property var fileListModel: root.communitiesStore.discordFileList
@ -205,11 +334,17 @@ StatusStackModal {
: fileListModel.selectedCount ? qsTr("Validate (%1/%2) files").arg(fileListModel.selectedCount).arg(fileListModel.count) : fileListModel.selectedCount ? qsTr("Validate (%1/%2) files").arg(fileListModel.selectedCount).arg(fileListModel.count)
: qsTr("Start channel import") : qsTr("Start channel import")
readonly property var nextAction: function () { readonly property var nextAction: function () {
if (!fileListView.fileListModel.selectedFilesValid) if (!fileListViewItem.fileListModel.selectedFilesValid)
return root.communitiesStore.requestExtractChannelsAndCategories() return root.communitiesStore.requestExtractChannelsAndCategories()
root.currentIndex++ d.currentPage = CreateChannelPopup.CurrentPage.DiscordImportUploadStart;
} }
ColumnLayout {
id: fileListView
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 24
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
@ -219,12 +354,12 @@ StatusStackModal {
maximumLineCount: 2 maximumLineCount: 2
wrapMode: Text.Wrap wrapMode: Text.Wrap
elide: Text.ElideRight elide: Text.ElideRight
text: fileListView.fileListModelEmpty ? qsTr("Select Discord channel JSON files to import") : text: fileListViewItem.fileListModelEmpty ? qsTr("Select Discord channel JSON files to import") :
root.communitiesStore.discordImportErrorsCount ? qsTr("Some of your community files cannot be used") : root.communitiesStore.discordImportErrorsCount ? qsTr("Some of your community files cannot be used") :
qsTr("Uncheck any files you would like to exclude from the import") qsTr("Uncheck any files you would like to exclude from the import")
} }
StatusBaseText { StatusBaseText {
visible: fileListView.fileListModelEmpty && !issuePill.visible visible: fileListViewItem.fileListModelEmpty && !issuePill.visible
font.pixelSize: 12 font.pixelSize: 12
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
text: qsTr("(JSON file format only)") text: qsTr("(JSON file format only)")
@ -233,7 +368,7 @@ StatusStackModal {
id: issuePill id: issuePill
type: root.communitiesStore.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning type: root.communitiesStore.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
count: root.communitiesStore.discordImportErrorsCount || root.communitiesStore.discordImportWarningsCount || 0 count: root.communitiesStore.discordImportErrorsCount || root.communitiesStore.discordImportWarningsCount || 0
visible: !!count && !fileListView.fileListModelEmpty visible: !!count && !fileListViewItem.fileListModelEmpty
} }
StatusButton { StatusButton {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
@ -250,7 +385,7 @@ StatusStackModal {
color: Theme.palette.baseColor4 color: Theme.palette.baseColor4
ColumnLayout { ColumnLayout {
visible: fileListView.fileListModelEmpty visible: fileListViewItem.fileListModelEmpty
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 60 anchors.topMargin: 60
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -261,32 +396,29 @@ StatusStackModal {
asset.name: "info" asset.name: "info"
} }
StatusBaseText { StatusBaseText {
id: infoText1
Layout.topMargin: 8 Layout.topMargin: 8
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
text: qsTr("Export the Discord channels chat history data using %1").arg("<a href='https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.40.4'>DiscordChatExporter</a>") text: qsTr("Export the Discord channels chat history data using %1").arg("<a href='https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.40.4'>DiscordChatExporter</a>")
onLinkActivated: Global.openLink(link) onLinkActivated: Global.openLink(link)
HoverHandler {
id: handler1
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
cursorShape: handler1.hovered && parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
StatusBaseText { StatusBaseText {
id: infoText2
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
text: qsTr("Refer to this <a href='https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md'>documentation</a> if you have any queries") text: qsTr("Refer to this <a href='https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md'>documentation</a> if you have any queries")
onLinkActivated: Global.openLink(link) onLinkActivated: Global.openLink(link)
HoverHandler { onHoveredLinkChanged: print(hoveredLink)
id: handler2
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
cursorShape: handler2.hovered && parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
} }
@ -302,12 +434,12 @@ StatusStackModal {
} }
StatusListView { StatusListView {
visible: !fileListView.fileListModelEmpty visible: !fileListViewItem.fileListModelEmpty
enabled: !root.communitiesStore.discordDataExtractionInProgress enabled: !root.communitiesStore.discordDataExtractionInProgress
anchors.fill: parent anchors.fill: parent
leftMargin: 8 leftMargin: 8
rightMargin: 8 rightMargin: 8
model: fileListView.fileListModel model: fileListViewItem.fileListModel
header: !atYBeginning ? floatingDivComp : null header: !atYBeginning ? floatingDivComp : null
headerPositioning: ListView.OverlayHeader headerPositioning: ListView.OverlayHeader
footer: !atYEnd ? floatingDivComp : null footer: !atYEnd ? floatingDivComp : null
@ -352,7 +484,7 @@ StatusStackModal {
} }
} }
} }
}
FileDialog { FileDialog {
id: fileDialog id: fileDialog
title: qsTr("Choose files to import") title: qsTr("Choose files to import")
@ -368,11 +500,7 @@ StatusStackModal {
} }
} }
}, },
Item {
ColumnLayout {
id: categoriesAndChannelsView
spacing: 24
readonly property bool canGoNext: root.communitiesStore.discordChannelsModel.hasSelectedItems readonly property bool canGoNext: root.communitiesStore.discordChannelsModel.hasSelectedItems
readonly property var nextAction: function () { readonly property var nextAction: function () {
d.requestImportDiscordChannel() d.requestImportDiscordChannel()
@ -382,6 +510,13 @@ StatusStackModal {
root.replace(progressComponent) root.replace(progressComponent)
} }
ColumnLayout {
id: categoriesAndChannelsView
spacing: 24
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
Component { Component {
id: progressComponent id: progressComponent
DiscordImportProgressContents { DiscordImportProgressContents {
@ -480,6 +615,7 @@ StatusStackModal {
} }
} }
} }
}
] ]
Connections { Connections {
@ -512,18 +648,17 @@ StatusStackModal {
ColumnLayout { ColumnLayout {
id: content id: content
width: scrollView.availableWidth width: scrollView.availableWidth
spacing: 0 spacing: Style.current.padding
StatusInput { StatusInput {
id: nameInput id: nameInput
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
input.edit.objectName: "createOrEditCommunityChannelNameInput" input.edit.objectName: "createOrEditCommunityChannelNameInput"
label: qsTr("Channel name") label: qsTr("Channel name")
charLimit: root.maxChannelNameLength charLimit: root.maxChannelNameLength
placeholderText: qsTr("# Name the channel") placeholderText: qsTr("# Name the channel")
input.onTextChanged: { input.onTextChanged: {
const cursorPosition = input.cursorPosition const cursorPosition = input.cursorPosition
input.text = Utils.convertSpacesToDashes(input.text) input.text = Utils.convertSpacesToDashes(input.text)
@ -532,7 +667,7 @@ StatusStackModal {
input.letterIconName = text input.letterIconName = text
} }
} }
input.asset.color: colorDialog.color.toString() input.asset.color: colorPanel.color.toString()
input.rightComponent: StatusRoundButton { input.rightComponent: StatusRoundButton {
objectName: "StatusChannelPopup_emojiButton" objectName: "StatusChannelPopup_emojiButton"
implicitWidth: 32 implicitWidth: 32
@ -559,48 +694,48 @@ StatusStackModal {
} }
Item { Item {
Layout.preferredHeight: 16
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 82
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
StatusBaseText {
width: parent.width
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
text: qsTr("Channel colour")
} }
StatusPickerButton {
id: colorSelectorButton
ColorPicker { property string validationError: ""
id: colorDialog width: parent.width
Layout.fillWidth: true anchors.bottom: parent.bottom
title: qsTr("Channel colour") bgColor: colorPanel.colorSelected ? colorPanel.color : Theme.palette.baseColor2
color: root.isEdit && root.channelColor ? root.channelColor : Theme.palette.primaryColor1 contentColor: colorPanel.colorSelected ? Theme.palette.white : Theme.palette.baseColor1
onPick: root.replace(colorPanel) text: colorPanel.colorSelected ? colorPanel.color.toString().toUpperCase() : qsTr("Pick a colour")
onClicked: { d.currentPage = CreateChannelPopup.CurrentPage.ColorPicker; d.colorPickerOpened = true; }
Component { onTextChanged: {
id: colorPanel if (colorPanel.colorSelected) {
ColorPanel { validationError = Utils.validateAndReturnError(text, communityColorValidator)
title: qsTr("Channel colour")
buttonText: qsTr("Select Colour")
Component.onCompleted: color = colorDialog.color
onAccepted: {
colorDialog.color = color
root.replaceItem = undefined
} }
} }
} }
} }
Item {
Layout.preferredHeight: 16
Layout.fillWidth: true
}
StatusInput { StatusInput {
id: descriptionTextArea id: descriptionTextArea
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Style.current.halfPadding
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
input.edit.objectName: "createOrEditCommunityChannelDescriptionInput" input.edit.objectName: "createOrEditCommunityChannelDescriptionInput"
input.verticalAlignment: TextEdit.AlignTop input.verticalAlignment: TextEdit.AlignTop
label: qsTr("Description") label: qsTr("Description")
charLimit: 140 charLimit: 140
placeholderText: qsTr("Describe the channel") placeholderText: qsTr("Describe the channel")
input.multiline: true input.multiline: true
minimumHeight: 88 minimumHeight: 108
maximumHeight: 88 maximumHeight: 108
validators: [ validators: [
StatusMinLengthValidator { StatusMinLengthValidator {
minLength: 1 minLength: 1
@ -612,6 +747,189 @@ StatusStackModal {
} }
] ]
} }
Separator {
Layout.fillWidth: true
visible: viewOnlyCanAddReactionCheckbox.visible
}
StatusCheckBox {
id: viewOnlyCanAddReactionCheckbox
Layout.fillWidth: true
Layout.preferredHeight: 48
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
leftSide: false
text: qsTr("Hide channel from members who don't have permissions to view the channel")
visible: false //TODO: Enable connect to the backend when it's ready https://github.com/status-im/status-desktop/issues/13291
checked: d.hideIfPermissionsNotMet
onToggled: {
d.hideIfPermissionsNotMet = checked;
}
}
Separator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 56
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
StatusBaseText {
text: qsTr("Permissions")
}
Item { Layout.fillWidth: true }
StatusButton {
text: qsTr("Add permission")
enabled: !!nameInput.text
property ListModel channelToAddPermission: ListModel { }
onClicked: {
channelToAddPermission.clear();
channelToAddPermission.append({"key": root.chatId, "name": nameInput.text});
const propertiess = {
channelsToEditModel: channelToAddPermission,
header: null,
topPadding: -root.subHeaderPadding - 8,
leftPadding: 0,
rightPadding: 16,
viewWidth: scrollView.availableWidth - 32
};
editPermissionView.pushEditView(propertiess);
d.currentPage = CreateChannelPopup.CurrentPage.ChannelPermissions;
}
}
}
PermissionsView {
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
viewWidth: (scrollView.availableWidth - 32)
permissionsModel: d.channelEditModel.channelPermissionsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
viewOnlyCanAddReaction: root.viewOnlyCanAddReaction
channelsModel: d.channelEditModel.liveChannelsModel
communityDetails: d.communityDetails
showChannelOptions: true
allowIntroPanel: false
onRemovePermissionRequested: {
console.assert(d.channelEditModel.removePermission(index))
}
onDuplicatePermissionRequested: {
const item = StatusQUtils.ModelUtils.get(d.channelEditModel.channelPermissionsModel, index);
const properties = {
holdingsToEditModel: item.holdingsListModel,
channelsToEditModel: item.channelsListModel,
permissionTypeToEdit: item.permissionType,
isPrivateToEditValue: item.isPrivate,
header: null,
topPadding: -root.subHeaderPadding - 8,
leftPadding: 0,
rightPadding: 16,
viewWidth: scrollView.availableWidth - 32
}
editPermissionView.pushEditView(properties);
editPermissionView.currentItem.resetChanges()
d.currentPage = CreateChannelPopup.CurrentPage.ChannelPermissions;
}
onEditPermissionRequested: {
const item = d.channelEditModel.channelPermissionsModel.get(index);
const requireHoldings = (item.holdingsListModel.count ?? item.holdingsListModel.rowCount()) > 0;
const properties = {
permissionKeyToEdit: item.key,
holdingsToEditModel: item.holdingsListModel,
channelsToEditModel: item.channelsListModel,
permissionTypeToEdit: item.permissionType,
isPrivateToEditValue: item.isPrivate,
header: null,
topPadding: -root.subHeaderPadding - 8,
leftPadding: 0,
rightPadding: 16,
viewWidth: scrollView.availableWidth - 32
}
editPermissionView.pushEditView(properties);
editPermissionView.currentItem.resetChanges()
d.currentPage = CreateChannelPopup.CurrentPage.ChannelPermissions;
}
onUserRestrictionsToggled: {
d.viewOnlyCanAddReaction = checked;
}
}
}
readonly property var nextAction: function () {
if (!root.isDiscordImport) {
if (!d.isFormValid()) {
scrollView.scrollBackUp()
return
}
d.saveAndClose()
} else {
d.currentPage = CreateChannelPopup.CurrentPage.DiscordImportUploadFile;
}
}
},
ColorPanel {
id: colorPanel
readonly property string stackTitleText: qsTr("Channel Colour")
readonly property string nextButtonText: qsTr("Select Channel Colour")
padding: 0
leftPadding: 16
rightPadding: 16
height: Math.min(parent.height, 624)
property bool colorSelected: root.isEdit && root.channelColor
color: root.isEdit && root.channelColor ? root.channelColor : Theme.palette.primaryColor1
onAccepted: {
colorSelected = true; d.colorPickerOpened = false; d.currentPage = CreateChannelPopup.CurrentPage.ChannelDetails;
}
readonly property var nextAction: function () {
accepted();
}
},
PermissionsSettingsPanel {
id: editPermissionView
leftPadding: 16
rightPadding: 16
initialPage.header: null
initialPage.topPadding: 0
initialPage.leftPadding: 0
viewWidth: scrollView.availableWidth - 32
readonly property string nextButtonText: !!currentItem.permissionKeyToEdit ?
qsTr("Update permission") : qsTr("Create permission")
readonly property string stackTitleText: !!currentItem.permissionKeyToEdit ?
qsTr("Edit #%1 permission").arg(nameInput.text) : qsTr("New #%1 permission").arg(nameInput.text)
readonly property string deleteButtonText: !!currentItem.permissionKeyToEdit ?
qsTr("Revert changes") : ""
readonly property bool canGoNext: !!currentItem && currentItem.isDirty && currentItem.isFullyFilled ? currentItem.isDirty && currentItem.isFullyFilled : false
readonly property bool deleteButtonEnabled: editPermissionView.canGoNext
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
permissionsModel: d.channelEditModel.channelPermissionsModel
channelsModel: d.channelEditModel.liveChannelsModel
communityDetails: d.communityDetails
showChannelSelector: false
readonly property var nextDeleteAction: function () {
if (!!currentItem.permissionKeyToEdit) {
currentItem.resetChanges();
}
}
readonly property var nextAction: function () {
if (!!currentItem.permissionKeyToEdit) {
currentItem.updatePermission();
} else {
currentItem.createPermission();
}
}
onCreatePermissionRequested: {
d.channelEditModel.appendPermission(holdings, channels, permissionType, isPrivate)
d.currentPage = CreateChannelPopup.CurrentPage.ChannelDetails;
}
onUpdatePermissionRequested: {
d.channelEditModel.editPermission(key, permissionType, holdings, channels, isPrivate)
d.currentPage = CreateChannelPopup.CurrentPage.ChannelDetails;
} }
} }
] ]

View File

@ -16,6 +16,7 @@ StatusDropdown {
property int mode: PermissionsDropdown.Mode.Add property int mode: PermissionsDropdown.Mode.Add
property int initialPermissionType: PermissionTypes.Type.None property int initialPermissionType: PermissionTypes.Type.None
property bool allowCommunityOptions: true
property bool enableAdminPermission: true property bool enableAdminPermission: true
@ -97,13 +98,14 @@ StatusDropdown {
CustomSeparator { CustomSeparator {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: d.sectionHeight Layout.preferredHeight: d.sectionHeight
visible: root.allowCommunityOptions
text: qsTr("Community") text: qsTr("Community")
} }
CustomPermissionListItem { CustomPermissionListItem {
permissionType: PermissionTypes.Type.Admin permissionType: PermissionTypes.Type.Admin
enabled: root.enableAdminPermission enabled: root.enableAdminPermission
visible: root.allowCommunityOptions
Layout.fillWidth: true Layout.fillWidth: true
objectName: "becomeAdmin" objectName: "becomeAdmin"
@ -112,6 +114,7 @@ StatusDropdown {
CustomPermissionListItem { CustomPermissionListItem {
permissionType: PermissionTypes.Type.Member permissionType: PermissionTypes.Type.Member
visible: root.allowCommunityOptions
Layout.fillWidth: true Layout.fillWidth: true
objectName: "becomeMember" objectName: "becomeMember"
} }

View File

@ -42,6 +42,7 @@ Item {
required property CurrenciesStore currencyStore required property CurrenciesStore currencyStore
property bool hasAddedContacts: false property bool hasAddedContacts: false
property var communityData property var communityData
property alias createChannelPopup: createChannelPopup
// Community transfer ownership related props: // Community transfer ownership related props:
required property bool isPendingOwnershipRequest required property bool isPendingOwnershipRequest
@ -52,6 +53,11 @@ Item {
communityData.memberRole === Constants.memberRole.admin || communityData.memberRole === Constants.memberRole.admin ||
communityData.memberRole === Constants.memberRole.tokenMaster communityData.memberRole === Constants.memberRole.tokenMaster
readonly property var permissionsModel: {
root.store.prepareTokenModelForCommunity(communityData.id)
return root.store.permissionsModel
}
signal infoButtonClicked signal infoButtonClicked
signal manageButtonClicked signal manageButtonClicked
@ -297,7 +303,6 @@ Item {
chatListPopupMenu: ChatContextMenuView { chatListPopupMenu: ChatContextMenuView {
id: chatContextMenuView id: chatContextMenuView
emojiPopup: root.emojiPopup
showDebugOptions: root.store.isDebugEnabledfir showDebugOptions: root.store.isDebugEnabledfir
// TODO pass the chatModel in its entirety instead of fetching the JSOn using just the id // TODO pass the chatModel in its entirety instead of fetching the JSOn using just the id
@ -368,17 +373,18 @@ Item {
onDisplayProfilePopup: { onDisplayProfilePopup: {
Global.openProfilePopup(publicKey) Global.openProfilePopup(publicKey)
} }
onDisplayEditChannelPopup: {
onEditCommunityChannel: { Global.openPopup(createChannelPopup, {
communitySectionModule.editCommunityChannel( isEdit: true,
chatId, channelName: chatName,
newName, channelDescription: chatDescription,
newDescription, channelEmoji: chatEmoji,
newEmoji, channelColor: chatColor,
newColor, categoryId: chatCategoryId,
newCategory, chatId: chatContextMenuView.chatId,
channelPosition // TODO change this to the signal once it is modifiable channelPosition: channelPosition,
) deleteChatConfirmationDialog: deleteChatConfirmationDialog
});
} }
} }
} }
@ -607,11 +613,63 @@ Item {
id: createChannelPopup id: createChannelPopup
CreateChannelPopup { CreateChannelPopup {
communitiesStore: root.communitiesStore communitiesStore: root.communitiesStore
assetsModel: root.store.assetsModel
collectiblesModel: root.store.collectiblesModel
permissionsModel: root.store.permissionsModel
channelsModel: root.store.chatCommunitySectionModule.model
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
activeCommunity: root.communityData
property int channelPosition: -1
property var deleteChatConfirmationDialog
onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor, onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor,
chCategoryId) { chCategoryId) {
root.store.createCommunityChannel(chName, chDescription, chEmoji, chColor, root.store.createCommunityChannel(chName, chDescription, chEmoji, chColor,
chCategoryId) chCategoryId)
chatId = root.store.currentChatContentModule().chatDetails.id
}
onEditCommunityChannel: {
root.store.editCommunityChannel(chatId,
chName,
chDescription,
chEmoji,
chColor,
chCategoryId,
channelPosition);
}
onAddPermissions: function (permissions) {
for (var i = 0; i < permissions.length; i++) {
root.store.permissionsStore.createPermission(permissions[i].holdingsListModel,
permissions[i].permissionType,
permissions[i].isPrivate,
permissions[i].channelsListModel)
}
}
onRemovePermissions: function (permissions) {
for (var i = 0; i < permissions.length; i++) {
root.store.permissionsStore.removePermission(permissions[i].id)
}
}
onEditPermissions: function (permissions) {
for (var i = 0; i < permissions.length; i++) {
root.store.permissionsStore.editPermission(permissions[i].id,
permissions[i].holdingsListModel,
permissions[i].permissionType,
permissions[i].channelsListModel,
permissions[i].isPrivate)
}
}
onSetViewOnlyCanAddReaction: function (checked) {
root.store.permissionsStore.setViewOnlyCanAddReaction(chatId, checked)
}
onSetHideIfPermissionsNotMet: function (checked) {
root.store.permissionsStore.setHideIfPermissionsNotMet(chatId, checked)
}
onDeleteCommunityChannel: {
Global.openPopup(deleteChatConfirmationDialog);
close()
} }
onClosed: { onClosed: {
destroy() destroy()

View File

@ -38,14 +38,14 @@ StatusScrollView {
readonly property alias dirtyValues: d.dirtyValues readonly property alias dirtyValues: d.dirtyValues
readonly property bool isFullyFilled: readonly property bool isFullyFilled: (dirtyValues.selectedHoldingsModel.count > 0 || !whoHoldsSwitch.checked) &&
(dirtyValues.selectedHoldingsModel.count > 0 || !whoHoldsSwitch.checked) &&
dirtyValues.permissionType !== PermissionTypes.Type.None && dirtyValues.permissionType !== PermissionTypes.Type.None &&
(d.isCommunityPermission || dirtyValues.selectedChannelsModel.count > 0) (d.isCommunityPermission || !showChannelSelector || dirtyValues.selectedChannelsModel.count > 0)
property int permissionType: PermissionTypes.Type.None property int permissionType: PermissionTypes.Type.None
property bool isPrivate: false property bool isPrivate: false
property bool holdingsRequired: true property bool holdingsRequired: true
property bool showChannelSelector: true
// roles: type, key, name, amount, imageSource // roles: type, key, name, amount, imageSource
property var selectedHoldingsModel: ListModel {} property var selectedHoldingsModel: ListModel {}
@ -430,6 +430,7 @@ StatusScrollView {
PermissionsDropdown { PermissionsDropdown {
id: permissionsDropdown id: permissionsDropdown
allowCommunityOptions: root.showChannelSelector
initialPermissionType: d.dirtyValues.permissionType initialPermissionType: d.dirtyValues.permissionType
enableAdminPermission: root.communityDetails.owner enableAdminPermission: root.communityDetails.owner
@ -454,7 +455,7 @@ StatusScrollView {
} }
} }
SequenceColumnLayout.Separator {} SequenceColumnLayout.Separator { visible: root.showChannelSelector }
StatusItemSelector { StatusItemSelector {
id: inSelector id: inSelector
@ -463,7 +464,7 @@ StatusScrollView {
addButton.visible: editable addButton.visible: editable
itemsClickable: editable itemsClickable: editable
visible: root.showChannelSelector
Layout.fillWidth: true Layout.fillWidth: true
icon: d.isCommunityPermission ? Style.svg("communities") : Style.svg("create-category") icon: d.isCommunityPermission ? Style.svg("communities") : Style.svg("create-category")
title: qsTr("In") title: qsTr("In")
@ -566,7 +567,7 @@ StatusScrollView {
color: Theme.palette.baseColor2 color: Theme.palette.baseColor2
} }
HidePermissionPanel { StatusIconSwitch {
Layout.topMargin: 12 Layout.topMargin: 12
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
@ -574,6 +575,9 @@ StatusScrollView {
enabled: d.dirtyValues.permissionType !== PermissionTypes.Type.Admin enabled: d.dirtyValues.permissionType !== PermissionTypes.Type.Admin
checked: d.dirtyValues.isPrivate checked: d.dirtyValues.isPrivate
title: qsTr("Hide permission")
subTitle: qsTr("Make this permission hidden from members who dont meet its requirements")
icon: "hide"
onToggled: d.dirtyValues.isPrivate = checked onToggled: d.dirtyValues.isPrivate = checked
} }
@ -600,7 +604,7 @@ StatusScrollView {
StatusWarningBox { StatusWarningBox {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Style.current.padding Layout.topMargin: Style.current.padding
visible: root.showChannelSelector
icon: "desktop" icon: "desktop"
text: qsTr("Any changes to community permissions will take effect after the control node receives and processes them") text: qsTr("Any changes to community permissions will take effect after the control node receives and processes them")
borderColor: Theme.palette.baseColor1 borderColor: Theme.palette.baseColor1
@ -613,7 +617,7 @@ StatusScrollView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Style.current.bigPadding Layout.topMargin: Style.current.bigPadding
visible: !root.isEditState visible: !root.isEditState && root.showChannelSelector
text: qsTr("Create permission") text: qsTr("Create permission")
enabled: root.isFullyFilled enabled: root.isFullyFilled
&& !root.permissionDuplicated && !root.permissionDuplicated

View File

@ -2,16 +2,27 @@ import QtQuick 2.14
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import shared.status 1.0
import shared.popups 1.0 import shared.popups 1.0
import utils 1.0 import utils 1.0
import AppLayouts.Communities.controls 1.0 import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.panels 1.0 import AppLayouts.Communities.panels 1.0
StatusScrollView { ColumnLayout {
id: root id: root
width: root.viewWidth
property int topPadding: count ? 16 : 0
spacing: 24
QtObject {
id: d
property int permissionIndexToRemove
}
required property var permissionsModel required property var permissionsModel
required property var assetsModel required property var assetsModel
@ -22,27 +33,16 @@ StatusScrollView {
required property var communityDetails required property var communityDetails
property int viewWidth: 560 // by design property int viewWidth: 560 // by design
property bool viewOnlyCanAddReaction
property bool showChannelOptions: false
property bool allowIntroPanel: true
signal editPermissionRequested(int index) signal editPermissionRequested(int index)
signal duplicatePermissionRequested(int index) signal duplicatePermissionRequested(int index)
signal removePermissionRequested(int index) signal removePermissionRequested(int index)
signal userRestrictionsToggled(bool checked)
readonly property alias count: repeater.count readonly property alias count: repeater.count
padding: 0
topPadding: count ? 16 : 0
QtObject {
id: d
property int permissionIndexToRemove
}
ColumnLayout {
id: mainLayout
width: root.viewWidth
spacing: 24
ListModel { ListModel {
id: communityItemModel id: communityItemModel
@ -58,7 +58,7 @@ StatusScrollView {
IntroPanel { IntroPanel {
Layout.fillWidth: true Layout.fillWidth: true
visible: root.count === 0 visible: (root.count === 0 && root.allowIntroPanel)
image: Style.png("community/permissions2_3") image: Style.png("community/permissions2_3")
title: qsTr("Permissions") title: qsTr("Permissions")
@ -100,8 +100,8 @@ StatusScrollView {
showButtons: (model.permissionType !== PermissionTypes.Type.TokenMaster && showButtons: (model.permissionType !== PermissionTypes.Type.TokenMaster &&
model.permissionType !== PermissionTypes.Type.Owner) && model.permissionType !== PermissionTypes.Type.Owner) &&
(root.communityDetails.owner || (!!root.communityDetails && (root.communityDetails.owner ||
((root.communityDetails.admin || root.communityDetails.tokenMaster) && model.permissionType !== PermissionTypes.Type.Admin)) ((root.communityDetails.admin || root.communityDetails.tokenMaster) && model.permissionType !== PermissionTypes.Type.Admin)))
onEditClicked: root.editPermissionRequested(model.index) onEditClicked: root.editPermissionRequested(model.index)
onDuplicateClicked: root.duplicatePermissionRequested(model.index) onDuplicateClicked: root.duplicatePermissionRequested(model.index)
@ -112,8 +112,31 @@ StatusScrollView {
} }
} }
} }
StatusBaseText {
id: noPermissionsLabel
Layout.fillWidth: true
Layout.fillHeight: true
visible: (root.count === 0 && root.showChannelOptions)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr("No channel permissions")
color: Style.current.secondaryText
} }
StatusIconSwitch {
Layout.fillWidth: true
padding: 0
visible: false //TODO: enable this when we have the backend support https://github.com/status-im/status-desktop/issues/13292
//visible: root.showChannelOptions
title: qsTr("Users with view only permissions can add reactions")
icon: "emojis"
checked: root.viewOnlyCanAddReaction
onToggled: {
root.userRestrictionsToggled(checked);
}
}
ConfirmationDialog { ConfirmationDialog {
id: declineAllDialog id: declineAllDialog

View File

@ -26,10 +26,11 @@ StatusMenu {
property bool chatMuted: false property bool chatMuted: false
property int channelPosition: -1 property int channelPosition: -1
property string chatCategoryId: "" property string chatCategoryId: ""
property var emojiPopup
property bool showDebugOptions: false property bool showDebugOptions: false
property alias deleteChatConfirmationDialog: deleteChatConfirmationDialogComponent
signal displayProfilePopup(string publicKey) signal displayProfilePopup(string publicKey)
signal displayEditChannelPopup(string chatId)
signal requestAllHistoricMessages(string chatId) signal requestAllHistoricMessages(string chatId)
signal unmuteChat(string chatId) signal unmuteChat(string chatId)
signal muteChat(string chatId, int interval) signal muteChat(string chatId, int interval)
@ -140,38 +141,7 @@ StatusMenu {
icon.name: "edit" icon.name: "edit"
enabled: root.isCommunityChat && root.amIChatAdmin enabled: root.isCommunityChat && root.amIChatAdmin
onTriggered: { onTriggered: {
Global.openPopup(editChannelPopup, { root.displayEditChannelPopup(root.chatId);
isEdit: true,
channelName: root.chatName,
channelDescription: root.chatDescription,
channelEmoji: root.chatEmoji,
channelColor: root.chatColor,
categoryId: root.chatCategoryId
});
}
}
Component {
id: editChannelPopup
CreateChannelPopup {
anchors.centerIn: parent
isEdit: true
isDeleteable: root.isCommunityChat
emojiPopup: root.emojiPopup
onCreateCommunityChannel: {
root.createCommunityChannel(root.chatId, chName, chDescription, chEmoji, chColor);
}
onEditCommunityChannel: {
root.editCommunityChannel(root.chatId, chName, chDescription, chEmoji, chColor,
chCategoryId);
}
onDeleteCommunityChannel: {
Global.openPopup(deleteChatConfirmationDialogComponent)
close()
}
onClosed: {
destroy()
}
} }
} }