                                       (.preventDefault e)))
                      :on-change (fn [e]
                                   (let [value (.. e -target -value)]
-                                    (swap! fields assoc :url value)))}]
+                                    (swap! fields assoc-in [:url :value] value)))}]
             [:span ".braid.chat"]]
            (when (and (show-status? :url) (not (valid? :url)))
-             [:div.error-message (first (messages :url))])
+             [:div.error-message (first (get-in @fields [:url :errors]))])
            [:div.explanation
             [:p "Pick something short and recognizeable."]
             [:p "Lowercase letters, numbers and dashes only."]]]]
 
          [:div.option.group-type
           [:h2 "Group Type"]
-          [:label {:class (when (= "public" (@fields :type)) "checked")}
+          [:label {:class (when (= "public" (get-in @fields [:type :value])) "checked")}
            [:input {:type "radio"
                     :name "type"
                     :value "public"
-                    :checked (when (= "public" (@fields :type)))
+                    :checked (when (= "public" (get-in @fields [:type :value])))
                     :on-focus (fn [_]
-                                (swap! focused? assoc :type true))
+                                (swap! fields assoc-in [:type :focused?] true))
                     :on-blur (fn [_]
-                               (swap! focused? assoc :type false))
+                               (swap! fields assoc-in [:type :focused?] false)
+                               (validate!))
                     :on-click (fn [e]
                                 (let [value (.. e -target -value)]
-                                  (swap! fields assoc :type value)))}]
+                                  (swap! fields assoc-in [:type :value] value)))}]
            [:span "Public Group"]
            [:div.explanation
             [:p "Anyone can find and join your group through the Braid Group Directory."]
             [:p "Unlimited everything. Free forever."]]]
-          [:label {:class (when (= "private" (@fields :type)) "checked")}
+          [:label {:class (when (= "private" (get-in @fields [:type :value])) "checked")}
            [:input {:type "radio"
                     :name "type"
                     :value "private"
-                    :checked (when (= "private" (@fields :type)))
+                    :checked (when (= "private" (get-in @fields [:type :value])))
                     :on-focus (fn [_]
-                                (swap! focused? assoc :type true))
+                                (swap! fields assoc-in [:type :focused?] true))
                     :on-blur (fn [_]
-                               (swap! focused? assoc :type false))
+                               (swap! fields assoc-in [:type :focused?] false)
+                               (validate!))
                     :on-click (fn [e]
                                 (let [value (.. e -target -value)]
-                                  (swap! fields assoc :type value)))}]
+                                  (swap! fields assoc-in [:type :value] value)))}]
            [:span "Private Group"]
            [:div.explanation
             [:p "Invite-only and hidden from the Braid Group Directory."]

(defn header-view []
  [:h1 "Braid"])

(defn form-view []
  (let [fields (r/atom {:name ""
                        :url ""
                        :type ""})
        focused? (r/atom {:name nil
                          :url nil
                          :type nil})]
    (fn []
      (let [blank? {:name (string/blank? (@fields :name))
                    :url (string/blank? (@fields :url))
                    :type (string/blank? (@fields :type))}
            validations {:name [{:valid? (not (blank? :name))
                                 :message "Your group needs a name."}]
                         :url [{:valid? (not (blank? :url))
                                :message "Your group needs a URL."}
                               {:valid? (boolean (re-matches #"[a-z0-9-]*" (@fields :url)))
                                :message "Your URL can only contain lowercase letters, numbers or dashes."}
                               {:valid? (not (re-matches #"-.*" (@fields :url)))
                                :message "Your URL can't start with a dash."}
                               {:valid? (not (re-matches #".*-" (@fields :url)))
                                :message "Your URL can't end with a dash."}]
                         :type [{:valid? (not (blank? :type))
                                 :message "You need to select a group type"}]}
            valid? (fn [field]
                     (->> (validations field)
                          (map :valid?)
                          (every? true?)))
            messages (fn [field]
                       (->> (validations field)
                            (remove :valid?)
                            (map :message)))
            _ (println (valid? :url))
            show-status? {:name (and
                                  (not (@focused? :name))
                                  (not (blank? :name)))
                          :url (and
                                 (not (@focused? :url))
                                 (not (blank? :url)))
                          :type (and
                                  (not (@focused? :type))
                                  (not (blank? :type)))}
            all-valid? (and (valid? :name) (valid? :url) (valid? :type))]
        [:form.register
         [header-view]


            [:input {:type "text"
                     :placeholder "Team Awesome"
                     :auto-focus true
                     :value (get-in @fields [:name :value])
                     :on-focus (fn [_]
                                 (swap! fields assoc-in [:name :focused?] true))
                     :on-blur (fn [_]
                                (swap! fields assoc-in [:name :focused?] false)
                                (validate!))
                     :on-change (fn [e]
                                  (let [value (.. e -target -value)]
                                    (swap! fields assoc-in [:name :value] value)))}]]
           (when (and (show-status? :name) (not (valid? :name)))
             [:div.error-message (first (get-in @fields [:name :errors]))])
           [:div.explanation
            [:p "Your group's name will show up in menus and headings."]
            [:p "It doesn't need to be formal and can always be changed later."]]]]
