   (fn [state [_ bool]]
     (helpers/set-page-error state bool)))
 
-(reg-event-db
+(reg-event-fx
   :set-preference
-  (fn [state [_ [k v]]]
-    ; TODO
-    (sync/chsk-send! [:braid.server/set-preferences {k v}])
-    (helpers/set-preferences state {k v})))
+  (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

                 (map (fn [id] [:hide-thread {:thread-id id :local-only? false}])))
           (helpers/get-open-threads state))}))

(reg-event-db
  :invite
  (fn [state [_ data]]
    (let [invite (schema/make-invitation data)]
      ; TODO
      (sync/chsk-send! [:braid.server/invite-to-group invite])
      state)))

(reg-event-db
  :generate-link
  (fn [state [_ {:keys [group-id expires complete]}]]
    (sync/chsk-send!
      ; TODO
      [:braid.server/generate-invite-link {:group-id group-id :expires expires}]
      5000
      (fn [reply]
        ; indicate error if it fails?
        (when-let [link (:link reply)]
          (complete link))))
    state))

(reg-event-db
  :accept-invite
  (fn [state [_ invite]]
    ; TODO
    (sync/chsk-send! [:braid.server/invitation-accept invite])
    (helpers/remove-invite state invite)))

(reg-event-db
  :decline-invite
  (fn [state [_ invite]]
    ; TODO
    (sync/chsk-send! [:braid.server/invitation-decline invite])
    (helpers/remove-invite state invite)))

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

(reg-event-db
  :remove-from-group
  (fn [state [ _ {:keys [group-id user-id] :as args}]]
    ; TODO
    (sync/chsk-send! [:braid.server/remove-from-group args])
    state))

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

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

(reg-event-db
  :make-group-public!
  (fn [state [_ group-id]]
    ; TODO
    (sync/chsk-send! [:braid.server/set-group-publicity [group-id true]])
    state))

(reg-event-db
  :make-group-private!
  (fn [state [_ group-id]]
    ; TODO
    (sync/chsk-send! [:braid.server/set-group-publicity [group-id false]])
    state))

(reg-event-db
  :new-bot
  (fn [state [_ {:keys [bot on-complete]}]]
    (let [bot (schema/make-bot bot)]
      ; TODO
      (sync/chsk-send!
        [:braid.server/create-bot bot]
        5000
        (fn [reply]
          (when (nil? (:braid/ok reply))
            (dispatch [:display-error
                       [(str "bot-" (bot :id) (rand))
                        (get reply :braid/error
                          "Something when wrong creating bot")]]))
          (on-complete (:braid/ok reply)))))
    state))

(reg-event-db
  :get-bot-info
  (fn [state [_ {:keys [bot-id on-complete]}]]
    ; TODO
    (sync/chsk-send!
      [:braid.server/get-bot-info bot-id]
      2000
      (fn [reply]
        (when-let [bot (:braid/ok reply)]
          (on-complete bot))))
    state))

(reg-event-fx
  :create-upload
  (fn [{state :db :as cofx} [_ {:keys [url thread-id group-id]}]]
    ; TODO
    (sync/chsk-send! [:braid.server/create-upload
                      (schema/make-upload {:url url :thread-id thread-id})])
    {:dispatch [:new-message {:content url :thread-id thread-id :group-id group-id}]}))

(reg-event-db
  :get-group-uploads
  (fn [state [_ {:keys [group-id on-success on-error]}]]
    ; TODO
    (sync/chsk-send!
      [:braid.server/uploads-in-group group-id]
      5000
      (fn [reply]
        (if-let [uploads (:braid/ok reply)]
          (on-success uploads)
          (on-error (get reply :braid/error "Couldn't get uploads in group")))))
    state))

(reg-event-db
  :check-auth

  (fn [state [_ query]]
    (helpers/set-search-query state query)))

(reg-event-fx
  :search-history
  (fn [{state :db :as cofx} [_ [query group-id]]]
    (if query
      {: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-fx
  :load-recent-threads
  (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 [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
