   (fn [state [_ query]]
     (helpers/set-search-query state query)))
 
-(reg-event-db
+(reg-event-fx
   :search-history
-  (fn [state [_ [query group-id]]]
+  (fn [{state :db :as cofx} [_ [query group-id]]]
     (if query
-      ; TODO
-      (do (sync/chsk-send!
-            [:braid.server/search [query group-id]]
-            15000
-            (fn [reply]
-              (dispatch [:set-page-loading false])
-              (if (:thread-ids reply)
-                (dispatch [:set-search-results [query reply]])
-                (dispatch [:set-page-error true]))))
-          (helpers/set-page-error state false))
-      state)))
+      {:websocket-send
+       (list
+         [:braid.server/search [query group-id]]
+         15000
+         (fn [reply]
+           (dispatch [:set-page-loading false])
+           (if (:thread-ids reply)
+             (dispatch [:set-search-results [query reply]])
+             (dispatch [:set-page-error true]))))
+       :db (helpers/set-page-error state false)}
+      {})))
 
 (reg-event-db
   :add-threads
   (fn [state [_ threads]]
     (helpers/add-threads state threads)))
 
-(reg-event-db
+(reg-event-fx
   :load-recent-threads
-  (fn [state [_ {:keys [group-id on-error on-complete]}]]
-    ; TODO
-    (sync/chsk-send!
-      [:braid.server/load-recent-threads group-id]
-      5000
-      (fn [reply]
-        (if-let [threads (:braid/ok reply)]
-          (dispatch [:add-threads threads])
-          (cond
-            (= reply :chsk/timeout) (on-error "Timed out")
-            (:braid/error reply) (on-error (:braid/error reply))
-            :else (on-error "Something went wrong")))
-        (on-complete (some? (:braid/ok reply)))))
-    state))
-
-(reg-event-db
+  (fn [cofx [_ {:keys [group-id on-error on-complete]}]]
+    {:websocket-send
+     (list
+       [:braid.server/load-recent-threads group-id]
+       5000
+       (fn [reply]
+         (if-let [threads (:braid/ok reply)]
+           (dispatch [:add-threads threads])
+           (cond
+             (= reply :chsk/timeout) (on-error "Timed out")
+             (:braid/error reply) (on-error (:braid/error reply))
+             :else (on-error "Something went wrong")))
+         (on-complete (some? (:braid/ok reply)))))}))
+
+(reg-event-fx
   :load-threads
-  (fn [state [_ {:keys [thread-ids on-complete]}]]
-    ; TODO
-    (sync/chsk-send!
-      [:braid.server/load-threads thread-ids]
-      5000
-      (fn [reply]
-        (when-let [threads (:threads reply)]
-          (dispatch [:add-threads threads]))
-        (when on-complete
-          (on-complete))))
-    state))
+  (fn [cofx [_ {:keys [thread-ids on-complete]}]]
+    {:websocket-send
+     (list
+       [:braid.server/load-threads thread-ids]
+       5000
+       (fn [reply]
+         (when-let [threads (:threads reply)]
+           (dispatch [:add-threads threads]))
+         (when on-complete
+           (on-complete))))}))
 
 (reg-event-db
   :set-channel-results

  (fn [state [_ err-key]]
    (helpers/clear-error state err-key)))

(reg-event-db
  :resend-message
  (fn [state [_ message]]
    ; TODO
    (sync/chsk-send!
      [:braid.server/new-message message]
      2000
      (fn [reply]
        (when (not= :braid/ok reply)
          (dispatch [:display-error [(str :failed-to-send (message :id)) "Message failed to send!"]])
          (dispatch [:set-message-failed message]))))

    (-> state
        (helpers/clear-error (str :failed-to-send (message :id)))
        (helpers/clear-message-failed message))
    ))

(reg-event-db
  :hide-thread
  (fn [state [_ {:keys [thread-id local-only?]} ]]
    ; TODO
    (when-not local-only?
      (sync/chsk-send! [:braid.server/hide-thread thread-id]))
    (helpers/hide-thread state thread-id)))

(reg-event-db
  :reopen-thread
  (fn [state [_ thread-id]]
    ; TODO
    (sync/chsk-send! [:braid.server/show-thread thread-id])
    (helpers/show-thread state thread-id)))

(reg-event-fx
  :unsub-thread
  (fn [cofx [_ data]]
    ; TODO
    (sync/chsk-send! [:braid.server/unsub-thread (data :thread-id)])
    {:dispatch [:hide-thread {:thread-id (data :thread-id) :local-only? true}]}))

(reg-event-db
  :create-tag
  (fn [state [_ {:keys [tag local-only?]}]]
    (let [tag (merge (schema/make-tag) tag)]
      ; TODO
      (when-not local-only?
        (sync/chsk-send!
          [:braid.server/create-tag tag]
          1000
          (fn [reply]
            (if-let [msg (:error reply)]
              (do
                (dispatch [:remove-tag {:tag-id (tag :id) :local-only? true}])
                (dispatch [:display-error [(str :bad-tag (tag :id)) msg]]))))))
      (-> state
          (helpers/add-tag tag)
          (helpers/subscribe-to-tag (tag :id))))))

(reg-event-db
  :unsubscribe-from-tag
  (fn [state [_ tag-id]]
    ; TODO
    (sync/chsk-send! [:braid.server/unsubscribe-from-tag tag-id])
    (helpers/unsubscribe-from-tag state tag-id)))

(reg-event-db
  :subscribe-to-tag
  (fn [state [_ {:keys [tag-id local-only?]}]]
    ; TODO
    (when-not local-only?
      (sync/chsk-send! [:braid.server/subscribe-to-tag tag-id]))
    (helpers/subscribe-to-tag state tag-id)))

(reg-event-db
  :set-tag-description
  (fn [state [_ {:keys [tag-id description local-only?]}]]
    ; TODO
    (when local-only?
      (sync/chsk-send!
        [:braid.server/set-tag-description {:tag-id tag-id :description description}]))
    (helpers/set-tag-description state tag-id description)))

(reg-event-db
  :retract-tag
  (fn [state [_ {:keys [tag-id local-only?]}]]
    ; TODO
    (when-not local-only?
      (sync/chsk-send! [:braid.server/retract-tag tag-id]))
    (helpers/remove-tag state tag-id)))

(reg-event-db
  :remove-group
  (fn [state [_ group-id]]
    (helpers/remove-group state group-id)))

(reg-event-db
  :create-group
  (fn [state [_ group]]
    (let [group (schema/make-group group)]
      ; TODO
      (sync/chsk-send!
        [:braid.server/create-group group]
        1000
        (fn [reply]
          (when-let [msg (reply :error)]
            (.error js/console msg)
            (dispatch [:display-error [(str :bad-group (group :id)) msg]])
            (dispatch [:remove-group (group :id)]))))
      (-> state
          (helpers/add-group group)
          (helpers/become-group-admin (:id group))))))

(reg-event-db
  :update-user-nickname
  (fn [state [_ {:keys [nickname user-id]}]]
    (helpers/update-user-nickname state user-id nickname)))

(reg-event-db
  :set-user-nickname
  (fn [state [_ {:keys [nickname on-error]}]]
    ; TODO
    (sync/chsk-send!
      [:braid.server/set-nickname {:nickname nickname}]
      1000
      (fn [reply]
        (if-let [msg (reply :error)]
          (on-error msg)
          (dispatch [:update-user-nickname
                     {:nickname nickname
                      :user-id (helpers/current-user-id state)}]))))
    state))

(reg-event-db
  :set-user-avatar
  (fn [state [_ avatar-url]]
    ; TODO
    (sync/chsk-send! [:braid.server/set-user-avatar avatar-url])
    (helpers/update-user-avatar state (helpers/current-user-id state) avatar-url)))

(reg-event-db
  :update-user-avatar
  (fn [state [_ {:keys [user-id avatar-url]}]]
    (helpers/update-user-avatar state user-id avatar-url)))

(reg-event-db
  :set-password
  (fn [state [_ [password on-success on-error]]]
    ; TODO
    (sync/chsk-send!
      [:braid.server/set-password {:password password}]
      3000
      (fn [reply]
        (cond
          (reply :error) (on-error reply)
          (= reply :chsk/timeout) (on-error
                                    {:error "Couldn't connect to server, please try again"})
          true (on-success))))
    state))

(reg-event-fx
  :add-notification-rule
  (fn [{state :db :as cofx} [_ rule]]
    (let [current-rules (get (helpers/get-user-preferences state) :notification-rules [])]
      {:dispatch [:set-preference [:notification-rules (conj current-rules rule)]]})))

(reg-event-fx
  :remove-notification-rule
  (fn [{state :db :as cofx} [_ rule]]
    (let [new-rules (->> (get (helpers/get-user-preferences state) :notification-rules [])
                         (into [] (remove (partial = rule))))]
      {:dispatch [:set-preference [:notification-rules new-rules]]})))

(reg-event-db

  (fn [state [_ bool]]
    (helpers/set-page-error state bool)))

(reg-event-fx
  :set-preference
  (fn [{state :db :as cofx} [_ [k v]]]
    {:websocket-send (list [:braid.server/set-preferences {k v}])
     :db (helpers/set-preferences state {k v})}))

(reg-event-db
  :add-users
