PageSourceSearch

https://hackorum.dev/assets/controllers/batch_loader_controller-d1b9c692.js

js hackorum.dev collected 2026-09-28 07:00:18 UTC 3,035 bytes, 102 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static values = {
5    url: String,
6    batchSize: { type: Number, default: 50 },
7    signedIn: { type: Boolean, default: false }
8  }
9
10  static targets = ["skeleton"]
11
12  connect() {
13    this.loadAllBatches()
14  }
15
16  async loadAllBatches() {
17    const skeletons = this.skeletonTargets
18    if (skeletons.length === 0) return
19
20    const allIds = skeletons.map(el => {
21      return el.closest(".message-card").dataset.messageId
22    })
23
24    for (let i = 0; i < allIds.length; i += this.batchSizeValue) {
25      const batchIds = allIds.slice(i, i + this.batchSizeValue)
26      await this.fetchAndInjectBatch(batchIds)
27    }
28  }
29
30  async fetchAndInjectBatch(ids) {
31    const url = `${this.urlValue}?ids=${ids.join(",")}`
32    try {
33      const response = await fetch(url)
34      if (!response.ok) return
35      const html = await response.text()
36      this.injectBatch(html)
37    } catch (e) {
38      console.warn("batch load failed", e)
39    }
40  }
41
42  findScrollAnchor() {
43    if (window.location.hash) {
44      const el = document.getElementById(window.location.hash.slice(1))
45      if (el) return el
46    }
47    for (const card of this.element.querySelectorAll('.message-card')) {
48      const rect = card.getBoundingClientRect()
49      if (rect.bottom > 0 && rect.top < window.innerHeight) return card
50    }
51    return null
52  }
53
54  injectBatch(html) {
55    const template = document.createElement("template")
56    template.innerHTML = html
57
58    const anchor = this.findScrollAnchor()
59    const anchorTop = anchor ? anchor.getBoundingClientRect().top : null
60
61    template.content.querySelectorAll("[data-message-id]").forEach(batchItem => {
62      const messageId = batchItem.dataset.messageId
63      const card = this.element.querySelector(
64        `.message-card[data-message-id="${messageId}"]`
65      )
66      if (!card) return
67
68      const skeleton = card.querySelector(".message-batch-skeleton")
69      if (!skeleton) return // already rendered inline, skip
70
71      const isRead = card.dataset.read === "true"
72
73      // The batchItem contains a div[data-message-id] wrapping .message-content
74      // Extract the .message-content element
75      const content = batchItem.firstElementChild
76      if (!content) return
77
78      if (isRead) {
79        content.classList.add("is-read")
80      }
81
82      skeleton.replaceWith(content)
83
84      if (this.signedInValue) {
85        const messageContent = card.querySelector(".message-content")
86        if (messageContent) {
87          messageContent.dataset.controller = "read-status"
88          messageContent.dataset.readStatusMessageIdValue = messageId
89          messageContent.dataset.readStatusReadUrlValue = `/messages/${messageId}/read.json`
90          messageContent.dataset.readStatusDelaySecondsValue = "5"
91        }
92      }
93    })
94
95    if (anchor && anchorTop !== null) {
96      const drift = anchor.getBoundingClientRect().top - anchorTop
97      if (Math.abs(drift) > 1) {
98        window.scrollBy(0, drift)
99      }
100    }
101  }
102}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.