|
35 | 35 | return null; |
36 | 36 | } |
37 | 37 |
|
| 38 | + function isPaginationTrigger(elt) { |
| 39 | + if (!elt) return false; |
| 40 | + if (elt.closest && elt.closest('[data-episode-load-more]')) return true; |
| 41 | + return !!(elt.classList && elt.classList.contains('episodes-load-more-trigger')); |
| 42 | + } |
| 43 | + |
38 | 44 | function bindBatchTracking(container) { |
39 | 45 | if (container.dataset.scrollRestoreBound === 'true') { |
40 | 46 | return; |
|
46 | 52 | // outerHTML swap (TV/comic "Show more") is detached from the DOM by |
47 | 53 | // the time afterSwap fires, so a containment check there always |
48 | 54 | // misses. The requesting element is still attached at beforeRequest. |
| 55 | + // Only count the pagination trigger itself - the track/lists/history |
| 56 | + // buttons on each episode row also issue HTMX requests from within |
| 57 | + // this container and must not be counted as a loaded batch. |
49 | 58 | document.body.addEventListener('htmx:beforeRequest', function(event) { |
50 | 59 | var elt = event.detail.elt; |
51 | 60 | if (!elt) return; |
52 | | - if (container.contains(elt) || elt === container) { |
| 61 | + if ((container.contains(elt) || elt === container) && isPaginationTrigger(elt)) { |
53 | 62 | var current = Number.parseInt(container.dataset.loadedBatches || '0', 10); |
54 | 63 | container.dataset.loadedBatches = String((Number.isNaN(current) ? 0 : current) + 1); |
55 | 64 | } |
|
60 | 69 | if (!link || !container.contains(link)) { |
61 | 70 | return; |
62 | 71 | } |
| 72 | + // The "Show more" link is intercepted by HTMX (no real navigation), |
| 73 | + // so it must not overwrite the saved state for an actual departure. |
| 74 | + if (link.closest('[data-episode-load-more]')) { |
| 75 | + return; |
| 76 | + } |
63 | 77 | var batches = Number.parseInt(container.dataset.loadedBatches || '0', 10); |
64 | 78 | var state = { |
65 | 79 | scrollY: window.scrollY, |
|
100 | 114 | }); |
101 | 115 | } |
102 | 116 |
|
103 | | - function replayPodcastPages(container, batches) { |
104 | | - if (batches <= 0) { |
| 117 | + function replayPodcastPages(container, remaining) { |
| 118 | + if (remaining <= 0) { |
105 | 119 | return Promise.resolve(); |
106 | 120 | } |
107 | 121 | var loadMore = getLoadMoreLink(container); |
108 | 122 | if (!loadMore) { |
109 | 123 | return Promise.resolve(); |
110 | 124 | } |
111 | | - var url = new URL(loadMore.url, window.location.origin); |
112 | | - var startPage = Number.parseInt(url.searchParams.get('page') || '2', 10); |
113 | | - if (Number.isNaN(startPage)) { |
114 | | - startPage = 2; |
115 | | - } |
116 | | - |
117 | | - var chain = Promise.resolve(); |
118 | | - for (var i = 0; i < batches; i++) { |
119 | | - (function(page) { |
120 | | - chain = chain.then(function() { |
121 | | - var pageUrl = new URL(loadMore.url, window.location.origin); |
122 | | - pageUrl.searchParams.set('page', page); |
123 | | - return fetch(pageUrl.toString()) |
124 | | - .then(function(response) { return response.ok ? response.text() : ''; }) |
125 | | - .then(function(html) { |
126 | | - if (html) { |
127 | | - var trigger = container.querySelector('.episodes-load-more-trigger'); |
128 | | - if (trigger) { |
129 | | - trigger.insertAdjacentHTML('beforebegin', html); |
130 | | - } else { |
131 | | - container.insertAdjacentHTML('beforeend', html); |
132 | | - } |
133 | | - } |
134 | | - }) |
135 | | - .catch(function() {}); |
136 | | - }); |
137 | | - })(startPage + i); |
138 | | - } |
139 | | - return chain; |
| 125 | + return fetch(loadMore.url) |
| 126 | + .then(function(response) { return response.ok ? response.text() : ''; }) |
| 127 | + .then(function(html) { |
| 128 | + if (!html) return; |
| 129 | + // Replace the old trigger (rather than inserting alongside it) so |
| 130 | + // its still-active "revealed" listener can't re-fire and |
| 131 | + // re-request the same page. htmx.process activates the hx-* |
| 132 | + // attributes on the freshly inserted markup (its own next-page |
| 133 | + // trigger and any per-episode buttons), since raw innerHTML |
| 134 | + // insertion bypasses HTMX's usual auto-processing on swap. |
| 135 | + var temp = document.createElement('div'); |
| 136 | + temp.innerHTML = html; |
| 137 | + var fragment = document.createDocumentFragment(); |
| 138 | + while (temp.firstChild) { |
| 139 | + fragment.appendChild(temp.firstChild); |
| 140 | + } |
| 141 | + var oldTrigger = container.querySelector('.episodes-load-more-trigger'); |
| 142 | + if (oldTrigger) { |
| 143 | + container.replaceChild(fragment, oldTrigger); |
| 144 | + } else { |
| 145 | + container.appendChild(fragment); |
| 146 | + } |
| 147 | + if (window.htmx) { |
| 148 | + htmx.process(container); |
| 149 | + } |
| 150 | + }) |
| 151 | + .catch(function() {}) |
| 152 | + .then(function() { |
| 153 | + return replayPodcastPages(container, remaining - 1); |
| 154 | + }); |
140 | 155 | } |
141 | 156 |
|
142 | 157 | function restoreScroll() { |
|
0 commit comments