Merge pull request #236 from status-im/feature/remove-status-lib

Cleanup after protocol's refactoring

Former-commit-id: 8ca1d645a3
This commit is contained in:
Roman Volosovskyi 2016-09-18 18:07:56 +03:00 committed by GitHub
commit 10c205f9f5
43 changed files with 62 additions and 215 deletions

View File

@ -3,13 +3,11 @@
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0-alpha11"]
[org.clojure/clojurescript "1.9.227"]
:dependencies [[org.clojure/clojure "1.9.0-alpha12"]
[org.clojure/clojurescript "1.9.229"]
[reagent "0.5.1" :exclusions [cljsjs/react]]
[re-frame "0.7.0"]
[prismatic/schema "1.0.4"]
^{:voom {:repo "git@github.com:status-im/status-lib.git" :branch "group-chat-statuses"}}
[status-im/protocol "0.2.3-20160914_155558-gfed628a"]
[natal-shell "0.3.0"]
[com.andrewmcveigh/cljs-time "0.4.0"]
[tailrecursion/cljs-priority-map "1.2.0"]
@ -19,8 +17,7 @@
[cljsjs/chance "0.7.3-0"]
[cljsjs/eccjs "0.3.1-0"]]
:plugins [[lein-cljsbuild "1.1.1"]
[lein-figwheel "0.5.0-2"]
[lein-voom "0.1.0-20160311_203101-g259fbfc"]]
[lein-figwheel "0.5.0-2"]]
:clean-targets ["target/" "index.ios.js" "index.android.js"]
:aliases {"prod-build" ^{:doc "Recompile code with prod profile."}
["do" "clean"

View File

@ -54,7 +54,7 @@ open -a /Applications/Genymotion.app/Contents/MacOS/player.app --args --vm-name
fi
# Install deps, prepare for genymotion and figwheel
lein voom build-deps && re-natal deps && re-natal use-android-device "${device_type}" && re-natal use-figwheel
lein deps && re-natal deps && re-natal use-android-device "${device_type}" && re-natal use-figwheel
# open figwheel in new tab
tab "lein figwheel ${cljs_build}"

View File

@ -1,16 +1,13 @@
(ns status-im.accounts.handlers
(:require [status-im.models.accounts :as accounts-model]
[re-frame.core :refer [register-handler after dispatch dispatch-sync debug]]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.protocol.core :as protocol]
[status-im.components.status :as status]
[status-im.utils.types :refer [json->clj]]
[status-im.persistence.simple-kv-store :as kv]
[status-im.protocol.state.storage :as storage]
[status-im.utils.identicon :refer [identicon]]
[status-im.db :refer [default-view]]
[status-im.utils.random :as random]
[status-im.persistence.realm.core :as realm]
[status-im.i18n :refer [label]]
[status-im.constants :refer [content-type-command-request]]
status-im.accounts.login.handlers
@ -29,9 +26,6 @@
(update db :accounts assoc address account))
((after save-account))))
(defn save-password [password]
(storage/put kv/kv-store :password password))
(defn account-created [result password]
(let [data (json->clj result)
public-key (:pubkey data)

View File

@ -1,7 +1,7 @@
(ns status-im.accounts.login.handlers
(:require [re-frame.core :refer [after dispatch]]
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.utils.types :refer [json->clj]]
[status-im.db :refer [default-view]]
[status-im.persistence.realm.core :as realm]

View File

@ -3,13 +3,9 @@
[status-im.components.status :as status]
[status-im.utils.types :refer [json->clj]]
[status-im.utils.identicon :refer [identicon]]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[clojure.string :as str]))
(defn on-account-changed
[error address new-account?]
(dispatch [:navigate-to-clean :accounts]))
(defn account-recovered [result password]
(let [_ (log/debug result)
data (json->clj result)

View File

@ -24,7 +24,7 @@
[status-im.accounts.recover.validations :as v]
[cljs.spec :as s]
[clojure.string :as str]
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(defn toolbar-title []
[view toolbar-title-container

View File

@ -25,10 +25,8 @@
white-form-text-input]]
[status-im.utils.listview :as lw]
[status-im.accounts.views.account :refer [account-view]]
[status-im.chat.sign-up :as sign-up-service]
[status-im.i18n :refer [label]]
[status-im.accounts.styles :as st]
[status-im.utils.logging :as log]))
[status-im.accounts.styles :as st]))
(defn toolbar-title []
(let [style (merge toolbar-title-text {:color color-white})]

View File

@ -27,9 +27,8 @@
[status-im.profile.screen :refer [profile my-profile]]
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
[status-im.utils.utils :refer [toast]]
[status-im.utils.encryption]
status-im.persistence.realm.core
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.components.status :as status]))
(defn init-back-button-handler! []
@ -47,7 +46,7 @@
(keyword (.toLowerCase o)))
(defn app-root []
(let [signed-up (subscribe [:get :signed-up])
(let [signed-up (subscribe [:signed-up?])
view-id (subscribe [:get :view-id])
account-id (subscribe [:get :current-account-id])
keyboard-height (subscribe [:get :keyboard-height])]

View File

@ -5,7 +5,6 @@
[clojure.string :as str]
[status-im.components.styles :refer [default-chat-color]]
[status-im.chat.suggestions :as suggestions]
[status-im.protocol.api :as api]
[status-im.protocol.core :as protocol]
[status-im.models.chats :as chats]
[status-im.models.messages :as messages]
@ -20,7 +19,6 @@
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.persistence.realm.core :as r]
[status-im.handlers.server :as server]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
[status-im.utils.phone-number :refer [format-phone-number
valid-mobile-number?]]
[status-im.components.status :as status]
@ -34,8 +32,7 @@
status-im.chat.handlers.receive-message
[cljs.core.async :as a]
status-im.chat.handlers.webview-bridge
status-im.chat.handlers.wallet-chat
[status-im.utils.logging :as log]))
status-im.chat.handlers.wallet-chat))
(register-handler :set-chat-ui-props
(fn [db [_ ui-element value]]
@ -219,8 +216,9 @@
(server/sign-up-confirm confirmation-code sign-up-service/on-send-code-response))))
(register-handler :set-signed-up
(fn [db [_ signed-up]]
(sign-up-service/set-signed-up db signed-up)))
(u/side-effect!
(fn [_ [_ signed-up]]
(dispatch [:account-update {:signed-up? signed-up}]))))
(defn load-messages!
([db] (load-messages! db nil))

View File

@ -1,7 +1,6 @@
(ns status-im.chat.handlers.animation
(:require [re-frame.core :refer [after dispatch debug path]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
[status-im.chat.constants :refer [input-height request-info-height
suggestions-header-height
minimum-command-suggestions-height

View File

@ -6,8 +6,7 @@
[status-im.commands.utils :refer [generate-hiccup]]
[status-im.constants :refer [content-type-command-request]]
[cljs.reader :refer [read-string]]
[status-im.models.chats :as c]
[status-im.utils.logging :as log]))
[status-im.models.chats :as c]))
(defn check-preview [{:keys [content] :as message}]
(if-let [preview (:preview content)]

View File

@ -1,8 +1,7 @@
(ns status-im.chat.handlers.unviewed-messages
(:require [re-frame.core :refer [after enrich path dispatch]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.persistence.realm.core :as realm]
[status-im.utils.logging :as log]))
[status-im.persistence.realm.core :as realm]))
(defn delivered-messages []
(-> (realm/get-by-fields :account :message :and {:outgoing false

View File

@ -3,7 +3,7 @@
[status-im.utils.handlers :refer [register-handler]]
[status-im.utils.handlers :as u]
[status-im.utils.types :as t]
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(register-handler :set-webview-bridge
(fn [db [_ bridge]]

View File

@ -1,7 +1,6 @@
(ns status-im.chat.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as s]
[status-im.components.react :refer [view
animated-view
text
@ -74,19 +73,6 @@
(assoc :last-message (= (js/parseInt idx) (dec messages-count))))]
(list-item [chat-message message]))))
(defn on-action-selected [position]
(case position
0 (dispatch [:navigate-to :add-participants])
1 (dispatch [:navigate-to :remove-participants])
2 (dispatch [:leave-group-chat])))
(defn overlay [{:keys [on-click-outside]} items]
[view st/actions-overlay
[touchable-highlight {:on-press on-click-outside
:style st/overlay-highlight}
[view nil]]
items])
(defn online-text [contact chat-id]
(if contact
(let [last-online (get contact :last-online)

View File

@ -1,9 +1,5 @@
(ns status-im.chat.sign-up
;status-im.handlers.sign-up
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.persistence.simple-kv-store :as kv]
[status-im.protocol.state.storage :as s]
[status-im.models.chats :as c]
[status-im.components.styles :refer [default-chat-color]]
[status-im.utils.utils :refer [on-error http-post toast]]
[status-im.utils.random :as random]
@ -24,10 +20,6 @@
:content-type text-content-type
:outgoing true})
(defn set-signed-up [db signed-up]
(s/put kv/kv-store :signed-up signed-up)
(assoc db :signed-up signed-up))
; todo fn name is not too smart, but...
(defn command-content
[command content]

View File

@ -9,8 +9,7 @@
separator-color
text1-color
text2-color
toolbar-background1]]
[status-im.utils.logging :as log]))
toolbar-background1]]))
(def chat-view
{:flex 1

View File

@ -6,7 +6,6 @@
[status-im.models.chats :as chats]
[status-im.constants :refer [response-suggesstion-resize-duration]]
[status-im.chat.constants :as c]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
[status-im.chat.views.plain-message :as plain-message]
[status-im.chat.views.command :as command]))

View File

@ -12,8 +12,7 @@
[status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]]
[status-im.chat.styles.screen :as st]
[status-im.i18n :refer [label label-pluralize]]
[status-im.utils.platform :refer [platform-specific]]
[status-im.utils.logging :as log]))
[status-im.utils.platform :refer [platform-specific]]))
(defview menu-item-icon-profile []
[chat-id [:chat :chat-id]

View File

@ -17,7 +17,6 @@
[status-im.utils.utils :refer [truncate-str]]
[status-im.utils.identicon :refer [identicon]]
[status-im.utils.listview :as lw]
[status-im.utils.logging :as log]
[clojure.string :as str]))
(defn- container-animation-logic [{:keys [to-value val]}]

View File

@ -21,8 +21,6 @@
content-type-status
content-type-command
content-type-command-request]]
[status-im.utils.logging :as log]
[status-im.protocol.api :as api]
[status-im.utils.identicon :refer [identicon]]
[status-im.chat.utils :as cu]))
@ -263,14 +261,14 @@
(into [view] children)))
(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from]}]
(let [my-identity (api/my-identity)
(let [my-identity (subscribe [:get :current-public-key])
status (subscribe [:get-in [:message-user-statuses message-id my-identity]])]
(r/create-class
{:component-did-mount
(fn []
(when (and (not outgoing)
(not= :seen (keyword @status))
(not= :seen (keyword (get-in user-statuses [my-identity :status]))))
(not= :seen (keyword (get-in user-statuses [@my-identity :status]))))
(dispatch [:send-seen! {:chat-id chat-id
:from from
:message-id message-id}])))

View File

@ -4,7 +4,7 @@
touchable-without-feedback
text]]
[status-im.components.carousel.styles :as st]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.components.react :as r]))

View File

@ -3,7 +3,7 @@
(:require [status-im.components.react :as r]
[status-im.utils.types :as t]
[re-frame.core :refer [dispatch]]
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(def status-js (slurp "resources/status.js"))

View File

@ -12,7 +12,7 @@
[status-im.components.text-field.styles :as st]
[status-im.i18n :refer [label]]
[status-im.components.animation :as anim]
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(def config {:label-top 16

View File

@ -39,7 +39,6 @@
:chat-ui-props {:show-actions? false
:show-bottom-info? false}
:selected-participants #{}
:signed-up false
:view-id default-view
:navigation-stack (list default-view)
:current-tag nil
@ -50,7 +49,6 @@
:tabs-bar-value (anim/create-value 0)}
:loading-allowed true})
(def protocol-initialized-path [:protocol-initialized])
(defn chat-staged-commands-path [chat-id]
[:chats chat-id :staged-commands])
(defn chat-command-path [chat-id]

View File

@ -2,7 +2,6 @@
(:require [re-frame.core :refer [after dispatch enrich]]
[status-im.utils.utils :refer [first-index]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.protocol.api :as api]
[status-im.protocol.core :as protocol]
[status-im.navigation.handlers :as nav]
[status-im.discovery.model :as discoveries]

View File

@ -1,8 +1,6 @@
(ns status-im.discovery.model
;status-im.models.discoveries
(:require [status-im.utils.logging :as log]
[status-im.persistence.realm.core :as r]
[status-im.constants :as c]))
(:require [taoensso.timbre :as log]
[status-im.persistence.realm.core :as r]))
(defn get-tag [tag]
(log/debug "Getting tag: " tag)

View File

@ -4,9 +4,7 @@
[schema.core :as s :include-macros true]
[status-im.db :refer [app-db schema]]
[status-im.persistence.realm.core :as realm]
[status-im.persistence.simple-kv-store :as kv]
[status-im.protocol.state.storage :as storage]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.utils.crypt :refer [gen-random-bytes]]
[status-im.components.status :as status]
[status-im.utils.handlers :refer [register-handler] :as u]
@ -23,24 +21,9 @@
status-im.qr-scanner.handlers
status-im.accounts.handlers
status-im.protocol.handlers
[status-im.utils.datetime :as time]
status-im.transactions.handlers
[status-im.utils.types :as t]))
;; -- Middleware ------------------------------------------------------------
;;
;; See https://github.com/Day8/re-frame/wiki/Using-Handler-Middleware
;;
(defn check-and-throw
"throw an exception if db doesn't match the schema."
[a-schema db]
(if-let [problems (s/check a-schema db)]
(throw (js/Error. (str "schema check failed: " problems)))))
(def validate-schema-mw
(after (partial check-and-throw schema)))
;; -- Common --------------------------------------------------------------
(defn set-el [db [_ k v]]
@ -66,9 +49,7 @@
(fn [db _]
(assoc db
:chats {}
:current-chat-id "console"
:signed-up (storage/get kv/kv-store :signed-up)
:password (storage/get kv/kv-store :password))))
:current-chat-id "console")))
(register-handler :initialize-account
(u/side-effect!

View File

@ -1,25 +0,0 @@
(ns status-im.handlers.content-suggestions
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.db :as db]
[status-im.utils.logging :as log]
[clojure.string :as s]))
;; TODO stub data?
(def suggestions
{:phone [{:header "Phone number formats"}
{:value "89171111111"
:description "Number format 1"}
{:value "+79171111111"
:description "Number format 2"}
{:value "9171111111"
:description "Number format 3"}]})
(defn get-content-suggestions [command text]
(or (when command
(when-let [command-suggestions ((keyword (:name command)) suggestions)]
(filterv (fn [s]
(or (:header s)
(and (.startsWith (:value s) (or text ""))
(not= (:value s) text))))
command-suggestions)))
[]))

View File

@ -1,7 +1,7 @@
(ns status-im.handlers.server
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.utils.utils :refer [on-error http-post]]
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(defn sign-up
[db phone-number handler]

View File

@ -23,15 +23,14 @@
[status-im.profile.screen :refer [profile my-profile]]
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
[status-im.utils.utils :refer [toast]]
[status-im.utils.encryption]
status-im.persistence.realm.core
[status-im.utils.logging :as log]))
[taoensso.timbre :as log]))
(defn orientation->keyword [o]
(keyword (.toLowerCase o)))
(defn app-root []
(let [signed-up (subscribe [:get :signed-up])
(let [signed-up (subscribe [:signed-up?])
_ (log/debug "signed up: " @signed-up)
view-id (subscribe [:get :view-id])
account-id (subscribe [:get :current-account-id])

View File

@ -4,7 +4,6 @@
[status-im.persistence.realm.core :as r]
[status-im.utils.random :as random :refer [timestamp]]
[clojure.string :refer [join blank?]]
[status-im.utils.logging :as log]
[status-im.constants :refer [content-type-status]]
[status-im.models.messages :refer [save-message]]
[status-im.persistence.realm-queries :refer [include-query]]))

View File

@ -2,8 +2,7 @@
(:require [status-im.persistence.realm.core :as r]
[status-im.utils.identicon :refer [identicon]]
[status-im.persistence.realm-queries :refer [include-query
exclude-query]]
[status-im.utils.logging :as log]))
exclude-query]]))
(defn get-contacts []
(-> (r/get-all :account :contact)

View File

@ -3,7 +3,6 @@
[re-frame.core :refer [dispatch]]
[cljs.reader :refer [read-string]]
[status-im.utils.random :refer [timestamp]]
[status-im.utils.logging :as log]
[clojure.string :refer [join split]]
[clojure.walk :refer [stringify-keys keywordize-keys]]
[status-im.constants :as c]

View File

@ -8,7 +8,6 @@
[status-im.constants :as c]
[status-im.utils.types :refer [clj->json json->clj]]
[status-im.commands.utils :refer [generate-hiccup]]
[status-im.utils.logging :as log]
[cljs.reader :as reader]
[clojure.string :as str]))

View File

@ -1,20 +0,0 @@
(ns status-im.models.protocol
(:require [cljs.reader :refer [read-string]]
[status-im.protocol.state.storage :as s]
[status-im.utils.types :refer [to-edn-string]]
[re-frame.db :refer [app-db]]
[status-im.db :as db]
[status-im.persistence.simple-kv-store :as kv]))
(defn set-initialized [db initialized?]
(assoc-in db db/protocol-initialized-path initialized?))
(defn update-identity [db {:keys [address] :as identity}]
(let [identity-string (to-edn-string identity)]
(s/put kv/kv-store :identity identity-string)
(assoc-in db [:accounts address] identity)))
(defn stored-identity [db]
(let [identity (s/get kv/kv-store :identity)]
(when identity
(read-string identity))))

View File

@ -4,7 +4,7 @@
[status-im.utils.types :refer [to-string]]
[status-im.utils.utils :as u]
[status-im.utils.fs :as fs]
[status-im.utils.logging :as log]
[taoensso.timbre :as log]
[status-im.persistence.realm.schemas :refer [base account]]
[clojure.string :as str])
(:refer-clojure :exclude [exists?]))

View File

@ -14,7 +14,9 @@
:email {:type "string" :optional true}
:status {:type "string" :optional true}
:photo-path "string"
:last-updated {:type "int" :default 0}}}
:last-updated {:type "int" :default 0}
:signed-up? {:type :bool
:default false}}}
{:name :kv-store
:primaryKey :key
:properties {:key "string"

View File

@ -1,29 +0,0 @@
(ns status-im.persistence.simple-kv-store
(:require [status-im.protocol.state.storage :as st]
[status-im.persistence.realm.core :as r]
[status-im.utils.types :refer [to-edn-string]]))
(defrecord SimpleKvStore [schema]
st/Storage
(put [_ key value]
(r/write schema
(fn []
(r/create schema :kv-store
{:key key
:value (to-edn-string value)} true))))
(get [_ key]
(some-> (r/get-by-field schema :kv-store :key key)
(r/single-cljs)
(r/decode-value)))
(contains-key? [_ key]
(r/exists? schema :kv-store {:key key}))
(delete [_ key]
(r/write schema
(fn []
(->> (r/get-by-field schema :kv-store :key key)
(r/single)
(r/delete schema))))))
(def kv-store (->SimpleKvStore :account))
(def base-kv-store (->SimpleKvStore :base))

View File

@ -1,21 +1,16 @@
; todo everything inside this namespace must be revievew in common with future
; changes in protocol lib
(ns status-im.protocol.handlers
(:require [status-im.utils.handlers :as u]
[status-im.utils.logging :as log]
[re-frame.core :refer [dispatch after]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.models.contacts :as contacts]
[status-im.models.messages :as messages]
[status-im.models.pending-messages :as pending-messages]
[status-im.models.chats :as chats]
[status-im.models.protocol :refer [update-identity
set-initialized]]
[status-im.protocol.core :as protocol]
[status-im.constants :refer [text-content-type]]
[status-im.i18n :refer [label]]
[status-im.utils.random :as random]
[taoensso.timbre :refer-macros [debug]]))
[taoensso.timbre :as log :refer-macros [debug]]))
(register-handler :initialize-protocol
(fn [db [_ current-account-id]]
@ -73,12 +68,6 @@
(dispatch [:message-sent message']))
(debug "Unknown message type" type)))))
(register-handler :protocol-initialized
(fn [db [_ identity]]
(-> db
(update-identity identity)
(set-initialized true))))
(defn system-message [message-id content]
{:from "system"
:message-id message-id

View File

@ -1,7 +0,0 @@
(ns status-im.protocol.protocol-handler
(:require [status-im.utils.logging :as log]
[status-im.constants :refer [ethereum-rpc-url]]
[re-frame.core :refer [dispatch]]
[status-im.models.protocol :refer [stored-identity]]
[status-im.persistence.simple-kv-store :as kv]
[status-im.models.chats :refer [active-group-chats]]))

View File

@ -5,7 +5,7 @@
[status-im.protocol.web3.utils :as u]
[status-im.protocol.encryption :as e]
[cljs.spec :as s]
[taoensso.timbre :refer-macros [debug] :as timbre]
[taoensso.timbre :refer-macros [debug] :as log]
[status-im.protocol.validation :refer-macros [valid?]]
[clojure.set :as set]))
@ -107,7 +107,7 @@
(defn delivery-callback
[web3 {:keys [id requires-ack? to]}]
(fn [error _]
(when error (timbre/error :shh-post-error error))
(when error (log/error :shh-post-error error))
(when-not error
(debug :delivery-callback)
(message-was-sent! web3 id to)
@ -173,7 +173,7 @@
callback (delivery-callback web3 data)]
(t/post-message! web3 message' callback))
(catch :default err
(timbre/error :post-message-error err))
(log/error :post-message-error err))
(finally
(attempt-was-made! web3 id to)))))))
(when-not @stop?

View File

@ -1,6 +1,6 @@
(ns status-im.subs
(:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub]]
(:require [re-frame.core :refer [register-sub subscribe]]
status-im.chat.subs
status-im.group-settings.subs
status-im.discovery.subs
@ -25,3 +25,10 @@
(register-sub :animations
(fn [db [_ k]]
(reaction (get-in @db [:animations k]))))
(register-sub :signed-up?
(fn []
(let [account-id (subscribe [:get :current-account-id])
accounts (subscribe [:get :accounts])]
(reaction (when (and @accounts @account-id)
(get-in @accounts [@account-id :signed-up?]))))))

View File

@ -0,0 +1,8 @@
(ns status-im.utils.random
(:require [cljsjs.chance]))
(defn timestamp []
(.getTime (js/Date.)))
(defn id []
(str (timestamp) "-" (.guid js/chance)))