feat(Community Permissions): Integrate In section with permission creation/editing

Closes: #8855
This commit is contained in:
Michał Cieślak 2023-01-27 10:22:04 +01:00 committed by Michał
parent 05b6420f83
commit dc3bcd7da1
11 changed files with 331 additions and 196 deletions

View File

@ -12,8 +12,10 @@ ColumnLayout {
property string name
property string icon
property string amountText
property string emoji
property bool isAmountVisible: false
property bool isImageSelectorVisible: true
property bool isEmojiSelectorVisible: false
property var iconsModel
Label {
@ -63,7 +65,7 @@ ColumnLayout {
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Type"
text: "Name"
}
TextField {
background: Rectangle {
@ -92,5 +94,22 @@ ColumnLayout {
onTextChanged: root.amountText = text
}
}
ColumnLayout {
visible: root.isEmojiSelectorVisible
Label {
Layout.fillWidth: true
text: "Emoji"
}
TextField {
background: Rectangle {
radius: 16
border.color: 'lightgrey'
}
Layout.fillWidth: true
text: root.emoji
onTextChanged: root.emoji = text
}
}
}
}

View File

@ -100,12 +100,16 @@ Flickable {
Repeater {
model: channelsListModel
CommunityPermissionsSettingItemEditor {
isEmojiSelectorVisible: true
panelText: "In [item " + model.index + "]"
name: model.name
icon: model.iconSource
name: model.text
icon: model.iconSource ? model.iconSource : ""
emoji: model.emoji ? model.emoji : ""
iconsModel: AssetsCollectiblesIconsModel {}
onNameChanged: model.name = name
onIconChanged: model.iconSource = icon
onEmojiChanged: model.emoji = emoji
}
}
}

View File

@ -45,18 +45,6 @@ SplitView {
])
}
}
function editPermission(index, holdings, permissions, channels, isPrivate) {
logs.logEvent("CommunitiesStore::editPermission - index: " + index)
}
function duplicatePermission(index) {
logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
}
function removePermission(index) {
logs.logEvent("CommunitiesStore::removePermission - index: " + index)
}
}
rootStore: QtObject {

View File

@ -31,6 +31,21 @@ SplitView {
logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
}
}
rootStore: QtObject {
readonly property QtObject chatCommunitySectionModule: QtObject {
readonly property var model: ChannelsModel {}
}
readonly property QtObject mainModuleInst: QtObject {
readonly property QtObject activeSection: QtObject {
readonly property string name: "Socks"
readonly property string image: ModelsData.icons.socks
readonly property color color: "red"
}
}
}
onEditPermission: logs.logEvent("CommunitiesStore::editPermission - index: " + index)
onRemovePermission: logs.logEvent("CommunitiesStore::removePermission - index: " + index)

View File

@ -48,14 +48,14 @@ ListModel {
name: "faq"
icon: ""
color: ""
colorId: 1
colorId: 5
},
ListElement {
itemId: 5
name: "report-scam"
icon: ""
color: ""
colorId: 1
colorId: 4
}
]
}

View File

@ -32,82 +32,79 @@ ListModel {
])
function createHoldingsModel1() {
var holdings = []
holdings.push({
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "SOCKS",
name: "SOCKS",
amount: 1.2,
imageSource: ModelsData.assets.socks
});
holdings.push({
operator: OperatorsUtils.Operators.Or,
type: HoldingTypes.Type.Asset,
key: "ZRX",
name: "ZRX",
amount: 15,
imageSource: ModelsData.assets.zrx
});
holdings.push({
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "Furbeard",
name: "Furbeard",
amount: 12,
imageSource: ModelsData.collectibles.kitty1
});
return holdings
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "SOCKS",
name: "SOCKS",
amount: 1.2,
imageSource: ModelsData.assets.socks
},
{
operator: OperatorsUtils.Operators.Or,
type: HoldingTypes.Type.Asset,
key: "ZRX",
name: "ZRX",
amount: 15,
imageSource: ModelsData.assets.zrx
},
{
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "Furbeard",
name: "Furbeard",
amount: 12,
imageSource: ModelsData.collectibles.kitty1
}
]
}
function createHoldingsModel2() {
var holdings = []
holdings.push({
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Collectible,
key: "Happy Meow",
name: "Happy Meow",
amount: 50.25,
imageSource: ModelsData.collectibles.kitty3
});
holdings.push({
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "AMP",
name: "AMP",
amount: 11,
imageSource: ModelsData.assets.amp
});
return holdings
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Collectible,
key: "Happy Meow",
name: "Happy Meow",
amount: 50.25,
imageSource: ModelsData.collectibles.kitty3
},
{
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "AMP",
name: "AMP",
amount: 11,
imageSource: ModelsData.assets.amp
}
]
}
function createChannelsModel1() {
var channels = []
channels.push({
key: "help",
iconSource: ModelsData.assets.zrx,
name: "#help"
});
channels.push({
key: "faq",
iconSource: ModelsData.assets.zrx,
name: "#faq"
});
return channels
return [
{
key: "general",
text: "#general",
color: "lightgreen",
emoji: "👋"
},
{
key: "faq",
text: "#faq",
color: "lightblue",
emoji: "⚽"
}
]
}
function createChannelsModel2() {
var channels = []
channels.push({
key: "welcome",
iconSource: ModelsData.assets.inch,
name: "#welcome"
});
channels.push({
key: "general",
iconSource: ModelsData.assets.inch,
name: "#general"
});
return channels
return [
{
key: "socks",
iconSource: ModelsData.icons.socks,
text: "Socks"
}
]
}
}

View File

@ -141,10 +141,22 @@ Control{
model: root.channelsListModel
StatusListItemTag {
readonly property bool isLetterIdenticon: !model.imageSource
asset.isLetterIdenticon: isLetterIdenticon
asset.emoji: model.emoji ? model.emoji : ""
asset.color: model.color ? model.color : ""
asset.width: isLetterIdenticon ? 20 : 28
asset.height: asset.width
leftPadding: isLetterIdenticon ? 6 : 2
height: d.flowRowHeight
width: (implicitWidth > content.width) ? content.width : implicitWidth
width: (implicitWidth > content.width)
? content.width : implicitWidth
title: model.text
asset.name: model.imageSource
asset.name: model.imageSource ? model.imageSource : ""
asset.isImage: true
asset.bgColor: "transparent"
closeButtonVisible: false

View File

@ -156,6 +156,7 @@ SettingsPageLayout {
id: permissionsView
CommunityPermissionsView {
viewWidth: root.viewWidth
rootStore: root.rootStore
store: root.store
onEditPermission: {
d.permissionIndexToEdit = index

View File

@ -91,7 +91,7 @@ QtObject {
function createPermission(holdings, permissions, isPrivate, channels, index = null) {
// TO BE REPLACED: It shold just be a call to the backend sharing `holdings`, `permissions`, `channels` and `isPrivate` properties.
var permission = {
const permission = {
isPrivate: true,
holdingsListModel: [],
permissionsObjectModel: {
@ -99,20 +99,20 @@ QtObject {
text: "",
imageSource: ""
},
channelsListModel: []
};
channelsListModel: [],
}
// Setting HOLDINGS:
for (var i = 0; i < holdings.count; i++ ) {
var entry = holdings.get(i);
// roles: type, key, name, amount, imageSource
for (let i = 0; i < holdings.count; i++ ) {
const entry = holdings.get(i)
permission.holdingsListModel.push({
type: entry.type,
key: entry.key,
name: entry.name,
amount: entry.amount,
imageSource: entry.imageSource
});
type: entry.type,
key: entry.key,
name: entry.name,
amount: entry.amount,
imageSource: entry.imageSource
})
}
// Setting PERMISSIONS:
@ -120,18 +120,27 @@ QtObject {
permission.permissionsObjectModel.text = permissions.text
permission.permissionsObjectModel.imageSource = permissions.imageSource
// Setting CHANNELS
for (let c = 0; c < channels.count; c++) {
const entry = channels.get(c)
permission.channelsListModel.push({
itemId: entry.itemId,
text: entry.text,
emoji: entry.emoji,
color: entry.color
})
}
// Setting PRIVATE permission property:
permission.isPrivate = isPrivate
// TODO: Set channels list. Now mocked data.
permission.channelsListModel = root.channelsModel
if(index !== null) {
if (index !== null) {
// Edit permission model:
console.log("TODO: Edit permissions - backend call")
root.permissionsModel.set(index, permission)
}
else {
} else {
// Add into permission model:
console.log("TODO: Create permissions - backend call - Now dummy data shown")
root.permissionsModel.append(permission)
@ -147,7 +156,8 @@ QtObject {
// TO BE REPLACED: Call to backend
console.log("TODO: Duplicate permissions - backend call")
const permission = root.permissionsModel.get(index)
createPermission(permission.holdingsListModel, permission.permissionsObjectModel, permission.isPrivate, permission.channelsListModel)
createPermission(permission.holdingsListModel, permission.permissionsObjectModel,
permission.isPrivate, permission.channelsListModel)
}
function removePermission(index) {

View File

@ -28,10 +28,15 @@ StatusScrollView {
property bool isEditState: false
property bool dirty: {
let trick = d.triggerDirtyTool // Trick: Used to force the reevaluation of dirty when an item of the list is updated
const trick = d.triggerDirtyTool // Trick: Used to force the reevaluation of dirty when an item of the list is updated
// Holdings:
const dirtyHoldingsList = d.checkIfHoldingsDirty()
if (d.checkIfHoldingsDirty())
return true
// Channels
if (d.checkIfInDirty())
return true
// Permissions:
let dirtyPermissionObj = false
@ -39,15 +44,12 @@ StatusScrollView {
dirtyPermissionObj = (d.dirtyValues.permissionObject.key !== root.permissionObject.key) ||
(d.dirtyValues.permissionObject.text !== root.permissionObject.text) ||
(d.dirtyValues.permissionObject.imageSource !== root.permissionObject.imageSource)
}
else {
} else {
dirtyPermissionObj = d.dirtyValues.permissionObject.key !== null
}
// TODO: Channels:
let dirtyChannelsList = false
return dirtyHoldingsList || dirtyPermissionObj || dirtyChannelsList || d.dirtyValues.isPrivateDirty
return dirtyPermissionObj || d.dirtyValues.isPrivateDirty
}
property bool saveChanges: false
property bool resetChanges: false
@ -60,7 +62,7 @@ StatusScrollView {
// roles: key, text, imageSource
property var permissionObject
// TODO roles:
// roles: itemId, text, emoji, color
property var channelsModel: ListModel {}
property bool isPrivate
@ -95,20 +97,23 @@ StatusScrollView {
onIsCommunityPermissionChanged: {
if (isCommunityPermission) {
inModelChannels.clear()
d.dirtyValues.channelsModel.clear()
inSelector.wholeCommunitySelected = true
inSelector.itemsModel = inModelCommunity
} else {
inSelector.itemsModel = 0
inSelector.wholeCommunitySelected = false
inSelector.itemsModel = inModelChannels
inSelector.itemsModel = d.dirtyValues.channelsModel
}
}
property bool triggerDirtyTool: false // Trick: Used to force the reevaluation of dirty when an item of the list is updated
// Trick: Used to force the reevaluation of dirty when an item of the list is updated
property int triggerDirtyTool: 0
property QtObject dirtyValues: QtObject {
property ListModel holdingsModel: ListModel {}
property ListModel channelsModel: ListModel {}
property QtObject permissionObject: QtObject {
property var key: null
property string text: ""
@ -166,36 +171,29 @@ StatusScrollView {
root.store.editPermission(root.permissionIndex,
d.dirtyValues.holdingsModel,
d.dirtyValues.permissionObject,
root.channelsModel,
d.dirtyValues.channelsModel,
d.dirtyValues.isPrivateDirty ? !root.isPrivate : root.isPrivate)
}
function loadInitValues() {
// Holdings:
d.dirtyValues.holdingsModel.clear()
if(root.holdingsModel) {
for(let i = 0; i < root.holdingsModel.count; i++) {
let item = root.holdingsModel.get(i)
let initItem = null
if(item.shortName) {
initItem = {
type: item.type,
key: item.key,
name: item.name,
shortName: item.shortName,
amount: item.amount,
imageSource: item.imageSource
}
}
else {
initItem = {
type: item.type,
key: item.key,
name: item.name,
amount: item.amount,
imageSource: item.imageSource
}
if (root.holdingsModel) {
for (let i = 0; i < root.holdingsModel.count; i++) {
const item = root.holdingsModel.get(i)
const initItem = {
type: item.type,
key: item.key,
name: item.name,
amount: item.amount,
imageSource: item.imageSource
}
if (item.shortName)
initItem.shortName = item.shortName
d.dirtyValues.holdingsModel.append(initItem)
}
}
@ -205,41 +203,94 @@ StatusScrollView {
d.dirtyValues.permissionObject.text = root.permissionObject ? root.permissionObject.text : ""
d.dirtyValues.permissionObject.imageSource = root.permissionObject ? root.permissionObject.imageSource : ""
// TODO: Channels
d.permissionType = root.permissionObject ? root.permissionObject.key : PermissionTypes.Type.None
// Channels
d.dirtyValues.channelsModel.clear()
if (root.channelsModel) {
for (let c = 0; c < root.channelsModel.count; c++) {
const item = root.channelsModel.get(c)
const initItem = {
itemId: item.itemId,
text: item.text,
emoji: item.emoji,
color: item.color,
operator: OperatorsUtils.Operators.None
}
d.dirtyValues.channelsModel.append(initItem)
}
}
if (root.channelsModel && (root.channelsModel.count || d.dirtyValues.permissionObject.key === null)) {
inSelector.wholeCommunitySelected = false
inSelector.itemsModel = d.dirtyValues.channelsModel
} else {
inSelector.wholeCommunitySelected = true
inSelector.itemsModel = inModelCommunity
}
// Is private permission
d.dirtyValues.isPrivateDirty = false
}
function checkIfHoldingsDirty() {
let dirty = false
if(root.holdingsModel) {
if(root.holdingsModel.count !== d.dirtyValues.holdingsModel.count) {
dirty = true
}
else {
// Check element by element
let equals = 0
for(let i = 0; i < root.holdingsModel.count; i++) {
const item1 = root.holdingsModel.get(i)
for(let j = 0; j < d.dirtyValues.holdingsModel.count; j++) {
let item2 = d.dirtyValues.holdingsModel.get(j)
// key, name, shortName, amount
if((item1.key === item2.key) &&
(item1.name === item2.name) &&
(item1.shortName === item2.shortName) &&
(item1.amount === item2.amount)) {
equals = equals + 1
}
}
if (!root.holdingsModel)
return d.dirtyValues.holdingsModel.count !== 0
if (root.holdingsModel.count !== d.dirtyValues.holdingsModel.count)
return true
// Check element by element
const count = root.holdingsModel.count
let equals = 0
for (let i = 0; i < count; i++) {
const item1 = root.holdingsModel.get(i)
for (let j = 0; j < count; j++) {
const item2 = d.dirtyValues.holdingsModel.get(j)
if (item1.key === item2.key
&& item1.name === item2.name
&& item1.shortName === item2.shortName
&& item1.amount === item2.amount) {
equals++
}
dirty = (equals !== root.holdingsModel.count)
}
}
else {
dirty = (d.dirtyValues.holdingsModel.count !== 0)
return equals !== count
}
function checkIfInDirty() {
if (!root.channelsModel)
return d.dirtyValues.channelsModel.count !== 0
if (root.channelsModel.count !== d.dirtyValues.channelsModel.count)
return true
const count = root.channelsModel.count
let equals = 0
for (let i = 0; i < count; i++) {
const item1 = root.channelsModel.get(i)
for (let j = 0; j < count; j++) {
const item2 = d.dirtyValues.channelsModel.get(j)
if (item1.itemId === item2.itemId
&& item1.text === item2.text
&& item1.emoji === item2.emoji
&& item1.color === item2.color) {
equals++
}
}
}
return dirty
return equals !== count
}
function holdingsTextFormat(type, name, amount) {
@ -258,11 +309,13 @@ StatusScrollView {
id: mainLayout
width: root.viewWidth
spacing: 0
CurveSeparatorWithText {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 14
text: qsTr("Anyone")
}
StatusItemSelector {
id: tokensSelector
@ -279,15 +332,26 @@ StatusScrollView {
itemsModel: SortFilterProxyModel {
sourceModel: d.dirtyValues.holdingsModel
proxyRoles: ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
proxyRoles: [
ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
},
ExpressionRole {
name: "operator"
// Direct call for singleton enum is not handled properly by SortFilterProxyModel.
readonly property int none: OperatorsUtils.Operators.None
expression: none
}
]
}
HoldingsDropdown {
id: dropdown
store: root.store
function addItem(type, item, amount) {
@ -348,7 +412,7 @@ StatusScrollView {
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(itemIndex, { type: HoldingTypes.Type.Asset, key, name, amount, imageSource })
d.triggerDirtyTool = !d.triggerDirtyTool
d.triggerDirtyTool++
dropdown.close()
}
@ -360,7 +424,7 @@ StatusScrollView {
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(itemIndex, { type: HoldingTypes.Type.Collectible, key, name, amount, imageSource })
d.triggerDirtyTool = !d.triggerDirtyTool
d.triggerDirtyTool++
dropdown.close()
}
@ -369,7 +433,7 @@ StatusScrollView {
const icon = Style.svg("profile/ensUsernames")
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Ens, key, name: domain, amount: 1, imageSource: icon })
d.triggerDirtyTool = !d.triggerDirtyTool
d.triggerDirtyTool++
dropdown.close()
}
@ -501,6 +565,10 @@ StatusScrollView {
useLetterIdenticons: !wholeCommunitySelected || !inDropdown.communityImage
tagLeftPadding: wholeCommunitySelected ? 2 : 6
asset.width: wholeCommunitySelected ? 28 : 20
asset.height: asset.width
property bool wholeCommunitySelected: false
function openInDropdown(parent, x, y) {
@ -511,8 +579,8 @@ StatusScrollView {
const selectedChannels = []
if (!inSelector.wholeCommunitySelected)
for (let i = 0; i < inModelChannels.count; i++)
selectedChannels.push(inModelChannels.get(i).itemId)
for (let i = 0; i < d.dirtyValues.channelsModel.count; i++)
selectedChannels.push(d.dirtyValues.channelsModel.get(i).itemId)
inDropdown.setSelectedChannels(selectedChannels)
inDropdown.open()
@ -527,17 +595,14 @@ StatusScrollView {
append({
imageSource: inDropdown.communityData.image,
text: inDropdown.communityData.name,
operator: OperatorsUtils.Operators.None
operator: OperatorsUtils.Operators.None,
color: ""
})
setProperty(0, "color", colorWorkaround)
}
}
ListModel {
id: inModelChannels
}
InDropdown {
id: inDropdown
@ -550,12 +615,12 @@ StatusScrollView {
communityColor: communityData.color
onChannelsSelected: {
inModelChannels.clear()
d.dirtyValues.channelsModel.clear()
inSelector.itemsModel = 0
inSelector.wholeCommunitySelected = false
channels.map(channel => {
inModelChannels.append({
channels.forEach(channel => {
d.dirtyValues.channelsModel.append({
itemId: channel.itemId,
text: "#" + channel.name,
emoji: channel.emoji,
@ -564,12 +629,12 @@ StatusScrollView {
})
})
inSelector.itemsModel = inModelChannels
inSelector.itemsModel = d.dirtyValues.channelsModel
close()
}
onCommunitySelected: {
inModelChannels.clear()
d.dirtyValues.channelsModel.clear()
inSelector.wholeCommunitySelected = true
inSelector.itemsModel = inModelCommunity
close()
@ -631,6 +696,7 @@ StatusScrollView {
PermissionConflictWarningPanel{
id: conflictPanel
visible: store.permissionConflict.exists
Layout.fillWidth: true
Layout.topMargin: 50 // by desing
@ -643,7 +709,9 @@ StatusScrollView {
visible: !root.isEditState
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
text: qsTr("Create permission")
enabled: d.dirtyValues.holdingsModel && d.dirtyValues.holdingsModel.count > 0 && d.dirtyValues.permissionObject.key !== null
enabled: d.dirtyValues.holdingsModel.count > 0
&& d.dirtyValues.permissionObject.key !== null
&& (d.dirtyValues.channelsModel.count > 0 || d.isCommunityPermission)
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@ -651,7 +719,7 @@ StatusScrollView {
root.store.createPermission(d.dirtyValues.holdingsModel,
d.dirtyValues.permissionObject,
d.dirtyValues.isPrivateDirty ? !root.isPrivate : root.isPrivate,
root.channelsModel)
d.dirtyValues.channelsModel)
root.permissionCreated()
}
}

View File

@ -15,6 +15,7 @@ import AppLayouts.Chat.helpers 1.0
StatusScrollView {
id: root
property var rootStore
property var store
property int viewWidth: 560 // by design
@ -43,6 +44,20 @@ StatusScrollView {
width: root.viewWidth
spacing: 24
ListModel {
id: communityItemModel
readonly property var communityData: rootStore.mainModuleInst.activeSection
Component.onCompleted: {
append({
text: communityData.name,
imageSource: communityData.image,
color: communityData.color
})
}
}
Repeater {
model: root.store.permissionsModel
delegate: PermissionItem {
@ -52,29 +67,35 @@ StatusScrollView {
proxyRoles: ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
// Direct call for singleton function is not handled properly
// by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
}
permissionName: model.permissionsObjectModel.text
permissionImageSource: model.permissionsObjectModel.imageSource
channelsListModel: SortFilterProxyModel {
SortFilterProxyModel {
id: proxiedChannelsModel
sourceModel: model.channelsListModel
proxyRoles: [
ExpressionRole {
name: "text"
expression: model.name
},
ExpressionRole {
name: "imageSource"
expression: model.iconSource
}
name: "imageSource"
expression: model.iconSource
}
]
}
channelsListModel: proxiedChannelsModel.count
? proxiedChannelsModel : communityItemModel
isPrivate: model.isPrivate
onEditClicked: root.editPermission(model.index, model.holdingsListModel, model.permissionsObjectModel, model.channelsListModel, model.isPrivate)
onEditClicked: root.editPermission(model.index, model.holdingsListModel,
model.permissionsObjectModel,
model.channelsListModel, model.isPrivate)
onDuplicateClicked: store.duplicatePermission(model.index)
onRemoveClicked: root.removePermission(model.index)
}