diff --git a/src/cljs/commiteth/bounties.cljs b/src/cljs/commiteth/bounties.cljs index 173d7e5..83aa4f3 100644 --- a/src/cljs/commiteth/bounties.cljs +++ b/src/cljs/commiteth/bounties.cljs @@ -4,7 +4,9 @@ [commiteth.common :refer [moment-timestamp issue-url]] [commiteth.handlers :as handlers] - [commiteth.db :as db])) + [commiteth.db :as db] + [commiteth.ui-model :as ui-model] + [commiteth.subscriptions :as subs])) (defn bounty-item [bounty] @@ -45,102 +47,172 @@ [:div.ui.tiny.circular.image [:img {:src avatar-url}]]]])) -(defn bounties-filter-tooltip [& content] +(defn bounties-filter-tooltip [content] [:div.open-bounties-filter-element-tooltip content]) -(defn bounties-filter-tooltip-value-input [label tooltip-open?] +(defn bounties-filter-tooltip-value-input [label tooltip-open? opts] [:div.open-bounties-filter-element-tooltip-value-input-container [:div.:input.open-bounties-filter-element-tooltip-value-input-label label] [:input.open-bounties-filter-element-tooltip-value-input - {:type "range" - :min 0 - :max 1000 - :step 10 - :on-focus #(reset! tooltip-open? true)}]]) + {:type "range" + :min (:min opts) + :max (:max opts) + :step (:step opts) + :value (:current-val opts) + :on-change (when-let [f (:on-change-val opts)] + #(-> % .-target .-value int f)) + :on-focus #(reset! tooltip-open? true)}]]) -(defn bounties-filter-tooltip-value [tooltip-open?] - [:div - "$0 - $1000+" - [bounties-filter-tooltip-value-input "Min" tooltip-open?] - [bounties-filter-tooltip-value-input "Max" tooltip-open?]]) +(defn bounties-filter-tooltip-value [current-filter-value tooltip-open?] + (let [default-min 0 + default-max 1000 + common-range-opts {:min default-min :max default-max} + current-min (or (first current-filter-value) default-min) + current-max (or (second current-filter-value) default-max) + on-change-fn (fn [min-val max-val] + (rf/dispatch [::handlers/set-open-bounty-filter-type + ::ui-model/bounty-filter-type|value + [(min min-val (dec default-max)) + (max max-val (inc default-min))]])) + on-min-change-fn (fn [new-min] + (let [new-max (max current-max (inc new-min))] + (on-change-fn new-min new-max))) + on-max-change-fn (fn [new-max] + (let [new-min (min current-min (dec new-max))] + (on-change-fn new-min new-max)))] + [:div + "$0 - $1000+" + [bounties-filter-tooltip-value-input "Min" tooltip-open? (merge common-range-opts + {:current-val current-min + :on-change-val on-min-change-fn})] + [bounties-filter-tooltip-value-input "Max" tooltip-open? (merge common-range-opts + {:current-val current-max + :on-change-val on-max-change-fn})]])) -(defn bounties-filter-tooltip-currency [tooltip-open?] +(defn bounties-filter-tooltip-currency [current-filter-value tooltip-open?] [:div.open-bounties-filter-list (for [t ["ETH" "SNT"]] - [:div.open-bounties-filter-list-option-checkbox - [:label - [:input - {:type "checkbox" - :on-focus #(reset! tooltip-open? true)}] - [:div.text t]]])]) + (let [active? (and current-filter-value (current-filter-value t))] + [:div.open-bounties-filter-list-option-checkbox + [:label + {:on-click #(rf/dispatch [::handlers/set-open-bounty-filter-type + ::ui-model/bounty-filter-type|currency + (cond + (and active? (= #{t} current-filter-value)) nil + active? (disj current-filter-value t) + :else (into #{t} current-filter-value))])} + [:input + {:type "checkbox" + :on-focus #(reset! tooltip-open? true)}] + [:div.text t]]]))]) -(defn bounties-filter-tooltip-date [] +(defn bounties-filter-tooltip-date [current-filter-value tooltip-open?] [:div.open-bounties-filter-list - (for [t ["Last week" "Last month" "Last 3 months"]] + (for [[option-type option-text] ui-model/bounty-filter-type-date-options-def] + ^{:key (str option-type)} [:div.open-bounties-filter-list-option - t])]) + (merge {:on-click #(do (rf/dispatch [::handlers/set-open-bounty-filter-type + ::ui-model/bounty-filter-type|date + option-type]) + (reset! tooltip-open? false))} + (when (= option-type current-filter-value) + {:class "active"})) + option-text])]) -(defn bounties-filter-tooltip-owner [tooltip-open?] +(defn bounties-filter-tooltip-owner [current-filter-value tooltip-open?] [:div.open-bounties-filter-list (for [t ["status-im" "aragon"]] - [:div.open-bounties-filter-list-option-checkbox - [:label - [:input - {:type "checkbox" - :on-focus #(reset! tooltip-open? true)}] - [:div.text t]]])]) + (let [active? (and current-filter-value (current-filter-value t))] + [:div.open-bounties-filter-list-option-checkbox + [:label + {:on-click #(rf/dispatch [::handlers/set-open-bounty-filter-type + ::ui-model/bounty-filter-type|owner + (cond + (and active? (= #{t} current-filter-value)) nil + active? (disj current-filter-value t) + :else (into #{t} current-filter-value))])} + [:input + {:type "checkbox" + :on-focus #(reset! tooltip-open? true) + :checked (when active? "checked")}] + [:div.text t]]]))]) -(defn bounties-filter [name tooltip-content] +(defn- tooltip-view-for-filter-type [filter-type] + (condp = filter-type + ::ui-model/bounty-filter-type|value bounties-filter-tooltip-value + ::ui-model/bounty-filter-type|currency bounties-filter-tooltip-currency + ::ui-model/bounty-filter-type|date bounties-filter-tooltip-date + ::ui-model/bounty-filter-type|owner bounties-filter-tooltip-owner)) + +(defn bounty-filter-view [filter-type current-filter-value] (let [open? (r/atom false)] - (fn [name tooltip-content] + (fn [filter-type current-filter-value] [:div.open-bounties-filter-element-container {:tab-index 0 :on-focus #(reset! open? true) :on-blur #(reset! open? false)} [:div.open-bounties-filter-element {:on-mouse-down #(swap! open? not) - :class (when @open? "open-bounties-filter-element-active")} - name] + :class (when (or current-filter-value @open?) + "open-bounties-filter-element-active")} + [:div.text + (if current-filter-value + (ui-model/bounty-filter-value->short-text filter-type current-filter-value) + (ui-model/bounty-filter-type->name filter-type))] + (when current-filter-value + [:img.remove + {:src "bounty-filter-remove.svg" + :tab-index 0 + :on-focus #(.stopPropagation %) + :on-mouse-down (fn [e] + (.stopPropagation e) + (rf/dispatch [::handlers/set-open-bounty-filter-type filter-type nil]) + (reset! open? false))}])] (when @open? [bounties-filter-tooltip - [tooltip-content open?]])]))) + [(tooltip-view-for-filter-type filter-type) current-filter-value open?]])]))) -(defn bounties-filters [] - [:div.open-bounties-filter - [bounties-filter "Value" bounties-filter-tooltip-value] - [bounties-filter "Currency" bounties-filter-tooltip-currency] - [bounties-filter "Date" bounties-filter-tooltip-date] - [bounties-filter "Owner" bounties-filter-tooltip-owner]]) +(defn bounty-filters-view [] + (let [current-filters (rf/subscribe [::subs/open-bounties-filters])] + [:div.open-bounties-filter + ; doall because derefs are not supported in lazy seqs: https://github.com/reagent-project/reagent/issues/18 + (doall + (for [filter-type ui-model/bounty-filter-types] + ^{:key (str filter-type)} + [bounty-filter-view + filter-type + (get @current-filters filter-type)]))])) (defn bounties-sort [] (let [open? (r/atom false)] (fn [] - (let [current-sorting (rf/subscribe [::db/bounty-sorting-type])] + (let [current-sorting (rf/subscribe [::subs/open-bounties-sorting-type])] [:div.open-bounties-sort {:tab-index 0 :on-blur #(reset! open? false)} [:div.open-bounties-sort-element {:on-click #(swap! open? not)} - (db/bounty-sorting-types @current-sorting) + (ui-model/bounty-sorting-types-def @current-sorting) [:div.icon-forward-white-box [:img {:src "icon-forward-white.svg"}]]] (when @open? [:div.open-bounties-sort-element-tooltip - (for [[sorting-type sorting-name] db/bounty-sorting-types] + (for [[sorting-type sorting-name] ui-model/bounty-sorting-types-def] + ^{:key (str sorting-type)} [:div.open-bounties-sort-type {:on-click #(do (reset! open? false) - (rf/dispatch [::handlers/set-bounty-sorting-type sorting-type]))} + (rf/dispatch [::handlers/set-open-bounties-sorting-type sorting-type]))} sorting-name])])])))) (defn bounties-list [open-bounties] [:div.ui.container.open-bounties-container [:div.open-bounties-header "Bounties"] [:div.open-bounties-filter-and-sort - [bounties-filters] + [bounty-filters-view] [bounties-sort]] (if (empty? open-bounties) [:div.view-no-data-container diff --git a/src/cljs/commiteth/db.cljs b/src/cljs/commiteth/db.cljs index e54bcd1..39e8f11 100644 --- a/src/cljs/commiteth/db.cljs +++ b/src/cljs/commiteth/db.cljs @@ -1,19 +1,19 @@ -(ns commiteth.db) - -(def bounty-sorting-types {::bounty-sorting-type|most-recent "Most recent" - ::bounty-sorting-type|lowest-value "Lowest value" - ::bounty-sorting-type|highest-value "Highest value" - ::bounty-sorting-type|owner "Owner"}) +(ns commiteth.db + (:require [commiteth.ui-model :as ui-model])) (def default-db - {:page :bounties - :user nil - :repos-loading? false - :repos {} - :activity-feed-loading? false - :open-bounties-loading? false - :open-bounties [] - :owner-bounties {} - :top-hunters [] - :activity-feed [] - ::bounty-sorting-type ::bounty-sorting-type|most-recent}) + {:page :bounties + :user nil + :repos-loading? false + :repos {} + :activity-feed-loading? false + :open-bounties-loading? false + :open-bounties [] + ::open-bounties-sorting-type ::ui-model/bounty-sorting-type|most-recent + ::open-bounties-filters {::ui-model/bounty-filter-type|value nil + ::ui-model/bounty-filter-type|currency nil + ::ui-model/bounty-filter-type|date nil + ::ui-model/bounty-filter-type|owner nil} + :owner-bounties {} + :top-hunters [] + :activity-feed []}) diff --git a/src/cljs/commiteth/handlers.cljs b/src/cljs/commiteth/handlers.cljs index b69f1ca..ef183be 100644 --- a/src/cljs/commiteth/handlers.cljs +++ b/src/cljs/commiteth/handlers.cljs @@ -455,6 +455,11 @@ (assoc db :user-dropdown-open? false))) (reg-event-db - ::set-bounty-sorting-type + ::set-open-bounties-sorting-type (fn [db [_ sorting-type]] - (assoc db ::db/bounty-sorting-type sorting-type))) + (assoc db ::db/open-bounties-sorting-type sorting-type))) + +(reg-event-db + ::set-open-bounty-filter-type + (fn [db [_ filter-type filter-value]] + (assoc-in db [::db/open-bounties-filters filter-type] filter-value))) diff --git a/src/cljs/commiteth/subscriptions.cljs b/src/cljs/commiteth/subscriptions.cljs index c6d4340..f9ef6b5 100644 --- a/src/cljs/commiteth/subscriptions.cljs +++ b/src/cljs/commiteth/subscriptions.cljs @@ -83,6 +83,11 @@ (:user-dropdown-open? db))) (reg-sub - ::db/bounty-sorting-type + ::open-bounties-sorting-type (fn [db _] - (::db/bounty-sorting-type db))) + (::db/open-bounties-sorting-type db))) + +(reg-sub + ::open-bounties-filters + (fn [db _] + (::db/open-bounties-filters db))) diff --git a/src/cljs/commiteth/ui_model.cljs b/src/cljs/commiteth/ui_model.cljs new file mode 100644 index 0000000..e274297 --- /dev/null +++ b/src/cljs/commiteth/ui_model.cljs @@ -0,0 +1,44 @@ +(ns commiteth.ui-model) + +;;;; bounty sorting types + +(def bounty-sorting-types-def {::bounty-sorting-type|most-recent "Most recent" + ::bounty-sorting-type|lowest-value "Lowest value" + ::bounty-sorting-type|highest-value "Highest value" + ::bounty-sorting-type|owner "Owner"}) + +;;;; bounty filter types + +(def bounty-filter-types-def {::bounty-filter-type|value "Value" + ::bounty-filter-type|currency "Currency" + ::bounty-filter-type|date "Date" + ::bounty-filter-type|owner "Owner"}) + +(def bounty-filter-types (keys bounty-filter-types-def)) + +(defn bounty-filter-type->name [filter-type] + (bounty-filter-types-def filter-type)) + +(def bounty-filter-type-date-options-def {::bounty-filter-type-date-option|last-week "Last week" + ::bounty-filter-type-date-option|last-month "Last month" + ::bounty-filter-type-date-option|last-3-months "Last 3 months"}) + +(def bounty-filter-type-date-options (keys bounty-filter-type-date-options-def)) + +(defn bounty-filter-type-date-option->name [option] + (bounty-filter-type-date-options-def option)) + +(defn bounty-filter-value->short-text [filter-type filter-value] + (cond + (= filter-type ::bounty-filter-type|date) + (bounty-filter-type-date-option->name filter-value) + + (#{::bounty-filter-type|owner + ::bounty-filter-type|currency} filter-type) + (str (bounty-filter-type->name filter-type) " (" (count filter-value) ")") + + (= filter-type ::bounty-filter-type|value) + (str "$" (first filter-value) "-$" (second filter-value)) + + :else + (str filter-type " with val " filter-value))) diff --git a/src/less/style.less b/src/less/style.less index 4e4b05e..669db74 100644 --- a/src/less/style.less +++ b/src/less/style.less @@ -430,18 +430,29 @@ font-weight: 500; line-height: 1.0; color: #8d99a4; - padding: 8px 12px; + //padding: 8px 12px; border-radius: 8px; border: solid 1px rgba(151, 151, 151, 0.2); margin-right: 10px; position: relative; + display: flex; + + .text { + margin: 8px 12px; + } + + .remove { + margin-left: -6px; + margin-right: 5px; + } } .open-bounties-filter-element:hover { cursor: pointer; } - .open-bounties-filter-element-active { + .open-bounties-filter-element-active + { background-color: #57a7ed; color: #ffffff; } @@ -582,6 +593,11 @@ border: solid 1px rgba(151, 151, 151, 0.2); } + .open-bounties-filter-list-option.active { + background-color: #57a7ed; + color: #ffffff; + } + .open-bounties-filter-list-option:not(:first-child) { margin-top: 8px; }