diff --git a/src/cljs/ulysses/db.cljs b/src/cljs/ulysses/db.cljs index 4dbe360..21740d3 100644 --- a/src/cljs/ulysses/db.cljs +++ b/src/cljs/ulysses/db.cljs @@ -59,16 +59,9 @@ ; builder: current faculty pool results given filters (with metrics) :builder-faculties-pool [] - ; builder: current workspace (bare-bones) + ; builder: current workspace, OR nil for default :builder-workspace nil - ; builder: current workspace faculties (list of ids) - :builder-workspace-faculties [] - - ; builder: until back-end persistence, just store faculty-workspace - ; relationships as "join rows", ex. {:faculty-id 3 :workspace-id 4} - :TMP-builder-faculty-workspaces [] - ;; ------------------------------------------------------------------------- ;; user data ;; ------------------------------------------------------------------------- diff --git a/src/cljs/ulysses/handlers.cljs b/src/cljs/ulysses/handlers.cljs index 1a78e5d..ba431d1 100644 --- a/src/cljs/ulysses/handlers.cljs +++ b/src/cljs/ulysses/handlers.cljs @@ -1,5 +1,6 @@ (ns ulysses.handlers (:require [re-frame.core :as re-frame :refer [register-handler dispatch]] + [clojure.set :as set] [ulysses.config :as config] [ulysses.db :as db] [ulysses.utils :refer [document-size @@ -9,8 +10,10 @@ clean-incoming-grant-op clean-incoming-grant-ops boolean? + hyphenate-keys parse-int-id str->int + clj->json to-id-map find-by-id remove-by-id @@ -210,81 +213,107 @@ ;; workspace management ---- +(register-handler + :workspace-load-from-op + (fn [db [_ grant-op-id]] + (request db + [:workspace] + :params {:grant_op_id grant-op-id} + :method :get + :handler + (fn [{:keys [user current]}] ; TODO destructure default from here probably + ;; receive user workspaces + (doseq [w user] + (dispatch [:workspace-receive w :dont-switch])) + ;; switch to the last workspace according to backend + (dispatch [:workspace-switch current]))))) + +; copy a workspace from :workspaces to :builder-workspace +(register-handler + :workspace-switch + (fn [db [_ workspace-id-maybe]] + (if workspace-id-maybe + (if-let [target (find-by-id workspace-id-maybe (:workspaces db))] + (assoc db :builder-workspace target) + ; TODO get default workspace + db) + ; TODO get default workspace + db))) + +(register-handler + :workspace-receive + (fn [db [_ response dont-switch?]] + (let [wbb (-> response + (hyphenate-keys) + (select-keys [:id :name + :grant-op-id :user-id + :updated-at :created-at + :faculties]) + (update :faculties + (fn [faculties] + (map :id faculties))))] + (when-not dont-switch? + (dispatch [:workspace-switch (:id wbb)])) + (update db :workspaces + (fn [ws] + (merge-by-id ws [wbb])))))) + (register-handler :workspace-save (fn [db _] - ; TODO - db)) - -;; NOTE most of the code in this section interfaces with -;; db :workspaces -- eventually, all five handlers -;; should really just be plain http requests so that -;; the back-end handles this stuff + (if-let [current (:builder-workspace db)] + (request db + [:workspace (:id current)] + :method :put + :params {:faculties (clj->json (or (:faculties current) []))} + :handler #(dispatch [:workspace-receive %])) + db))) (register-handler :workspace-new-blank (fn [db _] (if-let [op (:builder-grant-op db)] - (let [id (-> db :workspaces new-id)] - (dispatch [:workspace-switch id]) - (-> db - (update :workspaces - (fn [ws] - (conj ws - {:id id - :name (str id) - :user-id 1 ; temporary, of course - :grant-op-id (:id op)}))))) + (request db + [:workspace] + :params {:grant_op_id (:id op)} + :method :post + :handler #(dispatch [:workspace-receive %])) db))) (register-handler :workspace-new-default (fn [db _] - (dispatch [:workspace-new-blank]) - ; TODO - db)) + (if-let [op (:builder-grant-op db)] + (request db + [:workspace :default] + :params {:grant_op_id (:id op)} + :method :post + :handler #(dispatch [:workspace-receive %])) + db))) (register-handler :workspace-new-duplicate (fn [db _] - (if-let [op (:builder-grant-op db)] - (if-let [current (:builder-workspace db)] - (let [id (-> db :workspaces new-id)] - (dispatch [:workspace-switch id]) - ; NOTE TEMP transfer faculty one by one - (doseq [faculty-id (:builder-workspace-faculties db)] - (dispatch [:add-working-faculty faculty-id])) - (-> db - (update :workspaces - (fn [ws] - (conj ws - ; modify current with new id and name mod - (-> current - (assoc :id id) - (update :name #(str % " (dup)")))))))) - ; on the default ws - (do - (dispatch [:workspace-new-default]) - db)) - ; no grant op - db))) + (let [op (:builder-grant-op db) + current (:builder-workspace db)] + (if (and op current) + (request db + [:workspace :duplicate] + :params {:grant_op_id (:id op) + :from (:id current)} + :method :post + :handler #(dispatch [:workspace-receive %])) + db)))) -; rename current workspace (register-handler :workspace-rename (fn [db [_ name]] (if-let [current (:builder-workspace db)] - (-> db - (assoc-in [:builder-workspace :name] name) - (update :workspaces - (fn [ws] - (if-let [f (find-by-id (:id current) ws)] - (->> name - (assoc f :name) - (vector) - (merge-by-id ws) - (vec)) - ws)))) + (request db + [:workspace :name (:id current)] + :params {:name name} + :method :put + :handler #(dispatch [:workspace-receive %])) db))) ; delete current workspace @@ -294,45 +323,45 @@ (dispatch [:nil-workspace]) (dispatch [:workspace-switch nil]) (if-let [current (:builder-workspace db)] - (update db :workspaces - (fn [ws] - (vec (remove-by-id (:id current) ws)))) - db))) - -(register-handler - :workspace-switch - (fn [db [_ workspace-id-maybe]] - (if workspace-id-maybe - (if-let [target (find-by-id workspace-id-maybe (:workspaces db))] - (-> db - (assoc :builder-workspace target) - (assoc :builder-workspace-faculties - ; TMP workspace persistence - (->> db - :TMP-builder-faculty-workspaces - (filter #(= workspace-id-maybe (:workspace-id %))) - (map :faculty-id)))) - db) + (do + (request db + [:workspace (:id current)] + :method :delete) + (update db :workspaces + (fn [ws] + (vec (remove-by-id (:id current) ws))))) db))) ;; workspace faculties management ---- +; request faculty in the current workspace +; that we don't already have in :faculties +(register-handler + :workspace-faculty-request + (fn [db _] + (when-let [current (:builder-workspace db)] + (let [fc (or (:faculties current) []) + faculties (:faculties db) + need (remove (partial contains? faculties) fc)] + (doseq [id need] + (dispatch [:request-faculty id])))) + db)) + (register-handler :add-working-faculty (fn [db [_ faculty-id]] (dispatch [:request-faculty faculty-id]) + (dispatch [:workspace-save]) (if-let [current (:builder-workspace db)] - (-> db - (update :builder-workspace-faculties - (fn [fids] - (->> faculty-id - (conj fids) - (distinct)))) - (update :TMP-builder-faculty-workspaces - (fn [fws] - (conj fws - {:faculty-id faculty-id - :workspace-id (:id current)})))) + (update db :builder-workspace + (fn [w] + (assoc w :faculties + (-> w + :faculties + (or []) + (set) + (conj faculty-id) + (vec))))) ; TODO create new workspace from default, ; then add this new working faculty db))) @@ -340,25 +369,17 @@ (register-handler :remove-working-faculty (fn [db [_ faculty-id]] - ;; since the remove button is in the hover area... (dispatch [:nil-builder-workspace-faculty-hover]) + (dispatch [:workspace-save]) (if-let [current (:builder-workspace db)] - (-> db - (update :builder-workspace-faculties - (fn [fws] - (vec (remove #{faculty-id} fws)))) - (update :TMP-builder-faculty-workspaces - (let [current-id (:id current)] - (fn [fws] - (vec - (remove - (fn [fw] - (and - (= faculty-id (:faculty-id fw)) - (= current-id (:workspace-id fw)))) - fws)))))) + (update db :builder-workspace + (fn [w] + (assoc w :faculties + (->> (or (:faculties w) []) + (remove #{faculty-id}) + (vec))))) ; TODO create new workspace from default, - ; then remove this new working faculty + ; then add this new working faculty db))) ;; home filters ---- diff --git a/src/cljs/ulysses/pages/builder.cljs b/src/cljs/ulysses/pages/builder.cljs index 8b4ccf4..d87e8c4 100644 --- a/src/cljs/ulysses/pages/builder.cljs +++ b/src/cljs/ulysses/pages/builder.cljs @@ -238,7 +238,7 @@ (defn builder-panel [op faculties faculty-titles faculties-pool - workspaces workspace-current workspace-faculties workspace-faculty-hover + workspaces workspace-current workspace-faculty-hover filters] [:div.builder-panel [sidebar faculty-titles filters] @@ -246,7 +246,7 @@ [:div.builder-panel-half-section [:div.inner [workspace-header workspaces workspace-current] - [workspace (map-lookup faculties workspace-faculties)] + [workspace (map-lookup faculties (or (:faculties workspace-current) []))] [workspace-footer workspace-current]] [:div.inner [:h3 "Word Cloud"] @@ -285,7 +285,6 @@ workspaces (subscribe [:workspaces-current]) workspace (subscribe [:builder-workspace]) - workspace-faculties (subscribe [:builder-workspace-faculties]) workspace-faculty-hover (subscribe [:builder-workspace-faculty-hover]) filters (subscribe [:builder-filters]) @@ -300,6 +299,7 @@ (dispatch [:request-faculty-titles]) ; common with will-update: (dispatch [:request-builder-grant-op id]) + (dispatch [:workspace-load-from-op id]) (dispatch [:request-faculties-pool]))) :component-will-update (fn [] @@ -308,6 +308,7 @@ (when (and (not= id @last-id) (= :builder (first pwa))) (dispatch [:nil-builder]) (dispatch [:request-builder-grant-op id]) + (dispatch [:workspace-load-from-op id]) (dispatch [:request-faculties-pool])) (reset! last-id id))) :component-will-unmount @@ -329,7 +330,6 @@ @workspaces @workspace - @workspace-faculties @workspace-faculty-hover @filters]] diff --git a/src/cljs/ulysses/subs.cljs b/src/cljs/ulysses/subs.cljs index 9b03cf6..9117b93 100644 --- a/src/cljs/ulysses/subs.cljs +++ b/src/cljs/ulysses/subs.cljs @@ -99,11 +99,6 @@ (fn [db _] (reaction (:builder-workspace @db)))) -(register-sub - :builder-workspace-faculties - (fn [db _] - (reaction (:builder-workspace-faculties @db)))) - ;; ---------------------------------------------------------------------------- ;; user data ;; ---------------------------------------------------------------------------- diff --git a/src/cljs/ulysses/utils.cljs b/src/cljs/ulysses/utils.cljs index ce27bd3..3c85ebc 100644 --- a/src/cljs/ulysses/utils.cljs +++ b/src/cljs/ulysses/utils.cljs @@ -41,11 +41,21 @@ [f m] (into {} (for [[k v] m] [k (f v)]))) -(defn remap-kv +(defn remap-k + "map the keys of a hashmap, given key and value" + [f m] + (into {} (for [[k v] m] [(f k v) v]))) + +(defn remap-v "map the values of a hashmap, given key and value" [f m] (into {} (for [[k v] m] [k (f k v)]))) +(defn hyphenate-keys + "given a map, turn all underscores in keys into hyphens" + [m] + (remap-k (fn [k v] (-> k name (string/replace #"_" "-") keyword)) m)) + (defn map-subels "reagent util: apply f to each item in collection, associating :key metadata (first tries :id of each el; @@ -64,6 +74,11 @@ (when (and (string? s) (re-find #"^\d+$" s)) (js/parseInt s)))) +(defn clj->json + "clojure data to json string" + [a] + (.stringify js/JSON (clj->js a))) + (defn url-encode-component "urlencode" [s] @@ -301,7 +316,7 @@ return the maximum values for each metric" [metrics faculties] (let [fm (map :metrics faculties)] - (remap-kv + (remap-v (fn [metric _] (->> fm (map (fn [f] (get f metric))) @@ -356,14 +371,14 @@ (defn request "make an api request, with progress tracking" - [db endpoint & {:keys [verb + [db endpoint & {:keys [method params require-user? progress? handler error-handler finish] - :or {verb :get + :or {method :get params nil require-user? true progress? true @@ -372,24 +387,21 @@ finish noop}}] ; only allow requests if logged in or check is bypassed (when (or (not require-user?) (:user db)) - (let [progress-id (when progress? (:next-progress-id db)) - method (case verb :get ajax.core/GET - :post ajax.core/POST - :put ajax.core/PUT - :delete ajax.core/DELETE - ajax.core/GET)] + (let [progress-id (when progress? (:next-progress-id db))] (when progress? (dispatch [:progress progress-id 1])) - (method + (ajax.core/easy-ajax-request (string/join "/" (cons config/api-endpoint (map #(if (keyword? %) (name %) %) endpoint))) + method {:params (when params (remap url-encode-component params)) :with-credentials true + :format :url :response-format :json :keywords? true :handler handler