Lint & fix some shadowed core Clojure(Script) vars (#16500)

It's well known that shadowing core Clojure vars can lead to unexpected bugs. In
fact, it's a common source of bugs in other languages too. In the status-mobile
repository there are, in total, 562 shadowed vars, ~500 are core vars. Excluding
the "old code" we still have 285 offenders.

In status-mobile I've already seen two bugs caused by shadowed vars, both with
the shadowed var "name". But probably other problems happened in the past, and
others will happen in the future if we don't do something about this. This PR is
also my response to my frustration trying to review PRs and checking for
shadowed vars, humans were not meant for that!

In this commit we are enabling ":shadowed-var" to lint certain (not all) core
vars as errors (not warnings). In future PRs we can gradually unshadow more
vars. For the record, name is shadowed 40 times in the new code and 130 in
total, and type is shadowed 93 times in the new code and 124 in total!

What about non-core vars, should we allow shadowing? There are ~70 non-core
shadowed vars. In my opinion, we should also lint and disallow shadowing
non-core vars, since it may cause the same kind of bugs of shadowing core vars.
But this decision can be left for another moment/issue, after we have fixed the
most prominent problem of shadowing core vars.

Which vars are unshadowed in this PR? I fixed 62 errors and unshadowed
cljs.core/iter, cljs.core/time, cljs.core/count, cljs.core/key,
clojure.core/key.

Resources:

- [clj-kondo linter: shadowed-var](https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md#shadowed-var)
This commit is contained in:
Icaro Motta 2023-07-06 10:28:07 +00:00 committed by GitHub
parent 4d7cf3e94e
commit 9ed68ee7d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 173 additions and 202 deletions

View File

@ -14,6 +14,13 @@
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:shadowed-var {:level :error
;; We temporarily use :include to define an
;; allowlist of core Clojure vars. In the
;; future, as we progressively fix shadowed
;; vars, we should be able to delete this
;; option and lint all vars.
:include [count iter key time]}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867

View File

@ -287,11 +287,11 @@
and then compressed. Example input/output :
input key = zQ3shTAten2v9CwyQD1Kc7VXAqNPDcHZAMsfbLHCZEx6nFqk9 and
output key = 0x025596a7ff87da36860a84b0908191ce60a504afc94aac93c1abd774f182967ce6"
[key callback]
[input-key callback]
(log/info "[native-module] Deserializing and then compressing public key"
{:fn :deserialize-and-compress-key
:key key})
(.deserializeAndCompressKey ^js (status) key callback))
:key input-key})
(.deserializeAndCompressKey ^js (status) input-key callback))
(defn compressed-key->public-key
"Provides compressed key to status-go and gets back the uncompressed public key via deserialization"

View File

@ -235,31 +235,6 @@
[value velocity snap-points]
(.snapPoint ^js redash value velocity (to-array snap-points)))
(defn cancelable-loop
[{:keys [clock duration finished on-reach]}]
(let [time (value 0)
frame-time (value 0)
position (value 0)
to-value (value 1)
state {:time time
:frameTime frame-time
:finished finished
:position position}
config {:toValue to-value
:duration duration
:easing (:linear easings)}]
(block
[(timing clock state config)
(cond* (and* finished
(eq position to-value))
(call* [] on-reach))
(cond* finished
[(set finished 0)
(set time 0)
(set frame-time 0)
(set position 0)])
position])))
(defn with-easing
[{val :value
:keys [snap-points velocity offset state easing duration

View File

@ -1,12 +1,5 @@
(ns quo.previews.preview)
(defn descriptor->values
[{:keys [key options type]}]
{key (case type
:boolean [false true]
:text [nil "Just simple text"] ; NOTE(Ferossgp): add example with long text?
:select (mapv :key options))})
(defmacro list-comp
[[binding seq-expr & bindings] body-expr]
(cond (not binding)

View File

@ -68,8 +68,8 @@
:background-color (:ui-background @colors/theme)})
(defn customizer-boolean
[{:keys [label key state]}]
(let [state* (reagent/cursor state [key])]
[{:keys [label state] :as args}]
(let [state* (reagent/cursor state [(:key args)])]
[rn/view {:style container}
[label-view state label]
[rn/view
@ -95,8 +95,8 @@
"False"]]]]))
(defn customizer-text
[{:keys [label key state]}]
(let [state* (reagent/cursor state [key])]
[{:keys [label state] :as args}]
(let [state* (reagent/cursor state [(:key args)])]
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
@ -117,8 +117,8 @@
(defn customizer-select
[]
(let [open (reagent/atom nil)]
(fn [{:keys [label key state options]}]
(let [state* (reagent/cursor state [key])
(fn [{:keys [label state options] :as args}]
(let [state* (reagent/cursor state [(:key args)])
selected (value-for-key @state* options)]
[rn/view {:style container}
[label-view state label]
@ -133,15 +133,15 @@
[rn/view {:style (modal-view)}
[rn/scroll-view
(doall
(for [{:keys [key value]} options]
^{:key key}
(for [{k :key v :value} options]
^{:key k}
[rn/touchable-opacity
{:style (select-option-style (= @state* key))
{:style (select-option-style (= @state* k))
:on-press #(do
(reset! open false)
(reset! state* key))}
[quo/text {:color (if (= @state* key) :link :secondary)}
value]]))
(reset! state* k))}
[quo/text {:color (if (= @state* k) :link :secondary)}
v]]))
[rn/view {:flex-direction :row}
[rn/touchable-opacity
{:style (select-option-style false)
@ -175,17 +175,15 @@
{:style {:flex 1}
:padding-horizontal 16}
(doall
(for [{:keys [key type]
:as desc}
descriptors
:let [descriptor (merge desc
{:state state})]]
^{:key key}
(for [desc descriptors
:let [descriptor (merge desc {:state state})]]
^{:key (:key desc)}
[:<>
(case type
(case (:type desc)
:boolean [customizer-boolean descriptor]
:text [customizer-text descriptor]
:select [customizer-select descriptor])]))])
:select [customizer-select descriptor]
nil)]))])
(comment
[{:label "Show error:"

View File

@ -62,7 +62,7 @@
:customization-color customize jump-to and mention button color}"
[_]
(let [pressed? (reagent/atom false)]
(fn [{:keys [type label on-press count customization-color style]}]
(fn [{:keys [type label on-press customization-color style] :as args}]
[rn/touchable-opacity
{:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
@ -102,6 +102,6 @@
(case type
:jump-to label
:search-with-label label
(:mention :notification-down :notification-up) (str count))])
(:mention :notification-down :notification-up) (str (:count args)))])
(when (#{:jump-to :notification-down :notification-up} type)
[icon-view type])]])))

View File

@ -17,8 +17,8 @@
:outline colors/neutral-70}})
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
[k]
(get-in themes [(theme/get-theme) k]))
(defn counter
[{:keys [type override-text-color override-bg-color style accessibility-label max-value]

View File

@ -14,8 +14,8 @@
:error colors/danger-60}})
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
[k]
(get-in themes [(theme/get-theme) k]))
(defn info-message
"[info-message opts \"message\"]

View File

@ -30,12 +30,12 @@
:close-button colors/white}})
(defn get-color
[theme key]
(get-in themes [theme key]))
[theme k]
(get-in themes [theme k]))
(defn get-color-by-type
[theme type key]
(get-in themes [theme type key]))
[theme type k]
(get-in themes [theme type k]))
(defn container
[{:keys [theme type include-button?]}]

View File

@ -17,8 +17,8 @@
:background colors/neutral-95}})
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
[k]
(get-in themes [(theme/get-theme) k]))
(def ui-images
{:light {:horizontal (js/require "../resources/images/ui/message-gap-hborder-light.png")
@ -28,8 +28,8 @@
:circles (js/require "../resources/images/ui/message-gap-circle-bg-dark.png")}})
(defn get-image
[key]
(get-in ui-images [(theme/get-theme) key]))
[k]
(get-in ui-images [(theme/get-theme) k]))
;;; components
;;;; borders

View File

@ -5,12 +5,12 @@
(defn dynamic-button-view
[type dynamic-buttons style]
(when-let [{:keys [count on-press customization-color label]} (get dynamic-buttons type)]
(when-let [{:keys [on-press customization-color label] :as props} (get dynamic-buttons type)]
[dynamic-button/dynamic-button
{:type type
:label label
:on-press on-press
:count count
:count (:count props)
:style style
:customization-color customization-color}]))

View File

@ -150,8 +150,8 @@
[activity-reply-text-input props reply-input])
(when items
[rn/view style/footer-container
(for [{:keys [key] :as item} items]
^{:key key}
(for [item items]
^{:key (:key item)}
[footer-item-view item replying? reply-input])])])))
(defn view

View File

@ -30,10 +30,10 @@
:path-length path-length}))
(defn- linear-ease
[time start goal duration]
[input-time start goal duration]
(if (zero? duration)
start
(-> time
(-> input-time
(/ duration)
(* goal)
(+ start))))

View File

@ -4,10 +4,10 @@
[react-native.core :as rn]))
(defn info-count
([count]
(info-count {} count))
([props count]
(when (> count 0)
([amount]
(info-count {} amount))
([props amount]
(when (> amount 0)
[rn/view
(merge props
{:style (merge {:width 16
@ -23,4 +23,4 @@
{:style (merge typography/font-medium
typography/label
{:color colors/white :text-align :center})}
count]])))
amount]])))

View File

@ -41,8 +41,8 @@
:margin-right 8})
(defn get-color-by-type
[type key]
(get-in themes [(theme/get-theme) type key]))
[type k]
(get-in themes [(theme/get-theme) type k]))
(defn account-selector
"[account-selector opts]
@ -73,4 +73,3 @@
:size :paragraph-1
:style {:color account-text-color}}
account-text]]]))

View File

@ -33,7 +33,7 @@
(defn on-finalize [gesture handler] (.onFinalize ^js gesture handler))
(defn max-pointers [gesture count] (.maxPointers ^js gesture count))
(defn max-pointers [gesture amount] (.maxPointers ^js gesture amount))
(defn min-distance [gesture dist] (.minDistance ^js gesture dist))
@ -41,7 +41,7 @@
(defn hit-slop [gesture settings] (.hitSlop ^js gesture settings))
(defn number-of-taps [gesture count] (.numberOfTaps ^js gesture count))
(defn number-of-taps [gesture amount] (.numberOfTaps ^js gesture amount))
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))

View File

@ -10,9 +10,9 @@
(def key->string str)
(defn set-item!
[key value]
[k value]
(-> ^js async-storage
(.setItem (key->string key)
(.setItem (key->string k)
(clj->transit value))
(.catch (fn [error]
(log/error "[async-storage]" error)))))

View File

@ -53,19 +53,19 @@
"Takes the previous edit, either :gas or :gas-price and a value as string.
Wei for gas, and gwei for gas price.
Validates them and sets max fee"
[edit-value key value]
[edit-value k value]
(let [^js bn-value (money/bignumber value)
error-label-key (get-error-label-key key bn-value)
error-label-key (get-error-label-key k bn-value)
data (if error-label-key
{:value value
:max-fee 0
:error (i18n/label error-label-key)}
{:value value
:value-number (if (= :gasPrice key)
:value-number (if (= :gasPrice k)
(money/->wei :gwei bn-value)
bn-value)})]
(-> edit-value
(assoc key data)
(assoc k data)
edit-max-fee)))
;; TODO(rasom): this number is almost arbitrary, I was able to sent txs with

View File

@ -186,8 +186,8 @@
text))})))))
(defn i18n-text
[{:keys [style key]}]
[text {:style style} (i18n/label key)])
[{style :style k :key}]
[text {:style style} (i18n/label k)])
(defn touchable-opacity
[props content]

View File

@ -3,10 +3,10 @@
[status-im.ui.components.react :as react]))
(defn tab-title
[state key label active?]
[state k label active?]
[react/view {:align-items :center}
[react/touchable-highlight
{:on-press #(swap! state assoc :tab key)
{:on-press #(swap! state assoc :tab k)
:underlay-color colors/gray-lighter
:accessibility-label (str label "-item-button")
:style {:border-radius 8}}
@ -18,14 +18,14 @@
[react/view {:width 24 :height 3 :border-radius 4 :background-color colors/blue}])])
(defn tab-button
[state key label active?]
[state k label active?]
[react/view
{:flex 1
:align-items :center
:border-radius 8
:background-color (if active? colors/blue colors/blue-light)}
[react/touchable-highlight
{:on-press #(swap! state assoc :tab key)
{:on-press #(swap! state assoc :tab k)
:accessibility-label (str label "-item-button")
:style {:border-radius 8}
:flex 1}

View File

@ -191,9 +191,9 @@
(icons/icon icon {:color :dark})]}]])
(defn card-body-row
[key value primary?]
[k value primary?]
[react/view {:flex-direction :row}
[quo/text {:color (when-not primary? :secondary)} key]
[quo/text {:color (when-not primary? :secondary)} k]
[quo/text
{:style {:margin-right 4}
:color (when-not primary? :secondary)} ":"]
@ -268,13 +268,13 @@
:on-change-text on-change}]])
(defn help-label-kv
[{:keys [key value]}]
[{k :key v :value}]
[react/view {:style {:flex-direction :row}}
[quo/text {:color :secondary} key]
[quo/text {:color :secondary} k]
[quo/text
{:color :secondary
:style {:margin-right 4}} ":"]
[quo/text {:color :secondary} value]])
[quo/text {:color :secondary} v]])
(defn nonce-modal
[]

View File

@ -31,5 +31,5 @@
(defn index-by
"Given a collection and a unique key function, returns a map that indexes the collection.
Similar to group-by except that the map values are single objects (depends on key uniqueness)."
[key coll]
(into {} (map #(vector (key %) %) coll)))
[k coll]
(into {} (map #(vector (k %) %) coll)))

View File

@ -224,9 +224,9 @@
(.round (.dividedBy ^js bn1 bn2) 0))
(defn format-members
[count]
(if (> count 1000000)
(str (with-precision (/ count 1000000) 1) (i18n/label :t/M))
(if (and (> count 999) (< count 1000000))
(str (with-precision (/ count 1000) 1) (i18n/label :t/K))
count)))
[amount]
(if (> amount 1000000)
(str (with-precision (/ amount 1000000) 1) (i18n/label :t/M))
(if (and (> amount 999) (< amount 1000000))
(str (with-precision (/ amount 1000) 1) (i18n/label :t/K))
amount)))

View File

@ -44,14 +44,15 @@
[back-button] [options-button]])
(defn count-container
[count accessibility-label]
[amount accessibility-label]
[rn/view
{:style (style/count-container)
:accessibility-label accessibility-label}
[quo/text
{:size :label
:weight :medium
:style {:text-align :center}} count]])
:style {:text-align :center}}
amount]])
(defn contacts-section-header
[{:keys [title]}]

View File

@ -25,9 +25,9 @@
(defn filter-and-sort-items-by-tab
[tab items]
(let [key (if (= tab :tab/groups) :group-chat :chat-id)]
(let [k (if (= tab :tab/groups) :group-chat :chat-id)]
(->> items
(filter key)
(filter k)
(sort-by :timestamp >))))
(defn empty-state-content

View File

@ -128,11 +128,11 @@
(defn update-message
"Update the message and siblings with positional info"
[tree message]
(let [iter (red-black-tree/find tree message)
previous-message (red-black-tree/get-prev iter)
next-message (red-black-tree/get-next iter)
(let [iterator (red-black-tree/find tree message)
previous-message (red-black-tree/get-prev iterator)
next-message (red-black-tree/get-next iterator)
message-with-pos-data (add-group-info message previous-message next-message)]
(cond-> (red-black-tree/update iter message-with-pos-data)
(cond-> (red-black-tree/update iterator message-with-pos-data)
next-message
(-> (red-black-tree/find next-message)
(red-black-tree/update (update-next-message message-with-pos-data next-message)))
@ -145,11 +145,11 @@
(defn remove-message
"Remove a message in the list"
[tree prepared-message]
(let [iter (red-black-tree/find tree prepared-message)]
(if (not iter)
(let [iterator (red-black-tree/find tree prepared-message)]
(if (not iterator)
tree
(let [new-tree (red-black-tree/remove iter)
next-message (red-black-tree/get-next iter)]
(let [new-tree (red-black-tree/remove iterator)
next-message (red-black-tree/get-next iterator)]
(if (not next-message)
new-tree
(update-message new-tree next-message))))))

View File

@ -9,7 +9,7 @@
[status-im2.contexts.chat.photo-selector.album-selector.style :as style]))
(defn render-album
[{:keys [title count uri]} index _ {:keys [album? selected-album]}]
[{title :title size :count uri :uri} index _ {:keys [album? selected-album]}]
(let [selected? (= selected-album title)]
[rn/touchable-opacity
{:on-press (fn []
@ -31,7 +31,7 @@
[quo/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(when count (str count " " (i18n/label :t/images)))]]
(when size (str size " " (i18n/label :t/images)))]]
(when selected?
[rn/view
{:style {:position :absolute

View File

@ -20,8 +20,8 @@
:key :color
:type :select
:options (map (fn [color]
(let [key (get color :name)]
{:key key :value key}))
(let [k (get color :name)]
{:key k :value k}))
(quo/picker-colors))}])
(defn cool-preview

View File

@ -10,8 +10,11 @@
[{:label "Color:"
:key :color
:type :select
:options (map (fn [color] (let [key (get color :name)] {:key key :value key})) (quo/picker-colors))}
{:label "Blur:"
:options (map (fn [color]
(let [k (get color :name)]
{:key k :value k}))
(quo/picker-colors))}
{:label "Blur?"
:key :blur
:type :boolean}])
@ -43,4 +46,3 @@
:keyboard-should-persist-taps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -18,8 +18,8 @@
:key :color
:type :select
:options (map (fn [color]
(let [key (get color :name)]
{:key key :value key}))
(let [k (get color :name)]
{:key k :value k}))
(quo/picker-colors))}])
(defn cool-preview

View File

@ -72,8 +72,8 @@
:background-color (colors/theme-colors colors/neutral-20 colors/white)})
(defn customizer-boolean
[{:keys [label key state]}]
(let [state* (reagent/cursor state [key])]
[{:keys [label state] :as args}]
(let [state* (reagent/cursor state [(:key args)])]
[rn/view {:style container}
[label-view state label]
[rn/view
@ -100,8 +100,8 @@
"False"]]]]))
(defn customizer-text
[{:keys [label key state limit suffix]}]
(let [state* (reagent/cursor state [key])]
[{:keys [label state limit suffix] :as args}]
(let [state* (reagent/cursor state [(:key args)])]
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
@ -129,8 +129,8 @@
(defn customizer-select
[]
(let [open (reagent/atom nil)]
(fn [{:keys [label key state options]}]
(let [state* (reagent/cursor state [key])
(fn [{:keys [label state options] :as args}]
(let [state* (reagent/cursor state [(:key args)])
selected (value-for-key @state* options)]
[rn/view {:style container}
[label-view state label]
@ -145,15 +145,15 @@
[rn/view {:style (modal-view)}
[rn/scroll-view
(doall
(for [{:keys [key value]} options]
^{:key key}
(for [{k :key v :value} options]
^{:key k}
[rn/touchable-opacity
{:style (select-option-style (= @state* key))
{:style (select-option-style (= @state* k))
:on-press #(do
(reset! open false)
(reset! state* key))}
[rn/text {:color (if (= @state* key) :link :secondary)}
value]]))]
(reset! state* k))}
[rn/text {:color (if (= @state* k) :link :secondary)}
v]]))]
[rn/view
{:flex-direction :row
:padding-top 20
@ -192,17 +192,15 @@
{:style {:flex 1}
:padding-horizontal 16}
(doall
(for [{:keys [key type]
:as desc}
descriptors
:let [descriptor (merge desc
{:state state})]]
^{:key key}
(for [desc descriptors
:let [descriptor (merge desc {:state state})]]
^{:key (:key desc)}
[:<>
(case type
(case (:type desc)
:boolean [customizer-boolean descriptor]
:text [customizer-text descriptor]
:select [customizer-select descriptor])]))])
:select [customizer-select descriptor]
nil)]))])
(comment
[{:label "Show error:"

View File

@ -12,9 +12,9 @@
[status-im2.navigation.options :as options]))
(navigation/set-lazy-component-registrator
(fn [key]
(let [screen (views/screen key)]
(navigation/register-component key
(fn [screen-key]
(let [screen (views/screen screen-key)]
(navigation/register-component screen-key
(fn [] (gesture/gesture-handler-root-hoc screen))
(fn [] screen)))))
@ -257,10 +257,9 @@
(re-frame/reg-fx
:set-stack-root-fx
(fn [[stack comp]]
;; We don't have bottom tabs as separate stacks anymore,
;; So the old way of pushing screens in specific tabs will not work.
;; Disabled set-stack-root for :shell-stack as it is not working and
;; currently only being used for browser and some rare keycard flows after login
;; We don't have bottom tabs as separate stacks anymore,. So the old way of pushing screens in
;; specific tabs will not work. Disabled set-stack-root for :shell-stack as it is not working
;; and currently only being used for browser and some rare keycard flows after login
(when-not (= @state/root-id :shell-stack)
(log/debug :set-stack-root-fx stack comp)
(navigation/set-stack-root

View File

@ -51,18 +51,18 @@
{:padding-top (safe-area/get-top)})))
(defn screen
[key]
[screen-key]
(reagent.core/reactify-component
(fn []
(let [{:keys [component options]} (get (if js/goog.DEBUG
(get-screens)
screens)
(keyword key))
(keyword screen-key))
{:keys [insets sheet? theme]} options
user-theme (theme/get-theme)
background-color (or (get-in options [:layout :backgroundColor])
(when sheet? :transparent))]
^{:key (str "root" key @reloader/cnt)}
^{:key (str "root" screen-key @reloader/cnt)}
[theme/provider {:theme (or theme user-theme)}
[rn/view {:style (wrapped-screen-style insets background-color)}
[inactive]

View File

@ -12,14 +12,14 @@
(defn index-by
"Given a collection and a unique key function, returns a map that indexes the collection.
Similar to group-by except that the map values are single objects (depends on key uniqueness)."
[key coll]
(into {} (map #(vector (key %) %) coll)))
[k coll]
(into {} (map #(vector (k %) %) coll)))
(defn distinct-by
"Given a key and a collection returns a unique collection by that key"
[key coll]
(let [groups (group-by key coll)]
(map #(first (groups %)) (distinct (map key coll)))))
[k coll]
(let [groups (group-by k coll)]
(map #(first (groups %)) (distinct (map k coll)))))
(defn map-keys
[f m]

View File

@ -148,9 +148,8 @@
(defn- to-str
[ms old-fmt-fn yesterday-fmt-fn today-fmt-fn]
(let [date (t.coerce/from-long ms)
local (t/plus date time-zone-offset) ; NOTE(edge-case): this is wrong, it uses the current
; timezone offset,
; regardless of DST
;; NOTE(edge-case): this is wrong, it uses the current timezone offset, regardless of DST.
local (t/plus date time-zone-offset)
today (t/minus (t/today-at-midnight) time-zone-offset)
yesterday (t/plus today (t/days -1))]
(cond
@ -235,15 +234,15 @@
:number diff
:time-intervals name}))))
(defn seconds-ago
[time]
[date-time]
(let [now (t/now)]
(if (<= (.getTime ^js time) (.getTime ^js now))
(t/in-seconds (t/interval time now))
(if (<= (.getTime ^js date-time) (.getTime ^js now))
(t/in-seconds (t/interval date-time now))
0)))
(defn time-ago
[time]
(let [diff (seconds-ago time)
[date-time]
(let [diff (seconds-ago date-time)
unit (first (drop-while #(and (>= diff (:limit %))
(:limit %))
units))]
@ -253,8 +252,8 @@
(format-time-ago unit))))
(defn time-ago-long
[time]
(let [seconds-ago (seconds-ago time)
[date-time]
(let [seconds-ago (seconds-ago date-time)
unit (first (drop-while #(and (>= seconds-ago (:limit %))
(:limit %))
units))

View File

@ -14,19 +14,19 @@
(js/clearTimeout v)))
(defn debounce-and-dispatch
"Dispatches event only if there were no calls of this function in period of *time* ms"
[event time]
"Dispatches `event` iff it was not dispatched for the duration of `duration-ms`."
[event duration-ms]
(let [event-key (first event)]
(clear event-key)
(swap! timeout assoc event-key (js/setTimeout #(re-frame/dispatch event) time))))
(swap! timeout assoc event-key (js/setTimeout #(re-frame/dispatch event) duration-ms))))
(def chill (atom {}))
(defn dispatch-and-chill
"Dispateches event and ignores next calls in period of *time* ms"
[event time]
"Dispatches event and ignores subsequent calls for the duration of `duration-ms`."
[event duration-ms]
(let [event-key (first event)]
(when-not (get @chill event-key)
(swap! chill assoc event-key true)
(js/setTimeout #(swap! chill assoc event-key false) time)
(js/setTimeout #(swap! chill assoc event-key false) duration-ms)
(re-frame/dispatch event))))

View File

@ -61,9 +61,9 @@
(def label (memoize label-fn))
(defn label-pluralize
[count path & options]
[amount path & options]
(if (exists? (.t i18n))
(.p i18n count (name path) (clj->js options))
(.p i18n amount (name path) (clj->js options))
(name path)))
(def locale

View File

@ -13,12 +13,12 @@
(.insert tree item))
(defn update
[^js iter item]
(.update iter item))
[^js iterator item]
(.update iterator item))
(defn remove
[^js iter]
(.remove iter))
[^js iterator]
(.remove iterator))
(defn get-values
[^js tree]
@ -26,26 +26,26 @@
(defn get-prev-element
"Get previous item in the iterator, and wind it back to the initial state"
[^js iter]
(.prev iter)
(let [e (.-value iter)]
(.next iter)
[^js iterator]
(.prev iterator)
(let [e (.-value iterator)]
(.next iterator)
e))
(defn get-prev
[^js iter]
(when (.-hasPrev iter)
(get-prev-element iter)))
[^js iterator]
(when (.-hasPrev iterator)
(get-prev-element iterator)))
(defn get-next-element
"Get next item in the iterator, and wind it back to the initial state"
[^js iter]
(.next iter)
(let [e (.-value iter)]
(.prev iter)
[^js iterator]
(.next iterator)
(let [e (.-value iterator)]
(.prev iterator)
e))
(defn get-next
[^js iter]
(when (.-hasNext iter)
(get-next-element iter)))
[^js iterator]
(when (.-hasNext iterator)
(get-next-element iterator)))