      (merge event-chans)))
   ([fbref close-chan event]
    (let [event-chan (chan)
-         close-chan (chan)
          handle-event (fn [snapshot]
                         (put! event-chan
                               [event
                                (keyword (.key snapshot))
                                (js->clj (.val snapshot))]))]
      (.on fbref (clojure.core/name event) handle-event)
-     (go
-       (let [msg (<! close-chan)]
+     (go (<! close-chan)
          (.off fbref (clojure.core/name event)
-               handle-event)))
+               handle-event))
      event-chan)))
 
-(defn- id->fb-chan [id]
-  (fb->chan (id->fbref id)))
+(defn- id->fb-chan
+  ([id] (id->fb-chan id (chan)))
+  ([id close-chan]
+   (fb->chan (id->fbref id) close-chan)))
 
 (defn id->atom
   ([id] (id->atom (atom {}) id))


(defn init-item-sync!
  "Given vector PATH and atom DATA, create callbacks on channel
  created from item ID to update PATH in DATA.  Channels created to
  update DATA will be stored in atom CHANS as a hash-map of ID and
  chan."
  ([data id] (init-item-sync! data id [:entry]))
  ([data id path] (init-item-sync! data id (conj path :item) (conj path :channels)))
  ([data id path chan-path]
   (let [fbc (id->fb-chan id)]
     (swap! data assoc-in (conj chan-path id) fbc)
     (letfn [(handle-kid [k v child-path]
               (let [items (doall (map #(hash-map % {}) v))]
                 (swap! data assoc-in child-path (into {} items))
                 (doseq [id v]
                   (init-item-sync! data id (conj child-path id) chan-path))))]
       (go-loop []
         (when-let [msg (<! fbc)]
           (let [[event key val] msg
                 child-path (conj path key)]
             (case event 
               :child_added (if (= key :kids)
                              (handle-kid key val child-path)
                              (swap! data assoc-in child-path val))
               :child_changed (if (= key :kids)
                                (handle-kid key val child-path)
                                (swap! data assoc-in child-path val))
               ;; TODO: implement dissoc-in?
               :child_removed (swap! data assoc-in child-path nil) 
               (.log js/console (clj->js [event key val]))))
           (recur)))))))

(defn reset-item-sync! [id data path]
  (swap! data
         #(do
            (when-let [id-chans (get-in % (conj path :channels))]
              (doseq [[id chan] (seq id-chans)]
                (close! chan)))
            (update-in %
                       path
                       dissoc :item :channels)))
  (init-item-sync! data id path))

(ns hacker-agent.hacker-base
  (:require-macros [cljs.core.async.macros :refer [go go-loop]])
  (:require [reagent.core :as reagent :refer [atom]]
            [cljs.core.async :as async :refer [put! chan <! >! close! merge mult tap]]
            [pani.cljs.core :as p]))

(defonce url "https://hacker-news.firebaseio.com/v0")

(defonce root (js/Firebase. url))

(defonce channels (atom {}))

(defn- walk-root [r keys]
  (let [[k & ks] keys]
