From 934c499e93f7c4740b57d8a7e8abda0a99343633 Mon Sep 17 00:00:00 2001 From: arne Date: Tue, 18 Nov 2025 21:47:08 +0100 Subject: [PATCH] Add logout buttons --- src/computersandblues/lodestone/app.cljs | 75 ++++++++++++++----- src/computersandblues/lodestone/database.cljs | 5 ++ 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/computersandblues/lodestone/app.cljs b/src/computersandblues/lodestone/app.cljs index 3b8baa2..af11ded 100644 --- a/src/computersandblues/lodestone/app.cljs +++ b/src/computersandblues/lodestone/app.cljs @@ -6,6 +6,17 @@ [computersandblues.lodestone.database :as db] [computersandblues.lodestone.match :refer [query->matching-fn]])) +(def posts-init-state + {:query nil + ; TODO: pagination + ; :page 0 + ; :max-displayed-id nil + ; :order [:date :desc] + :per-page 50 + :loading #{} + :total 0 + :displayed-posts []}) + (defonce state (r/atom {:root nil :section :login @@ -13,15 +24,7 @@ :section/login {} ; TODO: Handle other lists - :section/posts {:query nil - :per-page 50 - :loading #{} - ; TODO: pagination - ; :page 0 - ; :max-displayed-id nil - ; :order [:date :desc] - :total 0 - :displayed-posts []}})) + :section/posts posts-init-state})) ; TODO Ensure that cached data is up to date ; TODO Manually fetch older / newer favorites @@ -266,11 +269,11 @@ (defn search [] [:input {:placeholder "Start typing to search…" + :type "search" :on-change (fn [e] (let [query (.. e -target -value)] (swap! state assoc-in [:section/posts :query] (if (str/blank? query) nil query)) - (refresh-displayed-posts! (:section/posts @state)))) - :value (-> @state :section/posts :query)}]) + (refresh-displayed-posts! (:section/posts @state))))}]) (defn- debug "Implements a lazy pretty-printer for whatever is passed in as `obj`. The @@ -288,7 +291,7 @@ [:pre @pprinted]]))) (defn user [{:keys [user]}] - (:username user)) + [:em (:username user)]) (defn- list-accounts [accounts] [:<> @@ -329,11 +332,13 @@ [:nav [:ul.controls [:li.control-element.url - [:a {:href (:url post) :target "_blank"} "↗ Open original post"]] + [:a.control-button {:href (:url post) :target "_blank"} "↗ Open original post"]] [:li.control-element.clipboard - [:a {:href "#" :on-click (fn [e] - (.preventDefault e) - (js/navigator.clipboard.writeText (:url post)))} "◎ Copy URL to clipboard"]]]] + [:a.control-button + {:href "#" + :on-click (fn [e] + (.preventDefault e) + (js/navigator.clipboard.writeText (:url post)))} "◎ Copy URL to clipboard"]]]] #_[debug post]]) (defn- refresh-displayed-posts! @@ -365,6 +370,15 @@ (debounced-refresh! (:section/posts @state)))}] (fetch-favorites! (merge defaults opts)))) +(defn- disconnect-account! [e] + (.preventDefault e) + (when (js/confirm "Are you sure? This will log you out and clear your local cache.") + (. (js/Promise.all #js [(db/clear! ::db/posts) + (db/clear! ::db/application)]) + (then (fn [_] + (swap! state #(-> (assoc % :section :login) + (assoc :section/posts posts-init-state)))))))) + (defn posts-section [{:keys [posts]}] (let [{:keys [per-page query total displayed-posts loading]} posts] [:section.posts @@ -374,11 +388,14 @@ (str "Loaded " total " posts" (when (or query (> total per-page)) (str ", displaying " (count displayed-posts) (when query " matches"))))] - [:div.search-form + [:section.search-form [search] (when (seq loading) " …") #_(cond (= api-state :loading) " …" - (= api-state :error) " API Error!")]] + (= api-state :error) " API Error!")] + [:section.buttons + [:a.control-button {:href "#" + :on-click disconnect-account!} "⊘ Disconnect account"]]] [:ul.results (map-indexed (fn [idx p] ^{:key idx} [:li.result [post {:post p}]]) displayed-posts)] #_[:div.load-buttons @@ -396,13 +413,33 @@ (seq (:body response)))}))} "Load all"]]])) +;; help section + +(defn help-section [] + [:section.help + [:h2 "Help"] + [:h3 "Search Syntax"] + [:ul.search-syntax + [:li "Three are several places that we take into account when looking for query results" + [:ul + [:li "Post content"] + [:li "Creator of the post"] + [:li "Any mentioned account"]]] + [:li "Upper- and lowercase does not make a difference."] + [:li "All words are matched separately. They do not have to appear in the post in the same order, but they all have to appear."] + [: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 toger + (defn app [] (let [section (:section @state) posts (:section/posts @state)] [:main#app (case section :login [login-section] - :posts [posts-section {:posts posts}])])) + :posts [posts-section {:posts posts}] + :help [help-section])])) ;; database diff --git a/src/computersandblues/lodestone/database.cljs b/src/computersandblues/lodestone/database.cljs index 89faecc..0ec3905 100644 --- a/src/computersandblues/lodestone/database.cljs +++ b/src/computersandblues/lodestone/database.cljs @@ -75,6 +75,11 @@ request (.getAll store key-range)] (promisify request))) +(defn clear! [store-id] + (let [store (open-store @+db+ store-id "readwrite") + request (.clear store)] + (promisify request))) + (defn open-cursor! ([store-id key-range] (open-cursor! store-id key-range "next")) ([store-id key-range direction]