diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb new file mode 100644 index 000000000..c3fd4f066 --- /dev/null +++ b/app/helpers/time_helper.rb @@ -0,0 +1,5 @@ +module TimeHelper + def local_datetime_tag(datetime, style: :time, **attributes) + tag.time **attributes, datetime: datetime.iso8601, data: { local_time_target: style } + end +end diff --git a/app/javascript/controllers/local_time_controller.js b/app/javascript/controllers/local_time_controller.js new file mode 100644 index 000000000..61a750dbf --- /dev/null +++ b/app/javascript/controllers/local_time_controller.js @@ -0,0 +1,62 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["time", "date", "datetime", "ago"] + + initialize() { + this.timeFormatter = new Intl.DateTimeFormat(undefined, { timeStyle: "short" }) + this.dateFormatter = new Intl.DateTimeFormat(undefined, { dateStyle: "long" }) + this.dateTimeFormatter = new Intl.DateTimeFormat(undefined, { timeStyle: "short", dateStyle: "short" }) + this.agoFormatter = new AgoFormatter() + } + + timeTargetConnected(target) { + this.#formatTime(this.timeFormatter, target) + } + + dateTargetConnected(target) { + this.#formatTime(this.dateFormatter, target) + } + + datetimeTargetConnected(target) { + this.#formatTime(this.dateTimeFormatter, target) + } + + agoTargetConnected(target) { + this.#formatTime(this.agoFormatter, target) + } + + #formatTime(formatter, target) { + const dt = new Date(target.getAttribute("datetime")) + target.textContent = formatter.format(dt) + target.title = this.dateTimeFormatter.format(dt) + } +} + +class AgoFormatter { + format(dt) { + const now = new Date() + const seconds = (now - dt) / 1000 + const minutes = seconds / 60 + const hours = minutes / 60 + const days = hours / 24 + const weeks = days / 7 + const months = days / (365 / 12) + const years = days / 365 + + if (years >= 1) return this.#pluralize("year", years) + if (months >= 1) return this.#pluralize("month", months) + if (weeks >= 1) return this.#pluralize("week", weeks) + if (days >= 1) return this.#pluralize("day", days) + if (hours >= 1) return this.#pluralize("hour", hours) + if (minutes >= 1) return this.#pluralize("minute", minutes) + + return "Less than a minute ago" + } + + #pluralize(word, quantity) { + quantity = Math.round(quantity) + const suffix = (quantity === 1) ? "" : "s" + return `${quantity} ${word}${suffix} ago` + } +} diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 2121cf22a..251af449a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -19,7 +19,7 @@ <%= yield :head %> - +