diff --git a/public/index.html b/public/index.html index b07109c..5742287 100644 --- a/public/index.html +++ b/public/index.html @@ -228,18 +228,13 @@ section.posts .post .attachments { margin-top: 16px; - max-height: 320px; + height: 320px; display: flex; align-items: flex-start; max-width: 100%; overflow: auto; } - section.posts .post .attachments img, - section.posts .post .attachments video { - max-height: 320px; - } - section.posts .post .controls { margin: 24px 0 0; padding: 0; diff --git a/src/computersandblues/lodestone/app.cljs b/src/computersandblues/lodestone/app.cljs index 3561957..7128011 100644 --- a/src/computersandblues/lodestone/app.cljs +++ b/src/computersandblues/lodestone/app.cljs @@ -160,9 +160,8 @@ (declare fetch-posts!) (defn- fetch-application-settings [] - (let [cursor (db/open-cursor ::db/application ::db/all)] - (. (db/first-result cursor) - (then #(js->clj % :keywordize-keys true))))) + (->> (db/open-cursor! ::db/application ::db/all) + (db/first-result (map #(js->clj % :keywordize-keys true))))) (defn setup-application! "Handles Mastodon application setup on the client side" @@ -199,7 +198,7 @@ (.then (fn [application] (when application (swap! state assoc :section :posts) - (promise-all [application (db/count ::db/posts)])))) + (promise-all [application (db/count! ::db/posts)])))) (.then (fn [[application post-count]] (when post-count (if (zero? post-count) @@ -288,32 +287,29 @@ (defn- refresh-displayed-posts! [{:keys [per-page query]}] - (let [; this `xform` is responsible for filtering and building the list of - ; results that is displayed to the user. it iterates through all stored - ; posts in the database and returns a result that will be rendered - ; by the `post` component below. + (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))) - posts-cursor (db/open-cursor ::db/posts - ::db/all - {:index ::db/post-created-at - :direction :desc}) refresh-id (now)] (swap! state update-in [:section/posts :loading] conj refresh-id) - (. (promise-all [(db/count ::db/posts) - (db/transduce-cursor xform posts-cursor)]) + (. (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 48 refresh-displayed-posts!)) - ; 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 [] @@ -353,55 +349,27 @@ (interleave (repeat ", ")) (drop 1))]) -(defn img-attachment [{:keys [attachment preview-url]}] - [:img {:src preview-url - :alt (:description attachment) - :loading "lazy"}]) - -(defn video-attachment [{:keys [attachment remote-url ext]}] - [:video {:controls true} - [:source {:type (str "video/" ext) :src remote-url}] - [:a {:href (:remote_url attachment)} (str "Original video at " (:remote_url attachment))]]) - -(defn gifv-attachment [{:keys [attachment remote-url ext]}] - (let [autoplay (r/atom false) - toggle-autoplay #(swap! autoplay not)] - (fn [] - [:video {:loop true :autoplay @autoplay :muted true :on-pointer-enter toggle-autoplay} - [:source {:type (str "video/" ext) :src remote-url}] - [:a {:href (:remote_url attachment)} (str "Original video at " (:remote_url attachment))]]))) - (defn attachment [{:keys [attachment]}] (let [preview-url (or (:preview_remote_url attachment) (:preview_url attachment)) remote-url (or (:remote_url attachment) (:url attachment)) - ext (last (str/split remote-url #"\.")) - props {:attachment attachment :preview-url preview-url :remote-url remote-url :ext ext}] + ext (last (str/split remote-url #"\."))] (case (:type attachment) - "image" [img-attachment props] - "video" [video-attachment props] - "gifv" [gifv-attachment props] + "image" [:img {:src preview-url + :srcset (str preview-url ", " remote-url) + :alt (:description attachment) + :lazy "lazy"}] + "video" [:video {:controls true} + [:source {:type (str "video/" ext) :src remote-url}] + [:a {:href (:remote_url attachment)} (str "Original video at " (:remote_url attachment))]] + "gifv" (let [autoplay (r/atom false) + toggle-autoplay #(swap! autoplay not)] + (fn [] + [:video {:loop true :autoplay @autoplay :muted true :on-pointer-enter toggle-autoplay} + [:source {:type (str "video/" ext) :src remote-url}] + [:a {:href (:remote_url attachment)} (str "Original video at " (:remote_url attachment))]])) [:div [:strong "Unsupported attachment"] [debug attachment]]))) - -(comment - ; query current results - (-> @state :section/posts :displayed-posts) - - ; run and time a query on the database - (do - (js/console.log :start (.toISOString (js/Date.))) - (let [xform (comp (mapcat (j/get :tags)) - #_(filter #(= "typescript" (j/get % :name))) - (take 10)) - posts-cursor (db/open-cursor ::db/posts ::db/all)] - (.. (db/transduce-cursor xform conj! (transient #{}) posts-cursor) - (then persistent!) - (then (fn [result] - (js/console.log :end (.toISOString (js/Date.))) - (js/console.log :accts result)))))) - ) - (defn post [{:keys [post]}] ; TODO: handle (:sensitive post) [:article.post @@ -464,11 +432,11 @@ "Returns a promise which resolves to the smallest or largest internal post id. This is useful to continue interrupted paginated requests." [max-or-min] - (let [posts-cursor (db/open-cursor ::db/posts ::db/all {:index ::db/post-internal-id - :direction (if (= max-or-min :min) :asc :desc)})] - (db/first-result (keep (j/get :internal_id)) posts-cursor))) + (->> (db/open-cursor! ::db/posts ::db/all {:index ::db/post-internal-id + :direction (if (= max-or-min :min) :asc :desc)}) + (db/first-result (keep (j/get :internal_id))))) -(defn- fetch-more-posts! [e] +(defn fetch-more-posts! [e] (.preventDefault e) (. (promise-all [(fetch-application-settings) (internal-post-id :min) (internal-post-id :max)]) (then (fn [[application min-id max-id]] @@ -539,7 +507,7 @@ [:li "If you want to search verbatim for a phrase, \"quote it like this\""] [:li "Lodestone tries to turn words into regular expressions. " [:code "fossi.*ergy"] " will match \"fossile energy\"." [:code "bo?ar"] " will match both boar and bar."]]]) -;; the component tying it all together +;; the component tying it all toger (defn app [] (let [section (:section @state) @@ -567,11 +535,11 @@ (defn- convert-internal-ids! [] ; TODO figure out when we can remove this again - (let [cursor (db/open-cursor ::db/posts ::db/all) - result (db/transduce-cursor (filter (comp string? (j/get :internal_id))) cursor)] - (.then result (fn [rows] - (doseq [row rows] - (db/put! ::db/posts (j/update! row :internal_id parse-long))))))) + (. (->> (db/open-cursor! ::db/posts ::db/all) + (db/transduce-cursor (filter (comp string? (j/get :internal_id))))) + (then (fn [rows] + (doseq [row rows] + (db/put! ::db/posts (j/update! row :internal_id parse-long))))))) ;; go go go diff --git a/src/computersandblues/lodestone/database.cljs b/src/computersandblues/lodestone/database.cljs index 7f2beba..05fcc60 100644 --- a/src/computersandblues/lodestone/database.cljs +++ b/src/computersandblues/lodestone/database.cljs @@ -1,5 +1,4 @@ -(ns computersandblues.lodestone.database - (:refer-clojure :exclude [count get])) +(ns computersandblues.lodestone.database) (defonce +db+ (atom nil)) @@ -73,15 +72,15 @@ request (.put store (clj->js object))] (promisify request)))) -(defn get - ([store-id k] (get @+db+ store-id k)) +(defn get! + ([store-id k] (get! @+db+ store-id k)) ([db store-id k] (let [store (open-store db store-id "readonly") request (.get store k)] (promisify request)))) -(defn get-all - ([store-id key-range] (get-all @+db+ store-id key-range)) +(defn get-all! + ([store-id key-range] (get-all! @+db+ store-id key-range)) ([db store-id key-range] (let [store (open-store db store-id "readonly") request (.getAll store key-range)] @@ -95,23 +94,23 @@ (promisify request)))) (defn delete! - ([store-id k] (get @+db+ store-id k)) + ([store-id k] (get! @+db+ store-id k)) ([db store-id k] (let [store (open-store db store-id "readwrite") request (.delete store k)] (promisify request)))) -(defn count - ([store-id] (count @+db+ store-id)) +(defn count! + ([store-id] (count! @+db+ store-id)) ([db store-id] (let [store (open-store db store-id "readonly") request (.count store)] (promisify request)))) -(defn open-cursor - ([store-id key-range] (open-cursor @+db+ store-id key-range {:direction :asc})) - ([store-id key-range opts] (open-cursor @+db+ store-id key-range opts)) +(defn open-cursor! + ([store-id key-range] (open-cursor! @+db+ store-id key-range {:direction :asc})) + ([store-id key-range opts] (open-cursor! @+db+ store-id key-range opts)) ([db store-id key-range {:keys [direction index]}] (let [store (open-store db store-id "readonly") key-range (if (= key-range ::all) @@ -161,21 +160,14 @@ (.continue cursor)))) (resolve @result))))))))) -(defn first-result - "Given a cursor, will return a promise that resolves to the first result. - - Optionally takes an `xform` that can be used to filter or transform values - returned by the cursor." - ([cursor-req] - (first-result (map identity) cursor-req)) - ([xform cursor-req] - (transduce-cursor (comp xform (take 1)) (fn [_ x] x) nil cursor-req))) +(defn first-result [xform cursor-req] + (transduce-cursor (comp xform (take 1)) (fn [_ x] x) cursor-req)) (comment (let [re (js/RegExp. "user" "i")] (do (print "starting…" (js/Date.)) - (-> (open-cursor ::posts ::all) + (-> (open-cursor! ::posts ::all) (transduce-cursor (comp (filter #(re-find re (.-content %))) (take 50) (map #(js->clj % :keywordize-keys true))))