-
-
Save fogus/0e38da839dc5ee15d074 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns tumblrsearch.core | |
(:require-macros [cljs.core.async.macros :refer [go]]) | |
(:require [clojure.browser.repl] | |
[cljs.core.async :as async :refer [put! chan <!]] | |
[om.core :as om :include-macros true] | |
[om.dom :as dom :include-macros true] | |
[figwheel.client :as fw :include-macros true] | |
) | |
(:import [goog.net Jsonp] | |
[goog Uri] | |
)) | |
(enable-console-print!) | |
;; state ------------------------------------------ | |
(defonce initial-state | |
(atom {:current-state :search | |
:current-search "" | |
:current-items [] | |
})) | |
(defn reset-state [state] | |
(assoc state | |
:current-state :search | |
:current-search "" | |
:current-items [])) | |
;; Search ------------------------------------------------ | |
;; | |
(def ENTER 13) | |
(def ESC 27) | |
(defn key-event->keycode [e] (.-keyCode e)) | |
(def legal-key #{ENTER ESC}) | |
(defn handle-change [e owner {:keys [text]}] | |
(om/set-state! owner :text (.. e -target -value))) | |
(defn gen-request [tag] | |
(str "http://api.tumblr.com/v2/tagged?tag=" | |
tag | |
"&api_key=?" | |
)) | |
(defn async-search [data ajax-chan] | |
(let [search-term (:current-search data) | |
uri (gen-request search-term) | |
req (Jsonp. (Uri. uri)) | |
] | |
(.send req nil | |
#(if (= (.. % -meta -status) 200) | |
; 200 response - ok | |
(put! ajax-chan {:error false | |
:search-term search-term | |
:items (js->clj (.. % -response) :keywordize-keys true)}) | |
; otherwise error | |
(put! ajax-chan {:error true}))))) | |
(defn search [data state owner] | |
(when (not (empty? (:text state))) | |
(om/transact! | |
data #(assoc % | |
:current-state :loading | |
:current-search (:text state) | |
:current-items [] | |
)) | |
(om/set-state! owner :text "") | |
(go (async-search @data (:ajax-chan state))))) | |
(defn render-search [data owner] | |
(reify | |
om/IInitState | |
(init-state [_] | |
{:text ""}) | |
om/IRenderState | |
(render-state [this state] | |
(dom/div nil | |
(dom/h1 nil "Tumblr Search") | |
; input | |
(dom/input | |
#js {:type "text" | |
:value (:text state) | |
:onChange #(handle-change % owner state) | |
:onKeyDown (fn [e] | |
(let [k (key-event->keycode e)] | |
(when (legal-key k) | |
(condp = k | |
ENTER (search data state owner) | |
ESC (om/set-state! owner :text "") | |
))))}) | |
; search button | |
(dom/button | |
#js {:onClick (fn [e] | |
(.preventDefault e) | |
(search data state owner))} | |
"Search") | |
; clear button | |
(dom/button | |
#js {:onClick (fn [e] | |
(.preventDefault e) | |
(om/set-state! owner :text "") | |
(om/transact! data reset-state) | |
)} | |
"Clear"))) | |
)) | |
;; Loading ------------------------------------------------ | |
(defn render-loading [data owner] | |
(om/component | |
(dom/div nil | |
(dom/h2 nil "loading") | |
(dom/button | |
#js {:onClick (fn [e] | |
(.preventDefault e) | |
(om/transact! data reset-state ))} | |
"Cancel")))) | |
;; loaded ------------------------------------------------ | |
(defn photo-view [item owner] | |
(reify | |
om/IRender | |
(render [_] | |
(dom/p nil | |
(dom/h2 nil (:slug item)) | |
(dom/a #js {:href (:post_url item) :target "_blank"} | |
(dom/img #js {:src (-> item :photos first :alt_sizes second :url)})) | |
)))) | |
(defn render-loaded [data owner] | |
(om/component | |
(dom/div nil | |
(dom/h1 nil (str "Current search: " | |
(:current-search data))) | |
(dom/button | |
#js {:onClick (fn [e] | |
(.preventDefault e) | |
(om/transact! data reset-state))} | |
"Reset") | |
(apply dom/div nil | |
(om/build-all photo-view | |
(filter #(= (:type %) "photo") (:current-items data)))) | |
))) | |
;; error ------------------------------------------------ | |
(defn render-error [data owner] | |
(om/component | |
(dom/div nil | |
(dom/h1 nil "error") | |
(dom/button | |
#js {:onClick (fn [e] | |
(.preventDefault e) | |
(om/transact! data reset-state ))} | |
"Cancel") | |
))) | |
;; App ----------------------------------------------- | |
(defn async-response-loop [data ajax-chan] | |
(go | |
(while true | |
(let [response (<! ajax-chan)] | |
(when (= (:current-state @data) :loading) | |
(cond | |
; loading and error | |
(:error response) | |
(om/transact! data | |
#(assoc % | |
:current-state :error | |
:current-search "" | |
:current-items [])) | |
; loading, not error, search term match | |
(= (:current-search @data) (:search-term response)) | |
(om/transact! data | |
#(assoc % | |
:current-state :loaded | |
:current-items (:items response)) | |
))))))) | |
(om/root | |
(fn [data owner] | |
(reify | |
om/IWillMount | |
(will-mount [_] | |
(let [ajax-chan (chan 1)] | |
(async-response-loop data ajax-chan) | |
(om/set-state! owner :ajax-chan ajax-chan) | |
)) | |
om/IRenderState | |
(render-state [this local-state] | |
(dom/div nil | |
(case (:current-state data) | |
:search (om/build render-search data | |
{:state {:ajax-chan (om/get-state owner :ajax-chan)}}) | |
:loading (om/build render-loading data) | |
:loaded (om/build render-loaded data) | |
(om/build render-error data) | |
)) | |
))) | |
initial-state | |
{:target (. js/document (getElementById "app"))}) | |
;; figwheel ----------------------------------------------- | |
(fw/watch-and-reload | |
:websocket-url "ws://localhost:3449/figwheel-ws" | |
:jsload-callback (fn [] (print "reloaded"))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment