Compare commits

..

No commits in common. "58d709f7be1272d9d9bb545a13b5d45834275fd6" and "c35eb6d11e08f46548511f077a403cdd02f1df76" have entirely different histories.

3 changed files with 51 additions and 96 deletions

View file

@ -228,18 +228,13 @@
section.posts .post .attachments { section.posts .post .attachments {
margin-top: 16px; margin-top: 16px;
max-height: 320px; height: 320px;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
max-width: 100%; max-width: 100%;
overflow: auto; overflow: auto;
} }
section.posts .post .attachments img,
section.posts .post .attachments video {
max-height: 320px;
}
section.posts .post .controls { section.posts .post .controls {
margin: 24px 0 0; margin: 24px 0 0;
padding: 0; padding: 0;

View file

@ -160,9 +160,8 @@
(declare fetch-posts!) (declare fetch-posts!)
(defn- fetch-application-settings [] (defn- fetch-application-settings []
(let [cursor (db/open-cursor ::db/application ::db/all)] (->> (db/open-cursor! ::db/application ::db/all)
(. (db/first-result cursor) (db/first-result (map #(js->clj % :keywordize-keys true)))))
(then #(js->clj % :keywordize-keys true)))))
(defn setup-application! (defn setup-application!
"Handles Mastodon application setup on the client side" "Handles Mastodon application setup on the client side"
@ -199,7 +198,7 @@
(.then (fn [application] (.then (fn [application]
(when application (when application
(swap! state assoc :section :posts) (swap! state assoc :section :posts)
(promise-all [application (db/count ::db/posts)])))) (promise-all [application (db/count! ::db/posts)]))))
(.then (fn [[application post-count]] (.then (fn [[application post-count]]
(when post-count (when post-count
(if (zero? post-count) (if (zero? post-count)
@ -288,32 +287,29 @@
(defn- refresh-displayed-posts! (defn- refresh-displayed-posts!
[{:keys [per-page query]}] [{:keys [per-page query]}]
(let [; this `xform` is responsible for filtering and building the list of (let [; this `xform` is responsible for filtering and building the final list
; results that is displayed to the user. it iterates through all stored ; of results by iterating through the posts in the database.
; posts in the database and returns a result that will be rendered
; by the `post` component below.
xform (comp xform (comp
(filter (query->matching-fn query)) (filter (query->matching-fn query))
(take per-page) (take per-page)
(map #(js->clj % :keywordize-keys true))) (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)] refresh-id (now)]
(swap! state update-in [:section/posts :loading] conj refresh-id) (swap! state update-in [:section/posts :loading] conj refresh-id)
(. (promise-all [(db/count ::db/posts) (. (promise-all [(db/count! ::db/posts)
(db/transduce-cursor xform posts-cursor)]) (->> (db/open-cursor! ::db/posts ::db/all
{:index ::db/post-created-at
:direction :desc})
(db/transduce-cursor xform))])
(then (fn [[total displayed-posts]] (then (fn [[total displayed-posts]]
(swap! state update :section/posts #(-> (assoc % :total total) (swap! state update :section/posts #(-> (assoc % :total total)
(assoc :displayed-posts displayed-posts) (assoc :displayed-posts displayed-posts)
(update :loading disj refresh-id)))))))) (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 ; 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. ; 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]))) (def search-result-inputs (r/reaction (select-keys (:section/posts @state) [:query :per-page :last-update])))
(defonce update-search-results (r/track! (fn [] (defonce update-search-results (r/track! (fn []
@ -353,55 +349,27 @@
(interleave (repeat ", ")) (interleave (repeat ", "))
(drop 1))]) (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]}] (defn attachment [{:keys [attachment]}]
(let [preview-url (or (:preview_remote_url attachment) (:preview_url attachment)) (let [preview-url (or (:preview_remote_url attachment) (:preview_url attachment))
remote-url (or (:remote_url attachment) (:url attachment)) remote-url (or (:remote_url attachment) (:url attachment))
ext (last (str/split remote-url #"\.")) ext (last (str/split remote-url #"\."))]
props {:attachment attachment :preview-url preview-url :remote-url remote-url :ext ext}]
(case (:type attachment) (case (:type attachment)
"image" [img-attachment props] "image" [:img {:src preview-url
"video" [video-attachment props] :srcset (str preview-url ", " remote-url)
"gifv" [gifv-attachment props] :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"] [:div [:strong "Unsupported attachment"]
[debug 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]}] (defn post [{:keys [post]}]
; TODO: handle (:sensitive post) ; TODO: handle (:sensitive post)
[:article.post [:article.post
@ -464,11 +432,11 @@
"Returns a promise which resolves to the smallest or largest internal post id. "Returns a promise which resolves to the smallest or largest internal post id.
This is useful to continue interrupted paginated requests." This is useful to continue interrupted paginated requests."
[max-or-min] [max-or-min]
(let [posts-cursor (db/open-cursor ::db/posts ::db/all {:index ::db/post-internal-id (->> (db/open-cursor! ::db/posts ::db/all {:index ::db/post-internal-id
:direction (if (= max-or-min :min) :asc :desc)})] :direction (if (= max-or-min :min) :asc :desc)})
(db/first-result (keep (j/get :internal_id)) posts-cursor))) (db/first-result (keep (j/get :internal_id)))))
(defn- fetch-more-posts! [e] (defn fetch-more-posts! [e]
(.preventDefault e) (.preventDefault e)
(. (promise-all [(fetch-application-settings) (internal-post-id :min) (internal-post-id :max)]) (. (promise-all [(fetch-application-settings) (internal-post-id :min) (internal-post-id :max)])
(then (fn [[application min-id max-id]] (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 "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."]]]) [: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 [] (defn app []
(let [section (:section @state) (let [section (:section @state)
@ -567,11 +535,11 @@
(defn- convert-internal-ids! [] (defn- convert-internal-ids! []
; TODO figure out when we can remove this again ; TODO figure out when we can remove this again
(let [cursor (db/open-cursor ::db/posts ::db/all) (. (->> (db/open-cursor! ::db/posts ::db/all)
result (db/transduce-cursor (filter (comp string? (j/get :internal_id))) cursor)] (db/transduce-cursor (filter (comp string? (j/get :internal_id)))))
(.then result (fn [rows] (then (fn [rows]
(doseq [row rows] (doseq [row rows]
(db/put! ::db/posts (j/update! row :internal_id parse-long))))))) (db/put! ::db/posts (j/update! row :internal_id parse-long)))))))
;; go go go ;; go go go

View file

@ -1,5 +1,4 @@
(ns computersandblues.lodestone.database (ns computersandblues.lodestone.database)
(:refer-clojure :exclude [count get]))
(defonce +db+ (atom nil)) (defonce +db+ (atom nil))
@ -73,15 +72,15 @@
request (.put store (clj->js object))] request (.put store (clj->js object))]
(promisify request)))) (promisify request))))
(defn get (defn get!
([store-id k] (get @+db+ store-id k)) ([store-id k] (get! @+db+ store-id k))
([db store-id k] ([db store-id k]
(let [store (open-store db store-id "readonly") (let [store (open-store db store-id "readonly")
request (.get store k)] request (.get store k)]
(promisify request)))) (promisify request))))
(defn get-all (defn get-all!
([store-id key-range] (get-all @+db+ store-id key-range)) ([store-id key-range] (get-all! @+db+ store-id key-range))
([db store-id key-range] ([db store-id key-range]
(let [store (open-store db store-id "readonly") (let [store (open-store db store-id "readonly")
request (.getAll store key-range)] request (.getAll store key-range)]
@ -95,23 +94,23 @@
(promisify request)))) (promisify request))))
(defn delete! (defn delete!
([store-id k] (get @+db+ store-id k)) ([store-id k] (get! @+db+ store-id k))
([db store-id k] ([db store-id k]
(let [store (open-store db store-id "readwrite") (let [store (open-store db store-id "readwrite")
request (.delete store k)] request (.delete store k)]
(promisify request)))) (promisify request))))
(defn count (defn count!
([store-id] (count @+db+ store-id)) ([store-id] (count! @+db+ store-id))
([db store-id] ([db store-id]
(let [store (open-store db store-id "readonly") (let [store (open-store db store-id "readonly")
request (.count store)] request (.count store)]
(promisify request)))) (promisify request))))
(defn open-cursor (defn open-cursor!
([store-id key-range] (open-cursor @+db+ store-id key-range {:direction :asc})) ([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)) ([store-id key-range opts] (open-cursor! @+db+ store-id key-range opts))
([db store-id key-range {:keys [direction index]}] ([db store-id key-range {:keys [direction index]}]
(let [store (open-store db store-id "readonly") (let [store (open-store db store-id "readonly")
key-range (if (= key-range ::all) key-range (if (= key-range ::all)
@ -161,21 +160,14 @@
(.continue cursor)))) (.continue cursor))))
(resolve @result))))))))) (resolve @result)))))))))
(defn first-result (defn first-result [xform cursor-req]
"Given a cursor, will return a promise that resolves to the first result. (transduce-cursor (comp xform (take 1)) (fn [_ x] x) cursor-req))
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)))
(comment (comment
(let [re (js/RegExp. "user" "i")] (let [re (js/RegExp. "user" "i")]
(do (print "starting…" (js/Date.)) (do (print "starting…" (js/Date.))
(-> (open-cursor ::posts ::all) (-> (open-cursor! ::posts ::all)
(transduce-cursor (comp (filter #(re-find re (.-content %))) (transduce-cursor (comp (filter #(re-find re (.-content %)))
(take 50) (take 50)
(map #(js->clj % :keywordize-keys true)))) (map #(js->clj % :keywordize-keys true))))