Files
archived-hc-harbor/app/javascript/controllers/currently_hacking_controller.js
2025-06-22 22:25:07 -04:00

124 lines
3.3 KiB
JavaScript

import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["container", "count"]
static values = {
interval: { type: Number, default: 60000 }, // 60 seconds to match cron
url: String
}
connect() {
this.lastFullFetch = Date.now() // Initialize to now to prevent immediate refetch on click
this.isExpanded = false
this.startPolling()
this.boundClickHandler = this.handleClick.bind(this)
this.containerTarget.addEventListener('click', this.boundClickHandler)
}
disconnect() {
this.stopPolling()
this.containerTarget.removeEventListener('click', this.boundClickHandler)
}
handleClick(event) {
const header = event.target.closest('.currently-hacking')
if (header) {
this.toggle()
// Poll immediately when opening if we haven't fetched the list recently
if (this.isExpanded) {
const now = Date.now()
const timeSinceLastFetch = now - this.lastFullFetch
if (timeSinceLastFetch > 30000) {
this.poll()
}
}
}
}
toggle() {
this.isExpanded = !this.isExpanded
const frame = document.getElementById("currently_hacking")
if (frame) {
frame.style.display = this.isExpanded ? 'block' : 'none'
}
}
isVisible() {
return this.isExpanded
}
startPolling() {
this.stopPolling() // Clear any existing interval
this.poll() // Initial poll
this.intervalId = setInterval(() => {
this.poll()
}, this.intervalValue)
}
stopPolling() {
if (this.intervalId) {
clearInterval(this.intervalId)
this.intervalId = null
}
}
async poll() {
try {
const includeList = this.isVisible()
const url = new URL(this.urlValue, window.location.origin)
url.searchParams.set('include_list', includeList.toString())
// Track when we request the full list, not just when we get it back
if (includeList) {
this.lastFullFetch = Date.now()
}
const response = await fetch(url, {
headers: {
"Accept": "application/json"
}
})
if (response.ok) {
const data = await response.json()
this.updateCount(data.count)
if (data.html) {
this.updateFrame(data.html)
}
}
} catch (error) {
console.error("Failed to poll currently hacking:", error)
}
}
updateCount(count) {
if (this.hasCountTarget) {
const plural = count === 1 ? "person" : "people"
this.countTarget.textContent = `${count} ${plural} currently hacking`
}
}
updateFrame(html) {
const frame = document.getElementById("currently_hacking")
if (frame && html) {
// Save scroll position before updating
const scrollContainer = frame.querySelector(".currently-hacking-list")
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0
// Update content
frame.innerHTML = html
frame.style.display = this.isExpanded ? 'block' : 'none'
// Restore scroll position after a brief delay to allow DOM update
if (scrollTop > 0) {
requestAnimationFrame(() => {
const newScrollContainer = frame.querySelector(".currently-hacking-list")
if (newScrollContainer) {
newScrollContainer.scrollTop = scrollTop
}
})
}
}
}
}