package main
import (
"fmt"
"sync/atomic"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/websocket"
)
var events = websocket.Namespaces{
"default": websocket.Events{
websocket.OnRoomJoined: onRoomJoined,
websocket.OnRoomLeft: onRoomLeft,
},
}
func main() {
// init the web application instance
// app := iris.New()
app := iris.Default()
// load templates
app.RegisterView(iris.HTML("./templates", ".html").Reload(true))
// setup the websocket server
ws := websocket.New(websocket.DefaultGorillaUpgrader, events)
app.Get("/my_endpoint", websocket.Handler(ws))
// register static assets request path and system directory
app.HandleDir("/js", iris.Dir("./static/assets/js"))
h := func(ctx iris.Context) {
ctx.ViewData("", page{PageID: "index page"})
if err := ctx.View("index.html"); err != nil {
ctx.HTML("
%s
", err.Error())
return
}
}
h2 := func(ctx iris.Context) {
ctx.ViewData("", page{PageID: "other page"})
if err := ctx.View("other.html"); err != nil {
ctx.HTML("%s
", err.Error())
return
}
}
// Open some browser tabs/or windows
// and navigate to
// http://localhost:8080/ and http://localhost:8080/other multiple times.
// Each page has its own online-visitors counter.
app.Get("/", h)
app.Get("/other", h2)
app.Listen(":8080")
}
type page struct {
PageID string
}
type pageView struct {
source string
count uint64
}
func (v *pageView) increment() {
atomic.AddUint64(&v.count, 1)
}
func (v *pageView) decrement() {
atomic.AddUint64(&v.count, ^uint64(0))
}
func (v *pageView) getCount() uint64 {
return atomic.LoadUint64(&v.count)
}
type (
pageViews []pageView
)
func (v *pageViews) Add(source string) {
args := *v
n := len(args)
for i := 0; i < n; i++ {
kv := &args[i]
if kv.source == source {
kv.increment()
return
}
}
c := cap(args)
if c > n {
args = args[:n+1]
kv := &args[n]
kv.source = source
kv.count = 1
*v = args
return
}
kv := pageView{}
kv.source = source
kv.count = 1
*v = append(args, kv)
}
func (v *pageViews) Get(source string) *pageView {
args := *v
n := len(args)
for i := 0; i < n; i++ {
kv := &args[i]
if kv.source == source {
return kv
}
}
return nil
}
func (v *pageViews) Reset() {
*v = (*v)[:0]
}
var v pageViews
func viewsCountBytes(viewsCount uint64) []byte {
// * there are other methods to convert uint64 to []byte
return []byte(fmt.Sprintf("%d", viewsCount))
}
func onRoomJoined(ns *websocket.NSConn, msg websocket.Message) error {
// the roomName here is the source.
pageSource := string(msg.Room)
v.Add(pageSource)
viewsCount := v.Get(pageSource).getCount()
if viewsCount == 0 {
viewsCount++ // count should be always > 0 here
}
// fire the "onNewVisit" client event
// on each connection joined to this room (source page)
// and notify of the new visit,
// including this connection (see nil on first input arg).
ns.Conn.Server().Broadcast(nil, websocket.Message{
Namespace: msg.Namespace,
Room: pageSource,
Event: "onNewVisit", // fire the "onNewVisit" client event.
Body: viewsCountBytes(viewsCount),
})
return nil
}
func onRoomLeft(ns *websocket.NSConn, msg websocket.Message) error {
// the roomName here is the source.
pageV := v.Get(msg.Room)
if pageV == nil {
return nil // for any case that this room is not a pageView source
}
// decrement -1 the specific counter for this page source.
pageV.decrement()
// fire the "onNewVisit" client event
// on each connection joined to this room (source page)
// and notify of the new, decremented by one, visits count.
ns.Conn.Server().Broadcast(nil, websocket.Message{
Namespace: msg.Namespace,
Room: msg.Room,
Event: "onNewVisit",
Body: viewsCountBytes(pageV.getCount()),
})
return nil
}