/
relative_time.cljs
82 lines (75 loc) · 2.13 KB
/
relative_time.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
(ns portal.ui.viewer.relative-time
(:refer-clojure :exclude [second])
(:require ["react" :as react]
[clojure.spec.alpha :as s]
[portal.ui.styled :as d]
[portal.ui.viewer.date-time :as date-time]))
;;; :spec
(s/def ::relative-time
(s/or :inst inst?
:unix number?
:iso-8601 string?))
;;;
(def ^:private millisecond 1)
(def ^:private second (* 1000 millisecond))
(def ^:private minute (* 60 second))
(def ^:private hour (* 60 minute))
(def ^:private day (* 24 hour))
(def ^:private week (* 7 day))
(def ^:private month (* 30 day))
(def ^:private year (* 365 day))
(def ^:private time-scales
[:year year
:month month
:week week
:day day
:hour hour
:minute minute
:second second
:millisecond millisecond])
(defn- relative-time
([b]
(relative-time (js/Date.) b))
([a b]
(let [diff (- (.getTime b) (.getTime a))
ms (Math/abs diff)]
(some
(fn [[unit scale]]
(when (> ms scale)
{:scale (Math/floor (/ ms scale))
:unit unit
:direction
(cond
(neg? diff) :past
(pos? diff) :future
:else :now)}))
(partition 2 time-scales)))))
(defn- format-relative-time [{:keys [scale unit direction]}]
(if (= direction :now)
"now"
(str scale
" "
(name unit)
(when (> scale 1) "s")
" "
(case direction
:past "ago"
:future "in the future"))))
(defn inspect-relative [value]
(let [value (date-time/parse value)
[now set-now!] (react/useState (js/Date.))]
(react/useEffect
(fn []
(let [i (js/setInterval
(fn []
(set-now! (js/Date.)))
1000)]
(fn []
(js/clearInterval i))))
#js [])
[d/div (format-relative-time (relative-time now value))]))
(def viewer
{:predicate date-time/parse
:component inspect-relative
:name :portal.viewer/relative-time
:doc "View value relative to the current time."})