Skip to content

Instantly share code, notes, and snippets.

@Conaws
Created March 11, 2017 14:02
Show Gist options
  • Save Conaws/a2b97321ff199a13aa5f250fc9c23bda to your computer and use it in GitHub Desktop.
Save Conaws/a2b97321ff199a13aa5f250fc9c23bda to your computer and use it in GitHub Desktop.
(ns re-kindle.fire.router
(:require
[posh.reagent :as posh :refer [posh!]]
[datascript.core :as d]
[re-kindle.util :refer [safe]]
[re-kindle.fire.auth :refer [user]]
[cljs-time.format :as format]
[cljs.tools.reader.edn :as edn]
[re-kindle.fire.core :as core :refer [db-ref save]]
[cljs.core.async :as async]
[taoensso.timbre :as log]
[cljs.spec :as s]
[cljs-time.core :as time :refer [now]]
[reagent.core :as r])
(:require-macros
[cljs.core.async.macros :refer [go]]))
(def formatter (format/formatters :basic-date-time))
(defn save-snapshot [path tx conn]
(core/save path (pr-str {:snapshot-key tx
:conn @conn})))
(s/def ::once-args (s/cat :path vector?
:startk :re-kindle.specs/RAtom
:startval :re-kindle.specs/RAtom
:schema map?
:component fn?))
(defn load-conn [path startk startval schema component]
(try (assert (s/valid? ::once-args [path startk startval schema component]))
(catch js/Object e
(pr-str (s/explain ::once-args [path startk startval schema component]))))
(let [ref (db-ref path)]
(.. ref
(once "value"
(fn [x]
(try
(if-let [{k :snapshot-key db :conn} (edn/read-string {:readers d/data-readers} (.val x))]
(let [datoms (set (d/datoms db :eavt))
;; _ (js/console.log datoms)
conn (d/conn-from-datoms datoms schema)
;; _ (js/console.log "schema" schema)
]
(do (posh! conn)
(reset! startval conn)
(reset! startk k)
(log/debugf "this is the k %s" (pr-str k))
(log/debugf "this is the startval %s" (pr-str @startval))
))
(save-snapshot path ":NEW" (d/create-conn schema))
)
(catch js/Object e
(js/console.log e)
(js/console.log "Odds are, your schema isn't working"))))))
(r/create-class
{:display-name "listener"
:component-will-unmount
(fn will-unmount-listener [this]
(.off ref))
:reagent-render
(fn
[path startk startval schema component & args]
(into [component startk startval] args))})))
(declare handle-event*)
(defn on-rolling [path startk startval snappath component]
(let [ref (db-ref path)]
(if (or (= ":NEW"
startk)
(nil? startk))
(.. ref
(on "child_added"
(fn [x]
(let [val (.val x)
k (.getKey x)
parsed-val (edn/read-string {:readers d/data-readers} val)
tx (:tx parsed-val)]
(do
(log/debugf "running on all child-added %s" (pr-str parsed-val))
(handle-event* @startval path parsed-val)
(save-snapshot snappath k @startval)
)
))))
(.. ref
;; (limitToLast 1)
orderByKey
(startAt (clj->js startk))
(on "child_added"
(fn [x]
(let [val (.val x)
k (.getKey x)
parsed-val (edn/read-string {:readers d/data-readers} val)
tx (:tx parsed-val)]
(do
(try
(do
(handle-event* @startval path parsed-val)
;; (d/transact! @startval tx)
(save-snapshot snappath k @startval)
;; (log/debugf "this is the startval %s" (pr-str @startval))
)
(catch js/Object e
(println e)))
)
)))))
(r/create-class
{:display-name "listener"
:component-will-unmount
(fn will-unmount-listener [this]
(.off ref))
:reagent-render
(fn
[path startk startval latestsnap component & args]
(into [component startval latestsnap] args))})))
(defn transact! [path msg]
(core/push path (str {:user/email (:email @user nil)
:msg msg
:time (format/unparse formatter (now))})))
(defmulti handle-event (fn [conn [ev-type & _]] ev-type))
(defmethod handle-event :default
[_ msg]
(log/debugf "Unhandled event: %s" (pr-str msg))
nil)
(defn handle-event* [conn txpath msg]
(log/debugf "handling event %s" msg)
(let [{:as ret :keys [tx followup]} (handle-event conn (:msg msg))]
;; handle-event
(log/tracef "Handler returned: %s" (pr-str ret))
(when tx
(log/debugf "Transacting: %s" (pr-str tx))
(let [x (d/transact! conn tx)
z (-> x :tempids)]
(when followup
(go (async/<! (async/timeout 1))
(transact! txpath (conj followup z)))
)
))
(doseq [new-msg (:dispatch ret)]
(transact! txpath new-msg))
(doseq [[to new-msg] (:dispatch-later ret)]
(go
(async/<! (async/timeout to))
(transact! txpath new-msg)
)
)
)
)
(defn mounter [{:keys [snappath
txpath
startk
connatom
schema
;; email
loading
main]
:as ctx}]
(let [latestsnap (r/atom nil)]
[load-conn snappath startk connatom schema
(fn [k connatom]
(if @k
[on-rolling txpath @k connatom snappath
(fn [connatom]
[main
(merge ctx {:conn @connatom
;; :email email
:dispatch (partial transact! txpath)})])]
[loading]))]))
(defn demo-mounter [schema c]
(safe [mounter
{:snappath ["re-kindle""snapshot-wf"]
:txpath ["re-kindle""txpath-wf"]
:main c
:schema schema
:loading (fn [] [:h1 "Loading mofo"])
:connatom (r/atom nil)
:startk (r/atom nil)
:email "cwhitesullivan@gmail.com"}]))
@Conaws
Copy link
Author

Conaws commented Jun 28, 2018

Mounter is a higher order react component, written using Reagent, a minimalist react wrapper for Clojurescript

The purpose of the component is to create a "poor-man's Datomic", using Datascript -- an in-memory database and datalog query engine, and Google's firebase for real-time collaboration.

This component makes it cheap and easy to build prototypes of user-interfaces based on graphs, it also stores an immutable event-log of all the transactions which impacted that graph, so it provides the developer with the option of building UIs based on the user's change-log -- such as notification feeds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment