Single Widgets visibility

Is there a way how to do hide/show single widgets instead of group? (Of course I can move required widget to separate group)

I've looked into documentation Control ui-control | Node-RED Dashboard 2.0

My end goal is to show "File Input" widget when user "maintenance" is logged in, and hide it when user "operator" (default one) is logged in. How to pick which user is logged in? Found solution for D1: Dashboard user login - #2 by MecatronicaMADE EDIT: need to investigate Displaying logged in user on Node-RED Dashboard 2.0 • FlowFuse

Example flow:

[{"id":"d0b7a6dee6187b73","type":"ui-control","z":"a03c5290870e7c02","name":"","ui":"65cf368b48fdb5f6","events":"all","x":695,"y":368.1111145019531,"wires":[[]]},{"id":"ecb6b6cf0824c0f8","type":"function","z":"a03c5290870e7c02","name":"function 8","func":"// Code added here will be run once\n// whenever the node is started.\nmsg.payload = {\n\n    groups: {\n        hide: [\"Visibility:tex:File\"]\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":320,"wires":[["d0b7a6dee6187b73"]]},{"id":"89764e8df08cd094","type":"inject","z":"a03c5290870e7c02","name":"Hide","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"3","topic":"","payload":"","payloadType":"date","x":350,"y":320,"wires":[["ecb6b6cf0824c0f8"]]},{"id":"70d09da481d1cbc1","type":"ui-file-input","z":"a03c5290870e7c02","group":"3c78bb667cb5cdb6","name":"File","order":1,"width":"3","height":"1","topic":"topic","topicType":"msg","label":"File Input","icon":"paperclip","allowMultiple":false,"accept":"","className":"","x":870,"y":360,"wires":[[]]},{"id":"b01e6f65f8760fc3","type":"function","z":"a03c5290870e7c02","name":"function 9","func":"// Code added here will be run once\n// whenever the node is started.\nmsg.payload = {\n\n    groups: {\n        show: [\"Visibility:tex:File\"]\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":420,"wires":[["d0b7a6dee6187b73"]]},{"id":"08e3e8a109611e64","type":"inject","z":"a03c5290870e7c02","name":"Show","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"3","topic":"","payload":"","payloadType":"date","x":350,"y":420,"wires":[["b01e6f65f8760fc3"]]},{"id":"24e1f41ded317956","type":"ui-button","z":"a03c5290870e7c02","group":"3c78bb667cb5cdb6","name":"Download","label":"Download","order":2,"width":"1","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":1020,"y":360,"wires":[[]]},{"id":"65cf368b48fdb5f6","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"3c78bb667cb5cdb6","type":"ui-group","name":"tex","page":"975e9a684fe100cc","width":"4","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"975e9a684fe100cc","type":"ui-page","name":"Visibility","ui":"65cf368b48fdb5f6","path":"/vistest","icon":"home","layout":"grid","theme":"c96c5c22ab511c72","order":1,"className":"","visible":"false","disabled":"false"},{"id":"c96c5c22ab511c72","type":"ui-theme","name":"Theme Name","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

Hi @KarolisL - thanks for the question.

It's not currently possible, but you're not the first person to have asked, so it's definitely on our radar. Could I trouble you to add a comment here: Show / hide widgets (finer granularity than on group level) · Issue #711 · FlowFuse/node-red-dashboard · GitHub

It just gives the issue more traffic and helps us when curating priorities

Documentation for multi-tenancy can also be found here: Building Multi-Tenant Dashboards | Node-RED Dashboard 2.0 in addition to the article you've linked to

1 Like

Based on work from @nygma2004

Initial screen:
image

After pressing UploadLogin:

After correct credentials are entered group with file input appears:

Note: I use global npm module "crypto-js" not to store passwords as plain text, so add it in your settings.js or load in function block Setup

    functionGlobalContext: {
	cryptojs:require('crypto-js')
    },
[{"id":"bdfba4adce8879a9","type":"ui-file-input","z":"d3d8a490d0e9960a","group":"04c08c114913c192","name":"","order":1,"width":"4","height":"1","topic":"topic","topicType":"msg","label":"HikvisionConfig.json upload","icon":"paperclip","allowMultiple":false,"accept":".json","className":"","x":220,"y":80,"wires":[["2af42c7221c7d9e7"]]},{"id":"6c4fdd05d4e3c10d","type":"debug","z":"d3d8a490d0e9960a","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":80,"wires":[]},{"id":"2af42c7221c7d9e7","type":"json","z":"d3d8a490d0e9960a","name":"","property":"payload","action":"obj","pretty":false,"x":470,"y":80,"wires":[["6c4fdd05d4e3c10d"]]},{"id":"d23f5dfb6ce75c58","type":"ui-button","z":"d3d8a490d0e9960a","group":"ad43ab200ca5494d","name":"","label":"Validate","order":1,"width":"1","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"check","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":160,"y":160,"wires":[[]]},{"id":"9cc172e14e78982e","type":"ui-button","z":"d3d8a490d0e9960a","group":"ad43ab200ca5494d","name":"","label":"Download","order":2,"width":"1","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"download","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":160,"y":200,"wires":[[]]},{"id":"fbd619879a13c7c9","type":"ui-button","z":"d3d8a490d0e9960a","group":"ad43ab200ca5494d","name":"","label":"UploadLogin","order":3,"width":"1","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"mdi-login","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":170,"y":240,"wires":[["ef150be92d36d820"]]},{"id":"e80306b59b972f8f","type":"ui-control","z":"d3d8a490d0e9960a","name":"","ui":"65cf368b48fdb5f6","events":"all","x":760,"y":440,"wires":[[]]},{"id":"0997738cf03d9068","type":"function","z":"d3d8a490d0e9960a","name":"Show","func":"msg.payload = {\n\n    groups: {\n        show: [\"Hikvision:File Input\"]\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":420,"wires":[["e80306b59b972f8f"]]},{"id":"7b121e4c813a7c93","type":"inject","z":"d3d8a490d0e9960a","name":"Hide","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":390,"y":500,"wires":[["8b8e181044168f49"]]},{"id":"8b8e181044168f49","type":"function","z":"d3d8a490d0e9960a","name":"Hide","func":"msg.payload = {\n\n    groups: {\n        hide: [\"Hikvision:File Input\"]\n    }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":460,"wires":[["e80306b59b972f8f"]]},{"id":"7b5a6c6c32ce89c7","type":"ui-control","z":"d3d8a490d0e9960a","name":"","ui":"65cf368b48fdb5f6","events":"all","x":240,"y":620,"wires":[["707d17ee380326ab","4a1d39d89b861855"]]},{"id":"707d17ee380326ab","type":"debug","z":"d3d8a490d0e9960a","name":"_clientHikvision","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":420,"y":620,"wires":[]},{"id":"ef150be92d36d820","type":"ui-template","z":"d3d8a490d0e9960a","group":"","page":"7bc2300bd082d6d4","ui":"","name":"Login Dialog","order":0,"width":0,"height":0,"head":"","format":"<template>\n    <v-dialog v-model=\"dialog\" min-width = \"20%\" max-width=\"20%\">\n      <!-- <v-card color=\"white\"> -->\n\n            <v-card class=\"mx-auto pa-12 pb-8\" elevation=\"8\" min-width=\"448\"  max-width=\"448\" rounded=\"lg\">\n              <div class=\"text-subtitle-1 text-medium-emphasis\">User name</div>\n\n              <v-text-field density=\"compact\" placeholder=\"User name\" prepend-inner-icon=\"mdi-account-circle\"\n                variant=\"outlined\" v-model=\"username\"></v-text-field>\n\n              <div class=\"text-subtitle-1 text-medium-emphasis d-flex align-center justify-space-between\">\n                Password\n              </div>\n\n              <!-- <v-text-field \n                :append-icon=\"showPsw ? 'mdi-eye' : 'mdi-eye-off'\"\n                :type=\"showPsw ? 'text' : 'password'\"\n                density=\"compact\" \n                placeholder=\"Enter your password\" \n                prepend-inner-icon=\"mdi-lock-outline\" \n                variant=\"outlined\"\n                @click:append-inner=\"showPsw = !showPsw\" \n                v-model=\"password\">\n              </v-text-field> -->\n\n              <v-text-field\n                :append-icon=\"showPsw ? 'mdi-eye' : 'mdi-eye-off'\"\n                :type=\"showPsw ? 'text' : 'password'\"\n                class=\"input-group--focused\"\n                name=\"input-10-2\"\n                prepend-inner-icon=\"mdi-lock-outline\"\n                v-model=\"password\"\n                @click:append=\"showPsw = !showPsw\"\n              ></v-text-field>\n\n              <v-card class=\"mb-14\" :color=\"color\" variant=\"tonal\">\n                <v-card-text class=\"font-weight-bold text-caption\">\n                  {{ message }}\n                </v-card-text>\n              </v-card>\n\n              <v-btn class=\"mb-8\" color=\"blue\" size=\"large\" variant=\"tonal\" block @click=\"LoginButton\">\n                Log In\n              </v-btn>\n\n              <v-card-text class=\"text-center\">\n              </v-card-text>\n            </v-card>\n      <!-- </v-card> -->\n    </v-dialog>\n</template>\n\n\n\n\n<script>\nexport default {\n  data() {\n    return {\n        dialog: false,\n        showPsw: false,\n        color: \"surface-variant\",\n        message: \"\",\n        username: \"\",\n        password: \"\"\n    }\n  },\n  watch: {\n      msg: function(){\n          // only show the dialog if msg.payload is an object and the socket id in the message\n          // matches our socket id (which means the popup was initiated from this session) or\n          // there is no _client property present which indicates it should be shown on all sessions.\n          if (this.msg.topic===\"close\") {\n            this.dialog = false;\n          } else {\n            if (!this.msg._client || this.msg._client.socketId === this.$socket.id) {   \n              console.log(\"OK we are in\");\n              if (typeof this.msg.payload !== object) msg.payload = {};\n              this.message = this.msg.payload.message ?? \"Access to Upload functionality is restricted. Please provide your user name and password in order to upload your files\";\n              this.color = this.msg.payload.color ?? \"surface-variant\";               \n              this.dialog = true;\n              this.username = \"\";\n              this.password = \"\";\n            }\n          }\n          // prevent redraw on deploy\n          this.msg.payload = null;\n      }\n  },\n  methods:{\n    LoginButton:function(){\n      // Do not hide the dialog, in case login fails\n      //this.dialog = false;\n      this.msg.topic = \"authenticate\";\n      this.msg.payload = { username: this.username, password: this.password };\n      this.send(this.msg);\n    }\n  },\n  computed : {\n  },\n  unmounted () {\n    this.dialog = false;\n  }\n}\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":170,"y":300,"wires":[["dbee019f331c0d93"]]},{"id":"d9e665a5de3bc42e","type":"ui-template","z":"d3d8a490d0e9960a","d":true,"group":"","page":"7bc2300bd082d6d4","ui":"","name":"Login Dialog","order":0,"width":0,"height":0,"head":"","format":"<template>\n  <div>\n    <v-img\n      class=\"mx-auto my-6\"\n      max-width=\"228\"\n      src=\"https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-v3-slim-text-light.svg\"\n    ></v-img>\n\n    <v-card\n      class=\"mx-auto pa-12 pb-8\"\n      elevation=\"8\"\n      max-width=\"448\"\n      rounded=\"lg\"\n    >\n      <div class=\"text-subtitle-1 text-medium-emphasis\">Account</div>\n\n      <v-text-field\n        density=\"compact\"\n        placeholder=\"Email address\"\n        prepend-inner-icon=\"mdi-email-outline\"\n        variant=\"outlined\"\n      ></v-text-field>\n\n      <div\n        class=\"text-subtitle-1 text-medium-emphasis d-flex align-center justify-space-between\"\n      >\n        Password\n\n        <a\n          class=\"text-caption text-decoration-none text-blue\"\n          href=\"#\"\n          rel=\"noopener noreferrer\"\n          target=\"_blank\"\n        >\n          Forgot login password?</a\n        >\n      </div>\n\n      <v-text-field\n        :append-inner-icon=\"visible ? 'mdi-eye-off' : 'mdi-eye'\"\n        :type=\"visible ? 'text' : 'password'\"\n        density=\"compact\"\n        placeholder=\"Enter your password\"\n        prepend-inner-icon=\"mdi-lock-outline\"\n        variant=\"outlined\"\n        @click:append-inner=\"visible = !visible\"\n      ></v-text-field>\n\n      <v-card class=\"mb-12\" color=\"surface-variant\" variant=\"tonal\">\n        <v-card-text class=\"text-medium-emphasis text-caption\">\n          Warning: After 3 consecutive failed login attempts, you account will\n          be temporarily locked for three hours. If you must login now, you can\n          also click \"Forgot login password?\" below to reset the login password.\n        </v-card-text>\n      </v-card>\n\n      <v-btn class=\"mb-8\" color=\"blue\" size=\"large\" variant=\"tonal\" block>\n        Log In\n      </v-btn>\n\n      <v-card-text class=\"text-center\">\n        <a\n          class=\"text-blue text-decoration-none\"\n          href=\"#\"\n          rel=\"noopener noreferrer\"\n          target=\"_blank\"\n        >\n          Sign up now <v-icon icon=\"mdi-chevron-right\"></v-icon>\n        </a>\n      </v-card-text>\n    </v-card>\n  </div>\n</template>\n\n\n\n\n<script>\nexport default {\n  data() {\n    return {\n        dialog: false,\n        color: \"surface-variant\",\n        message: \"\",\n        username: \"\",\n        password: \"\"\n    }\n  },\n  watch: {\n      msg: function(){\n          // only show the dialog if msg.payload is an object and the socket id in the message\n          // matches our socket id (which means the popup was initiated from this session) or\n          // there is no _client property present which indicates it should be shown on all sessions.\n          if (this.msg.topic===\"close\") {\n            this.dialog = false;\n          } else {\n            if (!this.msg._client || this.msg._client.socketId === this.$socket.id) {   \n              console.log(\"OK we are in\");\n              if (typeof this.msg.payload !== object) msg.payload = {};\n              this.message = this.msg.payload.message ?? \"Access to this page is restricted. Please provide your user name and password in order to access this page.\";\n              this.color = this.msg.payload.color ?? \"surface-variant\";               \n              this.dialog = true;\n            }\n          }\n          // prevent redraw on deploy\n          this.msg.payload = null;\n      }\n  },\n  methods:{\n    LoginButton:function(){\n      // Do not hide the dialog, in case login fails\n      //this.dialog = false;\n      this.msg.topic = \"authenticate\";\n      this.msg.payload = { username: this.username, password: this.password };\n      this.send(this.msg);\n    }\n  },\n  computed : {\n  },\n  unmounted () {\n    this.dialog = false;\n  }\n}\n</script>","storeOutMessages":true,"passthru":false,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":170,"y":340,"wires":[[]]},{"id":"4a1d39d89b861855","type":"function","z":"d3d8a490d0e9960a","name":"connectionHandle","func":"const connectionList = flow.get(\"connectionList\") || []\n\n\nclass ConnectionManager {\n    constructor() {\n        this.connectionList = this.initConnections();\n    }\n\n    initConnections() {\n        return flow.get(\"connectionList\") || [];\n    }\n\n    setConnections() {\n        return flow.set(\"connectionList\", this.connectionList);\n    }\n\n    handleIncomingObject(msg) {\n        const { payload, name, _client } = msg;\n       \n        switch (payload) {\n            case 'connect':\n                this.addConnection(_client, name);\n                break;\n            case 'change':\n                this.changePage(_client, name);\n                break;\n            case 'lost':\n                this.removeConnection(_client);\n                break;\n            default:\n                console.log(`Unknown payload: ${payload}`);\n        }\n    }\n\n    addConnection(client, page) {\n        if (!this.connectionList.find(conn => conn.client.socketId === client.socketId)) {\n            const newConnection = {\n                client,\n                page,\n                timestamp: this.addTimestamp()\n            }\n            this.connectionList.push(newConnection);\n            //node.warn(`Added connection: ${socketId}`);\n        } else {\n            node.warn(`Connection ${client.socketId} already exists`);\n        }\n        this.setConnections();\n    }\n\n    changePage(client, page) {\n        const connection = this.connectionList.find(conn => conn.client.socketId === client.socketId);\n        if (connection) {\n            connection.page = page;\n            connection.timestamp = this.addTimestamp()\n            //node.warn(`Changed page for connection ${socketId} to ${name}`);\n        } else {\n            node.warn(`Connection ${client.socketId} not found`);\n        }\n        this.setConnections();\n    }\n\n    addTimestamp(){\n        return new Date().toISOString()\n    }\n\n    removeConnection(client) {\n        this.connectionList = this.connectionList.filter(conn => conn.client.socketId !== client.socketId);\n        node.warn(`Removed connection: ${client.socketId}`);\n        this.setConnections();\n    }\n\n\n}\n\n// Usage\nconst connectionManager = new ConnectionManager();\nconnectionManager.handleIncomingObject(msg)\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":580,"wires":[["5252bf4fba7e7f87"]]},{"id":"0222ae18d780749a","type":"inject","z":"d3d8a490d0e9960a","name":"User access defaults","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"users\":[{\"group\":\"mod\",\"username\":\"mod\",\"password\":\"0caaf4bb118927a2dc43115b7123f243443930a4\"},{\"group\":\"mod\",\"username\":\"gos\",\"password\":\"0caaf4bb118927a2dc43115b7123f243443930a4\"},{\"group\":\"mod\",\"username\":\"lika\",\"password\":\"0caaf4bb118927a2dc43115b7123f243443930a4\"}],\"settings\":{\"timeout\":3600}}","payloadType":"json","x":210,"y":660,"wires":[["608da81036e9a561"]]},{"id":"608da81036e9a561","type":"change","z":"d3d8a490d0e9960a","name":"","rules":[{"t":"set","p":"useraccess","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":660,"wires":[[]]},{"id":"3c08efcae56c6470","type":"inject","z":"d3d8a490d0e9960a","name":"Psw","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"tgwconfig","payloadType":"str","x":230,"y":740,"wires":[["b37d5853ada3ee6e"]]},{"id":"b37d5853ada3ee6e","type":"function","z":"d3d8a490d0e9960a","name":"hashPsw","func":"const cryptoJs = global.get(\"cryptojs\");\n\nconst hash = cryptoJs.SHA1(msg.payload).toString()\n\nmsg.payload = hash\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":740,"wires":[["35795fbb104bd3e5"]]},{"id":"35795fbb104bd3e5","type":"debug","z":"d3d8a490d0e9960a","name":"pswHash","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":740,"wires":[]},{"id":"dbee019f331c0d93","type":"function","z":"d3d8a490d0e9960a","name":"Validate credentials","func":"const useraccess = flow.get(\"useraccess\");\nconst connectionList = flow.get(\"connectionList\") || []\nconst cryptoJs = global.get(\"cryptojs\");\nconst now = new Date();\n\nconst passHash = cryptoJs.SHA1(msg.payload.password).toString()\n\nif ((Array.isArray(useraccess.users) ? 1 : 0) + (typeof useraccess.settings === \"object\" ? 1 : 0) !== 2) {\n    node.status({fill:\"red\",shape:\"ring\",text:\"Invalid useraccess config\"});\n    return [null, null];\n}\n\n// Check if an authentication request is received (logon attempt)\nif (msg.topic===\"authenticate\") {\n    let authenticated = false;\n    //node.warn(\"Check authetication\");\n    // Check if the username/password is valid\n    for (let i=0; i<useraccess.users.length; i++) {\n        //node.warn(\"Useraccess loop: \" + i);\n        if ((msg.payload.username === useraccess.users[i].username) && (passHash === useraccess.users[i].password)) {\n            msg._client.user = useraccess.users[i];\n            authenticated = true;\n\n            //Find user client data\n            const connUserIndex = connectionList.findIndex(conn => conn.client.socketId === msg._client.socketId)\n            if (connUserIndex !== -1) {\n                // Connection exists, update user and logonTime\n                connectionList[connUserIndex].client.user = msg.payload.username;\n                connectionList[connUserIndex].logonTime = now.getTime();\n                flow.set(\"connectionList\", connectionList)\n            }\n            break;\n        } \n    }\n    if (authenticated) {\n            msg.topic = \"close\";\n            msg.payload = \"\";\n            node.status({ fill: \"green\", shape: \"ring\", text: \"User accepted\" });\n            return [msg, msg];\n    } \n    else {\n        msg.topic = \"login\";\n        msg.payload = { color: \"red\", message: \"Invalid username or password.\" };\n        node.status({ fill: \"red\", shape: \"ring\", text: \"Invalid user\" });\n        return [msg, null];\n    }\n\n}\n","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":340,"wires":[["ef150be92d36d820"],["0997738cf03d9068"]]},{"id":"003f2613ed2057d1","type":"inject","z":"d3d8a490d0e9960a","name":"Show","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":390,"y":400,"wires":[["0997738cf03d9068"]]},{"id":"5252bf4fba7e7f87","type":"function","z":"d3d8a490d0e9960a","name":"SearchSocketID","func":"const connectionList = flow.get(\"connectionList\") || []\nconst client = msg?._client\n\nif (connectionList.length > 0){\n    const connUserIndex = connectionList.findIndex(conn => conn?.client?.socketId === client.socketId)\n    //check if user is logged in with this socket ID.\n    if (connectionList[connUserIndex].client.user) {\n        return [msg, null]\n    }\n    else return [null, msg]\n}\n","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":580,"wires":[["0a178d25241b6bf7"],["e9764b1176c38447","0cacd9dba27e365c"]]},{"id":"e9764b1176c38447","type":"function","z":"d3d8a490d0e9960a","name":"PreventPopUp","func":"msg.topic = \"close\"\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":580,"wires":[["f49f276f352bfb16"]]},{"id":"f49f276f352bfb16","type":"link out","z":"d3d8a490d0e9960a","name":"link out 10","mode":"link","links":["b77cfda3c7b3a881"],"x":945,"y":580,"wires":[]},{"id":"b77cfda3c7b3a881","type":"link in","z":"d3d8a490d0e9960a","name":"link in 5","links":["f49f276f352bfb16"],"x":35,"y":300,"wires":[["ef150be92d36d820"]]},{"id":"0a178d25241b6bf7","type":"link out","z":"d3d8a490d0e9960a","name":"link out 11","mode":"link","links":["d432840f7e6b5855"],"x":755,"y":540,"wires":[]},{"id":"d432840f7e6b5855","type":"link in","z":"d3d8a490d0e9960a","name":"link in 6","links":["0a178d25241b6bf7"],"x":475,"y":400,"wires":[["0997738cf03d9068"]]},{"id":"0cacd9dba27e365c","type":"link out","z":"d3d8a490d0e9960a","name":"link out 12","mode":"link","links":["f055c2134fd17b54"],"x":755,"y":620,"wires":[]},{"id":"f055c2134fd17b54","type":"link in","z":"d3d8a490d0e9960a","name":"link in 7","links":["0cacd9dba27e365c"],"x":475,"y":500,"wires":[["8b8e181044168f49"]]},{"id":"04c08c114913c192","type":"ui-group","name":"File Input","page":"7bc2300bd082d6d4","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"ad43ab200ca5494d","type":"ui-group","name":"Hikvision file control","page":"7bc2300bd082d6d4","width":"3","height":"1","order":2,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"65cf368b48fdb5f6","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control","ui-file-input"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"7bc2300bd082d6d4","type":"ui-page","name":"Hikvision","ui":"65cf368b48fdb5f6","path":"/Hikvision","icon":"camera","layout":"grid","theme":"d1f899a55e3aa9ab","order":5,"className":"","visible":"true","disabled":"false"},{"id":"d1f899a55e3aa9ab","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.