Skip to content

Commit

Permalink
Allow for dynamic visibility of groups and pages
Browse files Browse the repository at this point in the history
  • Loading branch information
joepavitt committed Jan 10, 2024
1 parent 2d479c2 commit 4e89955
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 63 deletions.
16 changes: 14 additions & 2 deletions nodes/config/ui_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,25 @@ module.exports = function (RED) {
*/
function emit (event, msg, wNode) {
Object.values(uiShared.connections).forEach(conn => {
const nodeAllowsConstraints = wNode ? n.acceptsClientConfig?.includes(wNode.type) : true
if ((nodeAllowsConstraints && isValidConnection(conn, msg)) || !nodeAllowsConstraints) {
if (canSendTo(conn, wNode, msg)) {
conn.emit(event, msg)
}
})
}

/**
* Checks, given a received msg, and the associated SocketIO connection
* whether the msg has been configured to only be sent to particular connections
* @param {*} conn - SocketIO Connection Object
* @param {*} wNode - The Node-RED node we are sending this to
* @param {*} msg - The msg to be sent
* @returns {Boolean} - Whether the msg can be sent to this connection
*/
function canSendTo (conn, wNode, msg) {
const nodeAllowsConstraints = wNode ? n.acceptsClientConfig?.includes(wNode.type) : true
return (nodeAllowsConstraints && isValidConnection(conn, msg)) || !nodeAllowsConstraints
}

/**
* Checks, given a received msg, and the associated SocketIO connection
* whether the msg has been configured to only be sent to particular connections
Expand Down
6 changes: 1 addition & 5 deletions nodes/store/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ const config = {

/**
* Checks if a Client/Socket ID has been assigned to this message,
* and whether the node type is being scoped to a specific client.
* If so, do not store this in our centralised datastore
* @param {*} msg
* @returns
*/
function canSaveInStore (base, node, msg) {
// gets a list of node types that allow for client configuration/limits
const constrained = base.acceptsClientConfig

const checks = []

if (constrained.includes(node.type) && msg) {
if (msg) {
// core check
if (msg._client?.socketId) {
// we are in a node type that allows for definition of specific clients,
Expand Down
56 changes: 40 additions & 16 deletions nodes/widgets/ui_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ module.exports = function (RED) {
})
}

function emit (payload) {
ui.emit('ui-control:' + node.id, payload, node)
function emit (msg) {
ui.emit('ui-control:' + node.id, msg, node)
}

const evts = {
Expand All @@ -38,16 +38,20 @@ module.exports = function (RED) {
page = (page === undefined) ? '' : page

if (page === '-1' || page === '+1' || typeof (page) === 'number' || page === '') {
// special case for -1 and +1 to switch to previous/next tab
// number to pick specific index
// "" to refresh the page
emit({ page })
// special case for -1 and +1 to switch to previous/next tab
// number to pick specific index
// "" to refresh the page
// ensure consistency in payload format
msg.payload.page = page
emit(msg)
} else {
let pageFound = false
// check we have a valid tab/page name
RED.nodes.eachNode(function (n) {
if (n.type === 'ui-page') {
if (n.name === page) {
// ensure consistency in payload format
msg.payload.page = page
// send a message to the ui to switch to this tab
emit({ page })
pageFound = true
Expand Down Expand Up @@ -85,8 +89,10 @@ module.exports = function (RED) {
updateStore(allPages, pages.disable, msg, 'disabled', false)
}

// ensure consistency in payload format
msg.payload.pages = pages
// send to front end in order to action there too
emit({ pages })
emit(msg)
}

// show or hide ui groups
Expand All @@ -100,24 +106,42 @@ module.exports = function (RED) {
}
})
if ('show' in groups) {
updateStore(allGroups, groups.show, 'visible', true)
const gs = groups.show.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gs, 'visible', true)
}
if ('hide' in groups) {
updateStore(allGroups, groups.hide, 'visible', false)
const gh = groups.hide.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gh, 'visible', false)
}
if ('enable' in groups) {
updateStore(allGroups, groups.enable, 'disabled', false)
const ge = groups.enable.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, ge, 'disabled', false)
}
if ('disable' in groups) {
updateStore(allGroups, groups.disable, 'disabled', true)
const gd = groups.disable.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gd, 'disabled', true)
}
emit({ groups })
}
// ensure consistency in payload format
msg.payload.groups = groups
emit(msg)

// send specific visible/hidden commands via SocketIO here,
// so all logic stays server-side
// send specific visible/hidden commands via SocketIO here,
// so all logic stays server-side

wNode.send({ payload: 'input' })
wNode.send({ payload: 'input' })
}
},
onSocket: {
connection: function (conn) {
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"vite": "^5.0.0",
"vitepress": "^1.0.0-rc.26",
"vue-router": "^4.2.4",
"vuetify": "^3.4.1",
"vuetify": "^3.4.10",
"vuex": "^4.1.0"
},
"engines": {
Expand Down
9 changes: 8 additions & 1 deletion ui/src/store/ui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ const getters = {
widgets (state) {
return state.widgets
},
pageByName: (state) => (name) => {
if (state.pages) {
return Object.values(state.pages).filter((p) => {
return p.name === name
})
}
},
groupsByPage: (state) => (pageId) => {
if (state.groups) {
const groupsOnPage = Object.values(state.groups).filter((p) => {
Expand Down Expand Up @@ -69,7 +76,7 @@ const getters = {
*/
findBy: (state) => (item, prop, value) => {
if (state[item + 's']) {
return Object.values(state[item + 's']).find((i) => {
return Object.values(state[item + 's']).filter((i) => {
return i[prop] === value
})
}
Expand Down
89 changes: 59 additions & 30 deletions ui/src/widgets/ui-control/UIControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default {
computed: {
...mapState('data', ['messages']),
...mapState('ui', ['pages', 'groups', 'widgets']),
...mapGetters('ui', ['findBy'])
...mapGetters('ui', ['findBy', 'pageByName'])
},
watch: {
'$route.meta.id': {
Expand All @@ -33,8 +33,9 @@ export default {
const vue = this
// listen for messages
this.$socket.on('ui-control:' + this.id, (msg) => {
const payload = msg.payload
function set (type, name, prop, value) {
const item = vue.findBy(type, 'name', name)
const item = vue.findBy(type, 'name', name)[0]
vue.$store.commit('ui/setProperty', {
item: type,
itemId: item.id,
Expand All @@ -43,8 +44,36 @@ export default {
})
}
if ('page' in msg) {
let page = msg.tab || msg.page
function setGroup (name, prop, value) {
const [pageName, groupName] = name.split(':')
const groups = vue.findBy('group', 'name', groupName)
if (groups.length === 1) {
set('group', groupName, prop, value)
} else {
const pages = vue.pageByName(pageName)
if (!pages.length) {
console.error('page not found')
} else {
const pageId = pages[0].id
const g = groups.find((g) => {
return g.page === pageId
})
if (!g) {
console.error(`group "${groupName}" not found on page "${pageName}"`)
} else {
vue.$store.commit('ui/setProperty', {
item: 'group',
itemId: g.id,
property: prop,
value
})
}
}
}
}
if ('page' in payload) {
let page = payload.tab || payload.page
const pages = Object.values(this.pages).sort((a, b) => {
return a.order - b.order
})
Expand Down Expand Up @@ -112,56 +141,56 @@ export default {
}
}
if ('pages' in msg) {
if ('show' in msg.pages) {
if ('pages' in payload) {
if ('show' in payload.pages) {
// we are setting visibility: true
msg.pages.show.forEach((name) => {
set('page', name, 'visible', true)
payload.pages.show.forEach((pageName) => {
set('page', pageName, 'visible', true)
})
}
if ('hide' in msg.pages) {
if ('hide' in payload.pages) {
// we are setting visibility: false
msg.pages.hide.forEach((pageName) => {
set('page', name, 'visible', false)
payload.pages.hide.forEach((pageName) => {
set('page', pageName, 'visible', false)
})
}
if ('disable' in msg.pages) {
if ('disable' in payload.pages) {
// we are setting visibility: true
msg.pages.disable.forEach((name) => {
set('page', name, 'disabled', true)
payload.pages.disable.forEach((pageName) => {
set('page', pageName, 'disabled', true)
})
}
if ('enable' in msg.pages) {
if ('enable' in payload.pages) {
// we are setting visibility: false
msg.pages.enable.forEach((name) => {
set('page', name, 'disabled', false)
payload.pages.enable.forEach((pageName) => {
set('page', pageName, 'disabled', false)
})
}
}
if ('groups' in msg) {
if ('show' in msg.groups) {
if ('groups' in payload) {
if ('show' in payload.groups) {
// we are setting visibility: true
msg.groups.show.forEach((name) => {
set('group', name, 'visible', true)
payload.groups.show.forEach((name) => {
setGroup(name, 'visible', true)
})
}
if ('hide' in msg.groups) {
if ('hide' in payload.groups) {
// we are setting visibility: false
msg.groups.hide.forEach((name) => {
set('group', name, 'visible', false)
payload.groups.hide.forEach((name) => {
setGroup(name, 'visible', false)
})
}
if ('disable' in msg.groups) {
if ('disable' in payload.groups) {
// we are setting visibility: true
msg.groups.disable.forEach((name) => {
set('group', name, 'disabled', true)
payload.groups.disable.forEach((name) => {
setGroup(name, 'disabled', true)
})
}
if ('enable' in msg.groups) {
if ('enable' in payload.groups) {
// we are setting visibility: false
msg.groups.enable.forEach((name) => {
set('group', name, 'disabled', false)
payload.groups.enable.forEach((name) => {
setGroup(name, 'disabled', false)
})
}
}
Expand Down

0 comments on commit 4e89955

Please sign in to comment.