Files
once-campfire/app/javascript/controllers/presence_controller.js
Kevin McConnell df76a227dc Hello world
First open source release of Campfire 🎉
2025-08-21 09:31:59 +01:00

84 lines
2.0 KiB
JavaScript

import { Controller } from "@hotwired/stimulus"
import { cable } from "@hotwired/turbo-rails"
import { delay, nextFrame } from "helpers/timing_helpers"
const REFRESH_INTERVAL = 50 * 1000 // 50 seconds
// We delay transmitting visibility changes to ignore brief periods of invisibility,
// like switching to another tab and back
const VISIBILITY_CHANGE_DELAY = 5000 // 5 seconds
export default class extends Controller {
async connect() {
this.channel = await cable.subscribeTo({ channel: "PresenceChannel", room_id: Current.room.id }, {
connected: this.#websocketConnected,
disconnected: this.#websocketDisconnected
})
this.wasVisible = true
await nextFrame()
this.dispatch("present", { detail: { roomId: Current.room.id } })
}
disconnect() {
this.#stopRefreshTimer()
this.channel?.unsubscribe()
}
visibilityChanged = () => {
if (this.#isVisible) {
this.#visible()
} else {
this.#hidden()
}
}
#websocketConnected = () => {
this.connected = true
this.#startRefreshTimer()
}
#websocketDisconnected = () => {
this.connected = false
this.#stopRefreshTimer()
}
#visible = async () => {
await delay(VISIBILITY_CHANGE_DELAY)
if (this.connected && this.#isVisible && !this.wasVisible) {
this.channel.send({ action: "present" })
this.#startRefreshTimer()
this.wasVisible = true
}
}
#hidden = async () => {
await delay(VISIBILITY_CHANGE_DELAY)
if (this.connected && this.wasVisible && !this.#isVisible) {
this.#stopRefreshTimer()
this.channel.send({ action: "absent" })
this.wasVisible = false
}
}
#startRefreshTimer = () => {
this.refreshTimer ??= setInterval(this.#refresh, REFRESH_INTERVAL)
}
#stopRefreshTimer = () => {
clearInterval(this.refreshTimer)
this.refreshTimer = null
}
#refresh = () => {
this.channel.send({ action: "refresh" })
}
get #isVisible() {
return document.visibilityState === "visible"
}
}