diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 4852eb8..481ef68 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 4852eb8..481ef68 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 7613457..2ed6e07 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -31,7 +31,7 @@ _ "math" ) -const POST_STATUS_TO_ADMIN = true +const POST_STATUS_TO_ADMIN = false const DO_LOGGING = false var splitWhitespaceOrComma *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) @@ -74,6 +74,9 @@ NumDoors string numDoors uint8 + PingInterval string + pingInterval int + WatchPath string AdminUsers string @@ -232,7 +235,13 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + auth, ok := r.Header["Mattermost-User-Id"] + var authUser string = "" + if ok && len(auth) > 0 { + authUser = auth[0] + } + p.log(fmt.Sprintf("Requested path: %s %s AUTH: %s", r.URL.Path, c.IpAddress, authUser)) + if r.URL.Path == "/settings" { p.configLock.Lock() defer p.configLock.Unlock() @@ -459,6 +468,19 @@ newConfig.numDoors = uint8(numDoors) } + if strings.Trim(newConfig.PingInterval, " \n\t") == "" { + newConfig.pingInterval = -1 + } else { + pingInterval, err := strconv.ParseInt(newConfig.PingInterval, 10, 32) + if err != nil { + newConfig.PingInterval = "" + newConfig.pingInterval = -1 + configErr = multierror.Append(configErr, errors.Wrap(err, "Invalid ping interval")) + } else { + newConfig.pingInterval = int(pingInterval) + } + } + newConfig.adminUsers = make([]*model.User, 0, 4) split := splitWhitespaceOrComma.Split(newConfig.AdminUsers, -1) @@ -624,9 +646,43 @@ } else { } + go p.pingLoop(); + return nil } +func (p *BathroomMonitorPlugin) pingLoop() { + for { + + var tickChan <-chan time.Time = nil + var ticker *time.Ticker = nil + + p.configLock.Lock() + p.log(fmt.Sprintf("Setting pingLoop with interval %d", p.config.pingInterval)) + if p.config.pingInterval > 0 { + ticker = time.NewTicker(time.Duration(p.config.pingInterval) * time.Second) + tickChan = ticker.C + + } + p.configLock.Unlock() + + run := true + for ;run; { + select { + case <- tickChan: + p.log(fmt.Sprintf("Sending ping")) + p.API.PublishWebSocketEvent("ping", map[string]interface{}{}, &model.WebsocketBroadcast{}) + case <- p.configChanged: + run = false + } + } + + if ticker != nil { + ticker.Stop() + } + } +} + func main() { plugin.ClientMain((&BathroomMonitorPlugin{}).init()) } diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 4852eb8..481ef68 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 7613457..2ed6e07 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -31,7 +31,7 @@ _ "math" ) -const POST_STATUS_TO_ADMIN = true +const POST_STATUS_TO_ADMIN = false const DO_LOGGING = false var splitWhitespaceOrComma *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) @@ -74,6 +74,9 @@ NumDoors string numDoors uint8 + PingInterval string + pingInterval int + WatchPath string AdminUsers string @@ -232,7 +235,13 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + auth, ok := r.Header["Mattermost-User-Id"] + var authUser string = "" + if ok && len(auth) > 0 { + authUser = auth[0] + } + p.log(fmt.Sprintf("Requested path: %s %s AUTH: %s", r.URL.Path, c.IpAddress, authUser)) + if r.URL.Path == "/settings" { p.configLock.Lock() defer p.configLock.Unlock() @@ -459,6 +468,19 @@ newConfig.numDoors = uint8(numDoors) } + if strings.Trim(newConfig.PingInterval, " \n\t") == "" { + newConfig.pingInterval = -1 + } else { + pingInterval, err := strconv.ParseInt(newConfig.PingInterval, 10, 32) + if err != nil { + newConfig.PingInterval = "" + newConfig.pingInterval = -1 + configErr = multierror.Append(configErr, errors.Wrap(err, "Invalid ping interval")) + } else { + newConfig.pingInterval = int(pingInterval) + } + } + newConfig.adminUsers = make([]*model.User, 0, 4) split := splitWhitespaceOrComma.Split(newConfig.AdminUsers, -1) @@ -624,9 +646,43 @@ } else { } + go p.pingLoop(); + return nil } +func (p *BathroomMonitorPlugin) pingLoop() { + for { + + var tickChan <-chan time.Time = nil + var ticker *time.Ticker = nil + + p.configLock.Lock() + p.log(fmt.Sprintf("Setting pingLoop with interval %d", p.config.pingInterval)) + if p.config.pingInterval > 0 { + ticker = time.NewTicker(time.Duration(p.config.pingInterval) * time.Second) + tickChan = ticker.C + + } + p.configLock.Unlock() + + run := true + for ;run; { + select { + case <- tickChan: + p.log(fmt.Sprintf("Sending ping")) + p.API.PublishWebSocketEvent("ping", map[string]interface{}{}, &model.WebsocketBroadcast{}) + case <- p.configChanged: + run = false + } + } + + if ticker != nil { + ticker.Stop() + } + } +} + func main() { plugin.ClientMain((&BathroomMonitorPlugin{}).init()) } diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index f3720a1..35f1cbe 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -131,9 +131,15 @@ Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); + this.interval = setInterval(this.updateDoors.bind(this), 30000); } componentWillUnmount() { + if (this.interval) { + clearInterval(this.interval); + this.interval = null; + } + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); } diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 4852eb8..481ef68 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 7613457..2ed6e07 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -31,7 +31,7 @@ _ "math" ) -const POST_STATUS_TO_ADMIN = true +const POST_STATUS_TO_ADMIN = false const DO_LOGGING = false var splitWhitespaceOrComma *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) @@ -74,6 +74,9 @@ NumDoors string numDoors uint8 + PingInterval string + pingInterval int + WatchPath string AdminUsers string @@ -232,7 +235,13 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + auth, ok := r.Header["Mattermost-User-Id"] + var authUser string = "" + if ok && len(auth) > 0 { + authUser = auth[0] + } + p.log(fmt.Sprintf("Requested path: %s %s AUTH: %s", r.URL.Path, c.IpAddress, authUser)) + if r.URL.Path == "/settings" { p.configLock.Lock() defer p.configLock.Unlock() @@ -459,6 +468,19 @@ newConfig.numDoors = uint8(numDoors) } + if strings.Trim(newConfig.PingInterval, " \n\t") == "" { + newConfig.pingInterval = -1 + } else { + pingInterval, err := strconv.ParseInt(newConfig.PingInterval, 10, 32) + if err != nil { + newConfig.PingInterval = "" + newConfig.pingInterval = -1 + configErr = multierror.Append(configErr, errors.Wrap(err, "Invalid ping interval")) + } else { + newConfig.pingInterval = int(pingInterval) + } + } + newConfig.adminUsers = make([]*model.User, 0, 4) split := splitWhitespaceOrComma.Split(newConfig.AdminUsers, -1) @@ -624,9 +646,43 @@ } else { } + go p.pingLoop(); + return nil } +func (p *BathroomMonitorPlugin) pingLoop() { + for { + + var tickChan <-chan time.Time = nil + var ticker *time.Ticker = nil + + p.configLock.Lock() + p.log(fmt.Sprintf("Setting pingLoop with interval %d", p.config.pingInterval)) + if p.config.pingInterval > 0 { + ticker = time.NewTicker(time.Duration(p.config.pingInterval) * time.Second) + tickChan = ticker.C + + } + p.configLock.Unlock() + + run := true + for ;run; { + select { + case <- tickChan: + p.log(fmt.Sprintf("Sending ping")) + p.API.PublishWebSocketEvent("ping", map[string]interface{}{}, &model.WebsocketBroadcast{}) + case <- p.configChanged: + run = false + } + } + + if ticker != nil { + ticker.Stop() + } + } +} + func main() { plugin.ClientMain((&BathroomMonitorPlugin{}).init()) } diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index f3720a1..35f1cbe 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -131,9 +131,15 @@ Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); + this.interval = setInterval(this.updateDoors.bind(this), 30000); } componentWillUnmount() { + if (this.interval) { + clearInterval(this.interval); + this.interval = null; + } + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); } diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map index 8abc908..c8ea70c 100644 --- a/mattermost/webapp/dist/main.js.map +++ b/mattermost/webapp/dist/main.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","hostTable","createRef","headerLeft","headerCenter","headerRight","state","doors","settings","componentDidMount","getSettings","resizeTable","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","componentWillUnmount","unregisterWebSocketEventHandler","msg","setState","render","borderString","theme","sidebarText","Host","width","icon","content","columns","keys","Object","sort","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","info_icon","componentDidUpdate","current","max","clientWidth","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;;;AClFA;;;;AAEA,MAAMA,iBAAN,SAAgCC,eAAMC,SAAtC,CACA;AACIC,aAAW,CAACC,KAAD,EACX;AACI,UAAMA,KAAN;AAEA,SAAKC,SAAL,GAAiBJ,eAAMK,SAAN,EAAjB;AACA,SAAKC,UAAL,GAAkBN,eAAMK,SAAN,EAAlB;AACA,SAAKE,YAAL,GAAoBP,eAAMK,SAAN,EAApB;AACA,SAAKG,WAAL,GAAmBR,eAAMK,SAAN,EAAnB;AACA,SAAKI,KAAL,GAAa;AAACC,WAAK,EAAC;AAAP,KAAb;AACA,SAAKC,QAAL,GAAgB,EAAhB;AACH;;AACDC,mBAAiB,GACjB;AACI,SAAKC,WAAL;AACA,SAAKC,WAAL;AACH;;AACD,QAAMD,WAAN,GACA;AACI,QACA;AAEI,UAAIE,GAAG,GAAGC,MAAM,CAACC,QAAP,CAAgBC,MAAhB,GAAyB,2CAAnC;AACA,UAAIC,GAAG,GAAG,MAAMC,KAAK,CAACL,GAAD,CAArB;AACA,WAAKJ,QAAL,GAAgB,MAAMQ,GAAG,CAACE,IAAJ,EAAtB;AACH,KAND,CAOA,OAAOC,CAAP,EACA;AACIC,aAAO,CAACC,GAAR,CAAYF,CAAZ;AACAG,gBAAU,CAAC,KAAKZ,WAAL,CAAiBa,IAAjB,CAAsB,IAAtB,CAAD,EAA8B,IAA9B,CAAV;AACA;AACH;;AAEDC,YAAQ,CAACC,6BAAT,CAAuC,wCAAvC,EAAiF,KAAKC,WAAL,CAAiBH,IAAjB,CAAsB,IAAtB,CAAjF;AACA,SAAKG,WAAL;AACH;;AACDC,sBAAoB,GACpB;AACIH,YAAQ,CAACI,+BAAT,CAAyC,wCAAzC;AACH;;AACD,QAAMF,WAAN,CAAkBG,GAAlB,EACA;AACI;AACA,QACA;AACI,UAAIjB,GAAG,GAAGC,MAAM,CAACC,QAAP,CAAgBC,MAAhB,GAAyB,yCAAnC;AACA,UAAIC,GAAG,GAAG,MAAMC,KAAK,CAACL,GAAD,CAArB;AACA,UAAIM,IAAI,GAAG,MAAMF,GAAG,CAACE,IAAJ,EAAjB;AACA,WAAKY,QAAL,CAAc;AAAC,iBAAQZ;AAAT,OAAd;AACH,KAND,CAOA,OAAOC,CAAP,EACA;AACIC,aAAO,CAACC,GAAR,CAAYF,CAAZ;AACAG,gBAAU,CAAC,KAAKI,WAAL,CAAiBH,IAAjB,CAAsB,IAAtB,CAAD,EAA8B,IAA9B,CAAV;AACH;AACJ;;AACDQ,QAAM,GACN;AACI,QAAI,CAAC,KAAKzB,KAAL,CAAWC,KAAhB,EAAuB;AAEvB,QAAIyB,YAAY,GAAG,eAAe,KAAKhC,KAAL,CAAWiC,KAAX,CAAiBC,WAAnD;;AAEA,QAAIC,IAAI,GAAGnC,KAAK,IACZ;AAAK,WAAK,EAAE;AAAC,sBAAc,QAAf;AAAyBoC,aAAK,EAAC,MAA/B;AAAuC,uBAAc;AAArD;AAAZ,OACA;AAAO,SAAG,EAAE,KAAKnC,SAAjB;AAA4B,WAAK,EAAE;AAAC,2BAAkB,UAAnB;AAA+B,uBAAc,MAA7C;AAAqD,wBAAe;AAApE;AAAnC,OACA,yCACE;AAAI,SAAG,EAAE,KAAKE,UAAd;AAA0B,WAAK,EAAE;AAAC,yBAAgB6B,YAAjB;AAA+B,kBAAS;AAAxC;AAAjC,MADF,EAEE;AAAI,SAAG,EAAE,KAAK5B,YAAd;AAA6B,aAAO,EAAE,CAAtC;AAAyC,WAAK,EAAE;AAAC,iBAAQ,MAAT;AAAiB,sBAAa;AAA9B;AAAhD,OAAyF;AAAK,SAAG,EAAEJ,KAAK,CAACqC,IAAhB;AAAsB,WAAK,EAAE;AAAC,iBAAQ;AAAT;AAA7B,MAAzF,CAFF,EAGE;AAAI,SAAG,EAAE,KAAKhC,WAAd;AAA4B,WAAK,EAAE;AAAC,yBAAgB2B;AAAjB;AAAnC,MAHF,CADA,EAOA,yCACE;AAAI,WAAK,EAAE;AAAC,uBAAcA;AAAf;AAAX,MADF,EAEE;AAAI,WAAK,EAAE;AAAC,wBAAeA;AAAhB;AAAX,MAFF,CAPA,EAWA,yCACE;AAAI,aAAO,EAAE,CAAb;AAAgB,WAAK,EAAE;AAAC,sBAAa,QAAd;AAAwB,uBAAcA,YAAtC;AAAmD,wBAAeA,YAAlE;AAA+E,yBAAgBA;AAA/F;AAAvB,OAAsIhC,KAAK,CAACsC,OAA5I,CADF,CAXA,CADA,CADJ;;AAmBA,QAAIC,OAAO,GAAG,CAAd;AACA,QAAIC,IAAI,GAAGC,MAAM,CAACD,IAAP,CAAY,KAAKlC,KAAL,CAAWC,KAAvB,EAA8BmC,IAA9B,EAAX;AACA,QAAIN,KAAK,GAAGO,IAAI,CAACC,KAAL,CAAW,MAAOL;AAAQ;AAA1B,QAAsC,GAAlD;AACA,QAAIM,OAAO,GAAG,MAAd;AACA,QAAIC,KAAK,GAAG,EAAZ;AACA,QAAIC,GAAG,GAAG,EAAV;AACA,QAAIC,IAAI,GAAG,CAAX;;AACA,QAAIC,OAAO,GAAGjD,KAAK,IAAI;AAAK,SAAG,EAAEA,KAAK,CAACkD,GAAhB;AAAqB,WAAK,EAAC,MAA3B;AAAkC,WAAK,EAAE;AAAC,mBAAU;AAAX,OAAzC;AAA4D,WAAK,EAAElD,KAAK,CAACmD;AAAzE,MAAvB;;AACA,QAAIC,SAAS,GAAGT,IAAI,CAACC,KAAL,CAAWJ,IAAI,CAACa,MAAL,GAAcd,OAAzB,IAAoC,CAApD;;AACA,SAAK,IAAIe,CAAC,GAAG,CAAb,EAAgBA,CAAC,GAAGd,IAAI,CAACa;AAAO;AAAhC,MAA2EC,CAAC,EAA5E,EACA;AACI,UAAIC,GAAG,GAAG,IAAV;;AACA,UAAID,CAAC,GAAGd,IAAI,CAACa,MAAb,EACA;AACI,YAAIG,GAAG,GAAGhB,IAAI,CAACc,CAAD,CAAd;AACA,YAAIG,IAAI,GAAG,KAAKnD,KAAL,CAAWC,KAAX,CAAiBiD,GAAjB,CAAX;AACA,YAAIE,OAAO,GAAG,KAAKlD,QAAL,CAAciD,IAAI,GAAG,OAArB,CAAd;;AACA,YAAIC,OAAJ,EACA;AACIH,aAAG,GAAG,6BAAC,OAAD;AAAS,eAAG,EAAEG,OAAd;AAAuB,iBAAK,EAAE,KAAKlD,QAAL,CAAcD,KAAd,CAAoBiD,GAApB;AAA9B,YAAN,CADJ,CAEI;AACH;AACJ;;AACDT,SAAG,CAACY,IAAJ,CAAS;AAAI,aAAK,EAAEd;AAAX,SAAqBU,GAArB,CAAT;;AACA,UAAID,CAAC,GAAGf,OAAJ,IAAeA,OAAO,GAAG,CAAzB,IAA8Be,CAAC,IAAId,IAAI,CAACa,MAAL,GAAc,CAArD,EACA;AACI;;;;;;;;;AASAP,aAAK,CAACa,IAAN,CAAW;AAAI,eAAK,EAAE;AAAC,sBAAS;AAAV;AAAX,WAA+B,CAAC,GAAGZ,GAAJ,CAA/B,CAAX;AACAA,WAAG,CAACM,MAAJ,GAAa,CAAb;AACAL,YAAI;AACP;AACJ;;AACD,QAAIV,OAAO,GACP;AAAO,WAAK,EAAE;AAAC,kBAAS,KAAV;AAAiB,uBAAc,MAA/B;AAAuC,wBAAe;AAAtD;AAAd,OACCQ,KADD,CADJ;;AAIA,WAAO,6BAAC,IAAD;AAAM,aAAO,EAAER,OAAf;AAAwB,UAAI,EAAE,KAAK9B,QAAL,CAAcoD;AAA5C,MAAP;AACH;;AACDC,oBAAkB,GAClB;AACI,SAAKlD,WAAL;AACH;;AACDA,aAAW,GACX;AACI,SAAKR,UAAL,CAAgB2D,OAAhB,CAAwB1B,KAAxB,GAAgC,KAAK/B,WAAL,CAAiByD,OAAjB,CAAyB1B,KAAzB,GAAiCO,IAAI,CAACoB,GAAL,CAAS,EAAT,EAAa,CAAC,KAAK9D,SAAL,CAAe6D,OAAf,CAAuBE,WAAvB,GAAqC,KAAK5D,YAAL,CAAkB0D,OAAlB,CAA0BE,WAAhE,IAA+E,CAA5F,CAAjE;AACH;;AAtIL;;AAyIA,IAAIxC,QAAQ,GAAG,IAAf;AACA,IAAIyC,KAAK,GAAG,IAAZ;;AAEA,MAAMC,qBAAN,CACA;AACIC,YAAU,CAACC,QAAD,EAAWC,KAAX,EACV;AACI7C,YAAQ,GAAG4C,QAAX;AACAH,SAAK,GAAGI,KAAR;AACAD,YAAQ,CAACE,kCAAT,CAA4C1E,iBAA5C;AACH;;AANL;;AASAiB,MAAM,CAAC0D,cAAP,CAAsB,yBAAtB,EAAiD,IAAIL,qBAAJ,EAAjD,E;;;;;;;;;;;;;;;;;;;;;;;ACzJA,uB","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n\n this.hostTable = React.createRef();\n this.headerLeft = React.createRef();\n this.headerCenter = React.createRef();\n this.headerRight = React.createRef();\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n this.resizeTable();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n\n var borderString = \"1px solid \" + this.props.theme.sidebarText;\n\n var Host = props =>\n
\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n
{props.content}
\n
;\n\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns /*+ 1*/)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n /*\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n */\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n var content =\n \n {elems}\n
\n return ;\n }\n componentDidUpdate()\n {\n this.resizeTable();\n }\n resizeTable()\n {\n this.headerLeft.current.width = this.headerRight.current.width = Math.max(50, (this.hostTable.current.clientWidth - this.headerCenter.current.clientWidth) / 2);\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","hostTable","createRef","headerLeft","headerCenter","headerRight","state","doors","settings","componentDidMount","getSettings","resizeTable","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","interval","setInterval","componentWillUnmount","clearInterval","unregisterWebSocketEventHandler","msg","setState","render","borderString","theme","sidebarText","Host","width","icon","content","columns","keys","Object","sort","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","info_icon","componentDidUpdate","current","max","clientWidth","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n\n this.hostTable = React.createRef();\n this.headerLeft = React.createRef();\n this.headerCenter = React.createRef();\n this.headerRight = React.createRef();\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n this.resizeTable();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n this.interval = setInterval(this.updateDoors.bind(this), 30000);\n }\n componentWillUnmount()\n {\n if (this.interval)\n {\n clearInterval(this.interval);\n this.interval = null;\n }\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n\n var borderString = \"1px solid \" + this.props.theme.sidebarText;\n\n var Host = props =>\n
\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n
{props.content}
\n
;\n\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns /*+ 1*/)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n /*\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n */\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n var content =\n \n {elems}\n
\n return ;\n }\n componentDidUpdate()\n {\n this.resizeTable();\n }\n resizeTable()\n {\n this.headerLeft.current.width = this.headerRight.current.width = Math.max(50, (this.hostTable.current.clientWidth - this.headerCenter.current.clientWidth) / 2);\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/bathroom.tar.gz b/mattermost/bathroom.tar.gz index 130944c..bb856c1 100644 --- a/mattermost/bathroom.tar.gz +++ b/mattermost/bathroom.tar.gz Binary files differ diff --git a/mattermost/plugin.json b/mattermost/plugin.json index 872bd8c..9437d22 100644 --- a/mattermost/plugin.json +++ b/mattermost/plugin.json @@ -12,6 +12,7 @@ {"key":"NumDoors", "display_name":"Number of Door Sensors", "type":"text", "default":"1", "help_text":"How many Pis"}, {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/plugin_templ.json b/mattermost/plugin_templ.json index fad5981..9299035 100644 --- a/mattermost/plugin_templ.json +++ b/mattermost/plugin_templ.json @@ -16,6 +16,7 @@ {"key":"KeyPath", "display_name":"Public key path", "type":"text", "default":"./", "help_text":"Path for public keys to server Pi requests"}, #endif {"key":"AdminUsers", "display_name":"Plugin admin users", "type":"text", "default":"", "help_text":"Space- or comma-separated list of users to notify with plugin debug message"}, + {"key":"PingInterval", "display_name":"Ping interval", "type":"text", "default":"", "help_text":"Send a ping every X seconds to keep websockets alive through nginx (leave blank for none)"}, {"key":"DoorNames", "display_name":"Door names", "type":"text", "default":"", "help_text":"|-separated list of names of the office doors"}, {"key":"InfoIcon", "display_name":"Info icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display at the left before all the door icons"}, {"key":"UnknownIcon", "display_name":"Unknown icon url", "type":"text", "default":"", "help_text":"(Relative) URL of the icon to display for doors without their status set yet"}, diff --git a/mattermost/server/bathroom-linux-amd64 b/mattermost/server/bathroom-linux-amd64 index 4852eb8..481ef68 100755 --- a/mattermost/server/bathroom-linux-amd64 +++ b/mattermost/server/bathroom-linux-amd64 Binary files differ diff --git a/mattermost/server/bathroom.go b/mattermost/server/bathroom.go index 7613457..2ed6e07 100644 --- a/mattermost/server/bathroom.go +++ b/mattermost/server/bathroom.go @@ -31,7 +31,7 @@ _ "math" ) -const POST_STATUS_TO_ADMIN = true +const POST_STATUS_TO_ADMIN = false const DO_LOGGING = false var splitWhitespaceOrComma *regexp.Regexp = regexp.MustCompile(`\s+(^|[^,])|\s*,\s*`) @@ -74,6 +74,9 @@ NumDoors string numDoors uint8 + PingInterval string + pingInterval int + WatchPath string AdminUsers string @@ -232,7 +235,13 @@ } func (p *BathroomMonitorPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - p.log(fmt.Sprintf("Requested path: %s %s", r.URL.Path, c.IpAddress)) + auth, ok := r.Header["Mattermost-User-Id"] + var authUser string = "" + if ok && len(auth) > 0 { + authUser = auth[0] + } + p.log(fmt.Sprintf("Requested path: %s %s AUTH: %s", r.URL.Path, c.IpAddress, authUser)) + if r.URL.Path == "/settings" { p.configLock.Lock() defer p.configLock.Unlock() @@ -459,6 +468,19 @@ newConfig.numDoors = uint8(numDoors) } + if strings.Trim(newConfig.PingInterval, " \n\t") == "" { + newConfig.pingInterval = -1 + } else { + pingInterval, err := strconv.ParseInt(newConfig.PingInterval, 10, 32) + if err != nil { + newConfig.PingInterval = "" + newConfig.pingInterval = -1 + configErr = multierror.Append(configErr, errors.Wrap(err, "Invalid ping interval")) + } else { + newConfig.pingInterval = int(pingInterval) + } + } + newConfig.adminUsers = make([]*model.User, 0, 4) split := splitWhitespaceOrComma.Split(newConfig.AdminUsers, -1) @@ -624,9 +646,43 @@ } else { } + go p.pingLoop(); + return nil } +func (p *BathroomMonitorPlugin) pingLoop() { + for { + + var tickChan <-chan time.Time = nil + var ticker *time.Ticker = nil + + p.configLock.Lock() + p.log(fmt.Sprintf("Setting pingLoop with interval %d", p.config.pingInterval)) + if p.config.pingInterval > 0 { + ticker = time.NewTicker(time.Duration(p.config.pingInterval) * time.Second) + tickChan = ticker.C + + } + p.configLock.Unlock() + + run := true + for ;run; { + select { + case <- tickChan: + p.log(fmt.Sprintf("Sending ping")) + p.API.PublishWebSocketEvent("ping", map[string]interface{}{}, &model.WebsocketBroadcast{}) + case <- p.configChanged: + run = false + } + } + + if ticker != nil { + ticker.Stop() + } + } +} + func main() { plugin.ClientMain((&BathroomMonitorPlugin{}).init()) } diff --git a/mattermost/webapp/dist/main.js b/mattermost/webapp/dist/main.js index f3720a1..35f1cbe 100644 --- a/mattermost/webapp/dist/main.js +++ b/mattermost/webapp/dist/main.js @@ -131,9 +131,15 @@ Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); + this.interval = setInterval(this.updateDoors.bind(this), 30000); } componentWillUnmount() { + if (this.interval) { + clearInterval(this.interval); + this.interval = null; + } + Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); } diff --git a/mattermost/webapp/dist/main.js.map b/mattermost/webapp/dist/main.js.map index 8abc908..c8ea70c 100644 --- a/mattermost/webapp/dist/main.js.map +++ b/mattermost/webapp/dist/main.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","hostTable","createRef","headerLeft","headerCenter","headerRight","state","doors","settings","componentDidMount","getSettings","resizeTable","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","componentWillUnmount","unregisterWebSocketEventHandler","msg","setState","render","borderString","theme","sidebarText","Host","width","icon","content","columns","keys","Object","sort","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","info_icon","componentDidUpdate","current","max","clientWidth","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n\n this.hostTable = React.createRef();\n this.headerLeft = React.createRef();\n this.headerCenter = React.createRef();\n this.headerRight = React.createRef();\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n this.resizeTable();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n }\n componentWillUnmount()\n {\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n\n var borderString = \"1px solid \" + this.props.theme.sidebarText;\n\n var Host = props =>\n
\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n
{props.content}
\n
;\n\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns /*+ 1*/)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n /*\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n */\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n var content =\n \n {elems}\n
\n return ;\n }\n componentDidUpdate()\n {\n this.resizeTable();\n }\n resizeTable()\n {\n this.headerLeft.current.width = this.headerRight.current.width = Math.max(50, (this.hostTable.current.clientWidth - this.headerCenter.current.clientWidth) / 2);\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.jsx","webpack:///external \"React\""],"names":["BathroomComponent","React","Component","constructor","props","hostTable","createRef","headerLeft","headerCenter","headerRight","state","doors","settings","componentDidMount","getSettings","resizeTable","url","window","location","origin","res","fetch","json","e","console","log","setTimeout","bind","Registry","registerWebSocketEventHandler","updateDoors","interval","setInterval","componentWillUnmount","clearInterval","unregisterWebSocketEventHandler","msg","setState","render","borderString","theme","sidebarText","Host","width","icon","content","columns","keys","Object","sort","Math","floor","widthPx","elems","row","rows","IconImg","src","title","totalRows","length","i","img","key","door","imgFile","push","info_icon","componentDidUpdate","current","max","clientWidth","Store","BathroomMonitorPlugin","initialize","registry","store","registerLeftSidebarHeaderComponent","registerPlugin"],"mappings":"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","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import React from 'react';\n\nclass BathroomComponent extends React.Component\n{\n constructor(props)\n {\n super(props);\n\n this.hostTable = React.createRef();\n this.headerLeft = React.createRef();\n this.headerCenter = React.createRef();\n this.headerRight = React.createRef();\n this.state = {doors:{}};\n this.settings = {};\n }\n componentDidMount()\n {\n this.getSettings();\n this.resizeTable();\n }\n async getSettings()\n {\n try\n {\n\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/settings\";\n var res = await fetch(url);\n this.settings = await res.json();\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.getSettings.bind(this), 5000);\n return;\n }\n\n Registry.registerWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\", this.updateDoors.bind(this));\n this.updateDoors();\n this.interval = setInterval(this.updateDoors.bind(this), 30000);\n }\n componentWillUnmount()\n {\n if (this.interval)\n {\n clearInterval(this.interval);\n this.interval = null;\n }\n Registry.unregisterWebSocketEventHandler(\"custom_com.mattermost.bathroom_updated\");\n }\n async updateDoors(msg)\n {\n //var url = Store.getState().entities.general.config.SiteURL + \"/plugins/com.mattermost.bathroom/status\";\n try\n {\n var url = window.location.origin + \"/plugins/com.mattermost.bathroom/status\";\n var res = await fetch(url);\n var json = await res.json();\n this.setState({\"doors\":json});\n }\n catch (e)\n {\n console.log(e);\n setTimeout(this.updateDoors.bind(this), 3000);\n }\n }\n render()\n {\n if (!this.state.doors) return;\n\n var borderString = \"1px solid \" + this.props.theme.sidebarText;\n\n var Host = props =>\n
\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n
{props.content}
\n
;\n\n var columns = 6;\n var keys = Object.keys(this.state.doors).sort();\n var width = Math.floor(100 / (columns /*+ 1*/)) + \"%\";\n var widthPx = \"23px\";\n var elems = [];\n var row = [];\n var rows = 0;\n var IconImg = props => ;\n var totalRows = Math.floor(keys.length / columns) + 1;\n for (var i = 0; i < keys.length /*(Math.floor(keys.length / 6) + 1) * 6*/; i++)\n {\n var img = null;\n if (i < keys.length)\n {\n var key = keys[i];\n var door = this.state.doors[key];\n var imgFile = this.settings[door + \"_icon\"];\n if (imgFile)\n {\n img = ;\n //img = \n }\n }\n row.push({img});\n if (i % columns == columns - 1 || i == keys.length - 1)\n {\n /*\n if (rows == 0)\n {\n row.unshift(\n //\n :\n );\n }\n */\n elems.push({[...row]});\n row.length = 0;\n rows++;\n }\n }\n var content =\n \n {elems}\n
\n return ;\n }\n componentDidUpdate()\n {\n this.resizeTable();\n }\n resizeTable()\n {\n this.headerLeft.current.width = this.headerRight.current.width = Math.max(50, (this.hostTable.current.clientWidth - this.headerCenter.current.clientWidth) / 2);\n }\n}\n\nvar Registry = null;\nvar Store = null;\n\nclass BathroomMonitorPlugin\n{\n initialize(registry, store)\n {\n Registry = registry;\n Store = store;\n registry.registerLeftSidebarHeaderComponent(BathroomComponent);\n }\n}\n\nwindow.registerPlugin('com.mattermost.bathroom', new BathroomMonitorPlugin());\n","module.exports = React;"],"sourceRoot":""} \ No newline at end of file diff --git a/mattermost/webapp/src/index.jsx b/mattermost/webapp/src/index.jsx index bdec4bd..b87f43a 100644 --- a/mattermost/webapp/src/index.jsx +++ b/mattermost/webapp/src/index.jsx @@ -36,9 +36,15 @@ Registry.registerWebSocketEventHandler("custom_com.mattermost.bathroom_updated", this.updateDoors.bind(this)); this.updateDoors(); + this.interval = setInterval(this.updateDoors.bind(this), 30000); } componentWillUnmount() { + if (this.interval) + { + clearInterval(this.interval); + this.interval = null; + } Registry.unregisterWebSocketEventHandler("custom_com.mattermost.bathroom_updated"); } async updateDoors(msg)