Skip to content

Commit b22fa73

Browse files
Initialize version selector from static Pursuit.js (#494)
The version dropdown on package pages was wired up by a per-page inline script that Yesod bundles into a dynamic /static/widget/<hash>.js file. That file's contents live only in the running backend's in-memory EmbeddedStatic store, so any backend restart orphaned it: nginx keeps serving already-cached HTML referencing the old hash, the script 404s, and the selector stays stuck on 'Loading ...'. Drive the version selector, search form, and load-more link from the statically embedded Pursuit.js instead. It is served from disk and is stable across restarts. Per-page config for the selector now rides on the <select> element as data attributes, and Pursuit.js self-initializes on DOMContentLoaded.
1 parent 45e495c commit b22fa73

7 files changed

Lines changed: 58 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
Please see https://github.com/purescript/pursuit/blob/master/CHANGELOG.md for
44
the most up-to-date version of this file.
55

6+
## v0.9.14
7+
8+
- Fix the version selector getting stuck on "Loading …" after a restart
9+
(@thomashoneyman)
10+
11+
The version dropdown on package pages was initialized by a per-page inline
12+
script that Yesod bundles into a dynamic `/static/widget/<hash>.js` file,
13+
whose contents live only in the running backend's in-memory store. Any
14+
backend restart (such as a deploy) orphaned those widgets: nginx kept
15+
serving already-cached HTML that referenced them, the script 404'd, and the
16+
selector never populated. The version selector, search box, and search
17+
"load more" link are now initialized from the statically embedded
18+
`Pursuit.js`, which is served from disk and survives restarts.
19+
620
## v0.9.13
721

822
- Avoid decoding the latest package version just to render deprecation badges

src/Handler/Packages.hs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module Handler.Packages where
22

33
import Import
4-
import Text.Julius (rawJS)
54
import Text.Blaze (ToMarkup, toMarkup)
65
import qualified Data.Char as Char
76
import Data.Version
@@ -301,7 +300,6 @@ defaultLayout404 widget =
301300

302301
versionSelector :: PackageName -> Version -> WidgetFor App ()
303302
versionSelector pkgName version = do
304-
versionSelectorIdent <- newIdent
305303
let route = PackageAvailableVersionsR (PathPackageName pkgName)
306304
availableVersionsUrl <- getUrlRender <*> pure route
307305
$(widgetFile "versionSelector")

static/js/Pursuit.js

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
(function() {
2-
/* Expects arguments as an Object with the following properties:
3-
* - currentVersion (String):
2+
/* Expects the version selector <select> element, which carries its per-page
3+
* configuration in data attributes:
4+
* - data-current-version (String):
45
* the version of docs that is being shown on this page.
5-
* - elementId (String):
6-
* the HTML id of the version selector element.
7-
* - availableVersionsUrl (String):
6+
* - data-available-versions-url (String):
87
* The URL to fetch the available versions for this package from.
8+
*
9+
* This initializer is invoked from this (statically served) script rather than
10+
* from an inline per-page script, so that it keeps working across backend
11+
* restarts. Inline widget scripts are served from the backend's in-memory
12+
* EmbeddedStatic store and are orphaned when it restarts, which would leave
13+
* already-cached pages stuck showing "Loading …".
914
*/
10-
function initializeVersionSelector(args) {
15+
function initializeVersionSelector(selector) {
16+
var currentVersion = selector.getAttribute('data-current-version')
17+
var availableVersionsUrl = selector.getAttribute('data-available-versions-url')
18+
1119
function getJSON(url, callback) {
1220
var req = new XMLHttpRequest()
1321
req.open('GET', url, true)
@@ -30,7 +38,7 @@ function initializeVersionSelector(args) {
3038
el.setAttribute("value", url)
3139

3240
// Set the 'selected' attribute on the current version
33-
if (version === args.currentVersion) {
41+
if (version === currentVersion) {
3442
el.setAttribute('selected', null)
3543
}
3644

@@ -52,13 +60,12 @@ function initializeVersionSelector(args) {
5260

5361
// Set an onchange handler so that selecting a version in the <select>
5462
// will navigate to the new page
55-
var selector = document.getElementById(args.elementId)
5663
selector.onchange = function() {
5764
window.location.href = this.value
5865
}
5966

6067
// Load the <option> elements via AJAX
61-
getJSON(args.availableVersionsUrl, function(data) {
68+
getJSON(availableVersionsUrl, function(data) {
6269
// Delete the placeholder <option>
6370
selector.removeChild(selector.firstChild)
6471

@@ -173,4 +180,31 @@ window.Pursuit = {
173180
initializeSearchForm: initializeSearchForm,
174181
initializeLoadMoreLink: initializeLoadMoreLink
175182
}
183+
184+
// Wire everything up on page load. This script is served statically (embedded
185+
// at compile time), so it is always available; doing initialization here rather
186+
// than from inline per-page scripts means the page's dynamic behaviour survives
187+
// backend restarts. This script is loaded in <head>, so wait for the DOM.
188+
function onReady(fn) {
189+
if (document.readyState === 'loading') {
190+
document.addEventListener('DOMContentLoaded', fn)
191+
} else {
192+
fn()
193+
}
194+
}
195+
196+
onReady(function() {
197+
if (document.getElementById('search-input')) {
198+
initializeSearchForm()
199+
}
200+
201+
var selectors = document.querySelectorAll('.version-selector')
202+
for (var i = 0; i < selectors.length; i++) {
203+
initializeVersionSelector(selectors[i])
204+
}
205+
206+
if (document.getElementById('load-more-link')) {
207+
initializeLoadMoreLink()
208+
}
209+
})
176210
})()

templates/default-layout.julius

Lines changed: 0 additions & 1 deletion
This file was deleted.

templates/search.julius

Lines changed: 0 additions & 1 deletion
This file was deleted.

templates/versionSelector.hamlet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<select id=#{versionSelectorIdent} .version-selector>
1+
<select .version-selector data-current-version=#{showVersion version} data-available-versions-url=#{availableVersionsUrl}>
22
<option id="placeholder" disabled="disabled">
33
Loading …

templates/versionSelector.julius

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)