import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { url: String }
connect() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (!e.isIntersecting) return
this.markRead()
this.observer.disconnect()
})
})
this.observer.observe(this.element)
}
disconnect() {
this.observer?.disconnect()
}
async markRead() {
if (!this.urlValue) return
await fetch(this.urlValue, { method: "POST", headers: { "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content } })
}
}
<div data-controller="read-tracker" data-read-tracker-url-value="<%= mark_read_notification_path(notification) %>">
<%= notification.title %>
</div>
For notifications/messages, it’s nice to mark items read when they actually enter the viewport. IntersectionObserver + Stimulus keeps it lightweight, and Turbo streams can update badges in real time.