Compare commits
No commits in common. "58d709f7be1272d9d9bb545a13b5d45834275fd6" and "c35eb6d11e08f46548511f077a403cdd02f1df76" have entirely different histories.
58d709f7be
...
c35eb6d11e
3 changed files with 51 additions and 96 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue