feat(CommunityPermissions): Implement duplication checking
Moreover: - adapt flow to the design - introduce ModelChangeTracker utility component Closes: #9048
This commit is contained in:
parent
c78eaef2b6
commit
a97c8a720e
|
@ -27,6 +27,7 @@ SplitView {
|
||||||
|
|
||||||
isEditState: isEditStateCheckBox.checked
|
isEditState: isEditStateCheckBox.checked
|
||||||
isPrivate: isPrivateCheckBox.checked
|
isPrivate: isPrivateCheckBox.checked
|
||||||
|
duplicationWarningVisible: isDuplicationWarningVisibleCheckBox.checked
|
||||||
|
|
||||||
store: CommunitiesStore {
|
store: CommunitiesStore {
|
||||||
readonly property var assetsModel: AssetsModel {}
|
readonly property var assetsModel: AssetsModel {}
|
||||||
|
@ -50,10 +51,6 @@ SplitView {
|
||||||
logs.logEvent("CommunitiesStore::editPermission - index: " + index)
|
logs.logEvent("CommunitiesStore::editPermission - index: " + index)
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicatePermission(index) {
|
|
||||||
logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePermission(index) {
|
function removePermission(index) {
|
||||||
logs.logEvent("CommunitiesStore::removePermission - index: " + index)
|
logs.logEvent("CommunitiesStore::removePermission - index: " + index)
|
||||||
}
|
}
|
||||||
|
@ -111,6 +108,12 @@ SplitView {
|
||||||
|
|
||||||
text: "Is private"
|
text: "Is private"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: isDuplicationWarningVisibleCheckBox
|
||||||
|
|
||||||
|
text: "Is duplication warning visible"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|
|
@ -39,10 +39,6 @@ SplitView {
|
||||||
readonly property var collectiblesModel: CollectiblesModel {
|
readonly property var collectiblesModel: CollectiblesModel {
|
||||||
id: collectiblesModel
|
id: collectiblesModel
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicatePermission(index) {
|
|
||||||
logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootStore: QtObject {
|
rootStore: QtObject {
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import QtQml 2.14
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
property alias model: d.target
|
||||||
|
|
||||||
|
readonly property alias revision: d.revision
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
d.revision = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property Connections _d: Connections {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property int revision: 0
|
||||||
|
|
||||||
|
function onRowsInserted() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowsMoved() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowsRemoved() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLayoutChanged() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function onModelReset() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDataChanged() {
|
||||||
|
revision++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,4 +36,63 @@ QtObject {
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkItemsEquality(itemA, itemB, roles) {
|
||||||
|
return roles.every((role) => itemA[role] === itemB[role])
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkEqualityStrict(modelA, modelB, roles) {
|
||||||
|
if (modelA === modelB)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const countA = modelA === null ? 0 : modelA.rowCount()
|
||||||
|
const countB = modelB === null ? 0 : modelB.rowCount()
|
||||||
|
|
||||||
|
if (countA !== countB)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (countA === 0)
|
||||||
|
return true
|
||||||
|
|
||||||
|
for (let i = 0; i < countA; i++) {
|
||||||
|
const itemA = modelA.get(i)
|
||||||
|
const itemB = modelB.get(i)
|
||||||
|
|
||||||
|
if (!checkItemsEquality(itemA, itemB, roles))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkEqualitySet(modelA, modelB, roles) {
|
||||||
|
if (modelA === modelB)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const countA = modelA === null ? 0 : modelA.rowCount()
|
||||||
|
const countB = modelB === null ? 0 : modelB.rowCount()
|
||||||
|
|
||||||
|
if (countA !== countB)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (countA === 0)
|
||||||
|
return true
|
||||||
|
|
||||||
|
for (let i = 0; i < countA; i++) {
|
||||||
|
const itemA = modelA.get(i)
|
||||||
|
let found = false
|
||||||
|
|
||||||
|
for (let j = 0; j < countB; j++) {
|
||||||
|
const itemB = modelB.get(j)
|
||||||
|
|
||||||
|
if (checkItemsEquality(itemA, itemB, roles))
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,101 +16,24 @@ QtObject {
|
||||||
readonly property QtObject _d: QtObject {
|
readonly property QtObject _d: QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
component ModelObserver: Connections {
|
readonly property ModelChangeTracker trackerA: ModelChangeTracker {
|
||||||
function onRowsInserted() {
|
model: modelA
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowsMoved() {
|
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowsRemoved() {
|
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLayoutChanged() {
|
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
function onModelReset() {
|
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDataChanged() {
|
|
||||||
d.changeCounter++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property int changeCounter: 0
|
readonly property ModelChangeTracker trackerB: ModelChangeTracker {
|
||||||
|
model: modelB
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int revision: trackerA.revision + trackerB.revision
|
||||||
|
|
||||||
readonly property bool equal: checkEquality(modelA, modelB, roles, mode,
|
readonly property bool equal: checkEquality(modelA, modelB, roles, mode,
|
||||||
changeCounter)
|
revision)
|
||||||
|
|
||||||
readonly property Connections observerA: ModelObserver {
|
|
||||||
target: modelA
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property Connections observerB: ModelObserver {
|
|
||||||
target: modelB
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkEquality(modelA, modelB, roles, mode, dummy) {
|
function checkEquality(modelA, modelB, roles, mode, dummy) {
|
||||||
if (modelA === modelB)
|
|
||||||
return true
|
|
||||||
|
|
||||||
const countA = modelA === null ? 0 : modelA.rowCount()
|
|
||||||
const countB = modelB === null ? 0 : modelB.rowCount()
|
|
||||||
|
|
||||||
if (countA !== countB)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (countA === 0)
|
|
||||||
return true
|
|
||||||
|
|
||||||
if (mode === ModelsComparator.CompareMode.Strict)
|
if (mode === ModelsComparator.CompareMode.Strict)
|
||||||
return checkEqualityStrict(modelA, modelB, roles)
|
return ModelUtils.checkEqualityStrict(modelA, modelB, roles)
|
||||||
|
|
||||||
return checkEqualitySet(modelA, modelB, roles)
|
return ModelUtils.checkEqualitySet(modelA, modelB, roles)
|
||||||
}
|
|
||||||
|
|
||||||
function checkEqualityStrict(modelA, modelB, roles) {
|
|
||||||
const count = modelA.rowCount()
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const itemA = modelA.get(i)
|
|
||||||
const itemB = modelB.get(i)
|
|
||||||
|
|
||||||
if (!checkItemsEquality(itemA, itemB, roles))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkEqualitySet(modelA, modelB, roles) {
|
|
||||||
const count = modelA.rowCount()
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const itemA = modelA.get(i)
|
|
||||||
let found = false
|
|
||||||
|
|
||||||
for (let j = 0; j < count; j++) {
|
|
||||||
const itemB = modelB.get(j)
|
|
||||||
|
|
||||||
if (checkItemsEquality(itemA, itemB, roles))
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkItemsEquality(itemA, itemB, roles) {
|
|
||||||
return roles.every((role) => itemA[role] === itemB[role])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ module StatusQ.Core.Utils
|
||||||
|
|
||||||
EmojiJSON 1.0 emojiList.js
|
EmojiJSON 1.0 emojiList.js
|
||||||
JSONListModel 0.1 JSONListModel.qml
|
JSONListModel 0.1 JSONListModel.qml
|
||||||
|
ModelChangeTracker 0.1 ModelChangeTracker.qml
|
||||||
ModelsComparator 0.1 ModelsComparator.qml
|
ModelsComparator 0.1 ModelsComparator.qml
|
||||||
XSS 1.0 xss.js
|
XSS 1.0 xss.js
|
||||||
singleton Emoji 0.1 Emoji.qml
|
singleton Emoji 0.1 Emoji.qml
|
||||||
|
|
|
@ -195,5 +195,6 @@
|
||||||
<file>StatusQ/Components/LoadingComponent.qml</file>
|
<file>StatusQ/Components/LoadingComponent.qml</file>
|
||||||
<file>StatusQ/Core/Utils/ModelUtils.qml</file>
|
<file>StatusQ/Core/Utils/ModelUtils.qml</file>
|
||||||
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
|
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
|
||||||
|
<file>StatusQ/Core/Utils/ModelChangeTracker.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import AppLayouts.Chat.layouts 1.0
|
||||||
import AppLayouts.Chat.stores 1.0
|
import AppLayouts.Chat.stores 1.0
|
||||||
import AppLayouts.Chat.views.communities 1.0
|
import AppLayouts.Chat.views.communities 1.0
|
||||||
|
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
SettingsPageLayout {
|
SettingsPageLayout {
|
||||||
|
@ -57,8 +58,8 @@ SettingsPageLayout {
|
||||||
? d.permissionsViewState : d.welcomeViewState
|
? d.permissionsViewState : d.welcomeViewState
|
||||||
|
|
||||||
function initializeData() {
|
function initializeData() {
|
||||||
holdingsToEditModel = defaultListObject.createObject(d)
|
holdingsToEditModel = emptyModel
|
||||||
channelsToEditModel = defaultListObject.createObject(d)
|
channelsToEditModel = emptyModel
|
||||||
permissionTypeToEdit = PermissionTypes.Type.None
|
permissionTypeToEdit = PermissionTypes.Type.None
|
||||||
isPrivateToEditValue = false
|
isPrivateToEditValue = false
|
||||||
}
|
}
|
||||||
|
@ -146,6 +147,8 @@ SettingsPageLayout {
|
||||||
id: newPermissionView
|
id: newPermissionView
|
||||||
|
|
||||||
CommunityNewPermissionView {
|
CommunityNewPermissionView {
|
||||||
|
id: communityNewPermissionView
|
||||||
|
|
||||||
viewWidth: root.viewWidth
|
viewWidth: root.viewWidth
|
||||||
|
|
||||||
rootStore: root.rootStore
|
rootStore: root.rootStore
|
||||||
|
@ -190,6 +193,55 @@ SettingsPageLayout {
|
||||||
property: "dirty"
|
property: "dirty"
|
||||||
value: isEditState && dirty
|
value: isEditState && dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelChangeTracker {
|
||||||
|
id: holdingsTracker
|
||||||
|
|
||||||
|
model: communityNewPermissionView.dirtyValues.holdingsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelChangeTracker {
|
||||||
|
id: channelsTracker
|
||||||
|
|
||||||
|
model: communityNewPermissionView.dirtyValues.channelsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root
|
||||||
|
property: "saveChangesButtonEnabled"
|
||||||
|
value: !communityNewPermissionView.duplicationWarningVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicationWarningVisible: {
|
||||||
|
// dependencies
|
||||||
|
holdingsTracker.revision
|
||||||
|
channelsTracker.revision
|
||||||
|
communityNewPermissionView.dirtyValues.permissionType
|
||||||
|
communityNewPermissionView.dirtyValues.isPrivate
|
||||||
|
|
||||||
|
const model = root.store.permissionsModel
|
||||||
|
|
||||||
|
for (let i = 0; i < model.count; i++) {
|
||||||
|
if (root.state === d.editPermissionViewState
|
||||||
|
&& d.permissionIndexToEdit === i)
|
||||||
|
continue
|
||||||
|
|
||||||
|
const item = model.get(i)
|
||||||
|
|
||||||
|
const holdings = item.holdingsListModel
|
||||||
|
const channels = item.channelsListModel
|
||||||
|
const permissionType = item.permissionType
|
||||||
|
|
||||||
|
const same = (a, b) => ModelUtils.checkEqualitySet(a, b, ["key"])
|
||||||
|
|
||||||
|
if (same(dirtyValues.holdingsModel, holdings)
|
||||||
|
&& same(dirtyValues.channelsModel, channels)
|
||||||
|
&& dirtyValues.permissionType === permissionType)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,19 +253,24 @@ SettingsPageLayout {
|
||||||
rootStore: root.rootStore
|
rootStore: root.rootStore
|
||||||
store: root.store
|
store: root.store
|
||||||
|
|
||||||
onEditPermissionRequested: {
|
function setInitialValuesFromIndex(index) {
|
||||||
const item = root.store.permissionsModel.get(index)
|
const item = root.store.permissionsModel.get(index)
|
||||||
|
|
||||||
d.permissionIndexToEdit = index
|
|
||||||
d.holdingsToEditModel = item.holdingsListModel
|
d.holdingsToEditModel = item.holdingsListModel
|
||||||
d.channelsToEditModel = item.channelsListModel
|
d.channelsToEditModel = item.channelsListModel
|
||||||
d.permissionTypeToEdit = item.permissionType
|
d.permissionTypeToEdit = item.permissionType
|
||||||
d.isPrivateToEditValue = item.isPrivate
|
d.isPrivateToEditValue = item.isPrivate
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditPermissionRequested: {
|
||||||
|
setInitialValuesFromIndex(index)
|
||||||
|
d.permissionIndexToEdit = index
|
||||||
root.state = d.editPermissionViewState
|
root.state = d.editPermissionViewState
|
||||||
}
|
}
|
||||||
|
|
||||||
onDuplicatePermissionRequested: {
|
onDuplicatePermissionRequested: {
|
||||||
root.store.duplicatePermission(index)
|
setInitialValuesFromIndex(index)
|
||||||
|
root.state = d.newPermissionViewState
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemovePermissionRequested: {
|
onRemovePermissionRequested: {
|
||||||
|
@ -222,8 +279,7 @@ SettingsPageLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
ListModel {
|
||||||
id: defaultListObject
|
id: emptyModel
|
||||||
ListModel {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
Control {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property int iconSize: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: root.spacing
|
||||||
|
|
||||||
|
StatusIcon {
|
||||||
|
Layout.preferredWidth: d.iconSize
|
||||||
|
Layout.preferredHeight: d.iconSize
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
icon: "warning"
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
font.pixelSize: Style.current.primaryTextFontSize
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
text: qsTr("Permission with same properties is already active, edit properties to create a new permission.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,4 +6,5 @@ CommunityProfilePopupInviteMessagePanel 1.0 CommunityProfilePopupInviteMessagePa
|
||||||
HidePermissionPanel 1.0 HidePermissionPanel.qml
|
HidePermissionPanel 1.0 HidePermissionPanel.qml
|
||||||
JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml
|
JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml
|
||||||
PermissionConflictWarningPanel 1.0 PermissionConflictWarningPanel.qml
|
PermissionConflictWarningPanel 1.0 PermissionConflictWarningPanel.qml
|
||||||
|
PermissionDuplicationWarningPanel 1.0 PermissionDuplicationWarningPanel.qml
|
||||||
PermissionQualificationPanel 1.0 PermissionQualificationPanel.qml
|
PermissionQualificationPanel 1.0 PermissionQualificationPanel.qml
|
||||||
|
|
|
@ -179,14 +179,6 @@ QtObject {
|
||||||
createPermission(holdings, permissionType, isPrivate, channels, index)
|
createPermission(holdings, permissionType, isPrivate, channels, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicatePermission(index) {
|
|
||||||
// TO BE REPLACED: Call to backend
|
|
||||||
console.log("TODO: Duplicate permissions - backend call")
|
|
||||||
const permission = root.permissionsModel.get(index)
|
|
||||||
createPermission(permission.holdingsListModel, permission.permissionType,
|
|
||||||
permission.isPrivate, permission.channelsListModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePermission(index) {
|
function removePermission(index) {
|
||||||
console.log("TODO: Remove permissions - backend call")
|
console.log("TODO: Remove permissions - backend call")
|
||||||
root.permissionsModel.remove(index)
|
root.permissionsModel.remove(index)
|
||||||
|
|
|
@ -44,6 +44,8 @@ StatusScrollView {
|
||||||
|
|
||||||
readonly property alias dirtyValues: d.dirtyValues
|
readonly property alias dirtyValues: d.dirtyValues
|
||||||
|
|
||||||
|
property alias duplicationWarningVisible: duplicationPanel.visible
|
||||||
|
|
||||||
signal createPermissionClicked
|
signal createPermissionClicked
|
||||||
|
|
||||||
function resetChanges() {
|
function resetChanges() {
|
||||||
|
@ -156,7 +158,7 @@ StatusScrollView {
|
||||||
contentWidth: mainLayout.width
|
contentWidth: mainLayout.width
|
||||||
contentHeight: mainLayout.height
|
contentHeight: mainLayout.height
|
||||||
|
|
||||||
onPermissionTypeChanged: d.loadInitValues()
|
onPermissionTypeChanged: Qt.callLater(() => d.loadInitValues())
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: mainLayout
|
id: mainLayout
|
||||||
|
@ -517,6 +519,14 @@ StatusScrollView {
|
||||||
channels: store.permissionConflict.channels
|
channels: store.permissionConflict.channels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PermissionDuplicationWarningPanel {
|
||||||
|
id: duplicationPanel
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 50 // by desing
|
||||||
|
}
|
||||||
|
|
||||||
StatusButton {
|
StatusButton {
|
||||||
visible: !root.isEditState
|
visible: !root.isEditState
|
||||||
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
|
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
|
||||||
|
@ -524,6 +534,7 @@ StatusScrollView {
|
||||||
enabled: d.dirtyValues.holdingsModel.count > 0
|
enabled: d.dirtyValues.holdingsModel.count > 0
|
||||||
&& d.dirtyValues.permissionType !== PermissionTypes.Type.None
|
&& d.dirtyValues.permissionType !== PermissionTypes.Type.None
|
||||||
&& (d.dirtyValues.channelsModel.count > 0 || d.isCommunityPermission)
|
&& (d.dirtyValues.channelsModel.count > 0 || d.isCommunityPermission)
|
||||||
|
&& !root.duplicationWarningVisible
|
||||||
Layout.preferredHeight: 44
|
Layout.preferredHeight: 44
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
Loading…
Reference in New Issue