diff --git a/storybook/pages/CommunityNewPermissionViewPage.qml b/storybook/pages/CommunityNewPermissionViewPage.qml
index 1357cf75ed..ee8e3a8cb4 100644
--- a/storybook/pages/CommunityNewPermissionViewPage.qml
+++ b/storybook/pages/CommunityNewPermissionViewPage.qml
@@ -27,6 +27,7 @@ SplitView {
isEditState: isEditStateCheckBox.checked
isPrivate: isPrivateCheckBox.checked
+ duplicationWarningVisible: isDuplicationWarningVisibleCheckBox.checked
store: CommunitiesStore {
readonly property var assetsModel: AssetsModel {}
@@ -50,10 +51,6 @@ SplitView {
logs.logEvent("CommunitiesStore::editPermission - index: " + index)
}
- function duplicatePermission(index) {
- logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
- }
-
function removePermission(index) {
logs.logEvent("CommunitiesStore::removePermission - index: " + index)
}
@@ -111,6 +108,12 @@ SplitView {
text: "Is private"
}
+
+ CheckBox {
+ id: isDuplicationWarningVisibleCheckBox
+
+ text: "Is duplication warning visible"
+ }
}
Button {
diff --git a/storybook/pages/CommunityPermissionsViewPage.qml b/storybook/pages/CommunityPermissionsViewPage.qml
index fbcd16b5a2..413fea50eb 100644
--- a/storybook/pages/CommunityPermissionsViewPage.qml
+++ b/storybook/pages/CommunityPermissionsViewPage.qml
@@ -39,10 +39,6 @@ SplitView {
readonly property var collectiblesModel: CollectiblesModel {
id: collectiblesModel
}
-
- function duplicatePermission(index) {
- logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
- }
}
rootStore: QtObject {
diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/ModelChangeTracker.qml b/ui/StatusQ/src/StatusQ/Core/Utils/ModelChangeTracker.qml
new file mode 100644
index 0000000000..fa32aae2ec
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Core/Utils/ModelChangeTracker.qml
@@ -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++
+ }
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml b/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml
index ac59f5e0c7..c593e006b4 100644
--- a/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml
+++ b/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml
@@ -36,4 +36,63 @@ QtObject {
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
+ }
}
diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/ModelsComparator.qml b/ui/StatusQ/src/StatusQ/Core/Utils/ModelsComparator.qml
index 21abd0b08e..2d723b33fb 100644
--- a/ui/StatusQ/src/StatusQ/Core/Utils/ModelsComparator.qml
+++ b/ui/StatusQ/src/StatusQ/Core/Utils/ModelsComparator.qml
@@ -16,101 +16,24 @@ QtObject {
readonly property QtObject _d: QtObject {
id: d
- component ModelObserver: Connections {
- function onRowsInserted() {
- d.changeCounter++
- }
-
- function onRowsMoved() {
- d.changeCounter++
- }
-
- function onRowsRemoved() {
- d.changeCounter++
- }
-
- function onLayoutChanged() {
- d.changeCounter++
- }
-
- function onModelReset() {
- d.changeCounter++
- }
-
- function onDataChanged() {
- d.changeCounter++
- }
+ readonly property ModelChangeTracker trackerA: ModelChangeTracker {
+ model: modelA
}
- 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,
- changeCounter)
-
- readonly property Connections observerA: ModelObserver {
- target: modelA
- }
-
- readonly property Connections observerB: ModelObserver {
- target: modelB
- }
+ revision)
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)
- return checkEqualityStrict(modelA, modelB, roles)
+ return ModelUtils.checkEqualityStrict(modelA, modelB, roles)
- return 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])
+ return ModelUtils.checkEqualitySet(modelA, modelB, roles)
}
}
}
diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/qmldir b/ui/StatusQ/src/StatusQ/Core/Utils/qmldir
index 35ec7249d4..8fbc663629 100644
--- a/ui/StatusQ/src/StatusQ/Core/Utils/qmldir
+++ b/ui/StatusQ/src/StatusQ/Core/Utils/qmldir
@@ -2,6 +2,7 @@ module StatusQ.Core.Utils
EmojiJSON 1.0 emojiList.js
JSONListModel 0.1 JSONListModel.qml
+ModelChangeTracker 0.1 ModelChangeTracker.qml
ModelsComparator 0.1 ModelsComparator.qml
XSS 1.0 xss.js
singleton Emoji 0.1 Emoji.qml
diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc
index 167533aa44..12e767cce3 100644
--- a/ui/StatusQ/src/statusq.qrc
+++ b/ui/StatusQ/src/statusq.qrc
@@ -195,5 +195,6 @@
StatusQ/Components/LoadingComponent.qml
StatusQ/Core/Utils/ModelUtils.qml
StatusQ/Core/Utils/ModelsComparator.qml
+ StatusQ/Core/Utils/ModelChangeTracker.qml
diff --git a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml
index df690701ea..8b8ae66154 100644
--- a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml
+++ b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml
@@ -5,6 +5,7 @@ import AppLayouts.Chat.layouts 1.0
import AppLayouts.Chat.stores 1.0
import AppLayouts.Chat.views.communities 1.0
+import StatusQ.Core.Utils 0.1
import utils 1.0
SettingsPageLayout {
@@ -57,8 +58,8 @@ SettingsPageLayout {
? d.permissionsViewState : d.welcomeViewState
function initializeData() {
- holdingsToEditModel = defaultListObject.createObject(d)
- channelsToEditModel = defaultListObject.createObject(d)
+ holdingsToEditModel = emptyModel
+ channelsToEditModel = emptyModel
permissionTypeToEdit = PermissionTypes.Type.None
isPrivateToEditValue = false
}
@@ -146,6 +147,8 @@ SettingsPageLayout {
id: newPermissionView
CommunityNewPermissionView {
+ id: communityNewPermissionView
+
viewWidth: root.viewWidth
rootStore: root.rootStore
@@ -190,6 +193,55 @@ SettingsPageLayout {
property: "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
store: root.store
- onEditPermissionRequested: {
+ function setInitialValuesFromIndex(index) {
const item = root.store.permissionsModel.get(index)
- d.permissionIndexToEdit = index
d.holdingsToEditModel = item.holdingsListModel
d.channelsToEditModel = item.channelsListModel
d.permissionTypeToEdit = item.permissionType
d.isPrivateToEditValue = item.isPrivate
+ }
+
+ onEditPermissionRequested: {
+ setInitialValuesFromIndex(index)
+ d.permissionIndexToEdit = index
root.state = d.editPermissionViewState
}
onDuplicatePermissionRequested: {
- root.store.duplicatePermission(index)
+ setInitialValuesFromIndex(index)
+ root.state = d.newPermissionViewState
}
onRemovePermissionRequested: {
@@ -222,8 +279,7 @@ SettingsPageLayout {
}
}
- Component {
- id: defaultListObject
- ListModel {}
+ ListModel {
+ id: emptyModel
}
}
diff --git a/ui/app/AppLayouts/Chat/panels/communities/PermissionDuplicationWarningPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/PermissionDuplicationWarningPanel.qml
new file mode 100644
index 0000000000..3cb0c77a25
--- /dev/null
+++ b/ui/app/AppLayouts/Chat/panels/communities/PermissionDuplicationWarningPanel.qml
@@ -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.")
+ }
+ }
+}
diff --git a/ui/app/AppLayouts/Chat/panels/communities/qmldir b/ui/app/AppLayouts/Chat/panels/communities/qmldir
index 105b3070a3..edf906fe2d 100644
--- a/ui/app/AppLayouts/Chat/panels/communities/qmldir
+++ b/ui/app/AppLayouts/Chat/panels/communities/qmldir
@@ -6,4 +6,5 @@ CommunityProfilePopupInviteMessagePanel 1.0 CommunityProfilePopupInviteMessagePa
HidePermissionPanel 1.0 HidePermissionPanel.qml
JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml
PermissionConflictWarningPanel 1.0 PermissionConflictWarningPanel.qml
+PermissionDuplicationWarningPanel 1.0 PermissionDuplicationWarningPanel.qml
PermissionQualificationPanel 1.0 PermissionQualificationPanel.qml
diff --git a/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml b/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml
index d82725ea52..d2e53284eb 100644
--- a/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml
+++ b/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml
@@ -179,14 +179,6 @@ QtObject {
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) {
console.log("TODO: Remove permissions - backend call")
root.permissionsModel.remove(index)
diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml
index e609f8d852..c56d15ca4e 100644
--- a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml
+++ b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml
@@ -44,6 +44,8 @@ StatusScrollView {
readonly property alias dirtyValues: d.dirtyValues
+ property alias duplicationWarningVisible: duplicationPanel.visible
+
signal createPermissionClicked
function resetChanges() {
@@ -156,7 +158,7 @@ StatusScrollView {
contentWidth: mainLayout.width
contentHeight: mainLayout.height
- onPermissionTypeChanged: d.loadInitValues()
+ onPermissionTypeChanged: Qt.callLater(() => d.loadInitValues())
ColumnLayout {
id: mainLayout
@@ -517,6 +519,14 @@ StatusScrollView {
channels: store.permissionConflict.channels
}
+ PermissionDuplicationWarningPanel {
+ id: duplicationPanel
+
+ visible: false
+ Layout.fillWidth: true
+ Layout.topMargin: 50 // by desing
+ }
+
StatusButton {
visible: !root.isEditState
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
@@ -524,6 +534,7 @@ StatusScrollView {
enabled: d.dirtyValues.holdingsModel.count > 0
&& d.dirtyValues.permissionType !== PermissionTypes.Type.None
&& (d.dirtyValues.channelsModel.count > 0 || d.isCommunityPermission)
+ && !root.duplicationWarningVisible
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true