diff --git a/README.md b/README.md index 1766e45..da8b769 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,6 @@ After that npm run build ``` -You can upload these files to any static file server, for example using `rsync`: - -``` bash -rsync -rsvP --delete-after --exclude js/cljs-runtime/ public/ user@server:some/directory -``` - ## Development Just as when building a release, you need to ensure that JS dependencies are installed: diff --git a/public/index.html b/public/index.html index e87f052..61db30f 100644 --- a/public/index.html +++ b/public/index.html @@ -163,12 +163,6 @@ } @media screen and (min-width: 640px) { - section.posts .controls { - grid-template: - "a a" - "b c"; - } - section.posts .controls .post-info { grid-area: a; } diff --git a/src/computersandblues/lodestone/app.cljs b/src/computersandblues/lodestone/app.cljs index b91fc8c..98b6a9d 100644 --- a/src/computersandblues/lodestone/app.cljs +++ b/src/computersandblues/lodestone/app.cljs @@ -9,7 +9,6 @@ (def posts-init-state {:query nil - :last-update -1 ; TODO: pagination ; :page 0 ; :max-displayed-id nil @@ -43,9 +42,6 @@ (defn- promise-reject [val] (js/Promise.reject val)) -(defn- now [] - (js/Date.now)) - (defn- link-header "Given a JS `Response` object, will parse the `link` header and find a link of a given `link-type` if present. Useful for paginating API requests." @@ -156,6 +152,7 @@ (obtain-oauth-authorization-code! application)))))) (declare fetch-posts!) +(declare debounced-refresh!) (defn- fetch-application-settings [] (->> (db/open-cursor! ::db/application ::db/all) @@ -202,7 +199,7 @@ (if (zero? post-count) (fetch-posts! {:instance-url (:instance_url application) :bearer-token (:bearer_token application)}) - (swap! state assoc-in [:section/posts :last-update] (now)))))))) + (debounced-refresh! (:section/posts @state)))))))) ;;; views @@ -250,7 +247,7 @@ on-response on-response on-error on-error}}] ((fn paginate! [url] - (let [req-id (now)] + (let [req-id (js/Date.now)] (js/console.log :paginate! url :max-id max-id :min-id min-id) (swap! state update-in [:section/posts :loading] conj req-id) (-> (mastodon-request! {:url url :bearer-token bearer-token}) @@ -274,50 +271,23 @@ always prefer to call the most recent call to `f` as close to the delay as possible." [ms f] - (let [prev (volatile! (now))] + (let [prev (volatile! (js/Date.now)) + scheduled (volatile! (js/setTimeout (fn dummy []) ms))] (fn debounced-fn [& args] - (when (< ms (- (now) @prev)) - (js/requestAnimationFrame #(apply f args))) - (vreset! prev (now))))) + (let [now (js/Date.now)] + (js/clearTimeout @scheduled) + (vreset! scheduled (js/setTimeout (fn [] + (vreset! prev (js/Date.now)) + (apply f args)) + (max 0 (- ms (- now @prev))))))))) -(defn- refresh-displayed-posts! - [{:keys [per-page query]}] - (let [; this `xform` is responsible for filtering and building the final list - ; of results by iterating through the posts in the database. - xform (comp - (filter (query->matching-fn query)) - (take per-page) - (map #(js->clj % :keywordize-keys true))) - refresh-id (now)] - (swap! state update-in [:section/posts :loading] conj refresh-id) - (. (promise-all [(db/count! ::db/posts) - (->> (db/open-cursor! ::db/posts ::db/all - {:index ::db/post-created-at - :direction :desc}) - (db/transduce-cursor xform))]) - (then (fn [[total displayed-posts]] - (swap! state update :section/posts #(-> (assoc % :total total) - (assoc :displayed-posts displayed-posts) - (update :loading disj refresh-id)))))))) - -; we use reagent's machinery below to define a callback that runs whenever the -; values change that serve as input to the current search reults. - -(def debounced-refresh! (debounce 48 refresh-displayed-posts!)) - -(def search-result-inputs (r/reaction (select-keys (:section/posts @state) [:query :per-page :last-update]))) - -(defonce update-search-results (r/track! (fn [] - (let [inputs @search-result-inputs] - (debounced-refresh! inputs))))) - -(defn search [{:keys [query]}] +(defn search [] [:input {:placeholder "Start typing to search…" :type "search" - :initial-value query :on-change (fn [e] (let [query (.. e -target -value)] - (swap! state assoc-in [:section/posts :query] (if (str/blank? query) nil query))))}]) + (swap! state assoc-in [:section/posts :query] (if (str/blank? query) nil query)) + (debounced-refresh! (:section/posts @state))))}]) (defn- debug "Implements a lazy pretty-printer for whatever is passed in as `obj`. The @@ -399,6 +369,29 @@ (js/console.log :logging args) (apply f args))))) +(defn- refresh-displayed-posts! + [posts-section] + (let [{:keys [per-page query]} posts-section + ; this `xform` is responsible for filtering and building the final list + ; of results by iterating through the posts in the database. + xform (comp + (filter (query->matching-fn query)) + (take per-page) + (map #(js->clj % :keywordize-keys true))) + refresh-id (js/Date.now)] + (swap! state update-in [:section/posts :loading] conj refresh-id) + (. (promise-all [(db/count! ::db/posts) + (->> (db/open-cursor! ::db/posts ::db/all + {:index ::db/post-created-at + :direction :desc}) + (db/transduce-cursor xform))]) + (then (fn [[total displayed-posts]] + (swap! state update :section/posts #(-> (assoc % :total total) + (assoc :displayed-posts displayed-posts) + (update :loading disj refresh-id)))))))) + +(def debounced-refresh! (debounce 40 refresh-displayed-posts!)) + (defn- fetch-posts! [opts] (let [defaults {:max-id nil :on-response (fn [response] @@ -417,7 +410,7 @@ (.get url-params "min_id") (assoc :internal_id (parse-long (.get url-params "min_id"))))))) - (swap! state assoc-in [:section/posts :last-update] (now)))}] + (debounced-refresh! (:section/posts @state)))}] (paginate-posts! (merge defaults opts)))) (defn- internal-post-id @@ -459,7 +452,7 @@ [:path.arc {:d "M50,0 A50,50 180 0,1 100,50"}]])) (defn posts-section [{:keys [posts]}] - (let [{:keys [per-page query total displayed-posts loading query]} posts + (let [{:keys [per-page query total displayed-posts loading]} posts n-displayed (count displayed-posts)] [:section.posts [:h2 "Favorites"] @@ -472,7 +465,7 @@ " match" " matches")))))] [:section.search-form - [search {:query query}] + [search] [loading-indicator {:loading loading}] #_(cond (= api-state :loading) " …" (= api-state :error) " API Error!")]