Add relative-date-time component
This commit is contained in:
parent
52ec3f819c
commit
516a903565
@ -36,7 +36,9 @@ class Component
|
||||
<section id=journalItems class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-3" hx-target=this
|
||||
hx-swap=outerHTML aria-label="Prayer Requests"><?php
|
||||
$spacer = '<span> </span>';
|
||||
foreach ($reqs->items as /** @var Request $req */ $req) { ?>
|
||||
foreach ($reqs->items as /** @var Request $req */ $req) {
|
||||
$lastPrayed = $req->lastPrayed;
|
||||
$lastActivity = $lastPrayed->getOrDefault($req->history[0]->asOf); ?>
|
||||
<div class=col>
|
||||
<div class="card h-100">
|
||||
<div class="card-header p-0 d-flex" role=toolbar>
|
||||
@ -58,10 +60,8 @@ class Component
|
||||
<p class=request-text><?=htmlentities($req->currentText);?>
|
||||
</div>
|
||||
<div class="card-footer text-end text-muted px-1 py-0">
|
||||
<em><?php
|
||||
$lastPrayed = $req->lastPrayed;
|
||||
echo 'last ' . $lastPrayed->map(fn() => 'prayed')->getOrDefault('activity') . ' '
|
||||
. self::relativeDate($lastPrayed->getOrDefault($req->history[0]->asOf)); ?>
|
||||
<em>last <?=$lastPrayed->map(fn() => 'prayed')->getOrDefault('activity')?>
|
||||
<?=self::relativeDate($lastPrayed->getOrDefault($req->history[0]->asOf))?>
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
@ -115,9 +115,9 @@ class Component
|
||||
public static function relativeDate(string $date): string
|
||||
{
|
||||
$parsed = new DateTimeImmutable($date);
|
||||
$inZone = $parsed->setTimezone(new DateTimeZone($_REQUEST['time_zone']));
|
||||
return sprintf('<span title="%s">%s</span>', date_format($inZone, 'l, F j, Y \a\t g:ia T'),
|
||||
RelativeDate::between('now', $parsed));
|
||||
$iso = $parsed->format('c');
|
||||
$title = $parsed->setTimezone(new DateTimeZone($_REQUEST['time_zone']))->format('l, F j, Y \a\t g:ia T');
|
||||
return "<relative-date-time title=\"$title\" interval=5000>$iso</relative-date-time>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use strict"
|
||||
|
||||
/** myPrayerJournal script */
|
||||
this.mpj = {
|
||||
window.mpj = {
|
||||
/**
|
||||
* Show a message via toast
|
||||
* @param {string} message The message to show
|
||||
@ -102,3 +102,112 @@ htmx.on("htmx:configRequest", function (evt) {
|
||||
})
|
||||
|
||||
mpj.deriveTimeZone()
|
||||
|
||||
class RelativeDateTime extends HTMLElement {
|
||||
static observedAttributes = ["interval"]
|
||||
|
||||
static #LessThanXMinutes = Symbol()
|
||||
static #XMinutes = Symbol()
|
||||
static #AboutXHours = Symbol()
|
||||
static #XDays = Symbol()
|
||||
static #AboutXMonths = Symbol()
|
||||
static #XMonths = Symbol()
|
||||
static #AboutXYears = Symbol()
|
||||
static #OverXYears = Symbol()
|
||||
static #AlmostXYears = Symbol()
|
||||
|
||||
static #messages = new Map([
|
||||
[RelativeDateTime.#LessThanXMinutes, ['less than a minute', 'less than %d minutes']],
|
||||
[RelativeDateTime.#XMinutes, ['a minute', '%d minutes']],
|
||||
[RelativeDateTime.#AboutXHours, ['about an hour', 'about %d hours']],
|
||||
[RelativeDateTime.#XDays, ['a day', '%d days']],
|
||||
[RelativeDateTime.#AboutXMonths, ['about a month', 'about %d months']],
|
||||
[RelativeDateTime.#XMonths, ['a month', '%d months']],
|
||||
[RelativeDateTime.#AboutXYears, ['about a year', 'about %d years']],
|
||||
[RelativeDateTime.#OverXYears, ['over a year', 'over %d years']],
|
||||
[RelativeDateTime.#AlmostXYears, ['almost a year', 'almost %d years']],
|
||||
])
|
||||
|
||||
static #aDay = 1440.0
|
||||
static #almost2Days = 2520.0
|
||||
static #aMonth = 43200.0
|
||||
static #twoMonths = 86400.0
|
||||
|
||||
/** @type Date The date/time for this relative date */
|
||||
#jsDate
|
||||
/** @type ?number The interval timer for this element */
|
||||
#timeOut = null
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
#update() {
|
||||
|
||||
const now = new Date()
|
||||
const minutes = Math.abs((this.#jsDate.getTime() - now.getTime()) / 60 / 1000);
|
||||
const months = Math.round(minutes / RelativeDateTime.#aMonth);
|
||||
const years = Math.floor(months / 12);
|
||||
|
||||
/** @type symbol */
|
||||
let typ
|
||||
/** @type number */
|
||||
let nbr
|
||||
|
||||
if (minutes < 1.0) {
|
||||
typ = RelativeDateTime.#LessThanXMinutes
|
||||
nbr = 1
|
||||
} else if (minutes < 45.0) {
|
||||
typ = RelativeDateTime.#XMinutes
|
||||
nbr = Math.round(minutes)
|
||||
} else if (minutes < 90.0) {
|
||||
typ = RelativeDateTime.#AboutXHours
|
||||
nbr = 1
|
||||
} else if (minutes < RelativeDateTime.#aDay) {
|
||||
typ = RelativeDateTime.#AboutXHours
|
||||
nbr = Math.round(minutes / 60)
|
||||
} else if (minutes < RelativeDateTime.#almost2Days) {
|
||||
typ = RelativeDateTime.#XDays
|
||||
nbr = 1
|
||||
} else if (minutes < RelativeDateTime.#aMonth) {
|
||||
typ = RelativeDateTime.#XDays
|
||||
nbr = Math.round(minutes / RelativeDateTime.#aDay)
|
||||
} else if (minutes < RelativeDateTime.#twoMonths) {
|
||||
typ = RelativeDateTime.#AboutXMonths
|
||||
nbr = Math.round(minutes / RelativeDateTime.#aMonth)
|
||||
} else if (months < 12) {
|
||||
typ = RelativeDateTime.#XMonths
|
||||
nbr = Math.round(minutes / RelativeDateTime.#aMonth)
|
||||
} else if (months % 12 < 3) {
|
||||
typ = RelativeDateTime.#AboutXYears
|
||||
nbr = years
|
||||
} else if (months % 12 < 9) {
|
||||
typ = RelativeDateTime.#OverXYears
|
||||
nbr = years
|
||||
} else {
|
||||
typ = RelativeDateTime.#AlmostXYears
|
||||
nbr = years + 1
|
||||
}
|
||||
|
||||
const tmpl = RelativeDateTime.#messages.get(typ)
|
||||
const message = nbr === 1 ? tmpl[0] : tmpl[1].replace("%d", nbr.toString())
|
||||
this.innerText = this.#jsDate < now ? `${message} ago` : `in ${message}`
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.#jsDate = new Date(this.innerText)
|
||||
this.#update()
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.#timeOut) clearInterval(this.#timeOut)
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (this.#timeOut) clearInterval(this.#timeOut)
|
||||
if (this.#jsDate) this.#update()
|
||||
this.#timeOut = setInterval(() => this.#update, parseInt(newValue))
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("relative-date-time", RelativeDateTime)
|
||||
|
Loading…
Reference in New Issue
Block a user