diff --git a/project.clj b/project.clj index 8cc622297..6635733a7 100644 --- a/project.clj +++ b/project.clj @@ -120,7 +120,11 @@ :asset-path "/js/compiled/out" :output-to "resources/public/js/compiled/orcpub.js" :output-dir "resources/public/js/compiled/out" - :source-map-timestamp true}}}} + :source-map-timestamp true + :pretty-print true + :closure-defines {goog.DEBUG true} + :optimizations :none + }}}} :figwheel {;; :http-server-root "public" ;; default and assumes "resources" ;; :server-port 3449 ;; default @@ -185,9 +189,14 @@ [cider/piggieback "0.4.0"] [org.clojure/test.check "0.9.0"] [day8.re-frame/re-frame-10x "0.3.7"]] + :env {:dev-mode "true"} ;; need to add dev source path here to get user.clj loaded :source-paths ["web/cljs" "src/clj" "src/cljc" "src/cljs" "dev"] - :cljsbuild {:builds {:dev {:compiler {:closure-defines {"re_frame.trace.trace_enabled_QMARK_" true} + :cljsbuild {:builds {:dev {:compiler {:closure-defines {"re_frame.trace.trace_enabled_QMARK_" true + goog.DEBUG true + } + :optimizations :none + :pretty-print true ;; To console.log CLJS data-structures make sure you enable devtools in Chrome ;; https://github.com/binaryage/cljs-devtools :preloads [devtools.preload day8.re-frame-10x.preload]}}}} @@ -231,7 +240,7 @@ ;;:output-dir "resources/public/js/compiled/out" :optimizations :advanced :pretty-print false}}}}} - :lint {:dependencies [[clj-kondo "RELEASE"]]} + :lint {:dependencies [[clj-kondo "2024.05.22"]]} ;; Use like: lein with-profile +start-server repl :start-server {:repl-options {:init-ns user :init (start-server)}}}) diff --git a/src/clj/orcpub/db/schema.clj b/src/clj/orcpub/db/schema.clj index 6ddbbe9d0..48c3170bd 100644 --- a/src/clj/orcpub/db/schema.clj +++ b/src/clj/orcpub/db/schema.clj @@ -372,6 +372,7 @@ ::weapon5e/melee? ::weapon5e/ranged? ::weapon5e/heavy? + ::weapon5e/light? ::weapon5e/thrown? ::weapon5e/two-handed? ::weapon5e/finesse? diff --git a/src/clj/orcpub/index.clj b/src/clj/orcpub/index.clj index ec1302c1a..742aab29a 100644 --- a/src/clj/orcpub/index.clj +++ b/src/clj/orcpub/index.clj @@ -2,7 +2,11 @@ (:require [hiccup.page :refer [html5 include-css include-js]] [orcpub.oauth :as oauth] [orcpub.dnd.e5.views-2 :as views-2] - [orcpub.favicon :as fi])) + [orcpub.favicon :as fi] + [environ.core :refer [env]] + )) + +(def devmode? (env :dev-mode)) (defn meta-tag [property content] (if content @@ -124,18 +128,36 @@ html { (include-js "/js/cookies.js") (include-css "/assets/font-awesome/5.13.1/css/all.min.css") (include-css "https://fonts.googleapis.com/css?family=Open+Sans") - [:script " window.start.init({Palette:\"palette7\",Mode:\"banner bottom\",})"] - [:script - "let plugins = localStorage.getItem ('plugins'); - if(plugins === null || plugins === '{}') - { - fetch('https://' + window.location.host + '/homebrew.orcbrew') - .then(resp => resp.text()) - .then(text => { - if(!text.toUpperCase().includes('NOT FOUND')){ - localStorage.setItem('plugins',text); - window.location.reload(false); - } - }); - } - "]])) + [:script " window.start.init({Palette:\"palette7\",Mode:\"banner bottom\",})"] + (if devmode? + (println "dev mode - no script") + + [:script + "const protocol = window.location.protocol; + const apiUrl = `${protocol}://${window.location.host}`; + const pluginUrl = `${apiUrl}/homebrew.orcbrew`; + + let plugins = localStorage.getItem('plugins'); + if (plugins === null || plugins === '{}') { + fetch(pluginUrl) + .then(resp => { + if (!resp.ok) { + throw new Error(`Failed to fetch plugins: ${resp.status} ${resp.statusText}`); + } + return resp.text(); + }) + .then(text => { + if (!text.toUpperCase().includes('NOT FOUND')) { + localStorage.setItem('plugins', text); + window.location.reload(false); + } + }) + .catch(error => { + console.error('Error fetching plugins:', error); + // You can also add a fallback or default behavior here + }); + } + "] + ) + ])) + \ No newline at end of file diff --git a/src/clj/orcpub/styles/core.clj b/src/clj/orcpub/styles/core.clj index ce95ccfa6..8062422dc 100644 --- a/src/clj/orcpub/styles/core.clj +++ b/src/clj/orcpub/styles/core.clj @@ -933,8 +933,7 @@ {:background-color "rgba(0, 0, 0, 0.5)" :-webkit-backdrop-filter "blur(3px)" :backdrop-filter "blur(3px)" - :border-radius "5px" - }] + :border-radius "5px"}] [:.header-tab.mobile [:.title @@ -1291,10 +1290,52 @@ [:table.striped [:tr [(s/& (s/nth-child :even)) - {:background-color "rgba(0, 0, 0, 0.1)"}]]]]] + {:background-color "rgba(0, 0, 0, 0.1)"}]]]] + + ;;;; "Modal" styles + [:.modal-container + {:background-image "linear-gradient(to right, #d35730, #eda41e)" + :padding ".5em 2em"}] + + [:.modal-container :.m-b-10, + :.modal-container :.link-button + {:font-weight "bold"}] + + [:.modal-container :.link-button + {:color "#f7c257" + ;:font-weight "bold" + }] + + ;;;; WARNING TOOLTIP "warntip" + [:.warntiptext + {:width "20%" + :margin-top "10px" + :background-color "#d94b20" + :color "#fff" + :text-align "center" + :padding "5px 0" + :border-radius "0 0 6px 6px" + :position "absolute" + :border "solid 1px #e96868" + :z-index 1}] + + [:.warntip :.warntiptext + [:&:after + {:content "\" \"" + :position "absolute" + :bottom "100%" ;; At the bottom of the tooltip + :left "50%" + :margin-left "-5px" + :border-width "10px" + :border-style "solid" + :border-color "transparent transparent #e96868 transparent"}]]];concat-bracket margin-lefts margin-tops widths font-sizes props - media-queries)) + media-queries) ;concat +);def app + + + ;;);concat;app diff --git a/src/cljc/orcpub/common.cljc b/src/cljc/orcpub/common.cljc index e03261821..af39bef7a 100644 --- a/src/cljc/orcpub/common.cljc +++ b/src/cljc/orcpub/common.cljc @@ -177,3 +177,7 @@ (fn [[k v]] (str (safe-capitalize-kw k) " " (bonus-str v))) m))) +;; Case Insensitive `sort-by` +(defn aloof-sort-by [sorter coll] + (sort-by (comp s/lower-case sorter) coll) + ) diff --git a/src/cljc/orcpub/dnd/e5/magic_items.cljc b/src/cljc/orcpub/dnd/e5/magic_items.cljc index 3ad57a46b..52f76eba7 100644 --- a/src/cljc/orcpub/dnd/e5/magic_items.cljc +++ b/src/cljc/orcpub/dnd/e5/magic_items.cljc @@ -15,8 +15,10 @@ [clojure.set :refer [intersection difference]]) #?(:cljs (:require-macros [orcpub.dnd.e5.modifiers :as mod5e]))) -(spec/def ::name string?) -(spec/def ::type keyword?) +;(spec/def ::name string?) +(spec/def ::name (spec/and string? common/starts-with-letter?)) +;(spec/def ::type keyword?) +(spec/def ::type (spec/and keyword? common/keyword-starts-with-letter?)) (spec/def ::rarity keyword?) (spec/def ::description string?) (spec/def ::magical-attack-bonus int?) @@ -231,6 +233,7 @@ ::weapons5e/melee? ::weapons5e/ranged? ::weapons5e/heavy? + ::weapons5e/light? ::weapons5e/thrown? ::weapons5e/two-handed? ::weapons5e/finesse? diff --git a/src/cljc/orcpub/dnd/e5/weapons.cljc b/src/cljc/orcpub/dnd/e5/weapons.cljc index 55d953370..aa39f2f62 100644 --- a/src/cljc/orcpub/dnd/e5/weapons.cljc +++ b/src/cljc/orcpub/dnd/e5/weapons.cljc @@ -370,13 +370,47 @@ ::thrown true, ::range {::min 5, ::max 15}, :key :net - ::special? true}]) + ::special? true} + ;;;; + ;; Community Designed Weapons + ;;;; + {:name "Firearm, Hand (DMV)", + ::damage-type :piercing, + ::damage-die 10, + ::type :martial, + ::damage-die-count 1, + ::ranged? true, + ::heavy? true, + ::range {::min 30, ::max 90}, + :key :firearm-hand + ::two-handed? false} + {:name "Firearm, Burst (DMV)", + ::damage-type :piercing, + ::damage-die 6, + ::type :martial, + ::damage-die-count 2, + ::ranged? true, + ::heavy? true, + ::range {::min 10, ::max 30}, + :key :firearm-burst + ::two-handed? true} + {:name "Firearm, Long (DMV)", + ::damage-type :piercing, + ::damage-die 12, + ::type :martial, + ::damage-die-count 1, + ::ranged? true, + ::heavy? true, + ::range {::min 60, ::max 120}, + :key :firearm-long + ::two-handed? true}]) (def ammunition (common/add-keys [{:name "Arrow" ::type :ammunition :sell-qty 20 :cost {:num 1 :type :gp} :weight "1 lb."} {:name "Blowgun needle" ::type :ammunition :sell-qty 50 :cost {:num 1 :type :gp} :weight "1 lb."} {:name "Crossbow bolt" ::type :ammunition :sell-qty 20 :cost {:num 1 :type :gp} :weight "1½ lb."} + {:name "Firearm bullet" ::type :ammunition :sell-qty 20 :cost {:num 2 :type :gp} :weight "1½ lb."} {:name "Sling bullet" ::type :ammunition :sell-qty 20 :cost {:num 4 :type :cp} :weight "1½ lb."}])) (def weapons-map diff --git a/src/cljc/orcpub/pdf_spec.cljc b/src/cljc/orcpub/pdf_spec.cljc index 9aa5d0a1f..aa515c6cf 100644 --- a/src/cljc/orcpub/pdf_spec.cljc +++ b/src/cljc/orcpub/pdf_spec.cljc @@ -120,13 +120,13 @@ (defn traits-fields [built-char] (let [traits-by-type (group-by :type (es/entity-val built-char :traits)) - bonus-actions (sort-by :name (concat (es/entity-val built-char :bonus-actions) + bonus-actions (common/aloof-sort-by :name (concat (es/entity-val built-char :bonus-actions) (traits-by-type :b-action))) - actions (sort-by :name (concat (es/entity-val built-char :actions) + actions (common/aloof-sort-by :name (concat (es/entity-val built-char :actions) (traits-by-type :action))) - reactions (sort-by :name (concat (es/entity-val built-char :reactions) + reactions (common/aloof-sort-by :name (concat (es/entity-val built-char :reactions) (traits-by-type :reaction))) - traits (sort-by :name (traits-by-type nil)) + traits (common/aloof-sort-by :name (traits-by-type nil)) traits-str (traits-string traits) actions? (or (seq bonus-actions) (seq actions) diff --git a/src/cljs/orcpub/character_builder.cljs b/src/cljs/orcpub/character_builder.cljs index d2c8180d3..c49864cd7 100644 --- a/src/cljs/orcpub/character_builder.cljs +++ b/src/cljs/orcpub/character_builder.cljs @@ -339,9 +339,10 @@ {:name name :key (or key id)}) +;;; selection creator for character builder (defn inventory-adder [key options selected-keys] [comps/selection-adder - (sort-by + (common/aloof-sort-by :name (sequence (comp diff --git a/src/cljs/orcpub/dnd/e5/equipment_subs.cljs b/src/cljs/orcpub/dnd/e5/equipment_subs.cljs index 32e6deb75..b6c5a9f9c 100644 --- a/src/cljs/orcpub/dnd/e5/equipment_subs.cljs +++ b/src/cljs/orcpub/dnd/e5/equipment_subs.cljs @@ -19,12 +19,15 @@ [orcpub.route-map :as routes] [orcpub.dnd.e5.events :refer [url-for-route] :as events] [reagent.ratom :as ra] + [clojure.string :as s] [cljs-http.client :as http] [cljs.core.async :refer [ db :user-data :token)] @@ -36,7 +39,8 @@ (reg-sub-raw ::mi5e/custom-items (fn [app-db [_ user-data]] - (go (dispatch [:set-loading true]) + (if (and (:user @app-db) (:token (:user @app-db))) + (go (dispatch [:set-loading true]) (let [response ( prof-kw prof-map :name) (common/kw-to-name prof-kw))) @@ -3497,6 +3498,12 @@ (fn [event-kw & [arg-fn]] #(dispatch [event-kw (if arg-fn (arg-fn %) %)])))) +(defn delete-item-handler [item-key] + (fn [] + (dispatch [::mi/delete-custom-item item-key]) + (dispatch [::mi/hide-delete-confirmation item-key]) + (dispatch [::mi/reset-item]))) + (defn print-button-style [print-button-enabled] (if print-button-enabled {} @@ -3656,7 +3663,7 @@ (let [item-key (if (re-matches #"\d+" key) (js/parseInt key) (keyword key)) - item @(subscribe [::mi/item item-key]) + item @(subscribe [::mi/custom-item item-key]) username @(subscribe [:username]) owner? (= username (::mi/owner item))] [content-page @@ -3666,13 +3673,14 @@ [(if owner? {:title "Delete" :icon "trash" - :on-click (make-event-handler ::mi/delete-custom-item item-key)}) + :on-click (delete-item-handler item-key)}) (if owner? {:title "Edit" :icon "pencil" - :on-click (make-event-handler [::mi/edit-custom-item item])})]) + :on-click (make-event-handler ::mi/edit-custom-item item)})]) [:div.p-10.main-text-color - [item-component item]]])) + [item-component item] + ]])) (defn base-builder-field [name comp] [:div.field.main-text-color.m-t-0 @@ -3832,8 +3840,18 @@ {:name "All" :key :all} {:name "All Swords" :key :sword} {:name "All Axes" :key :axe}] - @(subscribe [::mi/custom-and-standard-weapons]))))])]] - (if other? + (common/aloof-sort-by :name + @(subscribe [::mi/custom-and-standard-weapons]) + ) + )))])]] + (when other? + ;;;batch-set multiple default values + (doseq [evt [[::mi/set-item-damage-die-count 1] + [::mi/set-item-damage-die 4] + [::mi/set-item-weapon-type :simple] + [::mi/set-item-melee-ranged :melee]] + ] + (dispatch evt)) [:div.main-text-color.m-b-10.m-t-10 [:span.f-s-18.f-w-b "Base Weapon Details"] [:div.flex.flex-wrap.m-t-10 @@ -3855,6 +3873,9 @@ [:div.m-l-10 {:on-click (make-event-handler ::mi/toggle-item-heavy?)} [labeled-checkbox "Heavy?" @(subscribe [::mi/item-heavy?])]] + [:div.m-l-10 + {:on-click (make-event-handler ::mi/toggle-item-light?)} + [labeled-checkbox "Light?" @(subscribe [::mi/item-light?])]] [:div.m-l-10 {:on-click (make-event-handler ::mi/toggle-item-ammunition?)} [labeled-checkbox "Ammunition?" @(subscribe [::mi/item-ammunition?])]]] @@ -3936,7 +3957,9 @@ :title (name type)}) damage-types/damage-types) :value @(subscribe [::mi/item-damage-type]) - :on-change (make-arg-event-handler ::mi/set-item-damage-type)}]]]])])) + :on-change (make-arg-event-handler ::mi/set-item-damage-type) + }]]]] + )])) (defn item-ability-bonuses [] @@ -4109,6 +4132,9 @@ {:class-name "input h-40" :type type}]]) +#_(defn item-input-field [title prop item & [class-names]] + (builder-input-field title prop item ::mi/set-item-name class-names)) + (defn spell-input-field [title prop spell & [class-names]] (builder-input-field title prop spell ::spells/set-spell-prop class-names)) @@ -4972,19 +4998,69 @@ false #(dispatch [::feats/toggle-feat-prop kw])])]]]) +;;;; TODO remove when no longer useful for debugging +#_(defn print-plugin-names [] + (let [plugins @(subscribe [::e5/plugins])] + (doseq [plugin-name (keys plugins)] + (js/console.log plugin-name)))) + +(def option-pack-styles + {:class-name "flex-grow-1 m-l-5 m-b-20" + :name "option-pack"}) + +(defn get-plugin-names [] + (let [plugins @(subscribe [::e5/plugins])] + (map (fn [plugin-name] + {:value plugin-name + :title plugin-name}) + (sort-by s/lower-case (keys plugins))))) + +;;; Create a datalist element and load the plugin names +(defn plugin-datalist [label plugin-val dispatch-event] + (let [selected-value (atom (or (:option-pack plugin-val) "")) ;TODO: reframe functions may or may not help handle this more efficiently + ] + (fn [] + [:div.flex-grow-1 + {:class-name "m-l-5 m-b-20" + :name "option-pack"} + [:div.f-w-b.m-b-5 label] + [:input {:type "text" + :list "plugins-list" + :name "plugins-choice" + :id "plugins-choice" + :class "input h-40" + :placeholder "Default Option Source" + :value @selected-value + :onChange #(do + ; When user types in input field: + ; 1. Update the local state of the component with the new value + ; 2. Dispatch event to update the state of the entire app + ; w/ new value to save to app db (can be used elsewhere in app) + (reset! selected-value (-> % .-target .-value)) + (dispatch [dispatch-event :option-pack @selected-value]) + ) + }] + [:datalist {:id "plugins-list" :class "width-100-p"} + (for [{:keys [title value]} (get-plugin-names)] + [:option {:key title :value value}])] + ] + ))) + (defn feat-builder [] - (let [feat @(subscribe [::feats/builder-item])] + (let [feat @(subscribe [::feats/builder-item]) + plugins @(subscribe [::e5/plugins])] [:div.p-20.main-text-color [:div.m-b-20.flex.flex-wrap [feat-input-field "Name" :name feat] - [feat-input-field - option-source-name-label - :option-pack - feat - "m-l-5 m-b-20"] + [plugin-datalist + option-source-name-label + feat + ::feats/set-feat-prop + ] + ;"m-l-5 m-b-20"] [:div.w-100-p [:div.f-w-b "Description"] @@ -5087,7 +5163,7 @@ < :weapon-prof {:name "Weapon Proficiency" :value-fn keyword - :values (concat + :values (common/aloof-sort-by :title (concat (map (fn [type] {:title (str "All " (name type)) @@ -5095,7 +5171,8 @@ [:simple :martial]) (map obj-to-item - @(subscribe [::mi/custom-and-standard-weapons])))} + @(subscribe [::mi/custom-and-standard-weapons])) + ))} :num-attacks {:name "Number of Attacks" :value-fn js/parseInt :values (map @@ -5364,12 +5441,11 @@ "Name" :name class]] - [:div.m-b-20.flex-grow-1 - [class-input-field - option-source-name-label - :option-pack - class - "m-l-5 m-b-20"]]] + [plugin-datalist + option-source-name-label + class + ::classes/set-class-prop] + ] [:div.m-b-20 [:div.f-w-b "Description"] @@ -5677,11 +5753,12 @@ classes) :value (get subclass :class) :on-change #(dispatch [::classes/set-subclass-prop :class (keyword %)])}]] - [subclass-input-field - option-source-name-label - :option-pack - subclass - "m-l-5 m-b-20"]] + [plugin-datalist + option-source-name-label + subclass + ::classes/set-subclass-prop + ] + ] (if (#{:fighter :rogue :warlock :cleric :paladin} class-key) (let [spellcasting (get subclass :spellcasting) spellcasting? (some? spellcasting)] @@ -5820,11 +5897,11 @@ races) :value (get subrace :race) :on-change #(dispatch [::races/set-subrace-prop :race (keyword %)])}]] - [subrace-input-field - option-source-name-label - :option-pack - subrace - "m-l-5 m-b-20"]] + [plugin-datalist + option-source-name-label + subrace + ::races/set-subrace-prop] + ] [:div.m-b-20.flex.flex-wrap [:div.m-r-5 [labeled-dropdown @@ -5934,11 +6011,11 @@ "Name" :name race] - [race-input-field - option-source-name-label - :option-pack - race - "m-l-5 m-b-20"]] + [plugin-datalist + option-source-name-label + race + ::races/set-race-prop] + ] [:div.m-b-20 [:div.f-w-b "Description"] @@ -6083,11 +6160,11 @@ "Name" :name background] - [background-input-field + [plugin-datalist option-source-name-label - :option-pack background - "m-l-5 m-b-20"]] + ::bg/set-background-prop] + ] [:div.m-b-20 [:div.f-w-b "Description"] @@ -6116,11 +6193,11 @@ :name selection "m-b-20"] - [selection-input-field + [plugin-datalist option-source-name-label - :option-pack selection - "m-l-5 m-b-20"]] + ::selections/set-selection-prop] + ] [:div [:div.flex.justify-cont-s-b [:div.f-s-24.f-w-b "Options"] @@ -6162,11 +6239,11 @@ :name language "m-b-20"] - [language-input-field + [plugin-datalist option-source-name-label - :option-pack language - "m-l-5 m-b-20"]] + ::langs/set-language-prop] + ] [:div.w-100-p [:div.f-s-24.f-w-b "Description"] @@ -6183,11 +6260,11 @@ :name boon "m-b-20"] - [boon-input-field + [plugin-datalist option-source-name-label - :option-pack boon - "m-l-5 m-b-20"]] + ::classes/set-boon-prop] + ] [:div.w-100-p [:div.f-s-24.f-w-b "Description"] @@ -6204,11 +6281,11 @@ :name invocation "m-b-20"] - [invocation-input-field + [plugin-datalist option-source-name-label - :option-pack invocation - "m-l-5 m-b-20"]] + ::classes/set-invocation-prop] + ] [:div.w-100-p [:div.f-s-24.f-w-b "Description"] @@ -6248,11 +6325,11 @@ :name monster "m-b-20 flex-grow-1"] - [monster-input-field + [plugin-datalist option-source-name-label - :option-pack monster - "m-l-5 m-b-20 flex-grow-1"]] + ::monsters/set-monster-prop] + ] [:div.flex.w-100-p.flex-wrap [:div.flex-grow-1.m-b-20.m-l-5 @@ -6327,7 +6404,7 @@ (get hit-points :modifier 0) #(let [v (js/parseInt %)] (dispatch [::monsters/set-monster-path-prop [:hit-points :modifier] (if (not (js/isNaN v)) v)])) - {:class-name "input h-40"}]];] + {:class-name "input h-40"}]] [monster-input-field "Speed" :speed @@ -6940,11 +7017,10 @@ :name encounter "m-b-20"] - [encounter-input-field + [plugin-datalist option-source-name-label - :option-pack encounter - "m-l-5 m-b-20"]] + ::encounters/set-encounter-prop]] [:div.m-t-20 [:div.f-s-24.f-w-b "Creatures"] [:div @@ -6966,11 +7042,11 @@ :name spell "m-b-20"] - [spell-input-field + [plugin-datalist option-source-name-label - :option-pack spell - "m-l-5 m-b-20"]] + ::spells/set-spell-prop] + ] [:div.flex.w-100-p.flex-wrap [:div.flex-grow-1.m-b-20 @@ -6993,7 +7069,7 @@ (sort spells/schools)) :value school :on-change #(dispatch [::spells/set-spell-prop :school %])}]] - [;:div.flex.flex-wrap + [ :div.flex-grow-1.m-l-5 [:div.m-t-20.m-r-20.m-b-10 [comps/labeled-checkbox @@ -7040,6 +7116,33 @@ [:span.m-l-5 name]]) @(subscribe [::spells/spellcasting-classes]))]]])) +(defn validate-name [name] + (if (nil? name) + [] ;if nil, no error + (if (common/starts-with-letter? (str name)) + nil + "Name must start with a letter" + ) + )) + +(def invalid-styling + {:color "red" + :font-size "12px" + :display "block"} + ) + +(defn valid-wel [name] + (when-let [messages (validate-name name)] + [:span {:required true + :id "verify-name" + :class "warntiptext" + :data-tip messages + :aria-live "polite" + } + messages] + ) + ) + (defn item-builder [] (let [{:keys [::mi/name ::mi/type ::mi/rarity ::mi/description ::mi/attunement] :as item} @(subscribe [::mi/builder-item]) @@ -7047,12 +7150,17 @@ item-rarities @(subscribe [::mi/rarities])] [:div.p-20.main-text-color [:div.flex.w-100-p.flex-wrap - [:div.flex-grow-1.m-b-20 + [:div.flex-grow-1.m-b-20.warntip [input-builder-field - "Item Name" - name - #(dispatch [::mi/set-item-name %]) - {:class-name "input h-40"}]] + "Item Name" ;:name + name + #(dispatch [::mi/set-item-name %]) + {:class-name "input h-40"}] + (when (seq name) + (valid-wel name)) + #_(when-let [messages (validate-name name)] + (validation-messages messages)) + ] [:div.flex-grow-1.m-l-5 (base-builder-field "Type" @@ -7453,6 +7561,7 @@ "Next"]]]) :hide-header-message? true]) +;; events are set and passed by the individual pages defined below this (defn builder-page [item-title reset-event save-event builder & [title]] [content-page (or title (str item-title " Builder")) @@ -7472,9 +7581,88 @@ :on-click #(dispatch [::combat/reset-combat])}] [combat-tracker]]) -(defn item-builder-page [] +#_(defn item-builder-page [] (builder-page "Item" ::mi/reset-item ::mi/save-item item-builder)) +#_(defn item-page [{:keys [key] :as arg}] + (let [item-key (if (re-matches #"\d+" key) + (js/parseInt key) + (keyword key)) + item @(subscribe [::mi/item item-key]) + username @(subscribe [:username]) + owner? (= username (::mi/owner item))] + [content-page + "Item Page" + (remove + nil? + [(if owner? + {:title "Delete" + :icon "trash" + :on-click (delete-item-handler item-key)}) + (if owner? + {:title "Edit" + :icon "pencil" + :on-click (make-event-handler [::mi/edit-custom-item item])})]) + [:div.p-10.main-text-color + [item-component item]]])) + +(defn get-owner? [item-key] ;item-key could be other things that need ownership with a refactor + (let [username @(subscribe [:username]) + item @(subscribe [::mi/custom-item item-key]) + builder-item @(subscribe [::mi/builder-item item-key])] + #_(println "username" username "item" item "item-key" item-key "ownitem" (::mi/owner item) "ownbuild" (::mi/owner builder-item) "time" common/ptime) + (= username (or (::mi/owner item) (::mi/owner builder-item))) + )) + +(defn deletion-modal-with [builder-page item-key] + (let [show? @(subscribe [::mi/delete-confirmation-shown? item-key]) + ] + [:span + [:div {:class (if show? "modal-container" "modal-container hidden")} + [:div.modal + [:div.modal_content + [:div.m-b-10 "Are you sure you want to delete this item?"] + [:div + [:button.form-button + {:on-click (make-event-handler ::mi/hide-delete-confirmation item-key)} + "cancel"] + [:span.link-button + {:on-click (delete-item-handler item-key)} + "delete"] + ]]]] + [builder-page]] + ) + ) + +(defn item-builder-buttons [item-key item] + (let [owner? (get-owner? item-key) + base-buttons [{:title "New Item" + :icon "plus" + :on-click #(dispatch [::mi/reset-item])} + {:title "Save to Browser Storage" + :icon "save" + :on-click #(dispatch [::mi/save-item])}] + ] + (if (and item-key owner?) ;Show if we have an item key and the user owns it + (conj base-buttons {:title "Delete" + :icon "trash" + :on-click (make-event-handler ::mi/show-delete-confirmation item-key)}) + base-buttons) + )) + +(defn item-builder-page [] + (let [item (subscribe [::mi/builder-item]) + item-key (:db/id @item) + buttons (item-builder-buttons item-key item) + ] + [content-page + "Item Builder" + buttons + [deletion-modal-with + item-builder + item-key] + ])) + (defn spell-builder-page [] (builder-page "Spell" ::spells/reset-spell ::spells/save-spell spell-builder)) @@ -7935,7 +8123,8 @@ (if homebrew? [:button.form-button.m-l-5 {:on-click (make-event-handler ::spells/delete-spell spell)} - "delete"])] + "delete"]) + ] [spell-component spell true]])]])) (defn spell-list-items [device-type] @@ -7993,8 +8182,25 @@ (if (= username owner) [:button.form-button.m-l-5 {:on-click (make-event-handler ::mi/edit-custom-item @(subscribe [::mi/custom-item id]))} - "edit"])] - [item-component item]])]])) + "edit"]) + (if (= username owner) + [:button.form-button.m-l-5 + {:on-click (make-event-handler ::mi/show-delete-confirmation id)} + "delete"])] + (if @(subscribe [::mi/delete-confirmation-shown? id]) + [:div.p-20.flex.justify-cont-end + [:div + [:div.m-b-10 "Are you sure you want to delete this item?"] + [:div.flex + [:button.form-button + {:on-click (make-event-handler ::mi/hide-delete-confirmation id)} + "cancel"] + [:span.link-button + {:on-click (delete-item-handler id)} + "delete"]]]]) + [item-component item] + ]) + ]])) (defn item-list-items [] [:div.item-list