206 lines
8.5 KiB
JavaScript

const statestore = require('../store/state.js')
const { addConnectionCredentials } = require('../utils/index.js')
module.exports = function (RED) {
function UiControlNode (config) {
const node = this
RED.nodes.createNode(this, config)
// which ui does this widget belong to
const ui = RED.nodes.getNode(config.ui)
function updateStore (all, items, msg, prop, value) {
items.forEach(function (item) {
const i = all[item]
if (i) {
// update the state store for each page
statestore.set(ui, i, msg, prop, value)
} else {
node.error("No item with the name '" + item + "' found", msg)
}
})
}
function emit (msg) {
ui.emit('ui-control:' + node.id, msg, node)
}
const evts = {
onInput: function (msg, send, done) {
const wNode = RED.nodes.getNode(node.id)
// handle the logic here of what to do when input is received
if (typeof msg.payload !== 'object') {
msg.payload = { page: msg.payload }
}
// switch to tab name (or number)
if ('tab' in msg.payload || 'page' in msg.payload) {
let page = msg.payload.page || msg.payload.tab
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
// 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(msg)
pageFound = true
}
}
})
if (!pageFound) {
node.error("No page with the name '" + page + "' found")
}
}
}
// show/hide or enable/disable tabs
if ('tabs' in msg.payload || 'pages' in msg.payload) {
const pages = msg.payload.pages || msg.payload.tabs
// get a map of page name to page object
const allPages = {}
RED.nodes.eachNode((n) => {
if (n.type === 'ui-page') {
allPages[n.name] = n
}
})
// const pMap = RED.nodes.forEach
if ('show' in pages) {
updateStore(allPages, pages.show, msg, 'visible', true)
}
if ('hide' in pages) {
updateStore(allPages, pages.hide, msg, 'visible', false)
}
if ('enable' in pages) {
updateStore(allPages, pages.enable, msg, 'disabled', false)
}
if ('disable' in pages) {
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(msg)
}
// show or hide ui groups
if ('groups' in msg.payload || 'group' in msg.payload) {
const groups = msg.payload.groups || msg.payload.group
// get a map of group name to group object
const allGroups = {}
RED.nodes.eachNode((n) => {
if (n.type === 'ui-group') {
allGroups[n.name] = n
}
})
if ('show' in groups) {
const gs = groups.show.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gs, msg, 'visible', true)
}
if ('hide' in groups) {
const gh = groups.hide.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gh, msg, 'visible', false)
}
if ('enable' in groups) {
const ge = groups.enable.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, ge, msg, 'disabled', false)
}
if ('disable' in groups) {
const gd = groups.disable.map((g) => {
const levels = g.split(':')
return levels.length > 1 ? levels[1] : g
})
updateStore(allGroups, gd, msg, 'disabled', true)
}
// 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
wNode.send({ payload: 'input' })
}
if ('url' in msg.payload) {
emit(msg)
}
},
onSocket: {
connection: function (conn) {
if (config.events === 'all' || config.events === 'connect') {
const wNode = RED.nodes.getNode(node.id)
let msg = {
payload: 'connect'
}
msg = addConnectionCredentials(RED, msg, conn, ui)
wNode.send(msg)
}
},
disconnect: function (conn) {
if (config.events === 'all' || config.events === 'connect') {
const wNode = RED.nodes.getNode(node.id)
let msg = {
payload: 'lost'
}
msg = addConnectionCredentials(RED, msg, conn, ui)
wNode.send(msg)
}
},
'ui-control': function (conn, id, evt, payload) {
if (id === node.id && (config.events === 'all' || config.events === 'change')) {
// this message was sent by this particular node
if (evt === 'change') {
const wNode = RED.nodes.getNode(node.id)
let msg = {
payload: 'change',
tab: payload.page, // index of tab
name: payload.name // page name
}
msg = addConnectionCredentials(RED, msg, conn, ui)
wNode.send(msg)
}
}
}
}
}
// inform the dashboard UI that we are adding this node
ui.register(null, null, node, config, evts)
node.on('close', function (removed, done) {
if (removed) {
// handle node being removed
ui?.deregister(null, null, node)
}
done()
})
}
RED.nodes.registerType('ui-control', UiControlNode)
}