Compare commits

..

No commits in common. "de2cf7f9c2f0033e79790bbbdc9eb974e7e4815f" and "7d359c2ed79132d4b76aa999bb9bcdecedb8cc1f" have entirely different histories.

3 changed files with 41 additions and 60 deletions

View file

@ -18,12 +18,6 @@ After that
npm run build 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 ## Development
Just as when building a release, you need to ensure that JS dependencies are installed: Just as when building a release, you need to ensure that JS dependencies are installed:

View file

@ -163,12 +163,6 @@
} }
@media screen and (min-width: 640px) { @media screen and (min-width: 640px) {
section.posts .controls {
grid-template:
"a a"
"b c";
}
section.posts .controls .post-info { section.posts .controls .post-info {
grid-area: a; grid-area: a;
} }

View file

@ -9,7 +9,6 @@
(def posts-init-state (def posts-init-state
{:query nil {:query nil
:last-update -1
; TODO: pagination ; TODO: pagination
; :page 0 ; :page 0
; :max-displayed-id nil ; :max-displayed-id nil
@ -43,9 +42,6 @@
(defn- promise-reject [val] (defn- promise-reject [val]
(js/Promise.reject val)) (js/Promise.reject val))
(defn- now []
(js/Date.now))
(defn- link-header (defn- link-header
"Given a JS `Response` object, will parse the `link` header and find a link of "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." a given `link-type` if present. Useful for paginating API requests."
@ -156,6 +152,7 @@
(obtain-oauth-authorization-code! application)))))) (obtain-oauth-authorization-code! application))))))
(declare fetch-posts!) (declare fetch-posts!)
(declare debounced-refresh!)
(defn- fetch-application-settings [] (defn- fetch-application-settings []
(->> (db/open-cursor! ::db/application ::db/all) (->> (db/open-cursor! ::db/application ::db/all)
@ -202,7 +199,7 @@
(if (zero? post-count) (if (zero? post-count)
(fetch-posts! {:instance-url (:instance_url application) (fetch-posts! {:instance-url (:instance_url application)
:bearer-token (:bearer_token application)}) :bearer-token (:bearer_token application)})
(swap! state assoc-in [:section/posts :last-update] (now)))))))) (debounced-refresh! (:section/posts @state))))))))
;;; views ;;; views
@ -250,7 +247,7 @@
on-response on-response on-response on-response
on-error on-error}}] on-error on-error}}]
((fn paginate! [url] ((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) (js/console.log :paginate! url :max-id max-id :min-id min-id)
(swap! state update-in [:section/posts :loading] conj req-id) (swap! state update-in [:section/posts :loading] conj req-id)
(-> (mastodon-request! {:url url :bearer-token bearer-token}) (-> (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 always prefer to call the most recent call to `f` as close to the delay
as possible." as possible."
[ms f] [ms f]
(let [prev (volatile! (now))] (let [prev (volatile! (js/Date.now))
scheduled (volatile! (js/setTimeout (fn dummy []) ms))]
(fn debounced-fn [& args] (fn debounced-fn [& args]
(when (< ms (- (now) @prev)) (let [now (js/Date.now)]
(js/requestAnimationFrame #(apply f args))) (js/clearTimeout @scheduled)
(vreset! prev (now))))) (vreset! scheduled (js/setTimeout (fn []
(vreset! prev (js/Date.now))
(apply f args))
(max 0 (- ms (- now @prev)))))))))
(defn- refresh-displayed-posts! (defn search []
[{: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]}]
[:input {:placeholder "Start typing to search…" [:input {:placeholder "Start typing to search…"
:type "search" :type "search"
:initial-value query
:on-change (fn [e] :on-change (fn [e]
(let [query (.. e -target -value)] (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 (defn- debug
"Implements a lazy pretty-printer for whatever is passed in as `obj`. The "Implements a lazy pretty-printer for whatever is passed in as `obj`. The
@ -399,6 +369,29 @@
(js/console.log :logging args) (js/console.log :logging args)
(apply f 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] (defn- fetch-posts! [opts]
(let [defaults {:max-id nil (let [defaults {:max-id nil
:on-response (fn [response] :on-response (fn [response]
@ -417,7 +410,7 @@
(.get url-params "min_id") (.get url-params "min_id")
(assoc :internal_id (parse-long (.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)))) (paginate-posts! (merge defaults opts))))
(defn- internal-post-id (defn- internal-post-id
@ -459,7 +452,7 @@
[:path.arc {:d "M50,0 A50,50 180 0,1 100,50"}]])) [:path.arc {:d "M50,0 A50,50 180 0,1 100,50"}]]))
(defn posts-section [{:keys [posts]}] (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)] n-displayed (count displayed-posts)]
[:section.posts [:section.posts
[:h2 "Favorites"] [:h2 "Favorites"]
@ -472,7 +465,7 @@
" match" " match"
" matches")))))] " matches")))))]
[:section.search-form [:section.search-form
[search {:query query}] [search]
[loading-indicator {:loading loading}] [loading-indicator {:loading loading}]
#_(cond (= api-state :loading) " …" #_(cond (= api-state :loading) " …"
(= api-state :error) " API Error!")] (= api-state :error) " API Error!")]