From 0914fbd53a18776bc056cdddd971e40bbd3a327a Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 09:09:17 +0300 Subject: [PATCH 01/44] Add API for card search --- .../src/clj/mtg_pairings_server/api/card.clj | 14 +++++++++++++ .../src/clj/mtg_pairings_server/api/http.clj | 2 ++ .../clj/mtg_pairings_server/service/card.clj | 21 +++++++++++++++++++ .../src/clj/mtg_pairings_server/sql_db.clj | 4 ++++ .../src/clj/mtg_pairings_server/util/sql.clj | 7 ++++++- 5 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj new file mode 100644 index 0000000..c5ff155 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj @@ -0,0 +1,14 @@ +(ns mtg-pairings-server.api.card + (:require [compojure.api.sweet :refer :all] + [schema.core :as s] + [mtg-pairings-server.service.card :refer :all] + [mtg-pairings-server.util.schema :refer :all] + [mtg-pairings-server.util :refer [response]])) + +(defroutes card-routes + (GET "/search" [] + :query-params [prefix :- s/Str + format :- (s/enum :standard :modern :legacy)] + :return [s/Str] + :summary "Hae kortteja nimen alkuosalla" + (response (search prefix format)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj index 1245ee1..4638840 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj @@ -2,6 +2,7 @@ (:require [compojure.api.sweet :refer :all] [compojure.api.exception :as ex] [config.core :refer [env]] + [mtg-pairings-server.api.card :refer [card-routes]] [mtg-pairings-server.api.player :refer [player-routes]] [mtg-pairings-server.api.tournament :refer [tournament-routes]] [mtg-pairings-server.middleware.error :refer [request-validation-error-handler sql-error-handler]] @@ -15,6 +16,7 @@ :spec "/swagger.json" :data {:info {:title "WER pairings backend API"}}}) (context "/api" [] + (context "/card" [] card-routes) (context "/tournament" [] tournament-routes) (context "/player" [] player-routes) (GET "/client-version" [] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj new file mode 100644 index 0000000..1a0bb62 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj @@ -0,0 +1,21 @@ +(ns mtg-pairings-server.service.card + (:require [korma.core :as sql] + [mtg-pairings-server.sql-db :as db] + [mtg-pairings-server.util.sql :refer [like]] + [clojure.string :as str])) + +(defn search [prefix format] + (let [name-param (-> prefix + (str/replace "%" "") + (str/lower-case) + (str \%)) + result (cond-> (-> (sql/select* db/card) + (sql/fields :name) + (sql/where {:lowername [like name-param]}) + (sql/order :lowername :asc) + (sql/limit 10)) + (= :standard format) (sql/where {:standard true}) + (= :modern format) (sql/where {:modern true}) + (= :legacy format) (sql/where {:legacy true}) + true (sql/exec))] + (map :name result))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index 1005dd7..89b9bc0 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -152,3 +152,7 @@ {:fk :pod}) (sql/belongs-to team {:fk :team})) + +(sql/defentity card + (sql/table :trader_card) + (sql/pk :id)) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj index bb63a14..a001cfc 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj @@ -1,6 +1,7 @@ (ns mtg-pairings-server.util.sql (:require [korma.core :as sql] - [korma.db :as db])) + [korma.db :as db] + [korma.sql.engine :as eng])) (defn unique-or-nil [results] @@ -49,3 +50,7 @@ sql/exec)] (assert (= count# 1) (str "Expected one deleted row, got " count#)) count#))) + +(defn like + [k v] + (eng/infix k "LIKE" v)) From b5bbfc5c79366c8b13b09f0019b608f7b9794b86 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 09:41:30 +0300 Subject: [PATCH 02/44] Autosuggest component --- mtg-pairings-server/project.clj | 2 + .../components/autosuggest.cljs | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index 2f430c6..57a74e9 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -88,6 +88,7 @@ :prod {:source-paths ["env/prod/cljs"]} :provided {:dependencies [[org.clojure/clojurescript "1.10.520"] [reagent "0.8.1"] + [cljs-http "0.1.46"] [com.google.errorprone/error_prone_annotations "2.3.3"] [com.google.code.findbugs/jsr305 "3.0.2"] [com.bhauman/figwheel-main "0.2.0" :exclusions [org.clojure/clojurescript]] @@ -103,6 +104,7 @@ [cljs-react-material-ui "0.2.50" :exclusions [org.clojure/clojurescript]] [cljsjs/prop-types "15.6.2-0"] [cljsjs/rc-slider "8.6.1-0"] + [cljsjs/react-autosuggest "9.4.3-0"] [garden "1.3.6"]]} :uberjar {:source-paths ["env/prod/cljs"] :main mtg-pairings-server.main diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs new file mode 100644 index 0000000..1c62d4a --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs @@ -0,0 +1,68 @@ +(ns mtg-pairings-server.components.autosuggest + (:require [reagent.core :as reagent :refer [atom]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [cljsjs.react-autosuggest] + [oops.core :refer [oget]])) + +(defn ^:private suggestion [suggestion opts] + (reagent/as-element + [ui/menu-item {:primary-text (reagent/as-element + [:div {:style {:white-space :nowrap + :text-overflow :ellipsis + :overflow :hidden}} + suggestion])}])) + +(defn ^:private suggestion-container [props] + (reagent/as-element + [ui/paper (js->clj (oget props "containerProps")) (oget props "children")])) + +(defn ^:private input [props] + (reagent/as-element + [ui/text-field (js->clj props)])) + +(def ^:private default-styles + {:container {:flex-grow 1 + :position "relative" + :width 256 + :display "inline-block"} + :suggestions-container-open {:position "absolute" + :z-index 10 + :left 0 + :right 0} + :suggestion {:display "block"} + :suggestions-list {:margin 0 + :padding 0 + :list-style-type "none"} + :suggestion-highlighted {:background-color "rgba(0, 0, 0, 0.1)"}}) + +(defn autosuggest [{:keys [on-suggestions-fetch-requested on-change] :as options}] + (let [value (atom "") + on-suggestions-fetch-requested (fn [event] + (on-suggestions-fetch-requested (oget event "value"))) + select-suggestion (fn [suggestion] + (reset! value "") + (on-change suggestion)) + on-suggestion-selected (fn [_ data] + (select-suggestion (oget data "suggestion")))] + (fn autosuggest-render [{:keys [suggestions styles on-suggestions-clear-requested] :as options}] + (let [input-props (merge (dissoc options :suggestions :styles :on-change :on-suggestions-fetch-requested :on-suggestions-clear-requested) + {:on-change (fn [event new-value] + (when (= "type" (oget new-value "method")) + (reset! value (oget event "target" "value")))) + :on-key-press (fn [event] + (when (and (= 13 (oget event "charCode")) + (some #{@value} @suggestions)) + (.preventDefault event) + (select-suggestion @value))) + :value @value})] + [:> js/Autosuggest {:suggestions @suggestions + :on-suggestions-fetch-requested on-suggestions-fetch-requested + :on-suggestions-clear-requested on-suggestions-clear-requested + :on-suggestion-selected on-suggestion-selected + :get-suggestion-value identity + :render-suggestions-container suggestion-container + :render-suggestion suggestion + :render-input-component input + :input-props input-props + :theme (merge default-styles styles)}])))) From c0cc0040075d248fd1855882791520a36c1b34f9 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 12:18:19 +0300 Subject: [PATCH 03/44] Decklist form --- .../src/clj/mtg_pairings_server/handler.clj | 1 + .../mtg_pairings_server/styles/decklist.clj | 21 ++ .../clj/mtg_pairings_server/styles/main.clj | 2 + .../clj/mtg_pairings_server/styles/table.clj | 2 +- .../src/cljs/mtg_pairings_server/core.cljs | 11 +- .../src/cljs/mtg_pairings_server/events.cljs | 42 ++-- .../mtg_pairings_server/pages/decklist.cljs | 186 ++++++++++++++++++ .../src/cljs/mtg_pairings_server/routes.cljs | 3 + .../mtg_pairings_server/subscriptions.cljs | 4 + 9 files changed, 249 insertions(+), 23 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 53bc369..863be0b 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -93,6 +93,7 @@ (GET "/tournaments/:id/organizer" [] (loading-page)) (GET "/tournaments/:id/organizer/menu" [] (loading-page)) (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) + (GET "/decklist" [] (loading-page)) (GET "/robots.txt" [] robots-txt) ws/routes) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj new file mode 100644 index 0000000..e10a54f --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -0,0 +1,21 @@ +(ns mtg-pairings-server.styles.decklist + (:require [garden.def :refer [defstyles]] + [garden.units :refer [px]] + [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) + +(defstyles styles + [:#decklist-submit + {:position :relative} + (when-desktop + [:& + {:max-width (px 880) + :margin "0 auto"}] + [:.deck-table-container + {:width "50%" + :display :inline-block + :vertical-align :top}]) + [:.deck-table + [:th.quantity :td.quantity + {:width (px 72)}] + [:th.error :td.error + {:width (px 48)}]]]) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj index e313644..a68968c 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj @@ -5,6 +5,7 @@ [garden.units :refer [px percent]] [mtg-pairings-server.styles.bracket :as bracket] [mtg-pairings-server.styles.common :refer [color]] + [mtg-pairings-server.styles.decklist :as decklist] [mtg-pairings-server.styles.organizer :as organizer] [mtg-pairings-server.styles.table :as table] [mtg-pairings-server.styles.tournament :as tournament] @@ -55,6 +56,7 @@ own-tournaments table/styles bracket/styles + decklist/styles organizer/styles tournament/styles mobile) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj index 0e3fe6b..b8f3202 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj @@ -105,7 +105,7 @@ ellipsis-overflow)])]) (defstyles styles - [:table + [:table.pairings-table :table.seatings-table :table.standings-table :table.pods-table {:border-spacing 0} [:th {:text-align :left diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 88095ac..18764d6 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -11,6 +11,7 @@ mtg-pairings-server.util.event-listener [mtg-pairings-server.subscriptions :as subs] [mtg-pairings-server.events :as events] + [mtg-pairings-server.pages.decklist :refer [decklist-submit]] [mtg-pairings-server.pages.main :refer [main-page]] [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] [mtg-pairings-server.pages.organizer :refer [organizer-page organizer-menu deck-construction-tables]] @@ -26,6 +27,9 @@ :accent3-color "#ff77a9" :picker-header-color "#5d99c6"}})) +(defn display-header? [page] + (not (contains? #{:organizer :decklist-submit} (:page page)))) + (defn current-page [] (let [page (subscribe [::subs/page]) hide-organizer-menu? (subscribe [::subs/organizer :menu])] @@ -33,8 +37,10 @@ [ui/mui-theme-provider {:mui-theme theme} [:div - (if (= :organizer (:page @page)) - (when-not @hide-organizer-menu? [organizer/menu]) + (when (and (= :organizer (:page @page)) + (not @hide-organizer-menu?)) + [organizer/menu]) + (when (display-header? @page) [header]) [notification] [:div#main-container @@ -46,6 +52,7 @@ :organizer [#'organizer-page] :organizer-menu [#'organizer-menu] :organizer-deck-construction [#'deck-construction-tables] + :decklist-submit [#'decklist-submit] nil)]]]))) (defn mount-root [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 876f859..f744cd3 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -31,26 +31,28 @@ (local-storage/store key obj))) (defn initial-db [] - (deep-merge {:tournaments {} - :tournament-count 0 - :tournaments-page 0 - :tournament-ids [] - :tournament-filter {:organizer "" - :date-from nil - :date-to nil - :players [0 100]} - :filters-active false - :max-players 100 - :player-tournaments [] - :pairings {:sort-key :table_number} - :pods {:sort-key :pod} - :seatings {:sort-key :table_number} - :page {:page :main - :id nil - :round nil} - :logged-in-user (local-storage/fetch :user) - :notification nil - :mobile? (mobile?)} + (deep-merge {:tournaments {} + :tournament-count 0 + :tournaments-page 0 + :tournament-ids [] + :tournament-filter {:organizer "" + :date-from nil + :date-to nil + :players [0 100]} + :filters-active false + :max-players 100 + :player-tournaments [] + :pairings {:sort-key :table_number} + :pods {:sort-key :pod} + :seatings {:sort-key :table_number} + :page {:page :main + :id nil + :round nil} + :logged-in-user (local-storage/fetch :user) + :notification nil + :mobile? (mobile?) + :decklist-tournament {:name "Testiturnaus" + :format :modern}} (transit/read (oget js/window "initial_db")))) (defn update-filters-active [db] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs new file mode 100644 index 0000000..f8a9395 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -0,0 +1,186 @@ +(ns mtg-pairings-server.pages.decklist + (:require [reagent.core :as reagent :refer [atom]] + [re-frame.core :refer [subscribe]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [cljs-react-material-ui.icons :as icons] + [prop-types] + [cljs.core.async :refer [ (get-in deck [:count :side]) 15) {:type :sideboard + :id :deck-error-sideboard + :text "Sideboardilla on yli 15 korttia"}) + (for [[card quantity] cards + :when (not (basic? card)) + :when (> quantity 4)] + {:type :card-over-4 + :id (str "deck-error-card--" card) + :card card + :text (str "Korttia " card " on yli 4 kappaletta")}))] + (filter some? errors))) + +(defn cards-with-error [deck] + (into #{} + (comp (filter #(= :card-over-4 (:type %))) + (map :card)) + (deck-errors deck))) + +(defn add-card [{:keys [board] :as deck} card] + (if (some #(= card (:name %)) (get deck board)) + deck + (-> deck + (update board conj {:name card, :quantity 1}) + (update-in [:count board] inc)))) + +(defn set-quantity [deck board index quantity] + (let [orig-quantity (get-in deck [board index :quantity])] + (-> deck + (assoc-in [board index :quantity] quantity) + (update-in [:count board] + (- quantity orig-quantity))))) + +(defn error-icon [] + (reagent/create-class + {:context-types #js {:muiTheme prop-types/object.isRequired} + :reagent-render (fn error-icon-render [] + (let [palette (:palette (get-theme (reagent/current-component)))] + [icons/alert-warning {:color (:accent3Color palette) + :style {:vertical-align :top}}]))})) + +(defn deck-table-row [deck board index card error?] + (let [on-change (fn [_ _ quantity] + (swap! deck set-quantity board index quantity))] + (fn deck-table-row-render [_ _ _ {:keys [name quantity]} error?] + [ui/table-row + [ui/table-row-column {:class-name :quantity + :style {:padding "0 12px"}} + [ui/select-field {:value quantity + :on-change on-change + :style {:width "48px" + :vertical-align :top} + :menu-style {:width "48px"} + :icon-style {:padding-left 0 + :padding-right 0 + :width "24px" + :fill "rgba(0, 0, 0, 0.54)"} + :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} + (for [i (range 1 (if (basic? name) + 31 + 5))] + ^{:key (str name "--quantity--" i)} + [ui/menu-item {:value i + :primary-text i + :inner-div-style {:padding "0 6px"}}])]] + [ui/table-row-column {:class-name :card + :style {:font-size "14px"}} + name] + [ui/table-row-column {:class-name :error + :style {:padding "12px"}} + (when error? + [error-icon])]]))) + +(defn deck-table [deck board] + (let [header-style {:color :black + :font-weight :bold + :font-size "16px" + :height "36px"}] + (fn deck-table-render [deck board] + (let [error-cards (cards-with-error @deck)] + [:div.deck-table-container + [:h2 (if (= :main board) + (str "Main deck (" (get-in @deck [:count :main]) ")") + (str "Sideboard (" (get-in @deck [:count :side]) ")"))] + [ui/table {:selectable false + :class-name :deck-table} + [ui/table-header {:display-select-all false + :adjust-for-checkbox false} + [ui/table-row {:style {:height "24px"}} + [ui/table-header-column {:class-name :quantity + :style (merge header-style + {:padding "0 12px"})} + "Määrä"] + [ui/table-header-column {:class-name :card + :style header-style} + "Kortti"] + [ui/table-header-column {:class-name :error}]]] + [ui/table-body + (for [[index {:keys [name] :as card}] (indexed (get @deck board))] + ^{:key (str name "--" board "--tr")} + [deck-table-row deck board index card (contains? error-cards name)])]]])))) + +(defonce deck (atom {:main [] + :side [] + :count {:main 0 + :side 0} + :board :main + :name "" + :player {:dci "" + :first-name "" + :last-name "" + :email ""}})) + +(defn decklist-submit [] + (let [on-change (fn [card] + (swap! deck add-card card)) + select-main #(swap! deck assoc :board :main) + select-side #(swap! deck assoc :board :side) + button-style {:position :relative + :top "-3px" + :width "70px" + :min-width "70px"}] + (fn decklist-submit-render [] + [:div#decklist-submit + [input on-change] + [ui/raised-button + {:label "Main" + :on-click select-main + :primary (= :main (:board @deck)) + :style button-style + :button-style {:border-radius "2px 0 0 2px"}}] + [ui/raised-button + {:label "Side" + :on-click select-side + :primary (= :side (:board @deck)) + :style button-style + :button-style {:border-radius "0 2px 2px 0"}}] + [:div + [deck-table deck :main] + [deck-table deck :side]]]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index 2f7738c..f7abc56 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -66,3 +66,6 @@ (let [id (js/parseInt id)] (dispatch [::events/load-deck-construction id]) (dispatch-page :organizer-deck-construction id))) + +(secretary/defroute decklist-submit-path "/decklist" [] + (dispatch-page :decklist-submit)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index 4a48b7a..2d7285f 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -150,3 +150,7 @@ (reg-sub ::notification (fn [db _] (:notification db))) + +(reg-sub ::decklist-tournament + (fn [db _] + (:decklist-tournament db))) From 67242d9c7833d8db3a438d07da452f7a6ecec7c4 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 14:11:38 +0300 Subject: [PATCH 04/44] Player info form --- mtg-pairings-server/dev.cljs.edn | 2 +- .../components/autosuggest.cljs | 5 -- .../mtg_pairings_server/pages/decklist.cljs | 66 +++++++++++++++++-- .../mtg_pairings_server/util/material_ui.cljs | 10 ++- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/mtg-pairings-server/dev.cljs.edn b/mtg-pairings-server/dev.cljs.edn index 535f3ac..d296cda 100644 --- a/mtg-pairings-server/dev.cljs.edn +++ b/mtg-pairings-server/dev.cljs.edn @@ -1,5 +1,5 @@ ^{:watch-dirs ["src/cljs" "src/cljc" "env/dev/cljs"] - :css-dirs ["resources/public/css"] + :css-dirs ["resources/public/css" "target/public/css"] :open-url false} {:main mtg-pairings-server.dev :preloads [re-frisk.preload] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs index 1c62d4a..b5e78b8 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs @@ -50,11 +50,6 @@ {:on-change (fn [event new-value] (when (= "type" (oget new-value "method")) (reset! value (oget event "target" "value")))) - :on-key-press (fn [event] - (when (and (= 13 (oget event "charCode")) - (some #{@value} @suggestions)) - (.preventDefault event) - (select-suggestion @value))) :value @value})] [:> js/Autosuggest {:suggestions @suggestions :on-suggestions-fetch-requested on-suggestions-fetch-requested diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index f8a9395..67220ff 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -9,8 +9,8 @@ [cljs-http.client :as http] [mtg-pairings-server.components.autosuggest :refer [autosuggest]] [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.util :refer [indexed]] - [mtg-pairings-server.util.material-ui :refer [get-theme]])) + [mtg-pairings-server.util :refer [indexed format-date]] + [mtg-pairings-server.util.material-ui :refer [get-theme text-field]])) (def basic? #{"Plains" "Island" "Swamp" "Mountain" "Forest" "Wastes" "Snow-Covered Plains" "Snow-Covered Island" "Snow-Covered Swamp" @@ -36,7 +36,8 @@ :on-change autosuggest-on-change :on-suggestions-fetch-requested on-suggestions-fetch-requested :on-suggestions-clear-requested on-suggestions-clear-requested - :id :decklist-autosuggest}]))) + :id :decklist-autosuggest + :floating-label-text "Lisää kortti..."}]))) (defn deck-errors [deck] (let [cards (reduce (fn [acc {:keys [name quantity]}] @@ -125,7 +126,7 @@ (fn deck-table-render [deck board] (let [error-cards (cards-with-error @deck)] [:div.deck-table-container - [:h2 (if (= :main board) + [:h3 (if (= :main board) (str "Main deck (" (get-in @deck [:count :main]) ")") (str "Sideboard (" (get-in @deck [:count :side]) ")"))] [ui/table {:selectable false @@ -146,6 +147,35 @@ ^{:key (str name "--" board "--tr")} [deck-table-row deck board index card (contains? error-cards name)])]]])))) +(defn player-info [deck] + (let [set-first-name #(swap! deck assoc-in [:player :first-name] %) + set-last-name #(swap! deck assoc-in [:player :last-name] %) + set-deck-name #(swap! deck assoc-in [:player :deck-name] %) + set-email #(swap! deck assoc-in [:player :email] %) + set-dci #(swap! deck assoc-in [:player :dci] %)] + (fn player-info-render [deck] + [:div#player-info + [:div.full-width + [text-field {:on-change set-deck-name + :floating-label-text "Pakan nimi" + :full-width true}]] + [:div.half-width.left + [text-field {:on-change set-first-name + :floating-label-text "Etunimi" + :full-width true}]] + [:div.half-width.right + [text-field {:on-change set-last-name + :floating-label-text "Sukunimi" + :full-width true}]] + [:div.half-width.left + [text-field {:on-change set-dci + :floating-label-text "DCI-numero" + :full-width true}]] + [:div.half-width.right + [text-field {:on-change set-email + :floating-label-text "Sähköposti" + :full-width true}]]]))) + (defonce deck (atom {:main [] :side [] :count {:main 0 @@ -155,19 +185,39 @@ :player {:dci "" :first-name "" :last-name "" + :deck-name "" :email ""}})) + + (defn decklist-submit [] (let [on-change (fn [card] (swap! deck add-card card)) select-main #(swap! deck assoc :board :main) select-side #(swap! deck assoc :board :side) button-style {:position :relative - :top "-3px" + :top "-5px" :width "70px" - :min-width "70px"}] + :min-width "70px"} + tournament (subscribe [::subs/decklist-tournament])] (fn decklist-submit-render [] [:div#decklist-submit + [:h2 "Lähetä pakkalista"] + [:p.intro + "Lähetä pakkalistasi " + [:span.tournament-date + (format-date (:date @tournament))] + " pelattavaan turnaukseen " + [:span.tournament-name + (:name @tournament)] + ", jonka formaatti on " + [:span.tournament-format + (case (:format @tournament) + :standard "Standard" + :modern "Modern" + :legacy "Legacy")] + "."] + [:h3 "Pakkalista"] [input on-change] [ui/raised-button {:label "Main" @@ -183,4 +233,6 @@ :button-style {:border-radius "0 2px 2px 0"}}] [:div [deck-table deck :main] - [deck-table deck :side]]]))) + [deck-table deck :side]] + [:h3 "Pelaajan tiedot"] + [player-info deck]]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs index 4a4c7a7..d1bc9ba 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs @@ -1,5 +1,13 @@ (ns mtg-pairings-server.util.material-ui - (:require [oops.core :refer [oget]])) + (:require [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [oops.core :refer [oget]])) (defn get-theme [component] (js->clj (oget component "context" "muiTheme") :keywordize-keys true)) + +(defn text-field [props] + (let [original-on-change (:on-change props) + on-change (fn [event] + (original-on-change (oget event "target" "value")))] + [ui/text-field (assoc props :on-change on-change)])) From f03cf758e68e0e733100e95c0d0c9d2aa932402c Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 14:11:56 +0300 Subject: [PATCH 05/44] Backend for saving decklist --- mtg-pairings-server/resources/private/db.sql | 29 ++++++++ .../src/clj/mtg_pairings_server/api/card.clj | 4 +- .../src/clj/mtg_pairings_server/handler.clj | 6 +- .../clj/mtg_pairings_server/service/card.clj | 21 ------ .../mtg_pairings_server/service/decklist.clj | 70 +++++++++++++++++++ .../src/clj/mtg_pairings_server/sql_db.clj | 54 ++++++++++---- .../mtg_pairings_server/styles/decklist.clj | 33 +++++++-- .../src/clj/mtg_pairings_server/util/sql.clj | 10 ++- .../src/cljc/mtg_pairings_server/transit.cljc | 24 ++++--- .../src/cljc/mtg_pairings_server/util.cljc | 9 +++ .../src/cljs/mtg_pairings_server/events.cljs | 3 +- .../src/cljs/mtg_pairings_server/routes.cljs | 5 +- 12 files changed, 212 insertions(+), 56 deletions(-) delete mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj diff --git a/mtg-pairings-server/resources/private/db.sql b/mtg-pairings-server/resources/private/db.sql index 03e029d..dd7f9c3 100644 --- a/mtg-pairings-server/resources/private/db.sql +++ b/mtg-pairings-server/resources/private/db.sql @@ -92,4 +92,33 @@ create table pod_seat ( seat int not null ); +create table decklist_tournament ( + id char(22) not null primary key, + "user" integer not null references smf_members(id_member), + name text not null, + date date not null, + deadline timestamptz, + format text not null +); + +create table decklist ( + id char(22) not null primary key, + tournament char(22) not null references decklist_tournament(id), + submitted timestamptz not null, + name text, + "last-name" text, + "first-name" text, + dci text, + email text +); + +create table decklist_card ( + decklist char(22) not null references decklist(id), + maindeck boolean not null, + card int not null references trader_card(id), + index int not null, + quantity int not null, + primary key (decklist, maindeck, card) +); + commit; diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj index c5ff155..e68b223 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj @@ -1,7 +1,7 @@ (ns mtg-pairings-server.api.card (:require [compojure.api.sweet :refer :all] [schema.core :as s] - [mtg-pairings-server.service.card :refer :all] + [mtg-pairings-server.service.decklist :refer [search-cards]] [mtg-pairings-server.util.schema :refer :all] [mtg-pairings-server.util :refer [response]])) @@ -11,4 +11,4 @@ format :- (s/enum :standard :modern :legacy)] :return [s/Str] :summary "Hae kortteja nimen alkuosalla" - (response (search prefix format)))) + (response (search-cards prefix format)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 863be0b..9a04470 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -12,6 +12,7 @@ [mtg-pairings-server.middleware :refer [wrap-site-middleware]] [mtg-pairings-server.middleware.cors :refer [wrap-allow-origin]] [mtg-pairings-server.middleware.log :refer [wrap-request-log]] + [mtg-pairings-server.service.decklist :as decklist] [mtg-pairings-server.service.tournament :as tournament] [mtg-pairings-server.service.player :as player] [mtg-pairings-server.transit :as transit] @@ -93,7 +94,10 @@ (GET "/tournaments/:id/organizer" [] (loading-page)) (GET "/tournaments/:id/organizer/menu" [] (loading-page)) (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) - (GET "/decklist" [] (loading-page)) + (GET "/decklist/tournament/:id" [] + :path-params [id :- s/Str] + (loading-page {:page {:page :decklist} + :decklist-tournament (decklist/get-tournament id)})) (GET "/robots.txt" [] robots-txt) ws/routes) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj deleted file mode 100644 index 1a0bb62..0000000 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/card.clj +++ /dev/null @@ -1,21 +0,0 @@ -(ns mtg-pairings-server.service.card - (:require [korma.core :as sql] - [mtg-pairings-server.sql-db :as db] - [mtg-pairings-server.util.sql :refer [like]] - [clojure.string :as str])) - -(defn search [prefix format] - (let [name-param (-> prefix - (str/replace "%" "") - (str/lower-case) - (str \%)) - result (cond-> (-> (sql/select* db/card) - (sql/fields :name) - (sql/where {:lowername [like name-param]}) - (sql/order :lowername :asc) - (sql/limit 10)) - (= :standard format) (sql/where {:standard true}) - (= :modern format) (sql/where {:modern true}) - (= :legacy format) (sql/where {:legacy true}) - true (sql/exec))] - (map :name result))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj new file mode 100644 index 0000000..b92cc2b --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -0,0 +1,70 @@ +(ns mtg-pairings-server.service.decklist + (:require [clojure.string :as str] + [clj-time.core :as time] + [korma.core :as sql] + [korma.db :refer [transaction]] + [mtg-pairings-server.sql-db :as db] + [mtg-pairings-server.util :refer [indexed]] + [mtg-pairings-server.util.sql :as sql-util :refer [like]])) + +(defn search-cards [prefix format] + (let [name-param (-> prefix + (str/replace "%" "") + (str/lower-case) + (str \%)) + result (cond-> (-> (sql/select* db/card) + (sql/fields :name) + (sql/where {:lowername [like name-param]}) + (sql/order :lowername :asc) + (sql/limit 10)) + (= :standard format) (sql/where {:standard true}) + (= :modern format) (sql/where {:modern true}) + (= :legacy format) (sql/where {:legacy true}) + true (sql/exec))] + (map :name result))) + +(defn generate-card->id [cards] + (into {} + (map (juxt :name :id)) + (sql/select db/card + (sql/where {:name [in cards]})))) + +(defn format-card [decklist maindeck card index quantity] + {:decklist decklist + :maindeck maindeck + :card card + :index index + :quantity quantity}) + +(defn save-decklist [tournament decklist] + (transaction + (let [old-id (:id decklist) + new-id (sql-util/generate-id) + card->id (generate-card->id (map :name + (concat (:main decklist) + (:side decklist))))] + (when-not old-id + (sql/insert db/decklist + (sql/values (-> (select-keys decklist [:name :last-name :first-name :dci :email]) + (assoc :id new-id + :tournament tournament + :submitted (time/now)))))) + (when old-id + (sql-util/update-unique db/decklist + (sql/set-fields (-> (select-keys decklist [:name :last-name :first-name :dci :email]) + (assoc :submitted (time/now)))) + (sql/where {:id old-id})) + (sql/delete db/decklist-card + (sql/where {:decklist old-id}))) + (sql/insert db/decklist-card + (sql/values + (concat + (for [[index {:keys [name quantity]}] (indexed (:main decklist))] + (format-card (or old-id new-id) true (card->id name) index quantity)) + (for [[index {:keys [name quantity]}] (indexed (:side decklist))] + (format-card (or old-id new-id) false (card->id name) index quantity)))))))) + +(defn get-tournament [id] + (some-> (sql-util/select-unique-or-nil db/decklist-tournament + (sql/where {:id id})) + (update :format keyword))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index 89b9bc0..30b56d2 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -6,9 +6,10 @@ [korma.db] [hikari-cp.core :as hikari] [mount.core :refer [defstate]] - [config.core :refer [env]]) - (:import (java.sql Date) - (org.joda.time LocalDate))) + [config.core :refer [env]] + [mtg-pairings-server.util :refer [some-value]]) + (:import (java.sql Date Timestamp) + (org.joda.time LocalDate DateTime))) (def datasource-options {:adapter "postgresql" :username (env :db-user) @@ -25,13 +26,16 @@ (korma.db/default-connection nil) (hikari/close-datasource (get-in @db [:pool :datasource])))) +(defn ^:private convert [conversions x] + (if-let [[_ converter] (some-value (fn [[cls _]] + (instance? cls x)) + conversions)] + (converter x) + x)) + (defn ^:private convert-instances-of - [cls f m] - (postwalk (fn [x] - (if (instance? cls x) - (f x) - x)) - m)) + [conversions m] + (postwalk (partial convert conversions) m)) (defn ^:private to-local-date-default-tz [date] @@ -39,12 +43,15 @@ (time-coerce/to-local-date (time/to-time-zone dt (time/default-time-zone))))) (def sql-date->joda-date - (partial convert-instances-of Date to-local-date-default-tz)) + (partial convert-instances-of {Date to-local-date-default-tz + Timestamp time-coerce/to-date-time})) (def joda-date->sql-date - (partial convert-instances-of LocalDate time-coerce/to-sql-date)) + (partial convert-instances-of {LocalDate time-coerce/to-sql-date + DateTime time-coerce/to-sql-time})) -(declare tournament player team round pairing result standings team-players seating user pod-round) +(declare tournament player team round pairing result standings team-players seating user pod-round + decklist decklist-card) (sql/defentity team-players (sql/table :team_players) @@ -156,3 +163,26 @@ (sql/defentity card (sql/table :trader_card) (sql/pk :id)) + +(sql/defentity decklist-tournament + (sql/table :decklist_tournament) + (sql/pk :id) + (sql/has-many decklist + {:fk :tournament}) + (sql/prepare joda-date->sql-date) + (sql/transform sql-date->joda-date)) + +(sql/defentity decklist + (sql/pk :id) + (sql/belongs-to decklist-tournament + {:fk :tournament}) + (sql/has-many decklist-card + {:fk :decklist}) + (sql/prepare joda-date->sql-date) + (sql/transform sql-date->joda-date)) + +(sql/defentity decklist-card + (sql/belongs-to decklist + {:fk :decklist}) + (sql/belongs-to card + {:fk :card})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index e10a54f..89ce6e0 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -13,9 +13,30 @@ [:.deck-table-container {:width "50%" :display :inline-block - :vertical-align :top}]) - [:.deck-table - [:th.quantity :td.quantity - {:width (px 72)}] - [:th.error :td.error - {:width (px 48)}]]]) + :vertical-align :top}] + [:#player-info + [:.full-width + {:width "100%"}] + [:.half-width + {:width "calc(50% - 24px)" + :display :inline-block} + [:&.left + {:margin-right (px 24)}] + [:&.right + {:margin-left (px 24)}]]]) + (when-mobile + [:#player-info + [:.full-width :.half-width + {:width "100%" + :display :block}]]) + [:.intro + [:.tournament-date :.tournament-name :.tournament-format + {:font-weight :bold}]] + [:h3 + {:margin-bottom 0}] + [:.deck-table-container + [:.deck-table + [:th.quantity :td.quantity + {:width (px 72)}] + [:th.error :td.error + {:width (px 48)}]]]]) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj index a001cfc..ff78768 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/util/sql.clj @@ -1,7 +1,8 @@ (ns mtg-pairings-server.util.sql (:require [korma.core :as sql] [korma.db :as db] - [korma.sql.engine :as eng])) + [korma.sql.engine :as eng]) + (:import (java.util Random Base64))) (defn unique-or-nil [results] @@ -54,3 +55,10 @@ (defn like [k v] (eng/infix k "LIKE" v)) + +(let [random (Random.) + encoder (.withoutPadding (Base64/getUrlEncoder))] + (defn generate-id [] + (let [bytes (byte-array 16)] + (.nextBytes random bytes) + (.encodeToString encoder bytes)))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc index 3dd93bf..d84fac2 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc @@ -1,32 +1,36 @@ (ns mtg-pairings-server.transit (:refer-clojure :exclude [read]) (:require [cognitect.transit :as transit] - [mtg-pairings-server.util :refer [parse-iso-date format-iso-date]]) + [mtg-pairings-server.util :refer [parse-iso-date parse-iso-date-time + format-iso-date format-iso-date-time]]) #?(:clj - (:import (org.joda.time LocalDate) + (:import (org.joda.time LocalDate DateTime) (clojure.lang Ratio) (java.io ByteArrayInputStream ByteArrayOutputStream)))) (def writers {#?(:clj LocalDate, :cljs goog.date.Date) (transit/write-handler (constantly "Date") format-iso-date) + #?(:clj DateTime, :cljs goog.date.DateTime) + (transit/write-handler (constantly "DateTime") format-iso-date-time) #?@(:clj [Ratio (transit/write-handler (constantly "d") double)])}) (def readers - {"Date" (transit/read-handler #(parse-iso-date %))}) + {"Date" (transit/read-handler parse-iso-date) + "DateTime" (transit/read-handler parse-iso-date-time)}) (defn write [x] - #?(:clj (let [out (ByteArrayOutputStream.) - writer (transit/writer out :json {:handlers writers})] - (transit/write writer x) - (.toString out "UTF-8")) + #?(:clj (let [out (ByteArrayOutputStream.) + writer (transit/writer out :json {:handlers writers})] + (transit/write writer x) + (.toString out "UTF-8")) :cljs (let [writer (transit/writer :json {:handlers writers})] (transit/write writer x)))) (defn read [^String s] - #?(:clj (let [in (ByteArrayInputStream. (.getBytes s "UTF-8")) - reader (transit/reader in :json {:handlers readers})] - (transit/read reader)) + #?(:clj (let [in (ByteArrayInputStream. (.getBytes s "UTF-8")) + reader (transit/reader in :json {:handlers readers})] + (transit/read reader)) :cljs (let [reader (transit/reader :json {:handlers readers})] (transit/read reader s)))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 9d9688a..c050535 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -45,6 +45,12 @@ (defn format-date [date] (some->> date (format/unparse-local-date (format/formatter "dd.MM.yyyy")))) +(defn parse-iso-date-time [datetime] + (some->> datetime (format/parse (format/formatters :date-time)))) + +(defn format-iso-date-time [datetime] + (some->> datetime (format/unparse (format/formatters :date-time)))) + (defn today-or-yesterday? [date] (let [yesterday (time/minus (time/today) (time/days 1)) tomorrow (time/plus (time/today) (time/days 1))] @@ -95,6 +101,9 @@ %2) m1 m2))) +(defn some-value [pred coll] + (first (filter pred coll))) + #?(:clj (defn response [body] (if body diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index f744cd3..fb10626 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -51,8 +51,7 @@ :logged-in-user (local-storage/fetch :user) :notification nil :mobile? (mobile?) - :decklist-tournament {:name "Testiturnaus" - :format :modern}} + :decklist-tournament {}} (transit/read (oget js/window "initial_db")))) (defn update-filters-active [db] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index f7abc56..4f7445a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -67,5 +67,8 @@ (dispatch [::events/load-deck-construction id]) (dispatch-page :organizer-deck-construction id))) -(secretary/defroute decklist-submit-path "/decklist" [] +(secretary/defroute new-decklist-submit-path "/decklist/tournament/:id" [id] + (dispatch-page :decklist-submit)) + +(secretary/defroute old-decklist-submit-path "/decklist/:id" [id] (dispatch-page :decklist-submit)) From 2be428fb39dbf287b36a7e88be52fcbb67a6f352 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 15:03:46 +0300 Subject: [PATCH 06/44] Save decklist --- .../src/clj/mtg_pairings_server/handler.clj | 13 +- .../mtg_pairings_server/service/decklist.clj | 52 +++++-- .../src/clj/mtg_pairings_server/sql_db.clj | 1 + .../src/cljs/mtg_pairings_server/events.cljs | 19 +++ .../mtg_pairings_server/pages/decklist.cljs | 135 +++++++++++------- .../src/cljs/mtg_pairings_server/routes.cljs | 2 +- .../mtg_pairings_server/subscriptions.cljs | 4 + 7 files changed, 161 insertions(+), 65 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 9a04470..52bd564 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -96,8 +96,14 @@ (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (loading-page {:page {:page :decklist} + (loading-page {:page {:page :decklist} :decklist-tournament (decklist/get-tournament id)})) + (GET "/decklist/:id" [] + :path-params [id :- s/Str] + (let [decklist (decklist/get-decklist id)] + (loading-page {:page {:page :decklist, :id id} + :decklist-tournament (decklist/get-tournament (:tournament decklist)) + :decklist decklist}))) (GET "/robots.txt" [] robots-txt) ws/routes) @@ -197,3 +203,8 @@ [{uid :uid, id :?data}] (ws/send! uid [:server/organizer-seatings (tournament/seatings id)]) (ws/send! uid [:server/organizer-pods (tournament/latest-pods id)])) + +(defmethod ws/event-handler :client/save-decklist + [{uid :uid, [tournament decklist] :?data}] + (let [id (decklist/save-decklist tournament decklist)] + (ws/send! uid [:server/decklist-saved id]))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index b92cc2b..4eb7dd2 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -1,5 +1,6 @@ (ns mtg-pairings-server.service.decklist (:require [clojure.string :as str] + [clojure.set :refer [rename-keys]] [clj-time.core :as time] [korma.core :as sql] [korma.db :refer [transaction]] @@ -43,28 +44,59 @@ card->id (generate-card->id (map :name (concat (:main decklist) (:side decklist))))] - (when-not old-id + (if old-id + (do + (sql-util/update-unique db/decklist + (sql/set-fields (-> (:player decklist) + (rename-keys {:deck-name :name}) + (select-keys [:name :first-name :last-name :email :dci]) + (assoc :submitted (time/now)))) + (sql/where {:id old-id})) + (sql/delete db/decklist-card + (sql/where {:decklist old-id}))) (sql/insert db/decklist - (sql/values (-> (select-keys decklist [:name :last-name :first-name :dci :email]) + (sql/values (-> (:player decklist) + (rename-keys {:deck-name :name}) + (select-keys [:name :first-name :last-name :email :dci]) (assoc :id new-id :tournament tournament :submitted (time/now)))))) - (when old-id - (sql-util/update-unique db/decklist - (sql/set-fields (-> (select-keys decklist [:name :last-name :first-name :dci :email]) - (assoc :submitted (time/now)))) - (sql/where {:id old-id})) - (sql/delete db/decklist-card - (sql/where {:decklist old-id}))) (sql/insert db/decklist-card (sql/values (concat (for [[index {:keys [name quantity]}] (indexed (:main decklist))] (format-card (or old-id new-id) true (card->id name) index quantity)) (for [[index {:keys [name quantity]}] (indexed (:side decklist))] - (format-card (or old-id new-id) false (card->id name) index quantity)))))))) + (format-card (or old-id new-id) false (card->id name) index quantity))))) + (or old-id new-id)))) (defn get-tournament [id] (some-> (sql-util/select-unique-or-nil db/decklist-tournament (sql/where {:id id})) (update :format keyword))) + +(defn format-cards [cards maindeck?] + (->> cards + ((if maindeck? filter remove) :maindeck) + (sort-by :index) + (mapv #(select-keys % [:name :quantity])))) + +(defn get-decklist [id] + (let [decklist (sql-util/select-unique db/decklist + (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) + (sql/with db/decklist-card + (sql/fields :maindeck :index :quantity) + (sql/with db/card + (sql/fields :name))) + (sql/where {:id id})) + main (format-cards (:decklist_card decklist) true) + side (format-cards (:decklist_card decklist) false) + player (select-keys decklist [:deck-name :first-name :last-name :email :dci])] + {:id id + :tournament (:tournament decklist) + :main main + :side side + :count {:main (transduce (map :quantity) + 0 main) + :side (transduce (map :quantity) + 0 side)} + :board :main + :player player})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index 30b56d2..1568a0d 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -182,6 +182,7 @@ (sql/transform sql-date->joda-date)) (sql/defentity decklist-card + (sql/table :decklist_card) (sql/belongs-to decklist {:fk :decklist}) (sql/belongs-to card diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index fb10626..a3aaf1b 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -3,6 +3,7 @@ [cljs.core.async :refer [ db + (assoc-in [:decklist-tournament :saving] true) + (assoc :decklist decklist)) + :ws-send [:client/save-decklist [tournament decklist]]})) + +(reg-event-fx :server/decklist-saved + (fn [{:keys [db]} [_ id]] + {:db (-> db + (assoc-in [:decklist-tournament :saving] false) + (assoc-in [:decklist-tournament :saved] true)) + :navigate (str "/decklist/" id)})) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 67220ff..5fd3606 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -1,6 +1,6 @@ (ns mtg-pairings-server.pages.decklist (:require [reagent.core :as reagent :refer [atom]] - [re-frame.core :refer [subscribe]] + [re-frame.core :refer [subscribe dispatch]] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] @@ -8,6 +8,7 @@ [cljs.core.async :refer [ (get-in deck [:count :side]) 15) {:type :sideboard - :id :deck-error-sideboard - :text "Sideboardilla on yli 15 korttia"}) + (concat (:main decklist) (:side decklist))) + errors (list* (when (< (get-in decklist [:count :main]) 60) {:type :maindeck + :id :deck-error-maindeck + :text "Maindeckissä on alle 60 korttia"}) + (when (> (get-in decklist [:count :side]) 15) {:type :sideboard + :id :deck-error-sideboard + :text "Sideboardilla on yli 15 korttia"}) (for [[card quantity] cards :when (not (basic? card)) :when (> quantity 4)] @@ -59,22 +60,22 @@ :text (str "Korttia " card " on yli 4 kappaletta")}))] (filter some? errors))) -(defn cards-with-error [deck] +(defn cards-with-error [decklist] (into #{} (comp (filter #(= :card-over-4 (:type %))) (map :card)) - (deck-errors deck))) + (decklist-errors decklist))) -(defn add-card [{:keys [board] :as deck} card] - (if (some #(= card (:name %)) (get deck board)) - deck - (-> deck +(defn add-card [{:keys [board] :as decklist} card] + (if (some #(= card (:name %)) (get decklist board)) + decklist + (-> decklist (update board conj {:name card, :quantity 1}) (update-in [:count board] inc)))) -(defn set-quantity [deck board index quantity] - (let [orig-quantity (get-in deck [board index :quantity])] - (-> deck +(defn set-quantity [decklist board index quantity] + (let [orig-quantity (get-in decklist [board index :quantity])] + (-> decklist (assoc-in [board index :quantity] quantity) (update-in [:count board] + (- quantity orig-quantity))))) @@ -86,10 +87,10 @@ [icons/alert-warning {:color (:accent3Color palette) :style {:vertical-align :top}}]))})) -(defn deck-table-row [deck board index card error?] +(defn decklist-table-row [decklist board index card error?] (let [on-change (fn [_ _ quantity] - (swap! deck set-quantity board index quantity))] - (fn deck-table-row-render [_ _ _ {:keys [name quantity]} error?] + (swap! decklist set-quantity board index quantity))] + (fn decklist-table-row-render [_ _ _ {:keys [name quantity]} error?] [ui/table-row [ui/table-row-column {:class-name :quantity :style {:padding "0 12px"}} @@ -118,17 +119,17 @@ (when error? [error-icon])]]))) -(defn deck-table [deck board] +(defn decklist-table [decklist board] (let [header-style {:color :black :font-weight :bold :font-size "16px" :height "36px"}] - (fn deck-table-render [deck board] - (let [error-cards (cards-with-error @deck)] + (fn decklist-table-render [decklist board] + (let [error-cards (cards-with-error @decklist)] [:div.deck-table-container [:h3 (if (= :main board) - (str "Main deck (" (get-in @deck [:count :main]) ")") - (str "Sideboard (" (get-in @deck [:count :side]) ")"))] + (str "Main deck (" (get-in @decklist [:count :main]) ")") + (str "Sideboard (" (get-in @decklist [:count :side]) ")"))] [ui/table {:selectable false :class-name :deck-table} [ui/table-header {:display-select-all false @@ -143,63 +144,71 @@ "Kortti"] [ui/table-header-column {:class-name :error}]]] [ui/table-body - (for [[index {:keys [name] :as card}] (indexed (get @deck board))] + (for [[index {:keys [name] :as card}] (indexed (get @decklist board))] ^{:key (str name "--" board "--tr")} - [deck-table-row deck board index card (contains? error-cards name)])]]])))) + [decklist-table-row decklist board index card (contains? error-cards name)])]]])))) -(defn player-info [deck] - (let [set-first-name #(swap! deck assoc-in [:player :first-name] %) - set-last-name #(swap! deck assoc-in [:player :last-name] %) - set-deck-name #(swap! deck assoc-in [:player :deck-name] %) - set-email #(swap! deck assoc-in [:player :email] %) - set-dci #(swap! deck assoc-in [:player :dci] %)] - (fn player-info-render [deck] +(defn player-info [decklist] + (let [set-first-name #(swap! decklist assoc-in [:player :first-name] %) + set-last-name #(swap! decklist assoc-in [:player :last-name] %) + set-deck-name #(swap! decklist assoc-in [:player :deck-name] %) + set-email #(swap! decklist assoc-in [:player :email] %) + set-dci #(swap! decklist assoc-in [:player :dci] %)] + (fn player-info-render [_] [:div#player-info [:div.full-width [text-field {:on-change set-deck-name :floating-label-text "Pakan nimi" - :full-width true}]] + :full-width true + :value (get-in @decklist [:player :deck-name])}]] [:div.half-width.left [text-field {:on-change set-first-name :floating-label-text "Etunimi" - :full-width true}]] + :full-width true + :value (get-in @decklist [:player :first-name])}]] [:div.half-width.right [text-field {:on-change set-last-name :floating-label-text "Sukunimi" - :full-width true}]] + :full-width true + :value (get-in @decklist [:player :last-name])}]] [:div.half-width.left [text-field {:on-change set-dci :floating-label-text "DCI-numero" - :full-width true}]] + :full-width true + :value (get-in @decklist [:player :dci])}]] [:div.half-width.right [text-field {:on-change set-email :floating-label-text "Sähköposti" - :full-width true}]]]))) + :full-width true + :value (get-in @decklist [:player :email])}]]]))) -(defonce deck (atom {:main [] +(def empty-decklist {:main [] :side [] :count {:main 0 :side 0} :board :main - :name "" :player {:dci "" :first-name "" :last-name "" :deck-name "" - :email ""}})) + :email ""}}) (defn decklist-submit [] - (let [on-change (fn [card] - (swap! deck add-card card)) - select-main #(swap! deck assoc :board :main) - select-side #(swap! deck assoc :board :side) + (let [saved-decklist (subscribe [::subs/decklist]) + decklist (atom (or @saved-decklist empty-decklist)) + on-change (fn [card] + (swap! decklist add-card card)) + select-main #(swap! decklist assoc :board :main) + select-side #(swap! decklist assoc :board :side) button-style {:position :relative :top "-5px" :width "70px" :min-width "70px"} - tournament (subscribe [::subs/decklist-tournament])] + tournament (subscribe [::subs/decklist-tournament]) + page (subscribe [::subs/page]) + save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] [:div#decklist-submit [:h2 "Lähetä pakkalista"] @@ -222,17 +231,37 @@ [ui/raised-button {:label "Main" :on-click select-main - :primary (= :main (:board @deck)) + :primary (= :main (:board @decklist)) :style button-style :button-style {:border-radius "2px 0 0 2px"}}] [ui/raised-button {:label "Side" :on-click select-side - :primary (= :side (:board @deck)) + :primary (= :side (:board @decklist)) :style button-style :button-style {:border-radius "0 2px 2px 0"}}] [:div - [deck-table deck :main] - [deck-table deck :side]] + [decklist-table decklist :main] + [decklist-table decklist :side]] [:h3 "Pelaajan tiedot"] - [player-info deck]]))) + [player-info decklist] + [ui/raised-button + {:label "Tallenna" + :on-click save-decklist + :primary true + :disabled (:saving @tournament) + :style {:margin-top "24px"}}] + (when (:saving @tournament) + [ui/circular-progress + {:size 36 + :style {:margin "24px 0 0 24px" + :vertical-align :top}}]) + (when (:saved @tournament) + (let [url (str "https://pairings.fi/decklist/" (:id @page))] + [:div.success-notice + [:h4 "Tallennus onnistui!"] + [:p + "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " + [:a {:href url} + url] + ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index 4f7445a..5c0c72a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -71,4 +71,4 @@ (dispatch-page :decklist-submit)) (secretary/defroute old-decklist-submit-path "/decklist/:id" [id] - (dispatch-page :decklist-submit)) + (dispatch-page :decklist-submit id)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index 2d7285f..3bf2411 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -154,3 +154,7 @@ (reg-sub ::decklist-tournament (fn [db _] (:decklist-tournament db))) + +(reg-sub ::decklist + (fn [db _] + (:decklist db))) From d5a2b68180ab44031c68a0dfd2592768cee7ac44 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 15:08:35 +0300 Subject: [PATCH 07/44] Move decklist components to components package --- .../components/decklist/submit.cljs | 265 +++++++++++++++++ .../mtg_pairings_server/pages/decklist.cljs | 266 +----------------- 2 files changed, 269 insertions(+), 262 deletions(-) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs new file mode 100644 index 0000000..6266e3b --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -0,0 +1,265 @@ +(ns mtg-pairings-server.components.decklist.submit + (:require [reagent.core :as reagent :refer [atom]] + [re-frame.core :refer [subscribe dispatch]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [cljs-react-material-ui.icons :as icons] + [prop-types] + [cljs.core.async :refer [ (get-in decklist [:count :side]) 15) {:type :sideboard + :id :deck-error-sideboard + :text "Sideboardilla on yli 15 korttia"}) + (for [[card quantity] cards + :when (not (basic? card)) + :when (> quantity 4)] + {:type :card-over-4 + :id (str "deck-error-card--" card) + :card card + :text (str "Korttia " card " on yli 4 kappaletta")}))] + (filter some? errors))) + +(defn cards-with-error [decklist] + (into #{} + (comp (filter #(= :card-over-4 (:type %))) + (map :card)) + (decklist-errors decklist))) + +(defn add-card [{:keys [board] :as decklist} card] + (if (some #(= card (:name %)) (get decklist board)) + decklist + (-> decklist + (update board conj {:name card, :quantity 1}) + (update-in [:count board] inc)))) + +(defn set-quantity [decklist board index quantity] + (let [orig-quantity (get-in decklist [board index :quantity])] + (-> decklist + (assoc-in [board index :quantity] quantity) + (update-in [:count board] + (- quantity orig-quantity))))) + +(defn error-icon [] + (reagent/create-class + {:context-types #js {:muiTheme prop-types/object.isRequired} + :reagent-render (fn error-icon-render [] + (let [palette (:palette (get-theme (reagent/current-component)))] + [icons/alert-warning {:color (:accent3Color palette) + :style {:vertical-align :top}}]))})) + +(defn decklist-table-row [decklist board index card error?] + (let [on-change (fn [_ _ quantity] + (swap! decklist set-quantity board index quantity))] + (fn decklist-table-row-render [_ _ _ {:keys [name quantity]} error?] + [ui/table-row + [ui/table-row-column {:class-name :quantity + :style {:padding "0 12px"}} + [ui/select-field {:value quantity + :on-change on-change + :style {:width "48px" + :vertical-align :top} + :menu-style {:width "48px"} + :icon-style {:padding-left 0 + :padding-right 0 + :width "24px" + :fill "rgba(0, 0, 0, 0.54)"} + :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} + (for [i (range 1 (if (basic? name) + 31 + 5))] + ^{:key (str name "--quantity--" i)} + [ui/menu-item {:value i + :primary-text i + :inner-div-style {:padding "0 6px"}}])]] + [ui/table-row-column {:class-name :card + :style {:font-size "14px"}} + name] + [ui/table-row-column {:class-name :error + :style {:padding "12px"}} + (when error? + [error-icon])]]))) + +(defn decklist-table [decklist board] + (let [header-style {:color :black + :font-weight :bold + :font-size "16px" + :height "36px"}] + (fn decklist-table-render [decklist board] + (let [error-cards (cards-with-error @decklist)] + [:div.deck-table-container + [:h3 (if (= :main board) + (str "Main deck (" (get-in @decklist [:count :main]) ")") + (str "Sideboard (" (get-in @decklist [:count :side]) ")"))] + [ui/table {:selectable false + :class-name :deck-table} + [ui/table-header {:display-select-all false + :adjust-for-checkbox false} + [ui/table-row {:style {:height "24px"}} + [ui/table-header-column {:class-name :quantity + :style (merge header-style + {:padding "0 12px"})} + "Määrä"] + [ui/table-header-column {:class-name :card + :style header-style} + "Kortti"] + [ui/table-header-column {:class-name :error}]]] + [ui/table-body + (for [[index {:keys [name] :as card}] (indexed (get @decklist board))] + ^{:key (str name "--" board "--tr")} + [decklist-table-row decklist board index card (contains? error-cards name)])]]])))) + +(defn player-info [decklist] + (let [set-first-name #(swap! decklist assoc-in [:player :first-name] %) + set-last-name #(swap! decklist assoc-in [:player :last-name] %) + set-deck-name #(swap! decklist assoc-in [:player :deck-name] %) + set-email #(swap! decklist assoc-in [:player :email] %) + set-dci #(swap! decklist assoc-in [:player :dci] %)] + (fn player-info-render [_] + [:div#player-info + [:div.full-width + [text-field {:on-change set-deck-name + :floating-label-text "Pakan nimi" + :full-width true + :value (get-in @decklist [:player :deck-name])}]] + [:div.half-width.left + [text-field {:on-change set-first-name + :floating-label-text "Etunimi" + :full-width true + :value (get-in @decklist [:player :first-name])}]] + [:div.half-width.right + [text-field {:on-change set-last-name + :floating-label-text "Sukunimi" + :full-width true + :value (get-in @decklist [:player :last-name])}]] + [:div.half-width.left + [text-field {:on-change set-dci + :floating-label-text "DCI-numero" + :full-width true + :value (get-in @decklist [:player :dci])}]] + [:div.half-width.right + [text-field {:on-change set-email + :floating-label-text "Sähköposti" + :full-width true + :value (get-in @decklist [:player :email])}]]]))) + +(def empty-decklist {:main [] + :side [] + :count {:main 0 + :side 0} + :board :main + :player {:dci "" + :first-name "" + :last-name "" + :deck-name "" + :email ""}}) + +(defn decklist-submit [] + (let [saved-decklist (subscribe [::subs/decklist]) + decklist (atom (or @saved-decklist empty-decklist)) + on-change (fn [card] + (swap! decklist add-card card)) + select-main #(swap! decklist assoc :board :main) + select-side #(swap! decklist assoc :board :side) + button-style {:position :relative + :top "-5px" + :width "70px" + :min-width "70px"} + tournament (subscribe [::subs/decklist-tournament]) + page (subscribe [::subs/page]) + save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] + (fn decklist-submit-render [] + [:div#decklist-submit + [:h2 "Lähetä pakkalista"] + [:p.intro + "Lähetä pakkalistasi " + [:span.tournament-date + (format-date (:date @tournament))] + " pelattavaan turnaukseen " + [:span.tournament-name + (:name @tournament)] + ", jonka formaatti on " + [:span.tournament-format + (case (:format @tournament) + :standard "Standard" + :modern "Modern" + :legacy "Legacy")] + "."] + [:h3 "Pakkalista"] + [input on-change] + [ui/raised-button + {:label "Main" + :on-click select-main + :primary (= :main (:board @decklist)) + :style button-style + :button-style {:border-radius "2px 0 0 2px"}}] + [ui/raised-button + {:label "Side" + :on-click select-side + :primary (= :side (:board @decklist)) + :style button-style + :button-style {:border-radius "0 2px 2px 0"}}] + [:div + [decklist-table decklist :main] + [decklist-table decklist :side]] + [:h3 "Pelaajan tiedot"] + [player-info decklist] + [ui/raised-button + {:label "Tallenna" + :on-click save-decklist + :primary true + :disabled (:saving @tournament) + :style {:margin-top "24px"}}] + (when (:saving @tournament) + [ui/circular-progress + {:size 36 + :style {:margin "24px 0 0 24px" + :vertical-align :top}}]) + (when (:saved @tournament) + (let [url (str "https://pairings.fi/decklist/" (:id @page))] + [:div.success-notice + [:h4 "Tallennus onnistui!"] + [:p + "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " + [:a {:href url} + url] + ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 5fd3606..24c0834 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -1,267 +1,9 @@ (ns mtg-pairings-server.pages.decklist (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [subscribe dispatch]] - [cljsjs.material-ui] - [cljs-react-material-ui.reagent :as ui] - [cljs-react-material-ui.icons :as icons] - [prop-types] - [cljs.core.async :refer [ (get-in decklist [:count :side]) 15) {:type :sideboard - :id :deck-error-sideboard - :text "Sideboardilla on yli 15 korttia"}) - (for [[card quantity] cards - :when (not (basic? card)) - :when (> quantity 4)] - {:type :card-over-4 - :id (str "deck-error-card--" card) - :card card - :text (str "Korttia " card " on yli 4 kappaletta")}))] - (filter some? errors))) - -(defn cards-with-error [decklist] - (into #{} - (comp (filter #(= :card-over-4 (:type %))) - (map :card)) - (decklist-errors decklist))) - -(defn add-card [{:keys [board] :as decklist} card] - (if (some #(= card (:name %)) (get decklist board)) - decklist - (-> decklist - (update board conj {:name card, :quantity 1}) - (update-in [:count board] inc)))) - -(defn set-quantity [decklist board index quantity] - (let [orig-quantity (get-in decklist [board index :quantity])] - (-> decklist - (assoc-in [board index :quantity] quantity) - (update-in [:count board] + (- quantity orig-quantity))))) - -(defn error-icon [] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn error-icon-render [] - (let [palette (:palette (get-theme (reagent/current-component)))] - [icons/alert-warning {:color (:accent3Color palette) - :style {:vertical-align :top}}]))})) - -(defn decklist-table-row [decklist board index card error?] - (let [on-change (fn [_ _ quantity] - (swap! decklist set-quantity board index quantity))] - (fn decklist-table-row-render [_ _ _ {:keys [name quantity]} error?] - [ui/table-row - [ui/table-row-column {:class-name :quantity - :style {:padding "0 12px"}} - [ui/select-field {:value quantity - :on-change on-change - :style {:width "48px" - :vertical-align :top} - :menu-style {:width "48px"} - :icon-style {:padding-left 0 - :padding-right 0 - :width "24px" - :fill "rgba(0, 0, 0, 0.54)"} - :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} - (for [i (range 1 (if (basic? name) - 31 - 5))] - ^{:key (str name "--quantity--" i)} - [ui/menu-item {:value i - :primary-text i - :inner-div-style {:padding "0 6px"}}])]] - [ui/table-row-column {:class-name :card - :style {:font-size "14px"}} - name] - [ui/table-row-column {:class-name :error - :style {:padding "12px"}} - (when error? - [error-icon])]]))) - -(defn decklist-table [decklist board] - (let [header-style {:color :black - :font-weight :bold - :font-size "16px" - :height "36px"}] - (fn decklist-table-render [decklist board] - (let [error-cards (cards-with-error @decklist)] - [:div.deck-table-container - [:h3 (if (= :main board) - (str "Main deck (" (get-in @decklist [:count :main]) ")") - (str "Sideboard (" (get-in @decklist [:count :side]) ")"))] - [ui/table {:selectable false - :class-name :deck-table} - [ui/table-header {:display-select-all false - :adjust-for-checkbox false} - [ui/table-row {:style {:height "24px"}} - [ui/table-header-column {:class-name :quantity - :style (merge header-style - {:padding "0 12px"})} - "Määrä"] - [ui/table-header-column {:class-name :card - :style header-style} - "Kortti"] - [ui/table-header-column {:class-name :error}]]] - [ui/table-body - (for [[index {:keys [name] :as card}] (indexed (get @decklist board))] - ^{:key (str name "--" board "--tr")} - [decklist-table-row decklist board index card (contains? error-cards name)])]]])))) - -(defn player-info [decklist] - (let [set-first-name #(swap! decklist assoc-in [:player :first-name] %) - set-last-name #(swap! decklist assoc-in [:player :last-name] %) - set-deck-name #(swap! decklist assoc-in [:player :deck-name] %) - set-email #(swap! decklist assoc-in [:player :email] %) - set-dci #(swap! decklist assoc-in [:player :dci] %)] - (fn player-info-render [_] - [:div#player-info - [:div.full-width - [text-field {:on-change set-deck-name - :floating-label-text "Pakan nimi" - :full-width true - :value (get-in @decklist [:player :deck-name])}]] - [:div.half-width.left - [text-field {:on-change set-first-name - :floating-label-text "Etunimi" - :full-width true - :value (get-in @decklist [:player :first-name])}]] - [:div.half-width.right - [text-field {:on-change set-last-name - :floating-label-text "Sukunimi" - :full-width true - :value (get-in @decklist [:player :last-name])}]] - [:div.half-width.left - [text-field {:on-change set-dci - :floating-label-text "DCI-numero" - :full-width true - :value (get-in @decklist [:player :dci])}]] - [:div.half-width.right - [text-field {:on-change set-email - :floating-label-text "Sähköposti" - :full-width true - :value (get-in @decklist [:player :email])}]]]))) - -(def empty-decklist {:main [] - :side [] - :count {:main 0 - :side 0} - :board :main - :player {:dci "" - :first-name "" - :last-name "" - :deck-name "" - :email ""}}) - - + [mtg-pairings-server.components.decklist.submit :as submit] + [mtg-pairings-server.subscriptions :as subs])) (defn decklist-submit [] - (let [saved-decklist (subscribe [::subs/decklist]) - decklist (atom (or @saved-decklist empty-decklist)) - on-change (fn [card] - (swap! decklist add-card card)) - select-main #(swap! decklist assoc :board :main) - select-side #(swap! decklist assoc :board :side) - button-style {:position :relative - :top "-5px" - :width "70px" - :min-width "70px"} - tournament (subscribe [::subs/decklist-tournament]) - page (subscribe [::subs/page]) - save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] - (fn decklist-submit-render [] - [:div#decklist-submit - [:h2 "Lähetä pakkalista"] - [:p.intro - "Lähetä pakkalistasi " - [:span.tournament-date - (format-date (:date @tournament))] - " pelattavaan turnaukseen " - [:span.tournament-name - (:name @tournament)] - ", jonka formaatti on " - [:span.tournament-format - (case (:format @tournament) - :standard "Standard" - :modern "Modern" - :legacy "Legacy")] - "."] - [:h3 "Pakkalista"] - [input on-change] - [ui/raised-button - {:label "Main" - :on-click select-main - :primary (= :main (:board @decklist)) - :style button-style - :button-style {:border-radius "2px 0 0 2px"}}] - [ui/raised-button - {:label "Side" - :on-click select-side - :primary (= :side (:board @decklist)) - :style button-style - :button-style {:border-radius "0 2px 2px 0"}}] - [:div - [decklist-table decklist :main] - [decklist-table decklist :side]] - [:h3 "Pelaajan tiedot"] - [player-info decklist] - [ui/raised-button - {:label "Tallenna" - :on-click save-decklist - :primary true - :disabled (:saving @tournament) - :style {:margin-top "24px"}}] - (when (:saving @tournament) - [ui/circular-progress - {:size 36 - :style {:margin "24px 0 0 24px" - :vertical-align :top}}]) - (when (:saved @tournament) - (let [url (str "https://pairings.fi/decklist/" (:id @page))] - [:div.success-notice - [:h4 "Tallennus onnistui!"] - [:p - "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " - [:a {:href url} - url] - ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))]))) + [submit/decklist-submit]) + From 7878980ec400fe2ae3953dcfdf17c7ebf88d32a7 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 15:53:35 +0300 Subject: [PATCH 08/44] List of tournaments for organizer --- .../src/clj/mtg_pairings_server/handler.clj | 11 +-- .../mtg_pairings_server/service/decklist.clj | 7 ++ .../mtg_pairings_server/styles/decklist.clj | 22 +++++- .../src/cljc/mtg_pairings_server/util.cljc | 3 + .../components/decklist/organizer.cljs | 70 +++++++++++++++++++ .../src/cljs/mtg_pairings_server/core.cljs | 6 +- .../src/cljs/mtg_pairings_server/events.cljs | 12 ++-- .../mtg_pairings_server/pages/decklist.cljs | 7 ++ .../src/cljs/mtg_pairings_server/routes.cljs | 9 +++ .../mtg_pairings_server/subscriptions.cljs | 12 +++- 10 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 52bd564..72fac78 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -96,14 +96,17 @@ (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (loading-page {:page {:page :decklist} + (loading-page {:page {:page :decklist-submit} :decklist-tournament (decklist/get-tournament id)})) + (GET "/decklist/organizer" [] + (loading-page {:page {:page :decklist-organizer} + :decklist-editor {:organizer-tournaments (decklist/get-tournaments)}})) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id)] - (loading-page {:page {:page :decklist, :id id} - :decklist-tournament (decklist/get-tournament (:tournament decklist)) - :decklist decklist}))) + (loading-page {:page {:page :decklist-submit, :id id} + :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) + :decklist decklist}}))) (GET "/robots.txt" [] robots-txt) ws/routes) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 4eb7dd2..3cac0b7 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -100,3 +100,10 @@ :side (transduce (map :quantity) + 0 side)} :board :main :player player})) + +(defn get-tournaments [] + (let [tournaments (sql/select db/decklist-tournament + (sql/fields :id :name :date :format :deadline) + (sql/with db/decklist + (sql/fields :id)))] + (map #(update % :decklist count) tournaments))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 89ce6e0..6f71c31 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -3,7 +3,7 @@ [garden.units :refer [px]] [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) -(defstyles styles +(defstyles submit [:#decklist-submit {:position :relative} (when-desktop @@ -40,3 +40,23 @@ {:width (px 72)}] [:th.error :td.error {:width (px 48)}]]]]) + +(defstyles organizer + [:#decklist-organizer-tournaments + {:margin "12px 24px"} + [:.tournaments + [:th.date :td.date + {:width (px 130)}] + [:th.deadline :td.deadline + {:width (px 160)}] + [:th.decklists :td.decklists + {:width (px 135)}] + [:.tournament-link + {:color :black + :display :block + :height (px 48) + :line-height (px 48)}]]]) + +(defstyles styles + organizer + submit) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index c050535..b88ff41 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -51,6 +51,9 @@ (defn format-iso-date-time [datetime] (some->> datetime (format/unparse (format/formatters :date-time)))) +(defn format-date-time [datetime] + (some->> datetime (format/unparse (format/formatter "dd.MM.yyyy hh:mm")))) + (defn today-or-yesterday? [date] (let [yesterday (time/minus (time/today) (time/days 1)) tomorrow (time/plus (time/today) (time/days 1))] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs new file mode 100644 index 0000000..de27696 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -0,0 +1,70 @@ +(ns mtg-pairings-server.components.decklist.organizer + (:require [reagent.core :as reagent :refer [atom]] + [re-frame.core :refer [subscribe dispatch]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [mtg-pairings-server.events :as events] + [mtg-pairings-server.routes :as routes] + [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.util :refer [format-date format-date-time]])) + +(defn tournament-row [tournament] + (let [column-style {:font-size "14px"} + edit-url (routes/decklist-organizer-tournament-path {:id (:id tournament)}) + submit-url (routes/new-decklist-submit-path {:id (:id tournament)})] + [ui/table-row + [ui/table-row-column {:class-name :date + :style column-style} + [:a.tournament-link {:href edit-url} + (format-date (:date tournament))]] + [ui/table-row-column {:class-name :deadline + :style column-style} + [:a.tournament-link {:href edit-url} + (format-date-time (:deadline tournament))]] + [ui/table-row-column {:class-name :name + :style column-style} + [:a.tournament-link {:href edit-url} + (:name tournament)]] + [ui/table-row-column {:class-name :decklists + :style column-style} + [:a.tournament-link {:href edit-url} + (:decklist tournament)]] + [ui/table-row-column {:class-name :submit-page + :style column-style} + [:a {:href submit-url} + (str "https://pairings.fi" submit-url)]]])) + +(defn all-tournaments [] + (let [tournaments (subscribe [::subs/decklist-organizer-tournaments]) + header-style {:color :black + :font-weight :bold + :font-size "16px" + :height "36px"}] + (fn all-tournaments-render [] + [:div#decklist-organizer-tournaments + [ui/table {:selectable false + :class-name :tournaments} + [ui/table-header {:display-select-all false + :adjust-for-checkbox false} + [ui/table-row {:style {:height "24px"}} + [ui/table-header-column {:class-name :date + :style header-style} + "Päivä"] + [ui/table-header-column {:class-name :deadline + :style header-style} + "Deadline"] + [ui/table-header-column {:class-name :name + :style header-style} + "Turnaus"] + [ui/table-header-column {:class-name :decklists + :style header-style} + "Dekkilistoja"] + [ui/table-header-column {:class-name :submit-page + :style header-style} + "Listojen lähetyssivu"]]] + [ui/table-body + (for [tournament @tournaments] + ^{:key (str (:id tournament) "--row")} + [tournament-row tournament])]]]))) + +(defn tournament [id]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 18764d6..0610113 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -11,7 +11,7 @@ mtg-pairings-server.util.event-listener [mtg-pairings-server.subscriptions :as subs] [mtg-pairings-server.events :as events] - [mtg-pairings-server.pages.decklist :refer [decklist-submit]] + [mtg-pairings-server.pages.decklist :refer [decklist-organizer decklist-submit]] [mtg-pairings-server.pages.main :refer [main-page]] [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] [mtg-pairings-server.pages.organizer :refer [organizer-page organizer-menu deck-construction-tables]] @@ -28,7 +28,8 @@ :picker-header-color "#5d99c6"}})) (defn display-header? [page] - (not (contains? #{:organizer :decklist-submit} (:page page)))) + (not (contains? #{:organizer :decklist-submit :decklist-organizer :decklist-organizer-tournament} + (:page page)))) (defn current-page [] (let [page (subscribe [::subs/page]) @@ -53,6 +54,7 @@ :organizer-menu [#'organizer-menu] :organizer-deck-construction [#'deck-construction-tables] :decklist-submit [#'decklist-submit] + (:decklist-organizer :decklist-organizer-tournament) [#'decklist-organizer] nil)]]]))) (defn mount-root [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index a3aaf1b..65f4041 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -56,7 +56,9 @@ :logged-in-user (local-storage/fetch :user) :notification nil :mobile? (mobile?) - :decklist-tournament {}} + :decklist-editor {:tournament {} + :organizer-tournaments [] + :organizer-tournament {}}} (transit/read (oget js/window "initial_db")))) (defn update-filters-active [db] @@ -361,13 +363,13 @@ (reg-event-fx ::save-decklist (fn [{:keys [db]} [_ tournament decklist]] {:db (-> db - (assoc-in [:decklist-tournament :saving] true) - (assoc :decklist decklist)) + (assoc-in [:decklist-editor :saving] true) + (assoc-in [:decklist-editor :decklist] decklist)) :ws-send [:client/save-decklist [tournament decklist]]})) (reg-event-fx :server/decklist-saved (fn [{:keys [db]} [_ id]] {:db (-> db - (assoc-in [:decklist-tournament :saving] false) - (assoc-in [:decklist-tournament :saved] true)) + (assoc-in [:decklist-editor :saving] false) + (assoc-in [:decklist-editor :saved] true)) :navigate (str "/decklist/" id)})) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 24c0834..fc06dfb 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -1,9 +1,16 @@ (ns mtg-pairings-server.pages.decklist (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [subscribe dispatch]] + [mtg-pairings-server.components.decklist.organizer :as organizer] [mtg-pairings-server.components.decklist.submit :as submit] [mtg-pairings-server.subscriptions :as subs])) (defn decklist-submit [] [submit/decklist-submit]) +(defn decklist-organizer [] + (let [page (subscribe [::subs/page])] + (fn decklist-organizer-render [] + (case (:page @page) + :decklist-organizer-tournament [organizer/tournament (:id @page)] + :decklist-organizer [organizer/all-tournaments])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index 5c0c72a..d8006c9 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -67,6 +67,15 @@ (dispatch [::events/load-deck-construction id]) (dispatch-page :organizer-deck-construction id))) +(secretary/defroute decklist-organizer-path "/decklist/organizer" [] + (dispatch-page :decklist-organizer)) + +(secretary/defroute decklist-organizer-new-tournament-path "/decklist/organizer/new" [] + (dispatch-page :decklist-organizer-tournament)) + +(secretary/defroute decklist-organizer-tournament-path "/decklist/organizer/:id" [id] + (dispatch-page :decklist-organizer-tournament id)) + (secretary/defroute new-decklist-submit-path "/decklist/tournament/:id" [id] (dispatch-page :decklist-submit)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index 3bf2411..86b0ce1 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -153,8 +153,16 @@ (reg-sub ::decklist-tournament (fn [db _] - (:decklist-tournament db))) + (get-in db [:decklist-editor :tournament]))) (reg-sub ::decklist (fn [db _] - (:decklist db))) + (get-in db [:decklist-editor :decklist]))) + +(reg-sub ::decklist-organizer-tournaments + (fn [db _] + (get-in db [:decklist-editor :organizer-tournaments]))) + +(reg-sub ::decklist-organizer-tournament + (fn [db _] + (get-in db [:decklist-editor :organizer-tournament]))) From 76c141419808b3d4df0922fff755101c94a88b8e Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 17:18:27 +0300 Subject: [PATCH 09/44] Page for organizer decklist tournament --- .../src/clj/mtg_pairings_server/handler.clj | 23 +- .../mtg_pairings_server/service/decklist.clj | 32 ++- .../src/clj/mtg_pairings_server/sql_db.clj | 3 +- .../mtg_pairings_server/styles/decklist.clj | 241 +++++++++++++++++- .../src/cljc/mtg_pairings_server/transit.cljc | 2 +- .../cljc/mtg_pairings_server/util/mobile.cljc | 6 + .../components/decklist/organizer.cljs | 195 ++++++++++++-- .../components/decklist/submit.cljs | 8 +- .../src/cljs/mtg_pairings_server/core.cljs | 5 +- .../src/cljs/mtg_pairings_server/events.cljs | 65 +++-- .../mtg_pairings_server/pages/decklist.cljs | 3 +- .../src/cljs/mtg_pairings_server/routes.cljs | 3 + .../mtg_pairings_server/subscriptions.cljs | 8 + 13 files changed, 535 insertions(+), 59 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 72fac78..f8d807b 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -100,7 +100,19 @@ :decklist-tournament (decklist/get-tournament id)})) (GET "/decklist/organizer" [] (loading-page {:page {:page :decklist-organizer} - :decklist-editor {:organizer-tournaments (decklist/get-tournaments)}})) + :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments)}})) + (GET "/decklist/organizer/new" [] + (loading-page {:page {:page :decklist-organizer-tournament}})) + (GET "/decklist/organizer/view/:id" [] + :path-params [id :- s/Str] + (let [decklist (decklist/get-decklist id)] + (loading-page {:page {:page :decklist-organizer-view, :id id} + :decklist-editor {:decklist decklist + :organizer-tournament (decklist/get-organizer-tournament (:tournament decklist))}}))) + (GET "/decklist/organizer/:id" [] + :path-params [id :- s/Str] + (loading-page {:page {:page :decklist-organizer-tournament, :id id} + :decklist-editor {:organizer-tournament (decklist/get-organizer-tournament id)}})) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id)] @@ -211,3 +223,12 @@ [{uid :uid, [tournament decklist] :?data}] (let [id (decklist/save-decklist tournament decklist)] (ws/send! uid [:server/decklist-saved id]))) + +(defmethod ws/event-handler :client/decklist-organizer-tournament + [{uid :uid, id :?data}] + (ws/send! uid [:server/decklist-organizer-tournament (decklist/get-organizer-tournament id)])) + +(defmethod ws/event-handler :client/save-decklist-organizer-tournament + [{uid :uid, tournament :?data}] + (let [id (decklist/save-organizer-tournament tournament)] + (ws/send! uid [:server/organizer-tournament-saved id]))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 3cac0b7..8fb64b2 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -71,9 +71,8 @@ (or old-id new-id)))) (defn get-tournament [id] - (some-> (sql-util/select-unique-or-nil db/decklist-tournament - (sql/where {:id id})) - (update :format keyword))) + (sql-util/select-unique db/decklist-tournament + (sql/where {:id id}))) (defn format-cards [cards maindeck?] (->> cards @@ -101,9 +100,34 @@ :board :main :player player})) -(defn get-tournaments [] +(defn get-organizer-tournaments [] (let [tournaments (sql/select db/decklist-tournament (sql/fields :id :name :date :format :deadline) (sql/with db/decklist (sql/fields :id)))] (map #(update % :decklist count) tournaments))) + +(defn get-organizer-tournament [id] + (sql-util/select-unique db/decklist-tournament + (sql/fields :id :name :date :format :deadline) + (sql/with db/decklist + (sql/fields :id :first-name :last-name :dci :submitted) + (sql/order :submitted :asc)) + (sql/where {:id id}))) + +(defn format-saved-tournament [tournament] + (-> tournament + (select-keys [:name :date :format :deadline]) + (update :format name))) + +(defn save-organizer-tournament [tournament] + (let [old-id (:id tournament) + new-id (sql-util/generate-id)] + (if old-id + (sql-util/update-unique db/decklist-tournament + (sql/set-fields (format-saved-tournament tournament)) + (sql/where {:id old-id})) + (sql/insert db/decklist-tournament + (sql/values (-> (format-saved-tournament tournament) + (assoc :id new-id))))) + (or old-id new-id))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index 1568a0d..e7648bd 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -170,7 +170,8 @@ (sql/has-many decklist {:fk :tournament}) (sql/prepare joda-date->sql-date) - (sql/transform sql-date->joda-date)) + (sql/transform sql-date->joda-date) + (sql/transform #(update % :format keyword))) (sql/defentity decklist (sql/pk :id) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 6f71c31..7903f14 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -1,7 +1,9 @@ (ns mtg-pairings-server.styles.decklist (:require [garden.def :refer [defstyles]] - [garden.units :refer [px]] - [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) + [garden.selectors :refer [after & first-child last-child]] + [garden.units :refer [px px- px+ px* px-div percent vh]] + [mtg-pairings-server.styles.common :refer [color ellipsis-overflow]] + [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile when-screen when-print]])) (defstyles submit [:#decklist-submit @@ -41,6 +43,226 @@ [:th.error :td.error {:width (px 48)}]]]]) +(def grey-border {:style :solid + :width (px 1) + :color (color :grey)}) + +(def body-height (px 952)) +(def left-margin (px 64)) +(def header-line-height (px 36)) +(def top-margin (px+ (px 1) (px* header-line-height 2))) +(def player-height (px 48)) +(def card-width (px 270)) +(def card-height (px 30)) +(def column-gap (px 20)) + +(defstyles cardlists + [:.maindeck :.sideboard + (when-screen + [:& + {:display :inline-block + :vertical-align :top}]) + (when-print + [:h3 + {:margin "16px 0"}]) + [:.cards + [:.card + {:width card-width + :height (px 24) + :line-height (px 24) + :margin-bottom (px 6)} + [:.quantity :.name + {:display :inline-block + :border-bottom grey-border}] + [:.quantity + {:width (px 34) + :text-align :center + :margin "0 8px"}] + [:.name + {:width (px 220) + :padding-left (px 6)}]]]] + [:.maindeck + [:.cards + {:width (px+ card-width column-gap card-width)}] + (when-screen + [:& + {:margin-right column-gap} + [:.cards + {:column {:count 2 + :gap column-gap}}]]) + (when-print + [:& + {:margin-left left-margin + :position :relative} + [:h3 + {:position :absolute + :top 0 + :left 0}] + [:.cards + {:display :flex + :flex {:direction :column + :wrap :wrap} + :align {:content :space-between} + :height body-height + :padding-top (px 22)} + [:.card + [(& first-child) + {:margin-top card-height}]]]])] + [:.sideboard + (when-screen + [:& + {:width card-width}]) + (when-print + [:& + {:position :absolute + :left (px+ left-margin card-width column-gap)} + (for [i (range 16) + :let [free-space (px* card-height (- 30 i)) + top (px+ top-margin free-space)]] + [(keyword (str "&.sideboard-" i)) + {:top top}])])]) + +(defstyles player-info + [:.player-info + {:position :absolute} + [:.last-name :.first-name :.dci :.name + {:display :inline-block}] + (when-screen + [:& + {:height header-line-height + :line-height header-line-height + :font-size (px 16) + :font-weight :bold + :display :inline-block + :right 0 + :top header-line-height + :width (px 300) + :text-align :right}] + [:.name + {:width (px 188)} + [:.last-name + [:.value + {:margin-right (px 5)} + [(& after) + {:content "\", \""}]]]] + [:.dci + {:width (px 100) + :margin-left (px 12)}]) + (when-print + [:& + {:height player-height + :padding-top (px 1) + :line-height (px- player-height (px 2)) + :width body-height + :border-bottom grey-border + :top (px+ (px-div (px- body-height player-height) 2) top-margin) + :left (px-div (px- body-height player-height) -2) + :transform "rotate(270deg)"}] + [:.label + {:vertical-align :top}] + [:.value + {:vertical-align :top + :font-size (px 20)}] + [:.dci + [:.label + {:width (px 40)}] + [:.value + [:.digit + {:display :inline-block + :vertical-align :top + :text-align :center + :border-left grey-border + :width (px 38) + :height (px 36) + :line-height (px 36) + :margin "5px 0"}]]] + [:.name + {:width (px- body-height (px 420))} + [:.first-name :.last-name + {:width (percent 50)} + [:.label + {:width (px 80)}]]])]) + +(defstyles deck-info + [:.deck-info + {:border-bottom grey-border} + [:.tournament-date :.tournament-name :.deck-name + {:display :inline-block + :height header-line-height + :line-height header-line-height}] + (when-screen + [:& + {:font-size (px 16) + :font-weight :bold}] + [:.deck-name + (merge ellipsis-overflow + {:width (px 580)})] + [:.tournament-name + {:width (px 768)}] + [:.tournament-date + {:width (px 100) + :margin-right (px 12)}]) + (when-print + [:& + {:padding-left left-margin} + [:.tournament-date :.tournament-name :.deck-name + [:.label + {:width (px 72) + :text-align :right}] + [:.value + {:padding-left (px 6) + :font-size (px 20)}]] + [:.tournament-date + {:width (px 200)}] + [:.tournament-name + [:a + {:color :black}]] + [:.deck-name + {:width (percent 100)}]])]) + +(defstyles organizer-decklist + [:.organizer-decklist + {:position :relative} + [:.label :.value + {:display :inline-block}] + (when-screen + [:& + {:width (px 880) + :margin "12px auto"}] + [:.label :.first-letters + {:display :none}]) + (when-print + [:& + {:height (px+ body-height top-margin) + :break-after :page}] + [:.label + {:color (color :dark-grey)}] + [:.first-letters + {:position :absolute + :top 0 + :right 0 + :width (px 84)} + [:.label + {:font-size (px 12) + :text-align :center + :width (percent 100)}] + [:.letter + {:font-size (px 32) + :font-weight :bold + :display :inline-block + :width (px 28) + :text-align :center + :text-transform :uppercase}]]) + cardlists + player-info + deck-info]) + +(def table-row-link {:color :black + :display :block + :height (px 48) + :line-height (px 48) + :padding "0 24px"}) + (defstyles organizer [:#decklist-organizer-tournaments {:margin "12px 24px"} @@ -52,10 +274,17 @@ [:th.decklists :td.decklists {:width (px 135)}] [:.tournament-link - {:color :black - :display :block - :height (px 48) - :line-height (px 48)}]]]) + table-row-link]]] + [:#decklist-organizer-tournament + {:margin "12px 24px"} + [:.decklists + [:th.dci :td.dci + {:width (px 150)}] + [:th.name :td.name + {:width (px 400)}] + [:.decklist-link + table-row-link]]] + organizer-decklist) (defstyles styles organizer diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc index d84fac2..98169ab 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/transit.cljc @@ -11,7 +11,7 @@ (def writers {#?(:clj LocalDate, :cljs goog.date.Date) (transit/write-handler (constantly "Date") format-iso-date) - #?(:clj DateTime, :cljs goog.date.DateTime) + #?(:clj DateTime, :cljs goog.date.UtcDateTime) (transit/write-handler (constantly "DateTime") format-iso-date-time) #?@(:clj [Ratio (transit/write-handler (constantly "d") double)])}) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc index 4c46748..73fd131 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc @@ -11,5 +11,11 @@ #?(:clj (defn when-desktop [& styles] (apply at-media {:min-width (px (inc mobile-max-width))} styles))) +#?(:clj (defn when-screen [& styles] + (apply at-media {:screen true} styles))) + +#?(:clj (defn when-print [& styles] + (apply at-media {:print true} styles))) + #?(:cljs (defn mobile? [] (<= (oget js/window "innerWidth") mobile-max-width))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index de27696..834a783 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -3,31 +3,41 @@ [re-frame.core :refer [subscribe dispatch]] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] + [cljs-time.coerce :as coerce] + [oops.core :refer [oget]] [mtg-pairings-server.events :as events] [mtg-pairings-server.routes :as routes] [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.util :refer [format-date format-date-time]])) + [mtg-pairings-server.util :refer [format-date format-date-time to-local-date indexed]] + [mtg-pairings-server.util.material-ui :refer [text-field]])) + +(def table-header-style {:color :black + :font-weight :bold + :font-size "16px" + :height "36px"}) (defn tournament-row [tournament] (let [column-style {:font-size "14px"} edit-url (routes/decklist-organizer-tournament-path {:id (:id tournament)}) - submit-url (routes/new-decklist-submit-path {:id (:id tournament)})] + submit-url (routes/new-decklist-submit-path {:id (:id tournament)}) + link-props {:href edit-url + :on-click #(dispatch [::events/load-decklist-organizer-tournament (:id tournament)])}] [ui/table-row [ui/table-row-column {:class-name :date :style column-style} - [:a.tournament-link {:href edit-url} + [:a.tournament-link link-props (format-date (:date tournament))]] [ui/table-row-column {:class-name :deadline :style column-style} - [:a.tournament-link {:href edit-url} + [:a.tournament-link link-props (format-date-time (:deadline tournament))]] [ui/table-row-column {:class-name :name :style column-style} - [:a.tournament-link {:href edit-url} + [:a.tournament-link link-props (:name tournament)]] [ui/table-row-column {:class-name :decklists :style column-style} - [:a.tournament-link {:href edit-url} + [:a.tournament-link link-props (:decklist tournament)]] [ui/table-row-column {:class-name :submit-page :style column-style} @@ -35,11 +45,7 @@ (str "https://pairings.fi" submit-url)]]])) (defn all-tournaments [] - (let [tournaments (subscribe [::subs/decklist-organizer-tournaments]) - header-style {:color :black - :font-weight :bold - :font-size "16px" - :height "36px"}] + (let [tournaments (subscribe [::subs/decklist-organizer-tournaments])] (fn all-tournaments-render [] [:div#decklist-organizer-tournaments [ui/table {:selectable false @@ -48,23 +54,178 @@ :adjust-for-checkbox false} [ui/table-row {:style {:height "24px"}} [ui/table-header-column {:class-name :date - :style header-style} + :style table-header-style} "Päivä"] [ui/table-header-column {:class-name :deadline - :style header-style} + :style table-header-style} "Deadline"] [ui/table-header-column {:class-name :name - :style header-style} + :style table-header-style} "Turnaus"] [ui/table-header-column {:class-name :decklists - :style header-style} + :style table-header-style} "Dekkilistoja"] [ui/table-header-column {:class-name :submit-page - :style header-style} + :style table-header-style} "Listojen lähetyssivu"]]] [ui/table-body (for [tournament @tournaments] ^{:key (str (:id tournament) "--row")} [tournament-row tournament])]]]))) -(defn tournament [id]) +(defn decklist-table [decklists] + [ui/table {:class-name :tournaments + :multi-selectable true} + [ui/table-header + [ui/table-row {:style {:height "24px"}} + [ui/table-header-column {:class-name :dci + :style table-header-style} + "DCI"] + [ui/table-header-column {:class-name :name + :style table-header-style} + "Nimi"] + [ui/table-header-column {:class-name :submitted + :style table-header-style} + "Lähetetty"]]] + [ui/table-body + (for [decklist decklists + :let [column-style {:font-size "14px" + :padding 0} + decklist-url (routes/decklist-organizer-view-path {:id (:id decklist)}) + link-props {:href decklist-url}]] + ^{:key (str (:id decklist) "--row")} + [ui/table-row {} + [ui/table-row-column {:class-name :dci + :style column-style} + [:a.decklist-link link-props + (:dci decklist)]] + [ui/table-row-column {:class-name :name + :style column-style} + [:a.decklist-link link-props + (str (:last-name decklist) ", " (:first-name decklist))]] + [ui/table-row-column {:class-name :submitted + :style column-style} + [:a.decklist-link link-props + (format-date-time (:submitted decklist))]]])]]) + +(defn tournament [id] + (let [saved-tournament (subscribe [::subs/decklist-organizer-tournament]) + saving? (subscribe [::subs/decklist-saving?]) + tournament (atom @saved-tournament) + set-name #(swap! tournament assoc :name %) + set-date (fn [_ date] + (swap! tournament assoc :date (to-local-date date))) + set-format (fn [_ _ format] + (swap! tournament assoc :format (keyword format))) + save-tournament #(dispatch [::events/save-decklist-organizer-tournament + (select-keys @tournament [:id :name :format :date :deadline])])] + (fn tournament-render [id] + (when (and (nil? @tournament) + (some? @saved-tournament)) + (reset! tournament @saved-tournament)) + [:div#decklist-organizer-tournament + [:div.tournament-info + [:div + [text-field {:on-change set-name + :floating-label-text "Turnauksen nimi" + :value (:name @tournament "")}]] + [:div + [ui/select-field {:on-change set-format + :value (:format @tournament) + :floating-label-text "Formaatti"} + [ui/menu-item {:value :standard + :primary-text "Standard"}] + [ui/menu-item {:value :modern + :primary-text "Modern"}] + [ui/menu-item {:value :legacy + :primary-text "Legacy"}]]] + [:div + [ui/date-picker {:value (some-> (:date @tournament) + (coerce/to-long) + (js/Date.)) + :on-change set-date + :container :inline + :dialog-container-style {:left "-9999px"} + :floating-label-text "Päivämäärä" + :locale "fi-FI" + :auto-ok true + :DateTimeFormat (oget js/Intl "DateTimeFormat")}]] + [:div + [ui/raised-button {:label "Tallenna" + :on-click save-tournament + :primary true + :disabled @saving?}] + (when @saving? + [ui/circular-progress + {:size 36 + :style {:margin-left "24px" + :vertical-align :top}}])]] + [:div.decklists + [decklist-table (:decklist @tournament)]]]))) + +(defn decklist-card [card] + [:div.card + [:div.quantity + (:quantity card)] + [:div.name + (:name card)]]) + +(defn render-decklist [decklist tournament] + (let [{:keys [player main side id], counts :count} decklist + {:keys [last-name first-name dci deck-name]} player + dci (vec dci) + {tournament-id :id, tournament-name :name, date :date} tournament] + [:div.organizer-decklist + [:div.first-letters + [:div.label "Alkukirjaimet"] + [:div.letter + (nth last-name 0)] + [:div.letter + (nth last-name 1)] + [:div.letter + (nth last-name 2)]] + [:div.deck-info + [:div.tournament-date + [:div.label "Päivä:"] + [:div.value (format-date date)]] + [:div.tournament-name + [:div.label "Turnaus:"] + [:div.value + [:a {:href (routes/decklist-organizer-tournament-path {:id tournament-id})} + tournament-name]]] + [:div.deck-name + [:div.label "Pakka:"] + [:div.value deck-name]]] + [:div.player-info + [:div.name + [:div.last-name + [:div.label "Sukunimi:"] + [:div.value last-name]] + [:div.first-name + [:div.label "Etunimi:"] + [:div.value first-name]]] + [:div.dci + [:div.label "DCI:"] + [:div.value + (for [index (range 10)] + ^{:key (str id "--dci--" index)} + [:span.digit (get dci index)])]]] + [:div.maindeck + [:h3 "Maindeck (" (counts :main) ")"] + [:div.cards + (for [card main] + ^{:key (str id "--main--" (:name card))} + [decklist-card card])]] + [:div.sideboard + {:class (str "sideboard-" (count side))} + [:h3 "Sideboard (" (counts :side) ")"] + [:div.cards + (for [card side] + ^{:key (str id "--side--" (:name card))} + [decklist-card card])]]])) + +(defn view-decklist [] + (let [decklist (subscribe [::subs/decklist]) + tournament (subscribe [::subs/decklist-organizer-tournament])] + (fn view-decklist-render [] + [render-decklist @decklist @tournament]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 6266e3b..d29f20e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -205,6 +205,8 @@ :width "70px" :min-width "70px"} tournament (subscribe [::subs/decklist-tournament]) + saving? (subscribe [::subs/decklist-saving?]) + saved? (subscribe [::subs/decklist-saved?]) page (subscribe [::subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] @@ -247,14 +249,14 @@ {:label "Tallenna" :on-click save-decklist :primary true - :disabled (:saving @tournament) + :disabled @saving? :style {:margin-top "24px"}}] - (when (:saving @tournament) + (when @saving? [ui/circular-progress {:size 36 :style {:margin "24px 0 0 24px" :vertical-align :top}}]) - (when (:saved @tournament) + (when @saved? (let [url (str "https://pairings.fi/decklist/" (:id @page))] [:div.success-notice [:h4 "Tallennus onnistui!"] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 0610113..90f794a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -28,7 +28,8 @@ :picker-header-color "#5d99c6"}})) (defn display-header? [page] - (not (contains? #{:organizer :decklist-submit :decklist-organizer :decklist-organizer-tournament} + (not (contains? #{:organizer :decklist-submit :decklist-organizer + :decklist-organizer-tournament :decklist-organizer-view} (:page page)))) (defn current-page [] @@ -54,7 +55,7 @@ :organizer-menu [#'organizer-menu] :organizer-deck-construction [#'deck-construction-tables] :decklist-submit [#'decklist-submit] - (:decklist-organizer :decklist-organizer-tournament) [#'decklist-organizer] + (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer] nil)]]]))) (defn mount-root [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 65f4041..8b9e521 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -36,29 +36,27 @@ (accountant/navigate! path))) (defn initial-db [] - (deep-merge {:tournaments {} - :tournament-count 0 - :tournaments-page 0 - :tournament-ids [] - :tournament-filter {:organizer "" - :date-from nil - :date-to nil - :players [0 100]} - :filters-active false - :max-players 100 - :player-tournaments [] - :pairings {:sort-key :table_number} - :pods {:sort-key :pod} - :seatings {:sort-key :table_number} - :page {:page :main - :id nil - :round nil} - :logged-in-user (local-storage/fetch :user) - :notification nil - :mobile? (mobile?) - :decklist-editor {:tournament {} - :organizer-tournaments [] - :organizer-tournament {}}} + (deep-merge {:tournaments {} + :tournament-count 0 + :tournaments-page 0 + :tournament-ids [] + :tournament-filter {:organizer "" + :date-from nil + :date-to nil + :players [0 100]} + :filters-active false + :max-players 100 + :player-tournaments [] + :pairings {:sort-key :table_number} + :pods {:sort-key :pod} + :seatings {:sort-key :table_number} + :page {:page :main + :id nil + :round nil} + :logged-in-user (local-storage/fetch :user) + :notification nil + :mobile? (mobile?) + :decklist-editor {:organizer-tournaments []}} (transit/read (oget js/window "initial_db")))) (defn update-filters-active [db] @@ -373,3 +371,24 @@ (assoc-in [:decklist-editor :saving] false) (assoc-in [:decklist-editor :saved] true)) :navigate (str "/decklist/" id)})) + +(reg-event-fx ::load-decklist-organizer-tournament + (fn [_ [_ id]] + {:ws-send [:client/decklist-organizer-tournament id]})) + +(reg-event-db :server/decklist-organizer-tournament + (fn [db [_ tournament]] + (assoc-in db [:decklist-editor :organizer-tournament] tournament))) + +(reg-event-fx ::save-decklist-organizer-tournament + (fn [{:keys [db]} [_ tournament]] + {:db (assoc-in db [:decklist-editor :saving] true) + :ws-send [:client/save-decklist-organizer-tournament tournament]})) + +(reg-event-fx :server/organizer-tournament-saved + (fn [{:keys [db]} [_ id]] + {:db (-> db + (assoc-in [:decklist-editor :organizer-tournament :id] id) + (assoc-in [:decklist-editor :saving] false) + (assoc-in [:decklist-editor :saved] true)) + :navigate (str "/decklist/organizer/" id)})) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index fc06dfb..e9187e8 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -13,4 +13,5 @@ (fn decklist-organizer-render [] (case (:page @page) :decklist-organizer-tournament [organizer/tournament (:id @page)] - :decklist-organizer [organizer/all-tournaments])))) + :decklist-organizer [organizer/all-tournaments] + :decklist-organizer-view [organizer/view-decklist])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index d8006c9..9486f9b 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -70,6 +70,9 @@ (secretary/defroute decklist-organizer-path "/decklist/organizer" [] (dispatch-page :decklist-organizer)) +(secretary/defroute decklist-organizer-view-path "/decklist/organizer/view/:id" [id] + (dispatch-page :decklist-organizer-view id)) + (secretary/defroute decklist-organizer-new-tournament-path "/decklist/organizer/new" [] (dispatch-page :decklist-organizer-tournament)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index 86b0ce1..114c5e2 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -155,6 +155,14 @@ (fn [db _] (get-in db [:decklist-editor :tournament]))) +(reg-sub ::decklist-saving? + (fn [db _] + (get-in db [:decklist-editor :saving]))) + +(reg-sub ::decklist-saved? + (fn [db _] + (get-in db [:decklist-editor :saved]))) + (reg-sub ::decklist (fn [db _] (get-in db [:decklist-editor :decklist]))) From bee5dd3d5977fe7d17d918c68499c33e4deda2f5 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 21:33:58 +0300 Subject: [PATCH 10/44] Support for printing multiple decklists --- .../src/clj/mtg_pairings_server/handler.clj | 11 ++++ .../mtg_pairings_server/service/decklist.clj | 13 +++-- .../components/decklist/organizer.cljs | 56 ++++++++++++++++--- .../src/cljs/mtg_pairings_server/core.cljs | 2 +- .../src/cljs/mtg_pairings_server/events.cljs | 16 ++++++ .../mtg_pairings_server/pages/decklist.cljs | 17 +++--- .../src/cljs/mtg_pairings_server/routes.cljs | 3 + .../mtg_pairings_server/subscriptions.cljs | 4 ++ 8 files changed, 97 insertions(+), 25 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index f8d807b..2134a46 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -232,3 +232,14 @@ [{uid :uid, tournament :?data}] (let [id (decklist/save-organizer-tournament tournament)] (ws/send! uid [:server/organizer-tournament-saved id]))) + +(defmethod ws/event-handler :client/load-organizer-tournament-decklist + [{uid :uid, id :?data}] + (ws/send! uid [:server/organizer-tournament-decklist (decklist/get-decklist id)])) + +(defmethod ws/event-handler :client/load-organizer-tournament-decklists + [{uid :uid, ids :?data}] + (ws/send! uid [:server/organizer-tournament-decklists (->> (map decklist/get-decklist ids) + (sort-by (fn [d] + [(get-in d [:player :last-name]) + (get-in d [:player :first-name])])))])) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 8fb64b2..7bd7a39 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -108,12 +108,13 @@ (map #(update % :decklist count) tournaments))) (defn get-organizer-tournament [id] - (sql-util/select-unique db/decklist-tournament - (sql/fields :id :name :date :format :deadline) - (sql/with db/decklist - (sql/fields :id :first-name :last-name :dci :submitted) - (sql/order :submitted :asc)) - (sql/where {:id id}))) + (-> (sql-util/select-unique db/decklist-tournament + (sql/fields :id :name :date :format :deadline) + (sql/with db/decklist + (sql/fields :id :first-name :last-name :dci :submitted) + (sql/order :submitted :asc)) + (sql/where {:id id})) + (update :decklist vec))) (defn format-saved-tournament [tournament] (-> tournament diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index 834a783..a030715 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -73,9 +73,10 @@ ^{:key (str (:id tournament) "--row")} [tournament-row tournament])]]]))) -(defn decklist-table [decklists] +(defn decklist-table [decklists on-select] [ui/table {:class-name :tournaments - :multi-selectable true} + :multi-selectable true + :on-row-selection on-select} [ui/table-header [ui/table-row {:style {:height "24px"}} [ui/table-header-column {:class-name :dci @@ -88,11 +89,13 @@ :style table-header-style} "Lähetetty"]]] [ui/table-body + {:deselect-on-clickaway false} (for [decklist decklists :let [column-style {:font-size "14px" :padding 0} decklist-url (routes/decklist-organizer-view-path {:id (:id decklist)}) - link-props {:href decklist-url}]] + link-props {:href decklist-url + :on-click #(dispatch [::events/load-organizer-tournament-decklist (:id decklist)])}]] ^{:key (str (:id decklist) "--row")} [ui/table-row {} [ui/table-row-column {:class-name :dci @@ -118,7 +121,16 @@ set-format (fn [_ _ format] (swap! tournament assoc :format (keyword format))) save-tournament #(dispatch [::events/save-decklist-organizer-tournament - (select-keys @tournament [:id :name :format :date :deadline])])] + (select-keys @tournament [:id :name :format :date :deadline])]) + selected-decklists (atom []) + on-select (fn [selection] + (let [selection (js->clj selection)] + (reset! selected-decklists (case selection + "all" (:decklist @tournament) + "none" [] + (map (:decklist @tournament) selection))))) + load-selected-decklists #(dispatch [::events/load-organizer-tournament-decklists + (map :id @selected-decklists)])] (fn tournament-render [id] (when (and (nil? @tournament) (some? @saved-tournament)) @@ -155,13 +167,24 @@ :on-click save-tournament :primary true :disabled @saving?}] - (when @saving? + (if @saving? [ui/circular-progress {:size 36 :style {:margin-left "24px" - :vertical-align :top}}])]] + :margin-right "24px" + :vertical-align :top}}] + [:div.placeholder + {:style {:display :inline-block + :width "84px" + :height "36px" + :vertical-align :top}}]) + [ui/raised-button {:label "Tulosta valitut listat" + :href (routes/decklist-organizer-print-path) + :on-click load-selected-decklists + :primary true + :disabled (empty? @selected-decklists)}]]] [:div.decklists - [decklist-table (:decklist @tournament)]]]))) + [decklist-table (:decklist @tournament) on-select]]]))) (defn decklist-card [card] [:div.card @@ -211,14 +234,14 @@ ^{:key (str id "--dci--" index)} [:span.digit (get dci index)])]]] [:div.maindeck - [:h3 "Maindeck (" (counts :main) ")"] + [:h3 "Maindeck (" (:main counts) ")"] [:div.cards (for [card main] ^{:key (str id "--main--" (:name card))} [decklist-card card])]] [:div.sideboard {:class (str "sideboard-" (count side))} - [:h3 "Sideboard (" (counts :side) ")"] + [:h3 "Sideboard (" (:side counts) ")"] [:div.cards (for [card side] ^{:key (str id "--side--" (:name card))} @@ -229,3 +252,18 @@ tournament (subscribe [::subs/decklist-organizer-tournament])] (fn view-decklist-render [] [render-decklist @decklist @tournament]))) + +(defn view-decklists [] + (let [decklists (subscribe [::subs/decklists]) + tournament (subscribe [::subs/decklist-organizer-tournament]) + print-page #(when (and (seq @decklists) + @tournament) + (.print js/window))] + (reagent/create-class + {:component-did-mount print-page + :component-did-update print-page + :reagent-render (fn view-decklists-render [] + [:div + (doall (for [decklist @decklists] + ^{:key (:id decklist)} + [render-decklist decklist @tournament]))])}))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 90f794a..bcdc369 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -55,7 +55,7 @@ :organizer-menu [#'organizer-menu] :organizer-deck-construction [#'deck-construction-tables] :decklist-submit [#'decklist-submit] - (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer] + (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer @page] nil)]]]))) (defn mount-root [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 8b9e521..c7b9d72 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -392,3 +392,19 @@ (assoc-in [:decklist-editor :saving] false) (assoc-in [:decklist-editor :saved] true)) :navigate (str "/decklist/organizer/" id)})) + +(reg-event-fx ::load-organizer-tournament-decklist + (fn [_ [_ id]] + {:ws-send [:client/load-organizer-tournament-decklist id]})) + +(reg-event-fx ::load-organizer-tournament-decklists + (fn [_ [_ id]] + {:ws-send [:client/load-organizer-tournament-decklists id]})) + +(reg-event-db :server/organizer-tournament-decklist + (fn [db [_ decklist]] + (assoc-in db [:decklist-editor :decklist] decklist))) + +(reg-event-db :server/organizer-tournament-decklists + (fn [db [_ decklists]] + (assoc-in db [:decklist-editor :decklists] decklists))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index e9187e8..1a431df 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -2,16 +2,15 @@ (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [subscribe dispatch]] [mtg-pairings-server.components.decklist.organizer :as organizer] - [mtg-pairings-server.components.decklist.submit :as submit] - [mtg-pairings-server.subscriptions :as subs])) + [mtg-pairings-server.components.decklist.submit :as submit])) (defn decklist-submit [] [submit/decklist-submit]) -(defn decklist-organizer [] - (let [page (subscribe [::subs/page])] - (fn decklist-organizer-render [] - (case (:page @page) - :decklist-organizer-tournament [organizer/tournament (:id @page)] - :decklist-organizer [organizer/all-tournaments] - :decklist-organizer-view [organizer/view-decklist])))) +(defn decklist-organizer [page] + (case (:page page) + :decklist-organizer-tournament [organizer/tournament (:id page)] + :decklist-organizer [organizer/all-tournaments] + :decklist-organizer-view (if (:id page) + [organizer/view-decklist] + [organizer/view-decklists]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index 9486f9b..305c473 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -70,6 +70,9 @@ (secretary/defroute decklist-organizer-path "/decklist/organizer" [] (dispatch-page :decklist-organizer)) +(secretary/defroute decklist-organizer-print-path "/decklist/organizer/print" [] + (dispatch-page :decklist-organizer-view)) + (secretary/defroute decklist-organizer-view-path "/decklist/organizer/view/:id" [id] (dispatch-page :decklist-organizer-view id)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index 114c5e2..e054158 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -167,6 +167,10 @@ (fn [db _] (get-in db [:decklist-editor :decklist]))) +(reg-sub ::decklists + (fn [db _] + (get-in db [:decklist-editor :decklists]))) + (reg-sub ::decklist-organizer-tournaments (fn [db _] (get-in db [:decklist-editor :organizer-tournaments]))) From 7a57625e32b03927347f75efa0abddeeb108f1e0 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 6 Apr 2019 23:37:08 +0300 Subject: [PATCH 11/44] Login functionality for decklist organizer --- mtg-pairings-server/project.clj | 1 + .../src/clj/mtg_pairings_server/api/http.clj | 15 +++---- .../src/clj/mtg_pairings_server/auth.clj | 45 +++++++++++++++++++ .../src/clj/mtg_pairings_server/handler.clj | 11 +++-- .../clj/mtg_pairings_server/middleware.clj | 9 ++-- .../mtg_pairings_server/middleware/log.clj | 3 +- .../src/clj/mtg_pairings_server/sql_db.clj | 4 ++ .../components/decklist/organizer.cljs | 21 +++++++++ .../src/cljs/mtg_pairings_server/events.cljs | 4 ++ .../mtg_pairings_server/pages/decklist.cljs | 19 +++++--- .../mtg_pairings_server/subscriptions.cljs | 4 ++ 11 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index 57a74e9..478f33b 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -6,6 +6,7 @@ [org.clojure/tools.logging "0.4.1"] [ring/ring-core "1.7.1"] [ring/ring-defaults "0.3.2"] + [buddy/buddy-auth "2.1.0"] [hiccup "1.0.5"] [yogthos/config "1.1.1"] [org.clojure/core.cache "0.7.2"] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj index 4638840..4243c7e 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj @@ -15,11 +15,10 @@ {:ui "/api-docs" :spec "/swagger.json" :data {:info {:title "WER pairings backend API"}}}) - (context "/api" [] - (context "/card" [] card-routes) - (context "/tournament" [] tournament-routes) - (context "/player" [] player-routes) - (GET "/client-version" [] - :no-doc true - {:status 200 - :body {:version (env :client-version)}}))) + (context "/card" [] card-routes) + (context "/tournament" [] tournament-routes) + (context "/player" [] player-routes) + (GET "/client-version" [] + :no-doc true + {:status 200 + :body {:version (env :client-version)}})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj new file mode 100644 index 0000000..3888d63 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -0,0 +1,45 @@ +(ns mtg-pairings-server.auth + (:require [buddy.auth.backends.session :refer [session-backend]] + [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] + [compojure.api.sweet :refer :all] + [ring.util.response :refer [redirect]] + [schema.core :as s] + [korma.core :as sql] + [mtg-pairings-server.sql-db :as db] + [mtg-pairings-server.util.sql :as sql-util] + [clojure.string :as str]) + (:import (java.security MessageDigest))) + +(defonce auth-backend (session-backend)) + +(defn wrap-auth [handler] + (-> handler + (wrap-authorization auth-backend) + (wrap-authentication auth-backend))) + +(defn ^:private ->hex [#^bytes bytes] + (.toString (BigInteger. 1 bytes) 16)) + +(defn ^:private authenticate [username password] + (when-let [user (sql-util/select-unique-or-nil db/smf-user + (sql/fields [:id_member :id] [:passwd :hash]) + (sql/where {:member_name username}))] + (let [input (str (str/lower-case username) password) + md (MessageDigest/getInstance "SHA-1") + _ (.update md (.getBytes input "UTF-8")) + sha1 (->hex (.digest md))] + (when (= sha1 (:hash user)) + {:id (:id user) + :username username})))) + +(defroutes auth-routes + (POST "/login" request + :form-params [username :- s/Str + password :- s/Str + __anti-forgery-token :- s/Str] + :query-params [next :- s/Str] + (if-let [user (authenticate username password)] + (let [new-session (assoc (:session request) :identity user)] + (-> (redirect next :see-other) + (assoc :session new-session))) + (redirect "/decklist/organizer" :see-other)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 2134a46..db4abfa 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -9,6 +9,7 @@ [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] [ring.middleware.jsonp :refer [wrap-json-with-padding]] [mtg-pairings-server.api.http :as http-api] + [mtg-pairings-server.auth :refer [auth-routes]] [mtg-pairings-server.middleware :refer [wrap-site-middleware]] [mtg-pairings-server.middleware.cors :refer [wrap-allow-origin]] [mtg-pairings-server.middleware.log :refer [wrap-request-log]] @@ -120,10 +121,12 @@ :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) :decklist decklist}}))) (GET "/robots.txt" [] robots-txt) + auth-routes ws/routes) (defroutes app-routes - http-api/app + (context "/api" [] + http-api/app) (wrap-site-middleware (routes site-routes @@ -144,9 +147,11 @@ (broadcast/disconnect uid)) (defmethod ws/event-handler :client/connect - [{:keys [uid]}] + [{:keys [uid ring-req]}] (log/debugf "New connection from %s" uid) - (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) + (ws/send! uid [:server/tournaments (tournament/client-tournaments)]) + (when-let [user (get-in ring-req [:session :identity])] + (ws/send! uid [:server/organizer-login (:username user)]))) (defmethod ws/event-handler :client/login [{uid :uid, dci-number :?data}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj index 1190b4e..b9414a6 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj @@ -1,6 +1,7 @@ (ns mtg-pairings-server.middleware (:require [ring.middleware.defaults :refer [site-defaults api-defaults wrap-defaults]] [config.core :refer [env]] + [mtg-pairings-server.auth :refer [wrap-auth]] [mtg-pairings-server.middleware.cache-control :refer [wrap-cache-control]] [mtg-pairings-server.middleware.error :refer [wrap-errors]])) @@ -18,6 +19,8 @@ wrap-errors)) (defn wrap-site-middleware [handler] - (cond-> (wrap-defaults handler (update site-defaults :security dissoc :frame-options :content-type-options)) - (env :dev) add-dev-middleware - (not (env :dev)) add-prod-middleware)) + (cond-> handler + true (wrap-auth) + true (wrap-defaults (update site-defaults :security dissoc :frame-options :content-type-options)) + (env :dev) (add-dev-middleware) + (not (env :dev)) (add-prod-middleware))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj index fd4c5be..ca20bf4 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj @@ -4,7 +4,8 @@ (def ^:private log-blacklist [#"^.*\.(ico|png|jpg|js|css|woff2|txt|map)$" - #"^/chsk$"]) + #"^/chsk$" + #"^/api/card"]) (defn ^:private logged? [uri] (not-any? #(re-matches % uri) log-blacklist)) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index e7648bd..fb6d5a1 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -188,3 +188,7 @@ {:fk :decklist}) (sql/belongs-to card {:fk :card})) + +(sql/defentity smf-user + (sql/table :smf_members) + (sql/pk :id_member)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index a030715..894970e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -267,3 +267,24 @@ (doall (for [decklist @decklists] ^{:key (:id decklist)} [render-decklist decklist @tournament]))])}))) + +(defn ^:private no-op []) + +(defn login [] + [:div#decklist-organizer-login + [:p "Kirjaudu sisään MtgSuomi-tunnuksillasi."] + [:form {:action (str "/login?next=" (oget js/window "location" "pathname")) + :method :post} + [:input {:type :hidden + :name :__anti-forgery-token + :value (oget js/window "csrf_token")}] + [text-field {:name :username + :floating-label-text "Käyttäjätunnus" + :on-change no-op}] + [text-field {:name :password + :type :password + :floating-label-text "Salasana" + :on-change no-op}] + [ui/raised-button {:type :submit + :label "Kirjaudu" + :primary true}]]]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index c7b9d72..12450ae 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -408,3 +408,7 @@ (reg-event-db :server/organizer-tournament-decklists (fn [db [_ decklists]] (assoc-in db [:decklist-editor :decklists] decklists))) + +(reg-event-db :server/organizer-login + (fn [db [_ username]] + (assoc-in db [:decklist-editor :user] username))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 1a431df..1199ec8 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -2,15 +2,20 @@ (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [subscribe dispatch]] [mtg-pairings-server.components.decklist.organizer :as organizer] - [mtg-pairings-server.components.decklist.submit :as submit])) + [mtg-pairings-server.components.decklist.submit :as submit] + [mtg-pairings-server.subscriptions :as subs])) (defn decklist-submit [] [submit/decklist-submit]) (defn decklist-organizer [page] - (case (:page page) - :decklist-organizer-tournament [organizer/tournament (:id page)] - :decklist-organizer [organizer/all-tournaments] - :decklist-organizer-view (if (:id page) - [organizer/view-decklist] - [organizer/view-decklists]))) + (let [user (subscribe [::subs/decklist-organizer-user])] + (fn decklist-organizer-render [page] + (if @user + (case (:page page) + :decklist-organizer-tournament [organizer/tournament (:id page)] + :decklist-organizer [organizer/all-tournaments] + :decklist-organizer-view (if (:id page) + [organizer/view-decklist] + [organizer/view-decklists])) + [organizer/login])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs index e054158..20c0f41 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs @@ -178,3 +178,7 @@ (reg-sub ::decklist-organizer-tournament (fn [db _] (get-in db [:decklist-editor :organizer-tournament]))) + +(reg-sub ::decklist-organizer-user + (fn [db _] + (get-in db [:decklist-editor :user]))) From 8e60811a1b742548eae98c667e6076c0febc391b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sun, 7 Apr 2019 00:25:26 +0300 Subject: [PATCH 12/44] Secure endpoints with tournament data --- .../src/clj/mtg_pairings_server/handler.clj | 54 ++++++++++++------- .../mtg_pairings_server/middleware/log.clj | 2 +- .../mtg_pairings_server/service/decklist.clj | 41 ++++++++------ .../mtg_pairings_server/pages/decklist.cljs | 14 +++-- 4 files changed, 72 insertions(+), 39 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index db4abfa..087b37f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -56,6 +56,9 @@ :body "User-agent: *\nDisallow: /\n" :headers {"Content-Type" "text/plain"}}) +(def forbidden {:status 403 + :body "403 Forbidden"}) + (defroutes site-routes (GET "/" [] (loading-page)) (GET "/tournaments" [] @@ -97,23 +100,33 @@ (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (loading-page {:page {:page :decklist-submit} - :decklist-tournament (decklist/get-tournament id)})) - (GET "/decklist/organizer" [] + (loading-page {:page {:page :decklist-submit} + :decklist-editor {:tournament (decklist/get-tournament id)}})) + (GET "/decklist/organizer" request (loading-page {:page {:page :decklist-organizer} - :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments)}})) + :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) (GET "/decklist/organizer/new" [] (loading-page {:page {:page :decklist-organizer-tournament}})) - (GET "/decklist/organizer/view/:id" [] + (GET "/decklist/organizer/view/:id" request :path-params [id :- s/Str] - (let [decklist (decklist/get-decklist id)] - (loading-page {:page {:page :decklist-organizer-view, :id id} - :decklist-editor {:decklist decklist - :organizer-tournament (decklist/get-organizer-tournament (:tournament decklist))}}))) - (GET "/decklist/organizer/:id" [] + (let [decklist (decklist/get-decklist id) + tournament (decklist/get-organizer-tournament (:tournament decklist)) + user-id (get-in request [:session :identity :id])] + (cond + (not user-id) (loading-page {:page {:page :decklist-organizer-view, :id id}}) + (= user-id (:user tournament)) (loading-page {:page {:page :decklist-organizer-view, :id id} + :decklist-editor {:decklist decklist + :organizer-tournament tournament}}) + :else forbidden))) + (GET "/decklist/organizer/:id" request :path-params [id :- s/Str] - (loading-page {:page {:page :decklist-organizer-tournament, :id id} - :decklist-editor {:organizer-tournament (decklist/get-organizer-tournament id)}})) + (let [tournament (decklist/get-organizer-tournament id) + user-id (get-in request [:session :identity :id])] + (cond + (not user-id) (loading-page {:page {:page :decklist-organizer-tournament, :id id}}) + (= user-id (:user tournament)) (loading-page {:page {:page :decklist-organizer-tournament, :id id} + :decklist-editor {:organizer-tournament tournament}}) + :else forbidden))) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id)] @@ -150,8 +163,7 @@ [{:keys [uid ring-req]}] (log/debugf "New connection from %s" uid) (ws/send! uid [:server/tournaments (tournament/client-tournaments)]) - (when-let [user (get-in ring-req [:session :identity])] - (ws/send! uid [:server/organizer-login (:username user)]))) + (ws/send! uid [:server/organizer-login (get-in ring-req [:session :identity :username] false)])) (defmethod ws/event-handler :client/login [{uid :uid, dci-number :?data}] @@ -230,13 +242,17 @@ (ws/send! uid [:server/decklist-saved id]))) (defmethod ws/event-handler :client/decklist-organizer-tournament - [{uid :uid, id :?data}] - (ws/send! uid [:server/decklist-organizer-tournament (decklist/get-organizer-tournament id)])) + [{uid :uid, id :?data, ring-req :ring-req}] + (let [tournament (decklist/get-organizer-tournament id)] + (when (= (get-in ring-req [:session :identity :id]) (:user tournament)) + (ws/send! uid [:server/decklist-organizer-tournament tournament])))) (defmethod ws/event-handler :client/save-decklist-organizer-tournament - [{uid :uid, tournament :?data}] - (let [id (decklist/save-organizer-tournament tournament)] - (ws/send! uid [:server/organizer-tournament-saved id]))) + [{uid :uid, tournament :?data, ring-req :ring-req}] + (let [user-id (get-in ring-req [:session :identity :id]) + id (decklist/save-organizer-tournament user-id tournament)] + (when id + (ws/send! uid [:server/organizer-tournament-saved id])))) (defmethod ws/event-handler :client/load-organizer-tournament-decklist [{uid :uid, id :?data}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj index ca20bf4..ebf073a 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj @@ -5,7 +5,7 @@ (def ^:private log-blacklist [#"^.*\.(ico|png|jpg|js|css|woff2|txt|map)$" #"^/chsk$" - #"^/api/card"]) + #"^/api/card/search"]) (defn ^:private logged? [uri] (not-any? #(re-matches % uri) log-blacklist)) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 7bd7a39..f691fec 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -100,16 +100,18 @@ :board :main :player player})) -(defn get-organizer-tournaments [] - (let [tournaments (sql/select db/decklist-tournament - (sql/fields :id :name :date :format :deadline) - (sql/with db/decklist - (sql/fields :id)))] - (map #(update % :decklist count) tournaments))) +(defn get-organizer-tournaments [user-id] + (when user-id + (let [tournaments (sql/select db/decklist-tournament + (sql/fields :id :name :date :format :deadline) + (sql/with db/decklist + (sql/fields :id)) + (sql/where {:user user-id}))] + (map #(update % :decklist count) tournaments)))) (defn get-organizer-tournament [id] (-> (sql-util/select-unique db/decklist-tournament - (sql/fields :id :name :date :format :deadline) + (sql/fields :id :user :name :date :format :deadline) (sql/with db/decklist (sql/fields :id :first-name :last-name :dci :submitted) (sql/order :submitted :asc)) @@ -121,14 +123,21 @@ (select-keys [:name :date :format :deadline]) (update :format name))) -(defn save-organizer-tournament [tournament] +(defn save-organizer-tournament [user-id tournament] (let [old-id (:id tournament) + existing (when old-id + (sql-util/select-unique-or-nil db/decklist-tournament + (sql/where {:id old-id}))) new-id (sql-util/generate-id)] - (if old-id - (sql-util/update-unique db/decklist-tournament - (sql/set-fields (format-saved-tournament tournament)) - (sql/where {:id old-id})) - (sql/insert db/decklist-tournament - (sql/values (-> (format-saved-tournament tournament) - (assoc :id new-id))))) - (or old-id new-id))) + (cond + (not existing) (do + (sql/insert db/decklist-tournament + (sql/values (-> (format-saved-tournament tournament) + (assoc :id new-id + :user user-id)))) + new-id) + (= user-id (:user existing)) (do + (sql-util/update-unique db/decklist-tournament + (sql/set-fields (format-saved-tournament tournament)) + (sql/where {:id old-id})) + old-id)))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 1199ec8..7c0f897 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -1,6 +1,8 @@ (ns mtg-pairings-server.pages.decklist (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [subscribe dispatch]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] [mtg-pairings-server.components.decklist.organizer :as organizer] [mtg-pairings-server.components.decklist.submit :as submit] [mtg-pairings-server.subscriptions :as subs])) @@ -8,14 +10,20 @@ (defn decklist-submit [] [submit/decklist-submit]) +(defn loading-indicator [] + [ui/circular-progress + {:size 36 + :style {:margin "24px"}}]) + (defn decklist-organizer [page] (let [user (subscribe [::subs/decklist-organizer-user])] (fn decklist-organizer-render [page] - (if @user + (case @user + nil [loading-indicator] + false [organizer/login] (case (:page page) :decklist-organizer-tournament [organizer/tournament (:id page)] :decklist-organizer [organizer/all-tournaments] :decklist-organizer-view (if (:id page) [organizer/view-decklist] - [organizer/view-decklists])) - [organizer/login])))) + [organizer/view-decklists])))))) From 31db08c4fc926e0cd8bb0ec5a7cd01b3664245f3 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sun, 7 Apr 2019 00:56:48 +0300 Subject: [PATCH 13/44] Remove cards from decklist --- .../mtg_pairings_server/styles/decklist.clj | 2 ++ .../src/cljc/mtg_pairings_server/util.cljc | 11 +++++++ .../components/decklist/submit.cljs | 33 ++++++++++++++----- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 7903f14..f57542f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -40,6 +40,8 @@ [:.deck-table [:th.quantity :td.quantity {:width (px 72)}] + [:th.actions :td.actions + {:width (px 48)}] [:th.error :td.error {:width (px 48)}]]]]) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index b88ff41..520e741 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -107,6 +107,17 @@ (defn some-value [pred coll] (first (filter pred coll))) +(defn index-where [pred coll] + (loop [i 0 + [x & xs] coll] + (cond + (pred x) i + (empty? xs) nil + :else (recur (inc i) xs)))) + +(defn dissoc-index [v index] + (vec (concat (subvec v 0 index) (subvec v (inc index))))) + #?(:clj (defn response [body] (if body diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index d29f20e..4525ec3 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -10,7 +10,7 @@ [mtg-pairings-server.components.autosuggest :refer [autosuggest]] [mtg-pairings-server.events :as events] [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.util :refer [indexed format-date]] + [mtg-pairings-server.util :refer [dissoc-index format-date index-where]] [mtg-pairings-server.util.material-ui :refer [get-theme text-field]])) (def basic? #{"Plains" "Island" "Swamp" "Mountain" "Forest" "Wastes" @@ -73,12 +73,21 @@ (update board conj {:name card, :quantity 1}) (update-in [:count board] inc)))) -(defn set-quantity [decklist board index quantity] - (let [orig-quantity (get-in decklist [board index :quantity])] +(defn set-quantity [decklist board name quantity] + (let [index (index-where #(= name (:name %)) (get decklist board)) + orig-quantity (get-in decklist [board index :quantity])] (-> decklist (assoc-in [board index :quantity] quantity) (update-in [:count board] + (- quantity orig-quantity))))) +(defn remove-card [decklist board name] + (let [cards (get decklist board) + index (index-where #(= name (:name %)) cards) + n (get-in decklist [board index :quantity])] + (-> decklist + (assoc board (dissoc-index cards index)) + (update-in [:count board] - n)))) + (defn error-icon [] (reagent/create-class {:context-types #js {:muiTheme prop-types/object.isRequired} @@ -87,10 +96,12 @@ [icons/alert-warning {:color (:accent3Color palette) :style {:vertical-align :top}}]))})) -(defn decklist-table-row [decklist board index card error?] +(defn decklist-table-row [decklist board card error?] (let [on-change (fn [_ _ quantity] - (swap! decklist set-quantity board index quantity))] - (fn decklist-table-row-render [_ _ _ {:keys [name quantity]} error?] + (swap! decklist set-quantity board (:name card) quantity)) + on-delete (fn [] + (swap! decklist remove-card board (:name card)))] + (fn decklist-table-row-render [_ _ {:keys [name quantity]} error?] [ui/table-row [ui/table-row-column {:class-name :quantity :style {:padding "0 12px"}} @@ -114,6 +125,11 @@ [ui/table-row-column {:class-name :card :style {:font-size "14px"}} name] + [ui/table-row-column {:class-name :actions + :style {:font-size "14px" + :padding 0}} + [ui/icon-button {:on-click on-delete} + [icons/content-remove-circle-outline]]] [ui/table-row-column {:class-name :error :style {:padding "12px"}} (when error? @@ -142,11 +158,12 @@ [ui/table-header-column {:class-name :card :style header-style} "Kortti"] + [ui/table-header-column {:class-name :actions}] [ui/table-header-column {:class-name :error}]]] [ui/table-body - (for [[index {:keys [name] :as card}] (indexed (get @decklist board))] + (for [{:keys [name] :as card} (get @decklist board)] ^{:key (str name "--" board "--tr")} - [decklist-table-row decklist board index card (contains? error-cards name)])]]])))) + [decklist-table-row decklist board card (contains? error-cards name)])]]])))) (defn player-info [decklist] (let [set-first-name #(swap! decklist assoc-in [:player :first-name] %) From 9c1544963dc8121962a85c1f462ca369a82e1a3e Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sun, 7 Apr 2019 01:48:05 +0300 Subject: [PATCH 14/44] Add some validation to decklist form --- .../mtg_pairings_server/styles/decklist.clj | 2 + .../src/cljc/mtg_pairings_server/util.cljc | 16 +- .../cljc/mtg_pairings_server/util/mtg.cljc | 4 + .../components/decklist/submit.cljs | 197 ++++++++++-------- 4 files changed, 136 insertions(+), 83 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index f57542f..11398ec 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -17,6 +17,8 @@ :display :inline-block :vertical-align :top}] [:#player-info + [:.full-width :.half-width + {:height (px 100)}] [:.full-width {:width "100%"}] [:.half-width diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 520e741..3b3e922 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -1,5 +1,7 @@ (ns mtg-pairings-server.util - (:require [#?(:clj clj-time.format + (:require #?(:clj [clojure.core.async :refer [> pairings (concat (map reverse-match pairings)) (remove #(= "***BYE***" (:team1_name %))))) + +(defn valid-dci? [dci-number] + (when dci-number + (re-matches #"[0-9]+" dci-number))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 4525ec3..2ca8f87 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -10,8 +10,10 @@ [mtg-pairings-server.components.autosuggest :refer [autosuggest]] [mtg-pairings-server.events :as events] [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.util :refer [dissoc-index format-date index-where]] - [mtg-pairings-server.util.material-ui :refer [get-theme text-field]])) + [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where]] + [mtg-pairings-server.util.mtg :refer [valid-dci?]] + [mtg-pairings-server.util.material-ui :refer [get-theme text-field]] + [clojure.string :as str])) (def basic? #{"Plains" "Island" "Swamp" "Mountain" "Forest" "Wastes" "Snow-Covered Plains" "Snow-Covered Island" "Snow-Covered Swamp" @@ -25,27 +27,34 @@ (defn input [on-change] (let [tournament (subscribe [::subs/decklist-tournament]) suggestions (atom []) - autosuggest-on-change (fn [value] - (on-change value)) - on-suggestions-fetch-requested (fn [prefix] - (go - (reset! suggestions ( (get-in decklist [:count :side]) 15) {:type :sideboard @@ -177,27 +186,41 @@ [text-field {:on-change set-deck-name :floating-label-text "Pakan nimi" :full-width true - :value (get-in @decklist [:player :deck-name])}]] + :value (get-in @decklist [:player :deck-name]) + :style {:vertical-align :top}}]] [:div.half-width.left - [text-field {:on-change set-first-name - :floating-label-text "Etunimi" - :full-width true - :value (get-in @decklist [:player :first-name])}]] + (let [value (get-in @decklist [:player :first-name])] + [text-field {:on-change set-first-name + :floating-label-text "Etunimi" + :full-width true + :value value + :error-text (when (str/blank? value) + "Etunimi on pakollinen") + :style {:vertical-align :top}}])] [:div.half-width.right - [text-field {:on-change set-last-name - :floating-label-text "Sukunimi" - :full-width true - :value (get-in @decklist [:player :last-name])}]] + (let [value (get-in @decklist [:player :last-name])] + [text-field {:on-change set-last-name + :floating-label-text "Sukunimi" + :full-width true + :value value + :error-text (when (str/blank? value) + "Etunimi on pakollinen") + :style {:vertical-align :top}}])] [:div.half-width.left - [text-field {:on-change set-dci - :floating-label-text "DCI-numero" - :full-width true - :value (get-in @decklist [:player :dci])}]] + (let [value (get-in @decklist [:player :dci])] + [text-field {:on-change set-dci + :floating-label-text "DCI-numero" + :full-width true + :value value + :error-text (when-not (valid-dci? value) + "Virheellinen DCI-numero") + :style {:vertical-align :top}}])] [:div.half-width.right [text-field {:on-change set-email :floating-label-text "Sähköposti" :full-width true - :value (get-in @decklist [:player :email])}]]]))) + :value (get-in @decklist [:player :email]) + :style {:vertical-align :top}}]]]))) (def empty-decklist {:main [] :side [] @@ -210,6 +233,14 @@ :deck-name "" :email ""}}) +(defn error-list [errors] + [ui/list + (for [error errors] + ^{:key (str "error--" (name (:type error)))} + [ui/list-item {:primary-text (:text error) + :left-icon (reagent/as-element [:div + [error-icon]])}])]) + (defn decklist-submit [] (let [saved-decklist (subscribe [::subs/decklist]) decklist (atom (or @saved-decklist empty-decklist)) @@ -227,58 +258,60 @@ page (subscribe [::subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] - [:div#decklist-submit - [:h2 "Lähetä pakkalista"] - [:p.intro - "Lähetä pakkalistasi " - [:span.tournament-date - (format-date (:date @tournament))] - " pelattavaan turnaukseen " - [:span.tournament-name - (:name @tournament)] - ", jonka formaatti on " - [:span.tournament-format - (case (:format @tournament) - :standard "Standard" - :modern "Modern" - :legacy "Legacy")] - "."] - [:h3 "Pakkalista"] - [input on-change] - [ui/raised-button - {:label "Main" - :on-click select-main - :primary (= :main (:board @decklist)) - :style button-style - :button-style {:border-radius "2px 0 0 2px"}}] - [ui/raised-button - {:label "Side" - :on-click select-side - :primary (= :side (:board @decklist)) - :style button-style - :button-style {:border-radius "0 2px 2px 0"}}] - [:div - [decklist-table decklist :main] - [decklist-table decklist :side]] - [:h3 "Pelaajan tiedot"] - [player-info decklist] - [ui/raised-button - {:label "Tallenna" - :on-click save-decklist - :primary true - :disabled @saving? - :style {:margin-top "24px"}}] - (when @saving? - [ui/circular-progress - {:size 36 - :style {:margin "24px 0 0 24px" - :vertical-align :top}}]) - (when @saved? - (let [url (str "https://pairings.fi/decklist/" (:id @page))] - [:div.success-notice - [:h4 "Tallennus onnistui!"] - [:p - "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " - [:a {:href url} - url] - ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))]))) + (let [errors (decklist-errors @decklist)] + [:div#decklist-submit + [:h2 "Lähetä pakkalista"] + [:p.intro + "Lähetä pakkalistasi " + [:span.tournament-date + (format-date (:date @tournament))] + " pelattavaan turnaukseen " + [:span.tournament-name + (:name @tournament)] + ", jonka formaatti on " + [:span.tournament-format + (case (:format @tournament) + :standard "Standard" + :modern "Modern" + :legacy "Legacy")] + "."] + [:h3 "Pakkalista"] + [input on-change] + [ui/raised-button + {:label "Main" + :on-click select-main + :primary (= :main (:board @decklist)) + :style button-style + :button-style {:border-radius "2px 0 0 2px"}}] + [ui/raised-button + {:label "Side" + :on-click select-side + :primary (= :side (:board @decklist)) + :style button-style + :button-style {:border-radius "0 2px 2px 0"}}] + [:div + [decklist-table decklist :main] + [decklist-table decklist :side]] + [:h3 "Pelaajan tiedot"] + [player-info decklist] + [ui/raised-button + {:label "Tallenna" + :on-click save-decklist + :primary true + :disabled (boolean (or @saving? (seq errors))) + :style {:margin-top "24px"}}] + (when @saving? + [ui/circular-progress + {:size 36 + :style {:margin "24px 0 0 24px" + :vertical-align :top}}]) + [error-list errors] + (when @saved? + (let [url (str "https://pairings.fi/decklist/" (:id @page))] + [:div.success-notice + [:h4 "Tallennus onnistui!"] + [:p + "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " + [:a {:href url} + url] + ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))])))) From 2c9fdc2c42c8047321f46cd83cbd4e2a410a5c43 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Tue, 9 Apr 2019 17:48:50 +0300 Subject: [PATCH 15/44] Allow creating new tournament --- .../mtg_pairings_server/styles/decklist.clj | 4 +++ .../components/decklist/organizer.cljs | 31 ++++++++++++++----- .../src/cljs/mtg_pairings_server/events.cljs | 11 +++++-- .../src/cljs/mtg_pairings_server/routes.cljs | 1 + 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 11398ec..c73b41f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -281,6 +281,10 @@ table-row-link]]] [:#decklist-organizer-tournament {:margin "12px 24px"} + [:.field + {:display :inline-block + :vertical-align :top + :margin-right "24px"}] [:.decklists [:th.dci :td.dci {:width (px 150)}] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index 894970e..99944fb 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -3,6 +3,7 @@ [re-frame.core :refer [subscribe dispatch]] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] + [cljs-react-material-ui.icons :as icons] [cljs-time.coerce :as coerce] [oops.core :refer [oget]] [mtg-pairings-server.events :as events] @@ -16,10 +17,15 @@ :font-size "16px" :height "36px"}) +(defn list-submit-link [tournament-id] + (let [submit-url (routes/new-decklist-submit-path {:id tournament-id})] + [:a {:href submit-url + :target :_blank} + (str "https://pairings.fi" submit-url)])) + (defn tournament-row [tournament] (let [column-style {:font-size "14px"} edit-url (routes/decklist-organizer-tournament-path {:id (:id tournament)}) - submit-url (routes/new-decklist-submit-path {:id (:id tournament)}) link-props {:href edit-url :on-click #(dispatch [::events/load-decklist-organizer-tournament (:id tournament)])}] [ui/table-row @@ -41,13 +47,19 @@ (:decklist tournament)]] [ui/table-row-column {:class-name :submit-page :style column-style} - [:a {:href submit-url} - (str "https://pairings.fi" submit-url)]]])) + [list-submit-link (:id tournament)]]])) (defn all-tournaments [] (let [tournaments (subscribe [::subs/decklist-organizer-tournaments])] (fn all-tournaments-render [] [:div#decklist-organizer-tournaments + [ui/raised-button {:href (routes/decklist-organizer-new-tournament-path) + :label "Uusi turnaus" + :icon (reagent/as-element [icons/content-add + {:style {:height "36px" + :width "30px" + :padding "6px 0 6px 6px" + :vertical-align :top}}])}] [ui/table {:selectable false :class-name :tournaments} [ui/table-header {:display-select-all false @@ -137,11 +149,11 @@ (reset! tournament @saved-tournament)) [:div#decklist-organizer-tournament [:div.tournament-info - [:div + [:div.field [text-field {:on-change set-name :floating-label-text "Turnauksen nimi" :value (:name @tournament "")}]] - [:div + [:div.field [ui/select-field {:on-change set-format :value (:format @tournament) :floating-label-text "Formaatti"} @@ -151,7 +163,7 @@ :primary-text "Modern"}] [ui/menu-item {:value :legacy :primary-text "Legacy"}]]] - [:div + [:div.field [ui/date-picker {:value (some-> (:date @tournament) (coerce/to-long) (js/Date.)) @@ -162,7 +174,12 @@ :locale "fi-FI" :auto-ok true :DateTimeFormat (oget js/Intl "DateTimeFormat")}]] - [:div + [:div.link + (when id + [:p + "Listojen lähetyssivu: " + [list-submit-link id]])] + [:div.buttons [ui/raised-button {:label "Tallenna" :on-click save-tournament :primary true diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 12450ae..6a15485 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -5,7 +5,7 @@ [oops.core :refer [oget]] [accountant.core :as accountant] [mtg-pairings-server.transit :as transit] - [mtg-pairings-server.util :refer [map-by format-time assoc-in-many deep-merge round-up]] + [mtg-pairings-server.util :refer [map-by format-time assoc-in-many deep-merge round-up dissoc-in]] [mtg-pairings-server.util.local-storage :as local-storage] [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) @@ -382,7 +382,8 @@ (reg-event-fx ::save-decklist-organizer-tournament (fn [{:keys [db]} [_ tournament]] - {:db (assoc-in db [:decklist-editor :saving] true) + {:db (update db :decklist-editor merge {:saving true + :organizer-tournament tournament}) :ws-send [:client/save-decklist-organizer-tournament tournament]})) (reg-event-fx :server/organizer-tournament-saved @@ -412,3 +413,9 @@ (reg-event-db :server/organizer-login (fn [db [_ username]] (assoc-in db [:decklist-editor :user] username))) + +(reg-event-db ::clear-organizer-decklist-tournament + (fn [db _] + (update db :decklist-editor merge {:organizer-tournament nil + :saving false + :saved false}))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs index 305c473..d3db717 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs @@ -77,6 +77,7 @@ (dispatch-page :decklist-organizer-view id)) (secretary/defroute decklist-organizer-new-tournament-path "/decklist/organizer/new" [] + (dispatch [::events/clear-organizer-decklist-tournament]) (dispatch-page :decklist-organizer-tournament)) (secretary/defroute decklist-organizer-tournament-path "/decklist/organizer/:id" [id] From 4f6b66d90cbbf5260a889116f311cf5a42d459f0 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Tue, 9 Apr 2019 19:39:54 +0300 Subject: [PATCH 16/44] Load earlier decklist --- .../src/clj/mtg_pairings_server/handler.clj | 6 + .../mtg_pairings_server/styles/decklist.clj | 12 +- .../components/decklist/submit.cljs | 139 +++++++++++------- .../src/cljs/mtg_pairings_server/events.cljs | 66 ++++++++- 4 files changed, 161 insertions(+), 62 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 087b37f..730e7cd 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -264,3 +264,9 @@ (sort-by (fn [d] [(get-in d [:player :last-name]) (get-in d [:player :first-name])])))])) + +(defmethod ws/event-handler :client/load-decklist-with-id + [{uid :uid, id :?data}] + (if-let [decklist (decklist/get-decklist id)] + (ws/send! uid [:server/organizer-tournament-decklist (dissoc decklist :id :player)]) + (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index c73b41f..dab77b1 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -27,7 +27,11 @@ [:&.left {:margin-right (px 24)}] [:&.right - {:margin-left (px 24)}]]]) + {:margin-left (px 24)}]]] + [:.decklist-import + [:.info :.form + {:display :inline-block + :width (percent 50)}]]) (when-mobile [:#player-info [:.full-width :.half-width @@ -45,7 +49,11 @@ [:th.actions :td.actions {:width (px 48)}] [:th.error :td.error - {:width (px 48)}]]]]) + {:width (px 48)}]]] + [:.decklist-import + {:margin "12px 0"} + [:.info :.form + {:padding (px 12)}]]]) (def grey-border {:style :solid :width (px 1) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 2ca8f87..12a1f25 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -7,6 +7,7 @@ [prop-types] [cljs.core.async :refer [ decklist - (update board conj {:name card, :quantity 1}) - (update-in [:count board] inc)))) - -(defn set-quantity [decklist board name quantity] - (let [index (index-where #(= name (:name %)) (get decklist board)) - orig-quantity (get-in decklist [board index :quantity])] - (-> decklist - (assoc-in [board index :quantity] quantity) - (update-in [:count board] + (- quantity orig-quantity))))) - -(defn remove-card [decklist board name] - (let [cards (get decklist board) - index (index-where #(= name (:name %)) cards) - n (get-in decklist [board index :quantity])] - (-> decklist - (assoc board (dissoc-index cards index)) - (update-in [:count board] - n)))) - (defn error-icon [] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn error-icon-render [] - (let [palette (:palette (get-theme (reagent/current-component)))] - [icons/alert-warning {:color (:accent3Color palette) - :style {:vertical-align :top}}]))})) + [icons/alert-warning {:style {:color "#f44336" + :vertical-align :top}}]) -(defn decklist-table-row [decklist board card error?] +(defn decklist-table-row [board card error?] (let [on-change (fn [_ _ quantity] - (swap! decklist set-quantity board (:name card) quantity)) - on-delete (fn [] - (swap! decklist remove-card board (:name card)))] - (fn decklist-table-row-render [_ _ {:keys [name quantity]} error?] + (dispatch [::events/decklist-set-quantity board (:name card) quantity])) + on-delete #(dispatch [::events/decklist-remove-card board (:name card)])] + (fn decklist-table-row-render [_ {:keys [name quantity]} error?] [ui/table-row [ui/table-row-column {:class-name :quantity :style {:padding "0 12px"}} @@ -172,14 +147,14 @@ [ui/table-body (for [{:keys [name] :as card} (get @decklist board)] ^{:key (str name "--" board "--tr")} - [decklist-table-row decklist board card (contains? error-cards name)])]]])))) + [decklist-table-row board card (contains? error-cards name)])]]])))) (defn player-info [decklist] - (let [set-first-name #(swap! decklist assoc-in [:player :first-name] %) - set-last-name #(swap! decklist assoc-in [:player :last-name] %) - set-deck-name #(swap! decklist assoc-in [:player :deck-name] %) - set-email #(swap! decklist assoc-in [:player :email] %) - set-dci #(swap! decklist assoc-in [:player :dci] %)] + (let [set-first-name #(dispatch [::events/decklist-update-player-info :first-name %]) + set-last-name #(dispatch [::events/decklist-update-player-info :last-name %]) + set-deck-name #(dispatch [::events/decklist-update-player-info :deck-name %]) + set-email #(dispatch [::events/decklist-update-player-info :email %]) + set-dci #(dispatch [::events/decklist-update-player-info :dci %])] (fn player-info-render [_] [:div#player-info [:div.full-width @@ -222,16 +197,68 @@ :value (get-in @decklist [:player :email]) :style {:vertical-align :top}}]]]))) -(def empty-decklist {:main [] - :side [] - :count {:main 0 - :side 0} - :board :main - :player {:dci "" - :first-name "" - :last-name "" - :deck-name "" - :email ""}}) +(defn decklist-import [] + (let [mobile? (subscribe [::subs/mobile?]) + selected (atom nil) + on-active (fn [tab] + (let [value (oget tab "props" "value")] + (reset! selected (if (= value @selected) + nil + value)))) + address (atom "") + address-on-change #(reset! address %) + import-from-address #(dispatch [::events/load-decklist-from-address @address]) + decklist (atom "") + decklist-on-change #(reset! decklist %) + import-decklist (fn [] + (println "Import decklist\n" @decklist))] + + (fn decklist-import-render [] + [:div.decklist-import + [ui/tabs {:value @selected} + [ui/tab {:label "Lataa aiempi lista" + :value "load-previous" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa aiempi lista"] + [:p + "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " + [:span.address "https://pairings.fi/decklist/abcd..."] + ")."]] + [:div.form + [text-field {:on-change address-on-change + :floating-label-text "Osoite" + :full-width (not @mobile?)}] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (str/blank? @address) + :on-click import-from-address}]]] + [ui/tab {:label "Lataa tekstimuotoinen lista" + :value "load-text" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa tekstimuotoinen lista"] + [:p + "Kopioi tekstikenttään tekstimuotoinen lista." + "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] + [:pre + "4 Lightning Bolt\n4 Chain Lightning\n..."] + [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] + [:div.form + [text-field {:on-change decklist-on-change + :multi-line true + :rows 7 + :rows-max 7 + :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} + :full-width (not @mobile?)}] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (str/blank? @decklist) + :on-click import-decklist}]]]]]))) (defn error-list [errors] [ui/list @@ -242,12 +269,9 @@ [error-icon]])}])]) (defn decklist-submit [] - (let [saved-decklist (subscribe [::subs/decklist]) - decklist (atom (or @saved-decklist empty-decklist)) - on-change (fn [card] - (swap! decklist add-card card)) - select-main #(swap! decklist assoc :board :main) - select-side #(swap! decklist assoc :board :side) + (let [decklist (subscribe [::subs/decklist]) + select-main #(dispatch [::events/decklist-select-board :main]) + select-side #(dispatch [::events/decklist-select-board :side]) button-style {:position :relative :top "-5px" :width "70px" @@ -276,7 +300,7 @@ :legacy "Legacy")] "."] [:h3 "Pakkalista"] - [input on-change] + [input] [ui/raised-button {:label "Main" :on-click select-main @@ -292,6 +316,7 @@ [:div [decklist-table decklist :main] [decklist-table decklist :side]] + [decklist-import] [:h3 "Pelaajan tiedot"] [player-info decklist] [ui/raised-button diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 6a15485..98304fb 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -5,7 +5,8 @@ [oops.core :refer [oget]] [accountant.core :as accountant] [mtg-pairings-server.transit :as transit] - [mtg-pairings-server.util :refer [map-by format-time assoc-in-many deep-merge round-up dissoc-in]] + [mtg-pairings-server.util :refer [map-by format-time assoc-in-many deep-merge round-up dissoc-in + index-where dissoc-index]] [mtg-pairings-server.util.local-storage :as local-storage] [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) @@ -35,6 +36,17 @@ (fn [path] (accountant/navigate! path))) +(def empty-decklist {:main [] + :side [] + :count {:main 0 + :side 0} + :board :main + :player {:dci "" + :first-name "" + :last-name "" + :deck-name "" + :email ""}}) + (defn initial-db [] (deep-merge {:tournaments {} :tournament-count 0 @@ -56,7 +68,8 @@ :logged-in-user (local-storage/fetch :user) :notification nil :mobile? (mobile?) - :decklist-editor {:organizer-tournaments []}} + :decklist-editor {:organizer-tournaments [] + :decklist empty-decklist}} (transit/read (oget js/window "initial_db")))) (defn update-filters-active [db] @@ -404,7 +417,7 @@ (reg-event-db :server/organizer-tournament-decklist (fn [db [_ decklist]] - (assoc-in db [:decklist-editor :decklist] decklist))) + (assoc-in db [:decklist-editor :decklist] (merge empty-decklist decklist)))) (reg-event-db :server/organizer-tournament-decklists (fn [db [_ decklists]] @@ -419,3 +432,50 @@ (update db :decklist-editor merge {:organizer-tournament nil :saving false :saved false}))) + +(reg-event-fx ::load-decklist-from-address + (fn [_ [_ address]] + (when-let [[_ code] (re-find #"/decklist/([A-z0-9_-]{22})$" address)] + {:ws-send [:client/load-decklist-with-id code]}))) + +(defn ^:private add-card [{:keys [board] :as decklist} name] + (if (some #(= name (:name %)) (get decklist board)) + decklist + (-> decklist + (update board conj {:name name, :quantity 1}) + (update-in [:count board] inc)))) + +(reg-event-db ::decklist-add-card + (fn [db [_ name]] + (update-in db [:decklist-editor :decklist] add-card name))) + +(defn ^:private set-quantity [decklist board name quantity] + (let [index (index-where #(= name (:name %)) (get decklist board)) + orig-quantity (get-in decklist [board index :quantity])] + (-> decklist + (assoc-in [board index :quantity] quantity) + (update-in [:count board] + (- quantity orig-quantity))))) + +(reg-event-db ::decklist-set-quantity + (fn [db [_ board name quantity]] + (update-in db [:decklist-editor :decklist] set-quantity board name quantity))) + +(defn ^:private remove-card [decklist board name] + (let [cards (get decklist board) + index (index-where #(= name (:name %)) cards) + n (get-in decklist [board index :quantity])] + (-> decklist + (assoc board (dissoc-index cards index)) + (update-in [:count board] - n)))) + +(reg-event-db ::decklist-remove-card + (fn [db [_ board name]] + (update-in db [:decklist-editor :decklist] remove-card board name))) + +(reg-event-db ::decklist-select-board + (fn [db [_ board]] + (assoc-in db [:decklist-editor :decklist :board] board))) + +(reg-event-db ::decklist-update-player-info + (fn [db [_ key value]] + (assoc-in db [:decklist-editor :decklist :player key] value))) From 68fb3c106001744bfec3f941546fdaf3e33264b5 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Tue, 9 Apr 2019 19:58:08 +0300 Subject: [PATCH 17/44] Use websocket to fetch card suggestions --- mtg-pairings-server/project.clj | 1 - .../src/clj/mtg_pairings_server/api/card.clj | 14 ----------- .../src/clj/mtg_pairings_server/api/http.clj | 2 -- .../src/clj/mtg_pairings_server/handler.clj | 4 ++++ .../mtg_pairings_server/middleware/log.clj | 3 +-- .../cljc/mtg_pairings_server/websocket.cljc | 7 ++++-- .../components/decklist/submit.cljs | 14 ++++------- .../src/cljs/mtg_pairings_server/events.cljs | 24 ++++++++++++------- 8 files changed, 30 insertions(+), 39 deletions(-) delete mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index 478f33b..db7e588 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -89,7 +89,6 @@ :prod {:source-paths ["env/prod/cljs"]} :provided {:dependencies [[org.clojure/clojurescript "1.10.520"] [reagent "0.8.1"] - [cljs-http "0.1.46"] [com.google.errorprone/error_prone_annotations "2.3.3"] [com.google.code.findbugs/jsr305 "3.0.2"] [com.bhauman/figwheel-main "0.2.0" :exclusions [org.clojure/clojurescript]] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj deleted file mode 100644 index e68b223..0000000 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/api/card.clj +++ /dev/null @@ -1,14 +0,0 @@ -(ns mtg-pairings-server.api.card - (:require [compojure.api.sweet :refer :all] - [schema.core :as s] - [mtg-pairings-server.service.decklist :refer [search-cards]] - [mtg-pairings-server.util.schema :refer :all] - [mtg-pairings-server.util :refer [response]])) - -(defroutes card-routes - (GET "/search" [] - :query-params [prefix :- s/Str - format :- (s/enum :standard :modern :legacy)] - :return [s/Str] - :summary "Hae kortteja nimen alkuosalla" - (response (search-cards prefix format)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj index 4243c7e..b0399a5 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/api/http.clj @@ -2,7 +2,6 @@ (:require [compojure.api.sweet :refer :all] [compojure.api.exception :as ex] [config.core :refer [env]] - [mtg-pairings-server.api.card :refer [card-routes]] [mtg-pairings-server.api.player :refer [player-routes]] [mtg-pairings-server.api.tournament :refer [tournament-routes]] [mtg-pairings-server.middleware.error :refer [request-validation-error-handler sql-error-handler]] @@ -15,7 +14,6 @@ {:ui "/api-docs" :spec "/swagger.json" :data {:info {:title "WER pairings backend API"}}}) - (context "/card" [] card-routes) (context "/tournament" [] tournament-routes) (context "/player" [] player-routes) (GET "/client-version" [] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 730e7cd..d0af98f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -270,3 +270,7 @@ (if-let [decklist (decklist/get-decklist id)] (ws/send! uid [:server/organizer-tournament-decklist (dissoc decklist :id :player)]) (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) + +(defmethod ws/event-handler :client/decklist-card-suggestions + [{[prefix format] :?data, reply-fn :?reply-fn}] + (reply-fn (decklist/search-cards prefix format))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj index ebf073a..fd4c5be 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj @@ -4,8 +4,7 @@ (def ^:private log-blacklist [#"^.*\.(ico|png|jpg|js|css|woff2|txt|map)$" - #"^/chsk$" - #"^/api/card/search"]) + #"^/chsk$"]) (defn ^:private logged? [uri] (not-any? #(re-matches % uri) log-blacklist)) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc index 8efb610..7fdc9fe 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc @@ -72,8 +72,11 @@ (defn send! [uid event] ((:send! @router) uid event)) :cljs - (defn send! [event] - ((:send! @router) event))) + (defn send! + ([event] + (send! event nil nil)) + ([event timeout callback] + ((:send! @router) event timeout callback)))) #?(:clj (defroutes routes (GET path request ((:ajax-get-or-ws-handshake-fn @router) request)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 12a1f25..8111535 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -5,8 +5,6 @@ [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] [prop-types] - [cljs.core.async :refer [ Date: Wed, 10 Apr 2019 20:01:22 +0300 Subject: [PATCH 18/44] Load decklist from text --- .../src/clj/mtg_pairings_server/handler.clj | 4 + .../mtg_pairings_server/service/decklist.clj | 28 +++++ .../clj/mtg_pairings_server/styles/common.clj | 15 +-- .../clj/mtg_pairings_server/styles/main.clj | 2 + .../mtg_pairings_server/styles/tooltip.clj | 24 ++++ .../components/decklist/submit.cljs | 114 ++++++++++-------- .../components/tooltip.cljs | 9 ++ .../src/cljs/mtg_pairings_server/events.cljs | 4 + 8 files changed, 144 insertions(+), 56 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/styles/tooltip.clj create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/components/tooltip.cljs diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index d0af98f..2ae5199 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -274,3 +274,7 @@ (defmethod ws/event-handler :client/decklist-card-suggestions [{[prefix format] :?data, reply-fn :?reply-fn}] (reply-fn (decklist/search-cards prefix format))) + +(defmethod ws/event-handler :client/load-text-decklist + [{uid :uid, [text-decklist format] :?data}] + (ws/send! uid [:server/organizer-tournament-decklist (decklist/load-text-decklist text-decklist format)])) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index f691fec..2a3ad85 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -141,3 +141,31 @@ (sql/set-fields (format-saved-tournament tournament)) (sql/where {:id old-id})) old-id)))) + +(defn parse-decklist-row [row format] + (when-not (str/blank? row) + (if-let [[_ quantity name] (re-matches #"(\d+)\s+(.+)" (str/trim row))] + (if-let [{:keys [name legal]} (sql-util/select-unique-or-nil db/card + (sql/fields :name [format :legal]) + (sql/where {:lowername (str/lower-case name)}))] + (if legal + {:name name + :quantity (Long/parseLong quantity)} + {:name name + :error "Ei sallittu tässä formaatissa"}) + {:name name + :error "Korttia ei löydy"}) + {:name row + :error "Virheellinen rivi"}))) + +(defn load-text-decklist [text-decklist format] + {:pre [(contains? #{:standard :modern :legacy} format)]} + (let [[maindeck sideboard] (str/split text-decklist #"[Ss]ideboard\s*") + maindeck-cards (keep #(parse-decklist-row % format) (str/split-lines maindeck)) + sideboard-cards (when sideboard + (keep #(parse-decklist-row % format) (str/split-lines sideboard)))] + {:main (vec maindeck-cards) + :side (vec (or sideboard-cards [])) + :count {:main (transduce (keep :quantity) + 0 maindeck-cards) + :side (transduce (keep :quantity) + 0 sideboard-cards)} + :board :main})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj index 3682373..f4f54e2 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj @@ -1,14 +1,15 @@ (ns mtg-pairings-server.styles.common - (:require [garden.color :refer [hex->rgb]])) + (:require [garden.color :refer [hex->rgb rgba]])) (def ellipsis-overflow {:white-space :nowrap :text-overflow :ellipsis :overflow :hidden :vertical-align :bottom}) -(def color {:turquoise (hex->rgb "337ab7") - :light-blue (hex->rgb "d0e9fc") - :light-grey (hex->rgb "e6e6e6") - :grey (hex->rgb "999999") - :dark-grey (hex->rgb "333333") - :light-green (hex->rgb "ccffcc")}) +(def color {:turquoise (hex->rgb "337ab7") + :light-blue (hex->rgb "d0e9fc") + :light-grey (hex->rgb "e6e6e6") + :grey (hex->rgb "999999") + :dark-grey (hex->rgb "333333") + :light-green (hex->rgb "ccffcc") + :transparent-grey (rgba 0 0 0 0.7)}) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj index a68968c..7cc80bd 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj @@ -8,6 +8,7 @@ [mtg-pairings-server.styles.decklist :as decklist] [mtg-pairings-server.styles.organizer :as organizer] [mtg-pairings-server.styles.table :as table] + [mtg-pairings-server.styles.tooltip :as tooltip] [mtg-pairings-server.styles.tournament :as tournament] [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) @@ -58,6 +59,7 @@ bracket/styles decklist/styles organizer/styles + tooltip/styles tournament/styles mobile) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tooltip.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tooltip.clj new file mode 100644 index 0000000..e4fae7d --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tooltip.clj @@ -0,0 +1,24 @@ +(ns mtg-pairings-server.styles.tooltip + (:require [garden.def :refer [defstyles]] + [garden.selectors :refer [& hover]] + [garden.units :refer [px percent]] + [mtg-pairings-server.styles.common :refer [color]])) + +(defstyles styles + [:.tooltip-container + {:position :relative + :display :inline-block} + [:.tooltip + {:visibility :hidden + :background-color (color :transparent-grey) + :color :white + :padding (px 6) + :border-radius (px 6) + :position :absolute + :top (percent 100) + :left (percent 50) + :transform "translate(-50%, 6px)" + :z-index 2}] + [(& hover) + [:.tooltip + {:visibility :visible}]]]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 8111535..934a8ae 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -7,6 +7,7 @@ [prop-types] [oops.core :refer [oget]] [mtg-pairings-server.components.autosuggest :refer [autosuggest]] + [mtg-pairings-server.components.tooltip :refer [tooltip]] [mtg-pairings-server.events :as events] [mtg-pairings-server.subscriptions :as subs] [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where]] @@ -47,59 +48,70 @@ (merge-with + acc {name quantity})) {} (concat (:main decklist) (:side decklist))) - errors (list* (when-not (valid-player-data? (:player decklist)) {:type :player-data - :id :missing-player-data - :text "Osa pelaajan tiedoista puuttuu"}) - (when (< (get-in decklist [:count :main]) 60) {:type :maindeck - :id :deck-error-maindeck - :text "Maindeckissä on alle 60 korttia"}) - (when (> (get-in decklist [:count :side]) 15) {:type :sideboard - :id :deck-error-sideboard - :text "Sideboardilla on yli 15 korttia"}) - (for [[card quantity] cards - :when (not (basic? card)) - :when (> quantity 4)] - {:type :card-over-4 - :id (str "deck-error-card--" card) - :card card - :text (str "Korttia " card " on yli 4 kappaletta")}))] + errors (concat [(when-not (valid-player-data? (:player decklist)) {:type :player-data + :id :missing-player-data + :text "Osa pelaajan tiedoista puuttuu"}) + (when (< (get-in decklist [:count :main]) 60) {:type :maindeck + :id :deck-error-maindeck + :text "Maindeckissä on alle 60 korttia"}) + (when (> (get-in decklist [:count :side]) 15) {:type :sideboard + :id :deck-error-sideboard + :text "Sideboardilla on yli 15 korttia"})] + (for [[card quantity] cards + :when (not (basic? card)) + :when (> quantity 4)] + {:type :card-over-4 + :id (str "deck-error-card--" card) + :card card + :text (str "Korttia " card " on yli 4 kappaletta")}) + (for [{:keys [error name]} (concat (:main decklist) (:side decklist)) + :when error] + {:type :other + :id (str "other-error-card--" name) + :text (str error ": " name)}))] (filter some? errors))) (defn cards-with-error [decklist] - (into #{} + (into {} (comp (filter #(= :card-over-4 (:type %))) - (map :card)) + (map (juxt :card :text))) (decklist-errors decklist))) -(defn error-icon [] - [icons/alert-warning {:style {:color "#f44336" - :vertical-align :top}}]) +(defn error-icon [error] + (let [icon [icons/alert-warning {:title error + :style {:color "#f44336" + :vertical-align :top}}]] + (if error + [tooltip {:label error} + icon] + icon))) -(defn decklist-table-row [board card error?] +(defn decklist-table-row [board card error] (let [on-change (fn [_ _ quantity] (dispatch [::events/decklist-set-quantity board (:name card) quantity])) on-delete #(dispatch [::events/decklist-remove-card board (:name card)])] - (fn decklist-table-row-render [_ {:keys [name quantity]} error?] + (fn decklist-table-row-render [_ {:keys [name quantity]} error] [ui/table-row [ui/table-row-column {:class-name :quantity :style {:padding "0 12px"}} - [ui/select-field {:value quantity - :on-change on-change - :style {:width "48px" - :vertical-align :top} - :menu-style {:width "48px"} - :icon-style {:padding-left 0 - :padding-right 0 - :width "24px" - :fill "rgba(0, 0, 0, 0.54)"} - :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} - (for [i (range 1 (if (basic? name) - 31 - 5))] - ^{:key (str name "--quantity--" i)} - [ui/menu-item {:value i - :primary-text i - :inner-div-style {:padding "0 6px"}}])]] + (when quantity + [ui/select-field {:value quantity + :on-change on-change + :style {:width "48px" + :vertical-align :top} + :menu-style {:width "48px"} + :icon-style {:padding-left 0 + :padding-right 0 + :width "24px" + :fill "rgba(0, 0, 0, 0.54)"} + :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} + (for [i (range 1 (if (basic? name) + 31 + 5))] + ^{:key (str name "--quantity--" i)} + [ui/menu-item {:value i + :primary-text i + :inner-div-style {:padding "0 6px"}}])])] [ui/table-row-column {:class-name :card :style {:font-size "14px"}} name] @@ -109,9 +121,10 @@ [ui/icon-button {:on-click on-delete} [icons/content-remove-circle-outline]]] [ui/table-row-column {:class-name :error - :style {:padding "12px"}} - (when error? - [error-icon])]]))) + :style {:padding "12px" + :overflow :visible}} + (when error + [error-icon error])]]))) (defn decklist-table [decklist board] (let [header-style {:color :black @@ -124,8 +137,10 @@ [:h3 (if (= :main board) (str "Main deck (" (get-in @decklist [:count :main]) ")") (str "Sideboard (" (get-in @decklist [:count :side]) ")"))] - [ui/table {:selectable false - :class-name :deck-table} + [ui/table {:selectable false + :class-name :deck-table + :wrapper-style {:overflow :visible} + :body-style {:overflow :visible}} [ui/table-header {:display-select-all false :adjust-for-checkbox false} [ui/table-row {:style {:height "24px"}} @@ -139,9 +154,9 @@ [ui/table-header-column {:class-name :actions}] [ui/table-header-column {:class-name :error}]]] [ui/table-body - (for [{:keys [name] :as card} (get @decklist board)] + (for [{:keys [name error] :as card} (get @decklist board)] ^{:key (str name "--" board "--tr")} - [decklist-table-row board card (contains? error-cards name)])]]])))) + [decklist-table-row board card (or error (get error-cards name))])]]])))) (defn player-info [decklist] (let [set-first-name #(dispatch [::events/decklist-update-player-info :first-name %]) @@ -205,7 +220,8 @@ decklist (atom "") decklist-on-change #(reset! decklist %) import-decklist (fn [] - (println "Import decklist\n" @decklist))] + (dispatch [::events/load-text-decklist @decklist]) + (reset! selected nil))] (fn decklist-import-render [] [:div.decklist-import @@ -260,7 +276,7 @@ ^{:key (str "error--" (name (:type error)))} [ui/list-item {:primary-text (:text error) :left-icon (reagent/as-element [:div - [error-icon]])}])]) + [error-icon nil]])}])]) (defn decklist-submit [] (let [decklist (subscribe [::subs/decklist]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tooltip.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tooltip.cljs new file mode 100644 index 0000000..3dc8f17 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tooltip.cljs @@ -0,0 +1,9 @@ +(ns mtg-pairings-server.components.tooltip + (:require [reagent.core :as reagent])) + +(defn tooltip [props & children] + (into [:div.tooltip-container + (dissoc props :label) + [:div.tooltip + (:label props)]] + children)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs index 6552e75..3336a06 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs @@ -487,3 +487,7 @@ (reg-event-fx ::decklist-card-suggestions (fn [_ [_ prefix format callback]] {:ws-send [[:client/decklist-card-suggestions [prefix format]] 1000 callback]})) + +(reg-event-fx ::load-text-decklist + (fn [{:keys [db]} [_ text-decklist]] + {:ws-send [:client/load-text-decklist [text-decklist (get-in db [:decklist-editor :tournament :format])]]})) From ed6e92e8011d9a0a1b5c0de380279004f7155753 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 05:11:55 +0300 Subject: [PATCH 19/44] Separate routes, events and subs for pairings and decklist --- .../src/clj/mtg_pairings_server/handler.clj | 18 +- .../components/decklist/organizer.cljs | 24 +-- .../components/decklist/submit.cljs | 63 +++---- .../components/filter.cljs | 7 +- .../mtg_pairings_server/components/main.cljs | 6 +- .../components/organizer.cljs | 4 +- .../components/tournament.cljs | 6 +- .../src/cljs/mtg_pairings_server/core.cljs | 18 +- .../mtg_pairings_server/events/common.cljs | 43 +++++ .../mtg_pairings_server/events/decklist.cljs | 172 ++++++++++++++++++ .../{events.cljs => events/pairings.cljs} | 160 +--------------- .../mtg_pairings_server/pages/decklist.cljs | 4 +- .../cljs/mtg_pairings_server/pages/main.cljs | 2 +- .../mtg_pairings_server/pages/organizer.cljs | 2 +- .../mtg_pairings_server/pages/tournament.cljs | 4 +- .../mtg_pairings_server/routes/common.cljs | 13 ++ .../mtg_pairings_server/routes/decklist.cljs | 27 +++ .../{routes.cljs => routes/pairings.cljs} | 36 +--- .../subscriptions/common.cljs | 10 + .../subscriptions/decklist.cljs | 34 ++++ .../pairings.cljs} | 42 +---- .../util/event_listener.cljs | 2 +- 22 files changed, 389 insertions(+), 308 deletions(-) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs rename mtg-pairings-server/src/cljs/mtg_pairings_server/{events.cljs => events/pairings.cljs} (69%) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/routes/common.cljs create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs rename mtg-pairings-server/src/cljs/mtg_pairings_server/{routes.cljs => routes/pairings.cljs} (62%) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/common.cljs create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs rename mtg-pairings-server/src/cljs/mtg_pairings_server/{subscriptions.cljs => subscriptions/pairings.cljs} (80%) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 2ae5199..70952dc 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -254,21 +254,21 @@ (when id (ws/send! uid [:server/organizer-tournament-saved id])))) -(defmethod ws/event-handler :client/load-organizer-tournament-decklist +(defmethod ws/event-handler :client/load-decklist [{uid :uid, id :?data}] - (ws/send! uid [:server/organizer-tournament-decklist (decklist/get-decklist id)])) + (ws/send! uid [:server/decklist (decklist/get-decklist id)])) -(defmethod ws/event-handler :client/load-organizer-tournament-decklists +(defmethod ws/event-handler :client/load-decklists [{uid :uid, ids :?data}] - (ws/send! uid [:server/organizer-tournament-decklists (->> (map decklist/get-decklist ids) - (sort-by (fn [d] - [(get-in d [:player :last-name]) - (get-in d [:player :first-name])])))])) + (ws/send! uid [:server/decklists (->> (map decklist/get-decklist ids) + (sort-by (fn [d] + [(get-in d [:player :last-name]) + (get-in d [:player :first-name])])))])) (defmethod ws/event-handler :client/load-decklist-with-id [{uid :uid, id :?data}] (if-let [decklist (decklist/get-decklist id)] - (ws/send! uid [:server/organizer-tournament-decklist (dissoc decklist :id :player)]) + (ws/send! uid [:server/decklist (dissoc decklist :id :player)]) (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) (defmethod ws/event-handler :client/decklist-card-suggestions @@ -277,4 +277,4 @@ (defmethod ws/event-handler :client/load-text-decklist [{uid :uid, [text-decklist format] :?data}] - (ws/send! uid [:server/organizer-tournament-decklist (decklist/load-text-decklist text-decklist format)])) + (ws/send! uid [:server/decklist (decklist/load-text-decklist text-decklist format)])) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index 99944fb..dcf7cf2 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -6,9 +6,9 @@ [cljs-react-material-ui.icons :as icons] [cljs-time.coerce :as coerce] [oops.core :refer [oget]] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.routes :as routes] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.events.decklist :as events] + [mtg-pairings-server.routes.decklist :as routes] + [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.util :refer [format-date format-date-time to-local-date indexed]] [mtg-pairings-server.util.material-ui :refer [text-field]])) @@ -27,7 +27,7 @@ (let [column-style {:font-size "14px"} edit-url (routes/decklist-organizer-tournament-path {:id (:id tournament)}) link-props {:href edit-url - :on-click #(dispatch [::events/load-decklist-organizer-tournament (:id tournament)])}] + :on-click #(dispatch [::events/load-organizer-tournament (:id tournament)])}] [ui/table-row [ui/table-row-column {:class-name :date :style column-style} @@ -50,7 +50,7 @@ [list-submit-link (:id tournament)]]])) (defn all-tournaments [] - (let [tournaments (subscribe [::subs/decklist-organizer-tournaments])] + (let [tournaments (subscribe [::subs/organizer-tournaments])] (fn all-tournaments-render [] [:div#decklist-organizer-tournaments [ui/raised-button {:href (routes/decklist-organizer-new-tournament-path) @@ -107,7 +107,7 @@ :padding 0} decklist-url (routes/decklist-organizer-view-path {:id (:id decklist)}) link-props {:href decklist-url - :on-click #(dispatch [::events/load-organizer-tournament-decklist (:id decklist)])}]] + :on-click #(dispatch [::events/load-decklist (:id decklist)])}]] ^{:key (str (:id decklist) "--row")} [ui/table-row {} [ui/table-row-column {:class-name :dci @@ -124,15 +124,15 @@ (format-date-time (:submitted decklist))]]])]]) (defn tournament [id] - (let [saved-tournament (subscribe [::subs/decklist-organizer-tournament]) - saving? (subscribe [::subs/decklist-saving?]) + (let [saved-tournament (subscribe [::subs/organizer-tournament]) + saving? (subscribe [::subs/saving?]) tournament (atom @saved-tournament) set-name #(swap! tournament assoc :name %) set-date (fn [_ date] (swap! tournament assoc :date (to-local-date date))) set-format (fn [_ _ format] (swap! tournament assoc :format (keyword format))) - save-tournament #(dispatch [::events/save-decklist-organizer-tournament + save-tournament #(dispatch [::events/save-tournament (select-keys @tournament [:id :name :format :date :deadline])]) selected-decklists (atom []) on-select (fn [selection] @@ -141,7 +141,7 @@ "all" (:decklist @tournament) "none" [] (map (:decklist @tournament) selection))))) - load-selected-decklists #(dispatch [::events/load-organizer-tournament-decklists + load-selected-decklists #(dispatch [::events/load-decklists (map :id @selected-decklists)])] (fn tournament-render [id] (when (and (nil? @tournament) @@ -266,13 +266,13 @@ (defn view-decklist [] (let [decklist (subscribe [::subs/decklist]) - tournament (subscribe [::subs/decklist-organizer-tournament])] + tournament (subscribe [::subs/organizer-tournament])] (fn view-decklist-render [] [render-decklist @decklist @tournament]))) (defn view-decklists [] (let [decklists (subscribe [::subs/decklists]) - tournament (subscribe [::subs/decklist-organizer-tournament]) + tournament (subscribe [::subs/organizer-tournament]) print-page #(when (and (seq @decklists) @tournament) (.print js/window))] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 934a8ae..52edbea 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -8,8 +8,9 @@ [oops.core :refer [oget]] [mtg-pairings-server.components.autosuggest :refer [autosuggest]] [mtg-pairings-server.components.tooltip :refer [tooltip]] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.events.decklist :as events] + [mtg-pairings-server.subscriptions.common :as common-subs] + [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] [mtg-pairings-server.util.material-ui :refer [get-theme text-field]] @@ -20,11 +21,11 @@ "Snow-Covered Mountain" "Snow-Covered Forest"}) (defn input [] - (let [tournament (subscribe [::subs/decklist-tournament]) - on-change #(dispatch [::events/decklist-add-card %]) + (let [tournament (subscribe [::subs/tournament]) + on-change #(dispatch [::events/add-card %]) suggestions (atom []) fetch-suggestions (debounce (fn [prefix] - (dispatch [::events/decklist-card-suggestions + (dispatch [::events/card-suggestions prefix (:format @tournament) #(reset! suggestions %)])) @@ -88,8 +89,8 @@ (defn decklist-table-row [board card error] (let [on-change (fn [_ _ quantity] - (dispatch [::events/decklist-set-quantity board (:name card) quantity])) - on-delete #(dispatch [::events/decklist-remove-card board (:name card)])] + (dispatch [::events/set-quantity board (:name card) quantity])) + on-delete #(dispatch [::events/remove-card board (:name card)])] (fn decklist-table-row-render [_ {:keys [name quantity]} error] [ui/table-row [ui/table-row-column {:class-name :quantity @@ -159,11 +160,11 @@ [decklist-table-row board card (or error (get error-cards name))])]]])))) (defn player-info [decklist] - (let [set-first-name #(dispatch [::events/decklist-update-player-info :first-name %]) - set-last-name #(dispatch [::events/decklist-update-player-info :last-name %]) - set-deck-name #(dispatch [::events/decklist-update-player-info :deck-name %]) - set-email #(dispatch [::events/decklist-update-player-info :email %]) - set-dci #(dispatch [::events/decklist-update-player-info :dci %])] + (let [set-first-name #(dispatch [::events/update-player-info :first-name %]) + set-last-name #(dispatch [::events/update-player-info :last-name %]) + set-deck-name #(dispatch [::events/update-player-info :deck-name %]) + set-email #(dispatch [::events/update-player-info :email %]) + set-dci #(dispatch [::events/update-player-info :dci %])] (fn player-info-render [_] [:div#player-info [:div.full-width @@ -207,7 +208,7 @@ :style {:vertical-align :top}}]]]))) (defn decklist-import [] - (let [mobile? (subscribe [::subs/mobile?]) + (let [mobile? (subscribe [::common-subs/mobile?]) selected (atom nil) on-active (fn [tab] (let [value (oget tab "props" "value")] @@ -216,11 +217,11 @@ value)))) address (atom "") address-on-change #(reset! address %) - import-from-address #(dispatch [::events/load-decklist-from-address @address]) + import-from-address #(dispatch [::events/import-address @address]) decklist (atom "") decklist-on-change #(reset! decklist %) import-decklist (fn [] - (dispatch [::events/load-text-decklist @decklist]) + (dispatch [::events/import-text @decklist]) (reset! selected nil))] (fn decklist-import-render [] @@ -280,16 +281,16 @@ (defn decklist-submit [] (let [decklist (subscribe [::subs/decklist]) - select-main #(dispatch [::events/decklist-select-board :main]) - select-side #(dispatch [::events/decklist-select-board :side]) + select-main #(dispatch [::events/select-board :main]) + select-side #(dispatch [::events/select-board :side]) button-style {:position :relative :top "-5px" :width "70px" :min-width "70px"} - tournament (subscribe [::subs/decklist-tournament]) - saving? (subscribe [::subs/decklist-saving?]) - saved? (subscribe [::subs/decklist-saved?]) - page (subscribe [::subs/page]) + tournament (subscribe [::subs/tournament]) + saving? (subscribe [::subs/saving?]) + saved? (subscribe [::subs/saved?]) + page (subscribe [::common-subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] (let [errors (decklist-errors @decklist)] @@ -329,6 +330,15 @@ [decklist-import] [:h3 "Pelaajan tiedot"] [player-info decklist] + (when @saved? + (let [url (str "https://pairings.fi/decklist/" (:id @page))] + [:div.success-notice + [:h4 "Tallennus onnistui!"] + [:p + "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " + [:a {:href url} + url] + ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]])) [ui/raised-button {:label "Tallenna" :on-click save-decklist @@ -340,13 +350,4 @@ {:size 36 :style {:margin "24px 0 0 24px" :vertical-align :top}}]) - [error-list errors] - (when @saved? - (let [url (str "https://pairings.fi/decklist/" (:id @page))] - [:div.success-notice - [:h4 "Tallennus onnistui!"] - [:p - "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " - [:a {:href url} - url] - ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]]))])))) + [error-list errors]])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs index f7f09e5..1337375 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs @@ -9,8 +9,9 @@ [prop-types] [oops.core :refer [oget]] [mtg-pairings-server.components.slider :refer [slider]] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.subscriptions.common :as common-subs] + [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.util :refer [to-local-date]] [mtg-pairings-server.util.material-ui :refer [get-theme]])) @@ -139,7 +140,7 @@ [clear-filters]]]))}))) (defn filters [] - (let [mobile? (subscribe [::subs/mobile?])] + (let [mobile? (subscribe [::common-subs/mobile?])] (fn filters-render [] (if @mobile? [mobile-filters] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs index 655106a..3118ea5 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs @@ -9,9 +9,9 @@ [oops.core :refer [oget]] [accountant.core :as accountant] [mtg-pairings-server.components.tournament :refer [tournament-card-header]] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.routes :refer [tournaments-path standings-path]] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.routes.pairings :refer [tournaments-path standings-path]] + [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.util :refer [format-date indexed]] [mtg-pairings-server.util.material-ui :refer [get-theme]])) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs index 627facf..736df24 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs @@ -6,8 +6,8 @@ [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] [goog.string :as gstring] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.util :refer [cls indexed]] [mtg-pairings-server.util.mtg :refer [duplicate-pairings]] [mtg-pairings-server.components.tournament :refer [standing-table]])) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs index 6a600f7..f6701d7 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs @@ -8,9 +8,9 @@ [goog.string :as gstring] [goog.string.format] [prop-types] - [mtg-pairings-server.events :as events] - [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.routes :refer [tournaments-path tournament-path pairings-path standings-path pods-path seatings-path bracket-path]] + [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.subscriptions.pairings :as subs] + [mtg-pairings-server.routes.pairings :refer [tournaments-path tournament-path pairings-path standings-path pods-path seatings-path bracket-path]] [mtg-pairings-server.components.paging :refer [with-paging]] [mtg-pairings-server.components.filter :refer [filters]] [mtg-pairings-server.util :refer [format-date indexed]] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index bcdc369..0177683 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -7,10 +7,14 @@ [mount.core :refer-macros [defstate]] [secretary.core :as secretary :include-macros true] [accountant.core :as accountant] - mtg-pairings-server.routes + mtg-pairings-server.routes.decklist + mtg-pairings-server.routes.pairings mtg-pairings-server.util.event-listener - [mtg-pairings-server.subscriptions :as subs] - [mtg-pairings-server.events :as events] + [mtg-pairings-server.subscriptions.common :as common-subs] + [mtg-pairings-server.subscriptions.decklist :as decklist-subs] + [mtg-pairings-server.subscriptions.pairings :as pairings-subs] + [mtg-pairings-server.events.decklist :as decklist-events] + [mtg-pairings-server.events.pairings :as pairings-events] [mtg-pairings-server.pages.decklist :refer [decklist-organizer decklist-submit]] [mtg-pairings-server.pages.main :refer [main-page]] [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] @@ -33,8 +37,8 @@ (:page page)))) (defn current-page [] - (let [page (subscribe [::subs/page]) - hide-organizer-menu? (subscribe [::subs/organizer :menu])] + (let [page (subscribe [::common-subs/page]) + hide-organizer-menu? (subscribe [::pairings-subs/organizer :menu])] (fn [] [ui/mui-theme-provider {:mui-theme theme} @@ -63,11 +67,11 @@ (defn ^:after-load figwheel-reload [] (clear-subscription-cache!) - (events/connect!) + (pairings-events/connect!) (mount-root)) (defn init! [] - (dispatch-sync [::events/initialize]) + (dispatch-sync [::pairings-events/initialize]) (accountant/configure-navigation! {:nav-handler (fn [path] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs new file mode 100644 index 0000000..5af41b4 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs @@ -0,0 +1,43 @@ +(ns mtg-pairings-server.events.common + (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] + [accountant.core :as accountant] + [mtg-pairings-server.util.local-storage :as local-storage] + [mtg-pairings-server.util.mobile :refer [mobile?]] + [mtg-pairings-server.websocket :as ws])) + +(defmethod ws/event-handler :chsk/recv + [{:keys [?data]}] + (let [[event data] ?data] + (dispatch [event data]))) + +(def channel-open? (atom false)) + +(reg-fx :ws-send + (fn ws-send + [data] + (let [[event timeout callback] (if (keyword? (first data)) + [data nil nil] + data)] + (if @channel-open? + (ws/send! event timeout callback) + (let [k (gensym)] + (add-watch channel-open? k (fn [_ _ _ new-val] + (when new-val + (remove-watch channel-open? k) + (ws/send! event timeout callback))))))))) + +(reg-event-db ::window-resized + (fn [db _] + (assoc db :mobile? (mobile?)))) + +(reg-fx :navigate + (fn [path] + (accountant/navigate! path))) + +(reg-fx :store + (fn [[key obj]] + (local-storage/store key obj))) + +(reg-event-db ::page + (fn [db [_ data]] + (assoc db :page data))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs new file mode 100644 index 0000000..68daac2 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -0,0 +1,172 @@ +(ns mtg-pairings-server.events.decklist + (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] + [oops.core :refer [oget]] + [mtg-pairings-server.events.common :as common-events] + [mtg-pairings-server.util :as util] + [mtg-pairings-server.websocket :as ws])) + +#_ +(defmethod ws/event-handler :chsk/state + [{:keys [?data]}] + (let [[_ new-state] ?data] + (when (:first-open? new-state) + (reset! common-events/channel-open? true)))) + +(def empty-decklist {:main [] + :side [] + :count {:main 0 + :side 0} + :board :main + :player {:dci "" + :first-name "" + :last-name "" + :deck-name "" + :email ""}}) + +#_ +(defn initial-db [] + (deep-merge {:tournaments {} + :tournament-count 0 + :tournaments-page 0 + :tournament-ids [] + :tournament-filter {:organizer "" + :date-from nil + :date-to nil + :players [0 100]} + :filters-active false + :max-players 100 + :player-tournaments [] + :pairings {:sort-key :table_number} + :pods {:sort-key :pod} + :seatings {:sort-key :table_number} + :page {:page :main + :id nil + :round nil} + :logged-in-user (local-storage/fetch :user) + :notification nil + :mobile? (mobile?) + :decklist-editor {:organizer-tournaments [] + :decklist empty-decklist}} + (transit/read (oget js/window "initial_db")))) + +#_ +(reg-event-db ::initialize + (fn [db _] + (merge db (initial-db)))) + +(reg-event-fx ::save-decklist + (fn [{:keys [db]} [_ tournament decklist]] + {:db (-> db + (assoc-in [:decklist-editor :saving] true) + (assoc-in [:decklist-editor :decklist] decklist)) + :ws-send [:client/save-decklist [tournament decklist]]})) + +(reg-event-fx :server/decklist-saved + (fn [{:keys [db]} [_ id]] + {:db (-> db + (assoc-in [:decklist-editor :saving] false) + (assoc-in [:decklist-editor :saved] true)) + :navigate (str "/decklist/" id)})) + +(reg-event-fx ::load-organizer-tournament + (fn [_ [_ id]] + {:ws-send [:client/decklist-organizer-tournament id]})) + +(reg-event-db :server/decklist-organizer-tournament + (fn [db [_ tournament]] + (assoc-in db [:decklist-editor :organizer-tournament] tournament))) + +(reg-event-fx ::save-tournament + (fn [{:keys [db]} [_ tournament]] + {:db (update db :decklist-editor merge {:saving true + :organizer-tournament tournament}) + :ws-send [:client/save-decklist-organizer-tournament tournament]})) + +(reg-event-fx :server/organizer-tournament-saved + (fn [{:keys [db]} [_ id]] + {:db (-> db + (assoc-in [:decklist-editor :organizer-tournament :id] id) + (assoc-in [:decklist-editor :saving] false) + (assoc-in [:decklist-editor :saved] true)) + :navigate (str "/decklist/organizer/" id)})) + +(reg-event-fx ::load-decklist + (fn [_ [_ id]] + {:ws-send [:client/load-decklist id]})) + +(reg-event-fx ::load-decklists + (fn [_ [_ id]] + {:ws-send [:client/load-decklists id]})) + +(reg-event-db :server/decklist + (fn [db [_ decklist]] + (assoc-in db [:decklist-editor :decklist] (merge empty-decklist decklist)))) + +(reg-event-db :server/decklists + (fn [db [_ decklists]] + (assoc-in db [:decklist-editor :decklists] decklists))) + +(reg-event-db :server/organizer-login + (fn [db [_ username]] + (assoc-in db [:decklist-editor :user] username))) + +(reg-event-db ::clear-tournament + (fn [db _] + (update db :decklist-editor merge {:organizer-tournament nil + :saving false + :saved false}))) + +(reg-event-fx ::import-address + (fn [_ [_ address]] + (when-let [[_ code] (re-find #"/decklist/([A-z0-9_-]{22})$" address)] + {:ws-send [:client/load-decklist-with-id code]}))) + +(defn ^:private add-card [{:keys [board] :as decklist} name] + (if (some #(= name (:name %)) (get decklist board)) + decklist + (-> decklist + (update board conj {:name name, :quantity 1}) + (update-in [:count board] inc)))) + +(reg-event-db ::add-card + (fn [db [_ name]] + (update-in db [:decklist-editor :decklist] add-card name))) + +(defn ^:private set-quantity [decklist board name quantity] + (let [index (util/index-where #(= name (:name %)) (get decklist board)) + orig-quantity (get-in decklist [board index :quantity])] + (-> decklist + (assoc-in [board index :quantity] quantity) + (update-in [:count board] + (- quantity orig-quantity))))) + +(reg-event-db ::set-quantity + (fn [db [_ board name quantity]] + (update-in db [:decklist-editor :decklist] set-quantity board name quantity))) + +(defn ^:private remove-card [decklist board name] + (let [cards (get decklist board) + index (util/index-where #(= name (:name %)) cards) + n (get-in decklist [board index :quantity])] + (-> decklist + (assoc board (util/dissoc-index cards index)) + (update-in [:count board] - n)))) + +(reg-event-db ::remove-card + (fn [db [_ board name]] + (update-in db [:decklist-editor :decklist] remove-card board name))) + +(reg-event-db ::select-board + (fn [db [_ board]] + (assoc-in db [:decklist-editor :decklist :board] board))) + +(reg-event-db ::update-player-info + (fn [db [_ key value]] + (assoc-in db [:decklist-editor :decklist :player key] value))) + +(reg-event-fx ::card-suggestions + (fn [_ [_ prefix format callback]] + {:ws-send [[:client/decklist-card-suggestions [prefix format]] 1000 callback]})) + +(reg-event-fx ::import-text + (fn [{:keys [db]} [_ text-decklist]] + {:ws-send [:client/load-text-decklist [text-decklist (get-in db [:decklist-editor :tournament :format])]]})) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs similarity index 69% rename from mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs rename to mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs index 3336a06..28b2559 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs @@ -1,9 +1,9 @@ -(ns mtg-pairings-server.events +(ns mtg-pairings-server.events.pairings (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] [cljs.core.async :refer [ db - (assoc-in [:decklist-editor :saving] true) - (assoc-in [:decklist-editor :decklist] decklist)) - :ws-send [:client/save-decklist [tournament decklist]]})) - -(reg-event-fx :server/decklist-saved - (fn [{:keys [db]} [_ id]] - {:db (-> db - (assoc-in [:decklist-editor :saving] false) - (assoc-in [:decklist-editor :saved] true)) - :navigate (str "/decklist/" id)})) - -(reg-event-fx ::load-decklist-organizer-tournament - (fn [_ [_ id]] - {:ws-send [:client/decklist-organizer-tournament id]})) - -(reg-event-db :server/decklist-organizer-tournament - (fn [db [_ tournament]] - (assoc-in db [:decklist-editor :organizer-tournament] tournament))) - -(reg-event-fx ::save-decklist-organizer-tournament - (fn [{:keys [db]} [_ tournament]] - {:db (update db :decklist-editor merge {:saving true - :organizer-tournament tournament}) - :ws-send [:client/save-decklist-organizer-tournament tournament]})) - -(reg-event-fx :server/organizer-tournament-saved - (fn [{:keys [db]} [_ id]] - {:db (-> db - (assoc-in [:decklist-editor :organizer-tournament :id] id) - (assoc-in [:decklist-editor :saving] false) - (assoc-in [:decklist-editor :saved] true)) - :navigate (str "/decklist/organizer/" id)})) - -(reg-event-fx ::load-organizer-tournament-decklist - (fn [_ [_ id]] - {:ws-send [:client/load-organizer-tournament-decklist id]})) - -(reg-event-fx ::load-organizer-tournament-decklists - (fn [_ [_ id]] - {:ws-send [:client/load-organizer-tournament-decklists id]})) - -(reg-event-db :server/organizer-tournament-decklist - (fn [db [_ decklist]] - (assoc-in db [:decklist-editor :decklist] (merge empty-decklist decklist)))) - -(reg-event-db :server/organizer-tournament-decklists - (fn [db [_ decklists]] - (assoc-in db [:decklist-editor :decklists] decklists))) - -(reg-event-db :server/organizer-login - (fn [db [_ username]] - (assoc-in db [:decklist-editor :user] username))) - -(reg-event-db ::clear-organizer-decklist-tournament - (fn [db _] - (update db :decklist-editor merge {:organizer-tournament nil - :saving false - :saved false}))) - -(reg-event-fx ::load-decklist-from-address - (fn [_ [_ address]] - (when-let [[_ code] (re-find #"/decklist/([A-z0-9_-]{22})$" address)] - {:ws-send [:client/load-decklist-with-id code]}))) - -(defn ^:private add-card [{:keys [board] :as decklist} name] - (if (some #(= name (:name %)) (get decklist board)) - decklist - (-> decklist - (update board conj {:name name, :quantity 1}) - (update-in [:count board] inc)))) - -(reg-event-db ::decklist-add-card - (fn [db [_ name]] - (update-in db [:decklist-editor :decklist] add-card name))) - -(defn ^:private set-quantity [decklist board name quantity] - (let [index (index-where #(= name (:name %)) (get decklist board)) - orig-quantity (get-in decklist [board index :quantity])] - (-> decklist - (assoc-in [board index :quantity] quantity) - (update-in [:count board] + (- quantity orig-quantity))))) - -(reg-event-db ::decklist-set-quantity - (fn [db [_ board name quantity]] - (update-in db [:decklist-editor :decklist] set-quantity board name quantity))) - -(defn ^:private remove-card [decklist board name] - (let [cards (get decklist board) - index (index-where #(= name (:name %)) cards) - n (get-in decklist [board index :quantity])] - (-> decklist - (assoc board (dissoc-index cards index)) - (update-in [:count board] - n)))) - -(reg-event-db ::decklist-remove-card - (fn [db [_ board name]] - (update-in db [:decklist-editor :decklist] remove-card board name))) - -(reg-event-db ::decklist-select-board - (fn [db [_ board]] - (assoc-in db [:decklist-editor :decklist :board] board))) - -(reg-event-db ::decklist-update-player-info - (fn [db [_ key value]] - (assoc-in db [:decklist-editor :decklist :player key] value))) - -(reg-event-fx ::decklist-card-suggestions - (fn [_ [_ prefix format callback]] - {:ws-send [[:client/decklist-card-suggestions [prefix format]] 1000 callback]})) - -(reg-event-fx ::load-text-decklist - (fn [{:keys [db]} [_ text-decklist]] - {:ws-send [:client/load-text-decklist [text-decklist (get-in db [:decklist-editor :tournament :format])]]})) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 7c0f897..4ea2b01 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -5,7 +5,7 @@ [cljs-react-material-ui.reagent :as ui] [mtg-pairings-server.components.decklist.organizer :as organizer] [mtg-pairings-server.components.decklist.submit :as submit] - [mtg-pairings-server.subscriptions :as subs])) + [mtg-pairings-server.subscriptions.decklist :as subs])) (defn decklist-submit [] [submit/decklist-submit]) @@ -16,7 +16,7 @@ :style {:margin "24px"}}]) (defn decklist-organizer [page] - (let [user (subscribe [::subs/decklist-organizer-user])] + (let [user (subscribe [::subs/user])] (fn decklist-organizer-render [page] (case @user nil [loading-indicator] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs index f815026..661c932 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs @@ -4,7 +4,7 @@ [cljs-react-material-ui.reagent :as ui] [mtg-pairings-server.components.main :refer [own-tournament pairing]] [mtg-pairings-server.components.tournament :refer [newest-tournaments-list]] - [mtg-pairings-server.subscriptions :as subs])) + [mtg-pairings-server.subscriptions.pairings :as subs])) (defn get-latest-pairing [player-tournaments] (let [t (first player-tournaments) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/organizer.cljs index ae35f0f..94ff0e2 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/organizer.cljs @@ -1,7 +1,7 @@ (ns mtg-pairings-server.pages.organizer (:require [re-frame.core :refer [subscribe]] [mtg-pairings-server.components.organizer :refer [menu pairings seatings pods standings clock]] - [mtg-pairings-server.subscriptions :as subs])) + [mtg-pairings-server.subscriptions.pairings :as subs])) (defn organizer-page [] (let [organizer-mode (subscribe [::subs/organizer-mode]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs index 443521d..8415c2d 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs @@ -4,8 +4,8 @@ [cljs-react-material-ui.core] [cljs-react-material-ui.reagent :as ui] [mtg-pairings-server.components.tournament :refer [tournament-list tournament-card-header tournament pairings standings pods seatings bracket]] - [mtg-pairings-server.routes :refer [tournament-path]] - [mtg-pairings-server.subscriptions :as subs] + [mtg-pairings-server.routes.pairings :refer [tournament-path]] + [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.util :refer [format-date]])) (defn tournaments-page [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/common.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/common.cljs new file mode 100644 index 0000000..489a87e --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/common.cljs @@ -0,0 +1,13 @@ +(ns mtg-pairings-server.routes.common + (:require [re-frame.core :refer [dispatch]] + [mtg-pairings-server.events.common :as events])) + +(defn dispatch-page + ([page] + (dispatch-page page nil nil)) + ([page id] + (dispatch-page page id nil)) + ([page id round] + (dispatch [::events/page {:page page + :id id + :round round}]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs new file mode 100644 index 0000000..73c336d --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs @@ -0,0 +1,27 @@ +(ns mtg-pairings-server.routes.decklist + (:require [re-frame.core :refer [dispatch]] + [secretary.core :as secretary :include-macros true] + [mtg-pairings-server.events.decklist :as events] + [mtg-pairings-server.routes.common :refer [dispatch-page]])) + +(secretary/defroute decklist-organizer-path "/decklist/organizer" [] + (dispatch-page :decklist-organizer)) + +(secretary/defroute decklist-organizer-print-path "/decklist/organizer/print" [] + (dispatch-page :decklist-organizer-view)) + +(secretary/defroute decklist-organizer-view-path "/decklist/organizer/view/:id" [id] + (dispatch-page :decklist-organizer-view id)) + +(secretary/defroute decklist-organizer-new-tournament-path "/decklist/organizer/new" [] + (dispatch [::events/clear-tournament]) + (dispatch-page :decklist-organizer-tournament)) + +(secretary/defroute decklist-organizer-tournament-path "/decklist/organizer/:id" [id] + (dispatch-page :decklist-organizer-tournament id)) + +(secretary/defroute new-decklist-submit-path "/decklist/tournament/:id" [id] + (dispatch-page :decklist-submit)) + +(secretary/defroute old-decklist-submit-path "/decklist/:id" [id] + (dispatch-page :decklist-submit id)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs similarity index 62% rename from mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs rename to mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs index d3db717..c22f061 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs @@ -1,17 +1,8 @@ -(ns mtg-pairings-server.routes +(ns mtg-pairings-server.routes.pairings (:require [re-frame.core :refer [dispatch]] [secretary.core :as secretary :include-macros true] - [mtg-pairings-server.events :as events])) - -(defn dispatch-page - ([page] - (dispatch-page page nil nil)) - ([page id] - (dispatch-page page id nil)) - ([page id round] - (dispatch [::events/page {:page page - :id id - :round round}]))) + [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.routes.common :refer [dispatch-page]])) (secretary/defroute "/" [] (dispatch-page :main)) @@ -67,24 +58,3 @@ (dispatch [::events/load-deck-construction id]) (dispatch-page :organizer-deck-construction id))) -(secretary/defroute decklist-organizer-path "/decklist/organizer" [] - (dispatch-page :decklist-organizer)) - -(secretary/defroute decklist-organizer-print-path "/decklist/organizer/print" [] - (dispatch-page :decklist-organizer-view)) - -(secretary/defroute decklist-organizer-view-path "/decklist/organizer/view/:id" [id] - (dispatch-page :decklist-organizer-view id)) - -(secretary/defroute decklist-organizer-new-tournament-path "/decklist/organizer/new" [] - (dispatch [::events/clear-organizer-decklist-tournament]) - (dispatch-page :decklist-organizer-tournament)) - -(secretary/defroute decklist-organizer-tournament-path "/decklist/organizer/:id" [id] - (dispatch-page :decklist-organizer-tournament id)) - -(secretary/defroute new-decklist-submit-path "/decklist/tournament/:id" [id] - (dispatch-page :decklist-submit)) - -(secretary/defroute old-decklist-submit-path "/decklist/:id" [id] - (dispatch-page :decklist-submit id)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/common.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/common.cljs new file mode 100644 index 0000000..986a6dd --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/common.cljs @@ -0,0 +1,10 @@ +(ns mtg-pairings-server.subscriptions.common + (:require [re-frame.core :refer [reg-sub subscribe]])) + +(reg-sub ::mobile? + (fn [db _] + (:mobile? db))) + +(reg-sub ::page + (fn [db _] + (:page db))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs new file mode 100644 index 0000000..4971470 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs @@ -0,0 +1,34 @@ +(ns mtg-pairings-server.subscriptions.decklist + (:require [re-frame.core :refer [reg-sub subscribe]])) + +(reg-sub ::tournament + (fn [db _] + (get-in db [:decklist-editor :tournament]))) + +(reg-sub ::saving? + (fn [db _] + (get-in db [:decklist-editor :saving]))) + +(reg-sub ::saved? + (fn [db _] + (get-in db [:decklist-editor :saved]))) + +(reg-sub ::decklist + (fn [db _] + (get-in db [:decklist-editor :decklist]))) + +(reg-sub ::decklists + (fn [db _] + (get-in db [:decklist-editor :decklists]))) + +(reg-sub ::organizer-tournaments + (fn [db _] + (get-in db [:decklist-editor :organizer-tournaments]))) + +(reg-sub ::organizer-tournament + (fn [db _] + (get-in db [:decklist-editor :organizer-tournament]))) + +(reg-sub ::user + (fn [db _] + (get-in db [:decklist-editor :user]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/pairings.cljs similarity index 80% rename from mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs rename to mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/pairings.cljs index 20c0f41..47d920a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/pairings.cljs @@ -1,14 +1,10 @@ -(ns mtg-pairings-server.subscriptions +(ns mtg-pairings-server.subscriptions.pairings (:require [re-frame.core :refer [reg-sub subscribe]] [cljs-time.core :as time] [clojure.string :as str] [mtg-pairings-server.util :refer [today-or-yesterday?]] [mtg-pairings-server.util.mtg :refer [duplicate-pairings]])) -(reg-sub ::mobile? - (fn [db _] - (:mobile? db))) - (reg-sub ::logged-in-user (fn [db _] (:logged-in-user db))) @@ -17,10 +13,6 @@ (fn [db _] (:player-tournaments db))) -(reg-sub ::page - (fn [db _] - (:page db))) - (reg-sub ::tournaments-page (fn [db _] (:tournaments-page db))) @@ -150,35 +142,3 @@ (reg-sub ::notification (fn [db _] (:notification db))) - -(reg-sub ::decklist-tournament - (fn [db _] - (get-in db [:decklist-editor :tournament]))) - -(reg-sub ::decklist-saving? - (fn [db _] - (get-in db [:decklist-editor :saving]))) - -(reg-sub ::decklist-saved? - (fn [db _] - (get-in db [:decklist-editor :saved]))) - -(reg-sub ::decklist - (fn [db _] - (get-in db [:decklist-editor :decklist]))) - -(reg-sub ::decklists - (fn [db _] - (get-in db [:decklist-editor :decklists]))) - -(reg-sub ::decklist-organizer-tournaments - (fn [db _] - (get-in db [:decklist-editor :organizer-tournaments]))) - -(reg-sub ::decklist-organizer-tournament - (fn [db _] - (get-in db [:decklist-editor :organizer-tournament]))) - -(reg-sub ::decklist-organizer-user - (fn [db _] - (get-in db [:decklist-editor :user]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/event_listener.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/event_listener.cljs index 58d0992..e5f1367 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/event_listener.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/event_listener.cljs @@ -1,7 +1,7 @@ (ns mtg-pairings-server.util.event-listener (:require [re-frame.core :refer [dispatch]] [mount.core :refer-macros [defstate]] - [mtg-pairings-server.events :as events] + [mtg-pairings-server.events.common :as events] [mtg-pairings-server.util.local-storage :as local-storage])) (defn resize-listener [_] From 8edba17563112f29de6ec6768488c29295260a17 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 06:07:52 +0300 Subject: [PATCH 20/44] Wait for WS to open before sending events --- .../cljc/mtg_pairings_server/websocket.cljc | 38 +++++++++++++++++-- .../mtg_pairings_server/events/common.cljs | 10 +---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc index 7fdc9fe..4b40d60 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/websocket.cljc @@ -1,5 +1,6 @@ (ns mtg-pairings-server.websocket - (:require [taoensso.sente :as sente] + (:require #?(:cljs [cljs.core.async :as async :include-macros true]) + [taoensso.sente :as sente] [taoensso.timbre :as log] #?(:clj [mount.core :refer [defstate]] :cljs [mount.core :refer-macros [defstate]]) @@ -54,14 +55,45 @@ #?(:clj (defmethod event-handler :chsk/ws-ping [_])) +(defn event-handler* [event] + (log/debugf "handling event %s" (first (:event event))) + (event-handler event)) + ;; Event router +(declare router) + +(defn ws-state [] + (some-> router deref :state deref)) + +#?(:cljs + (def send-ch (atom (async/chan 10)))) +#?(:cljs + (defn start-send-loop [] + (when-not @send-ch + (reset! send-ch (async/chan 10))) + (async/go-loop [[event timeout callback :as data] (async/ Date: Sat, 13 Apr 2019 06:08:26 +0300 Subject: [PATCH 21/44] Separate core namespaces for each UI --- .../prod/cljs/mtg_pairings_server/prod.cljs | 8 --- mtg-pairings-server/prod.cljs.edn | 5 -- .../src/clj/mtg_pairings_server/handler.clj | 9 ++- .../src/cljs/mtg_pairings_server/core.cljs | 20 +++--- .../cljs/mtg_pairings_server/decklist.cljs | 50 +++++++++++++ .../mtg_pairings_server/events/decklist.cljs | 43 +++-------- .../mtg_pairings_server/events/pairings.cljs | 25 +------ .../cljs/mtg_pairings_server/pairings.cljs | 71 +++++++++++++++++++ .../mtg_pairings_server/routes/decklist.cljs | 35 +++++---- .../mtg_pairings_server/util/material_ui.cljs | 10 +++ 10 files changed, 176 insertions(+), 100 deletions(-) delete mode 100644 mtg-pairings-server/env/prod/cljs/mtg_pairings_server/prod.cljs delete mode 100644 mtg-pairings-server/prod.cljs.edn create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/pairings.cljs diff --git a/mtg-pairings-server/env/prod/cljs/mtg_pairings_server/prod.cljs b/mtg-pairings-server/env/prod/cljs/mtg_pairings_server/prod.cljs deleted file mode 100644 index cff3ad3..0000000 --- a/mtg-pairings-server/env/prod/cljs/mtg_pairings_server/prod.cljs +++ /dev/null @@ -1,8 +0,0 @@ -(ns mtg-pairings-server.prod - (:require [mtg-pairings-server.core] - [mount.core :as m])) - -;;ignore println statements in prod -(set! *print-fn* (fn [& _])) - -(m/start) diff --git a/mtg-pairings-server/prod.cljs.edn b/mtg-pairings-server/prod.cljs.edn deleted file mode 100644 index fb3ab82..0000000 --- a/mtg-pairings-server/prod.cljs.edn +++ /dev/null @@ -1,5 +0,0 @@ -^{:watch-dirs ["src/cljs" "src/cljc" "env/prod/cljs"]} -{:main mtg-pairings-server.prod - :output-to "target/js/prod-main.js" - :output-dir "target/js/compiled" - :optimizations :advanced} diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 70952dc..db77bf9 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -159,10 +159,13 @@ [{:keys [uid]}] (broadcast/disconnect uid)) -(defmethod ws/event-handler :client/connect - [{:keys [uid ring-req]}] +(defmethod ws/event-handler :client/connect-pairings + [{:keys [uid]}] (log/debugf "New connection from %s" uid) - (ws/send! uid [:server/tournaments (tournament/client-tournaments)]) + (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) + +(defmethod ws/event-handler :client/connect-decklist + [{:keys [uid ring-req]}] (ws/send! uid [:server/organizer-login (get-in ring-req [:session :identity :username] false)])) (defmethod ws/event-handler :client/login diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 0177683..6afab7c 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -2,12 +2,11 @@ (:require [reagent.core :as reagent :refer [atom]] [re-frame.core :refer [dispatch-sync subscribe clear-subscription-cache!]] [cljsjs.material-ui] - [cljs-react-material-ui.core :refer [get-mui-theme]] [cljs-react-material-ui.reagent :as ui] [mount.core :refer-macros [defstate]] [secretary.core :as secretary :include-macros true] [accountant.core :as accountant] - mtg-pairings-server.routes.decklist + [mtg-pairings-server.routes.decklist :as decklist-routes] mtg-pairings-server.routes.pairings mtg-pairings-server.util.event-listener [mtg-pairings-server.subscriptions.common :as common-subs] @@ -20,16 +19,8 @@ [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] [mtg-pairings-server.pages.organizer :refer [organizer-page organizer-menu deck-construction-tables]] [mtg-pairings-server.components.organizer :as organizer] - [mtg-pairings-server.components.main :refer [header notification]])) - -(def theme (get-mui-theme - {:palette {:primary1-color "#90caf9" - :primary2-color "#5d99c6" - :primary3-color "#c3fdff" - :accent1-color "#ec407a" - :accent2-color "#b4004e" - :accent3-color "#ff77a9" - :picker-header-color "#5d99c6"}})) + [mtg-pairings-server.components.main :refer [header notification]] + [mtg-pairings-server.util.material-ui :refer [theme]])) (defn display-header? [page] (not (contains? #{:organizer :decklist-submit :decklist-organizer @@ -68,10 +59,15 @@ (defn ^:after-load figwheel-reload [] (clear-subscription-cache!) (pairings-events/connect!) + (decklist-events/connect!) (mount-root)) (defn init! [] (dispatch-sync [::pairings-events/initialize]) + (dispatch-sync [::decklist-events/initialize]) + (decklist-routes/initialize-routes "/decklist") + (pairings-events/connect!) + (decklist-events/connect!) (accountant/configure-navigation! {:nav-handler (fn [path] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs new file mode 100644 index 0000000..9e1c06e --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs @@ -0,0 +1,50 @@ +(ns mtg-pairings-server.decklist + (:require [reagent.core :as reagent :refer [atom]] + [re-frame.core :refer [dispatch-sync subscribe]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [mount.core :as m :refer-macros [defstate]] + [secretary.core :as secretary :include-macros true] + [accountant.core :as accountant] + [mtg-pairings-server.events.decklist :as events] + [mtg-pairings-server.pages.decklist :refer [decklist-organizer decklist-submit]] + [mtg-pairings-server.routes.decklist :as routes] + [mtg-pairings-server.subscriptions.common :as subs] + [mtg-pairings-server.util.material-ui :refer [theme]])) + +(defn current-page [] + (let [page (subscribe [::subs/page])] + (fn [] + [ui/mui-theme-provider + {:mui-theme theme} + [:div + [:div#main-container + (case (:page @page) + :decklist-submit [#'decklist-submit] + (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer @page] + nil)]]]))) + +(defn mount-root [] + (reagent/render [current-page] (.getElementById js/document "app"))) + +(defn init! [] + (dispatch-sync [::events/initialize]) + (routes/initialize-routes "") + (events/connect!) + (accountant/configure-navigation! + {:nav-handler + (fn [path] + (secretary/dispatch! path)) + :path-exists? + (fn [path] + (secretary/locate-route path))}) + (accountant/dispatch-current!) + (mount-root)) + +(defstate core + :start (init!)) + +;;ignore println statements in prod +(set! *print-fn* (fn [& _])) + +(m/start) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 68daac2..273b05d 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -1,17 +1,11 @@ (ns mtg-pairings-server.events.decklist (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] [oops.core :refer [oget]] - [mtg-pairings-server.events.common :as common-events] + [mtg-pairings-server.transit :as transit] [mtg-pairings-server.util :as util] + [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) -#_ -(defmethod ws/event-handler :chsk/state - [{:keys [?data]}] - (let [[_ new-state] ?data] - (when (:first-open? new-state) - (reset! common-events/channel-open? true)))) - (def empty-decklist {:main [] :side [] :count {:main 0 @@ -23,37 +17,18 @@ :deck-name "" :email ""}}) -#_ (defn initial-db [] - (deep-merge {:tournaments {} - :tournament-count 0 - :tournaments-page 0 - :tournament-ids [] - :tournament-filter {:organizer "" - :date-from nil - :date-to nil - :players [0 100]} - :filters-active false - :max-players 100 - :player-tournaments [] - :pairings {:sort-key :table_number} - :pods {:sort-key :pod} - :seatings {:sort-key :table_number} - :page {:page :main - :id nil - :round nil} - :logged-in-user (local-storage/fetch :user) - :notification nil - :mobile? (mobile?) - :decklist-editor {:organizer-tournaments [] - :decklist empty-decklist}} - (transit/read (oget js/window "initial_db")))) - -#_ + (util/deep-merge {:decklist-editor {:organizer-tournaments [] + :decklist empty-decklist}} + (transit/read (oget js/window "initial_db")))) + (reg-event-db ::initialize (fn [db _] (merge db (initial-db)))) +(defn connect! [] + (ws/send! [:client/connect-decklist])) + (reg-event-fx ::save-decklist (fn [{:keys [db]} [_ tournament decklist]] {:db (-> db diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs index 28b2559..48993da 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs @@ -3,7 +3,6 @@ [cljs.core.async :refer [clj (oget component "context" "muiTheme") :keywordize-keys true)) From 914b3947d9207d451d45beaa96514846ad99ac20 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 06:16:00 +0300 Subject: [PATCH 22/44] Fix NPE on decklist page --- .../components/decklist/organizer.cljs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index dcf7cf2..58b8720 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -213,17 +213,15 @@ (defn render-decklist [decklist tournament] (let [{:keys [player main side id], counts :count} decklist {:keys [last-name first-name dci deck-name]} player + [l1 l2 l3] last-name dci (vec dci) {tournament-id :id, tournament-name :name, date :date} tournament] [:div.organizer-decklist [:div.first-letters [:div.label "Alkukirjaimet"] - [:div.letter - (nth last-name 0)] - [:div.letter - (nth last-name 1)] - [:div.letter - (nth last-name 2)]] + [:div.letter l1] + [:div.letter l2] + [:div.letter l3]] [:div.deck-info [:div.tournament-date [:div.label "Päivä:"] From b3752f7150da45eeedfcce47b5ffb3aa77795c5b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 06:30:38 +0300 Subject: [PATCH 23/44] Separate events namespace on server --- .../src/clj/mtg_pairings_server/events.clj | 139 ++++++++++++++++++ .../src/clj/mtg_pairings_server/handler.clj | 134 ----------------- 2 files changed, 139 insertions(+), 134 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/events.clj diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj new file mode 100644 index 0000000..f3cf0f1 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -0,0 +1,139 @@ +(ns mtg-pairings-server.events + (:require [taoensso.timbre :as log] + [mtg-pairings-server.service.decklist :as decklist] + [mtg-pairings-server.service.player :as player] + [mtg-pairings-server.service.tournament :as tournament] + [mtg-pairings-server.util.broadcast :as broadcast] + [mtg-pairings-server.websocket :as ws])) + + +(defmethod ws/event-handler + :chsk/uidport-open + [_]) + +(defmethod ws/event-handler :chsk/uidport-close + [{:keys [uid]}] + (broadcast/disconnect uid)) + +(defmethod ws/event-handler :client/connect-pairings + [{:keys [uid]}] + (log/debugf "New connection from %s" uid) + (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) + +(defmethod ws/event-handler :client/connect-decklist + [{:keys [uid ring-req]}] + (ws/send! uid [:server/organizer-login (get-in ring-req [:session :identity :username] false)])) + +(defmethod ws/event-handler :client/login + [{uid :uid, dci-number :?data}] + (try + (if-let [player (player/player dci-number)] + (do + (ws/send! uid [:server/login player]) + (ws/send! uid [:server/player-tournaments (player/tournaments dci-number)]) + (broadcast/login uid dci-number)) + (ws/send! uid [:server/login nil])) + (catch NumberFormatException _ + (ws/send! uid [:server/login nil])))) + +(defmethod ws/event-handler :client/logout + [{:keys [uid]}] + (broadcast/logout uid)) + +(defmethod ws/event-handler :client/tournaments + [{:keys [uid]}] + (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) + +(defmethod ws/event-handler :client/tournament + [{uid :uid, id :?data}] + (ws/send! uid [:server/tournament (tournament/client-tournament id)])) + +(defmethod ws/event-handler :client/pairings + [{uid :uid, [id round] :?data}] + (ws/send! uid [:server/pairings [id round (tournament/get-round id round)]])) + +(defmethod ws/event-handler :client/standings + [{uid :uid, [id round] :?data}] + (ws/send! uid [:server/standings [id round (tournament/standings id round false)]])) + +(defmethod ws/event-handler :client/pods + [{uid :uid, [id round] :?data}] + (ws/send! uid [:server/pods [id round (tournament/pods id round)]])) + +(defmethod ws/event-handler :client/seatings + [{uid :uid, id :?data}] + (ws/send! uid [:server/seatings [id (tournament/seatings id)]])) + +(defmethod ws/event-handler :client/bracket + [{uid :uid, id :?data}] + (ws/send! uid [:server/bracket [id (tournament/bracket id)]])) + +(defmethod ws/event-handler :client/organizer-tournament + [{uid :uid, id :?data}] + (broadcast/watch uid id) + (ws/send! uid [:server/organizer-tournament (tournament/tournament id)])) + +(defmethod ws/event-handler :client/organizer-pairings + [{uid :uid, [id round] :?data}] + (ws/send! uid [:server/organizer-pairings (tournament/get-round id round)])) + +(defmethod ws/event-handler :client/organizer-standings + [{uid :uid, [id round] :?data}] + (ws/send! uid [:server/organizer-standings (tournament/standings id round false)])) + +(defmethod ws/event-handler :client/organizer-pods + [{uid :uid, [id number] :?data}] + (when-not (Double/isNaN number) + (ws/send! uid [:server/organizer-pods (tournament/pods id number)]))) + +(defmethod ws/event-handler :client/organizer-seatings + [{uid :uid, id :?data}] + (ws/send! uid [:server/organizer-seatings (tournament/seatings id)])) + +(defmethod ws/event-handler :client/deck-construction + [{uid :uid, id :?data}] + (ws/send! uid [:server/organizer-seatings (tournament/seatings id)]) + (ws/send! uid [:server/organizer-pods (tournament/latest-pods id)])) + +(defmethod ws/event-handler :client/save-decklist + [{uid :uid, [tournament decklist] :?data}] + (let [id (decklist/save-decklist tournament decklist)] + (ws/send! uid [:server/decklist-saved id]))) + +(defmethod ws/event-handler :client/decklist-organizer-tournament + [{uid :uid, id :?data, ring-req :ring-req}] + (let [tournament (decklist/get-organizer-tournament id)] + (when (= (get-in ring-req [:session :identity :id]) (:user tournament)) + (ws/send! uid [:server/decklist-organizer-tournament tournament])))) + +(defmethod ws/event-handler :client/save-decklist-organizer-tournament + [{uid :uid, tournament :?data, ring-req :ring-req}] + (let [user-id (get-in ring-req [:session :identity :id]) + id (decklist/save-organizer-tournament user-id tournament)] + (when id + (ws/send! uid [:server/organizer-tournament-saved id])))) + +(defmethod ws/event-handler :client/load-decklist + [{uid :uid, id :?data}] + (ws/send! uid [:server/decklist (decklist/get-decklist id)])) + +(defmethod ws/event-handler :client/load-decklists + [{uid :uid, ids :?data}] + (ws/send! uid [:server/decklists (->> (map decklist/get-decklist ids) + (sort-by (fn [d] + [(get-in d [:player :last-name]) + (get-in d [:player :first-name])])))])) + +(defmethod ws/event-handler :client/load-decklist-with-id + [{uid :uid, id :?data}] + (if-let [decklist (decklist/get-decklist id)] + (ws/send! uid [:server/decklist (dissoc decklist :id :player)]) + (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) + +(defmethod ws/event-handler :client/decklist-card-suggestions + [{[prefix format] :?data, reply-fn :?reply-fn}] + (reply-fn (decklist/search-cards prefix format))) + +(defmethod ws/event-handler :client/load-text-decklist + [{uid :uid, [text-decklist format] :?data}] + (ws/send! uid [:server/decklist (decklist/load-text-decklist text-decklist format)])) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index db77bf9..dbe186c 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -5,7 +5,6 @@ [config.core :refer [env]] [hiccup.page :refer [include-js include-css html5]] [schema.core :as s] - [taoensso.timbre :as log] [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] [ring.middleware.jsonp :refer [wrap-json-with-padding]] [mtg-pairings-server.api.http :as http-api] @@ -15,9 +14,7 @@ [mtg-pairings-server.middleware.log :refer [wrap-request-log]] [mtg-pairings-server.service.decklist :as decklist] [mtg-pairings-server.service.tournament :as tournament] - [mtg-pairings-server.service.player :as player] [mtg-pairings-server.transit :as transit] - [mtg-pairings-server.util.broadcast :as broadcast] [mtg-pairings-server.websocket :as ws])) (defn escape-quotes [s] @@ -150,134 +147,3 @@ wrap-json-with-padding wrap-request-log wrap-allow-origin)) - -(defmethod ws/event-handler - :chsk/uidport-open - [_]) - -(defmethod ws/event-handler :chsk/uidport-close - [{:keys [uid]}] - (broadcast/disconnect uid)) - -(defmethod ws/event-handler :client/connect-pairings - [{:keys [uid]}] - (log/debugf "New connection from %s" uid) - (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) - -(defmethod ws/event-handler :client/connect-decklist - [{:keys [uid ring-req]}] - (ws/send! uid [:server/organizer-login (get-in ring-req [:session :identity :username] false)])) - -(defmethod ws/event-handler :client/login - [{uid :uid, dci-number :?data}] - (try - (if-let [player (player/player dci-number)] - (do - (ws/send! uid [:server/login player]) - (ws/send! uid [:server/player-tournaments (player/tournaments dci-number)]) - (broadcast/login uid dci-number)) - (ws/send! uid [:server/login nil])) - (catch NumberFormatException _ - (ws/send! uid [:server/login nil])))) - -(defmethod ws/event-handler :client/logout - [{:keys [uid]}] - (broadcast/logout uid)) - -(defmethod ws/event-handler :client/tournaments - [{:keys [uid]}] - (ws/send! uid [:server/tournaments (tournament/client-tournaments)])) - -(defmethod ws/event-handler :client/tournament - [{uid :uid, id :?data}] - (ws/send! uid [:server/tournament (tournament/client-tournament id)])) - -(defmethod ws/event-handler :client/pairings - [{uid :uid, [id round] :?data}] - (ws/send! uid [:server/pairings [id round (tournament/get-round id round)]])) - -(defmethod ws/event-handler :client/standings - [{uid :uid, [id round] :?data}] - (ws/send! uid [:server/standings [id round (tournament/standings id round false)]])) - -(defmethod ws/event-handler :client/pods - [{uid :uid, [id round] :?data}] - (ws/send! uid [:server/pods [id round (tournament/pods id round)]])) - -(defmethod ws/event-handler :client/seatings - [{uid :uid, id :?data}] - (ws/send! uid [:server/seatings [id (tournament/seatings id)]])) - -(defmethod ws/event-handler :client/bracket - [{uid :uid, id :?data}] - (ws/send! uid [:server/bracket [id (tournament/bracket id)]])) - -(defmethod ws/event-handler :client/organizer-tournament - [{uid :uid, id :?data}] - (broadcast/watch uid id) - (ws/send! uid [:server/organizer-tournament (tournament/tournament id)])) - -(defmethod ws/event-handler :client/organizer-pairings - [{uid :uid, [id round] :?data}] - (ws/send! uid [:server/organizer-pairings (tournament/get-round id round)])) - -(defmethod ws/event-handler :client/organizer-standings - [{uid :uid, [id round] :?data}] - (ws/send! uid [:server/organizer-standings (tournament/standings id round false)])) - -(defmethod ws/event-handler :client/organizer-pods - [{uid :uid, [id number] :?data}] - (when-not (Double/isNaN number) - (ws/send! uid [:server/organizer-pods (tournament/pods id number)]))) - -(defmethod ws/event-handler :client/organizer-seatings - [{uid :uid, id :?data}] - (ws/send! uid [:server/organizer-seatings (tournament/seatings id)])) - -(defmethod ws/event-handler :client/deck-construction - [{uid :uid, id :?data}] - (ws/send! uid [:server/organizer-seatings (tournament/seatings id)]) - (ws/send! uid [:server/organizer-pods (tournament/latest-pods id)])) - -(defmethod ws/event-handler :client/save-decklist - [{uid :uid, [tournament decklist] :?data}] - (let [id (decklist/save-decklist tournament decklist)] - (ws/send! uid [:server/decklist-saved id]))) - -(defmethod ws/event-handler :client/decklist-organizer-tournament - [{uid :uid, id :?data, ring-req :ring-req}] - (let [tournament (decklist/get-organizer-tournament id)] - (when (= (get-in ring-req [:session :identity :id]) (:user tournament)) - (ws/send! uid [:server/decklist-organizer-tournament tournament])))) - -(defmethod ws/event-handler :client/save-decklist-organizer-tournament - [{uid :uid, tournament :?data, ring-req :ring-req}] - (let [user-id (get-in ring-req [:session :identity :id]) - id (decklist/save-organizer-tournament user-id tournament)] - (when id - (ws/send! uid [:server/organizer-tournament-saved id])))) - -(defmethod ws/event-handler :client/load-decklist - [{uid :uid, id :?data}] - (ws/send! uid [:server/decklist (decklist/get-decklist id)])) - -(defmethod ws/event-handler :client/load-decklists - [{uid :uid, ids :?data}] - (ws/send! uid [:server/decklists (->> (map decklist/get-decklist ids) - (sort-by (fn [d] - [(get-in d [:player :last-name]) - (get-in d [:player :first-name])])))])) - -(defmethod ws/event-handler :client/load-decklist-with-id - [{uid :uid, id :?data}] - (if-let [decklist (decklist/get-decklist id)] - (ws/send! uid [:server/decklist (dissoc decklist :id :player)]) - (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) - -(defmethod ws/event-handler :client/decklist-card-suggestions - [{[prefix format] :?data, reply-fn :?reply-fn}] - (reply-fn (decklist/search-cards prefix format))) - -(defmethod ws/event-handler :client/load-text-decklist - [{uid :uid, [text-decklist format] :?data}] - (ws/send! uid [:server/decklist (decklist/load-text-decklist text-decklist format)])) From f340137e0d7768390f5e97c067d16ced950e262a Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 06:33:26 +0300 Subject: [PATCH 24/44] Separate JS builds for the UIs --- mtg-pairings-server/Dockerfile | 4 +- mtg-pairings-server/decklist.cljs.edn | 4 + mtg-pairings-server/pairings.cljs.edn | 4 + mtg-pairings-server/project.clj | 2 +- mtg-pairings-server/scripts/tag-assets.sh | 15 ++-- .../src/clj/mtg_pairings_server/handler.clj | 84 +++++++++---------- .../src/clj/mtg_pairings_server/server.clj | 1 + 7 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 mtg-pairings-server/decklist.cljs.edn create mode 100644 mtg-pairings-server/pairings.cljs.edn diff --git a/mtg-pairings-server/Dockerfile b/mtg-pairings-server/Dockerfile index 097a573..4388c86 100644 --- a/mtg-pairings-server/Dockerfile +++ b/mtg-pairings-server/Dockerfile @@ -11,9 +11,9 @@ COPY ./env ./env COPY ./resources ./resources COPY ./test ./test COPY ./test-resources ./test-resources -COPY prod.cljs.edn . +COPY decklist.cljs.edn pairings.cljs.edn ./ RUN lein test -RUN lein with-profile provided,prod do clean, garden once, minify-assets, fig:min +RUN lein with-profile provided,prod do clean, garden once, minify-assets, fig:min pairings, fig:min decklist RUN ./scripts/tag-assets.sh RUN lein uberjar diff --git a/mtg-pairings-server/decklist.cljs.edn b/mtg-pairings-server/decklist.cljs.edn new file mode 100644 index 0000000..be6e8cc --- /dev/null +++ b/mtg-pairings-server/decklist.cljs.edn @@ -0,0 +1,4 @@ +{:main mtg-pairings-server.decklist + :output-to "target/js/decklist-main.js" + :output-dir "target/js/compiled/decklist" + :optimizations :advanced} diff --git a/mtg-pairings-server/pairings.cljs.edn b/mtg-pairings-server/pairings.cljs.edn new file mode 100644 index 0000000..e1df048 --- /dev/null +++ b/mtg-pairings-server/pairings.cljs.edn @@ -0,0 +1,4 @@ +{:main mtg-pairings-server.pairings + :output-to "target/js/pairings-main.js" + :output-dir "target/js/compiled/pairings" + :optimizations :advanced} diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index db7e588..d82c12f 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -48,7 +48,7 @@ :target "target/public/css/main.min.css"}]] :aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"] - "fig:min" ["run" "-m" "figwheel.main" "-bo" "prod"]} + "fig:min" ["run" "-m" "figwheel.main" "-bo"]} :cljfmt {:indents {reg-sub [[:inner 0]] reg-fx [[:inner 0]] diff --git a/mtg-pairings-server/scripts/tag-assets.sh b/mtg-pairings-server/scripts/tag-assets.sh index 17a7f0c..d1efa58 100755 --- a/mtg-pairings-server/scripts/tag-assets.sh +++ b/mtg-pairings-server/scripts/tag-assets.sh @@ -5,14 +5,19 @@ set -euo pipefail mkdir -p resources/public/js mkdir -p resources/public/css -jssha=$(sha1sum target/js/prod-main.js) -jsfile="js/prod-main.${jssha:0:8}.js" -cp target/js/prod-main.js "resources/public/$jsfile" +pairingsjssha=$(sha1sum target/js/pairings-main.js) +pairingsjsfile="js/pairings-main.${pairingsjssha:0:8}.js" +cp target/js/pairings-main.js "resources/public/$pairingsjsfile" + +decklistjssha=$(sha1sum target/js/decklist-main.js) +decklistjsfile="js/decklist-main.${decklistjssha:0:8}.js" +cp target/js/decklist-main.js "resources/public/$decklistjsfile" csssha=$(sha1sum target/public/css/main.min.css) cssfile="css/main.${csssha:0:8}.css" cp target/public/css/main.min.css "resources/public/$cssfile" -echo "{:main-css \"/$cssfile\" - :main-js \"/$jsfile\"} +echo "{:main-css \"/$cssfile\" + :pairings-js \"/$pairingsjsfile\" + :decklist-js \"/$decklistjsfile\"} " > resources/config.edn diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index dbe186c..adb410d 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -20,10 +20,10 @@ (defn escape-quotes [s] (str/escape s {\' "\\'"})) -(defn loading-page - ([] - (loading-page {})) - ([initial-db] +(defn index + ([js-file] + (index js-file {})) + ([js-file initial-db] (let [html (html5 {:lang :fi} [:head @@ -42,7 +42,7 @@ "var initial_db = '" (escape-quotes (transit/write initial-db)) "'; ")] (include-js (if (env :dev) "/js/dev-main.js" - (env :main-js)))])] + (env js-file)))])] {:status 200 :body html :headers {"Content-Type" "text/html" @@ -57,79 +57,79 @@ :body "403 Forbidden"}) (defroutes site-routes - (GET "/" [] (loading-page)) + (GET "/" [] (index :pairings-js)) (GET "/tournaments" [] - (loading-page {:page {:page :tournaments}})) + (index :pairings-js {:page {:page :tournaments}})) (GET "/tournaments/:id" [] :path-params [id :- s/Int] - (loading-page {:page {:page :tournament, :id id} - :tournaments {id (tournament/client-tournament id)}})) + (index :pairings-js {:page {:page :tournament, :id id} + :tournaments {id (tournament/client-tournament id)}})) (GET "/tournaments/:id/pairings-:round" [] :path-params [id :- s/Int round :- s/Int] - (loading-page {:page {:page :pairings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pairings {id {round (tournament/get-round id round)}}})) + (index :pairings-js {:page {:page :pairings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pairings {id {round (tournament/get-round id round)}}})) (GET "/tournaments/:id/standings-:round" [] :path-params [id :- s/Int round :- s/Int] - (loading-page {:page {:page :standings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :standings {id {round (tournament/standings id round false)}}})) + (index :pairings-js {:page {:page :standings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :standings {id {round (tournament/standings id round false)}}})) (GET "/tournaments/:id/pods-:round" [] :path-params [id :- s/Int round :- s/Int] - (loading-page {:page {:page :pods, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pods {id {round (tournament/pods id round)}}})) + (index :pairings-js {:page {:page :pods, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pods {id {round (tournament/pods id round)}}})) (GET "/tournaments/:id/seatings" [] :path-params [id :- s/Int] - (loading-page {:page {:page :seatings, :id id} - :tournaments {id (tournament/client-tournament id)} - :seatings {id (tournament/seatings id)}})) + (index :pairings-js {:page {:page :seatings, :id id} + :tournaments {id (tournament/client-tournament id)} + :seatings {id (tournament/seatings id)}})) (GET "/tournaments/:id/bracket" [] :path-params [id :- s/Int] - (loading-page {:page {:page :bracket, :id id} - :tournaments {id (tournament/client-tournament id)} - :bracket {id (tournament/bracket id)}})) - (GET "/tournaments/:id/organizer" [] (loading-page)) - (GET "/tournaments/:id/organizer/menu" [] (loading-page)) - (GET "/tournaments/:id/organizer/deck-construction" [] (loading-page)) + (index :pairings-js {:page {:page :bracket, :id id} + :tournaments {id (tournament/client-tournament id)} + :bracket {id (tournament/bracket id)}})) + (GET "/tournaments/:id/organizer" [] (index :pairings-js)) + (GET "/tournaments/:id/organizer/menu" [] (index :pairings-js)) + (GET "/tournaments/:id/organizer/deck-construction" [] (index :pairings-js)) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (loading-page {:page {:page :decklist-submit} - :decklist-editor {:tournament (decklist/get-tournament id)}})) + (index :decklist-js {:page {:page :decklist-submit} + :decklist-editor {:tournament (decklist/get-tournament id)}})) (GET "/decklist/organizer" request - (loading-page {:page {:page :decklist-organizer} - :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) + (index :decklist-js {:page {:page :decklist-organizer} + :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) (GET "/decklist/organizer/new" [] - (loading-page {:page {:page :decklist-organizer-tournament}})) + (index :decklist-js {:page {:page :decklist-organizer-tournament}})) (GET "/decklist/organizer/view/:id" request :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id) tournament (decklist/get-organizer-tournament (:tournament decklist)) user-id (get-in request [:session :identity :id])] (cond - (not user-id) (loading-page {:page {:page :decklist-organizer-view, :id id}}) - (= user-id (:user tournament)) (loading-page {:page {:page :decklist-organizer-view, :id id} - :decklist-editor {:decklist decklist - :organizer-tournament tournament}}) + (not user-id) (index :decklist-js {:page {:page :decklist-organizer-view, :id id}}) + (= user-id (:user tournament)) (index :decklist-js {:page {:page :decklist-organizer-view, :id id} + :decklist-editor {:decklist decklist + :organizer-tournament tournament}}) :else forbidden))) (GET "/decklist/organizer/:id" request :path-params [id :- s/Str] (let [tournament (decklist/get-organizer-tournament id) user-id (get-in request [:session :identity :id])] (cond - (not user-id) (loading-page {:page {:page :decklist-organizer-tournament, :id id}}) - (= user-id (:user tournament)) (loading-page {:page {:page :decklist-organizer-tournament, :id id} - :decklist-editor {:organizer-tournament tournament}}) + (not user-id) (index :decklist-js {:page {:page :decklist-organizer-tournament, :id id}}) + (= user-id (:user tournament)) (index :decklist-js {:page {:page :decklist-organizer-tournament, :id id} + :decklist-editor {:organizer-tournament tournament}}) :else forbidden))) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id)] - (loading-page {:page {:page :decklist-submit, :id id} - :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) - :decklist decklist}}))) + (index :decklist-js {:page {:page :decklist-submit, :id id} + :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) + :decklist decklist}}))) (GET "/robots.txt" [] robots-txt) auth-routes ws/routes) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/server.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/server.clj index d2e48ac..9c3bc3b 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/server.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/server.clj @@ -2,6 +2,7 @@ (:require [taoensso.timbre :as log] [mount.core :refer [defstate]] [aleph.http :as http] + [mtg-pairings-server.events] [mtg-pairings-server.handler] [config.core :refer [env]])) From b8d87fa2c9fe338e1b099bf34738026b7014672b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 08:04:40 +0300 Subject: [PATCH 25/44] Enable serving decklists from different domain --- .../src/clj/mtg_pairings_server/auth.clj | 10 +- .../src/clj/mtg_pairings_server/handler.clj | 166 ++++++++++-------- .../clj/mtg_pairings_server/middleware.clj | 2 + .../middleware/decklist.clj | 24 +++ .../mtg_pairings_server/middleware/log.clj | 1 + .../src/cljc/mtg_pairings_server/util.cljc | 9 +- .../components/decklist/organizer.cljs | 16 +- .../components/decklist/submit.cljs | 5 +- .../mtg_pairings_server/events/decklist.cljs | 5 +- .../mtg_pairings_server/routes/decklist.cljs | 25 ++- 10 files changed, 160 insertions(+), 103 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/middleware/decklist.clj diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 3888d63..850c723 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -1,13 +1,14 @@ (ns mtg-pairings-server.auth (:require [buddy.auth.backends.session :refer [session-backend]] [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] + [clojure.string :as str] [compojure.api.sweet :refer :all] + [config.core :refer [env]] [ring.util.response :refer [redirect]] [schema.core :as s] [korma.core :as sql] [mtg-pairings-server.sql-db :as db] - [mtg-pairings-server.util.sql :as sql-util] - [clojure.string :as str]) + [mtg-pairings-server.util.sql :as sql-util]) (:import (java.security MessageDigest))) (defonce auth-backend (session-backend)) @@ -42,4 +43,7 @@ (let [new-session (assoc (:session request) :identity user)] (-> (redirect next :see-other) (assoc :session new-session))) - (redirect "/decklist/organizer" :see-other)))) + (let [path (if (str/blank? (env :decklist-prefix)) + "/decklist/organizer" + "/organizer")] + (redirect path :see-other))))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index adb410d..93b7d52 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -48,91 +48,101 @@ :headers {"Content-Type" "text/html" "Cache-Control" "no-cache"}}))) +(let [index (partial index :pairings-js)] + (defroutes pairings-routes + (GET "/" [] + (index)) + (GET "/tournaments" [] + (index {:page {:page :tournaments}})) + (GET "/tournaments/:id" [] + :path-params [id :- s/Int] + (index {:page {:page :tournament, :id id} + :tournaments {id (tournament/client-tournament id)}})) + (GET "/tournaments/:id/pairings-:round" [] + :path-params [id :- s/Int + round :- s/Int] + (index {:page {:page :pairings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pairings {id {round (tournament/get-round id round)}}})) + (GET "/tournaments/:id/standings-:round" [] + :path-params [id :- s/Int + round :- s/Int] + (index {:page {:page :standings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :standings {id {round (tournament/standings id round false)}}})) + (GET "/tournaments/:id/pods-:round" [] + :path-params [id :- s/Int + round :- s/Int] + (index {:page {:page :pods, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pods {id {round (tournament/pods id round)}}})) + (GET "/tournaments/:id/seatings" [] + :path-params [id :- s/Int] + (index {:page {:page :seatings, :id id} + :tournaments {id (tournament/client-tournament id)} + :seatings {id (tournament/seatings id)}})) + (GET "/tournaments/:id/bracket" [] + :path-params [id :- s/Int] + (index {:page {:page :bracket, :id id} + :tournaments {id (tournament/client-tournament id)} + :bracket {id (tournament/bracket id)}})) + (GET "/tournaments/:id/organizer" [] (index :pairings-js)) + (GET "/tournaments/:id/organizer/menu" [] (index :pairings-js)) + (GET "/tournaments/:id/organizer/deck-construction" [] (index :pairings-js)))) + +(defmacro validate-request [user-id tournament & body] + `(if (and ~user-id (not= ~user-id (:user ~tournament))) + {:status 403 + :body "403 Forbidden"} + (do ~@body))) + +(let [index (partial index :decklist-js)] + (defroutes decklist-routes + (GET "/decklist/tournament/:id" [] + :path-params [id :- s/Str] + (index {:page {:page :decklist-submit} + :decklist-editor {:tournament (decklist/get-tournament id)}})) + (GET "/decklist/organizer" request + (index {:page {:page :decklist-organizer} + :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) + (GET "/decklist/organizer/new" [] + (index {:page {:page :decklist-organizer-tournament}})) + (GET "/decklist/organizer/view/:id" request + :path-params [id :- s/Str] + (let [decklist (decklist/get-decklist id) + tournament (decklist/get-organizer-tournament (:tournament decklist)) + user-id (get-in request [:session :identity :id])] + (validate-request user-id tournament + (index {:page {:page :decklist-organizer-view, :id id} + :decklist-editor (when user-id + {:decklist decklist + :organizer-tournament tournament})})))) + (GET "/decklist/organizer/:id" request + :path-params [id :- s/Str] + (let [tournament (decklist/get-organizer-tournament id) + user-id (get-in request [:session :identity :id])] + (validate-request user-id tournament + (index {:page {:page :decklist-organizer-tournament, :id id} + :decklist-editor (when user-id + {:organizer-tournament tournament})})))) + (GET "/decklist/:id" [] + :path-params [id :- s/Str] + (let [decklist (decklist/get-decklist id)] + (index {:page {:page :decklist-submit, :id id} + :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) + :decklist decklist}}))))) + (def robots-txt {:status 200 :body "User-agent: *\nDisallow: /\n" :headers {"Content-Type" "text/plain"}}) -(def forbidden {:status 403 - :body "403 Forbidden"}) - (defroutes site-routes - (GET "/" [] (index :pairings-js)) - (GET "/tournaments" [] - (index :pairings-js {:page {:page :tournaments}})) - (GET "/tournaments/:id" [] - :path-params [id :- s/Int] - (index :pairings-js {:page {:page :tournament, :id id} - :tournaments {id (tournament/client-tournament id)}})) - (GET "/tournaments/:id/pairings-:round" [] - :path-params [id :- s/Int - round :- s/Int] - (index :pairings-js {:page {:page :pairings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pairings {id {round (tournament/get-round id round)}}})) - (GET "/tournaments/:id/standings-:round" [] - :path-params [id :- s/Int - round :- s/Int] - (index :pairings-js {:page {:page :standings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :standings {id {round (tournament/standings id round false)}}})) - (GET "/tournaments/:id/pods-:round" [] - :path-params [id :- s/Int - round :- s/Int] - (index :pairings-js {:page {:page :pods, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pods {id {round (tournament/pods id round)}}})) - (GET "/tournaments/:id/seatings" [] - :path-params [id :- s/Int] - (index :pairings-js {:page {:page :seatings, :id id} - :tournaments {id (tournament/client-tournament id)} - :seatings {id (tournament/seatings id)}})) - (GET "/tournaments/:id/bracket" [] - :path-params [id :- s/Int] - (index :pairings-js {:page {:page :bracket, :id id} - :tournaments {id (tournament/client-tournament id)} - :bracket {id (tournament/bracket id)}})) - (GET "/tournaments/:id/organizer" [] (index :pairings-js)) - (GET "/tournaments/:id/organizer/menu" [] (index :pairings-js)) - (GET "/tournaments/:id/organizer/deck-construction" [] (index :pairings-js)) - (GET "/decklist/tournament/:id" [] - :path-params [id :- s/Str] - (index :decklist-js {:page {:page :decklist-submit} - :decklist-editor {:tournament (decklist/get-tournament id)}})) - (GET "/decklist/organizer" request - (index :decklist-js {:page {:page :decklist-organizer} - :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) - (GET "/decklist/organizer/new" [] - (index :decklist-js {:page {:page :decklist-organizer-tournament}})) - (GET "/decklist/organizer/view/:id" request - :path-params [id :- s/Str] - (let [decklist (decklist/get-decklist id) - tournament (decklist/get-organizer-tournament (:tournament decklist)) - user-id (get-in request [:session :identity :id])] - (cond - (not user-id) (index :decklist-js {:page {:page :decklist-organizer-view, :id id}}) - (= user-id (:user tournament)) (index :decklist-js {:page {:page :decklist-organizer-view, :id id} - :decklist-editor {:decklist decklist - :organizer-tournament tournament}}) - :else forbidden))) - (GET "/decklist/organizer/:id" request - :path-params [id :- s/Str] - (let [tournament (decklist/get-organizer-tournament id) - user-id (get-in request [:session :identity :id])] - (cond - (not user-id) (index :decklist-js {:page {:page :decklist-organizer-tournament, :id id}}) - (= user-id (:user tournament)) (index :decklist-js {:page {:page :decklist-organizer-tournament, :id id} - :decklist-editor {:organizer-tournament tournament}}) - :else forbidden))) - (GET "/decklist/:id" [] - :path-params [id :- s/Str] - (let [decklist (decklist/get-decklist id)] - (index :decklist-js {:page {:page :decklist-submit, :id id} - :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) - :decklist decklist}}))) - (GET "/robots.txt" [] robots-txt) + decklist-routes + pairings-routes auth-routes - ws/routes) + ws/routes + (GET "/robots.txt" [] robots-txt)) (defroutes app-routes (context "/api" [] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj index b9414a6..7794ab8 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj @@ -3,6 +3,7 @@ [config.core :refer [env]] [mtg-pairings-server.auth :refer [wrap-auth]] [mtg-pairings-server.middleware.cache-control :refer [wrap-cache-control]] + [mtg-pairings-server.middleware.decklist :refer [wrap-decklist-prefix]] [mtg-pairings-server.middleware.error :refer [wrap-errors]])) (defn add-dev-middleware [handler] @@ -15,6 +16,7 @@ (defn add-prod-middleware [handler] (-> handler + wrap-decklist-prefix (wrap-cache-control {#"\.(css|js|txt)$" "max-age=31536000"}) wrap-errors)) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/decklist.clj new file mode 100644 index 0000000..696b4b8 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/decklist.clj @@ -0,0 +1,24 @@ +(ns mtg-pairings-server.middleware.decklist + (:require [config.core :refer [env]])) + +(def rewrite-blacklist [#"^.*\.(ico|png|jpg|js|css|woff2|txt|map)$" + #"^/chsk$" + #"^robots.txt$" + #"^/login$"]) + +(defn rewrite? [uri] + (not-any? #(re-matches % uri) rewrite-blacklist)) + +(defn rewrite [uri] + (let [prefix (env :decklist-prefix)] + (cond + (nil? prefix) uri + (= "/" uri) prefix + :else (str prefix uri)))) + +(defn wrap-decklist-prefix [handler] + (fn [{:keys [uri headers] :as request}] + (if (and (= (env :decklist-host) (get headers "host")) + (rewrite? uri)) + (handler (update request :uri rewrite)) + (handler request)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj index fd4c5be..71eeeb2 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware/log.clj @@ -16,6 +16,7 @@ uri (:uri request)] (when (logged? uri) (log/info (get-in request [:headers "x-forwarded-for"] (:remote-addr request)) + (get-in request [:headers "host"]) (-> request :request-method name str/upper-case) uri (:status response))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 3b3e922..9a2f725 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -14,7 +14,8 @@ [ring.util.response :as ring]) #?@(:cljs [[goog.string :as gstring] - [goog.string.format]]) + [goog.string.format] + [oops.core :refer [oget]]]) [clojure.string :as str])) (defn map-values @@ -156,3 +157,9 @@ (recur ( db (assoc-in [:decklist-editor :saving] false) (assoc-in [:decklist-editor :saved] true)) - :navigate (str "/decklist/" id)})) + :navigate (routes/old-decklist-path {:id id})})) (reg-event-fx ::load-organizer-tournament (fn [_ [_ id]] @@ -63,7 +64,7 @@ (assoc-in [:decklist-editor :organizer-tournament :id] id) (assoc-in [:decklist-editor :saving] false) (assoc-in [:decklist-editor :saved] true)) - :navigate (str "/decklist/organizer/" id)})) + :navigate (routes/organizer-tournament-path {:id id})})) (reg-event-fx ::load-decklist (fn [_ [_ id]] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs index 69ffe33..9ed167e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs @@ -1,32 +1,39 @@ (ns mtg-pairings-server.routes.decklist (:require [re-frame.core :refer [dispatch]] [secretary.core :as secretary :include-macros true] - [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.common :refer [dispatch-page]])) +(declare organizer-path + organizer-print-path + organizer-view-path + organizer-new-tournament-path + organizer-tournament-path + new-decklist-path + old-decklist-path) + (let [initialized? (atom false)] (defn initialize-routes [prefix] (when-not @initialized? - (secretary/defroute decklist-organizer-path (str prefix "/organizer") [] + (secretary/defroute organizer-path (str prefix "/organizer") [] (dispatch-page :decklist-organizer)) - (secretary/defroute decklist-organizer-print-path (str prefix "/organizer/print") [] + (secretary/defroute organizer-print-path (str prefix "/organizer/print") [] (dispatch-page :decklist-organizer-view)) - (secretary/defroute decklist-organizer-view-path (str prefix "/organizer/view/:id") [id] + (secretary/defroute organizer-view-path (str prefix "/organizer/view/:id") [id] (dispatch-page :decklist-organizer-view id)) - (secretary/defroute decklist-organizer-new-tournament-path (str prefix "/organizer/new") [] - (dispatch [::events/clear-tournament]) + (secretary/defroute organizer-new-tournament-path (str prefix "/organizer/new") [] + (dispatch [:mtg-pairings-server.events.decklist/clear-tournament]) (dispatch-page :decklist-organizer-tournament)) - (secretary/defroute decklist-organizer-tournament-path (str prefix "/organizer/:id") [id] + (secretary/defroute organizer-tournament-path (str prefix "/organizer/:id") [id] (dispatch-page :decklist-organizer-tournament id)) - (secretary/defroute new-decklist-submit-path (str prefix "/tournament/:id") [id] + (secretary/defroute new-decklist-path (str prefix "/tournament/:id") [id] (dispatch-page :decklist-submit)) - (secretary/defroute old-decklist-submit-path (str prefix "/:id") [id] + (secretary/defroute old-decklist-path (str prefix "/:id") [id] (dispatch-page :decklist-submit id)) (reset! initialized? true)))) From d4003844e6c432351eb366b2444c852233288a9a Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 10:16:11 +0300 Subject: [PATCH 26/44] Logout for decklist organizer --- .../src/clj/mtg_pairings_server/auth.clj | 14 ++++++++++---- .../components/decklist/organizer.cljs | 3 +++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 850c723..7e5e0a0 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -33,6 +33,11 @@ {:id (:id user) :username username})))) +(defn organizer-path [] + (if (str/blank? (env :decklist-prefix)) + "/decklist/organizer" + "/organizer")) + (defroutes auth-routes (POST "/login" request :form-params [username :- s/Str @@ -43,7 +48,8 @@ (let [new-session (assoc (:session request) :identity user)] (-> (redirect next :see-other) (assoc :session new-session))) - (let [path (if (str/blank? (env :decklist-prefix)) - "/decklist/organizer" - "/organizer")] - (redirect path :see-other))))) + (redirect (organizer-path) :see-other))) + (GET "/logout" request + (let [new-session (dissoc (:session request) :identity)] + (-> (redirect (organizer-path) :see-other) + (assoc :session new-session))))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index c85bffe..7e288df 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -60,6 +60,9 @@ :width "30px" :padding "6px 0 6px 6px" :vertical-align :top}}])}] + [ui/raised-button {:href "/logout" + :label "Kirjaudu ulos" + :style {:margin-left "12px"}}] [ui/table {:selectable false :class-name :tournaments} [ui/table-header {:display-select-all false From b5324398683cb4ca457c3e8ff7713121c439ee9b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 10:31:25 +0300 Subject: [PATCH 27/44] Identify decklist cards by id rather than name --- .../src/clj/mtg_pairings_server/handler.clj | 3 +- .../mtg_pairings_server/util/decklist.cljc | 15 +++++++ .../components/decklist/submit.cljs | 41 +++++++++---------- .../mtg_pairings_server/events/decklist.cljs | 22 +++++----- 4 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 93b7d52..28c23cd 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -15,6 +15,7 @@ [mtg-pairings-server.service.decklist :as decklist] [mtg-pairings-server.service.tournament :as tournament] [mtg-pairings-server.transit :as transit] + [mtg-pairings-server.util.decklist :refer [add-id-to-card add-id-to-cards]] [mtg-pairings-server.websocket :as ws])) (defn escape-quotes [s] @@ -127,7 +128,7 @@ {:organizer-tournament tournament})})))) (GET "/decklist/:id" [] :path-params [id :- s/Str] - (let [decklist (decklist/get-decklist id)] + (let [decklist (add-id-to-cards "server-card__" (decklist/get-decklist id))] (index {:page {:page :decklist-submit, :id id} :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) :decklist decklist}}))))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc new file mode 100644 index 0000000..9dcad45 --- /dev/null +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc @@ -0,0 +1,15 @@ +(ns mtg-pairings-server.util.decklist) + +(defn add-id-to-card + ([card] + (add-id-to-card "card__" card)) + ([prefix card] + (assoc card :id (gensym prefix)))) + +(defn add-id-to-cards + ([decklist] + (add-id-to-cards "card__" decklist)) + ([prefix decklist] + (-> decklist + (update :main #(mapv (partial add-id-to-card prefix) %)) + (update :side #(mapv (partial add-id-to-card prefix) %))))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 7be99a0..3220356 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -90,30 +90,29 @@ (defn decklist-table-row [board card error] (let [on-change (fn [_ _ quantity] - (dispatch [::events/set-quantity board (:name card) quantity])) - on-delete #(dispatch [::events/remove-card board (:name card)])] + (dispatch [::events/set-quantity board (:id card) quantity])) + on-delete #(dispatch [::events/remove-card board (:id card)])] (fn decklist-table-row-render [_ {:keys [name quantity]} error] [ui/table-row [ui/table-row-column {:class-name :quantity :style {:padding "0 12px"}} (when quantity - [ui/select-field {:value quantity - :on-change on-change - :style {:width "48px" - :vertical-align :top} - :menu-style {:width "48px"} - :icon-style {:padding-left 0 - :padding-right 0 - :width "24px" - :fill "rgba(0, 0, 0, 0.54)"} - :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}} - (for [i (range 1 (if (basic? name) - 31 - 5))] - ^{:key (str name "--quantity--" i)} - [ui/menu-item {:value i - :primary-text i - :inner-div-style {:padding "0 6px"}}])])] + (into [ui/select-field {:value quantity + :on-change on-change + :style {:width "48px" + :vertical-align :top} + :menu-style {:width "48px"} + :icon-style {:padding-left 0 + :padding-right 0 + :width "24px" + :fill "rgba(0, 0, 0, 0.54)"} + :underline-style {:border-color "rgba(0, 0, 0, 0.24)"}}] + (for [i (range 1 (if (basic? name) + 31 + 5))] + [ui/menu-item {:value i + :primary-text i + :inner-div-style {:padding "0 6px"}}])))] [ui/table-row-column {:class-name :card :style {:font-size "14px"}} name] @@ -156,8 +155,8 @@ [ui/table-header-column {:class-name :actions}] [ui/table-header-column {:class-name :error}]]] [ui/table-body - (for [{:keys [name error] :as card} (get @decklist board)] - ^{:key (str name "--" board "--tr")} + (for [{:keys [id name error] :as card} (get @decklist board)] + ^{:key (str id "--tr")} [decklist-table-row board card (or error (get error-cards name))])]]])))) (defn player-info [decklist] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 07ff335..fddc829 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -4,6 +4,7 @@ [mtg-pairings-server.routes.decklist :as routes] [mtg-pairings-server.transit :as transit] [mtg-pairings-server.util :as util] + [mtg-pairings-server.util.decklist :refer [add-id-to-card add-id-to-cards]] [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) @@ -76,7 +77,8 @@ (reg-event-db :server/decklist (fn [db [_ decklist]] - (assoc-in db [:decklist-editor :decklist] (merge empty-decklist decklist)))) + (assoc-in db [:decklist-editor :decklist] (-> (merge empty-decklist decklist) + (add-id-to-cards))))) (reg-event-db :server/decklists (fn [db [_ decklists]] @@ -101,35 +103,35 @@ (if (some #(= name (:name %)) (get decklist board)) decklist (-> decklist - (update board conj {:name name, :quantity 1}) + (update board conj (add-id-to-card {:name name, :quantity 1})) (update-in [:count board] inc)))) (reg-event-db ::add-card (fn [db [_ name]] (update-in db [:decklist-editor :decklist] add-card name))) -(defn ^:private set-quantity [decklist board name quantity] - (let [index (util/index-where #(= name (:name %)) (get decklist board)) +(defn ^:private set-quantity [decklist board id quantity] + (let [index (util/index-where #(= id (:id %)) (get decklist board)) orig-quantity (get-in decklist [board index :quantity])] (-> decklist (assoc-in [board index :quantity] quantity) (update-in [:count board] + (- quantity orig-quantity))))) (reg-event-db ::set-quantity - (fn [db [_ board name quantity]] - (update-in db [:decklist-editor :decklist] set-quantity board name quantity))) + (fn [db [_ board id quantity]] + (update-in db [:decklist-editor :decklist] set-quantity board id quantity))) -(defn ^:private remove-card [decklist board name] +(defn ^:private remove-card [decklist board id] (let [cards (get decklist board) - index (util/index-where #(= name (:name %)) cards) + index (util/index-where #(= id (:id %)) cards) n (get-in decklist [board index :quantity])] (-> decklist (assoc board (util/dissoc-index cards index)) (update-in [:count board] - n)))) (reg-event-db ::remove-card - (fn [db [_ board name]] - (update-in db [:decklist-editor :decklist] remove-card board name))) + (fn [db [_ board id]] + (update-in db [:decklist-editor :decklist] remove-card board id))) (reg-event-db ::select-board (fn [db [_ board]] From ed381eb11793617fbadb6bff405cd81236d05733 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 11:53:27 +0300 Subject: [PATCH 28/44] Send decklist by email --- .../src/clj/mtg_pairings_server/events.clj | 10 ++- .../src/clj/mtg_pairings_server/handler.clj | 5 +- .../mtg_pairings_server/service/decklist.clj | 68 ++++++++++--------- .../clj/mtg_pairings_server/service/email.clj | 51 ++++++++++++++ .../mtg_pairings_server/styles/decklist.clj | 2 + .../mtg_pairings_server/util/decklist.cljc | 3 + .../components/decklist/submit.cljs | 5 +- 7 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj index f3cf0f1..7684413 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -1,12 +1,12 @@ (ns mtg-pairings-server.events (:require [taoensso.timbre :as log] [mtg-pairings-server.service.decklist :as decklist] + [mtg-pairings-server.service.email :as email] [mtg-pairings-server.service.player :as player] [mtg-pairings-server.service.tournament :as tournament] [mtg-pairings-server.util.broadcast :as broadcast] [mtg-pairings-server.websocket :as ws])) - (defmethod ws/event-handler :chsk/uidport-open [_]) @@ -97,8 +97,12 @@ (defmethod ws/event-handler :client/save-decklist [{uid :uid, [tournament decklist] :?data}] - (let [id (decklist/save-decklist tournament decklist)] - (ws/send! uid [:server/decklist-saved id]))) + (let [{:keys [id send-email?]} (decklist/save-decklist tournament decklist)] + (ws/send! uid [:server/decklist-saved id]) + (when send-email? + (let [tournament (decklist/get-tournament (:tournament decklist)) + {:keys [subject text]} (email/generate-message tournament (assoc decklist :id id))] + (email/send-email (get-in decklist [:player :email]) subject text))))) (defmethod ws/event-handler :client/decklist-organizer-tournament [{uid :uid, id :?data, ring-req :ring-req}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 28c23cd..10c6216 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -7,8 +7,9 @@ [schema.core :as s] [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] [ring.middleware.jsonp :refer [wrap-json-with-padding]] + [ring.util.response :refer [redirect]] [mtg-pairings-server.api.http :as http-api] - [mtg-pairings-server.auth :refer [auth-routes]] + [mtg-pairings-server.auth :as auth :refer [auth-routes]] [mtg-pairings-server.middleware :refer [wrap-site-middleware]] [mtg-pairings-server.middleware.cors :refer [wrap-allow-origin]] [mtg-pairings-server.middleware.log :refer [wrap-request-log]] @@ -99,6 +100,8 @@ (let [index (partial index :decklist-js)] (defroutes decklist-routes + (GET "/decklist" [] + (redirect (auth/organizer-path))) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] (index {:page {:page :decklist-submit} diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 2a3ad85..7caf352 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -37,9 +37,41 @@ :index index :quantity quantity}) +(defn get-tournament [id] + (sql-util/select-unique db/decklist-tournament + (sql/where {:id id}))) + +(defn format-cards [cards maindeck?] + (->> cards + ((if maindeck? filter remove) :maindeck) + (sort-by :index) + (mapv #(select-keys % [:name :quantity])))) + +(defn get-decklist [id] + (let [decklist (sql-util/select-unique db/decklist + (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) + (sql/with db/decklist-card + (sql/fields :maindeck :index :quantity) + (sql/with db/card + (sql/fields :name))) + (sql/where {:id id})) + main (format-cards (:decklist_card decklist) true) + side (format-cards (:decklist_card decklist) false) + player (select-keys decklist [:deck-name :first-name :last-name :email :dci]) + email-disabled? (not (str/blank? (:email player)))] + {:id id + :tournament (:tournament decklist) + :main main + :side side + :count {:main (transduce (map :quantity) + 0 main) + :side (transduce (map :quantity) + 0 side)} + :board :main + :player (assoc player :email-disabled? email-disabled?)})) + (defn save-decklist [tournament decklist] (transaction (let [old-id (:id decklist) + old-decklist (some-> old-id (get-decklist)) new-id (sql-util/generate-id) card->id (generate-card->id (map :name (concat (:main decklist) @@ -68,37 +100,9 @@ (format-card (or old-id new-id) true (card->id name) index quantity)) (for [[index {:keys [name quantity]}] (indexed (:side decklist))] (format-card (or old-id new-id) false (card->id name) index quantity))))) - (or old-id new-id)))) - -(defn get-tournament [id] - (sql-util/select-unique db/decklist-tournament - (sql/where {:id id}))) - -(defn format-cards [cards maindeck?] - (->> cards - ((if maindeck? filter remove) :maindeck) - (sort-by :index) - (mapv #(select-keys % [:name :quantity])))) - -(defn get-decklist [id] - (let [decklist (sql-util/select-unique db/decklist - (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) - (sql/with db/decklist-card - (sql/fields :maindeck :index :quantity) - (sql/with db/card - (sql/fields :name))) - (sql/where {:id id})) - main (format-cards (:decklist_card decklist) true) - side (format-cards (:decklist_card decklist) false) - player (select-keys decklist [:deck-name :first-name :last-name :email :dci])] - {:id id - :tournament (:tournament decklist) - :main main - :side side - :count {:main (transduce (map :quantity) + 0 main) - :side (transduce (map :quantity) + 0 side)} - :board :main - :player player})) + {:id (or old-id new-id) + :send-email? (and (not (str/blank? (get-in decklist [:player :email]))) + (str/blank? (get-in old-decklist [:player :email])))}))) (defn get-organizer-tournaments [user-id] (when user-id @@ -153,7 +157,7 @@ :quantity (Long/parseLong quantity)} {:name name :error "Ei sallittu tässä formaatissa"}) - {:name name + {:name name :error "Korttia ei löydy"}) {:name row :error "Virheellinen rivi"}))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj new file mode 100644 index 0000000..1806497 --- /dev/null +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj @@ -0,0 +1,51 @@ +(ns mtg-pairings-server.service.email + (:require [aleph.http :as http] + [byte-streams :as bs] + [config.core :refer [env]] + [cheshire.core :as json] + [clojure.string :as str] + [taoensso.timbre :as log] + [mtg-pairings-server.util :refer [format-date]] + [mtg-pairings-server.util.decklist :refer [decklist-url]]) + (:import (clojure.lang ExceptionInfo))) + +(def mailgun-endpoint "https://api.eu.mailgun.net/v3/m.pairings.fi/messages") + +(defn send-email [to subject text] + (try + @(http/post mailgun-endpoint + {:basic-auth ["api" (env :mailgun-api-key)] + :form-params {:from "Pairings.fi " + :to to + :subject subject + :text text}}) + (catch ExceptionInfo ex + (let [response (-> (ex-data ex) + (:body) + (bs/to-string) + (json/parse-string true))] + (log/error "Error sending email" response))))) + +(defn generate-message [tournament decklist] + (let [{:keys [id player main side]} decklist + {:keys [dci first-name last-name deck-name]} player + {:keys [name date]} tournament] + {:subject (str "Pakkalistasi turnaukseen " name) + :text (str "Pakkalistasi turnaukseen " name + " on tallennettu. Pääset muokkaamaan pakkalistaasi osoitteessa " + (decklist-url id) " . " + "Huomaathan, että linkin avulla kuka tahansa pääsee katsomaan ja muokkaamaan listaasi. " + "Älä siis päästä linkkiä vääriin käsiin.\n\n" + "Turnaus: " name "\n" + "Päivämäärä: " (format-date date) "\n" + "Pelaaja: " first-name " " last-name " (" dci ")\n" + (when-not (str/blank? deck-name) + (str "Pakka: " deck-name "\n")) + "\nMaindeck (" (get-in decklist [:count :main]) ")\n" + (str/join "\n" (for [{:keys [name quantity]} main] + (format "%2d %s" quantity name))) + "\n\nSideboard (" (get-in decklist [:count :side]) ")\n" + (str/join "\n" (for [{:keys [name quantity]} side] + (format "%2d %s" quantity name))) + "\n\n" + "Tämä viesti on lähetetty automaattisesti eikä siihen voi vastata.\n")})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index dab77b1..084d039 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -300,6 +300,8 @@ {:width (px 400)}] [:.decklist-link table-row-link]]] + [:#decklist-organizer-login + {:margin "12px 24px"}] organizer-decklist) (defstyles styles diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc index 9dcad45..2442367 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc @@ -13,3 +13,6 @@ (-> decklist (update :main #(mapv (partial add-id-to-card prefix) %)) (update :side #(mapv (partial add-id-to-card prefix) %))))) + +(defn decklist-url [id] + (str "https://decklist.pairings.fi/" id)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 3220356..f480b4e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -205,7 +205,10 @@ :floating-label-text "Sähköposti" :full-width true :value (get-in @decklist [:player :email]) - :style {:vertical-align :top}}]]]))) + :style {:vertical-align :top} + :disabled (get-in @decklist [:player :email-disabled?]) + :title (when (get-in @decklist [:player :email-disabled?]) + "Tästä pakkalistasta on jo lähetetty sähköpostiviesti.")}]]]))) (defn decklist-import [] (let [mobile? (subscribe [::common-subs/mobile?]) From c4fb7297bca0309d4abf807cca7d4d22d1205c96 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 13:43:41 +0300 Subject: [PATCH 29/44] Add error handling and fix styles --- .../src/clj/mtg_pairings_server/events.clj | 28 ++++++++++------ .../mtg_pairings_server/service/decklist.clj | 5 +-- .../clj/mtg_pairings_server/service/email.clj | 14 +++----- .../clj/mtg_pairings_server/styles/common.clj | 15 --------- .../mtg_pairings_server/styles/decklist.clj | 2 ++ .../clj/mtg_pairings_server/styles/main.clj | 2 +- .../mtg_pairings_server/styles/common.cljc | 26 +++++++++++++++ .../mtg_pairings_server/util/decklist.cljc | 19 ++++++++++- .../components/decklist/organizer.cljs | 30 ++++++++++++++++- .../components/decklist/submit.cljs | 25 +++++++++----- .../mtg_pairings_server/events/decklist.cljs | 33 ++++++++++++------- .../subscriptions/decklist.cljs | 4 +++ .../mtg_pairings_server/util/material_ui.cljs | 11 ++----- 13 files changed, 147 insertions(+), 67 deletions(-) delete mode 100644 mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj create mode 100644 mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj index 7684413..f9f65f8 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -97,12 +97,16 @@ (defmethod ws/event-handler :client/save-decklist [{uid :uid, [tournament decklist] :?data}] - (let [{:keys [id send-email?]} (decklist/save-decklist tournament decklist)] - (ws/send! uid [:server/decklist-saved id]) - (when send-email? - (let [tournament (decklist/get-tournament (:tournament decklist)) - {:keys [subject text]} (email/generate-message tournament (assoc decklist :id id))] - (email/send-email (get-in decklist [:player :email]) subject text))))) + (try + (let [{:keys [id send-email?]} (decklist/save-decklist tournament decklist)] + (ws/send! uid [:server/decklist-saved id]) + (when send-email? + (let [tournament (decklist/get-tournament (:tournament decklist)) + {:keys [subject text]} (email/generate-message tournament (assoc decklist :id id))] + (email/send-email (get-in decklist [:player :email]) subject text)))) + (catch Exception e + (log/error e "Error saving decklist") + (ws/send! uid [:server/decklist-error])))) (defmethod ws/event-handler :client/decklist-organizer-tournament [{uid :uid, id :?data, ring-req :ring-req}] @@ -112,10 +116,14 @@ (defmethod ws/event-handler :client/save-decklist-organizer-tournament [{uid :uid, tournament :?data, ring-req :ring-req}] - (let [user-id (get-in ring-req [:session :identity :id]) - id (decklist/save-organizer-tournament user-id tournament)] - (when id - (ws/send! uid [:server/organizer-tournament-saved id])))) + (try + (let [user-id (get-in ring-req [:session :identity :id]) + id (decklist/save-organizer-tournament user-id tournament)] + (when id + (ws/send! uid [:server/organizer-tournament-saved id]))) + (catch Exception e + (log/error e "Error saving tournament") + (ws/send! uid [:server/decklist-error])))) (defmethod ws/event-handler :client/load-decklist [{uid :uid, id :?data}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 7caf352..f512aac 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -147,7 +147,8 @@ old-id)))) (defn parse-decklist-row [row format] - (when-not (str/blank? row) + (when-not (or (str/blank? row) + (re-matches #"^Maindeck.*" row)) (if-let [[_ quantity name] (re-matches #"(\d+)\s+(.+)" (str/trim row))] (if-let [{:keys [name legal]} (sql-util/select-unique-or-nil db/card (sql/fields :name [format :legal]) @@ -164,7 +165,7 @@ (defn load-text-decklist [text-decklist format] {:pre [(contains? #{:standard :modern :legacy} format)]} - (let [[maindeck sideboard] (str/split text-decklist #"[Ss]ideboard\s*") + (let [[maindeck sideboard] (str/split text-decklist #"[Ss]ideboard( \(\d*\))?\s*") maindeck-cards (keep #(parse-decklist-row % format) (str/split-lines maindeck)) sideboard-cards (when sideboard (keep #(parse-decklist-row % format) (str/split-lines sideboard)))] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj index 1806497..72af7c4 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/email.clj @@ -6,7 +6,7 @@ [clojure.string :as str] [taoensso.timbre :as log] [mtg-pairings-server.util :refer [format-date]] - [mtg-pairings-server.util.decklist :refer [decklist-url]]) + [mtg-pairings-server.util.decklist :refer [decklist-url ->text]]) (:import (clojure.lang ExceptionInfo))) (def mailgun-endpoint "https://api.eu.mailgun.net/v3/m.pairings.fi/messages") @@ -27,7 +27,7 @@ (log/error "Error sending email" response))))) (defn generate-message [tournament decklist] - (let [{:keys [id player main side]} decklist + (let [{:keys [id player]} decklist {:keys [dci first-name last-name deck-name]} player {:keys [name date]} tournament] {:subject (str "Pakkalistasi turnaukseen " name) @@ -41,11 +41,7 @@ "Pelaaja: " first-name " " last-name " (" dci ")\n" (when-not (str/blank? deck-name) (str "Pakka: " deck-name "\n")) - "\nMaindeck (" (get-in decklist [:count :main]) ")\n" - (str/join "\n" (for [{:keys [name quantity]} main] - (format "%2d %s" quantity name))) - "\n\nSideboard (" (get-in decklist [:count :side]) ")\n" - (str/join "\n" (for [{:keys [name quantity]} side] - (format "%2d %s" quantity name))) - "\n\n" + "\n" + (->text decklist) + "\n" "Tämä viesti on lähetetty automaattisesti eikä siihen voi vastata.\n")})) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj deleted file mode 100644 index f4f54e2..0000000 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/common.clj +++ /dev/null @@ -1,15 +0,0 @@ -(ns mtg-pairings-server.styles.common - (:require [garden.color :refer [hex->rgb rgba]])) - -(def ellipsis-overflow {:white-space :nowrap - :text-overflow :ellipsis - :overflow :hidden - :vertical-align :bottom}) - -(def color {:turquoise (hex->rgb "337ab7") - :light-blue (hex->rgb "d0e9fc") - :light-grey (hex->rgb "e6e6e6") - :grey (hex->rgb "999999") - :dark-grey (hex->rgb "333333") - :light-green (hex->rgb "ccffcc") - :transparent-grey (rgba 0 0 0 0.7)}) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 084d039..8a89f7f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -293,6 +293,8 @@ {:display :inline-block :vertical-align :top :margin-right "24px"}] + [:.notices + {:display :inline-block}] [:.decklists [:th.dci :td.dci {:width (px 150)}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj index 7cc80bd..2396cb4 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj @@ -32,7 +32,7 @@ :line-height 1.1}] [:a {:text-decoration :none - :color (color :turquoise)}] + :color (color :primary2-color)}] [:#main-container {:margin-bottom (px 10)}]) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc new file mode 100644 index 0000000..f5755ac --- /dev/null +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc @@ -0,0 +1,26 @@ +(ns mtg-pairings-server.styles.common + (:require [garden.color :refer [hex->rgb rgba rgb->hex]])) + +(def ellipsis-overflow {:white-space :nowrap + :text-overflow :ellipsis + :overflow :hidden + :vertical-align :bottom}) + +(def color {:light-blue (hex->rgb "d0e9fc") + :light-grey (hex->rgb "e6e6e6") + :grey (hex->rgb "999999") + :dark-grey (hex->rgb "333333") + :light-green (hex->rgb "ccffcc") + :transparent-grey (rgba 0 0 0 0.7) + :primary1-color (hex->rgb "90caf9") + :primary2-color (hex->rgb "5d99c6") + :primary3-color (hex->rgb "c3fdff") + :accent1-color (hex->rgb "ec407a") + :accent2-color (hex->rgb "b4004e") + :accent3-color (hex->rgb "ff77a9") + :error-color (hex->rgb "f44336") + :picker-header-color (hex->rgb "4ba3c7")}) + +(def palette (into {} (for [key [:primary1-color :primary2-color :primary3-color :accent1-color + :accent2-color :accent3-color :error-color :picker-header-color]] + [key (rgb->hex (color key))]))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc index 2442367..8e4391a 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc @@ -1,4 +1,5 @@ -(ns mtg-pairings-server.util.decklist) +(ns mtg-pairings-server.util.decklist + (:require [clojure.string :as str])) (defn add-id-to-card ([card] @@ -16,3 +17,19 @@ (defn decklist-url [id] (str "https://decklist.pairings.fi/" id)) + +(defn pad [n] + (if (< n 10) + (str " " n) + (str n))) + +(defn ->text [decklist] + (let [{:keys [main side]} decklist] + (str "Maindeck (" (get-in decklist [:count :main]) ")\n" + (str/join "\n" (for [{:keys [name quantity]} main] + (str (pad quantity) " " name))) + "\n\nSideboard (" (get-in decklist [:count :side]) ")\n" + (str/join "\n" (for [{:keys [name quantity]} side] + (str (pad quantity) " " name))) + "\n"))) + diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index 7e288df..0cd9363 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -9,6 +9,7 @@ [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.decklist :as routes] [mtg-pairings-server.subscriptions.decklist :as subs] + [mtg-pairings-server.styles.common :refer [palette]] [mtg-pairings-server.util :refer [format-date format-date-time to-local-date indexed get-host]] [mtg-pairings-server.util.material-ui :refer [text-field]])) @@ -126,6 +127,30 @@ [:a.decklist-link link-props (format-date-time (:submitted decklist))]]])]]) +(defn notice [type text] + (let [[color background] (case type + :success [:black (:primary1-color palette)] + :error [:white (:error-color palette)]) + on-delete #(dispatch [::events/clear-status (case type + :success :saved + :error :error)])] + (fn notice-render [type text] + [ui/chip + {:background-color background + :label-color color + :on-request-delete on-delete} + text]))) + +(defn notices [] + (let [saved? (subscribe [::subs/saved?]) + error? (subscribe [::subs/error?])] + (fn notices-render [] + [:div.notices + (when @saved? + [notice :success "Tallennus onnistui"]) + (when @error? + [notice :error "Tallennus epäonnistui"])]))) + (defn tournament [id] (let [saved-tournament (subscribe [::subs/organizer-tournament]) saving? (subscribe [::subs/saving?]) @@ -198,11 +223,14 @@ :width "84px" :height "36px" :vertical-align :top}}]) + [notices] + [:br] [ui/raised-button {:label "Tulosta valitut listat" :href (routes/organizer-print-path) :on-click load-selected-decklists :primary true - :disabled (empty? @selected-decklists)}]]] + :disabled (empty? @selected-decklists) + :style {:margin-top "12px"}}]]] [:div.decklists [decklist-table (:decklist @tournament) on-select]]]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index f480b4e..e32faa1 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -13,6 +13,7 @@ [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host]] + [mtg-pairings-server.util.decklist :refer [->text]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] [mtg-pairings-server.util.material-ui :refer [get-theme text-field]] [clojure.string :as str])) @@ -293,6 +294,7 @@ tournament (subscribe [::subs/tournament]) saving? (subscribe [::subs/saving?]) saved? (subscribe [::subs/saved?]) + error? (subscribe [::subs/error?]) page (subscribe [::common-subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] @@ -333,15 +335,6 @@ [decklist-import] [:h3 "Pelaajan tiedot"] [player-info decklist] - (when @saved? - (let [url (str (get-host) (routes/old-decklist-path {:id (:id @page)}))] - [:div.success-notice - [:h4 "Tallennus onnistui!"] - [:p - "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " - [:a {:href url} - url] - ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]])) [ui/raised-button {:label "Tallenna" :on-click save-decklist @@ -353,4 +346,18 @@ {:size 36 :style {:margin "24px 0 0 24px" :vertical-align :top}}]) + (when @saved? + (let [url (str (get-host) (routes/old-decklist-path {:id (:id @page)}))] + [:div.success-notice + [:h4 "Tallennus onnistui!"] + [:p + "Pakkalistasi tallennus onnistui. Pääset muokkaamaan pakkalistaasi osoitteessa " + [:a {:href url} + url] + ". Jos annoit sähköpostiosoitteesi, pakkalistasi sekä sama osoite lähetettiin sinulle myös sähköpostitse. "]])) + (when @error? + [:div.error-notice + [:h4 "Tallennus epäonnistui"] + [:p "Pakkalistan tallennus epäonnistui. Voit kopioida pakkalistasi tekstimuodossa alta ja yrittää myöhemmin uudelleen."] + [:pre (->text @decklist)]]) [error-list errors]])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index fddc829..8ed16d3 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -31,18 +31,23 @@ (defn connect! [] (ws/send! [:client/connect-decklist])) +(reg-event-db :server/decklist-error + (fn [db _] + (update db :decklist-editor merge {:error true + :saving false + :saved false}))) + (reg-event-fx ::save-decklist (fn [{:keys [db]} [_ tournament decklist]] - {:db (-> db - (assoc-in [:decklist-editor :saving] true) - (assoc-in [:decklist-editor :decklist] decklist)) + {:db (update db :decklist-editor merge {:decklist decklist + :saving true}) :ws-send [:client/save-decklist [tournament decklist]]})) (reg-event-fx :server/decklist-saved (fn [{:keys [db]} [_ id]] - {:db (-> db - (assoc-in [:decklist-editor :saving] false) - (assoc-in [:decklist-editor :saved] true)) + {:db (update db :decklist-editor merge {:saved true + :saving false + :error false}) :navigate (routes/old-decklist-path {:id id})})) (reg-event-fx ::load-organizer-tournament @@ -61,10 +66,11 @@ (reg-event-fx :server/organizer-tournament-saved (fn [{:keys [db]} [_ id]] - {:db (-> db - (assoc-in [:decklist-editor :organizer-tournament :id] id) - (assoc-in [:decklist-editor :saving] false) - (assoc-in [:decklist-editor :saved] true)) + {:db (util/assoc-in-many db + [:decklist-editor :organizer-tournament :id] id + [:decklist-editor :saved] true + [:decklist-editor :saving] false + [:decklist-editor :error] false) :navigate (routes/organizer-tournament-path {:id id})})) (reg-event-fx ::load-decklist @@ -92,7 +98,12 @@ (fn [db _] (update db :decklist-editor merge {:organizer-tournament nil :saving false - :saved false}))) + :saved false + :error false}))) + +(reg-event-db ::clear-status + (fn [db [_ key]] + (assoc-in db [:decklist-editor key] false))) (reg-event-fx ::import-address (fn [_ [_ address]] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs index 4971470..6fc655a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs @@ -13,6 +13,10 @@ (fn [db _] (get-in db [:decklist-editor :saved]))) +(reg-sub ::error? + (fn [db _] + (get-in db [:decklist-editor :error]))) + (reg-sub ::decklist (fn [db _] (get-in db [:decklist-editor :decklist]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs index 5560cf3..20c2473 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs @@ -2,16 +2,11 @@ (:require [cljsjs.material-ui] [cljs-react-material-ui.core :refer [get-mui-theme]] [cljs-react-material-ui.reagent :as ui] - [oops.core :refer [oget]])) + [oops.core :refer [oget]] + [mtg-pairings-server.styles.common :refer [palette]])) (def theme (get-mui-theme - {:palette {:primary1-color "#90caf9" - :primary2-color "#5d99c6" - :primary3-color "#c3fdff" - :accent1-color "#ec407a" - :accent2-color "#b4004e" - :accent3-color "#ff77a9" - :picker-header-color "#5d99c6"}})) + {:palette palette})) (defn get-theme [component] (js->clj (oget component "context" "muiTheme") :keywordize-keys true)) From b9ff284f318bd9c3d4afbaaecb6368f514bbda31 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 14:26:59 +0300 Subject: [PATCH 30/44] Easier handling of color palette --- .../mtg_pairings_server/styles/common.cljc | 46 +++-- .../components/decklist/submit.cljs | 3 +- .../components/filter.cljs | 73 +++---- .../mtg_pairings_server/components/main.cljs | 194 ++++++++---------- .../components/tournament.cljs | 27 +-- 5 files changed, 165 insertions(+), 178 deletions(-) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc index f5755ac..0cae06a 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc @@ -1,26 +1,38 @@ (ns mtg-pairings-server.styles.common - (:require [garden.color :refer [hex->rgb rgba rgb->hex]])) + (:require [garden.color :refer [hex->rgb rgb? rgba rgb->hex]])) (def ellipsis-overflow {:white-space :nowrap :text-overflow :ellipsis :overflow :hidden :vertical-align :bottom}) -(def color {:light-blue (hex->rgb "d0e9fc") - :light-grey (hex->rgb "e6e6e6") - :grey (hex->rgb "999999") - :dark-grey (hex->rgb "333333") - :light-green (hex->rgb "ccffcc") - :transparent-grey (rgba 0 0 0 0.7) - :primary1-color (hex->rgb "90caf9") - :primary2-color (hex->rgb "5d99c6") - :primary3-color (hex->rgb "c3fdff") - :accent1-color (hex->rgb "ec407a") - :accent2-color (hex->rgb "b4004e") - :accent3-color (hex->rgb "ff77a9") - :error-color (hex->rgb "f44336") - :picker-header-color (hex->rgb "4ba3c7")}) +(defn rgba? [color] + (every? color [:red :green :blue :alpha])) + +(defn ->str [c] + (cond + (rgba? c) (str "rgba(" (:red c) ", " (:green c) ", " (:blue c) ", " (:alpha c) ")") + (rgb? c) (rgb->hex c))) + +(def color {:light-blue (hex->rgb "d0e9fc") + :light-grey (hex->rgb "e6e6e6") + :grey (hex->rgb "999999") + :dark-grey (hex->rgb "333333") + :light-green (hex->rgb "ccffcc") + :transparent-grey (rgba 0 0 0 0.7) + :primary1-color (hex->rgb "90caf9") + :primary2-color (hex->rgb "5d99c6") + :primary3-color (hex->rgb "c3fdff") + :accent1-color (hex->rgb "ec407a") + :accent2-color (hex->rgb "b4004e") + :accent3-color (hex->rgb "ff77a9") + :error-color (hex->rgb "f44336") + :picker-header-color (hex->rgb "4ba3c7") + :text-color (rgba 0 0 0 0.87) + :secondary-text-color (rgba 0 0 0 0.54) + :alternate-text-color (hex->rgb "ffffff")}) (def palette (into {} (for [key [:primary1-color :primary2-color :primary3-color :accent1-color - :accent2-color :accent3-color :error-color :picker-header-color]] - [key (rgb->hex (color key))]))) + :accent2-color :accent3-color :error-color :picker-header-color + :text-color :secondary-text-color :alternate-text-color]] + [key (->str (color key))]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index e32faa1..197e2ce 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -4,7 +4,6 @@ [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] - [prop-types] [oops.core :refer [oget]] [mtg-pairings-server.components.autosuggest :refer [autosuggest]] [mtg-pairings-server.components.tooltip :refer [tooltip]] @@ -15,7 +14,7 @@ [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host]] [mtg-pairings-server.util.decklist :refer [->text]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] - [mtg-pairings-server.util.material-ui :refer [get-theme text-field]] + [mtg-pairings-server.util.material-ui :refer [text-field]] [clojure.string :as str])) (def basic? #{"Plains" "Island" "Swamp" "Mountain" "Forest" "Wastes" diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs index 1337375..10d1a9d 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/filter.cljs @@ -6,14 +6,13 @@ [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] [cljs-time.coerce :as coerce] - [prop-types] [oops.core :refer [oget]] [mtg-pairings-server.components.slider :refer [slider]] [mtg-pairings-server.events.pairings :as events] + [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.pairings :as subs] - [mtg-pairings-server.util :refer [to-local-date]] - [mtg-pairings-server.util.material-ui :refer [get-theme]])) + [mtg-pairings-server.util :refer [to-local-date]])) (defn organizer-filter [] (let [organizers (subscribe [::subs/organizers]) @@ -81,18 +80,15 @@ (defn player-filter [] (let [players (subscribe [::subs/tournament-filter :players]) max-players (subscribe [::subs/max-players])] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn player-filter-render [] - (let [palette (:palette (get-theme (reagent/current-component)))] - [:div.filter.player-filter - [:label.filter-label "Pelaajamäärä"] - [slider {:min (atom 0) - :max max-players - :value players - :step 10 - :color (:accent1Color palette) - :on-change #(dispatch [::events/tournament-filter [:players %]])}]]))}))) + (fn player-filter-render [] + [:div.filter.player-filter + [:label.filter-label "Pelaajamäärä"] + [slider {:min (atom 0) + :max max-players + :value players + :step 10 + :color (:accent1-color styles/palette) + :on-change #(dispatch [::events/tournament-filter [:players %]])}]]))) (defn clear-filters [] (let [filters-active? (subscribe [::subs/filters-active])] @@ -113,31 +109,28 @@ (defn mobile-filters [] (let [filters-active? (subscribe [::subs/filters-active])] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn mobile-filters-render [] - (let [palette (:palette (get-theme (reagent/current-component)))] - [ui/card - {:class-name "filters mobile-filters hidden-desktop"} - [ui/card-header - {:title "Hakutyökalut" - :title-style {:line-height "24px"} - :act-as-expander true - :show-expandable-button true - :avatar (reagent/as-element - [ui/avatar - {:icon (icons/content-filter-list) - :size 24 - :background-color (if @filters-active? - (:accent1Color palette) - (:primary1Color palette))}])}] - [ui/card-text - {:expandable true - :style {:padding-top 0}} - [organizer-filter] - [date-filter] - [player-filter] - [clear-filters]]]))}))) + (fn mobile-filters-render [] + [ui/card + {:class-name "filters mobile-filters hidden-desktop"} + [ui/card-header + {:title "Hakutyökalut" + :title-style {:line-height "24px"} + :act-as-expander true + :show-expandable-button true + :avatar (reagent/as-element + [ui/avatar + {:icon (icons/content-filter-list) + :size 24 + :background-color (if @filters-active? + (:accent1-color styles/palette) + (:primary1-color styles/palette))}])}] + [ui/card-text + {:expandable true + :style {:padding-top 0}} + [organizer-filter] + [date-filter] + [player-filter] + [clear-filters]]]))) (defn filters [] (let [mobile? (subscribe [::common-subs/mobile?])] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs index 3118ea5..2bbe08b 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs @@ -5,56 +5,51 @@ [cljs-react-material-ui.core] [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] - [prop-types] [oops.core :refer [oget]] [accountant.core :as accountant] [mtg-pairings-server.components.tournament :refer [tournament-card-header]] [mtg-pairings-server.events.pairings :as events] [mtg-pairings-server.routes.pairings :refer [tournaments-path standings-path]] + [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.pairings :as subs] - [mtg-pairings-server.util :refer [format-date indexed]] - [mtg-pairings-server.util.material-ui :refer [get-theme]])) + [mtg-pairings-server.util :refer [format-date indexed]])) (defn pairing [data pairing?] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn [data pairing?] - (let [palette (:palette (get-theme (reagent/current-component))) - bye? (= (:table_number data) 0)] - [ui/list-item - {:class :mui-pairing - :left-avatar (reagent/as-element [ui/avatar - {:background-color (if bye? - (:accent3Color palette) - (:primary1Color palette)) - :color (:textColor palette)} - (when-not bye? - (or (:table_number data) (:pod data)))]) - :primary-text (if pairing? - (str "Kierros " (:round_number data)) - (if (:pod data) - (str "Pod " (:pod data)) - "Seating")) - :secondary-text (reagent/as-element - (if pairing? - [:div - [:span.names - [:span {:style {:color "rgba(0, 0, 0, 0.87)"}} - (str (:team1_name data) " (" (:team1_points data) ")")] - [:span.hidden-mobile " - "] - [:br.hidden-desktop] - [:span (if bye? - (:team2_name data) - (str (:team2_name data) " (" (:team2_points data) ")"))]] - (when-not bye? - [:span.points - [:span (:team1_wins data)] - [:span.hidden-mobile " - "] - [:br.hidden-desktop] - [:span (:team2_wins data)]])] - [:span.names (or (:team1_name data) - (str "Seat " (:seat data)))])) - :secondary-text-lines 2}]))})) + (let [bye? (= (:table_number data) 0)] + [ui/list-item + {:class :mui-pairing + :left-avatar (reagent/as-element [ui/avatar + {:background-color (if bye? + (:accent3-color styles/palette) + (:primary1-color styles/palette)) + :color (:text-color styles/palette)} + (when-not bye? + (or (:table_number data) (:pod data)))]) + :primary-text (if pairing? + (str "Kierros " (:round_number data)) + (if (:pod data) + (str "Pod " (:pod data)) + "Seating")) + :secondary-text (reagent/as-element + (if pairing? + [:div + [:span.names + [:span {:style {:color (:text-color styles/palette)}} + (str (:team1_name data) " (" (:team1_points data) ")")] + [:span.hidden-mobile " - "] + [:br.hidden-desktop] + [:span (if bye? + (:team2_name data) + (str (:team2_name data) " (" (:team2_points data) ")"))]] + (when-not bye? + [:span.points + [:span (:team1_wins data)] + [:span.hidden-mobile " - "] + [:br.hidden-desktop] + [:span (:team2_wins data)]])] + [:span.names (or (:team1_name data) + (str "Seat " (:seat data)))])) + :secondary-text-lines 2}])) (defn header [] (let [user (subscribe [::subs/logged-in-user]) @@ -62,45 +57,42 @@ login! (fn [] (dispatch [::events/login @dci-number]) (reset! dci-number ""))] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn header-render [] - (let [palette (:palette (get-theme (reagent/current-component)))] - [ui/app-bar - {:id :header - :title (when @user (:name @user)) - :title-style {:color (:secondaryTextColor palette)} - :icon-element-right (when-not @user - (reagent/as-element [:div - [ui/text-field - {:floating-label-text "DCI-numero" - :value @dci-number - :on-change (fn [_ new-value] - (reset! dci-number new-value)) - :on-key-down (fn [e] - (when (= 13 (oget e "keyCode")) - (login!))) - :underline-focus-style {:border-color (:accent3Color palette)} - :floating-label-focus-style {:color (:accent3Color palette)} - :style {:margin-top "-24px" - :width "160px"}}] - [ui/raised-button - {:label "Kirjaudu" - :on-click login! - :overlay-style {:background-color :white} - :style {:margin-left "12px"}}]])) - :icon-element-left (reagent/as-element - [ui/icon-menu - {:icon-button-element (reagent/as-element - [ui/icon-button - [icons/navigation-menu]])} - [ui/menu-item {:primary-text "Etusivu" - :on-click #(accountant/navigate! "/")}] - [ui/menu-item {:primary-text "Turnausarkisto" - :on-click #(accountant/navigate! (tournaments-path))}] - (when @user - [ui/menu-item {:primary-text "Kirjaudu ulos" - :on-click #(dispatch [::events/logout])}])])}]))}))) + (fn header-render [] + [ui/app-bar + {:id :header + :title (when @user (:name @user)) + :title-style {:color (:text-color styles/palette)} + :icon-element-right (when-not @user + (reagent/as-element [:div + [ui/text-field + {:floating-label-text "DCI-numero" + :value @dci-number + :on-change (fn [_ new-value] + (reset! dci-number new-value)) + :on-key-down (fn [e] + (when (= 13 (oget e "keyCode")) + (login!))) + :underline-focus-style {:border-color (:accent3-color styles/palette)} + :floating-label-focus-style {:color (:accent3-color styles/palette)} + :style {:margin-top "-24px" + :width "160px"}}] + [ui/raised-button + {:label "Kirjaudu" + :on-click login! + :overlay-style {:background-color :white} + :style {:margin-left "12px"}}]])) + :icon-element-left (reagent/as-element + [ui/icon-menu + {:icon-button-element (reagent/as-element + [ui/icon-button + [icons/navigation-menu]])} + [ui/menu-item {:primary-text "Etusivu" + :on-click #(accountant/navigate! "/")}] + [ui/menu-item {:primary-text "Turnausarkisto" + :on-click #(accountant/navigate! (tournaments-path))}] + (when @user + [ui/menu-item {:primary-text "Kirjaudu ulos" + :on-click #(dispatch [::events/logout])}])])}]))) (defn combine-pairings-and-pods [pairings pods] (->> (concat pairings pods) @@ -108,28 +100,24 @@ (reverse))) (defn own-tournament [t] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn own-tournament-render [t] - (let [palette (:palette (get-theme (reagent/current-component)))] - [ui/card - (tournament-card-header t {:act-as-expander true - :show-expandable-button true}) - [ui/card-text - {:expandable true - :style {:padding-top 0}} - [ui/list - [ui/list-item - {:primary-text (str "Standings, kierros " (:max_standings_round t)) - :left-avatar (reagent/as-element [ui/avatar - {:icon (reagent/as-element [icons/action-list]) - :background-color (:primary1Color palette)}]) - :on-click #(accountant/navigate! (standings-path {:id (:id t), :round (:max_standings_round t)}))}] - (for [p (combine-pairings-and-pods (:pairings t) (:pod-seats t))] - ^{:key [(:id t) (:round_number p) (:id p)]} - [pairing p (boolean (:team1_name p))]) - (when (:seating t) - [pairing (:seating t) false])]]]))})) + [ui/card + (tournament-card-header t {:act-as-expander true + :show-expandable-button true}) + [ui/card-text + {:expandable true + :style {:padding-top 0}} + [ui/list + [ui/list-item + {:primary-text (str "Standings, kierros " (:max_standings_round t)) + :left-avatar (reagent/as-element [ui/avatar + {:icon (reagent/as-element [icons/action-list]) + :background-color (:primary1-color styles/palette)}]) + :on-click #(accountant/navigate! (standings-path {:id (:id t), :round (:max_standings_round t)}))}] + (for [p (combine-pairings-and-pods (:pairings t) (:pod-seats t))] + ^{:key [(:id t) (:round_number p) (:id p)]} + [pairing p (boolean (:team1_name p))]) + (when (:seating t) + [pairing (:seating t) false])]]]) (defn notification [] (let [text (subscribe [::subs/notification])] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs index f6701d7..0e1d52b 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs @@ -7,14 +7,13 @@ [cljs-react-material-ui.icons :as icons] [goog.string :as gstring] [goog.string.format] - [prop-types] [mtg-pairings-server.events.pairings :as events] [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.routes.pairings :refer [tournaments-path tournament-path pairings-path standings-path pods-path seatings-path bracket-path]] [mtg-pairings-server.components.paging :refer [with-paging]] [mtg-pairings-server.components.filter :refer [filters]] - [mtg-pairings-server.util :refer [format-date indexed]] - [mtg-pairings-server.util.material-ui :refer [get-theme]])) + [mtg-pairings-server.styles.common :as styles] + [mtg-pairings-server.util :refer [format-date indexed]])) (defn tournament-card-header ([data] @@ -97,19 +96,15 @@ [tournament t])])]]) (defn sortable-header [{:keys [class column sort-key dispatch-key]} & children] - (reagent/create-class - {:context-types #js {:muiTheme prop-types/object.isRequired} - :reagent-render (fn sortable-header-render [{:keys [class column sort-key dispatch-key]} & children] - (let [palette (:palette (get-theme (reagent/current-component)))] - [:th {:class class - :style (when (= column sort-key) {:color (:accent1Color palette)}) - :on-click #(dispatch [dispatch-key column])} - [icons/hardware-keyboard-arrow-down - {:style {:vertical-align :baseline - :position :absolute - :left 0 - :color nil}}] - children]))})) + [:th {:class class + :style (when (= column sort-key) {:color (:accent1-color styles/palette)}) + :on-click #(dispatch [dispatch-key column])} + [icons/hardware-keyboard-arrow-down + {:style {:vertical-align :baseline + :position :absolute + :left 0 + :color nil}}] + children]) (defn pairing-row [pairing] (let [bye? (= (:table_number pairing) 0)] From 160cf2f3193a2553daecec69fece128c53c2c500 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 14:57:38 +0300 Subject: [PATCH 31/44] Mobile styling --- .../mtg_pairings_server/styles/decklist.clj | 15 +++--- .../mtg_pairings_server/styles/common.cljc | 11 ++-- .../components/autosuggest.cljs | 5 +- .../components/decklist/submit.cljs | 51 ++++++++++++------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 8a89f7f..a31d212 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -29,14 +29,19 @@ [:&.right {:margin-left (px 24)}]]] [:.decklist-import + {:margin "12px 0"} [:.info :.form {:display :inline-block - :width (percent 50)}]]) + :width (percent 50) + :padding (px 12)}]]) (when-mobile [:#player-info [:.full-width :.half-width {:width "100%" - :display :block}]]) + :display :block}]] + [:.decklist-import + [:.form + {:padding-bottom (px 12)}]]) [:.intro [:.tournament-date :.tournament-name :.tournament-format {:font-weight :bold}]] @@ -49,11 +54,7 @@ [:th.actions :td.actions {:width (px 48)}] [:th.error :td.error - {:width (px 48)}]]] - [:.decklist-import - {:margin "12px 0"} - [:.info :.form - {:padding (px 12)}]]]) + {:width (px 48)}]]]]) (def grey-border {:style :solid :width (px 1) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc index 0cae06a..e0ef84e 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc @@ -15,7 +15,7 @@ (rgb? c) (rgb->hex c))) (def color {:light-blue (hex->rgb "d0e9fc") - :light-grey (hex->rgb "e6e6e6") + :light-grey (hex->rgb "e0e0e0") :grey (hex->rgb "999999") :dark-grey (hex->rgb "333333") :light-green (hex->rgb "ccffcc") @@ -32,7 +32,8 @@ :secondary-text-color (rgba 0 0 0 0.54) :alternate-text-color (hex->rgb "ffffff")}) -(def palette (into {} (for [key [:primary1-color :primary2-color :primary3-color :accent1-color - :accent2-color :accent3-color :error-color :picker-header-color - :text-color :secondary-text-color :alternate-text-color]] - [key (->str (color key))]))) +(def palette (into {} (for [[k c] color] + [k (->str c)]))) + +(defn border [width style color] + (str width " " (name style) " " color)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs index b5e78b8..fc96064 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs @@ -3,7 +3,8 @@ [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] [cljsjs.react-autosuggest] - [oops.core :refer [oget]])) + [oops.core :refer [oget]] + [mtg-pairings-server.util :refer [deep-merge]])) (defn ^:private suggestion [suggestion opts] (reagent/as-element @@ -60,4 +61,4 @@ :render-suggestion suggestion :render-input-component input :input-props input-props - :theme (merge default-styles styles)}])))) + :theme (deep-merge default-styles styles)}])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 197e2ce..8c9c539 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -9,6 +9,7 @@ [mtg-pairings-server.components.tooltip :refer [tooltip]] [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.decklist :as routes] + [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host]] @@ -23,6 +24,7 @@ (defn input [] (let [tournament (subscribe [::subs/tournament]) + mobile? (subscribe [::common-subs/mobile?]) on-change #(dispatch [::events/add-card %]) suggestions (atom []) fetch-suggestions (debounce (fn [prefix] @@ -33,12 +35,17 @@ 250) clear-suggestions #(reset! suggestions [])] (fn input-render [_ _] - [autosuggest {:suggestions suggestions - :on-change on-change - :on-suggestions-fetch-requested fetch-suggestions - :on-suggestions-clear-requested clear-suggestions - :id :decklist-autosuggest - :floating-label-text "Lisää kortti..."}]))) + (let [width (if @mobile? + "calc(100vw - 140px)" + 256)] + [autosuggest {:suggestions suggestions + :on-change on-change + :on-suggestions-fetch-requested fetch-suggestions + :on-suggestions-clear-requested clear-suggestions + :id :decklist-autosuggest + :floating-label-text "Lisää kortti..." + :styles {:container {:width width} + :input {:width width}}}])))) (defn valid-player-data? [{:keys [first-name last-name dci]}] (and (valid-dci? dci) @@ -89,7 +96,8 @@ icon))) (defn decklist-table-row [board card error] - (let [on-change (fn [_ _ quantity] + (let [mobile? (subscribe [::common-subs/mobile?]) + on-change (fn [_ _ quantity] (dispatch [::events/set-quantity board (:id card) quantity])) on-delete #(dispatch [::events/remove-card board (:id card)])] (fn decklist-table-row-render [_ {:keys [name quantity]} error] @@ -114,10 +122,11 @@ :primary-text i :inner-div-style {:padding "0 6px"}}])))] [ui/table-row-column {:class-name :card - :style {:font-size "14px"}} + :style {:font-size "16px" + :padding-left (if @mobile? 0 "24px")}} name] [ui/table-row-column {:class-name :actions - :style {:font-size "14px" + :style {:font-size "16px" :padding 0}} [ui/icon-button {:on-click on-delete} [icons/content-remove-circle-outline]]] @@ -128,7 +137,8 @@ [error-icon error])]]))) (defn decklist-table [decklist board] - (let [header-style {:color :black + (let [mobile? (subscribe [::common-subs/mobile?]) + header-style {:color :black :font-weight :bold :font-size "16px" :height "36px"}] @@ -150,7 +160,9 @@ {:padding "0 12px"})} "Määrä"] [ui/table-header-column {:class-name :card - :style header-style} + :style (merge header-style + (when @mobile? + {:padding-left 0}))} "Kortti"] [ui/table-header-column {:class-name :actions}] [ui/table-header-column {:class-name :error}]]] @@ -225,11 +237,16 @@ decklist-on-change #(reset! decklist %) import-decklist (fn [] (dispatch [::events/import-text @decklist]) - (reset! selected nil))] + (reset! selected nil)) + border (styles/border "1px" :solid (styles/palette :light-grey))] (fn decklist-import-render [] [:div.decklist-import - [ui/tabs {:value @selected} + [ui/tabs {:value @selected + :content-container-style (when @selected + {:border-bottom border + :border-left (when-not @mobile? border) + :border-right (when-not @mobile? border)})} [ui/tab {:label "Lataa aiempi lista" :value "load-previous" :on-active on-active @@ -239,17 +256,17 @@ "Lataa aiempi lista"] [:p "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " - [:span.address "https://pairings.fi/decklist/abcd..."] + [:span.address "https://decklist.pairings.fi/abcd..."] ")."]] [:div.form [text-field {:on-change address-on-change :floating-label-text "Osoite" - :full-width (not @mobile?)}] + :full-width true}] [:br] [ui/raised-button {:label "Lataa" :disabled (str/blank? @address) :on-click import-from-address}]]] - [ui/tab {:label "Lataa tekstimuotoinen lista" + [ui/tab {:label "Lataa tekstilista" :value "load-text" :on-active on-active :style {:color :black}} @@ -268,7 +285,7 @@ :rows 7 :rows-max 7 :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} - :full-width (not @mobile?)}] + :full-width true}] [:br] [ui/raised-button {:label "Lataa" :disabled (str/blank? @decklist) From 65a8696de27cec254da1cb47d0fdfabb4008819d Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 15:59:22 +0300 Subject: [PATCH 32/44] Fix ->hex --- mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 7e5e0a0..56fd70a 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -18,8 +18,8 @@ (wrap-authorization auth-backend) (wrap-authentication auth-backend))) -(defn ^:private ->hex [#^bytes bytes] - (.toString (BigInteger. 1 bytes) 16)) +(defn ^:private ->hex [bytes] + (apply str (for [b bytes] (format "%02x" b)))) (defn ^:private authenticate [username password] (when-let [user (sql-util/select-unique-or-nil db/smf-user From 990eb976b0fcb573357ac041fb8b57c398046155 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Sat, 13 Apr 2019 16:15:49 +0300 Subject: [PATCH 33/44] Remove unnecessary dependency --- mtg-pairings-server/project.clj | 1 - .../src/clj/mtg_pairings_server/auth.clj | 11 +---------- .../src/clj/mtg_pairings_server/middleware.clj | 2 -- .../src/cljc/mtg_pairings_server/util.cljc | 2 +- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index d82c12f..c4f1851 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -6,7 +6,6 @@ [org.clojure/tools.logging "0.4.1"] [ring/ring-core "1.7.1"] [ring/ring-defaults "0.3.2"] - [buddy/buddy-auth "2.1.0"] [hiccup "1.0.5"] [yogthos/config "1.1.1"] [org.clojure/core.cache "0.7.2"] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 56fd70a..70e0fd8 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -1,7 +1,5 @@ (ns mtg-pairings-server.auth - (:require [buddy.auth.backends.session :refer [session-backend]] - [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] - [clojure.string :as str] + (:require [clojure.string :as str] [compojure.api.sweet :refer :all] [config.core :refer [env]] [ring.util.response :refer [redirect]] @@ -11,13 +9,6 @@ [mtg-pairings-server.util.sql :as sql-util]) (:import (java.security MessageDigest))) -(defonce auth-backend (session-backend)) - -(defn wrap-auth [handler] - (-> handler - (wrap-authorization auth-backend) - (wrap-authentication auth-backend))) - (defn ^:private ->hex [bytes] (apply str (for [b bytes] (format "%02x" b)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj index 7794ab8..97e7c9d 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/middleware.clj @@ -1,7 +1,6 @@ (ns mtg-pairings-server.middleware (:require [ring.middleware.defaults :refer [site-defaults api-defaults wrap-defaults]] [config.core :refer [env]] - [mtg-pairings-server.auth :refer [wrap-auth]] [mtg-pairings-server.middleware.cache-control :refer [wrap-cache-control]] [mtg-pairings-server.middleware.decklist :refer [wrap-decklist-prefix]] [mtg-pairings-server.middleware.error :refer [wrap-errors]])) @@ -22,7 +21,6 @@ (defn wrap-site-middleware [handler] (cond-> handler - true (wrap-auth) true (wrap-defaults (update site-defaults :security dissoc :frame-options :content-type-options)) (env :dev) (add-dev-middleware) (not (env :dev)) (add-prod-middleware))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 9a2f725..08b8b2f 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -55,7 +55,7 @@ (some->> datetime (format/unparse (format/formatters :date-time)))) (defn format-date-time [datetime] - (some->> datetime (format/unparse (format/formatter "dd.MM.yyyy hh:mm")))) + (some->> datetime (format/unparse (format/formatter "dd.MM.yyyy HH:mm")))) (defn today-or-yesterday? [date] (let [yesterday (time/minus (time/today) (time/days 1)) From e7f84097825cf7332be1f39b803dddc31752d190 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 1 Jul 2019 09:37:01 +0300 Subject: [PATCH 34/44] Use AdoptOpenJDK Java 12 image --- mtg-pairings-server/Dockerfile | 17 +++++++++-------- mtg-pairings-server/build.sh | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mtg-pairings-server/Dockerfile b/mtg-pairings-server/Dockerfile index 4388c86..508b726 100644 --- a/mtg-pairings-server/Dockerfile +++ b/mtg-pairings-server/Dockerfile @@ -1,7 +1,8 @@ -FROM openjdk:11-slim AS builder +FROM adoptopenjdk/openjdk12-openj9:alpine AS builder ENV LEIN_ROOT true -RUN apt-get update && apt-get install -y curl -RUN curl -Lo /usr/bin/lein https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein && chmod +x /usr/bin/lein +RUN apk --no-cache add curl bash \ + && curl -Lo /usr/bin/lein https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein \ + && chmod +x /usr/bin/lein WORKDIR /app COPY project.clj . RUN lein deps @@ -12,12 +13,12 @@ COPY ./resources ./resources COPY ./test ./test COPY ./test-resources ./test-resources COPY decklist.cljs.edn pairings.cljs.edn ./ -RUN lein test -RUN lein with-profile provided,prod do clean, garden once, minify-assets, fig:min pairings, fig:min decklist -RUN ./scripts/tag-assets.sh -RUN lein uberjar +RUN lein test \ + && lein with-profile provided,prod do clean, garden once, minify-assets, fig:min pairings, fig:min decklist \ + && ./scripts/tag-assets.sh \ + && lein uberjar -FROM openjdk:11-jre-slim +FROM adoptopenjdk/openjdk12-openj9:alpine-jre WORKDIR /app COPY --from=builder /app/target/mtg-pairings.jar . CMD ["java", "-jar", "mtg-pairings.jar"] diff --git a/mtg-pairings-server/build.sh b/mtg-pairings-server/build.sh index fe809a5..c381191 100755 --- a/mtg-pairings-server/build.sh +++ b/mtg-pairings-server/build.sh @@ -4,5 +4,5 @@ set -euo pipefail version=$(git log --pretty=format:'%h' -n 1) tag=arttuka/pairings:$version -docker build . -t $tag --network mtgsuomi-deployment_default +docker build . -t $tag --network mtgsuomi-deployment_default --pull docker tag $tag arttuka/pairings:latest From 41d0af0214734be0966b27219b3b6cecf0410219 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 1 Jul 2019 12:57:05 +0300 Subject: [PATCH 35/44] Use lein-buster for tagging assets --- mtg-pairings-server/.gitignore | 4 ++-- mtg-pairings-server/Dockerfile | 12 ++++++---- mtg-pairings-server/decklist.cljs.edn | 4 ++-- .../dev-resources/public/css/slider.css | 1 + mtg-pairings-server/dev.cljs.edn | 2 +- mtg-pairings-server/pairings.cljs.edn | 4 ++-- mtg-pairings-server/project.clj | 23 ++++++++++++++----- .../{public/css => private}/slider.css | 0 mtg-pairings-server/scripts/tag-assets.sh | 23 ------------------- .../src/clj/mtg_pairings_server/handler.clj | 20 ++++++++++------ 10 files changed, 46 insertions(+), 47 deletions(-) create mode 120000 mtg-pairings-server/dev-resources/public/css/slider.css rename mtg-pairings-server/resources/{public/css => private}/slider.css (100%) delete mode 100755 mtg-pairings-server/scripts/tag-assets.sh diff --git a/mtg-pairings-server/.gitignore b/mtg-pairings-server/.gitignore index 7c86dd0..34aa19c 100644 --- a/mtg-pairings-server/.gitignore +++ b/mtg-pairings-server/.gitignore @@ -18,5 +18,5 @@ pom.xml.asc .classpath .DS_Store /resources/public/js -/resources/public/css/main.css -/resources/public/css/main.min.css +/resources/public/css +/resources/manifest.json diff --git a/mtg-pairings-server/Dockerfile b/mtg-pairings-server/Dockerfile index 508b726..fdb9401 100644 --- a/mtg-pairings-server/Dockerfile +++ b/mtg-pairings-server/Dockerfile @@ -6,17 +6,21 @@ RUN apk --no-cache add curl bash \ WORKDIR /app COPY project.clj . RUN lein deps -COPY ./scripts ./scripts COPY ./src ./src COPY ./env ./env +COPY ./dev-resources ./dev-resources COPY ./resources ./resources COPY ./test ./test COPY ./test-resources ./test-resources COPY decklist.cljs.edn pairings.cljs.edn ./ RUN lein test \ - && lein with-profile provided,prod do clean, garden once, minify-assets, fig:min pairings, fig:min decklist \ - && ./scripts/tag-assets.sh \ - && lein uberjar + && lein with-profile provided,prod do \ + clean, \ + garden once, \ + minify-assets, \ + fig:min pairings, \ + fig:min decklist, \ + uberjar FROM adoptopenjdk/openjdk12-openj9:alpine-jre WORKDIR /app diff --git a/mtg-pairings-server/decklist.cljs.edn b/mtg-pairings-server/decklist.cljs.edn index be6e8cc..ddd2e14 100644 --- a/mtg-pairings-server/decklist.cljs.edn +++ b/mtg-pairings-server/decklist.cljs.edn @@ -1,4 +1,4 @@ {:main mtg-pairings-server.decklist - :output-to "target/js/decklist-main.js" - :output-dir "target/js/compiled/decklist" + :output-to "target/public/js/decklist-main.js" + :output-dir "target/public/js/compiled/decklist" :optimizations :advanced} diff --git a/mtg-pairings-server/dev-resources/public/css/slider.css b/mtg-pairings-server/dev-resources/public/css/slider.css new file mode 120000 index 0000000..410ee4d --- /dev/null +++ b/mtg-pairings-server/dev-resources/public/css/slider.css @@ -0,0 +1 @@ +../../../resources/private/slider.css \ No newline at end of file diff --git a/mtg-pairings-server/dev.cljs.edn b/mtg-pairings-server/dev.cljs.edn index d296cda..dbc65d0 100644 --- a/mtg-pairings-server/dev.cljs.edn +++ b/mtg-pairings-server/dev.cljs.edn @@ -1,5 +1,5 @@ ^{:watch-dirs ["src/cljs" "src/cljc" "env/dev/cljs"] - :css-dirs ["resources/public/css" "target/public/css"] + :css-dirs ["dev-resources/public/css" "target/public/css"] :open-url false} {:main mtg-pairings-server.dev :preloads [re-frisk.preload] diff --git a/mtg-pairings-server/pairings.cljs.edn b/mtg-pairings-server/pairings.cljs.edn index e1df048..043b154 100644 --- a/mtg-pairings-server/pairings.cljs.edn +++ b/mtg-pairings-server/pairings.cljs.edn @@ -1,4 +1,4 @@ {:main mtg-pairings-server.pairings - :output-to "target/js/pairings-main.js" - :output-dir "target/js/compiled/pairings" + :output-to "target/public/js/pairings-main.js" + :output-dir "target/public/js/compiled/pairings" :optimizations :advanced} diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index c4f1851..aef41f0 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -26,11 +26,15 @@ :plugins [[lein-asset-minifier "0.4.6"] [lein-cljfmt "0.6.4"] [lein-ancient "0.6.15"] - [lein-garden "0.3.0" :exclusions [org.apache.commons/commons-compress]]] + [lein-garden "0.3.0" :exclusions [org.apache.commons/commons-compress]] + [no.terjedahl/lein-buster "0.2.0"]] :uberjar-name "mtg-pairings.jar" - :clean-targets ^{:protect false} ["target"] + :clean-targets ^{:protect false} ["target" + "resources/public/js" + "resources/public/css" + "resources/manifest.json"] :source-paths ["src/clj" "src/cljc" "src/cljs"] :resource-paths ["resources"] @@ -43,9 +47,16 @@ :pretty-print? true}}]} :minify-assets [[:css {:source ["target/public/css/main.css" - "resources/public/css/slider.css"] + "resources/private/slider.css"] :target "target/public/css/main.min.css"}]] + :buster {:files ["target/public/js/pairings-main.js" + "target/public/js/decklist-main.js" + "target/public/css/main.min.css"] + :files-base "target/public" + :output-base "resources/public" + :manifest "resources/manifest.json"} + :aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"] "fig:min" ["run" "-m" "figwheel.main" "-bo"]} @@ -72,7 +83,7 @@ :profiles {:dev {:repl-options {:init-ns mtg-pairings-server.repl :nrepl-middleware [cider.piggieback/wrap-cljs-repl]} :source-paths ["dev" "env/dev/clj" "env/dev/cljs"] - :resource-paths ["target"] + :resource-paths ["dev-resources" "target"] :test-paths ["test/clj"] :dependencies [[org.clojure/tools.namespace "0.2.11"] [binaryage/devtools "0.9.10"] @@ -108,5 +119,5 @@ :uberjar {:source-paths ["env/prod/cljs"] :main mtg-pairings-server.main :aot :all - :uberjar-exclusions [#"public/css/slider.css"] - :omit-source true}}) + :omit-source true + :auto-clean false}}) diff --git a/mtg-pairings-server/resources/public/css/slider.css b/mtg-pairings-server/resources/private/slider.css similarity index 100% rename from mtg-pairings-server/resources/public/css/slider.css rename to mtg-pairings-server/resources/private/slider.css diff --git a/mtg-pairings-server/scripts/tag-assets.sh b/mtg-pairings-server/scripts/tag-assets.sh deleted file mode 100755 index d1efa58..0000000 --- a/mtg-pairings-server/scripts/tag-assets.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -mkdir -p resources/public/js -mkdir -p resources/public/css - -pairingsjssha=$(sha1sum target/js/pairings-main.js) -pairingsjsfile="js/pairings-main.${pairingsjssha:0:8}.js" -cp target/js/pairings-main.js "resources/public/$pairingsjsfile" - -decklistjssha=$(sha1sum target/js/decklist-main.js) -decklistjsfile="js/decklist-main.${decklistjssha:0:8}.js" -cp target/js/decklist-main.js "resources/public/$decklistjsfile" - -csssha=$(sha1sum target/public/css/main.min.css) -cssfile="css/main.${csssha:0:8}.css" -cp target/public/css/main.min.css "resources/public/$cssfile" - -echo "{:main-css \"/$cssfile\" - :pairings-js \"/$pairingsjsfile\" - :decklist-js \"/$decklistjsfile\"} -" > resources/config.edn diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 10c6216..04030d6 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -1,8 +1,10 @@ (ns mtg-pairings-server.handler (:require [compojure.api.sweet :refer :all] [compojure.route :refer [not-found resources]] + [clojure.java.io :as io] [clojure.string :as str] [config.core :refer [env]] + [cheshire.core :as json] [hiccup.page :refer [include-js include-css html5]] [schema.core :as s] [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] @@ -22,6 +24,10 @@ (defn escape-quotes [s] (str/escape s {\' "\\'"})) +(def asset-manifest (delay (some-> (io/resource "manifest.json") + (io/reader) + (json/parse-stream)))) + (defn index ([js-file] (index js-file {})) @@ -35,7 +41,7 @@ :content "width=device-width, initial-scale=1"}] (include-css (if (env :dev) "/css/main.css" - (env :main-css))) + (str \/ (@asset-manifest "css/main.min.css")))) (when (env :dev) (include-css "/css/slider.css"))] [:body {:class "body-container"} @@ -44,13 +50,13 @@ "var initial_db = '" (escape-quotes (transit/write initial-db)) "'; ")] (include-js (if (env :dev) "/js/dev-main.js" - (env js-file)))])] + (str \/ (@asset-manifest js-file))))])] {:status 200 :body html :headers {"Content-Type" "text/html" "Cache-Control" "no-cache"}}))) -(let [index (partial index :pairings-js)] +(let [index (partial index "js/pairings-main.js")] (defroutes pairings-routes (GET "/" [] (index)) @@ -88,9 +94,9 @@ (index {:page {:page :bracket, :id id} :tournaments {id (tournament/client-tournament id)} :bracket {id (tournament/bracket id)}})) - (GET "/tournaments/:id/organizer" [] (index :pairings-js)) - (GET "/tournaments/:id/organizer/menu" [] (index :pairings-js)) - (GET "/tournaments/:id/organizer/deck-construction" [] (index :pairings-js)))) + (GET "/tournaments/:id/organizer" [] (index)) + (GET "/tournaments/:id/organizer/menu" [] (index)) + (GET "/tournaments/:id/organizer/deck-construction" [] (index)))) (defmacro validate-request [user-id tournament & body] `(if (and ~user-id (not= ~user-id (:user ~tournament))) @@ -98,7 +104,7 @@ :body "403 Forbidden"} (do ~@body))) -(let [index (partial index :decklist-js)] +(let [index (partial index "js/decklist-main.js")] (defroutes decklist-routes (GET "/decklist" [] (redirect (auth/organizer-path))) From dff4e2e46712b702b4e6f823e1e5fe4fd22eb8e2 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 1 Jul 2019 13:02:04 +0300 Subject: [PATCH 36/44] Update dependencies --- mtg-pairings-server/project.clj | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index aef41f0..614cd8d 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -1,19 +1,19 @@ (defproject mtg-pairings-server "2.2.1" :license {:name "MIT License" :url "http://www.opensource.org/licenses/mit-license.php"} - :dependencies [[org.clojure/clojure "1.10.0"] - [org.clojure/core.async "0.4.490"] + :dependencies [[org.clojure/clojure "1.10.1"] + [org.clojure/core.async "0.4.500"] [org.clojure/tools.logging "0.4.1"] [ring/ring-core "1.7.1"] [ring/ring-defaults "0.3.2"] [hiccup "1.0.5"] - [yogthos/config "1.1.1"] + [yogthos/config "1.1.4"] [org.clojure/core.cache "0.7.2"] [aleph "0.4.6"] [clj-time "0.15.1"] [korma "0.4.3" :exclusions [com.mchange/c3p0]] [mount "0.1.16"] - [org.postgresql/postgresql "42.2.5"] + [org.postgresql/postgresql "42.2.6"] [hikari-cp "2.7.1"] [metosin/compojure-api "1.1.12"] [org.flatland/ordered "1.5.7"] @@ -22,7 +22,7 @@ [com.taoensso/sente "1.14.0-RC2"] [com.taoensso/timbre "4.10.0"] [com.cognitect/transit-clj "0.8.313"] - [com.fzakaria/slf4j-timbre "0.3.12"]] + [com.fzakaria/slf4j-timbre "0.3.13"]] :plugins [[lein-asset-minifier "0.4.6"] [lein-cljfmt "0.6.4"] [lein-ancient "0.6.15"] @@ -85,14 +85,14 @@ :source-paths ["dev" "env/dev/clj" "env/dev/cljs"] :resource-paths ["dev-resources" "target"] :test-paths ["test/clj"] - :dependencies [[org.clojure/tools.namespace "0.2.11"] + :dependencies [[org.clojure/tools.namespace "0.3.0"] [binaryage/devtools "0.9.10"] [com.bhauman/rebel-readline-cljs "0.1.4" :exclusions [org.clojure/clojurescript]] - [ring/ring-mock "0.3.2"] + [ring/ring-mock "0.4.0"] [ring/ring-devel "1.7.1"] - [prone "1.6.1"] + [prone "1.6.4"] [hawk "0.2.11"] - [cider/piggieback "0.4.0" :exclusions [org.clojure/clojurescript]] + [cider/piggieback "0.4.1" :exclusions [org.clojure/clojurescript]] [re-frisk "0.5.4.1" :exclusions [org.clojure/clojurescript]]]} :test {:source-paths ^:replace ["src/clj" "src/cljc" "src/cljs"] :resource-paths ["test-resources"]} @@ -101,21 +101,20 @@ [reagent "0.8.1"] [com.google.errorprone/error_prone_annotations "2.3.3"] [com.google.code.findbugs/jsr305 "3.0.2"] - [com.bhauman/figwheel-main "0.2.0" :exclusions [org.clojure/clojurescript]] + [com.bhauman/figwheel-main "0.2.1" :exclusions [org.clojure/clojurescript]] [clj-commons/secretary "1.2.4"] [venantius/accountant "0.2.4"] [com.cognitect/transit-cljs "0.8.256"] [com.andrewmcveigh/cljs-time "0.5.2"] - [re-frame "0.10.6" :exclusions [cljsjs/react org.clojure/clojurescript]] + [re-frame "0.10.7" :exclusions [cljsjs/react org.clojure/clojurescript]] [binaryage/oops "0.7.0"] - [cljsjs/react "16.8.3-0"] - [cljsjs/react-dom "16.8.3-0"] - [cljsjs/react-dom-server "16.8.3-0"] + [cljsjs/react "16.8.6-0"] + [cljsjs/react-dom "16.8.6-0"] + [cljsjs/react-dom-server "16.8.6-0"] [cljs-react-material-ui "0.2.50" :exclusions [org.clojure/clojurescript]] - [cljsjs/prop-types "15.6.2-0"] [cljsjs/rc-slider "8.6.1-0"] [cljsjs/react-autosuggest "9.4.3-0"] - [garden "1.3.6"]]} + [garden "1.3.9"]]} :uberjar {:source-paths ["env/prod/cljs"] :main mtg-pairings-server.main :aot :all From 0599e45b6b9703158ad3228285c067d94dc30dd7 Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 1 Jul 2019 14:08:11 +0300 Subject: [PATCH 37/44] Add eastwood, kibit and cljfmt to build --- mtg-pairings-server/Dockerfile | 7 +- mtg-pairings-server/project.clj | 9 ++- .../src/clj/mtg_pairings_server/auth.clj | 10 +-- .../src/clj/mtg_pairings_server/events.clj | 8 +- .../src/clj/mtg_pairings_server/handler.clj | 78 +++++++++---------- .../mtg_pairings_server/service/decklist.clj | 20 ++--- .../service/tournament.clj | 60 +++++++------- .../cljc/mtg_pairings_server/util/mtg.cljc | 24 +++--- .../components/decklist/submit.cljs | 3 +- .../mtg_pairings_server/components/main.cljs | 13 ++-- .../components/organizer.cljs | 10 +-- .../components/tournament.cljs | 11 +-- .../mtg_pairings_server/events/decklist.cljs | 3 +- .../mtg_pairings_server/util/material_ui.cljs | 3 - .../test-resources/eastwood.clj | 11 +++ 15 files changed, 146 insertions(+), 124 deletions(-) create mode 100644 mtg-pairings-server/test-resources/eastwood.clj diff --git a/mtg-pairings-server/Dockerfile b/mtg-pairings-server/Dockerfile index fdb9401..352715e 100644 --- a/mtg-pairings-server/Dockerfile +++ b/mtg-pairings-server/Dockerfile @@ -13,13 +13,18 @@ COPY ./resources ./resources COPY ./test ./test COPY ./test-resources ./test-resources COPY decklist.cljs.edn pairings.cljs.edn ./ -RUN lein test \ +RUN lein do \ + test, \ + kibit, \ + eastwood, \ + cljfmt check \ && lein with-profile provided,prod do \ clean, \ garden once, \ minify-assets, \ fig:min pairings, \ fig:min decklist, \ + buster, \ uberjar FROM adoptopenjdk/openjdk12-openj9:alpine-jre diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index 614cd8d..5ced876 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -23,10 +23,12 @@ [com.taoensso/timbre "4.10.0"] [com.cognitect/transit-clj "0.8.313"] [com.fzakaria/slf4j-timbre "0.3.13"]] - :plugins [[lein-asset-minifier "0.4.6"] + :plugins [[lein-ancient "0.6.15"] + [lein-asset-minifier "0.4.6"] [lein-cljfmt "0.6.4"] - [lein-ancient "0.6.15"] [lein-garden "0.3.0" :exclusions [org.apache.commons/commons-compress]] + [lein-kibit "0.1.6"] + [jonase/eastwood "0.3.6"] [no.terjedahl/lein-buster "0.2.0"]] :uberjar-name "mtg-pairings.jar" @@ -80,6 +82,9 @@ sql-util/update-unique [[:inner 0]] validate-request [[:inner 0]]}} + :eastwood {:namespaces [:source-paths :test-paths] + :config-files ["test-resources/eastwood.clj"]} + :profiles {:dev {:repl-options {:init-ns mtg-pairings-server.repl :nrepl-middleware [cider.piggieback/wrap-cljs-repl]} :source-paths ["dev" "env/dev/clj" "env/dev/cljs"] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 70e0fd8..1759a0b 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -10,7 +10,7 @@ (:import (java.security MessageDigest))) (defn ^:private ->hex [bytes] - (apply str (for [b bytes] (format "%02x" b)))) + (str/join (for [b bytes] (format "%02x" b)))) (defn ^:private authenticate [username password] (when-let [user (sql-util/select-unique-or-nil db/smf-user @@ -37,10 +37,10 @@ :query-params [next :- s/Str] (if-let [user (authenticate username password)] (let [new-session (assoc (:session request) :identity user)] - (-> (redirect next :see-other) - (assoc :session new-session))) + (assoc (redirect next :see-other) + :session new-session)) (redirect (organizer-path) :see-other))) (GET "/logout" request (let [new-session (dissoc (:session request) :identity)] - (-> (redirect (organizer-path) :see-other) - (assoc :session new-session))))) + (assoc (redirect (organizer-path) :see-other) + :session new-session)))) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj index f9f65f8..ab308a1 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -131,10 +131,10 @@ (defmethod ws/event-handler :client/load-decklists [{uid :uid, ids :?data}] - (ws/send! uid [:server/decklists (->> (map decklist/get-decklist ids) - (sort-by (fn [d] - [(get-in d [:player :last-name]) - (get-in d [:player :first-name])])))])) + (ws/send! uid [:server/decklists (sort-by (fn [d] + [(get-in d [:player :last-name]) + (get-in d [:player :first-name])]) + (map decklist/get-decklist ids))])) (defmethod ws/event-handler :client/load-decklist-with-id [{uid :uid, id :?data}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 04030d6..6826ac3 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -56,47 +56,47 @@ :headers {"Content-Type" "text/html" "Cache-Control" "no-cache"}}))) -(let [index (partial index "js/pairings-main.js")] +(let [pairings-index (partial index "js/pairings-main.js")] (defroutes pairings-routes (GET "/" [] - (index)) + (pairings-index)) (GET "/tournaments" [] - (index {:page {:page :tournaments}})) + (pairings-index {:page {:page :tournaments}})) (GET "/tournaments/:id" [] :path-params [id :- s/Int] - (index {:page {:page :tournament, :id id} - :tournaments {id (tournament/client-tournament id)}})) + (pairings-index {:page {:page :tournament, :id id} + :tournaments {id (tournament/client-tournament id)}})) (GET "/tournaments/:id/pairings-:round" [] :path-params [id :- s/Int round :- s/Int] - (index {:page {:page :pairings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pairings {id {round (tournament/get-round id round)}}})) + (pairings-index {:page {:page :pairings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pairings {id {round (tournament/get-round id round)}}})) (GET "/tournaments/:id/standings-:round" [] :path-params [id :- s/Int round :- s/Int] - (index {:page {:page :standings, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :standings {id {round (tournament/standings id round false)}}})) + (pairings-index {:page {:page :standings, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :standings {id {round (tournament/standings id round false)}}})) (GET "/tournaments/:id/pods-:round" [] :path-params [id :- s/Int round :- s/Int] - (index {:page {:page :pods, :id id, :round round} - :tournaments {id (tournament/client-tournament id)} - :pods {id {round (tournament/pods id round)}}})) + (pairings-index {:page {:page :pods, :id id, :round round} + :tournaments {id (tournament/client-tournament id)} + :pods {id {round (tournament/pods id round)}}})) (GET "/tournaments/:id/seatings" [] :path-params [id :- s/Int] - (index {:page {:page :seatings, :id id} - :tournaments {id (tournament/client-tournament id)} - :seatings {id (tournament/seatings id)}})) + (pairings-index {:page {:page :seatings, :id id} + :tournaments {id (tournament/client-tournament id)} + :seatings {id (tournament/seatings id)}})) (GET "/tournaments/:id/bracket" [] :path-params [id :- s/Int] - (index {:page {:page :bracket, :id id} - :tournaments {id (tournament/client-tournament id)} - :bracket {id (tournament/bracket id)}})) - (GET "/tournaments/:id/organizer" [] (index)) - (GET "/tournaments/:id/organizer/menu" [] (index)) - (GET "/tournaments/:id/organizer/deck-construction" [] (index)))) + (pairings-index {:page {:page :bracket, :id id} + :tournaments {id (tournament/client-tournament id)} + :bracket {id (tournament/bracket id)}})) + (GET "/tournaments/:id/organizer" [] (pairings-index)) + (GET "/tournaments/:id/organizer/menu" [] (pairings-index)) + (GET "/tournaments/:id/organizer/deck-construction" [] (pairings-index)))) (defmacro validate-request [user-id tournament & body] `(if (and ~user-id (not= ~user-id (:user ~tournament))) @@ -104,43 +104,43 @@ :body "403 Forbidden"} (do ~@body))) -(let [index (partial index "js/decklist-main.js")] +(let [decklist-index (partial index "js/decklist-main.js")] (defroutes decklist-routes (GET "/decklist" [] (redirect (auth/organizer-path))) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (index {:page {:page :decklist-submit} - :decklist-editor {:tournament (decklist/get-tournament id)}})) + (decklist-index {:page {:page :decklist-submit} + :decklist-editor {:tournament (decklist/get-tournament id)}})) (GET "/decklist/organizer" request - (index {:page {:page :decklist-organizer} - :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) + (decklist-index {:page {:page :decklist-organizer} + :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) (GET "/decklist/organizer/new" [] - (index {:page {:page :decklist-organizer-tournament}})) + (decklist-index {:page {:page :decklist-organizer-tournament}})) (GET "/decklist/organizer/view/:id" request :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id) tournament (decklist/get-organizer-tournament (:tournament decklist)) user-id (get-in request [:session :identity :id])] (validate-request user-id tournament - (index {:page {:page :decklist-organizer-view, :id id} - :decklist-editor (when user-id - {:decklist decklist - :organizer-tournament tournament})})))) + (decklist-index {:page {:page :decklist-organizer-view, :id id} + :decklist-editor (when user-id + {:decklist decklist + :organizer-tournament tournament})})))) (GET "/decklist/organizer/:id" request :path-params [id :- s/Str] (let [tournament (decklist/get-organizer-tournament id) user-id (get-in request [:session :identity :id])] (validate-request user-id tournament - (index {:page {:page :decklist-organizer-tournament, :id id} - :decklist-editor (when user-id - {:organizer-tournament tournament})})))) + (decklist-index {:page {:page :decklist-organizer-tournament, :id id} + :decklist-editor (when user-id + {:organizer-tournament tournament})})))) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (add-id-to-cards "server-card__" (decklist/get-decklist id))] - (index {:page {:page :decklist-submit, :id id} - :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) - :decklist decklist}}))))) + (decklist-index {:page {:page :decklist-submit, :id id} + :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) + :decklist decklist}}))))) (def robots-txt {:status 200 diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index f512aac..c194796 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -114,13 +114,13 @@ (map #(update % :decklist count) tournaments)))) (defn get-organizer-tournament [id] - (-> (sql-util/select-unique db/decklist-tournament - (sql/fields :id :user :name :date :format :deadline) - (sql/with db/decklist - (sql/fields :id :first-name :last-name :dci :submitted) - (sql/order :submitted :asc)) - (sql/where {:id id})) - (update :decklist vec))) + (let [tournament (sql-util/select-unique db/decklist-tournament + (sql/fields :id :user :name :date :format :deadline) + (sql/with db/decklist + (sql/fields :id :first-name :last-name :dci :submitted) + (sql/order :submitted :asc)) + (sql/where {:id id}))] + (update tournament :decklist vec))) (defn format-saved-tournament [tournament] (-> tournament @@ -136,9 +136,9 @@ (cond (not existing) (do (sql/insert db/decklist-tournament - (sql/values (-> (format-saved-tournament tournament) - (assoc :id new-id - :user user-id)))) + (sql/values (assoc (format-saved-tournament tournament) + :id new-id + :user user-id))) new-id) (= user-id (:user existing)) (do (sql-util/update-unique db/decklist-tournament diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/tournament.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/tournament.clj index 8eca869..eb17d6f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/tournament.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/tournament.clj @@ -104,32 +104,32 @@ (sql/fields :tournament) (sql/aggregate (count :*) :count :tournament))] [tournament count])) - rounds (->> - (sql/select db/round - (sql/fields :tournament :num) - (sql/fields [(sql/sqlfn "not exists" (sql/subselect db/pairing - (sql/join :left db/result - (= :pairing.id :result.pairing)) - (sql/where {:pairing.round :round.id - :result.pairing nil}))) - :results]) - (sql/order :num) - (sql/where - (and (sql/sqlfn "exists" (sql/subselect db/pairing - (sql/where {:round :round.id}))) - (not :playoff)))) - (util/group-kv :tournament #(select-keys % [:num :results]))) - standings (->> - (sql/select db/standings - (sql/where {:hidden false}) - (sql/fields :tournament [:round :num]) - (sql/order :round)) - (util/group-kv :tournament #(select-keys % [:num]))) - pod-rounds (->> - (sql/select db/pod-round - (sql/fields :tournament :id) - (sql/order :id)) - (util/group-kv :tournament :id))] + rounds (util/group-kv :tournament + #(select-keys % [:num :results]) + (sql/select db/round + (sql/fields :tournament :num) + (sql/fields [(sql/sqlfn "not exists" (sql/subselect db/pairing + (sql/join :left db/result + (= :pairing.id :result.pairing)) + (sql/where {:pairing.round :round.id + :result.pairing nil}))) + :results]) + (sql/order :num) + (sql/where + (and (sql/sqlfn "exists" (sql/subselect db/pairing + (sql/where {:round :round.id}))) + (not :playoff))))) + standings (util/group-kv :tournament + #(select-keys % [:num]) + (sql/select db/standings + (sql/where {:hidden false}) + (sql/fields :tournament [:round :num]) + (sql/order :round))) + pod-rounds (util/group-kv :tournament + :id + (sql/select db/pod-round + (sql/fields :tournament :id) + (sql/order :id)))] (for [t tourns :let [id (:id t)]] (update-round-data @@ -609,10 +609,10 @@ (defn generate-draft-pods [sanction-id dropped-dcis] (let [tournament-id (sanctionid->id sanction-id) std (-> (sql-util/select-unique db/standings - (sql/where (and {:tournament tournament-id - :round (sql/subselect db/standings - (sql/aggregate (max :round) :round) - (sql/where {:tournament tournament-id}))}))) + (sql/where {:tournament tournament-id + :round (sql/subselect db/standings + (sql/aggregate (max :round) :round) + (sql/where {:tournament tournament-id}))})) :standings edn/read-string) max-round (:round (sql-util/select-unique db/round diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mtg.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mtg.cljc index 67864b7..c058637 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mtg.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mtg.cljc @@ -1,5 +1,6 @@ (ns mtg-pairings-server.util.mtg - (:require [mtg-pairings-server.util :refer [map-values]])) + (:require [clojure.set :refer [rename-keys]] + [mtg-pairings-server.util :refer [map-values]])) (defn ^:private add-result [acc {:keys [team1_wins team2_wins draws]}] (let [result {:match-points (cond @@ -35,15 +36,15 @@ :ogw (avg-of :pgw opponents)))) (defn reverse-match [match] - (clojure.set/rename-keys match {:team1 :team2 - :team2 :team1 - :team1_name :team2_name - :team2_name :team1_name - :team1_wins :team2_wins - :team2_wins :team1_wins - :team1_points :team2_points - :team2_points :team1_points - :draws :draws})) + (rename-keys match {:team1 :team2 + :team2 :team1 + :team1_name :team2_name + :team2_name :team1_name + :team1_wins :team2_wins + :team2_wins :team1_wins + :team1_points :team2_points + :team2_points :team1_points + :draws :draws})) (defn has-result? [match] (some? (:team1_wins match))) @@ -87,3 +88,6 @@ (defn valid-dci? [dci-number] (when dci-number (re-matches #"[0-9]+" dci-number))) + +(defn bye? [pairing] + (zero? (:table_number pairing))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 8c9c539..60c3329 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -227,8 +227,7 @@ selected (atom nil) on-active (fn [tab] (let [value (oget tab "props" "value")] - (reset! selected (if (= value @selected) - nil + (reset! selected (when (not= value @selected) value)))) address (atom "") address-on-change #(reset! address %) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs index 2bbe08b..a754487 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/main.cljs @@ -12,18 +12,19 @@ [mtg-pairings-server.routes.pairings :refer [tournaments-path standings-path]] [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.pairings :as subs] - [mtg-pairings-server.util :refer [format-date indexed]])) + [mtg-pairings-server.util :refer [format-date indexed]] + [mtg-pairings-server.util.mtg :refer [bye?]])) (defn pairing [data pairing?] - (let [bye? (= (:table_number data) 0)] + (let [bye (bye? data)] [ui/list-item {:class :mui-pairing :left-avatar (reagent/as-element [ui/avatar - {:background-color (if bye? + {:background-color (if bye (:accent3-color styles/palette) (:primary1-color styles/palette)) :color (:text-color styles/palette)} - (when-not bye? + (when-not bye (or (:table_number data) (:pod data)))]) :primary-text (if pairing? (str "Kierros " (:round_number data)) @@ -38,10 +39,10 @@ (str (:team1_name data) " (" (:team1_points data) ")")] [:span.hidden-mobile " - "] [:br.hidden-desktop] - [:span (if bye? + [:span (if bye (:team2_name data) (str (:team2_name data) " (" (:team2_points data) ")"))]] - (when-not bye? + (when-not bye [:span.points [:span (:team1_wins data)] [:span.hidden-mobile " - "] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs index 736df24..e9b50e1 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/organizer.cljs @@ -9,7 +9,7 @@ [mtg-pairings-server.events.pairings :as events] [mtg-pairings-server.subscriptions.pairings :as subs] [mtg-pairings-server.util :refer [cls indexed]] - [mtg-pairings-server.util.mtg :refer [duplicate-pairings]] + [mtg-pairings-server.util.mtg :refer [bye? duplicate-pairings]] [mtg-pairings-server.components.tournament :refer [standing-table]])) (defn round-select [type a rounds] @@ -101,11 +101,11 @@ :disabled (not @clock-running)}]]))) (defn pairing [data] - (let [bye? (= (:table_number data) 0)] - [:div.row.pairing.no-round {:class (when bye? :bye)} - [:span.table-number (when-not bye? (or (:table_number data) (:pod data)))] + (let [bye (bye? data)] + [:div.row.pairing.no-round {:class (when bye :bye)} + [:span.table-number (when-not bye (or (:table_number data) (:pod data)))] [:span.player (:team1_name data) [:span.points (:team1_points data)]] - [:span.player.opponent (:team2_name data) [:span.points (when-not bye? (:team2_points data))]]])) + [:span.player.opponent (:team2_name data) [:span.points (when-not bye (:team2_points data))]]])) (defn pairings [] (let [pairings (subscribe [::subs/organizer :pairings]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs index 0e1d52b..8b686f5 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/tournament.cljs @@ -13,7 +13,8 @@ [mtg-pairings-server.components.paging :refer [with-paging]] [mtg-pairings-server.components.filter :refer [filters]] [mtg-pairings-server.styles.common :as styles] - [mtg-pairings-server.util :refer [format-date indexed]])) + [mtg-pairings-server.util :refer [format-date indexed]] + [mtg-pairings-server.util.mtg :refer [bye?]])) (defn tournament-card-header ([data] @@ -107,22 +108,22 @@ children]) (defn pairing-row [pairing] - (let [bye? (= (:table_number pairing) 0)] + (let [bye (bye? pairing)] [:tr - [:td.table (when-not bye? (:table_number pairing))] + [:td.table (when-not bye (:table_number pairing))] [:td.players [:span.player1 (:team1_name pairing)] [:span.player2.hidden-desktop (:team2_name pairing)]] [:td.players2.hidden-mobile (:team2_name pairing)] - (if bye? + (if bye [:td.points [:span.team1-points (:team1_points pairing)]] [:td.points [:span.team1-points (:team1_points pairing)] [:span.hidden-mobile " - "] [:span.team2-points (:team2_points pairing)]]) - (if bye? + (if bye [:td.result] [:td.result [:span.team1-wins (:team1_wins pairing)] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 8ed16d3..0b9ef7c 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -83,8 +83,7 @@ (reg-event-db :server/decklist (fn [db [_ decklist]] - (assoc-in db [:decklist-editor :decklist] (-> (merge empty-decklist decklist) - (add-id-to-cards))))) + (assoc-in db [:decklist-editor :decklist] (add-id-to-cards (merge empty-decklist decklist))))) (reg-event-db :server/decklists (fn [db [_ decklists]] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs index 20c2473..e2b7563 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/util/material_ui.cljs @@ -8,9 +8,6 @@ (def theme (get-mui-theme {:palette palette})) -(defn get-theme [component] - (js->clj (oget component "context" "muiTheme") :keywordize-keys true)) - (defn text-field [props] (let [original-on-change (:on-change props) on-change (fn [event] diff --git a/mtg-pairings-server/test-resources/eastwood.clj b/mtg-pairings-server/test-resources/eastwood.clj new file mode 100644 index 0000000..44b1306 --- /dev/null +++ b/mtg-pairings-server/test-resources/eastwood.clj @@ -0,0 +1,11 @@ +(disable-warning + {:linter :redefd-vars + :if-inside-macroexpansion-of #{'mount.core/defstate} + :within-depth 6 + :reason "defstate generates multiple defs"}) + +(disable-warning + {:linter :constant-test + :if-inside-macroexpansion-of #{'korma.core/aggregate} + :within-depth 6 + :reason "aggregate generates constant tests"}) From e40650228ea65e2796703c3c7a4bcf19a834304b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 8 Jul 2019 11:45:45 +0300 Subject: [PATCH 38/44] Use namespaced keywords for pages --- .../src/clj/mtg_pairings_server/handler.clj | 38 +++++++---- .../src/cljs/mtg_pairings_server/core.cljs | 67 +++++++++++-------- .../cljs/mtg_pairings_server/decklist.cljs | 23 ++++--- .../mtg_pairings_server/events/decklist.cljs | 2 +- .../mtg_pairings_server/events/pairings.cljs | 18 ++--- .../mtg_pairings_server/pages/decklist.cljs | 18 ++--- .../pages/{main.cljs => pairings.cljs} | 26 ++++++- .../mtg_pairings_server/pages/tournament.cljs | 31 --------- .../cljs/mtg_pairings_server/pairings.cljs | 53 ++++++++------- .../mtg_pairings_server/routes/decklist.cljs | 14 ++-- .../mtg_pairings_server/routes/pairings.cljs | 22 +++--- 11 files changed, 165 insertions(+), 147 deletions(-) rename mtg-pairings-server/src/cljs/mtg_pairings_server/pages/{main.cljs => pairings.cljs} (70%) delete mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 6826ac3..612f6fd 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -61,37 +61,46 @@ (GET "/" [] (pairings-index)) (GET "/tournaments" [] - (pairings-index {:page {:page :tournaments}})) + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/tournaments}})) (GET "/tournaments/:id" [] :path-params [id :- s/Int] - (pairings-index {:page {:page :tournament, :id id} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/tournament + :id id} :tournaments {id (tournament/client-tournament id)}})) (GET "/tournaments/:id/pairings-:round" [] :path-params [id :- s/Int round :- s/Int] - (pairings-index {:page {:page :pairings, :id id, :round round} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/pairings + :id id + :round round} :tournaments {id (tournament/client-tournament id)} :pairings {id {round (tournament/get-round id round)}}})) (GET "/tournaments/:id/standings-:round" [] :path-params [id :- s/Int round :- s/Int] - (pairings-index {:page {:page :standings, :id id, :round round} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/standings + :id id + :round round} :tournaments {id (tournament/client-tournament id)} :standings {id {round (tournament/standings id round false)}}})) (GET "/tournaments/:id/pods-:round" [] :path-params [id :- s/Int round :- s/Int] - (pairings-index {:page {:page :pods, :id id, :round round} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/pods + :id id + :round round} :tournaments {id (tournament/client-tournament id)} :pods {id {round (tournament/pods id round)}}})) (GET "/tournaments/:id/seatings" [] :path-params [id :- s/Int] - (pairings-index {:page {:page :seatings, :id id} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/seatings + :id id} :tournaments {id (tournament/client-tournament id)} :seatings {id (tournament/seatings id)}})) (GET "/tournaments/:id/bracket" [] :path-params [id :- s/Int] - (pairings-index {:page {:page :bracket, :id id} + (pairings-index {:page {:page :mtg-pairings-server.pages.pairings/bracket + :id id} :tournaments {id (tournament/client-tournament id)} :bracket {id (tournament/bracket id)}})) (GET "/tournaments/:id/organizer" [] (pairings-index)) @@ -110,20 +119,21 @@ (redirect (auth/organizer-path))) (GET "/decklist/tournament/:id" [] :path-params [id :- s/Str] - (decklist-index {:page {:page :decklist-submit} + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/submit} :decklist-editor {:tournament (decklist/get-tournament id)}})) (GET "/decklist/organizer" request - (decklist-index {:page {:page :decklist-organizer} + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/organizer} :decklist-editor {:organizer-tournaments (decklist/get-organizer-tournaments (get-in request [:session :identity :id]))}})) (GET "/decklist/organizer/new" [] - (decklist-index {:page {:page :decklist-organizer-tournament}})) + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/organizer-tournament}})) (GET "/decklist/organizer/view/:id" request :path-params [id :- s/Str] (let [decklist (decklist/get-decklist id) tournament (decklist/get-organizer-tournament (:tournament decklist)) user-id (get-in request [:session :identity :id])] (validate-request user-id tournament - (decklist-index {:page {:page :decklist-organizer-view, :id id} + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/organizer-view + :id id} :decklist-editor (when user-id {:decklist decklist :organizer-tournament tournament})})))) @@ -132,13 +142,15 @@ (let [tournament (decklist/get-organizer-tournament id) user-id (get-in request [:session :identity :id])] (validate-request user-id tournament - (decklist-index {:page {:page :decklist-organizer-tournament, :id id} + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/organizer-tournament + :id id} :decklist-editor (when user-id {:organizer-tournament tournament})})))) (GET "/decklist/:id" [] :path-params [id :- s/Str] (let [decklist (add-id-to-cards "server-card__" (decklist/get-decklist id))] - (decklist-index {:page {:page :decklist-submit, :id id} + (decklist-index {:page {:page :mtg-pairings-server.pages.decklist/submit + :id id} :decklist-editor {:tournament (decklist/get-tournament (:tournament decklist)) :decklist decklist}}))))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs index 6afab7c..a58783f 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/core.cljs @@ -14,44 +14,53 @@ [mtg-pairings-server.subscriptions.pairings :as pairings-subs] [mtg-pairings-server.events.decklist :as decklist-events] [mtg-pairings-server.events.pairings :as pairings-events] - [mtg-pairings-server.pages.decklist :refer [decklist-organizer decklist-submit]] - [mtg-pairings-server.pages.main :refer [main-page]] - [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] - [mtg-pairings-server.pages.organizer :refer [organizer-page organizer-menu deck-construction-tables]] + [mtg-pairings-server.pages.decklist :as decklist-pages :refer [decklist-organizer decklist-submit]] + [mtg-pairings-server.pages.organizer :as organizer-pages :refer [organizer-page organizer-menu deck-construction-tables]] + [mtg-pairings-server.pages.pairings :as pairings-pages :refer [main-page tournament-page tournament-subpage tournaments-page]] [mtg-pairings-server.components.organizer :as organizer] [mtg-pairings-server.components.main :refer [header notification]] [mtg-pairings-server.util.material-ui :refer [theme]])) (defn display-header? [page] - (not (contains? #{:organizer :decklist-submit :decklist-organizer - :decklist-organizer-tournament :decklist-organizer-view} - (:page page)))) + (not (contains? #{::organizer-pages/main + ::decklist-pages/submit + ::decklist-pages/organizer + ::decklist-pages/organizer-tournament + ::decklist-pages/organizer-view} + page))) (defn current-page [] - (let [page (subscribe [::common-subs/page]) + (let [page-data (subscribe [::common-subs/page]) hide-organizer-menu? (subscribe [::pairings-subs/organizer :menu])] (fn [] - [ui/mui-theme-provider - {:mui-theme theme} - [:div - (when (and (= :organizer (:page @page)) - (not @hide-organizer-menu?)) - [organizer/menu]) - (when (display-header? @page) - [header]) - [notification] - [:div#main-container - (case (:page @page) - :main [#'main-page] - :tournaments [#'tournaments-page] - :tournament [#'tournament-page (:id @page)] - (:pairings :standings :pods :seatings :bracket) [#'tournament-subpage (:id @page) (:page @page) (:round @page)] - :organizer [#'organizer-page] - :organizer-menu [#'organizer-menu] - :organizer-deck-construction [#'deck-construction-tables] - :decklist-submit [#'decklist-submit] - (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer @page] - nil)]]]))) + (let [{:keys [page id round]} @page-data] + [ui/mui-theme-provider + {:mui-theme theme} + [:div + (when (and (= ::organizer-pages/main page) + (not @hide-organizer-menu?)) + [organizer/menu]) + (when (display-header? page) + [header]) + [notification] + [:div#main-container + (case page + ::pairings-pages/main [#'main-page] + ::pairings-pages/tournaments [#'tournaments-page] + ::pairings-pages/tournament [#'tournament-page id] + (::pairings-pages/pairings + ::pairings-pages/standings + ::pairings-pages/pods + ::pairings-pages/seatings + ::pairings-pages/bracket) [#'tournament-subpage id page round] + ::organizer-pages/main [#'organizer-page] + ::organizer-pages/menu [#'organizer-menu] + ::organizer-pages/deck-construction [#'deck-construction-tables] + ::decklist-pages/submit [#'decklist-submit] + (::decklist-pages/organizer + ::decklist-pages/organizer-tournament + ::decklist-pages/organizer-view) [#'decklist-organizer id page] + nil)]]])))) (defn mount-root [] (reagent/render [current-page] (.getElementById js/document "app"))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs index 9e1c06e..4a19cea 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/decklist.cljs @@ -7,22 +7,25 @@ [secretary.core :as secretary :include-macros true] [accountant.core :as accountant] [mtg-pairings-server.events.decklist :as events] - [mtg-pairings-server.pages.decklist :refer [decklist-organizer decklist-submit]] + [mtg-pairings-server.pages.decklist :as decklist-pages :refer [decklist-organizer decklist-submit]] [mtg-pairings-server.routes.decklist :as routes] [mtg-pairings-server.subscriptions.common :as subs] [mtg-pairings-server.util.material-ui :refer [theme]])) (defn current-page [] - (let [page (subscribe [::subs/page])] + (let [page-data (subscribe [::subs/page])] (fn [] - [ui/mui-theme-provider - {:mui-theme theme} - [:div - [:div#main-container - (case (:page @page) - :decklist-submit [#'decklist-submit] - (:decklist-organizer :decklist-organizer-tournament :decklist-organizer-view) [#'decklist-organizer @page] - nil)]]]))) + (let [{:keys [page id]} @page-data] + [ui/mui-theme-provider + {:mui-theme theme} + [:div + [:div#main-container + (case page + ::decklist-pages/submit [#'decklist-submit] + (::decklist-pages/organizer + ::decklist-pages/organizer-tournament + ::decklist-pages/organizer-view) [#'decklist-organizer id page] + nil)]]])))) (defn mount-root [] (reagent/render [current-page] (.getElementById js/document "app"))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 0b9ef7c..93dd4ef 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -26,7 +26,7 @@ (reg-event-db ::initialize (fn [db _] - (merge db (initial-db)))) + (util/deep-merge db (initial-db)))) (defn connect! [] (ws/send! [:client/connect-decklist])) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs index 48993da..773fc0c 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs @@ -25,7 +25,7 @@ :pairings {:sort-key :table_number} :pods {:sort-key :pod} :seatings {:sort-key :table_number} - :page {:page :main + :page {:page :mtg-pairings-server.pages.pairings/main :id nil :round nil} :logged-in-user (local-storage/fetch :user) @@ -42,7 +42,7 @@ (reg-event-db ::initialize (fn [db _] - (merge db (initial-db)))) + (deep-merge db (initial-db)))) (defn connect! [] (ws/send! [:client/connect-pairings]) @@ -284,8 +284,8 @@ (fn [{:keys [db]} [_ action value]] (let [{:keys [id page]} (:page db)] (case page - :organizer (resolve-organizer-action db id action value) - :organizer-menu (send-organizer-action db id action value))))) + :mtg-pairings-server.pages.organizer/main (resolve-organizer-action db id action value) + :mtg-pairings-server.pages.organizer/menu (send-organizer-action db id action value))))) (reg-fx :popup (fn [id] @@ -299,14 +299,14 @@ (fn [{:keys [db]} _] (let [{:keys [id page]} (:page db)] (case page - :organizer {:db (assoc-in db [:organizer :menu] true) - :popup (get-in db [:page :id])} - :organizer-menu {:store [["organizer" id] {:action :close-popup}] - :close-popup nil})))) + :mtg-pairings-server.pages.organizer/main {:db (assoc-in db [:organizer :menu] true) + :popup (get-in db [:page :id])} + :mtg-pairings-server.pages.organizer/menu {:store [["organizer" id] {:action :close-popup}] + :close-popup nil})))) (reg-event-fx ::local-storage-updated (fn [{:keys [db]} [_ k v]] - (when (and (= (get-in db [:page :page]) :organizer) + (when (and (= (get-in db [:page :page]) :mtg-pairings-server.pages.organizer/main) (= k ["organizer" (get-in db [:page :id])])) (resolve-organizer-action db (get-in db [:page :id]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 4ea2b01..6293a3a 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -13,17 +13,17 @@ (defn loading-indicator [] [ui/circular-progress {:size 36 - :style {:margin "24px"}}]) + :style {:margin "24px"}}]) -(defn decklist-organizer [page] +(defn decklist-organizer [id page] (let [user (subscribe [::subs/user])] - (fn decklist-organizer-render [page] + (fn decklist-organizer-render [id page] (case @user nil [loading-indicator] false [organizer/login] - (case (:page page) - :decklist-organizer-tournament [organizer/tournament (:id page)] - :decklist-organizer [organizer/all-tournaments] - :decklist-organizer-view (if (:id page) - [organizer/view-decklist] - [organizer/view-decklists])))))) + (case page + ::organizer-tournament [organizer/tournament id] + ::organizer [organizer/all-tournaments] + ::organizer-view (if id + [organizer/view-decklist] + [organizer/view-decklists])))))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/pairings.cljs similarity index 70% rename from mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs rename to mtg-pairings-server/src/cljs/mtg_pairings_server/pages/pairings.cljs index 661c932..b975e39 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/main.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/pairings.cljs @@ -1,9 +1,9 @@ -(ns mtg-pairings-server.pages.main +(ns mtg-pairings-server.pages.pairings (:require [re-frame.core :refer [subscribe]] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] [mtg-pairings-server.components.main :refer [own-tournament pairing]] - [mtg-pairings-server.components.tournament :refer [newest-tournaments-list]] + [mtg-pairings-server.components.tournament :refer [newest-tournaments-list tournament-list tournament-card-header tournament pairings standings pods seatings bracket]] [mtg-pairings-server.subscriptions.pairings :as subs])) (defn get-latest-pairing [player-tournaments] @@ -45,3 +45,25 @@ ^{:key [:tournament (:id t)]} [own-tournament t])] [newest-tournaments-list])))) + +(defn tournaments-page [] + [tournament-list]) + +(defn tournament-page [id] + (let [data (subscribe [::subs/tournament id])] + (fn tournament-page-render [id] + [tournament @data]))) + +(defn tournament-subpage [id type round] + (let [tournament (subscribe [::subs/tournament id])] + (fn tournament-subpage-render [id type round] + [ui/card + [tournament-card-header @tournament] + [ui/card-text + {:style {:padding-top 0}} + (case type + ::pairings [pairings id round] + ::standings [standings id round] + ::pods [pods id round] + ::seatings [seatings id] + ::bracket [bracket id])]]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs deleted file mode 100644 index 8415c2d..0000000 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/tournament.cljs +++ /dev/null @@ -1,31 +0,0 @@ -(ns mtg-pairings-server.pages.tournament - (:require [re-frame.core :refer [subscribe]] - [cljsjs.material-ui] - [cljs-react-material-ui.core] - [cljs-react-material-ui.reagent :as ui] - [mtg-pairings-server.components.tournament :refer [tournament-list tournament-card-header tournament pairings standings pods seatings bracket]] - [mtg-pairings-server.routes.pairings :refer [tournament-path]] - [mtg-pairings-server.subscriptions.pairings :as subs] - [mtg-pairings-server.util :refer [format-date]])) - -(defn tournaments-page [] - [tournament-list]) - -(defn tournament-page [id] - (let [data (subscribe [::subs/tournament id])] - (fn tournament-page-render [id] - [tournament @data]))) - -(defn tournament-subpage [id type round] - (let [tournament (subscribe [::subs/tournament id])] - (fn tournament-subpage-render [id type round] - [ui/card - [tournament-card-header @tournament] - [ui/card-text - {:style {:padding-top 0}} - (case type - :pairings [pairings id round] - :standings [standings id round] - :pods [pods id round] - :seatings [seatings id] - :bracket [bracket id])]]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pairings.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pairings.cljs index 048c3e0..9174aa1 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pairings.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pairings.cljs @@ -9,9 +9,8 @@ [mtg-pairings-server.components.main :refer [header notification]] [mtg-pairings-server.components.organizer :as organizer] [mtg-pairings-server.events.pairings :as events] - [mtg-pairings-server.pages.main :refer [main-page]] - [mtg-pairings-server.pages.tournament :refer [tournament-page tournament-subpage tournaments-page]] - [mtg-pairings-server.pages.organizer :refer [organizer-page organizer-menu deck-construction-tables]] + [mtg-pairings-server.pages.pairings :as pairings-pages :refer [main-page tournament-page tournament-subpage tournaments-page]] + [mtg-pairings-server.pages.organizer :as organizer-pages :refer [organizer-page organizer-menu deck-construction-tables]] [mtg-pairings-server.routes.pairings] [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.pairings :as subs] @@ -19,32 +18,36 @@ [mtg-pairings-server.util.material-ui :refer [theme]])) (defn display-header? [page] - (not (contains? #{:organizer} - (:page page)))) + (not (contains? #{::organizer-pages/main} page))) (defn current-page [] - (let [page (subscribe [::common-subs/page]) + (let [page-data (subscribe [::common-subs/page]) hide-organizer-menu? (subscribe [::subs/organizer :menu])] (fn [] - [ui/mui-theme-provider - {:mui-theme theme} - [:div - (when (and (= :organizer (:page @page)) - (not @hide-organizer-menu?)) - [organizer/menu]) - (when (display-header? @page) - [header]) - [notification] - [:div#main-container - (case (:page @page) - :main [#'main-page] - :tournaments [#'tournaments-page] - :tournament [#'tournament-page (:id @page)] - (:pairings :standings :pods :seatings :bracket) [#'tournament-subpage (:id @page) (:page @page) (:round @page)] - :organizer [#'organizer-page] - :organizer-menu [#'organizer-menu] - :organizer-deck-construction [#'deck-construction-tables] - nil)]]]))) + (let [{:keys [page id round]} @page-data] + [ui/mui-theme-provider + {:mui-theme theme} + [:div + (when (and (= ::organizer-pages/main page) + (not @hide-organizer-menu?)) + [organizer/menu]) + (when (display-header? page) + [header]) + [notification] + [:div#main-container + (case page + ::pairings-pages/main [#'main-page] + ::pairings-pages/tournaments [#'tournaments-page] + ::pairings-pages/tournament [#'tournament-page id] + (::pairings-pages/pairings + ::pairings-pages/standings + ::pairings-pages/pods + ::pairings-pages/seatings + ::pairings-pages/bracket) [#'tournament-subpage id page round] + ::organizer-pages/main [#'organizer-page] + ::organizer-pages/menu [#'organizer-menu] + ::organizer-pages/deck-construction [#'deck-construction-tables] + nil)]]])))) (defn mount-root [] (reagent/render [current-page] (.getElementById js/document "app"))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs index 9ed167e..ae80fc2 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs @@ -15,25 +15,25 @@ (defn initialize-routes [prefix] (when-not @initialized? (secretary/defroute organizer-path (str prefix "/organizer") [] - (dispatch-page :decklist-organizer)) + (dispatch-page :mtg-pairings-server.pages.decklist/organizer)) (secretary/defroute organizer-print-path (str prefix "/organizer/print") [] - (dispatch-page :decklist-organizer-view)) + (dispatch-page :mtg-pairings-server.pages.decklist/organizer-view)) (secretary/defroute organizer-view-path (str prefix "/organizer/view/:id") [id] - (dispatch-page :decklist-organizer-view id)) + (dispatch-page :mtg-pairings-server.pages.decklist/organizer-view id)) (secretary/defroute organizer-new-tournament-path (str prefix "/organizer/new") [] (dispatch [:mtg-pairings-server.events.decklist/clear-tournament]) - (dispatch-page :decklist-organizer-tournament)) + (dispatch-page :mtg-pairings-server.pages.decklist/organizer-tournament)) (secretary/defroute organizer-tournament-path (str prefix "/organizer/:id") [id] - (dispatch-page :decklist-organizer-tournament id)) + (dispatch-page :mtg-pairings-server.pages.decklist/organizer-tournament id)) (secretary/defroute new-decklist-path (str prefix "/tournament/:id") [id] - (dispatch-page :decklist-submit)) + (dispatch-page :mtg-pairings-server.pages.decklist/submit)) (secretary/defroute old-decklist-path (str prefix "/:id") [id] - (dispatch-page :decklist-submit id)) + (dispatch-page :mtg-pairings-server.pages.decklist/submit id)) (reset! initialized? true)))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs index c22f061..3c09bd2 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/pairings.cljs @@ -5,56 +5,56 @@ [mtg-pairings-server.routes.common :refer [dispatch-page]])) (secretary/defroute "/" [] - (dispatch-page :main)) + (dispatch-page :mtg-pairings-server.pages.pairings/main)) (secretary/defroute tournaments-path "/tournaments" [] - (dispatch-page :tournaments)) + (dispatch-page :mtg-pairings-server.pages.pairings/tournaments)) (secretary/defroute tournament-path "/tournaments/:id" [id] (let [id (js/parseInt id)] (dispatch [::events/load-tournament id]) - (dispatch-page :tournament id))) + (dispatch-page :mtg-pairings-server.pages.pairings/tournament id))) (secretary/defroute pairings-path "/tournaments/:id/pairings-:round" [id round] (let [id (js/parseInt id) round (js/parseInt round)] (dispatch [::events/load-pairings id round]) - (dispatch-page :pairings id round))) + (dispatch-page :mtg-pairings-server.pages.pairings/pairings id round))) (secretary/defroute standings-path "/tournaments/:id/standings-:round" [id round] (let [id (js/parseInt id) round (js/parseInt round)] (dispatch [::events/load-standings id round]) - (dispatch-page :standings id round))) + (dispatch-page :mtg-pairings-server.pages.pairings/standings id round))) (secretary/defroute pods-path "/tournaments/:id/pods-:round" [id round] (let [id (js/parseInt id) round (js/parseInt round)] (dispatch [::events/load-pods id round]) - (dispatch-page :pods id round))) + (dispatch-page :mtg-pairings-server.pages.pairings/pods id round))) (secretary/defroute seatings-path "/tournaments/:id/seatings" [id] (let [id (js/parseInt id)] (dispatch [::events/load-seatings id]) - (dispatch-page :seatings id))) + (dispatch-page :mtg-pairings-server.pages.pairings/seatings id))) (secretary/defroute bracket-path "/tournaments/:id/bracket" [id] (let [id (js/parseInt id)] (dispatch [::events/load-bracket id]) - (dispatch-page :bracket id))) + (dispatch-page :mtg-pairings-server.pages.pairings/bracket id))) (secretary/defroute organizer-path "/tournaments/:id/organizer" [id] (let [id (js/parseInt id)] (dispatch [::events/load-organizer-tournament id]) - (dispatch-page :organizer id))) + (dispatch-page :mtg-pairings-server.pages.organizer/main id))) (secretary/defroute organizer-menu-path "/tournaments/:id/organizer/menu" [id] (let [id (js/parseInt id)] (dispatch [::events/load-organizer-tournament id]) - (dispatch-page :organizer-menu id))) + (dispatch-page :mtg-pairings-server.pages.organizer/menu id))) (secretary/defroute deck-construction-path "/tournaments/:id/organizer/deck-construction" [id] (let [id (js/parseInt id)] (dispatch [::events/load-deck-construction id]) - (dispatch-page :organizer-deck-construction id))) + (dispatch-page :mtg-pairings-server.pages.organizer/deck-construction id))) From 8f4f5cd680658b77b8eb928eccf778a58e372ffe Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 8 Jul 2019 13:10:02 +0300 Subject: [PATCH 39/44] Better error handling for decklist submit --- .../src/clj/mtg_pairings_server/events.clj | 2 +- .../mtg_pairings_server/service/decklist.clj | 40 ++-- .../mtg_pairings_server/styles/decklist.clj | 16 +- .../src/cljc/mtg_pairings_server/util.cljc | 4 + .../components/decklist/submit.cljs | 172 +++++++++++------- .../mtg_pairings_server/events/decklist.cljs | 21 ++- .../subscriptions/decklist.cljs | 12 +- 7 files changed, 161 insertions(+), 106 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj index ab308a1..5bc43d5 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -140,7 +140,7 @@ [{uid :uid, id :?data}] (if-let [decklist (decklist/get-decklist id)] (ws/send! uid [:server/decklist (dissoc decklist :id :player)]) - (ws/send! uid [:server/decklist-load-error "Pakkalistaa ei löytynyt"]))) + (ws/send! uid [:server/decklist-load-error :not-found]))) (defmethod ws/event-handler :client/decklist-card-suggestions [{[prefix format] :?data, reply-fn :?reply-fn}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index c194796..429fff7 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -48,25 +48,25 @@ (mapv #(select-keys % [:name :quantity])))) (defn get-decklist [id] - (let [decklist (sql-util/select-unique db/decklist - (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) - (sql/with db/decklist-card - (sql/fields :maindeck :index :quantity) - (sql/with db/card - (sql/fields :name))) - (sql/where {:id id})) - main (format-cards (:decklist_card decklist) true) - side (format-cards (:decklist_card decklist) false) - player (select-keys decklist [:deck-name :first-name :last-name :email :dci]) - email-disabled? (not (str/blank? (:email player)))] - {:id id - :tournament (:tournament decklist) - :main main - :side side - :count {:main (transduce (map :quantity) + 0 main) - :side (transduce (map :quantity) + 0 side)} - :board :main - :player (assoc player :email-disabled? email-disabled?)})) + (when-let [decklist (sql-util/select-unique-or-nil db/decklist + (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) + (sql/with db/decklist-card + (sql/fields :maindeck :index :quantity) + (sql/with db/card + (sql/fields :name))) + (sql/where {:id id}))] + (let [main (format-cards (:decklist_card decklist) true) + side (format-cards (:decklist_card decklist) false) + player (select-keys decklist [:deck-name :first-name :last-name :email :dci]) + email-disabled? (not (str/blank? (:email player)))] + {:id id + :tournament (:tournament decklist) + :main main + :side side + :count {:main (transduce (map :quantity) + 0 main) + :side (transduce (map :quantity) + 0 side)} + :board :main + :player (assoc player :email-disabled? email-disabled?)}))) (defn save-decklist [tournament decklist] (transaction @@ -148,7 +148,7 @@ (defn parse-decklist-row [row format] (when-not (or (str/blank? row) - (re-matches #"^Maindeck.*" row)) + (re-matches #"^[Mm]aindeck.*" row)) (if-let [[_ quantity name] (re-matches #"(\d+)\s+(.+)" (str/trim row))] (if-let [{:keys [name legal]} (sql-util/select-unique-or-nil db/card (sql/fields :name [format :legal]) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index a31d212..28b7a03 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -18,7 +18,7 @@ :vertical-align :top}] [:#player-info [:.full-width :.half-width - {:height (px 100)}] + {:height (px 72)}] [:.full-width {:width "100%"}] [:.half-width @@ -31,9 +31,12 @@ [:.decklist-import {:margin "12px 0"} [:.info :.form - {:display :inline-block - :width (percent 50) - :padding (px 12)}]]) + {:display :inline-block + :vertical-align :top + :width (percent 50) + :padding (px 12)} + [:.text-field-container + {:height (px 72)}]]]) (when-mobile [:#player-info [:.full-width :.half-width @@ -54,7 +57,10 @@ [:th.actions :td.actions {:width (px 48)}] [:th.error :td.error - {:width (px 48)}]]]]) + {:width (px 48)}]]] + [:.decklist-import + [:.decklist-import-error + {:color (color :error-color)}]]]) (def grey-border {:style :solid :width (px 1) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 08b8b2f..0574e5d 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -163,3 +163,7 @@ (str (oget js/window "location" "protocol") "//" (oget js/window "location" "host")))) + +(defn valid-email? [email] + (some->> email + (re-matches #".+@.+"))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 60c3329..42f7032 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -1,5 +1,6 @@ (ns mtg-pairings-server.components.decklist.submit (:require [reagent.core :as reagent :refer [atom]] + [reagent.ratom :refer [make-reaction]] [re-frame.core :refer [subscribe dispatch]] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] @@ -12,7 +13,7 @@ [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.decklist :as subs] - [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host]] + [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host valid-email?]] [mtg-pairings-server.util.decklist :refer [->text]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] [mtg-pairings-server.util.material-ui :refer [text-field]] @@ -88,7 +89,7 @@ (defn error-icon [error] (let [icon [icons/alert-warning {:title error - :style {:color "#f44336" + :style {:color (:error-color styles/palette) :vertical-align :top}}]] (if error [tooltip {:label error} @@ -122,7 +123,7 @@ :primary-text i :inner-div-style {:padding "0 6px"}}])))] [ui/table-row-column {:class-name :card - :style {:font-size "16px" + :style {:font-size "16px" :padding-left (if @mobile? 0 "24px")}} name] [ui/table-row-column {:class-name :actions @@ -171,7 +172,7 @@ ^{:key (str id "--tr")} [decklist-table-row board card (or error (get error-cards name))])]]])))) -(defn player-info [decklist] +(defn player-info [player] (let [set-first-name #(dispatch [::events/update-player-info :first-name %]) set-last-name #(dispatch [::events/update-player-info :last-name %]) set-deck-name #(dispatch [::events/update-player-info :deck-name %]) @@ -183,10 +184,10 @@ [text-field {:on-change set-deck-name :floating-label-text "Pakan nimi" :full-width true - :value (get-in @decklist [:player :deck-name]) + :value (:deck-name @player) :style {:vertical-align :top}}]] [:div.half-width.left - (let [value (get-in @decklist [:player :first-name])] + (let [value (:first-name @player)] [text-field {:on-change set-first-name :floating-label-text "Etunimi" :full-width true @@ -195,7 +196,7 @@ "Etunimi on pakollinen") :style {:vertical-align :top}}])] [:div.half-width.right - (let [value (get-in @decklist [:player :last-name])] + (let [value (:last-name @player)] [text-field {:on-change set-last-name :floating-label-text "Sukunimi" :full-width true @@ -204,7 +205,7 @@ "Etunimi on pakollinen") :style {:vertical-align :top}}])] [:div.half-width.left - (let [value (get-in @decklist [:player :dci])] + (let [value (:dci @player)] [text-field {:on-change set-dci :floating-label-text "DCI-numero" :full-width true @@ -213,82 +214,112 @@ "Virheellinen DCI-numero") :style {:vertical-align :top}}])] [:div.half-width.right - [text-field {:on-change set-email - :floating-label-text "Sähköposti" - :full-width true - :value (get-in @decklist [:player :email]) - :style {:vertical-align :top} - :disabled (get-in @decklist [:player :email-disabled?]) - :title (when (get-in @decklist [:player :email-disabled?]) - "Tästä pakkalistasta on jo lähetetty sähköpostiviesti.")}]]]))) + (let [value (:email @player)] + [text-field {:on-change set-email + :floating-label-text "Sähköposti" + :full-width true + :value value + :error-text (when-not (or (str/blank? value) + (valid-email? value)) + "Virheellinen sähköposti") + :style {:vertical-align :top} + :disabled (:email-disabled? @player) + :title (when (:email-disabled? @player) + "Tästä pakkalistasta on jo lähetetty sähköpostiviesti.")}])]]))) + +(defn valid-code [address] + (when address + (let [[_ code] (re-find #"/([A-z0-9_-]{22})$" address)] + code))) (defn decklist-import [] (let [mobile? (subscribe [::common-subs/mobile?]) + loaded? (subscribe [::subs/loaded?]) selected (atom nil) on-active (fn [tab] (let [value (oget tab "props" "value")] (reset! selected (when (not= value @selected) value)))) address (atom "") + code (make-reaction #(valid-code @address)) address-on-change #(reset! address %) - import-from-address #(dispatch [::events/import-address @address]) + import-from-address #(dispatch [::events/import-address @code]) decklist (atom "") decklist-on-change #(reset! decklist %) import-decklist (fn [] (dispatch [::events/import-text @decklist]) (reset! selected nil)) + import-error (subscribe [::subs/error :import-address]) border (styles/border "1px" :solid (styles/palette :light-grey))] - - (fn decklist-import-render [] - [:div.decklist-import - [ui/tabs {:value @selected - :content-container-style (when @selected - {:border-bottom border - :border-left (when-not @mobile? border) - :border-right (when-not @mobile? border)})} - [ui/tab {:label "Lataa aiempi lista" - :value "load-previous" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa aiempi lista"] - [:p - "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " - [:span.address "https://decklist.pairings.fi/abcd..."] - ")."]] - [:div.form - [text-field {:on-change address-on-change - :floating-label-text "Osoite" - :full-width true}] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (str/blank? @address) - :on-click import-from-address}]]] - [ui/tab {:label "Lataa tekstilista" - :value "load-text" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa tekstimuotoinen lista"] - [:p - "Kopioi tekstikenttään tekstimuotoinen lista." - "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] - [:pre - "4 Lightning Bolt\n4 Chain Lightning\n..."] - [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] - [:div.form - [text-field {:on-change decklist-on-change - :multi-line true - :rows 7 - :rows-max 7 - :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} - :full-width true}] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (str/blank? @decklist) - :on-click import-decklist}]]]]]))) + (reagent/create-class + {:component-did-mount (fn [] + (add-watch loaded? ::decklist-import + (fn [_ _ _ new] + (when new + (reset! selected nil) + (reset! address ""))))) + :component-will-unmount (fn [] + (remove-watch loaded? ::decklist-import)) + :reagent-render (fn decklist-import-render [] + [:div.decklist-import + [ui/tabs {:value @selected + :content-container-style (when @selected + {:border-bottom border + :border-left (when-not @mobile? border) + :border-right (when-not @mobile? border)})} + [ui/tab {:label "Lataa aiempi lista" + :value "load-previous" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa aiempi lista"] + [:p + "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " + [:span.address "https://decklist.pairings.fi/abcd..."] + ")."]] + [:div.form + [:div.text-field-container + [text-field {:on-change address-on-change + :floating-label-text "Osoite" + :full-width true + :error-text (when-not (or (str/blank? @address) + @code) + "Virheellinen osoite")}]] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (nil? @code) + :on-click import-from-address}] + (when @import-error + [:p.decklist-import-error + (case @import-error + :not-found "Pakkalistaa ei löytynyt" + "Virhe pakkalistan latauksessa")])]] + [ui/tab {:label "Lataa tekstilista" + :value "load-text" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa tekstimuotoinen lista"] + [:p + "Kopioi tekstikenttään tekstimuotoinen lista." + "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] + [:pre + "4 Lightning Bolt\n4 Chain Lightning\n..."] + [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] + [:div.form + [text-field {:on-change decklist-on-change + :multi-line true + :rows 7 + :rows-max 7 + :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} + :full-width true + :name :text-decklist}] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (str/blank? @decklist) + :on-click import-decklist}]]]]])}))) (defn error-list [errors] [ui/list @@ -300,6 +331,7 @@ (defn decklist-submit [] (let [decklist (subscribe [::subs/decklist]) + player (reagent/cursor decklist [:player]) select-main #(dispatch [::events/select-board :main]) select-side #(dispatch [::events/select-board :side]) button-style {:position :relative @@ -309,7 +341,7 @@ tournament (subscribe [::subs/tournament]) saving? (subscribe [::subs/saving?]) saved? (subscribe [::subs/saved?]) - error? (subscribe [::subs/error?]) + error? (subscribe [::subs/error :save]) page (subscribe [::common-subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] @@ -349,7 +381,7 @@ [decklist-table decklist :side]] [decklist-import] [:h3 "Pelaajan tiedot"] - [player-info decklist] + [player-info player] [ui/raised-button {:label "Tallenna" :on-click save-decklist diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 93dd4ef..94b7385 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -70,7 +70,7 @@ [:decklist-editor :organizer-tournament :id] id [:decklist-editor :saved] true [:decklist-editor :saving] false - [:decklist-editor :error] false) + [:decklist-editor :error :save] false) :navigate (routes/organizer-tournament-path {:id id})})) (reg-event-fx ::load-decklist @@ -83,7 +83,9 @@ (reg-event-db :server/decklist (fn [db [_ decklist]] - (assoc-in db [:decklist-editor :decklist] (add-id-to-cards (merge empty-decklist decklist))))) + (util/assoc-in-many db + [:decklist-editor :decklist] (add-id-to-cards (merge empty-decklist decklist)) + [:decklist-editor :loaded] true))) (reg-event-db :server/decklists (fn [db [_ decklists]] @@ -98,16 +100,23 @@ (update db :decklist-editor merge {:organizer-tournament nil :saving false :saved false - :error false}))) + :error {:save false + :import-address nil}}))) (reg-event-db ::clear-status (fn [db [_ key]] (assoc-in db [:decklist-editor key] false))) (reg-event-fx ::import-address - (fn [_ [_ address]] - (when-let [[_ code] (re-find #"/decklist/([A-z0-9_-]{22})$" address)] - {:ws-send [:client/load-decklist-with-id code]}))) + (fn [{:keys [db]} [_ code]] + {:db (util/assoc-in-many db + [:decklist-editor :error :import-address] nil + [:decklist-editor :loaded] false) + :ws-send [:client/load-decklist-with-id code]})) + +(reg-event-db :server/decklist-load-error + (fn [db [_ error]] + (assoc-in db [:decklist-editor :error :import-address] error))) (defn ^:private add-card [{:keys [board] :as decklist} name] (if (some #(= name (:name %)) (get decklist board)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs index 6fc655a..bfa0f5b 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs @@ -13,10 +13,6 @@ (fn [db _] (get-in db [:decklist-editor :saved]))) -(reg-sub ::error? - (fn [db _] - (get-in db [:decklist-editor :error]))) - (reg-sub ::decklist (fn [db _] (get-in db [:decklist-editor :decklist]))) @@ -36,3 +32,11 @@ (reg-sub ::user (fn [db _] (get-in db [:decklist-editor :user]))) + +(reg-sub ::error + (fn [db [_ k]] + (get-in db [:decklist-editor :error k]))) + +(reg-sub ::loaded? + (fn [db _] + (get-in db [:decklist-editor :loaded]))) From bfebf779145555996062bd9751a323a9616f861c Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 8 Jul 2019 14:45:49 +0300 Subject: [PATCH 40/44] Improve usability of decklist organizer --- .../src/clj/mtg_pairings_server/auth.clj | 4 +- .../src/clj/mtg_pairings_server/events.clj | 7 +- .../mtg_pairings_server/styles/decklist.clj | 66 ++++--- .../components/decklist/organizer.cljs | 186 +++++++++++------- .../components/decklist/submit.cljs | 2 +- .../mtg_pairings_server/events/decklist.cljs | 58 ++++-- .../mtg_pairings_server/pages/decklist.cljs | 20 +- .../mtg_pairings_server/routes/decklist.cljs | 2 + .../subscriptions/decklist.cljs | 22 ++- 9 files changed, 242 insertions(+), 125 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj index 1759a0b..b5cddfb 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/auth.clj @@ -17,8 +17,8 @@ (sql/fields [:id_member :id] [:passwd :hash]) (sql/where {:member_name username}))] (let [input (str (str/lower-case username) password) - md (MessageDigest/getInstance "SHA-1") - _ (.update md (.getBytes input "UTF-8")) + md (doto (MessageDigest/getInstance "SHA-1") + (.update (.getBytes input "UTF-8"))) sha1 (->hex (.digest md))] (when (= sha1 (:hash user)) {:id (:id user) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj index 5bc43d5..6c4c8be 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/events.clj @@ -114,6 +114,11 @@ (when (= (get-in ring-req [:session :identity :id]) (:user tournament)) (ws/send! uid [:server/decklist-organizer-tournament tournament])))) +(defmethod ws/event-handler :client/decklist-organizer-tournaments + [{uid :uid, ring-req :ring-req}] + (let [tournaments (decklist/get-organizer-tournaments (get-in ring-req [:session :identity :id]))] + (ws/send! uid [:server/decklist-organizer-tournaments tournaments]))) + (defmethod ws/event-handler :client/save-decklist-organizer-tournament [{uid :uid, tournament :?data, ring-req :ring-req}] (try @@ -123,7 +128,7 @@ (ws/send! uid [:server/organizer-tournament-saved id]))) (catch Exception e (log/error e "Error saving tournament") - (ws/send! uid [:server/decklist-error])))) + (ws/send! uid [:server/decklist-tournament-error])))) (defmethod ws/event-handler :client/load-decklist [{uid :uid, id :?data}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 28b7a03..a102bfe 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -283,35 +283,43 @@ :padding "0 24px"}) (defstyles organizer - [:#decklist-organizer-tournaments - {:margin "12px 24px"} - [:.tournaments - [:th.date :td.date - {:width (px 130)}] - [:th.deadline :td.deadline - {:width (px 160)}] - [:th.decklists :td.decklists - {:width (px 135)}] - [:.tournament-link - table-row-link]]] - [:#decklist-organizer-tournament - {:margin "12px 24px"} - [:.field - {:display :inline-block - :vertical-align :top - :margin-right "24px"}] - [:.notices - {:display :inline-block}] - [:.decklists - [:th.dci :td.dci - {:width (px 150)}] - [:th.name :td.name - {:width (px 400)}] - [:.decklist-link - table-row-link]]] - [:#decklist-organizer-login - {:margin "12px 24px"}] - organizer-decklist) + [:#decklist-organizer + [:#decklist-organizer-tournaments + {:margin "12px 24px"} + [:.tournaments + [:th.date :td.date + {:width (px 130)}] + [:th.deadline :td.deadline + {:width (px 160)}] + [:th.decklists :td.decklists + {:width (px 135)}] + [:.tournament-link + table-row-link]]] + [:#decklist-organizer-tournament + {:margin "12px 24px"} + [:.fields + {:margin-bottom (px 24)} + [:.field + {:display :inline-block + :vertical-align :top + :margin-right (px 24) + :height (px 72)}]] + + [:.notices + {:display :inline-block}] + [:.decklists + [:th.dci :td.dci + {:width (px 150)}] + [:th.name :td.name + {:width (px 400)}] + [:.decklist-link + table-row-link]]] + [:#decklist-organizer-login + {:margin "12px 24px"}] + organizer-decklist + (when-print + [:.decklist-organizer-header + {:display "none !important"}])]) (defstyles styles organizer diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index 0cd9363..e22bad4 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -5,6 +5,7 @@ [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] [cljs-time.coerce :as coerce] + [clojure.string :as str] [oops.core :refer [oget]] [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.decklist :as routes] @@ -13,6 +14,35 @@ [mtg-pairings-server.util :refer [format-date format-date-time to-local-date indexed get-host]] [mtg-pairings-server.util.material-ui :refer [text-field]])) +(defn header [] + (let [logged-in? (subscribe [::subs/user]) + button-style {:margin-left "12px" + :margin-right "12px"}] + + (fn header-render [] + (let [disabled? (not @logged-in?)] + [ui/toolbar {:class-name :decklist-organizer-header + :style {:background-color (palette :primary1-color)}} + [ui/toolbar-group {:first-child true} + [ui/raised-button {:href (routes/organizer-path) + :label "Kaikki turnaukset" + :style button-style + :disabled disabled?}] + [ui/raised-button {:href (routes/organizer-new-tournament-path) + :label "Uusi turnaus" + :icon (reagent/as-element [icons/content-add + {:style {:height "36px" + :width "30px" + :padding "6px 0 6px 6px" + :vertical-align :top}}]) + :style button-style + :disabled disabled?}]] + [ui/toolbar-group {:last-child true} + [ui/raised-button {:href "/logout" + :label "Kirjaudu ulos" + :style button-style + :disabled disabled?}]]])))) + (def table-header-style {:color :black :font-weight :bold :font-size "16px" @@ -26,9 +56,7 @@ (defn tournament-row [tournament] (let [column-style {:font-size "14px"} - edit-url (routes/organizer-tournament-path {:id (:id tournament)}) - link-props {:href edit-url - :on-click #(dispatch [::events/load-organizer-tournament (:id tournament)])}] + link-props {:href (routes/organizer-tournament-path {:id (:id tournament)})}] [ui/table-row [ui/table-row-column {:class-name :date :style column-style} @@ -54,16 +82,6 @@ (let [tournaments (subscribe [::subs/organizer-tournaments])] (fn all-tournaments-render [] [:div#decklist-organizer-tournaments - [ui/raised-button {:href (routes/organizer-new-tournament-path) - :label "Uusi turnaus" - :icon (reagent/as-element [icons/content-add - {:style {:height "36px" - :width "30px" - :padding "6px 0 6px 6px" - :vertical-align :top}}])}] - [ui/raised-button {:href "/logout" - :label "Kirjaudu ulos" - :style {:margin-left "12px"}}] [ui/table {:selectable false :class-name :tournaments} [ui/table-header {:display-select-all false @@ -89,20 +107,40 @@ ^{:key (str (:id tournament) "--row")} [tournament-row tournament])]]]))) -(defn decklist-table [decklists on-select] - [ui/table {:class-name :tournaments - :multi-selectable true - :on-row-selection on-select} +(defn sortable-header [{:keys [class-name column]} & children] + (let [sort-data (subscribe [::subs/decklist-sort])] + (fn sortable-header-render [{:keys [class-name column]} & children] + (let [selected? (= column (:key @sort-data)) + icon (if (and selected? + (not (:ascending @sort-data))) + icons/hardware-keyboard-arrow-up + icons/hardware-keyboard-arrow-down) + style (cond-> (assoc table-header-style :cursor :pointer) + selected? (assoc :color (:accent1-color palette)))] + [ui/table-header-column {:class-name class-name + :style style + :on-click #(dispatch [::events/sort-decklists column])} + [icon {:style {:vertical-align :baseline + :position :absolute + :left 0 + :color nil}}] + children])))) + +(defn decklist-table [decklists selected-decklists on-select] + [ui/table {:class-name :tournaments + :multi-selectable true + :on-row-selection on-select + :all-rows-selected (= (count decklists) (count selected-decklists))} [ui/table-header [ui/table-row {:style {:height "24px"}} [ui/table-header-column {:class-name :dci :style table-header-style} "DCI"] - [ui/table-header-column {:class-name :name - :style table-header-style} + [sortable-header {:class-name :name + :column :name} "Nimi"] - [ui/table-header-column {:class-name :submitted - :style table-header-style} + [sortable-header {:class-name :submitted + :column :submitted} "Lähetetty"]]] [ui/table-body {:deselect-on-clickaway false} @@ -113,7 +151,7 @@ link-props {:href decklist-url :on-click #(dispatch [::events/load-decklist (:id decklist)])}]] ^{:key (str (:id decklist) "--row")} - [ui/table-row {} + [ui/table-row {:selected (contains? selected-decklists (:id decklist))} [ui/table-row-column {:class-name :dci :style column-style} [:a.decklist-link link-props @@ -131,19 +169,19 @@ (let [[color background] (case type :success [:black (:primary1-color palette)] :error [:white (:error-color palette)]) - on-delete #(dispatch [::events/clear-status (case type - :success :saved - :error :error)])] + display? (atom true) + on-delete #(reset! display? false)] (fn notice-render [type text] - [ui/chip - {:background-color background - :label-color color - :on-request-delete on-delete} - text]))) + (when @display? + [ui/chip + {:background-color background + :label-color color + :on-request-delete on-delete} + text])))) (defn notices [] (let [saved? (subscribe [::subs/saved?]) - error? (subscribe [::subs/error?])] + error? (subscribe [::subs/error :save-tournament])] (fn notices-render [] [:div.notices (when @saved? @@ -153,6 +191,7 @@ (defn tournament [id] (let [saved-tournament (subscribe [::subs/organizer-tournament]) + decklists (subscribe [::subs/organizer-decklists]) saving? (subscribe [::subs/saving?]) tournament (atom @saved-tournament) set-name #(swap! tournament assoc :name %) @@ -162,46 +201,56 @@ (swap! tournament assoc :format (keyword format))) save-tournament #(dispatch [::events/save-tournament (select-keys @tournament [:id :name :format :date :deadline])]) - selected-decklists (atom []) + selected-decklists (atom #{}) on-select (fn [selection] (let [selection (js->clj selection)] - (reset! selected-decklists (case selection - "all" (:decklist @tournament) - "none" [] - (map (:decklist @tournament) selection))))) - load-selected-decklists #(dispatch [::events/load-decklists - (map :id @selected-decklists)])] + (reset! selected-decklists + (into #{} (case selection + "all" (map :id @decklists) + "none" [] + (map (comp :id @decklists) selection)))))) + load-selected-decklists #(dispatch [::events/load-decklists @selected-decklists])] (fn tournament-render [id] (when (and (nil? @tournament) (some? @saved-tournament)) (reset! tournament @saved-tournament)) [:div#decklist-organizer-tournament [:div.tournament-info - [:div.field - [text-field {:on-change set-name - :floating-label-text "Turnauksen nimi" - :value (:name @tournament "")}]] - [:div.field - [ui/select-field {:on-change set-format - :value (:format @tournament) - :floating-label-text "Formaatti"} - [ui/menu-item {:value :standard - :primary-text "Standard"}] - [ui/menu-item {:value :modern - :primary-text "Modern"}] - [ui/menu-item {:value :legacy - :primary-text "Legacy"}]]] - [:div.field - [ui/date-picker {:value (some-> (:date @tournament) - (coerce/to-long) - (js/Date.)) - :on-change set-date - :container :inline - :dialog-container-style {:left "-9999px"} - :floating-label-text "Päivämäärä" - :locale "fi-FI" - :auto-ok true - :DateTimeFormat (oget js/Intl "DateTimeFormat")}]] + [:div.fields + [:div.field + (let [value (:name @tournament "")] + [text-field {:on-change set-name + :floating-label-text "Turnauksen nimi" + :value value + :error-text (when (str/blank? value) + "Nimi on pakollinen")}])] + [:div.field + (let [value (:format @tournament)] + [ui/select-field {:on-change set-format + :value value + :floating-label-text "Formaatti" + :error-text (when-not value + "Formaatti on pakollinen")} + [ui/menu-item {:value :standard + :primary-text "Standard"}] + [ui/menu-item {:value :modern + :primary-text "Modern"}] + [ui/menu-item {:value :legacy + :primary-text "Legacy"}]])] + [:div.field + (let [value (some-> (:date @tournament) + (coerce/to-long) + (js/Date.))] + [ui/date-picker {:value value + :error-text (when-not value + "Päivämäärä on pakollinen") + :on-change set-date + :container :inline + :dialog-container-style {:left "-9999px"} + :floating-label-text "Päivämäärä" + :locale "fi-FI" + :auto-ok true + :DateTimeFormat (oget js/Intl "DateTimeFormat")}])]] [:div.link (when id [:p @@ -211,7 +260,11 @@ [ui/raised-button {:label "Tallenna" :on-click save-tournament :primary true - :disabled @saving?}] + :disabled (or @saving? + (str/blank? (:name @tournament)) + (nil? (:format @tournament)) + (nil? (:date @tournament))) + :style {:width "200px"}}] (if @saving? [ui/circular-progress {:size 36 @@ -230,9 +283,10 @@ :on-click load-selected-decklists :primary true :disabled (empty? @selected-decklists) - :style {:margin-top "12px"}}]]] + :style {:margin-top "12px" + :width "200px"}}]]] [:div.decklists - [decklist-table (:decklist @tournament) on-select]]]))) + [decklist-table @decklists @selected-decklists on-select]]]))) (defn decklist-card [card] [:div.card diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 42f7032..41d9799 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -341,7 +341,7 @@ tournament (subscribe [::subs/tournament]) saving? (subscribe [::subs/saving?]) saved? (subscribe [::subs/saved?]) - error? (subscribe [::subs/error :save]) + error? (subscribe [::subs/error :save-decklist]) page (subscribe [::common-subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] (fn decklist-submit-render [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 94b7385..b80df99 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -21,7 +21,9 @@ (defn initial-db [] (util/deep-merge {:decklist-editor {:organizer-tournaments [] - :decklist empty-decklist}} + :decklist empty-decklist + :sort {:key :submitted + :ascending true}}} (transit/read (oget js/window "initial_db")))) (reg-event-db ::initialize @@ -33,9 +35,17 @@ (reg-event-db :server/decklist-error (fn [db _] - (update db :decklist-editor merge {:error true - :saving false - :saved false}))) + (util/assoc-in-many db + [:decklist-editor :saving] false + [:decklist-editor :saved] false + [:decklist-editor :error :save-decklist] true))) + +(reg-event-db :server/decklist-tournament-error + (fn [db _] + (util/assoc-in-many db + [:decklist-editor :saving] false + [:decklist-editor :saved] false + [:decklist-editor :error :save-tournament] true))) (reg-event-fx ::save-decklist (fn [{:keys [db]} [_ tournament decklist]] @@ -45,19 +55,30 @@ (reg-event-fx :server/decklist-saved (fn [{:keys [db]} [_ id]] - {:db (update db :decklist-editor merge {:saved true - :saving false - :error false}) + {:db (util/assoc-in-many db + [:decklist-editor :saved] true + [:decklist-editor :saving] false + [:decklist-editor :error :save-decklist] false) :navigate (routes/old-decklist-path {:id id})})) (reg-event-fx ::load-organizer-tournament - (fn [_ [_ id]] - {:ws-send [:client/decklist-organizer-tournament id]})) + (fn [{:keys [db]} [_ id]] + (when (not= (get-in db [:decklist-editor :organizer-tournament :id]) id) + {:ws-send [:client/decklist-organizer-tournament id]}))) (reg-event-db :server/decklist-organizer-tournament (fn [db [_ tournament]] (assoc-in db [:decklist-editor :organizer-tournament] tournament))) +(reg-event-fx ::load-organizer-tournaments + (fn [{:keys [db]} _] + (when (empty? (get-in db [:decklist-editor :organizer-tournaments])) + {:ws-send [:client/decklist-organizer-tournaments]}))) + +(reg-event-db :server/decklist-organizer-tournaments + (fn [db [_ tournaments]] + (assoc-in db [:decklist-editor :organizer-tournaments] tournaments))) + (reg-event-fx ::save-tournament (fn [{:keys [db]} [_ tournament]] {:db (update db :decklist-editor merge {:saving true @@ -70,9 +91,17 @@ [:decklist-editor :organizer-tournament :id] id [:decklist-editor :saved] true [:decklist-editor :saving] false - [:decklist-editor :error :save] false) + [:decklist-editor :error :save-tournament] false) :navigate (routes/organizer-tournament-path {:id id})})) +(reg-event-db ::sort-decklists + (fn [db [_ sort-key]] + (let [previous-key (get-in db [:decklist-editor :sort :key])] + (if (= previous-key sort-key) + (update-in db [:decklist-editor :sort :ascending] not) + (assoc-in db [:decklist-editor :sort] {:key sort-key + :ascending true}))))) + (reg-event-fx ::load-decklist (fn [_ [_ id]] {:ws-send [:client/load-decklist id]})) @@ -100,12 +129,9 @@ (update db :decklist-editor merge {:organizer-tournament nil :saving false :saved false - :error {:save false - :import-address nil}}))) - -(reg-event-db ::clear-status - (fn [db [_ key]] - (assoc-in db [:decklist-editor key] false))) + :error {:save-decklist false + :save-tournament false + :import-address nil}}))) (reg-event-fx ::import-address (fn [{:keys [db]} [_ code]] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs index 6293a3a..09fc03d 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/pages/decklist.cljs @@ -18,12 +18,14 @@ (defn decklist-organizer [id page] (let [user (subscribe [::subs/user])] (fn decklist-organizer-render [id page] - (case @user - nil [loading-indicator] - false [organizer/login] - (case page - ::organizer-tournament [organizer/tournament id] - ::organizer [organizer/all-tournaments] - ::organizer-view (if id - [organizer/view-decklist] - [organizer/view-decklists])))))) + [:div#decklist-organizer + [organizer/header] + (case @user + nil [loading-indicator] + false [organizer/login] + (case page + ::organizer-tournament [organizer/tournament id] + ::organizer [organizer/all-tournaments] + ::organizer-view (if id + [organizer/view-decklist] + [organizer/view-decklists])))]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs index ae80fc2..03a97bb 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/routes/decklist.cljs @@ -15,6 +15,7 @@ (defn initialize-routes [prefix] (when-not @initialized? (secretary/defroute organizer-path (str prefix "/organizer") [] + (dispatch [:mtg-pairings-server.events.decklist/load-organizer-tournaments]) (dispatch-page :mtg-pairings-server.pages.decklist/organizer)) (secretary/defroute organizer-print-path (str prefix "/organizer/print") [] @@ -28,6 +29,7 @@ (dispatch-page :mtg-pairings-server.pages.decklist/organizer-tournament)) (secretary/defroute organizer-tournament-path (str prefix "/organizer/:id") [id] + (dispatch [:mtg-pairings-server.events.decklist/load-organizer-tournament id]) (dispatch-page :mtg-pairings-server.pages.decklist/organizer-tournament id)) (secretary/defroute new-decklist-path (str prefix "/tournament/:id") [id] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs index bfa0f5b..0c4383e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs @@ -1,5 +1,7 @@ (ns mtg-pairings-server.subscriptions.decklist - (:require [re-frame.core :refer [reg-sub subscribe]])) + (:require [re-frame.core :refer [reg-sub subscribe]] + [clojure.string :as str] + [cljs-time.coerce :as coerce])) (reg-sub ::tournament (fn [db _] @@ -29,6 +31,24 @@ (fn [db _] (get-in db [:decklist-editor :organizer-tournament]))) +(reg-sub ::decklist-sort + (fn [db _] + (get-in db [:decklist-editor :sort]))) + +(reg-sub ::organizer-decklists + :<- [::organizer-tournament] + :<- [::decklist-sort] + (fn [[tournament sort-data] _] + (let [decklists (:decklist tournament) + keyfn (case (:key sort-data) + :name (juxt (comp str/lower-case :last-name) + (comp str/lower-case :first-name)) + :submitted (comp coerce/to-long :submitted)) + sorted-decklists (sort-by keyfn decklists)] + (vec (if (:ascending sort-data) + sorted-decklists + (reverse sorted-decklists)))))) + (reg-sub ::user (fn [db _] (get-in db [:decklist-editor :user]))) From ffaf72798a0a4fcf2edd0885bd48ced333eaad2b Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 8 Jul 2019 17:25:00 +0300 Subject: [PATCH 41/44] Use full page for printing decklists --- mtg-pairings-server/project.clj | 6 +- .../mtg_pairings_server/styles/bracket.clj | 3 +- .../mtg_pairings_server/styles/decklist.clj | 115 +++++++++++------- .../clj/mtg_pairings_server/styles/main.clj | 8 +- .../clj/mtg_pairings_server/styles/table.clj | 3 +- .../mtg_pairings_server/styles/tournament.clj | 2 +- .../mtg_pairings_server/styles/common.cljc | 69 ++++++++++- .../cljc/mtg_pairings_server/util/mobile.cljc | 21 ---- .../components/decklist/organizer.cljs | 26 ++-- .../mtg_pairings_server/events/common.cljs | 2 +- .../mtg_pairings_server/events/decklist.cljs | 2 +- .../mtg_pairings_server/events/pairings.cljs | 2 +- 12 files changed, 165 insertions(+), 94 deletions(-) delete mode 100644 mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc diff --git a/mtg-pairings-server/project.clj b/mtg-pairings-server/project.clj index 5ced876..3d5e397 100644 --- a/mtg-pairings-server/project.clj +++ b/mtg-pairings-server/project.clj @@ -22,7 +22,8 @@ [com.taoensso/sente "1.14.0-RC2"] [com.taoensso/timbre "4.10.0"] [com.cognitect/transit-clj "0.8.313"] - [com.fzakaria/slf4j-timbre "0.3.13"]] + [com.fzakaria/slf4j-timbre "0.3.13"] + [garden "1.3.9"]] :plugins [[lein-ancient "0.6.15"] [lein-asset-minifier "0.4.6"] [lein-cljfmt "0.6.4"] @@ -118,8 +119,7 @@ [cljsjs/react-dom-server "16.8.6-0"] [cljs-react-material-ui "0.2.50" :exclusions [org.clojure/clojurescript]] [cljsjs/rc-slider "8.6.1-0"] - [cljsjs/react-autosuggest "9.4.3-0"] - [garden "1.3.9"]]} + [cljsjs/react-autosuggest "9.4.3-0"]]} :uberjar {:source-paths ["env/prod/cljs"] :main mtg-pairings-server.main :aot :all diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/bracket.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/bracket.clj index 0f5be49..32672c6 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/bracket.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/bracket.clj @@ -1,8 +1,7 @@ (ns mtg-pairings-server.styles.bracket (:require [garden.def :refer [defstyles]] [garden.units :refer [px px-]] - [mtg-pairings-server.styles.common :refer [color]] - [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) + [mtg-pairings-server.styles.common :refer [color when-desktop when-mobile]])) (def line-height 20) (def bracket-height 50) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index a102bfe..4e37fa8 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -1,9 +1,8 @@ (ns mtg-pairings-server.styles.decklist (:require [garden.def :refer [defstyles]] [garden.selectors :refer [after & first-child last-child]] - [garden.units :refer [px px- px+ px* px-div percent vh]] - [mtg-pairings-server.styles.common :refer [color ellipsis-overflow]] - [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile when-screen when-print]])) + [garden.units :refer [mm px px- px+ px* px-div percent vh]] + [mtg-pairings-server.styles.common :refer [calc color ellipsis-overflow when-desktop when-mobile when-screen when-print]])) (defstyles submit [:#decklist-submit @@ -66,28 +65,51 @@ :width (px 1) :color (color :grey)}) -(def body-height (px 952)) +(def print-height (mm 297)) +(def print-width (mm 210)) (def left-margin (px 64)) (def header-line-height (px 36)) (def top-margin (px+ (px 1) (px* header-line-height 2))) +(def print-body-height (calc (- print-height top-margin))) (def player-height (px 48)) (def card-width (px 270)) (def card-height (px 30)) +(def column-margin (px 10)) (def column-gap (px 20)) +(def print-body-width (calc (- print-width player-height))) +(def print-card-width (calc (- (/ print-body-width 2) (* column-margin 2)))) +(def letters-width (px 90)) +(def date-width (px 200)) +(def tournament-name-width (calc (- print-body-width letters-width date-width))) +(def deck-name-width (calc (- print-body-width letters-width))) + (defstyles cardlists + [:.decklists + {:position :relative}] [:.maindeck :.sideboard (when-screen [:& {:display :inline-block - :vertical-align :top}]) + :vertical-align :top} + [:.cards + [:.card + {:width card-width} + [:.name + {:width (px 220)}]]]]) (when-print [:h3 - {:margin "16px 0"}]) + {:margin "16px 0"}] + [:.cards + [:.card + {:width print-card-width + :margin-left column-margin + :margin-right column-margin} + [:.name + {:width (calc (- print-card-width (px 50)))}]]]) [:.cards [:.card - {:width card-width - :height (px 24) + {:height (px 24) :line-height (px 24) :margin-bottom (px 6)} [:.quantity :.name @@ -98,35 +120,33 @@ :text-align :center :margin "0 8px"}] [:.name - {:width (px 220) - :padding-left (px 6)}]]]] + {:padding-left (px 6)}]]]] [:.maindeck - [:.cards - {:width (px+ card-width column-gap card-width)}] (when-screen [:& {:margin-right column-gap} [:.cards - {:column {:count 2 + {:width (px+ (px* card-width 2) (px* column-margin 4)) + :column {:count 2 :gap column-gap}}]]) (when-print [:& - {:margin-left left-margin + {:width print-body-width + :margin-left player-height :position :relative} [:h3 {:position :absolute :top 0 - :left 0}] + :left column-margin}] [:.cards {:display :flex :flex {:direction :column :wrap :wrap} :align {:content :space-between} - :height body-height + :max-height print-body-height :padding-top (px 22)} - [:.card - [(& first-child) - {:margin-top card-height}]]]])] + [:.card:first-child + {:margin-top card-height}]]])] [:.sideboard (when-screen [:& @@ -134,12 +154,10 @@ (when-print [:& {:position :absolute - :left (px+ left-margin card-width column-gap)} - (for [i (range 16) - :let [free-space (px* card-height (- 30 i)) - top (px+ top-margin free-space)]] - [(keyword (str "&.sideboard-" i)) - {:top top}])])]) + :right 0 + :bottom 0} + [:h3 + {:margin-left column-margin}]])]) (defstyles player-info [:.player-info @@ -169,14 +187,15 @@ :margin-left (px 12)}]) (when-print [:& - {:height player-height - :padding-top (px 1) - :line-height (px- player-height (px 2)) - :width body-height - :border-bottom grey-border - :top (px+ (px-div (px- body-height player-height) 2) top-margin) - :left (px-div (px- body-height player-height) -2) - :transform "rotate(270deg)"}] + {:height player-height + :padding-top (px 1) + :line-height (px- player-height (px 2)) + :width print-body-height + :border-bottom grey-border + :top 0 + :left 0 + :transform "translateY(297mm) rotate(270deg)" + :transform-origin "top left"}] [:.label {:vertical-align :top}] [:.value @@ -196,11 +215,13 @@ :line-height (px 36) :margin "5px 0"}]]] [:.name - {:width (px- body-height (px 420))} + {:width (calc (- print-body-height (px 420)))} [:.first-name :.last-name - {:width (percent 50)} + {:width (percent 50) + :padding-right (px 16)} + ellipsis-overflow [:.label - {:width (px 80)}]]])]) + {:margin-right (px 8)}]]])]) (defstyles deck-info [:.deck-info @@ -223,8 +244,9 @@ :margin-right (px 12)}]) (when-print [:& - {:padding-left left-margin} + {:padding-left player-height} [:.tournament-date :.tournament-name :.deck-name + ellipsis-overflow [:.label {:width (px 72) :text-align :right}] @@ -232,16 +254,18 @@ {:padding-left (px 6) :font-size (px 20)}]] [:.tournament-date - {:width (px 200)}] + {:width date-width}] [:.tournament-name + {:width tournament-name-width} [:a {:color :black}]] [:.deck-name - {:width (percent 100)}]])]) + {:width deck-name-width}]])]) (defstyles organizer-decklist [:.organizer-decklist - {:position :relative} + {:position :relative + :width (percent 100)} [:.label :.value {:display :inline-block}] (when-screen @@ -252,24 +276,25 @@ {:display :none}]) (when-print [:& - {:height (px+ body-height top-margin) - :break-after :page}] + {:width print-width + :height print-height + :break-after :page}] [:.label {:color (color :dark-grey)}] [:.first-letters {:position :absolute :top 0 :right 0 - :width (px 84)} + :width letters-width} [:.label - {:font-size (px 12) + {:font-size (px 14) :text-align :center :width (percent 100)}] [:.letter {:font-size (px 32) :font-weight :bold :display :inline-block - :width (px 28) + :width (px 30) :text-align :center :text-transform :uppercase}]]) cardlists diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj index 2396cb4..9b3fb81 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/main.clj @@ -4,17 +4,19 @@ [garden.stylesheet :refer [at-import]] [garden.units :refer [px percent]] [mtg-pairings-server.styles.bracket :as bracket] - [mtg-pairings-server.styles.common :refer [color]] + [mtg-pairings-server.styles.common :refer [color when-desktop when-mobile when-print]] [mtg-pairings-server.styles.decklist :as decklist] [mtg-pairings-server.styles.organizer :as organizer] [mtg-pairings-server.styles.table :as table] [mtg-pairings-server.styles.tooltip :as tooltip] - [mtg-pairings-server.styles.tournament :as tournament] - [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) + [mtg-pairings-server.styles.tournament :as tournament])) (defstyles base (at-import "https://fonts.googleapis.com/css?family=Lato:700") (at-import "https://fonts.googleapis.com/css?family=Roboto:400,500,700") + (when-print + ["@page" + {:size "A4"}]) [:html {:-webkit-text-size-adjust "100%" :-ms-text-size-adjust "100%"}] diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj index b8f3202..63b0eed 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/table.clj @@ -2,8 +2,7 @@ (:require [garden.def :refer [defstyles]] [garden.selectors :refer [nth-child &]] [garden.units :refer [px percent]] - [mtg-pairings-server.styles.common :refer [ellipsis-overflow color]] - [mtg-pairings-server.util.mobile :refer [when-mobile when-desktop]])) + [mtg-pairings-server.styles.common :refer [ellipsis-overflow color when-mobile when-desktop]])) (defstyles pairings-table [:table.pairings-table diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tournament.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tournament.clj index db4cece..cd258f6 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tournament.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/tournament.clj @@ -1,7 +1,7 @@ (ns mtg-pairings-server.styles.tournament (:require [garden.def :refer [defstyles]] [garden.units :refer [px percent]] - [mtg-pairings-server.util.mobile :refer [when-desktop when-mobile]])) + [mtg-pairings-server.styles.common :refer [when-desktop when-mobile]])) (defstyles mobile-filters [:.mobile-filters diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc index e0ef84e..1bce5b8 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/styles/common.cljc @@ -1,5 +1,11 @@ (ns mtg-pairings-server.styles.common - (:require [garden.color :refer [hex->rgb rgb? rgba rgb->hex]])) + (:require [clojure.string :as str] + [clojure.walk :refer [postwalk]] + [garden.color :refer [hex->rgb rgb? rgba rgb->hex]] + [garden.compiler :refer [CSSRenderer]] + #?(:clj [garden.stylesheet :refer [at-media]]) + #?(:clj [garden.units :refer [px percent]]) + #?(:cljs [oops.core :refer [oget]]))) (def ellipsis-overflow {:white-space :nowrap :text-overflow :ellipsis @@ -37,3 +43,64 @@ (defn border [width style color] (str width " " (name style) " " color)) + +(def mobile-max-width 767) + +#?(:clj (defn when-mobile [& styles] + (apply at-media {:max-width (px mobile-max-width)} styles))) + +#?(:clj (defn when-desktop [& styles] + (apply at-media {:min-width (px (inc mobile-max-width))} styles))) + +#?(:clj (defn when-screen [& styles] + (apply at-media {:screen true} styles))) + +#?(:clj (defn when-print [& styles] + (apply at-media {:print true} styles))) + +#?(:cljs (defn mobile? [] + (<= (oget js/window "innerWidth") mobile-max-width))) + +(declare calc*) + +(defrecord Calc [expression s] + Object + (toString [_] + (str "(calc" s ")")) + CSSRenderer + (render-css [_] + (str "calc" (calc* expression)))) + +(defn ^:private calc* [token] + (cond + (vector? token) + (let [[op & vals] token] + (str \( + (str/join (interpose (str " " op " ") (map calc* vals))) + \))) + + (number? token) + (str token) + + (string? token) + token + + ;; CSSUnit + (and (:magnitude token) (:unit token)) + (str (:magnitude token) (name (:unit token))) + + ;; Calc + (:expression token) + (calc* (:expression token)) + + :else + (throw (ex-info (str "Don't know how to calc token " token) {:token token})))) + +(defmacro calc [expression] + `(Calc. ~(postwalk (fn [token] + (if (and (sequential? token) + (contains? #{'+ '- '* '/} (first token))) + (into [(str (first token))] (rest token)) + token)) + expression) + ~(str expression))) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc deleted file mode 100644 index 73fd131..0000000 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/mobile.cljc +++ /dev/null @@ -1,21 +0,0 @@ -(ns mtg-pairings-server.util.mobile - (:require #?(:clj [garden.stylesheet :refer [at-media]]) - #?(:clj [garden.units :refer [px percent]]) - #?(:cljs [oops.core :refer [oget]]))) - -(def mobile-max-width 767) - -#?(:clj (defn when-mobile [& styles] - (apply at-media {:max-width (px mobile-max-width)} styles))) - -#?(:clj (defn when-desktop [& styles] - (apply at-media {:min-width (px (inc mobile-max-width))} styles))) - -#?(:clj (defn when-screen [& styles] - (apply at-media {:screen true} styles))) - -#?(:clj (defn when-print [& styles] - (apply at-media {:print true} styles))) - -#?(:cljs (defn mobile? [] - (<= (oget js/window "innerWidth") mobile-max-width))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index e22bad4..e83d36e 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -333,19 +333,19 @@ (for [index (range 10)] ^{:key (str id "--dci--" index)} [:span.digit (get dci index)])]]] - [:div.maindeck - [:h3 "Maindeck (" (:main counts) ")"] - [:div.cards - (for [card main] - ^{:key (str id "--main--" (:name card))} - [decklist-card card])]] - [:div.sideboard - {:class (str "sideboard-" (count side))} - [:h3 "Sideboard (" (:side counts) ")"] - [:div.cards - (for [card side] - ^{:key (str id "--side--" (:name card))} - [decklist-card card])]]])) + [:div.decklists + [:div.maindeck + [:h3 "Maindeck (" (:main counts) ")"] + [:div.cards + (for [card main] + ^{:key (str id "--main--" (:name card))} + [decklist-card card])]] + [:div.sideboard + [:h3 "Sideboard (" (:side counts) ")"] + [:div.cards + (for [card side] + ^{:key (str id "--side--" (:name card))} + [decklist-card card])]]]])) (defn view-decklist [] (let [decklist (subscribe [::subs/decklist]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs index 72c2f56..0110720 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/common.cljs @@ -1,8 +1,8 @@ (ns mtg-pairings-server.events.common (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] [accountant.core :as accountant] + [mtg-pairings-server.styles.common :refer [mobile?]] [mtg-pairings-server.util.local-storage :as local-storage] - [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) (defmethod ws/event-handler :chsk/recv diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index b80df99..f31a521 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -2,10 +2,10 @@ (:require [re-frame.core :refer [dispatch reg-fx reg-event-db reg-event-fx]] [oops.core :refer [oget]] [mtg-pairings-server.routes.decklist :as routes] + [mtg-pairings-server.styles.common :refer [mobile?]] [mtg-pairings-server.transit :as transit] [mtg-pairings-server.util :as util] [mtg-pairings-server.util.decklist :refer [add-id-to-card add-id-to-cards]] - [mtg-pairings-server.util.mobile :refer [mobile?]] [mtg-pairings-server.websocket :as ws])) (def empty-decklist {:main [] diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs index 773fc0c..ed5ef61 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/pairings.cljs @@ -3,11 +3,11 @@ [cljs.core.async :refer [ Date: Mon, 8 Jul 2019 19:16:44 +0300 Subject: [PATCH 42/44] Sorting of decklist cards by type --- .../src/clj/mtg_pairings_server/handler.clj | 2 + .../mtg_pairings_server/service/decklist.clj | 50 ++--- .../src/clj/mtg_pairings_server/sql_db.clj | 9 +- .../mtg_pairings_server/styles/decklist.clj | 24 ++- .../mtg_pairings_server/util/decklist.cljc | 32 +++ .../components/autosuggest.cljs | 20 +- .../components/decklist/organizer.cljs | 36 ++-- .../components/decklist/submit.cljs | 184 ++++++++++-------- .../mtg_pairings_server/events/decklist.cljs | 30 +-- .../subscriptions/decklist.cljs | 13 +- 10 files changed, 250 insertions(+), 150 deletions(-) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj index 612f6fd..1636199 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/handler.clj @@ -137,6 +137,8 @@ :decklist-editor (when user-id {:decklist decklist :organizer-tournament tournament})})))) + (GET "/decklist/organizer/print" [] + (redirect (auth/organizer-path))) (GET "/decklist/organizer/:id" request :path-params [id :- s/Str] (let [tournament (decklist/get-organizer-tournament id) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj index 429fff7..537468f 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/service/decklist.clj @@ -12,17 +12,16 @@ (let [name-param (-> prefix (str/replace "%" "") (str/lower-case) - (str \%)) - result (cond-> (-> (sql/select* db/card) - (sql/fields :name) - (sql/where {:lowername [like name-param]}) - (sql/order :lowername :asc) - (sql/limit 10)) - (= :standard format) (sql/where {:standard true}) - (= :modern format) (sql/where {:modern true}) - (= :legacy format) (sql/where {:legacy true}) - true (sql/exec))] - (map :name result))) + (str \%))] + (cond-> (-> (sql/select* db/card) + (sql/fields :name :types) + (sql/where {:lowername [like name-param]}) + (sql/order :lowername :asc) + (sql/limit 10)) + (= :standard format) (sql/where {:standard true}) + (= :modern format) (sql/where {:modern true}) + (= :legacy format) (sql/where {:legacy true}) + true (sql/exec)))) (defn generate-card->id [cards] (into {} @@ -30,11 +29,10 @@ (sql/select db/card (sql/where {:name [in cards]})))) -(defn format-card [decklist maindeck card index quantity] +(defn format-card [decklist maindeck card quantity] {:decklist decklist :maindeck maindeck :card card - :index index :quantity quantity}) (defn get-tournament [id] @@ -44,16 +42,17 @@ (defn format-cards [cards maindeck?] (->> cards ((if maindeck? filter remove) :maindeck) - (sort-by :index) - (mapv #(select-keys % [:name :quantity])))) + (map #(update % :types set)) + (mapv #(select-keys % [:name :quantity :types])))) (defn get-decklist [id] (when-let [decklist (sql-util/select-unique-or-nil db/decklist (sql/fields :id [:name :deck-name] :first-name :last-name :dci :email :tournament) (sql/with db/decklist-card - (sql/fields :maindeck :index :quantity) + (sql/fields :maindeck :quantity) (sql/with db/card - (sql/fields :name))) + (sql/fields :name :types) + (sql/order :name))) (sql/where {:id id}))] (let [main (format-cards (:decklist_card decklist) true) side (format-cards (:decklist_card decklist) false) @@ -96,10 +95,10 @@ (sql/insert db/decklist-card (sql/values (concat - (for [[index {:keys [name quantity]}] (indexed (:main decklist))] - (format-card (or old-id new-id) true (card->id name) index quantity)) - (for [[index {:keys [name quantity]}] (indexed (:side decklist))] - (format-card (or old-id new-id) false (card->id name) index quantity))))) + (for [{:keys [name quantity]} (:main decklist)] + (format-card (or old-id new-id) true (card->id name) quantity)) + (for [{:keys [name quantity]} (:side decklist)] + (format-card (or old-id new-id) false (card->id name) quantity))))) {:id (or old-id new-id) :send-email? (and (not (str/blank? (get-in decklist [:player :email]))) (str/blank? (get-in old-decklist [:player :email])))}))) @@ -150,12 +149,13 @@ (when-not (or (str/blank? row) (re-matches #"^[Mm]aindeck.*" row)) (if-let [[_ quantity name] (re-matches #"(\d+)\s+(.+)" (str/trim row))] - (if-let [{:keys [name legal]} (sql-util/select-unique-or-nil db/card - (sql/fields :name [format :legal]) - (sql/where {:lowername (str/lower-case name)}))] + (if-let [{:keys [name legal types]} (sql-util/select-unique-or-nil db/card + (sql/fields :name [format :legal] :types) + (sql/where {:lowername (str/lower-case name)}))] (if legal {:name name - :quantity (Long/parseLong quantity)} + :quantity (Long/parseLong quantity) + :types (set types)} {:name name :error "Ei sallittu tässä formaatissa"}) {:name name diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj index fb6d5a1..0893cc8 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/sql_db.clj @@ -1,5 +1,6 @@ (ns mtg-pairings-server.sql-db (:require [clojure.walk :refer [postwalk]] + [clojure.java.jdbc :as jdbc] [clj-time.core :as time] [clj-time.coerce :as time-coerce] [korma.core :as sql] @@ -9,7 +10,13 @@ [config.core :refer [env]] [mtg-pairings-server.util :refer [some-value]]) (:import (java.sql Date Timestamp) - (org.joda.time LocalDate DateTime))) + (org.joda.time LocalDate DateTime) + (org.postgresql.jdbc PgArray))) + +(extend-protocol jdbc/IResultSetReadColumn + PgArray + (result-set-read-column [pgobj _ _] + (vec (.getArray pgobj)))) (def datasource-options {:adapter "postgresql" :username (env :db-user) diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index 4e37fa8..b22682d 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -102,9 +102,9 @@ {:margin "16px 0"}] [:.cards [:.card - {:width print-card-width - :margin-left column-margin - :margin-right column-margin} + {:width print-card-width + :margin {:left column-margin + :right column-margin}} [:.name {:width (calc (- print-card-width (px 50)))}]]]) [:.cards @@ -120,7 +120,13 @@ :text-align :center :margin "0 8px"}] [:.name - {:padding-left (px 6)}]]]] + {:padding-left (px 6)}]] + [:.type-header + {:height (px 24) + :line-height (px 24) + :margin {:top 0 + :bottom (px 6)} + :break-after :avoid-column}]]] [:.maindeck (when-screen [:& @@ -138,6 +144,8 @@ {:position :absolute :top 0 :left column-margin}] + [:.type-header + {:margin-left column-margin}] [:.cards {:display :flex :flex {:direction :column @@ -145,7 +153,7 @@ :align {:content :space-between} :max-height print-body-height :padding-top (px 22)} - [:.card:first-child + [:.card:first-child :.type-header:first-child {:margin-top card-height}]]])] [:.sideboard (when-screen @@ -276,9 +284,9 @@ {:display :none}]) (when-print [:& - {:width print-width - :height print-height - :break-after :page}] + {:width print-width + :height print-height + :break-after :page}] [:.label {:color (color :dark-grey)}] [:.first-letters diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc index 8e4391a..90ebe64 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util/decklist.cljc @@ -33,3 +33,35 @@ (str (pad quantity) " " name))) "\n"))) +(def card-types + [:creature + :artifact + :enchantment + :instant + :sorcery + :planeswalker + :land + :error]) + +(def type->header + {:artifact "Artifact" + :creature "Creature" + :enchantment "Enchantment" + :instant "Instant" + :land "Land" + :planeswalker "Planeswalker" + :sorcery "Sorcery" + :error "Virheelliset"}) + +(defn by-type [decklist] + (let [{:keys [main]} decklist + cards-by-type (loop [cards main + parts {} + [type & types] (map type->header card-types)] + (if type + (let [grouped-cards (group-by #(contains? (:types %) type) cards)] + (recur (get grouped-cards false) + (assoc parts (keyword (str/lower-case type)) (get grouped-cards true)) + types)) + (assoc parts :error cards)))] + (assoc decklist :main cards-by-type))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs index fc96064..8471d89 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/autosuggest.cljs @@ -6,13 +6,13 @@ [oops.core :refer [oget]] [mtg-pairings-server.util :refer [deep-merge]])) -(defn ^:private suggestion [suggestion opts] +(defn ^:private suggestion [suggestion->string suggestion opts] (reagent/as-element [ui/menu-item {:primary-text (reagent/as-element [:div {:style {:white-space :nowrap :text-overflow :ellipsis :overflow :hidden}} - suggestion])}])) + (suggestion->string (js->clj suggestion :keywordize-keys true))])}])) (defn ^:private suggestion-container [props] (reagent/as-element @@ -45,20 +45,20 @@ (reset! value "") (on-change suggestion)) on-suggestion-selected (fn [_ data] - (select-suggestion (oget data "suggestion")))] - (fn autosuggest-render [{:keys [suggestions styles on-suggestions-clear-requested] :as options}] - (let [input-props (merge (dissoc options :suggestions :styles :on-change :on-suggestions-fetch-requested :on-suggestions-clear-requested) - {:on-change (fn [event new-value] - (when (= "type" (oget new-value "method")) - (reset! value (oget event "target" "value")))) - :value @value})] + (select-suggestion (js->clj (oget data "suggestion") :keywordize-keys true)))] + (fn autosuggest-render [{:keys [suggestions styles on-suggestions-clear-requested suggestion->string] :as options}] + (let [input-props (merge (dissoc options :suggestions :styles :on-change :on-suggestions-fetch-requested :on-suggestions-clear-requested :suggestion->string) + {:on-change (fn [event new-value] + (when (= "type" (oget new-value "method")) + (reset! value (oget event "target" "value")))) + :value @value})] [:> js/Autosuggest {:suggestions @suggestions :on-suggestions-fetch-requested on-suggestions-fetch-requested :on-suggestions-clear-requested on-suggestions-clear-requested :on-suggestion-selected on-suggestion-selected :get-suggestion-value identity :render-suggestions-container suggestion-container - :render-suggestion suggestion + :render-suggestion (partial suggestion suggestion->string) :render-input-component input :input-props input-props :theme (deep-merge default-styles styles)}])))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index e83d36e..e1e86e0 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -12,6 +12,7 @@ [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.styles.common :refer [palette]] [mtg-pairings-server.util :refer [format-date format-date-time to-local-date indexed get-host]] + [mtg-pairings-server.util.decklist :refer [card-types type->header]] [mtg-pairings-server.util.material-ui :refer [text-field]])) (defn header [] @@ -205,10 +206,10 @@ on-select (fn [selection] (let [selection (js->clj selection)] (reset! selected-decklists - (into #{} (case selection - "all" (map :id @decklists) - "none" [] - (map (comp :id @decklists) selection)))))) + (set (case selection + "all" (map :id @decklists) + "none" [] + (map (comp :id @decklists) selection)))))) load-selected-decklists #(dispatch [::events/load-decklists @selected-decklists])] (fn tournament-render [id] (when (and (nil? @tournament) @@ -336,28 +337,33 @@ [:div.decklists [:div.maindeck [:h3 "Maindeck (" (:main counts) ")"] - [:div.cards - (for [card main] - ^{:key (str id "--main--" (:name card))} - [decklist-card card])]] + (into [:div.cards] + (mapcat (fn [type] + (when-let [cards (get main type)] + (list* [:h4.type-header (type->header type)] + (for [card cards] + [decklist-card card]))))) + card-types)] [:div.sideboard [:h3 "Sideboard (" (:side counts) ")"] - [:div.cards - (for [card side] - ^{:key (str id "--side--" (:name card))} - [decklist-card card])]]]])) + (into [:div.cards] + (for [card side] + [decklist-card card]))]]])) (defn view-decklist [] - (let [decklist (subscribe [::subs/decklist]) + (let [decklist (subscribe [::subs/decklist-by-type]) tournament (subscribe [::subs/organizer-tournament])] (fn view-decklist-render [] [render-decklist @decklist @tournament]))) (defn view-decklists [] - (let [decklists (subscribe [::subs/decklists]) + (let [decklists (subscribe [::subs/decklists-by-type]) tournament (subscribe [::subs/organizer-tournament]) + printed? (clojure.core/atom false) print-page #(when (and (seq @decklists) - @tournament) + @tournament + (not @printed?)) + (reset! printed? true) (.print js/window))] (reagent/create-class {:component-did-mount print-page diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 41d9799..1f5301c 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -14,7 +14,7 @@ [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.decklist :as subs] [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host valid-email?]] - [mtg-pairings-server.util.decklist :refer [->text]] + [mtg-pairings-server.util.decklist :refer [->text card-types type->header]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] [mtg-pairings-server.util.material-ui :refer [text-field]] [clojure.string :as str])) @@ -43,6 +43,7 @@ :on-change on-change :on-suggestions-fetch-requested fetch-suggestions :on-suggestions-clear-requested clear-suggestions + :suggestion->string :name :id :decklist-autosuggest :floating-label-text "Lisää kortti..." :styles {:container {:width width} @@ -54,10 +55,11 @@ (not (str/blank? last-name)))) (defn decklist-errors [decklist] - (let [cards (reduce (fn [acc {:keys [name quantity]}] + (let [all-cards (concat (mapcat (:main decklist) card-types) (:side decklist)) + cards (reduce (fn [acc {:keys [name quantity]}] (merge-with + acc {name quantity})) {} - (concat (:main decklist) (:side decklist))) + all-cards) errors (concat [(when-not (valid-player-data? (:player decklist)) {:type :player-data :id :missing-player-data :text "Osa pelaajan tiedoista puuttuu"}) @@ -74,7 +76,7 @@ :id (str "deck-error-card--" card) :card card :text (str "Korttia " card " on yli 4 kappaletta")}) - (for [{:keys [error name]} (concat (:main decklist) (:side decklist)) + (for [{:keys [error name]} all-cards :when error] {:type :other :id (str "other-error-card--" name) @@ -137,6 +139,27 @@ (when error [error-icon error])]]))) +(defn decklist-header-row [type] + [ui/table-row + [ui/table-row-column {:class-name :quantity}] + [ui/table-row-column {:class-name :card + :style {:font-size "16px" + :font-weight :bold}} + (type->header type)] + [ui/table-row-column {:class-name :actions}] + [ui/table-row-column {:class-name :error}]]) + +(defn table-body-by-type [decklist board error-cards] + (mapcat (fn [type] + (when-let [cards (get-in decklist [board type])] + (list* + ^{:key (str (name type) "--header")} + [decklist-header-row type] + (for [{:keys [id name error] :as card} cards] + ^{:key (str id "--tr")} + [decklist-table-row board card (or error (get error-cards name))])))) + card-types)) + (defn decklist-table [decklist board] (let [mobile? (subscribe [::common-subs/mobile?]) header-style {:color :black @@ -168,9 +191,11 @@ [ui/table-header-column {:class-name :actions}] [ui/table-header-column {:class-name :error}]]] [ui/table-body - (for [{:keys [id name error] :as card} (get @decklist board)] - ^{:key (str id "--tr")} - [decklist-table-row board card (or error (get error-cards name))])]]])))) + (case board + :main (table-body-by-type @decklist :main error-cards) + :side (for [{:keys [id name error] :as card} (:side @decklist)] + ^{:key (str id "--tr")} + [decklist-table-row :side card (or error (get error-cards name))]))]]])))) (defn player-info [player] (let [set-first-name #(dispatch [::events/update-player-info :first-name %]) @@ -252,74 +277,79 @@ import-error (subscribe [::subs/error :import-address]) border (styles/border "1px" :solid (styles/palette :light-grey))] (reagent/create-class - {:component-did-mount (fn [] - (add-watch loaded? ::decklist-import - (fn [_ _ _ new] - (when new - (reset! selected nil) - (reset! address ""))))) - :component-will-unmount (fn [] - (remove-watch loaded? ::decklist-import)) - :reagent-render (fn decklist-import-render [] - [:div.decklist-import - [ui/tabs {:value @selected - :content-container-style (when @selected - {:border-bottom border - :border-left (when-not @mobile? border) - :border-right (when-not @mobile? border)})} - [ui/tab {:label "Lataa aiempi lista" - :value "load-previous" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa aiempi lista"] - [:p - "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " - [:span.address "https://decklist.pairings.fi/abcd..."] - ")."]] - [:div.form - [:div.text-field-container - [text-field {:on-change address-on-change - :floating-label-text "Osoite" - :full-width true - :error-text (when-not (or (str/blank? @address) - @code) - "Virheellinen osoite")}]] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (nil? @code) - :on-click import-from-address}] - (when @import-error - [:p.decklist-import-error - (case @import-error - :not-found "Pakkalistaa ei löytynyt" - "Virhe pakkalistan latauksessa")])]] - [ui/tab {:label "Lataa tekstilista" - :value "load-text" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa tekstimuotoinen lista"] - [:p - "Kopioi tekstikenttään tekstimuotoinen lista." - "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] - [:pre - "4 Lightning Bolt\n4 Chain Lightning\n..."] - [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] - [:div.form - [text-field {:on-change decklist-on-change - :multi-line true - :rows 7 - :rows-max 7 - :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} - :full-width true - :name :text-decklist}] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (str/blank? @decklist) - :on-click import-decklist}]]]]])}))) + {:component-did-mount + (fn [] + (add-watch loaded? ::decklist-import + (fn [_ _ _ new] + (when new + (reset! selected nil) + (reset! address ""))))) + + :component-will-unmount + (fn [] + (remove-watch loaded? ::decklist-import)) + + :reagent-render + (fn decklist-import-render [] + [:div.decklist-import + [ui/tabs {:value @selected + :content-container-style (when @selected + {:border-bottom border + :border-left (when-not @mobile? border) + :border-right (when-not @mobile? border)})} + [ui/tab {:label "Lataa aiempi lista" + :value "load-previous" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa aiempi lista"] + [:p + "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " + [:span.address "https://decklist.pairings.fi/abcd..."] + ")."]] + [:div.form + [:div.text-field-container + [text-field {:on-change address-on-change + :floating-label-text "Osoite" + :full-width true + :error-text (when-not (or (str/blank? @address) + @code) + "Virheellinen osoite")}]] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (nil? @code) + :on-click import-from-address}] + (when @import-error + [:p.decklist-import-error + (case @import-error + :not-found "Pakkalistaa ei löytynyt" + "Virhe pakkalistan latauksessa")])]] + [ui/tab {:label "Lataa tekstilista" + :value "load-text" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa tekstimuotoinen lista"] + [:p + "Kopioi tekstikenttään tekstimuotoinen lista." + "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] + [:pre + "4 Lightning Bolt\n4 Chain Lightning\n..."] + [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] + [:div.form + [text-field {:on-change decklist-on-change + :multi-line true + :rows 7 + :rows-max 7 + :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} + :full-width true + :name :text-decklist}] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (str/blank? @decklist) + :on-click import-decklist}]]]]])}))) (defn error-list [errors] [ui/list @@ -330,7 +360,7 @@ [error-icon nil]])}])]) (defn decklist-submit [] - (let [decklist (subscribe [::subs/decklist]) + (let [decklist (subscribe [::subs/decklist-by-type]) player (reagent/cursor decklist [:player]) select-main #(dispatch [::events/select-board :main]) select-side #(dispatch [::events/select-board :side]) @@ -343,7 +373,7 @@ saved? (subscribe [::subs/saved?]) error? (subscribe [::subs/error :save-decklist]) page (subscribe [::common-subs/page]) - save-decklist #(dispatch [::events/save-decklist (:id @tournament) @decklist])] + save-decklist #(dispatch [::events/save-decklist (:id @tournament)])] (fn decklist-submit-render [] (let [errors (decklist-errors @decklist)] [:div#decklist-submit diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index f31a521..6246456 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -48,10 +48,9 @@ [:decklist-editor :error :save-tournament] true))) (reg-event-fx ::save-decklist - (fn [{:keys [db]} [_ tournament decklist]] - {:db (update db :decklist-editor merge {:decklist decklist - :saving true}) - :ws-send [:client/save-decklist [tournament decklist]]})) + (fn [{:keys [db]} [_ tournament]] + {:db (assoc-in db [:decklist-editor :saving] true) + :ws-send [:client/save-decklist [tournament (get-in db [:decklist-editor :decklist])]]})) (reg-event-fx :server/decklist-saved (fn [{:keys [db]} [_ id]] @@ -103,8 +102,9 @@ :ascending true}))))) (reg-event-fx ::load-decklist - (fn [_ [_ id]] - {:ws-send [:client/load-decklist id]})) + (fn [{:keys [db]} [_ id]] + {:db (assoc-in db [:decklist-editor :decklist] empty-decklist) + :ws-send [:client/load-decklist id]})) (reg-event-fx ::load-decklists (fn [_ [_ id]] @@ -144,16 +144,20 @@ (fn [db [_ error]] (assoc-in db [:decklist-editor :error :import-address] error))) -(defn ^:private add-card [{:keys [board] :as decklist} name] - (if (some #(= name (:name %)) (get decklist board)) +(defn ^:private add-card [{:keys [board] :as decklist} card] + (if (some #(= (:name card) (:name %)) (get decklist board)) decklist - (-> decklist - (update board conj (add-id-to-card {:name name, :quantity 1})) - (update-in [:count board] inc)))) + (let [new-card (-> card + (update :types set) + (assoc :quantity 1) + (add-id-to-card))] + (-> decklist + (update board conj new-card) + (update-in [:count board] inc))))) (reg-event-db ::add-card - (fn [db [_ name]] - (update-in db [:decklist-editor :decklist] add-card name))) + (fn [db [_ card]] + (update-in db [:decklist-editor :decklist] add-card card))) (defn ^:private set-quantity [decklist board id quantity] (let [index (util/index-where #(= id (:id %)) (get decklist board)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs index 0c4383e..c14b5a7 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/subscriptions/decklist.cljs @@ -1,7 +1,8 @@ (ns mtg-pairings-server.subscriptions.decklist (:require [re-frame.core :refer [reg-sub subscribe]] [clojure.string :as str] - [cljs-time.coerce :as coerce])) + [cljs-time.coerce :as coerce] + [mtg-pairings-server.util.decklist :refer [by-type]])) (reg-sub ::tournament (fn [db _] @@ -19,10 +20,20 @@ (fn [db _] (get-in db [:decklist-editor :decklist]))) +(reg-sub ::decklist-by-type + :<- [::decklist] + (fn [decklist _] + (by-type decklist))) + (reg-sub ::decklists (fn [db _] (get-in db [:decklist-editor :decklists]))) +(reg-sub ::decklists-by-type + :<- [::decklists] + (fn [decklists _] + (map by-type decklists))) + (reg-sub ::organizer-tournaments (fn [db _] (get-in db [:decklist-editor :organizer-tournaments]))) From c27771545276c911b3b34fe3d898bccd176c1d4d Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Mon, 8 Jul 2019 22:37:36 +0300 Subject: [PATCH 43/44] Deadline for submitting decklists --- .../mtg_pairings_server/styles/decklist.clj | 12 +- .../src/cljc/mtg_pairings_server/util.cljc | 4 +- .../components/decklist/organizer.cljs | 138 ++++------ .../components/decklist/print.cljs | 68 +++++ .../components/decklist/submit.cljs | 240 ++++++++++-------- .../mtg_pairings_server/events/decklist.cljs | 5 +- 6 files changed, 268 insertions(+), 199 deletions(-) create mode 100644 mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/print.cljs diff --git a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj index b22682d..64d28ac 100644 --- a/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj +++ b/mtg-pairings-server/src/clj/mtg_pairings_server/styles/decklist.clj @@ -44,8 +44,11 @@ [:.decklist-import [:.form {:padding-bottom (px 12)}]]) + (when-print + [:.no-print + {:display :none}]) [:.intro - [:.tournament-date :.tournament-name :.tournament-format + [:.tournament-date :.tournament-name :.tournament-format :.tournament-deadline {:font-weight :bold}]] [:h3 {:margin-bottom 0}] @@ -83,7 +86,6 @@ (def tournament-name-width (calc (- print-body-width letters-width date-width))) (def deck-name-width (calc (- print-body-width letters-width))) - (defstyles cardlists [:.decklists {:position :relative}] @@ -270,8 +272,8 @@ [:.deck-name {:width deck-name-width}]])]) -(defstyles organizer-decklist - [:.organizer-decklist +(defstyles print-decklist + [:.print-decklist {:position :relative :width (percent 100)} [:.label :.value @@ -349,11 +351,11 @@ table-row-link]]] [:#decklist-organizer-login {:margin "12px 24px"}] - organizer-decklist (when-print [:.decklist-organizer-header {:display "none !important"}])]) (defstyles styles organizer + print-decklist submit) diff --git a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc index 0574e5d..d8317a1 100644 --- a/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc +++ b/mtg-pairings-server/src/cljc/mtg_pairings_server/util.cljc @@ -55,7 +55,9 @@ (some->> datetime (format/unparse (format/formatters :date-time)))) (defn format-date-time [datetime] - (some->> datetime (format/unparse (format/formatter "dd.MM.yyyy HH:mm")))) + (some->> datetime + #?(:cljs time/to-default-time-zone) + (format/unparse (format/formatter "dd.MM.yyyy HH:mm")))) (defn today-or-yesterday? [date] (let [yesterday (time/minus (time/today) (time/days 1)) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs index e1e86e0..bb0f663 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/organizer.cljs @@ -7,6 +7,7 @@ [cljs-time.coerce :as coerce] [clojure.string :as str] [oops.core :refer [oget]] + [mtg-pairings-server.components.decklist.print :refer [render-decklist]] [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.decklist :as routes] [mtg-pairings-server.subscriptions.decklist :as subs] @@ -56,7 +57,8 @@ (str (get-host) submit-url)])) (defn tournament-row [tournament] - (let [column-style {:font-size "14px"} + (let [column-style {:font-size "14px" + :padding 0} link-props {:href (routes/organizer-tournament-path {:id (:id tournament)})}] [ui/table-row [ui/table-row-column {:class-name :date @@ -76,7 +78,7 @@ [:a.tournament-link link-props (:decklist tournament)]] [ui/table-row-column {:class-name :submit-page - :style column-style} + :style {:font-size "14px"}} [list-submit-link (:id tournament)]]])) (defn all-tournaments [] @@ -190,18 +192,32 @@ (when @error? [notice :error "Tallennus epäonnistui"])]))) +(defn date-picker [opts] + [ui/date-picker (merge {:container :inline + :dialog-container-style {:left "-9999px"} + :locale "fi-FI" + :auto-ok true + :DateTimeFormat (oget js/Intl "DateTimeFormat") + :text-field-style {:width "128px"}} + opts)]) + (defn tournament [id] (let [saved-tournament (subscribe [::subs/organizer-tournament]) decklists (subscribe [::subs/organizer-decklists]) saving? (subscribe [::subs/saving?]) - tournament (atom @saved-tournament) + tournament (atom nil) set-name #(swap! tournament assoc :name %) set-date (fn [_ date] - (swap! tournament assoc :date (to-local-date date))) + (swap! tournament assoc :date date)) + set-deadline (fn [_ date] + (swap! tournament assoc :deadline date)) set-format (fn [_ _ format] (swap! tournament assoc :format (keyword format))) save-tournament #(dispatch [::events/save-tournament - (select-keys @tournament [:id :name :format :date :deadline])]) + (-> @tournament + (select-keys [:id :name :format :date :deadline]) + (update :date to-local-date) + (update :deadline coerce/to-date-time))]) selected-decklists (atom #{}) on-select (fn [selection] (let [selection (js->clj selection)] @@ -214,7 +230,9 @@ (fn tournament-render [id] (when (and (nil? @tournament) (some? @saved-tournament)) - (reset! tournament @saved-tournament)) + (reset! tournament (-> @saved-tournament + (update :date coerce/to-date) + (update :deadline coerce/to-date)))) [:div#decklist-organizer-tournament [:div.tournament-info [:div.fields @@ -231,7 +249,8 @@ :value value :floating-label-text "Formaatti" :error-text (when-not value - "Formaatti on pakollinen")} + "Formaatti on pakollinen") + :style {:width "128px"}} [ui/menu-item {:value :standard :primary-text "Standard"}] [ui/menu-item {:value :modern @@ -239,19 +258,30 @@ [ui/menu-item {:value :legacy :primary-text "Legacy"}]])] [:div.field - (let [value (some-> (:date @tournament) - (coerce/to-long) - (js/Date.))] - [ui/date-picker {:value value - :error-text (when-not value - "Päivämäärä on pakollinen") - :on-change set-date - :container :inline - :dialog-container-style {:left "-9999px"} - :floating-label-text "Päivämäärä" - :locale "fi-FI" - :auto-ok true - :DateTimeFormat (oget js/Intl "DateTimeFormat")}])]] + (let [value (:date @tournament)] + [date-picker {:value value + :error-text (when-not value + "Päivämäärä on pakollinen") + :on-change set-date + :floating-label-text "Päivämäärä" + :min-date (js/Date.)}])] + [:div.field + (let [value (:deadline @tournament)] + [date-picker {:value value + :error-text (when-not value + "Listojen lähettämisen deadline on pakollinen") + :on-change set-deadline + :floating-label-text "Deadline" + :min-date (js/Date.)}])] + [:div.field + (let [value (:deadline @tournament)] + [ui/time-picker {:value value + :floating-label-text "Deadline klo" + :on-change set-deadline + :format "24hr" + :auto-ok true + :minutes-step 10 + :text-field-style {:width "128px"}}])]] [:div.link (when id [:p @@ -264,7 +294,8 @@ :disabled (or @saving? (str/blank? (:name @tournament)) (nil? (:format @tournament)) - (nil? (:date @tournament))) + (nil? (:date @tournament)) + (nil? (:deadline @tournament))) :style {:width "200px"}}] (if @saving? [ui/circular-progress @@ -287,68 +318,9 @@ :style {:margin-top "12px" :width "200px"}}]]] [:div.decklists - [decklist-table @decklists @selected-decklists on-select]]]))) - -(defn decklist-card [card] - [:div.card - [:div.quantity - (:quantity card)] - [:div.name - (:name card)]]) - -(defn render-decklist [decklist tournament] - (let [{:keys [player main side id], counts :count} decklist - {:keys [last-name first-name dci deck-name]} player - [l1 l2 l3] last-name - dci (vec dci) - {tournament-id :id, tournament-name :name, date :date} tournament] - [:div.organizer-decklist - [:div.first-letters - [:div.label "Alkukirjaimet"] - [:div.letter l1] - [:div.letter l2] - [:div.letter l3]] - [:div.deck-info - [:div.tournament-date - [:div.label "Päivä:"] - [:div.value (format-date date)]] - [:div.tournament-name - [:div.label "Turnaus:"] - [:div.value - [:a {:href (routes/organizer-tournament-path {:id tournament-id})} - tournament-name]]] - [:div.deck-name - [:div.label "Pakka:"] - [:div.value deck-name]]] - [:div.player-info - [:div.name - [:div.last-name - [:div.label "Sukunimi:"] - [:div.value last-name]] - [:div.first-name - [:div.label "Etunimi:"] - [:div.value first-name]]] - [:div.dci - [:div.label "DCI:"] - [:div.value - (for [index (range 10)] - ^{:key (str id "--dci--" index)} - [:span.digit (get dci index)])]]] - [:div.decklists - [:div.maindeck - [:h3 "Maindeck (" (:main counts) ")"] - (into [:div.cards] - (mapcat (fn [type] - (when-let [cards (get main type)] - (list* [:h4.type-header (type->header type)] - (for [card cards] - [decklist-card card]))))) - card-types)] - [:div.sideboard - [:h3 "Sideboard (" (:side counts) ")"] - (into [:div.cards] - (for [card side] - [decklist-card card]))]]])) + (if (seq @decklists) + [decklist-table @decklists @selected-decklists on-select] + [:p "Ei lähetettyjä listoja"])]]))) (defn view-decklist [] (let [decklist (subscribe [::subs/decklist-by-type]) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/print.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/print.cljs new file mode 100644 index 0000000..24a19f8 --- /dev/null +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/print.cljs @@ -0,0 +1,68 @@ +(ns mtg-pairings-server.components.decklist.print + (:require [reagent.core :as reagent :refer [atom]] + [cljsjs.material-ui] + [cljs-react-material-ui.reagent :as ui] + [mtg-pairings-server.routes.decklist :as routes] + [mtg-pairings-server.util :refer [format-date]] + [mtg-pairings-server.util.decklist :refer [card-types type->header]])) + +(defn decklist-card [card] + [:div.card + [:div.quantity + (:quantity card)] + [:div.name + (:name card)]]) + +(defn render-decklist [decklist tournament] + (let [{:keys [player main side id], counts :count} decklist + {:keys [last-name first-name dci deck-name]} player + [l1 l2 l3] last-name + dci (vec dci) + {tournament-id :id, tournament-name :name, date :date} tournament] + [:div.print-decklist + [:div.first-letters + [:div.label "Alkukirjaimet"] + [:div.letter l1] + [:div.letter l2] + [:div.letter l3]] + [:div.deck-info + [:div.tournament-date + [:div.label "Päivä:"] + [:div.value (format-date date)]] + [:div.tournament-name + [:div.label "Turnaus:"] + [:div.value + [:a {:href (routes/organizer-tournament-path {:id tournament-id})} + tournament-name]]] + [:div.deck-name + [:div.label "Pakka:"] + [:div.value deck-name]]] + [:div.player-info + [:div.name + [:div.last-name + [:div.label "Sukunimi:"] + [:div.value last-name]] + [:div.first-name + [:div.label "Etunimi:"] + [:div.value first-name]]] + [:div.dci + [:div.label "DCI:"] + [:div.value + (for [index (range 10)] + ^{:key (str id "--dci--" index)} + [:span.digit (get dci index)])]]] + [:div.decklists + [:div.maindeck + [:h3 "Maindeck (" (:main counts) ")"] + (into [:div.cards] + (mapcat (fn [type] + (when-let [cards (get main type)] + (list* [:h4.type-header (type->header type)] + (for [card cards] + [decklist-card card]))))) + card-types)] + [:div.sideboard + [:h3 "Sideboard (" (:side counts) ")"] + (into [:div.cards] + (for [card side] + [decklist-card card]))]]])) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs index 1f5301c..435c526 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/components/decklist/submit.cljs @@ -2,22 +2,24 @@ (:require [reagent.core :as reagent :refer [atom]] [reagent.ratom :refer [make-reaction]] [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] [cljsjs.material-ui] [cljs-react-material-ui.reagent :as ui] [cljs-react-material-ui.icons :as icons] + [cljs-time.core :as time] [oops.core :refer [oget]] [mtg-pairings-server.components.autosuggest :refer [autosuggest]] + [mtg-pairings-server.components.decklist.print :refer [render-decklist]] [mtg-pairings-server.components.tooltip :refer [tooltip]] [mtg-pairings-server.events.decklist :as events] [mtg-pairings-server.routes.decklist :as routes] [mtg-pairings-server.styles.common :as styles] [mtg-pairings-server.subscriptions.common :as common-subs] [mtg-pairings-server.subscriptions.decklist :as subs] - [mtg-pairings-server.util :refer [debounce dissoc-index format-date index-where get-host valid-email?]] + [mtg-pairings-server.util :refer [debounce dissoc-index format-date format-date-time index-where get-host valid-email?]] [mtg-pairings-server.util.decklist :refer [->text card-types type->header]] [mtg-pairings-server.util.mtg :refer [valid-dci?]] - [mtg-pairings-server.util.material-ui :refer [text-field]] - [clojure.string :as str])) + [mtg-pairings-server.util.material-ui :refer [text-field]])) (def basic? #{"Plains" "Island" "Swamp" "Mountain" "Forest" "Wastes" "Snow-Covered Plains" "Snow-Covered Island" "Snow-Covered Swamp" @@ -106,7 +108,9 @@ (fn decklist-table-row-render [_ {:keys [name quantity]} error] [ui/table-row [ui/table-row-column {:class-name :quantity - :style {:padding "0 12px"}} + :style {:padding "0 12px" + :text-align :center + :font-size "16px"}} (when quantity (into [ui/select-field {:value quantity :on-change on-change @@ -132,7 +136,7 @@ :style {:font-size "16px" :padding 0}} [ui/icon-button {:on-click on-delete} - [icons/content-remove-circle-outline]]] + [icons/action-delete]]] [ui/table-row-column {:class-name :error :style {:padding "12px" :overflow :visible}} @@ -140,14 +144,17 @@ [error-icon error])]]))) (defn decklist-header-row [type] - [ui/table-row - [ui/table-row-column {:class-name :quantity}] - [ui/table-row-column {:class-name :card - :style {:font-size "16px" - :font-weight :bold}} - (type->header type)] - [ui/table-row-column {:class-name :actions}] - [ui/table-row-column {:class-name :error}]]) + (let [mobile? (subscribe [::common-subs/mobile?])] + (fn decklist-header-row-render [type] + [ui/table-row + [ui/table-row-column {:class-name :quantity}] + [ui/table-row-column {:class-name :card + :style {:font-size "16px" + :font-weight :bold + :padding-left (if @mobile? 0 "24px")}} + (type->header type)] + [ui/table-row-column {:class-name :actions}] + [ui/table-row-column {:class-name :error}]]))) (defn table-body-by-type [decklist board error-cards] (mapcat (fn [type] @@ -276,80 +283,72 @@ (reset! selected nil)) import-error (subscribe [::subs/error :import-address]) border (styles/border "1px" :solid (styles/palette :light-grey))] - (reagent/create-class - {:component-did-mount - (fn [] - (add-watch loaded? ::decklist-import - (fn [_ _ _ new] - (when new - (reset! selected nil) - (reset! address ""))))) - - :component-will-unmount - (fn [] - (remove-watch loaded? ::decklist-import)) - - :reagent-render - (fn decklist-import-render [] - [:div.decklist-import - [ui/tabs {:value @selected - :content-container-style (when @selected - {:border-bottom border - :border-left (when-not @mobile? border) - :border-right (when-not @mobile? border)})} - [ui/tab {:label "Lataa aiempi lista" - :value "load-previous" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa aiempi lista"] - [:p - "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " - [:span.address "https://decklist.pairings.fi/abcd..."] - ")."]] - [:div.form - [:div.text-field-container - [text-field {:on-change address-on-change - :floating-label-text "Osoite" - :full-width true - :error-text (when-not (or (str/blank? @address) - @code) - "Virheellinen osoite")}]] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (nil? @code) - :on-click import-from-address}] - (when @import-error - [:p.decklist-import-error - (case @import-error - :not-found "Pakkalistaa ei löytynyt" - "Virhe pakkalistan latauksessa")])]] - [ui/tab {:label "Lataa tekstilista" - :value "load-text" - :on-active on-active - :style {:color :black}} - [:div.info - [:h3 - "Lataa tekstimuotoinen lista"] - [:p - "Kopioi tekstikenttään tekstimuotoinen lista." - "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] - [:pre - "4 Lightning Bolt\n4 Chain Lightning\n..."] - [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] - [:div.form - [text-field {:on-change decklist-on-change - :multi-line true - :rows 7 - :rows-max 7 - :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} - :full-width true - :name :text-decklist}] - [:br] - [ui/raised-button {:label "Lataa" - :disabled (str/blank? @decklist) - :on-click import-decklist}]]]]])}))) + (add-watch loaded? ::decklist-import + (fn [_ _ _ new] + (when new + (reset! selected nil) + (reset! address "") + (remove-watch loaded? ::decklist-import)))) + (fn decklist-import-render [] + [:div.decklist-import + [ui/tabs {:value @selected + :content-container-style (when @selected + {:border-bottom border + :border-left (when-not @mobile? border) + :border-right (when-not @mobile? border)})} + [ui/tab {:label "Lataa aiempi lista" + :value "load-previous" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa aiempi lista"] + [:p + "Lataa aiemmin syötetty pakkalista antamalla sen osoite (esim. " + [:span.address "https://decklist.pairings.fi/abcd..."] + ")."]] + [:div.form + [:div.text-field-container + [text-field {:on-change address-on-change + :floating-label-text "Osoite" + :full-width true + :error-text (when-not (or (str/blank? @address) + @code) + "Virheellinen osoite")}]] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (nil? @code) + :on-click import-from-address}] + (when @import-error + [:p.decklist-import-error + (case @import-error + :not-found "Pakkalistaa ei löytynyt" + "Virhe pakkalistan latauksessa")])]] + [ui/tab {:label "Lataa tekstilista" + :value "load-text" + :on-active on-active + :style {:color :black}} + [:div.info + [:h3 + "Lataa tekstimuotoinen lista"] + [:p + "Kopioi tekstikenttään tekstimuotoinen lista." + "Listassa tulee olla seuraavassa muodossa: lukumäärä, välilyönti, kortin nimi. Esimerkki:"] + [:pre + "4 Lightning Bolt\n4 Chain Lightning\n..."] + [:p "Maindeckin ja sideboardin väliin tulee rivi, jolla lukee pelkästään \"Sideboard\"."]] + [:div.form + [text-field {:on-change decklist-on-change + :multi-line true + :rows 7 + :rows-max 7 + :textarea-style {:background-color "rgba(0, 0, 0, 0.05)"} + :full-width true + :name :text-decklist}] + [:br] + [ui/raised-button {:label "Lataa" + :disabled (str/blank? @decklist) + :on-click import-decklist}]]]]]))) (defn error-list [errors] [ui/list @@ -359,39 +358,22 @@ :left-icon (reagent/as-element [:div [error-icon nil]])}])]) -(defn decklist-submit [] - (let [decklist (subscribe [::subs/decklist-by-type]) - player (reagent/cursor decklist [:player]) +(defn decklist-submit-form [tournament decklist] + (let [player (reagent/cursor decklist [:player]) select-main #(dispatch [::events/select-board :main]) select-side #(dispatch [::events/select-board :side]) button-style {:position :relative :top "-5px" :width "70px" :min-width "70px"} - tournament (subscribe [::subs/tournament]) saving? (subscribe [::subs/saving?]) saved? (subscribe [::subs/saved?]) error? (subscribe [::subs/error :save-decklist]) page (subscribe [::common-subs/page]) save-decklist #(dispatch [::events/save-decklist (:id @tournament)])] - (fn decklist-submit-render [] + (fn decklist-submit-form-render [tournament decklist] (let [errors (decklist-errors @decklist)] - [:div#decklist-submit - [:h2 "Lähetä pakkalista"] - [:p.intro - "Lähetä pakkalistasi " - [:span.tournament-date - (format-date (:date @tournament))] - " pelattavaan turnaukseen " - [:span.tournament-name - (:name @tournament)] - ", jonka formaatti on " - [:span.tournament-format - (case (:format @tournament) - :standard "Standard" - :modern "Modern" - :legacy "Legacy")] - "."] + [:div [:h3 "Pakkalista"] [input] [ui/raised-button @@ -438,3 +420,45 @@ [:p "Pakkalistan tallennus epäonnistui. Voit kopioida pakkalistasi tekstimuodossa alta ja yrittää myöhemmin uudelleen."] [:pre (->text @decklist)]]) [error-list errors]])))) + +(defn decklist-submit [] + (let [tournament (subscribe [::subs/tournament]) + decklist (subscribe [::subs/decklist-by-type]) + deadline-gone? (atom false) + update-deadline (fn update-deadline [] + (if (time/after? (time/now) (:deadline @tournament)) + (reset! deadline-gone? true) + (.setTimeout js/window update-deadline 1000)))] + (update-deadline) + (fn decklist-submit-render [] + [:div#decklist-submit + [:div {:class (when @deadline-gone? :no-print)} + [:h2 "Lähetä pakkalista"] + [:p.intro + "Lähetä pakkalistasi " + [:span.tournament-date + (format-date (:date @tournament))] + " pelattavaan turnaukseen " + [:span.tournament-name + (:name @tournament)] + ", jonka formaatti on " + [:span.tournament-format + (case (:format @tournament) + :standard "Standard" + :modern "Modern" + :legacy "Legacy")] + "."] + [:p.intro + "Lista on lähetettävä viimeistään " + [:span.tournament-deadline + (format-date-time (:deadline @tournament))] + "."] + (if-not @deadline-gone? + [decklist-submit-form tournament decklist] + [:div + [:p.deadline-gone + "Listojen lähetys tähän turnaukseen on päättynyt."] + (when (:id @decklist) + [:h3 "Lähettämäsi lista"])])] + (when (and @deadline-gone? (:id @decklist)) + [render-decklist @decklist @tournament])]))) diff --git a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs index 6246456..6c728cd 100644 --- a/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs +++ b/mtg-pairings-server/src/cljs/mtg_pairings_server/events/decklist.cljs @@ -80,8 +80,9 @@ (reg-event-fx ::save-tournament (fn [{:keys [db]} [_ tournament]] - {:db (update db :decklist-editor merge {:saving true - :organizer-tournament tournament}) + {:db (-> db + (assoc-in [:decklist-editor :saving] true) + (update-in [:decklist-editor :organizer-tournament] merge tournament)) :ws-send [:client/save-decklist-organizer-tournament tournament]})) (reg-event-fx :server/organizer-tournament-saved From 64b9d1282521bb3de6f36a21ad353a52fe5b650e Mon Sep 17 00:00:00 2001 From: Arttu Kaipiainen Date: Thu, 11 Jul 2019 11:06:10 +0300 Subject: [PATCH 44/44] Update to .NET 4.7.2 --- .../MtgPairings/MtgPairings/ApiKeyDialog.xaml | 70 +-- .../MtgPairings/ApiKeyDialog.xaml.cs | 88 +-- .../MtgPairings/MtgPairings/App.config | 14 +- .../MtgPairings/MtgPairings/App.xaml.cs | 84 +-- .../MtgPairings/Domain/TrackableTournament.cs | 136 ++--- .../MtgPairings/Domain/UploadEvent.cs | 44 +- .../MtgPairings/MtgPairings/MainWindow.xaml | 130 ++--- .../MtgPairings/MainWindow.xaml.cs | 515 +++++++++--------- .../MtgPairings/MtgPairings.csproj | 380 ++++++------- .../MtgPairings/Properties/AssemblyInfo.cs | 110 ++-- .../Properties/Resources.Designer.cs | 2 +- .../Service/UploadFailedException.cs | 52 +- .../MtgPairings/Service/UploadWorker.cs | 190 +++---- .../MtgPairings/Service/Uploader.cs | 392 ++++++------- .../MtgPairings/Service/VersionChecker.cs | 76 +-- .../MtgPairings/VersionDialog.xaml | 64 +-- .../MtgPairings/VersionDialog.xaml.cs | 56 +- .../MtgPairings/MtgPairings/packages.config | 7 +- .../MtgPairingsTest/MtgPairingsTest.csproj | 11 +- .../MtgPairingsTest/packages.config | 2 +- 20 files changed, 1208 insertions(+), 1215 deletions(-) diff --git a/mtg-pairings-win/MtgPairings/MtgPairings/ApiKeyDialog.xaml b/mtg-pairings-win/MtgPairings/MtgPairings/ApiKeyDialog.xaml index d15e6ad..0e4a236 100644 --- a/mtg-pairings-win/MtgPairings/MtgPairings/ApiKeyDialog.xaml +++ b/mtg-pairings-win/MtgPairings/MtgPairings/ApiKeyDialog.xaml @@ -1,35 +1,35 @@ - - - - - - - - - - - - - -