diff --git a/cmd/nonshy/main.go b/cmd/nonshy/main.go index a8ab1a4..e0526b5 100644 --- a/cmd/nonshy/main.go +++ b/cmd/nonshy/main.go @@ -10,6 +10,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/models/backfill" "code.nonshy.com/nonshy/website/pkg/redis" + "code.nonshy.com/nonshy/website/pkg/worker" "github.com/urfave/cli/v2" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -75,6 +76,9 @@ func main() { Port: c.Int("port"), } + // Kick off background worker threads. + go worker.WatchBareRTC() + return app.Run() }, }, diff --git a/pkg/config/config.go b/pkg/config/config.go index b374b63..90d0d36 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -64,6 +64,9 @@ const ( // How frequently to refresh LastLoginAt since sessions are long-lived. LastLoginAtCooldown = 8 * time.Hour + + // Chat room status refresh interval. + ChatStatusRefreshInterval = 30 * time.Second ) var ( diff --git a/pkg/controller/chat/chat.go b/pkg/controller/chat/chat.go index 3131149..c275e38 100644 --- a/pkg/controller/chat/chat.go +++ b/pkg/controller/chat/chat.go @@ -11,6 +11,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/photo" "code.nonshy.com/nonshy/website/pkg/session" "code.nonshy.com/nonshy/website/pkg/templates" + "code.nonshy.com/nonshy/website/pkg/worker" "github.com/golang-jwt/jwt/v4" ) @@ -106,6 +107,9 @@ func Landing() http.HandlerFunc { var vars = map[string]interface{}{ "ChatAPI": strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/statistics", "IsShyUser": isShy, + + // Pre-populate the "who's online" widget from backend cache data + "ChatStatistics": worker.GetChatStatistics(), } if err := tmpl.Execute(w, r, vars); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/pkg/templates/template_vars.go b/pkg/templates/template_vars.go index 21ffabf..f3f829c 100644 --- a/pkg/templates/template_vars.go +++ b/pkg/templates/template_vars.go @@ -8,6 +8,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" + "code.nonshy.com/nonshy/website/pkg/worker" ) // MergeVars mixes in globally available template variables. The http.Request is optional. @@ -37,6 +38,7 @@ func MergeUserVars(r *http.Request, m map[string]interface{}) { m["NavFriendRequests"] = 0 // Friend requests m["NavUnreadNotifications"] = 0 // general notifications m["NavTotalNotifications"] = 0 // Total of above + m["NavChatStatistics"] = worker.GetChatStatistics() // Admin notification counts for nav bar. m["NavCertificationPhotos"] = 0 // Cert. photos needing approval diff --git a/pkg/worker/barertc.go b/pkg/worker/barertc.go new file mode 100644 index 0000000..8763ae9 --- /dev/null +++ b/pkg/worker/barertc.go @@ -0,0 +1,92 @@ +package worker + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "sync" + "time" + + "code.nonshy.com/nonshy/website/pkg/config" + "code.nonshy.com/nonshy/website/pkg/log" +) + +// ChatStatistics is the json result of the BareRTC /api/statistics endpoint. +type ChatStatistics struct { + UserCount int + Usernames []string +} + +// GetChatStatistics returns the latest (cached) chat statistics. +func GetChatStatistics() ChatStatistics { + chatStatisticsMu.RLock() + defer chatStatisticsMu.RUnlock() + + if cachedChatStatistics != nil { + return *cachedChatStatistics + } + return ChatStatistics{ + Usernames: []string{}, + } +} + +var ( + cachedChatStatistics *ChatStatistics + chatStatisticsMu sync.RWMutex +) + +// WatchBareRTC is a worker goroutine that caches the current online chatters in the chat room. +func WatchBareRTC() { + if config.Current.BareRTC.JWTSecret == "" || config.Current.BareRTC.URL == "" { + log.Error("Worker (WatchBareRTC): chat room is not configured, will not watch chat room status") + return + } + + // Check it immediately. + DoCheckBareRTC() + + // And on an interval forever. + ticker := time.NewTicker(config.ChatStatusRefreshInterval) + for range ticker.C { + DoCheckBareRTC() + } +} + +// DoCheckBareRTC invokes the attempt to refresh data from the chat server about who's online. +func DoCheckBareRTC() { + log.Info("Refresh BareRTC") + req, err := http.NewRequest(http.MethodGet, config.Current.BareRTC.URL+"/api/statistics", nil) + + // Lock the cached statistics. + chatStatisticsMu.Lock() + defer chatStatisticsMu.Unlock() + + if err != nil { + log.Error("WatchBareRTC: couldn't make request: %s", err) + cachedChatStatistics = nil + return + } + + client := http.Client{ + Timeout: 30 * time.Second, + } + + res, err := client.Do(req) + if err != nil { + log.Error("WatchBareRTC: request error: %s", err) + cachedChatStatistics = nil + return + } + + if res.StatusCode == http.StatusOK { + var cs ChatStatistics + body, _ := ioutil.ReadAll(res.Body) + res.Body.Close() + if err = json.Unmarshal(body, &cs); err != nil { + log.Error("WatchBareRTC: json decode error: %s", err) + return + } + + cachedChatStatistics = &cs + } +} diff --git a/web/static/css/theme.css b/web/static/css/theme.css index 953abe3..f61aa59 100644 --- a/web/static/css/theme.css +++ b/web/static/css/theme.css @@ -86,4 +86,44 @@ abbr { /* Hide an element */ .nonshy-hidden { display: none; +} + +/*** + * Mobile navbar notification count badge no. + */ + +/* mobile view: just superset text */ +.nonshy-navbar-notification-count { + + font-size: xx-small; + padding-bottom: 12px; + margin-right: -4px; + margin-left: -4px; +} + +/* desktop view: colored badge similar to bulma `tag is-warning ml-1`*/ +.nonshy-navbar-notification-tag { + align-items: center; + border-radius: 4px; + display: inline-flex; + font-size: xx-small; + height: 1em; + justify-content: center; + vertical-align: top; + line-height: 1.5; + padding: .75em; + white-space: nowrap; + margin-bottom: auto; +} +.nonshy-navbar-notification-tag.is-warning { + background-color: #ffd324; + color: rgba(0, 0, 0, 0.7); +} +.nonshy-navbar-notification-tag.is-info { + background-color: #0f81cc; + color: #fff; +} +.nonshy-navbar-notification-tag.is-danger { + background-color: #ff0537; + color: #fff; } \ No newline at end of file diff --git a/web/templates/base.html b/web/templates/base.html index 029cfc2..1233518 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -52,6 +52,7 @@ Chat + {{if .NavChatStatistics.UserCount}}{{.NavChatStatistics.UserCount}}{{end}} @@ -67,13 +68,13 @@ Friends - {{if .NavFriendRequests}}{{.NavFriendRequests}}{{end}} + {{if .NavFriendRequests}}{{.NavFriendRequests}}{{end}} Messages - {{if .NavUnreadMessages}}{{.NavUnreadMessages}}{{end}} + {{if .NavUnreadMessages}}{{.NavUnreadMessages}}{{end}} {{end}} @@ -134,8 +135,8 @@
{{.CurrentUser.Username}} - {{if .NavUnreadNotifications}}{{.NavUnreadNotifications}}{{end}} - {{if .NavAdminNotifications}}{{.NavAdminNotifications}}{{end}} + {{if .NavUnreadNotifications}}{{.NavUnreadNotifications}}{{end}} + {{if .NavAdminNotifications}}{{.NavAdminNotifications}}{{end}}
@@ -145,7 +146,7 @@ Dashboard {{if .NavUnreadNotifications}} - + {{.NavUnreadNotifications}} @@ -181,7 +182,7 @@ Admin - {{if .NavAdminNotifications}}{{.NavAdminNotifications}}{{end}} + {{if .NavAdminNotifications}}{{.NavAdminNotifications}}{{end}} {{end}} {{if .SessionImpersonated}} @@ -216,9 +217,12 @@ {{if .LoggedIn}}
{{if .CurrentUser.Certified}} - + {{if gt .NavChatStatistics.UserCount 0}} + {{.NavChatStatistics.UserCount}} + {{end}} {{if gt .NavFriendRequests 0}} - {{.NavFriendRequests}} + {{.NavFriendRequests}} {{end}} @@ -244,21 +248,21 @@ {{if gt .NavUnreadMessages 0}} - {{.NavUnreadMessages}} + {{.NavUnreadMessages}} {{end}} {{if gt .NavUnreadNotifications 0}} - {{.NavUnreadNotifications}} + {{.NavUnreadNotifications}} {{end}} {{if gt .NavAdminNotifications 0}} - {{.NavAdminNotifications}} + {{.NavAdminNotifications}} {{end}}
diff --git a/web/templates/chat.html b/web/templates/chat.html index deeaca1..dda8848 100644 --- a/web/templates/chat.html +++ b/web/templates/chat.html @@ -128,13 +128,41 @@