Skip to content

Commit

Permalink
Add support for live polling the dashboard
Browse files Browse the repository at this point in the history
Adds support for live polling the dashboard, adding replaceable content areas anywhere on the page, register event listeners which act upon the `GoodJob` javacript object, and a footer.

### Polling UX
1. Toggle the `Live Poll` checkbox in the navbar (every 5000ms by default)
2. Add a query parameter to the url `/good_job?poll=5000`. The integer (in ms) sets the poll interval.

### Register event listeners
1. Add `data-gj-action` attribute with the event name and the `GoodJob` function to call when the event is triggered. `change#togglePoll` => `element.addEventListener('change, GoodJob.togglePoll)`

### Adding replaceable content areas
1. Add the `data-gj-poll-replace` attribute to the element
2. Add a unique ID to the same element

Fixes bensheldon#526
  • Loading branch information
danielwestendorf committed Feb 21, 2022
1 parent b921729 commit cfc1674
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
77 changes: 76 additions & 1 deletion engine/app/assets/scripts.js
Original file line number Diff line number Diff line change
@@ -1 +1,76 @@
GoodJob = {};
GoodJob = {
// Register functions to execute when the DOM is ready
ready: (callback) => {
if (document.readyState != "loading"){
callback()
} else {
document.addEventListener("DOMContentLoaded", callback)
}
},

init: () => {
GoodJob.updateSettings()
GoodJob.addListeners()
GoodJob.pollUpdates()
},

addListeners() {
const gjActionEls = document.querySelectorAll('[data-gj-action]')

for (let i = 0; i < gjActionEls.length; i++) {
const el = gjActionEls[i]
const [eventName, func] = el.dataset.gjAction.split('#')

el.addEventListener(eventName, GoodJob[func])
}
},

updateSettings() {
const queryString = window.location.search
const urlParams = new URLSearchParams(queryString)

// livepoll interval and enablement
if (urlParams.has('poll')) {
GoodJob.pollEnabled = true
GoodJob.pollInterval = parseInt(urlParams.get('poll'))
} else {
GoodJob.pollEnabled = false
GoodJob.pollInterval = 5000 // default 5sec
}
},

togglePoll: (ev) => {
GoodJob.pollEnabled = ev.currentTarget.checked
},

pollUpdates: () => {
setTimeout(() => {
if (GoodJob.pollEnabled == true) {
fetch(window.location.href)
.then(resp => resp.text())
.then(GoodJob.updateContent)
.finally(GoodJob.pollUpdates)
} else {
GoodJob.pollUpdates()
}
}, GoodJob.pollInterval)
},

updateContent: (newContent) => {
const domParser = new DOMParser()
const parsedDOM = domParser.parseFromString(newContent, "text/html")

const newElements = parsedDOM.querySelectorAll('[data-gj-poll-replace]')

for (let i = 0; i < newElements.length; i++) {
const newEl = newElements[i]
const oldEl = document.getElementById(newEl.id)

if (oldEl) {
oldEl.parentNode.replaceChild(newEl, oldEl)
}
}
}
};

GoodJob.ready(GoodJob.init)
26 changes: 24 additions & 2 deletions engine/app/views/layouts/good_job/base.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
</div>
</li>
</ul>
<div class="text-muted" title="Now is <%= Time.current %>">Times are displayed in <%= Time.current.zone %> timezone</div>
<div>
<input type="checkbox" id="toggle-poll" name="toggle-poll" data-gj-action='change#togglePoll' <%= 'checked' if params[:poll].present? %>>
<label for="toggle-poll">Live Poll</label>
</div>
</div>
</div>
</nav>
Expand All @@ -70,7 +73,26 @@
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% end %>
<%= yield %>

<div id="content" data-gj-poll-replace>
<%= yield %>
</div>
</div>

<footer class="footer mt-auto py-3 bg-light fixed-bottom" id="footer" data-gj-poll-replace>
<div class="container-fluid">
<div class="row">
<div class="col-6">
<span class="text-muted" title="Now is <%= Time.current %>">
Last updated: <%= Time.current %>
</span>
</div>

<div class="col-6 text-end">
Remember, you're doing a Good Job too!
</div>
</div>
</div>
</footer>
</body>
</html>

0 comments on commit cfc1674

Please sign in to comment.