diff --git a/CHANGELOG.md b/CHANGELOG.md index e2407206..cd75f6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* a "clear" button to the search addons tab that removes all search filters, including search terms. * new column for installed addons "starred" that will add an installed addon to the 'user-catalogue'. - star button disabled when addon is being ignored or isn't matched against the catalogue. * new column for installed addons "size" with the total size of the addon on disk, including any grouped addons. @@ -43,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* minor cosmetic fix. the buttons on the search tab are now a consistent height. * possible cache stampede fetching strongbox release info. A lock is now acquired to ensure checks happen sequentially. - it was possible for the GUI to fire off many requests to Github simultaneously, bypassing cache and overwriting each other. diff --git a/TODO.md b/TODO.md index 27ecc0f7..85937ee9 100644 --- a/TODO.md +++ b/TODO.md @@ -63,8 +63,19 @@ see CHANGELOG.md for a more formal list of changes by release - import it with find-addon? - done +* search, a 'clear' button + - resets favourited, search input, tags, etc + - done + +* search, buttons are slightly different sizes + - input star and dropdown are a few pixels shorter than the buttons on either side + - done + ## todo +* user-catalogue + - switch to log window to see progress when refresh-catalogue triggered + * user catalogue, schedule refreshes - ensure the user catalogue doesn't get too stale and perform an update in the background if it looks like it's very old - update README @@ -76,9 +87,6 @@ see CHANGELOG.md for a more formal list of changes by release * user catalogue, refreshing may guarantee exceeding github limit. - if we know this, add a warning? refuse? -* search, a 'clear' button - - resets favourited, search input, tags, etc - * 'core/state :db-stats', seems like a nice idea to put more information here - known-hosts - num addons per-host diff --git a/src/strongbox/cli.clj b/src/strongbox/cli.clj index bf2067b0..571bfdd6 100644 --- a/src/strongbox/cli.clj +++ b/src/strongbox/cli.clj @@ -197,16 +197,10 @@ (doseq [path path-list] (core/state-bind path listener)))) -(defn-spec reset-search-navigation nil? - "returns the search results to page 1" - [] - (swap! core/state assoc-in [:search :page] 0) - nil) - (defn-spec search-add-filter nil? "adds a new filter to the search `filter-by` state." [filter-by :search/filter-by, val any?] - (reset-search-navigation) + (core/reset-search-navigation) (case filter-by :source (swap! core/state assoc-in [:search :filter-by filter-by] (utils/nilable val)) :tag (swap! core/state update-in [:search :filter-by filter-by] conj val) @@ -216,17 +210,23 @@ (defn-spec search-rm-filter nil? "removes a filter from the search `filter-by` state." [filter-by :search/filter-by, val any?] - (reset-search-navigation) + (core/reset-search-navigation) (swap! core/state update-in [:search :filter-by filter-by] clojure.set/difference #{val}) nil) (defn-spec search-toggle-filter nil? "toggles boolean filters on and off" [filter-by :search/filter-by] - (reset-search-navigation) + (core/reset-search-navigation) (swap! core/state update-in [:search :filter-by filter-by] not) nil) +(defn-spec clear-search! nil? + "resets the search state to defaults and jogs the search results" + [] + (core/reset-search-state!) + (bump-search)) + ;; (defn-spec change-catalogue nil? @@ -708,7 +708,7 @@ "refresh the details of an individual `addon` in the user catalogue, optionally writing the updated catalogue to file." [addon :addon/summary, db :addon/summary-list] (logging/with-addon addon - (info "refreshing user-catalogue addon:" (:name addon)) + (info "refreshing user-catalogue entry") (try (let [{:keys [source source-id url]} addon refreshed-addon (core/db-addon-by-source-and-source-id db source source-id) @@ -716,13 +716,11 @@ refreshed-addon (or refreshed-addon (find-addon url attempt-dry-run?))] (if-not refreshed-addon - (warn (format "failed to refresh details of addon in user-catalogue: couldn't find addon '%s' in catalogue or online" - (:name addon))) + (warn "failed to refresh user-catalogue entry as the addon was not found in the catalogue or online") (core/add-user-addon! refreshed-addon))) (catch Exception e - (error (format "an unexpected error happened while updating the details for '%s' in the user-catalogue: %s" - (:name addon) (.getMessage e))))))) + (error (format "an unexpected error happened while refreshing the user-catalogue entry: %s" (.getMessage e))))))) (defn-spec refresh-user-catalogue nil? "refresh the details of all addons in the user catalogue, writing the updated catalogue to file once." diff --git a/src/strongbox/core.clj b/src/strongbox/core.clj index a33fe009..d39f4be5 100644 --- a/src/strongbox/core.clj +++ b/src/strongbox/core.clj @@ -923,12 +923,24 @@ (partition-all cap (seque 100 (filter match-fn db)))))))) (defn-spec empty-search-results nil? - "empties app state of search results. - this is to clear out anything between catalogue reloads." + "empties app state of search *results* but not filters. + this handles catalogue reloads but preserves user filtering." [] (swap! state update-in [:search] merge (select-keys -search-state-template [:page :results :selected-results-list])) nil) +(defn-spec reset-search-navigation nil? + "resets the search results to page 1" + [] + (swap! state assoc-in [:search :page] 0) + nil) + +(defn-spec reset-search-state! nil? + "replaces search state with default settings." + [] + (swap! state update-in [:search] merge -search-state-template) + nil) + (defn-spec db-load-user-catalogue nil? "loads the user catalogue into state, but only if it hasn't already been loaded." [] diff --git a/src/strongbox/jfx.clj b/src/strongbox/jfx.clj index 0987951f..889a59b3 100644 --- a/src/strongbox/jfx.clj +++ b/src/strongbox/jfx.clj @@ -510,7 +510,14 @@ "#search-addons " {"#search-install-button" - {:-fx-min-width "90px"} + {:-fx-min-width "90px" + :-fx-padding ".4em 1em"} + + "#search-text-field " + {:-fx-min-width "100px" + :-fx-padding ".4em .5em" + :-fx-background-radius "0" + :-fx-text-fill (colour :table-font-colour)} "#search-random-button" {:-fx-min-width "80px"} @@ -518,7 +525,7 @@ "#search-user-catalogue-button" {:-fx-font-weight "bold" :-fx-font-size "1.2em" - :-fx-padding "2 7 " + :-fx-padding "3 7 " ".starred" {:-fx-text-fill (colour :star-starred) ;; the yellow of the star doesn't stand out from the gray gradient behind it. @@ -527,16 +534,15 @@ :-fx-stroke-width ".2" :-fx-effect (str "dropshadow( gaussian , " (colour :star-starred) " , 10, 0.0 , 0 , 0 )")}}} + "#search-addon-hosts-list" + {:-fx-pref-height "2em"} + "#search-prev-button" {:-fx-min-width "80px"} "#search-next-button" {:-fx-min-width "70px"} - "#search-text-field " - {:-fx-min-width "100px" - :-fx-text-fill (colour :table-font-colour)} - "#search-selected-tag-bar" {:-fx-padding "0 0 10 10" :-fx-spacing "10" @@ -2088,6 +2094,7 @@ :title "addon host" :items known-host-list :show-checked-count false + :id "search-addon-hosts-list" :on-checked-items-changed (fn [val] (cli/search-add-filter :source val)) :disable disable-host-selector?} @@ -2097,6 +2104,22 @@ ;; :text "random" ;; :on-action (handler cli/random-search)} + {:fx/type :button + :id "search-clear-button" + :text "clear" + :on-action (fn [_] + (when-let [ccb (first (select "#search-addon-hosts-list"))] + (.clearChecks (.getCheckModel ccb))) + (cli/clear-search!)) + :disable (let [ss (dissoc search-state :results) + as (dissoc core/-search-state-template :results)] + (if (clojure.string/blank? (:term ss)) + ;; we use alternating `" "` and `nil` to 'bump' search results. + ;; if we have one of those, don't consider the search term. + (= (dissoc ss :term) + (dissoc as :term)) + (= ss as)))} + {:fx/type :h-box :id "spacer" :h-box/hgrow :ALWAYS} diff --git a/test/strongbox/core_test.clj b/test/strongbox/core_test.clj index 410af24c..3cfefd8f 100644 --- a/test/strongbox/core_test.clj +++ b/test/strongbox/core_test.clj @@ -1465,18 +1465,78 @@ expected-empty-search-state (assoc core/-search-state-template :term search-term)] (with-global-fake-routes-in-isolation fake-routes (with-running-app + + ;; we have 4 search results to start with + (cli/bump-search) + (Thread/sleep 50) ;; searching happens in the background + (is (= 4 (-> (core/get-state :search) :results first count))) + + ;; search for 'a' + ;; we should have three results: + ;; 1. "*A* New Simple Percent" + ;; 2. "Skins for *A*ddOns" + ;; 3. "Chinchill*a*" (cli/search search-term) - ;; searching happens in the background (Thread/sleep 50) - ;; we have one search result from a catalogue of 4 addons - (is (= 1 (-> (core/get-state :search) :results count))) + (is (= 3 (-> (core/get-state :search) :results first count))) + ;; empty the stale search state (core/empty-search-results) + (Thread/sleep 50) (is (= expected-empty-search-state (core/get-state :search))) + ;; do the search again without specifying a search term + ;; we should have three search results again (cli/bump-search) (Thread/sleep 50) - (is (= 1 (-> (core/get-state :search) :results count)))))))) + (is (= 3 (-> (core/get-state :search) :results first count)))))))) + +(deftest reset-search-state + (testing "search state can be cleared entirely" + (let [dummy-catalogue (slurp (fixture-path "catalogue--v2.json")) + fake-routes {"https://raw.githubusercontent.com/ogri-la/strongbox-catalogue/master/short-catalogue.json" + {:get (fn [req] {:status 200 :body dummy-catalogue})}} + search-term "a"] + (with-global-fake-routes-in-isolation fake-routes + (with-running-app + (cli/bump-search) + + ;; we have four results initially + (Thread/sleep 50) + (is (= 4 (-> (core/get-state :search) :results first count))) + + ;; search for 'a' + ;; we should have three results: + ;; 1. "*A* New Simple Percent" + ;; 2. "Skins for *A*ddOns" + ;; 3. "Chinchill*a*" + (cli/search search-term) + (Thread/sleep 50) + (is (= 3 (-> (core/get-state :search) :results first count))) + + ;; filter by host and we still have one search result + (cli/search-add-filter :source ["curseforge"]) + (Thread/sleep 50) + ;;(is (= {} (core/get-state :search))) + (is (= 1 (-> (core/get-state :search) :results first count))) + (is (= ["curseforge"] (core/get-state :search :filter-by :source))) + + ;; filter by tag and we still have one search result + (cli/search-add-filter :tag :unit-frames) + (Thread/sleep 50) + (is (= 1 (-> (core/get-state :search) :results count))) + (is (= #{:unit-frames} (core/get-state :search :filter-by :tag))) + + (core/reset-search-state!) + (Thread/sleep 50) + + ;; we have 4 search results from a catalogue of 4 addons + (is (= 4 (-> (core/get-state :search) :results first count))) + + ;; and all the filters have been removed + (is (nil? (core/get-state :search :term))) + (is (empty? (core/get-state :search :filter-by :tag))) + (is (nil? (core/get-state :search :filter-by :source)))))))) (deftest -download-strongbox-release (testing "standard github response for strongbox release data can be parsed and the release version extracted"