diff --git a/src/application-globals.rkt b/src/application-globals.rkt index 661f1a1..b139192 100644 --- a/src/application-globals.rkt +++ b/src/application-globals.rkt @@ -136,6 +136,7 @@ ,(if (config-true? 'feature_search_suggestions) `(script (@ (type "module") (src ,(get-static-url "search-suggestions.js")))) "") + (script (@ (type "module") (src ,(get-static-url "countdown.js")))) (link (@ (rel "icon") (href ,(u (λ (v) (config-true? 'strict_proxy)) (λ (v) (u-proxy-url v)) (head-data^-icon-url head-data)))))) diff --git a/static/countdown.js b/static/countdown.js new file mode 100644 index 0000000..9ae6b46 --- /dev/null +++ b/static/countdown.js @@ -0,0 +1,53 @@ +// countdown timer for gacha enthusiasts +// sample: bandori/wiki/BanG_Dream!_Wikia +// sample: ensemble-stars/wiki/The_English_Ensemble_Stars_Wiki + +import {h, htm, render, useState, useEffect, createContext, useContext, signal, computed, effect} from "./preact.js" +const html = htm.bind(h) + +const now = signal(Date.now()) +setInterval(() => now.value = Date.now(), 1000) + +const units = [ + ["w", 7*24*60*60*1000], + ["d", 24*60*60*1000], + ["h", 60*60*1000], + ["m", 60*1000], + ["s", 1000] +] + +function getDisplayTime(datetime, now, or) { + let difference = datetime - now + let foundSignificantField = false + if (difference > 0) { + return units.map(([letter, duration], index) => { + const multiplier = Math.floor(difference / duration) + difference -= multiplier * duration + if (multiplier > 0 || foundSignificantField) { + foundSignificantField = true + return multiplier + letter + } + }).filter(s => s).join(" ") + } else if (or) { + return or + } else { + return `[timer ended on ${new Date(datetime).toLocaleString()}]` + } +} + +function Countdown(props) { + return html`${props.display}` +} + +document.querySelectorAll(".countdown").forEach(eCountdown => { + // grab information and make variables + const eDate = eCountdown.querySelector(".countdowndate") + const eOr = eCountdown.nextElementSibling + const or = eOr?.textContent + const datetime = new Date(eDate.textContent).getTime() + // the mapped signal + const display = computed(() => getDisplayTime(datetime, now.value, or)) + // clear content and render + while (eDate.childNodes[0] !== undefined) eDate.childNodes[0].remove() + render(html`<${Countdown} display=${display} />`, eDate); +})