import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["container", "count", "content"] static values = { interval: { type: Number, default: 60000 }, // 60 seconds countUrl: String, fullUrl: String } connect() { this.isExpanded = false this.isLoading = false this.isVisible = false this.startCountPolling() this.boundClickHandler = this.handleClick.bind(this) this.containerTarget.addEventListener('click', this.boundClickHandler) } disconnect() { this.stopCountPolling() this.containerTarget.removeEventListener('click', this.boundClickHandler) } handleClick(event) { const header = event.target.closest('.currently-hacking') if (header) { this.toggle() } } async toggle() { this.isExpanded = !this.isExpanded if (this.isExpanded) { this.showLoading() this.contentTarget.style.display = 'block' await this.gimmeAll() } else { this.contentTarget.style.display = 'none' } } showLoading() { this.contentTarget.innerHTML = `
Loading...
` } showBanner() { if (!this.isVisible) { this.isVisible = true this.containerTarget.classList.remove('hidden') setTimeout(() => { this.containerTarget.classList.remove('-translate-y-full') this.containerTarget.classList.add('translate-y-0') }, 300) } } startCountPolling() { this.stopCountPolling() this.pollCount() this.countIntervalId = setInterval(() => { this.pollCount() }, this.intervalValue) } stopCountPolling() { if (this.countIntervalId) { clearInterval(this.countIntervalId) this.countIntervalId = null } } async pollCount() { try { const response = await fetch(this.countUrlValue, { headers: { "Accept": "application/json" } }) if (response.ok) { const data = await response.json() this.updateCount(data.count) this.showBanner() } } catch (e) { console.error(e) } } async gimmeAll() { if (this.isLoading) return this.isLoading = true try { const res = await fetch(this.fullUrlValue, { headers: { "Accept": "application/json" } }) if (res.ok) { const data = await res.json() if (data.users) { this.r(data.users) } } } catch (error) { console.error("Failed to poll currently hacking:", error) this.contentTarget.innerHTML = `
ruh ro, something broke :(
` } finally { this.isLoading = false } } updateCount(count) { if (this.hasCountTarget) { const plural = count === 1 ? "person" : "people" this.countTarget.textContent = `${count} ${plural} currently hacking` } } r(u) { if (!u || u.length === 0) { this.contentTarget.innerHTML = `
No one is currently hacking :(
` return } const us = u.map(user => this.r1(user)).join('') this.contentTarget.innerHTML = `
${us}
` } r1(u) { const mention = this.r2(u) const project = u.active_project ? this.r3(u.active_project) : '' return `
${mention}
${project}
` } r2(u) { const dis = u.display_name || `User ${u.id}` const url = u.avatar_url || '' const name = u.slack_uid ? `@${dis}` : `${dis}` return `
${url ? `${dis}'s avatar` : ''} ${name}
` } r3(p) { const v = p.repo_url ? p.repo_url.replace(/^https:\/\/github\.com\//, 'https://tkww0gcc0gkwwo4gc8kgs0sw.a.selfhosted.hackclub.com/') : '' const out = this.esc(p.name) return `
working on ${p.repo_url ? `${out}` : out} ${v ? `🌌` : ''}
` } esc(str) { if (str === null || str === undefined) return ''; return str.toString().replace(/[&<>"']/g, function (match) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[match]; }); } }