mirror of
https://github.com/penpot/penpot.git
synced 2025-03-13 16:21:57 -05:00
Introduce cljs.spec and refactor all forms.
This commit is contained in:
parent
29e6ebdb83
commit
fce36cfdd9
34 changed files with 1187 additions and 1144 deletions
|
@ -6,19 +6,25 @@
|
|||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.data.auth
|
||||
(:require [beicon.core :as rx]
|
||||
(:require [cljs.spec :as s]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.schema :as us]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.projects :as udp]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.data.messages :as udm]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.util.storage :refer (storage)]))
|
||||
|
||||
(s/def ::username string?)
|
||||
(s/def ::password string?)
|
||||
(s/def ::fullname string?)
|
||||
(s/def ::email us/email?)
|
||||
(s/def ::token string?)
|
||||
|
||||
;; --- Logged In
|
||||
|
||||
(defrecord LoggedIn [data]
|
||||
|
@ -58,8 +64,12 @@
|
|||
(rx/map logged-in)
|
||||
(rx/catch rp/client-error? on-error)))))
|
||||
|
||||
(s/def ::login-event
|
||||
(s/keys :req-un [::username ::password]))
|
||||
|
||||
(defn login
|
||||
[params]
|
||||
{:pre [(us/valid? ::login-event params)]}
|
||||
(map->Login params))
|
||||
|
||||
;; --- Logout
|
||||
|
@ -80,40 +90,33 @@
|
|||
|
||||
;; --- Register
|
||||
|
||||
(defrecord Register [data]
|
||||
;; TODO: clean form on success
|
||||
|
||||
(defrecord Register [data on-error]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state stream]
|
||||
(letfn [(on-error [{payload :payload}]
|
||||
(->> (:payload payload)
|
||||
(udf/assign-errors :register)
|
||||
(rx/of)))]
|
||||
(letfn [(handle-error [{payload :payload}]
|
||||
(on-error payload)
|
||||
(rx/empty))]
|
||||
(rx/merge
|
||||
(->> (rp/req :auth/register data)
|
||||
(rx/map :payload)
|
||||
(rx/map (constantly ::registered))
|
||||
(rx/catch rp/client-error? on-error))
|
||||
(rx/catch rp/client-error? handle-error))
|
||||
(->> stream
|
||||
(rx/filter #(= % ::registered))
|
||||
(rx/take 1)
|
||||
(rx/map #(login data)))
|
||||
(->> stream
|
||||
(rx/filter logged-in?)
|
||||
(rx/take 1)
|
||||
(rx/map #(udf/clean :register)))))))
|
||||
(rx/map #(login data)))))))
|
||||
|
||||
(def register-schema
|
||||
{:username [us/required us/string]
|
||||
:fullname [us/required us/string]
|
||||
:email [us/required us/email]
|
||||
:password [us/required us/string]})
|
||||
(s/def ::register-event
|
||||
(s/keys :req-un [::fullname ::username ::email ::password]))
|
||||
|
||||
(defn register
|
||||
"Create a register event instance."
|
||||
[data]
|
||||
(let [[errors data] (us/validate data register-schema)]
|
||||
(if errors
|
||||
(udf/assign-errors :register errors)
|
||||
(Register. data))))
|
||||
[data on-error]
|
||||
{:pre [(us/valid? ::register-event data)
|
||||
(fn? on-error)]}
|
||||
(Register. data on-error))
|
||||
|
||||
;; --- Recovery Request
|
||||
|
||||
|
@ -122,9 +125,7 @@
|
|||
(-apply-watch [_ state stream]
|
||||
(letfn [(on-error [{payload :payload}]
|
||||
(println "on-error" payload)
|
||||
(->> (:payload payload)
|
||||
(udf/assign-errors :recovery-request)
|
||||
(rx/of)))]
|
||||
(rx/empty))]
|
||||
(rx/merge
|
||||
(->> (rp/req :auth/recovery-request data)
|
||||
(rx/map (constantly ::recovery-requested))
|
||||
|
@ -132,11 +133,14 @@
|
|||
(->> stream
|
||||
(rx/filter #(= % ::recovery-requested))
|
||||
(rx/take 1)
|
||||
(rx/do #(udm/info! (tr "auth.message.recovery-token-sent")))
|
||||
(rx/map #(udf/clean :recovery-request)))))))
|
||||
(rx/do #(udm/info! (tr "auth.message.recovery-token-sent"))))))))
|
||||
|
||||
(s/def ::recovery-request-event
|
||||
(s/keys :req-un [::username]))
|
||||
|
||||
(defn recovery-request
|
||||
[data]
|
||||
{:pre [(us/valid? ::recovery-request-event data)]}
|
||||
(RecoveryRequest. data))
|
||||
|
||||
;; --- Check Recovery Token
|
||||
|
@ -153,8 +157,9 @@
|
|||
(rx/catch rp/client-error? on-error)))))
|
||||
|
||||
(defn validate-recovery-token
|
||||
[data]
|
||||
(ValidateRecoveryToken. data))
|
||||
[token]
|
||||
{:pre [(string? token)]}
|
||||
(ValidateRecoveryToken. token))
|
||||
|
||||
;; --- Recovery (Password)
|
||||
|
||||
|
@ -171,8 +176,10 @@
|
|||
(rx/mapcat on-success)
|
||||
(rx/catch rp/client-error? on-error)))))
|
||||
|
||||
(s/def ::recovery-event
|
||||
(s/keys :req-un [::username ::token]))
|
||||
|
||||
(defn recovery
|
||||
[{:keys [token password]}]
|
||||
[{:keys [token password] :as data}]
|
||||
{:pre [(us/valid? ::recovery-event data)]}
|
||||
(Recovery. token password))
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.data.projects :as dp]
|
||||
[uxbox.main.data.colors :as dc]
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.data.forms
|
||||
(:require [beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.i18n :refer (tr)]))
|
||||
|
||||
;; --- Assign Errors
|
||||
|
||||
(defrecord AssignErrors [type errors]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:errors type] errors)))
|
||||
|
||||
(defn assign-errors
|
||||
([type] (assign-errors type nil))
|
||||
([type errors]
|
||||
(AssignErrors. type errors)))
|
||||
|
||||
;; --- Assign Field Value
|
||||
|
||||
(defrecord AssignFieldValue [type field value]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [form-path (into [:forms type] (if (coll? field) field [field]))
|
||||
errors-path (into [:errors type] (if (coll? field) field [field]))]
|
||||
(-> state
|
||||
(assoc-in form-path value)
|
||||
(update-in (butlast errors-path) dissoc (last errors-path))))))
|
||||
|
||||
(defn assign-field-value
|
||||
[type field value]
|
||||
(AssignFieldValue. type field value))
|
||||
|
||||
;; --- Clean Errors
|
||||
|
||||
(defrecord CleanErrors [type]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:errors type] nil)))
|
||||
|
||||
(defn clean-errors
|
||||
[type]
|
||||
(CleanErrors. type))
|
||||
|
||||
;; --- Clean Form
|
||||
|
||||
(defrecord CleanForm [type]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:forms type] nil)))
|
||||
|
||||
(defn clean-form
|
||||
[type]
|
||||
(CleanForm. type))
|
||||
|
||||
;; --- Clean
|
||||
|
||||
(defrecord Clean [type]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/of (clean-form type)
|
||||
(clean-errors type))))
|
||||
|
||||
(defn clean
|
||||
[type]
|
||||
(Clean. type))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn focus-form-data
|
||||
[type]
|
||||
(-> (l/in [:forms type])
|
||||
(l/derive st/state)))
|
||||
|
||||
(defn focus-form-errors
|
||||
[type]
|
||||
(-> (l/in [:errors type])
|
||||
(l/derive st/state)))
|
||||
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
[uxbox.util.router :as r]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.datetime :as dt]
|
||||
|
@ -35,7 +35,7 @@
|
|||
(rs/emit! (fetch-page-history id)
|
||||
(fetch-pinned-page-history id)))]
|
||||
(as-> rs/stream $
|
||||
(rx/filter udp/page-synced? $)
|
||||
(rx/filter udp/page-persisted? $)
|
||||
(rx/delay 500 $)
|
||||
(rx/map (comp :id :page) $)
|
||||
(rx/on-value $ on-value))))
|
||||
|
@ -137,7 +137,7 @@
|
|||
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/of (udp/update-page id))))
|
||||
(rx/of (udp/persist-page id))))
|
||||
|
||||
(defn apply-selected-history
|
||||
[id]
|
||||
|
|
|
@ -3,21 +3,43 @@
|
|||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.data.pages
|
||||
(:require [cuerdas.core :as str]
|
||||
(:require [cljs.spec :as s]
|
||||
[cuerdas.core :as str]
|
||||
[beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.datetime :as dt]
|
||||
[uxbox.util.data :refer (without-keys replace-by-id)]))
|
||||
|
||||
;; --- Specs
|
||||
|
||||
(s/def ::id uuid?)
|
||||
(s/def ::name string?)
|
||||
(s/def ::project uuid?)
|
||||
(s/def ::grid-x-axis number?)
|
||||
(s/def ::grid-y-axis number?)
|
||||
(s/def ::grid-color us/color?)
|
||||
(s/def ::grid-alignment boolean?)
|
||||
(s/def ::width number?)
|
||||
(s/def ::height number?)
|
||||
(s/def ::layout string?)
|
||||
|
||||
(s/def ::metadata
|
||||
(s/keys :req-un [::width ::height]
|
||||
:opt-un [::grid-y-axis
|
||||
::grid-x-axis
|
||||
::grid-color
|
||||
::grid-alignment
|
||||
::layout]))
|
||||
|
||||
|
||||
;; --- Protocols
|
||||
|
||||
|
@ -99,75 +121,84 @@
|
|||
[projectid]
|
||||
(FetchPages. projectid))
|
||||
|
||||
;; --- Page Created
|
||||
|
||||
(defrecord PageCreated [data]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(-> state
|
||||
(assoc-page data)
|
||||
(assoc-packed-page data))))
|
||||
|
||||
(s/def ::page-created-event
|
||||
(s/keys :req-un [::id ::name ::project ::metadata]))
|
||||
|
||||
(defn page-created
|
||||
[data]
|
||||
{:pre [(us/valid? ::page-created-event data)]}
|
||||
(PageCreated. data))
|
||||
|
||||
;; --- Create Page
|
||||
|
||||
(defrecord CreatePage [name project metadata]
|
||||
(defrecord CreatePage [name project width height layout]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [this state s]
|
||||
(letfn [(on-created [{page :payload}]
|
||||
(rx/of
|
||||
#(assoc-page % page)
|
||||
#(assoc-packed-page % page)))]
|
||||
(let [params {:name name
|
||||
:project project
|
||||
:data {}
|
||||
:metadata metadata}]
|
||||
(->> (rp/req :create/page params)
|
||||
(rx/mapcat on-created))))))
|
||||
(let [params {:name name
|
||||
:project project
|
||||
:data {}
|
||||
:metadata {:width width
|
||||
:height height
|
||||
:layout layout}}]
|
||||
(->> (rp/req :create/page params)
|
||||
(rx/map :payload)
|
||||
(rx/map page-created)))))
|
||||
|
||||
(def ^:private create-page-schema
|
||||
{:name [sc/required sc/string]
|
||||
:metadata [sc/required]
|
||||
:project [sc/required sc/uuid]})
|
||||
(s/def ::create-page-event
|
||||
(s/keys :req-un [::name ::project ::width ::height ::layout]))
|
||||
|
||||
(defn create-page
|
||||
[data]
|
||||
(-> (sc/validate! data create-page-schema)
|
||||
(map->CreatePage)))
|
||||
{:pre [(us/valid? ::create-page-event data)]}
|
||||
(map->CreatePage data))
|
||||
|
||||
;; --- Page Synced
|
||||
;; --- Page Persisted
|
||||
|
||||
(defrecord PageSynced [page]
|
||||
(defrecord PagePersisted [data]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [this state]
|
||||
(-> state
|
||||
(assoc-in [:pages (:id page) :version] (:version page))
|
||||
(assoc-page page))))
|
||||
(-apply-update [_ state]
|
||||
(assoc-page state data)))
|
||||
|
||||
(defn- page-synced?
|
||||
(defn- page-persisted?
|
||||
[event]
|
||||
(instance? PageSynced event))
|
||||
(instance? PagePersisted event))
|
||||
|
||||
;; --- Sync Page
|
||||
;; TODO: add specs
|
||||
|
||||
(defrecord SyncPage [id]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [this state s]
|
||||
(let [page (pack-page state id)]
|
||||
(->> (rp/req :update/page page)
|
||||
(rx/map (comp ->PageSynced :payload))))))
|
||||
(defn page-persisted
|
||||
[data]
|
||||
{:pre [(map? data)]}
|
||||
(PagePersisted. data))
|
||||
|
||||
(defn sync-page
|
||||
[id]
|
||||
(SyncPage. id))
|
||||
;; --- Persist Page
|
||||
|
||||
;; --- Update Page
|
||||
|
||||
(defrecord UpdatePage [id]
|
||||
(defrecord PersistPage [id]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [this state s]
|
||||
(let [page (get-in state [:pages id])]
|
||||
(if (:history page)
|
||||
(rx/empty)
|
||||
(rx/of (sync-page id))))))
|
||||
(let [page (pack-page state id)]
|
||||
(->> (rp/req :update/page page)
|
||||
(rx/map :payload)
|
||||
(rx/map page-persisted)))))))
|
||||
|
||||
(defn update-page?
|
||||
(defn persist-page?
|
||||
[v]
|
||||
(instance? UpdatePage v))
|
||||
(instance? PersistPage v))
|
||||
|
||||
(defn update-page
|
||||
(defn persist-page
|
||||
[id]
|
||||
(UpdatePage. id))
|
||||
(PersistPage. id))
|
||||
|
||||
(defn watch-page-changes
|
||||
"A function that starts watching for `IPageUpdate`
|
||||
|
@ -182,59 +213,82 @@
|
|||
(as-> rs/stream $
|
||||
(rx/filter #(satisfies? IPageUpdate %) $)
|
||||
(rx/debounce 1000 $)
|
||||
(rx/on-next $ #(rs/emit! (update-page id)))))
|
||||
(rx/on-next $ #(rs/emit! (persist-page id)))))
|
||||
|
||||
;; --- Update Page Metadata
|
||||
;; --- Page Metadata Persisted
|
||||
|
||||
;; This is a simplified version of `UpdatePage` event
|
||||
(defrecord MetadataPersisted [id data]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
;; TODO: page-data update
|
||||
(assoc-in state [:pages id :version] (:version data))))
|
||||
|
||||
(s/def ::metadata-persisted-event
|
||||
(s/keys :req-un [::id ::version]))
|
||||
|
||||
(defn metadata-persisted
|
||||
[{:keys [id] :as data}]
|
||||
{:pre [(us/valid? ::metadata-persisted-event data)]}
|
||||
(MetadataPersisted. id data))
|
||||
|
||||
;; --- Persist Page Metadata
|
||||
|
||||
;; This is a simplified version of `PersistPage` event
|
||||
;; that does not sends the heavyweiht `:data` attribute
|
||||
;; and only serves for update other page data.
|
||||
|
||||
;; TODO: sync also with the pagedata-by-id index.
|
||||
|
||||
(defrecord UpdatePageMetadata [id name width height layout options]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(letfn [(updater [page]
|
||||
(merge page
|
||||
(when options {:options options})
|
||||
(when width {:width width})
|
||||
(when height {:height height})
|
||||
(when name {:name name})))]
|
||||
(update-in state [:pages id] updater)))
|
||||
|
||||
(defrecord PersistMetadata [id]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [this state s]
|
||||
(letfn [(on-success [{page :payload}]
|
||||
#(assoc-in % [:pages id :version] (:version page)))]
|
||||
(->> (rp/req :update/page-metadata (into {} this))
|
||||
(rx/map on-success)))))
|
||||
(-apply-watch [_ state stream]
|
||||
(let [page (get-in state [:pages id])]
|
||||
(->> (rp/req :update/page-metadata page)
|
||||
(rx/map :payload)
|
||||
(rx/map metadata-persisted)))))
|
||||
|
||||
(def ^:private update-page-schema
|
||||
{:id [sc/required]
|
||||
:project [sc/required]
|
||||
:version [sc/required]
|
||||
:name [sc/required sc/string]
|
||||
:metadata [sc/required]})
|
||||
|
||||
(defn update-page-metadata
|
||||
[data]
|
||||
(-> (sc/validate! data update-page-schema {:strip false})
|
||||
(dissoc data :data)
|
||||
(map->UpdatePageMetadata)))
|
||||
(defn persist-metadata
|
||||
[id]
|
||||
{:pre [(uuid? id)]}
|
||||
(PersistMetadata. id))
|
||||
|
||||
;; --- Update Page Options
|
||||
|
||||
(defrecord UpdatePageOptions [id options]
|
||||
(defrecord UpdateMetadata [id metadata]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:pages id :metadata] metadata))
|
||||
|
||||
rs/WatchEvent
|
||||
(-apply-watch [this state s]
|
||||
(let [page (get-in state [:pages id])
|
||||
page (assoc page :options options)]
|
||||
(rx/of (map->UpdatePageMetadata page)))))
|
||||
(rx/of (persist-metadata id))))
|
||||
|
||||
(defn update-page-options
|
||||
[id options]
|
||||
(UpdatePageOptions. id options))
|
||||
(defn update-metadata
|
||||
[id metadata]
|
||||
{:pre [(uuid? id) (us/valid? ::metadata metadata)]}
|
||||
(UpdateMetadata. id metadata))
|
||||
|
||||
;; --- Update Page
|
||||
|
||||
(defrecord UpdatePage [id name width height layout]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [this state]
|
||||
(println "update-page" this)
|
||||
(-> state
|
||||
(assoc-in [:pages id :name] name)
|
||||
(assoc-in [:pages id :metadata :width] width)
|
||||
(assoc-in [:pages id :metadata :height] height)
|
||||
(assoc-in [:pages id :metadata :layout] layout)))
|
||||
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state stream]
|
||||
(rx/of (persist-metadata id))))
|
||||
|
||||
(s/def ::update-page-event
|
||||
(s/keys :req-un [::name ::width ::height ::layout]))
|
||||
|
||||
(defn update-page
|
||||
[id {:keys [name width height layout] :as data}]
|
||||
{:pre [(uuid? id) (us/valid? ::update-page-event data)]}
|
||||
(UpdatePage. id name width height layout))
|
||||
|
||||
;; --- Delete Page (by id)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.main.data.pages :as udp]))
|
||||
|
||||
;; --- Helpers
|
||||
|
@ -25,6 +25,7 @@
|
|||
(let [page {:id (:page-id project)
|
||||
:name (:page-name project)
|
||||
:version (:page-version project)
|
||||
:project (:id project)
|
||||
:data (:page-data project)
|
||||
:created-at (:page-created-at project)
|
||||
:modified-at (:page-modified-at project)
|
||||
|
@ -176,7 +177,6 @@
|
|||
:width [sc/required sc/integer]
|
||||
:height [sc/required sc/integer]
|
||||
:layout [sc/required sc/string]})
|
||||
|
||||
(defn create-project
|
||||
[params]
|
||||
(-> (sc/validate! params create-project-schema)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.workers :as uw]
|
||||
[uxbox.main.constants :as c]
|
||||
[uxbox.main.geom :as geom]
|
||||
|
@ -594,6 +594,6 @@
|
|||
|
||||
(defn align-point
|
||||
[point]
|
||||
(let [message {:cmd :grid/align :point point}]
|
||||
(let [message {:cmd :grid-align :point point}]
|
||||
(->> (uw/ask! worker message)
|
||||
(rx/map :point))))
|
||||
|
|
|
@ -5,15 +5,20 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.data.users
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.main.repo :as rp]
|
||||
(:require [cljs.spec :as s]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.data.messages :as udm]))
|
||||
|
||||
(s/def ::fullname string?)
|
||||
(s/def ::email us/email?)
|
||||
(s/def ::username string?)
|
||||
(s/def ::theme string?)
|
||||
|
||||
;; --- Profile Fetched
|
||||
|
||||
(defrecord ProfileFetched [data]
|
||||
|
@ -55,37 +60,31 @@
|
|||
|
||||
;; --- Update Profile
|
||||
|
||||
(defrecord UpdateProfile [data]
|
||||
(defrecord UpdateProfile [data on-success on-error]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(letfn [(on-error [{payload :payload}]
|
||||
(->> (:payload payload)
|
||||
(udf/assign-errors :profile/main)
|
||||
(rx/of)))]
|
||||
(letfn [(handle-error [{payload :payload}]
|
||||
(on-error payload)
|
||||
(rx/empty))]
|
||||
(->> (rp/req :update/profile data)
|
||||
(rx/map :payload)
|
||||
(rx/do on-success)
|
||||
(rx/map profile-updated)
|
||||
(rx/catch rp/client-error? on-error)))))
|
||||
(rx/catch rp/client-error? handle-error)))))
|
||||
|
||||
(def update-profile-schema
|
||||
{:fullname [sc/required sc/string]
|
||||
:email [sc/required sc/email]
|
||||
:username [sc/required sc/string]})
|
||||
(s/def ::update-profile-event
|
||||
(s/keys :req-un [::fullname ::email ::username ::theme]))
|
||||
|
||||
(defn update-profile
|
||||
[data]
|
||||
(let [[errors data] (sc/validate data update-profile-schema)]
|
||||
(if errors
|
||||
(udf/assign-errors :profile/main errors)
|
||||
(UpdateProfile. data))))
|
||||
[data on-success on-error]
|
||||
{:pre [(us/valid? ::update-profile-event data)
|
||||
(fn? on-error)
|
||||
(fn? on-success)]}
|
||||
(UpdateProfile. data on-success on-error))
|
||||
|
||||
;; --- Password Updated
|
||||
|
||||
(defrecord PasswordUpdated []
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:forms :profile/password] {}))
|
||||
|
||||
rs/EffectEvent
|
||||
(-apply-effect [_ state]
|
||||
(udm/info! (tr "settings.password-saved"))))
|
||||
|
@ -99,28 +98,22 @@
|
|||
(defrecord UpdatePassword [data]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(letfn [(on-error [{payload :payload}]
|
||||
(->> (:payload payload)
|
||||
(udf/assign-errors :profile/password)
|
||||
(rx/of)))]
|
||||
(let [params {:old-password (:old-password data)
|
||||
:password (:password-1 data)}]
|
||||
(let [params {:old-password (:old-password data)
|
||||
:password (:password-1 data)}]
|
||||
(->> (rp/req :update/profile-password params)
|
||||
(rx/map password-updated)
|
||||
(rx/catch rp/client-error? on-error))))))
|
||||
(rx/map password-updated)))))
|
||||
|
||||
(def update-password-schema
|
||||
[[:password-1 sc/required sc/string [sc/min-len 6]]
|
||||
[:password-2 sc/required sc/string
|
||||
[sc/identical-to :password-1 :message "errors.form.password-not-match"]]
|
||||
[:old-password sc/required sc/string]])
|
||||
(s/def ::password-1 string?)
|
||||
(s/def ::password-2 string?)
|
||||
(s/def ::old-password string?)
|
||||
|
||||
(s/def ::update-password-event
|
||||
(s/keys :req-un [::password-1 ::password-2 ::old-password]))
|
||||
|
||||
(defn update-password
|
||||
[data]
|
||||
(let [[errors data] (sc/validate data update-password-schema)]
|
||||
(if errors
|
||||
(udf/assign-errors :profile/password errors)
|
||||
(UpdatePassword. data))))
|
||||
{:pre [(us/valid? ::update-password-event data)]}
|
||||
(UpdatePassword. data))
|
||||
|
||||
;; --- Update Photo
|
||||
|
||||
|
@ -132,7 +125,7 @@
|
|||
(rx/map fetch-profile))))
|
||||
|
||||
(defn update-photo
|
||||
([file]
|
||||
(UpdatePhoto. file (constantly nil)))
|
||||
([file] (update-photo file (constantly nil)))
|
||||
([file done]
|
||||
{:pre [(us/file? file) (fn? done)]}
|
||||
(UpdatePhoto. file done)))
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.data.workspace
|
||||
(:require [beicon.core :as rx]
|
||||
(:require [cljs.spec :as s]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.main.constants :as c]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.workers :as uw]
|
||||
[uxbox.main.state :as st]
|
||||
|
@ -18,7 +20,6 @@
|
|||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.main.data.shapes :as uds]
|
||||
[uxbox.main.data.shapes-impl :as shimpl]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.data.lightbox :as udl]
|
||||
[uxbox.main.data.history :as udh]
|
||||
[uxbox.util.datetime :as dt]
|
||||
|
@ -210,44 +211,32 @@
|
|||
(-apply-watch [_ state s]
|
||||
(let [page (get-in state [:pages id])
|
||||
opts (:options page)
|
||||
message {:cmd :grid/init
|
||||
message {:cmd :grid-init
|
||||
:width c/viewport-width
|
||||
:height c/viewport-height
|
||||
:x-axis (:grid/x-axis opts c/grid-x-axis)
|
||||
:y-axis (:grid/y-axis opts c/grid-y-axis)}]
|
||||
:x-axis (:grid-x-axis opts c/grid-x-axis)
|
||||
:y-axis (:grid-y-axis opts c/grid-y-axis)}]
|
||||
(rx/merge
|
||||
(->> (uw/send! worker message)
|
||||
(rx/map #(activate-flag :grid/indexed)))
|
||||
(when (:grid/alignment opts)
|
||||
(rx/of (activate-flag :grid/alignment)))))))
|
||||
(rx/map #(activate-flag :grid-indexed)))
|
||||
(when (:grid-alignment opts)
|
||||
(rx/of (activate-flag :grid-alignment)))))))
|
||||
|
||||
(defn initialize-alignment-index
|
||||
[id]
|
||||
(InitializeAlignmentIndex. id))
|
||||
|
||||
;; --- Update Workspace Settings (Form)
|
||||
;; --- Update Metadata
|
||||
|
||||
(defrecord SubmitWorkspaceSettings [id options]
|
||||
;; Is a workspace aware wrapper over uxbox.data.pages/UpdateMetadata event.
|
||||
|
||||
(defrecord UpdateMetadata [id metadata]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/of (udp/update-page-options id options)
|
||||
(initialize-alignment-index id)
|
||||
(udf/clean :workspace/settings)))
|
||||
(rx/of (udp/update-metadata id metadata)
|
||||
(initialize-alignment-index id))))
|
||||
|
||||
rs/EffectEvent
|
||||
(-apply-effect [_ state]
|
||||
(udl/close!)))
|
||||
|
||||
(def submit-workspace-settings-schema
|
||||
{:grid/y-axis [sc/required sc/integer [sc/in-range 2 100]]
|
||||
:grid/x-axis [sc/required sc/integer [sc/in-range 2 100]]
|
||||
:grid/alignment [sc/boolean]
|
||||
:grid/color [sc/required sc/color]})
|
||||
|
||||
(defn submit-workspace-settings
|
||||
[id data]
|
||||
(let [schema submit-workspace-settings-schema
|
||||
[errors data] (sc/validate data schema)]
|
||||
(if errors
|
||||
(udf/assign-errors :workspace/settings errors)
|
||||
(SubmitWorkspaceSettings. id data))))
|
||||
(defn update-metadata
|
||||
[id metadata]
|
||||
{:pre [(uuid? id) (us/valid? ::udp/metadata metadata)]}
|
||||
(UpdateMetadata. id metadata))
|
||||
|
|
|
@ -11,38 +11,23 @@
|
|||
[uxbox.main.repo.impl :refer (request send!)]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
(defn decode-page
|
||||
[{:keys [data metadata] :as page}]
|
||||
(merge page
|
||||
(when data {:data (t/decode data)})
|
||||
(when metadata {:metadata (t/decode metadata)})))
|
||||
|
||||
(defn decode-payload
|
||||
[{:keys [payload] :as rsp}]
|
||||
(if (sequential? payload)
|
||||
(assoc rsp :payload (mapv decode-page payload))
|
||||
(assoc rsp :payload (decode-page payload))))
|
||||
|
||||
(defmethod request :fetch/pages
|
||||
[type data]
|
||||
(let [params {:url (str url "/pages")
|
||||
:method :get}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :fetch/pages-by-project
|
||||
[type {:keys [project] :as params}]
|
||||
(let [url (str url "/projects/" project "/pages")]
|
||||
(->> (send! {:method :get :url url})
|
||||
(rx/map decode-payload))))
|
||||
(send! {:method :get :url url})))
|
||||
|
||||
(defmethod request :fetch/page-history
|
||||
[type {:keys [page] :as params}]
|
||||
(let [url (str url "/pages/" page "/history")
|
||||
query (select-keys params [:max :since :pinned])
|
||||
params {:method :get :url url :query query}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :delete/page
|
||||
[_ id]
|
||||
|
@ -51,41 +36,30 @@
|
|||
:method :delete})))
|
||||
|
||||
(defmethod request :create/page
|
||||
[type {:keys [data metadata] :as body}]
|
||||
(let [body (assoc body
|
||||
:data (t/encode data)
|
||||
:metadata (t/encode metadata))
|
||||
params {:url (str url "/pages")
|
||||
[type body]
|
||||
(let [params {:url (str url "/pages")
|
||||
:method :post
|
||||
:body body}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :update/page
|
||||
[type {:keys [id data metadata] :as body}]
|
||||
(let [body (assoc body
|
||||
:data (t/encode data)
|
||||
:metadata (t/encode metadata))
|
||||
params {:url (str url "/pages/" id)
|
||||
[type {:keys [id] :as body}]
|
||||
(let [params {:url (str url "/pages/" id)
|
||||
:method :put
|
||||
:body body}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :update/page-history
|
||||
[type {:keys [id page] :as data}]
|
||||
(let [params {:url (str url "/pages/" page "/history/" id)
|
||||
:method :put
|
||||
:body data}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :update/page-metadata
|
||||
[type {:keys [id metadata] :as body}]
|
||||
(let [body (dissoc body :data)
|
||||
body (assoc body :metadata (t/encode metadata))
|
||||
params {:url (str url "/pages/" id "/metadata")
|
||||
:method :put
|
||||
:body body}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
|
|
@ -14,27 +14,15 @@
|
|||
|
||||
(defmethod request :fetch/projects
|
||||
[type data]
|
||||
(letfn [(decode-payload [{:keys [payload] :as response}]
|
||||
(assoc response :payload (mapv decode-page payload)))
|
||||
(decode-page [{:keys [page-metadata page-data] :as project}]
|
||||
(assoc project
|
||||
:page-metadata (t/decode page-metadata)
|
||||
:page-data (t/decode page-data)))]
|
||||
;; Obtain the list of projects and decode the embedded
|
||||
;; page data in order to have it usable.
|
||||
(->> (send! {:url (str url "/projects")
|
||||
:method :get})
|
||||
(rx/map decode-payload))))
|
||||
;; Obtain the list of projects and decode the embedded
|
||||
;; page data in order to have it usable.
|
||||
(send! {:url (str url "/projects")
|
||||
:method :get}))
|
||||
|
||||
(defmethod request :fetch/project-by-token
|
||||
[_ token]
|
||||
(letfn [(decode-pages [response]
|
||||
(let [pages (->> (get-in response [:payload :pages])
|
||||
(mapv pages/decode-page))]
|
||||
(assoc-in response [:payload :pages] pages)))]
|
||||
(->> (send! {:url (str url "/projects-by-token/" token)
|
||||
:method :get})
|
||||
(rx/map decode-pages))))
|
||||
(send! {:url (str url "/projects-by-token/" token)
|
||||
:method :get}))
|
||||
|
||||
(defmethod request :create/project
|
||||
[_ data]
|
||||
|
|
|
@ -11,27 +11,18 @@
|
|||
[uxbox.main.repo.impl :refer (request send!)]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
(defn- decode-payload
|
||||
[{:keys [payload] :as rsp}]
|
||||
(let [metadata (:metadata payload)]
|
||||
(assoc rsp :payload
|
||||
(assoc payload :metadata (t/decode metadata)))))
|
||||
|
||||
(defmethod request :fetch/profile
|
||||
[type _]
|
||||
(let [url (str url "/profile/me")
|
||||
params {:method :get :url url}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :update/profile
|
||||
[type {:keys [metadata] :as body}]
|
||||
(let [body (assoc body :metadata (t/encode metadata))
|
||||
params {:url (str url "/profile/me")
|
||||
[type body]
|
||||
(let [params {:url (str url "/profile/me")
|
||||
:method :put
|
||||
:body body}]
|
||||
(->> (send! params)
|
||||
(rx/map decode-payload))))
|
||||
(send! params)))
|
||||
|
||||
(defmethod request :update/profile-password
|
||||
[type data]
|
||||
|
|
|
@ -138,6 +138,8 @@
|
|||
|
||||
(def routes
|
||||
[["/auth/login" :auth/login]
|
||||
["/auth/register" :auth/register]
|
||||
["/auth/recovery/request" :auth/recovery-request]
|
||||
["/auth/recovery/token/:token" :auth/recovery]
|
||||
["/settings/profile" :settings/profile]
|
||||
["/settings/password" :settings/password]
|
||||
|
@ -145,7 +147,6 @@
|
|||
["/dashboard/projects" :dashboard/projects]
|
||||
["/dashboard/elements" :dashboard/elements]
|
||||
|
||||
|
||||
["/dashboard/icons" :dashboard/icons]
|
||||
["/dashboard/icons/:type/:id" :dashboard/icons]
|
||||
["/dashboard/icons/:type" :dashboard/icons]
|
||||
|
|
|
@ -5,74 +5,22 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.auth.login
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[rum.core :as rum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.auth :as da]
|
||||
[uxbox.main.data.messages :as udm]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.navigation :as nav]
|
||||
[uxbox.util.mixins :as mx :include-macros true]))
|
||||
[uxbox.main.ui.navigation :as nav]))
|
||||
|
||||
(defn- login-submit
|
||||
[event local]
|
||||
(dom/prevent-default event)
|
||||
(let [form (:form @local)]
|
||||
(rs/emit! (da/login {:username (:email form)
|
||||
:password (:password form)}))))
|
||||
|
||||
(defn- login-submit-enabled?
|
||||
[local]
|
||||
(let [form (:form @local)]
|
||||
(and (not (str/empty? (:email form "")))
|
||||
(not (str/empty? (:password form ""))))))
|
||||
|
||||
(defn- login-field-change
|
||||
[local field event]
|
||||
(let [value (str/trim (dom/event->value event))]
|
||||
(swap! local assoc-in [:form field] value)))
|
||||
|
||||
(defn- login-page-render
|
||||
[own local]
|
||||
(let [on-submit #(login-submit % local)
|
||||
submit-enabled? (login-submit-enabled? local)
|
||||
form (:form @local)]
|
||||
(html
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "email"
|
||||
:ref "email"
|
||||
:value (:email form "")
|
||||
:on-change #(login-field-change local :email %)
|
||||
:placeholder "Email or Username"
|
||||
:type "text"}]
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:ref "password"
|
||||
:value (:password form "")
|
||||
:on-change #(login-field-change local :password %)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not submit-enabled? "btn-disabled")
|
||||
:disabled (not submit-enabled?)
|
||||
:value "Continue"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/recovery-request)} "Forgot your password?"]
|
||||
[:a {:on-click #(rt/go :auth/register)} "Don't have an account?"]]]]]])))
|
||||
(def form-data (forms/focus-data :login st/state))
|
||||
(def set-value! (partial forms/set-value! :login))
|
||||
|
||||
(defn- login-page-will-mount
|
||||
[own]
|
||||
|
@ -80,9 +28,54 @@
|
|||
(rt/go :dashboard/projects))
|
||||
own)
|
||||
|
||||
(def login-page
|
||||
(mx/component
|
||||
{:render #(login-page-render % (:rum/local %))
|
||||
:will-mount login-page-will-mount
|
||||
:name "login-page"
|
||||
:mixins [(mx/local)]}))
|
||||
(def +login-form+
|
||||
{:email [forms/required forms/string]
|
||||
:password [forms/required forms/string]})
|
||||
|
||||
(mx/defc login-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[]
|
||||
(let [data (mx/react form-data)
|
||||
valid? (forms/valid? data +login-form+)]
|
||||
(letfn [(on-change [event field]
|
||||
(let [value (dom/event->value event)]
|
||||
(set-value! field value)))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(rs/emit! (da/login {:username (:email data)
|
||||
:password (:password data)})))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "email"
|
||||
:ref "email"
|
||||
:value (:email data "")
|
||||
:on-change #(on-change % :email)
|
||||
:placeholder "Email or Username"
|
||||
:type "text"}]
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:ref "password"
|
||||
:value (:password data "")
|
||||
:on-change #(on-change % :password)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Continue"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/recovery-request)} "Forgot your password?"]
|
||||
[:a {:on-click #(rt/go :auth/register)} "Don't have an account?"]]]])))
|
||||
|
||||
(mx/defc login-page
|
||||
{:mixins [mx/static]
|
||||
:will-mount login-page-will-mount}
|
||||
[]
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(login-form)]])
|
||||
|
|
|
@ -5,81 +5,59 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.auth.recovery
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[rum.core :as rum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as us]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.data.auth :as uda]
|
||||
[uxbox.main.data.messages :as udm]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.navigation :as nav]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]))
|
||||
[uxbox.main.ui.navigation :as nav]))
|
||||
|
||||
;; --- Constants
|
||||
;; --- Recovery Form
|
||||
|
||||
(def form-data
|
||||
(-> (l/in [:forms :recovery])
|
||||
(l/derive st/state)))
|
||||
(def form-data (forms/focus-data :recovery st/state))
|
||||
(def set-value! (partial forms/set-value! :recovery))
|
||||
|
||||
(def form-errors
|
||||
(-> (l/in [:errors :recovery])
|
||||
(l/derive st/state)))
|
||||
(def +recovery-form+
|
||||
{:password [forms/required forms/string]})
|
||||
|
||||
(def set-value!
|
||||
(partial udf/assign-field-value :recovery))
|
||||
|
||||
;; --- Recovery Request Form
|
||||
|
||||
(def schema
|
||||
{:password [us/required us/string]})
|
||||
|
||||
(defn- form-render
|
||||
[own token]
|
||||
(let [form (mx/react form-data)
|
||||
errors (mx/react form-errors)
|
||||
valid? (us/valid? form schema)]
|
||||
(mx/defc recovery-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[token]
|
||||
(let [data (merge (mx/react form-data)
|
||||
{:token token})
|
||||
valid? (forms/valid? data +recovery-form+)]
|
||||
(letfn [(on-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(rs/emit! (set-value! field value))))
|
||||
(set-value! field value)))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(rs/emit! (uda/recovery (assoc form :token token))))]
|
||||
(html
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
(rs/emit! (uda/recovery data)
|
||||
(forms/clear :recovery)))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:value (:password data "")
|
||||
:on-change (partial on-change :password)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Recover password"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]])))
|
||||
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:value (:password form "")
|
||||
:on-change (partial on-change :password)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
(forms/input-error errors :password)
|
||||
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Recover password"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]]))))
|
||||
|
||||
(def form
|
||||
(mx/component
|
||||
{:render form-render
|
||||
:name "form"
|
||||
:mixins [mx/static mx/reactive]}))
|
||||
|
||||
;; --- Recovery Request Page
|
||||
;; --- Recovery Page
|
||||
|
||||
(defn- recovery-page-will-mount
|
||||
[own]
|
||||
|
@ -87,18 +65,13 @@
|
|||
(rs/emit! (uda/validate-recovery-token token))
|
||||
own))
|
||||
|
||||
(defn- recovery-page-render
|
||||
[own token]
|
||||
(html
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(form token)]]))
|
||||
|
||||
(def recovery-page
|
||||
(mx/component
|
||||
{:render recovery-page-render
|
||||
:will-mount recovery-page-will-mount
|
||||
:name "recovery-page"
|
||||
:mixins [mx/static]}))
|
||||
(mx/defc recovery-page
|
||||
{:mixins [mx/static]
|
||||
:will-mount recovery-page-will-mount
|
||||
:will-unmount (forms/cleaner-fn :recovery)}
|
||||
[token]
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(recovery-form token)]])
|
||||
|
|
|
@ -5,92 +5,63 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.auth.recovery-request
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[rum.core :as rum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as us]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.auth :as uda]
|
||||
[uxbox.main.data.messages :as udm]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.navigation :as nav]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]))
|
||||
[uxbox.main.ui.navigation :as nav]))
|
||||
|
||||
;; --- Recovery Request Constants
|
||||
(def form-data (forms/focus-data :recovery-request st/state))
|
||||
(def set-value! (partial forms/set-value! :recovery-request))
|
||||
|
||||
(def form-data
|
||||
(-> (l/in [:forms :recovery-request])
|
||||
(l/derive st/state)))
|
||||
(def +recovery-request-form+
|
||||
{:username [forms/required forms/string]})
|
||||
|
||||
(def form-errors
|
||||
(-> (l/in [:errors :recovery-request])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def set-value!
|
||||
(partial udf/assign-field-value :recovery-request))
|
||||
|
||||
;; --- Recovery Request Form
|
||||
|
||||
(def schema
|
||||
{:username [us/required us/string]})
|
||||
|
||||
(defn- form-render
|
||||
[own]
|
||||
(let [form (mx/react form-data)
|
||||
errors (mx/react form-errors)
|
||||
valid? (us/valid? form schema)]
|
||||
(mx/defc recovery-request-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[]
|
||||
(let [data (mx/react form-data)
|
||||
valid? (forms/valid? data +recovery-request-form+)]
|
||||
(letfn [(on-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(rs/emit! (set-value! field value))))
|
||||
(set-value! field value)))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(rs/emit! (uda/recovery-request form)))]
|
||||
(html
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "username"
|
||||
:value (:username form "")
|
||||
:on-change (partial on-change :username)
|
||||
:placeholder "username or email address"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :username)
|
||||
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Recover password"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]]))))
|
||||
|
||||
(def form
|
||||
(mx/component
|
||||
{:render form-render
|
||||
:name "form"
|
||||
:mixins [mx/static mx/reactive]}))
|
||||
(rs/emit! (uda/recovery-request data)
|
||||
(forms/clear :recovery-request)))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "username"
|
||||
:value (:username data "")
|
||||
:on-change (partial on-change :username)
|
||||
:placeholder "username or email address"
|
||||
:type "text"}]
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Recover password"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]])))
|
||||
|
||||
;; --- Recovery Request Page
|
||||
|
||||
(defn- recovery-request-page-render
|
||||
[own]
|
||||
(html
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(form)]]))
|
||||
|
||||
(def recovery-request-page
|
||||
(mx/component
|
||||
{:render recovery-request-page-render
|
||||
:name "recovery-request-page"
|
||||
:mixins [mx/static]}))
|
||||
(mx/defc recovery-request-page
|
||||
{:mixins [mx/static]
|
||||
:will-unmount (forms/cleaner-fn :recovery-request)}
|
||||
[]
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(recovery-request-form)]])
|
||||
|
|
|
@ -5,116 +5,104 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.auth.register
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[rum.core :as rum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as us]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.auth :as uda]
|
||||
[uxbox.main.data.messages :as udm]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.navigation :as nav]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
||||
;; --- Constants
|
||||
|
||||
(def form-data
|
||||
(-> (l/in [:forms :register])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def form-errors
|
||||
(-> (l/in [:errors :register])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def set-value!
|
||||
(partial udf/assign-field-value :register))
|
||||
[uxbox.main.ui.navigation :as nav]))
|
||||
|
||||
;; --- Register Form
|
||||
|
||||
(defn- register-form-render
|
||||
[own]
|
||||
(let [form (mx/react form-data)
|
||||
(def form-data (forms/focus-data :register st/state))
|
||||
(def form-errors (forms/focus-errors :register st/state))
|
||||
(def set-value! (partial forms/set-value! :register))
|
||||
(def set-error! (partial forms/set-error! :register))
|
||||
|
||||
(def +register-form+
|
||||
{:username [forms/required forms/string]
|
||||
:fullname [forms/required forms/string]
|
||||
:email [forms/required forms/email]
|
||||
:password [forms/required forms/string]})
|
||||
|
||||
(mx/defc register-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[]
|
||||
(let [data (mx/react form-data)
|
||||
errors (mx/react form-errors)
|
||||
valid? (us/valid? form uda/register-schema)]
|
||||
valid? (forms/valid? data +register-form+)]
|
||||
(letfn [(on-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(rs/emit! (set-value! field value))))
|
||||
(set-value! field value)))
|
||||
(on-error [{:keys [type code] :as payload}]
|
||||
(case code
|
||||
:uxbox.services.users/email-already-exists
|
||||
(set-error! :email "Email already exists")
|
||||
:uxbox.services.users/username-already-exists
|
||||
(set-error! :username "Username already exists")))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(rs/emit! (uda/register form)))]
|
||||
(html
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "fullname"
|
||||
:value (:fullname form "")
|
||||
:on-change (partial on-change :fullname)
|
||||
:placeholder "Full Name"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :fullname)
|
||||
(rs/emit! (uda/register data on-error)))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
{:name "fullname"
|
||||
:value (:fullname data "")
|
||||
:on-change (partial on-change :fullname)
|
||||
:placeholder "Full Name"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :fullname)
|
||||
|
||||
[:input.input-text
|
||||
{:name "username"
|
||||
:value (:username form "")
|
||||
:on-change (partial on-change :username)
|
||||
:placeholder "Username"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :username)
|
||||
[:input.input-text
|
||||
{:name "username"
|
||||
:value (:username data "")
|
||||
:on-change (partial on-change :username)
|
||||
:placeholder "Username"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :username)
|
||||
|
||||
[:input.input-text
|
||||
{:name "email"
|
||||
:ref "email"
|
||||
:value (:email form "")
|
||||
:on-change (partial on-change :email)
|
||||
:placeholder "Email"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :email)
|
||||
[:input.input-text
|
||||
{:name "email"
|
||||
:ref "email"
|
||||
:value (:email data "")
|
||||
:on-change (partial on-change :email)
|
||||
:placeholder "Email"
|
||||
:type "text"}]
|
||||
(forms/input-error errors :email)
|
||||
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:ref "password"
|
||||
:value (:password form "")
|
||||
:on-change (partial on-change :password)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
(forms/input-error errors :password)
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:ref "password"
|
||||
:value (:password data "")
|
||||
:on-change (partial on-change :password)
|
||||
:placeholder "Password"
|
||||
:type "password"}]
|
||||
(forms/input-error errors :password)
|
||||
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Register"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
;; [:a {:on-click #(rt/go :auth/recover-password)} "Forgot your password?"]
|
||||
[:a {:on-click #(rt/go :auth/login)} "Already have an account?"]]]]))))
|
||||
|
||||
(def register-form
|
||||
(mx/component
|
||||
{:render register-form-render
|
||||
:name "register-form"
|
||||
:mixins [mx/static mx/reactive]}))
|
||||
[:input.btn-primary
|
||||
{:name "login"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:value "Register"
|
||||
:type "submit"}]
|
||||
[:div.login-links
|
||||
;; [:a {:on-click #(rt/go :auth/recover-password)} "Forgot your password?"]
|
||||
[:a {:on-click #(rt/go :auth/login)} "Already have an account?"]]]])))
|
||||
|
||||
;; --- Register Page
|
||||
|
||||
(defn- register-page-render
|
||||
(mx/defc register-page
|
||||
{:mixins [mx/static]}
|
||||
[own]
|
||||
(html
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(register-form)]]))
|
||||
|
||||
(def register-page
|
||||
(mx/component
|
||||
{:render register-page-render
|
||||
:name "register-page"
|
||||
:mixins [mx/static]}))
|
||||
[:div.login
|
||||
[:div.login-body
|
||||
(uum/messages)
|
||||
[:a i/logo]
|
||||
(register-form)]])
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[rum.core :as rum]
|
||||
[lentes.core :as l]
|
||||
[goog.events :as events]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.math :as mth]
|
||||
[uxbox.util.data :as data]
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.colorpicker :refer (colorpicker)]
|
||||
[uxbox.main.ui.dashboard.header :refer (header)]
|
||||
[uxbox.main.ui.forms :as form]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.keyboard :as k]
|
||||
[uxbox.main.ui.lightbox :as lbx]
|
||||
|
@ -24,8 +23,7 @@
|
|||
[uxbox.util.i18n :as t :refer (tr)]
|
||||
[uxbox.util.lens :as ul]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as sc]))
|
||||
[uxbox.util.rstore :as rs]))
|
||||
|
||||
;; --- Refs
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.datetime :as dt]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.lens :as ul]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
(ns uxbox.main.ui.forms
|
||||
(:require [sablono.core :refer-macros [html]]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.schema :as sc]))
|
||||
|
||||
(defn input-error
|
||||
[errors field]
|
||||
(when-let [error (get errors field)]
|
||||
(html
|
||||
[:ul.form-errors
|
||||
[:li {:key error} (tr error)]])))
|
||||
|
||||
(defn error-class
|
||||
[errors field]
|
||||
(when (get errors field)
|
||||
"invalid"))
|
|
@ -6,98 +6,81 @@
|
|||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.settings.password
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[lentes.core :as l]
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.i18n :as t :refer (tr)]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.main.ui.settings.header :refer (header)]
|
||||
[uxbox.util.dom :as dom]))
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.ui.settings.header :refer (header)]))
|
||||
|
||||
;; --- Password Form
|
||||
(def form-data (forms/focus-data :profile-password st/state))
|
||||
(def form-errors (forms/focus-errors :profile-password st/state))
|
||||
(def set-value! (partial forms/set-value! :profile-password))
|
||||
(def set-errors! (partial forms/set-errors! :profile-password))
|
||||
|
||||
(def formdata
|
||||
(-> (l/in [:forms :profile/password])
|
||||
(l/derive st/state)))
|
||||
(def +password-form+
|
||||
[[:password-1 forms/required forms/string [forms/min-len 6]]
|
||||
[:password-2 forms/required forms/string
|
||||
[forms/identical-to :password-1 :message "errors.form.password-not-match"]]
|
||||
[:old-password forms/required forms/string]])
|
||||
|
||||
(def formerrors
|
||||
(-> (l/in [:errors :profile/password])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def assign-field-value
|
||||
(partial udf/assign-field-value :profile/password))
|
||||
|
||||
(defn password-form-render
|
||||
[own]
|
||||
(let [form (mx/react formdata)
|
||||
errors (mx/react formerrors)
|
||||
valid? (sc/valid? form udu/update-password-schema)]
|
||||
(letfn [(on-field-change [field event]
|
||||
(mx/defc password-form
|
||||
{:mixins [mx/reactive mx/static]}
|
||||
[]
|
||||
(let [data (mx/react form-data)
|
||||
errors (mx/react form-errors)
|
||||
valid? (forms/valid? data +password-form+)]
|
||||
(letfn [(on-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(rs/emit! (assign-field-value field value))))
|
||||
(set-value! field value)))
|
||||
(on-submit [event]
|
||||
(rs/emit! (udu/update-password form)))]
|
||||
(html
|
||||
[:form.password-form
|
||||
[:span.user-settings-label "Change password"]
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :old-password)
|
||||
:value (:old-password form "")
|
||||
:on-change (partial on-field-change :old-password)
|
||||
:placeholder "Old password"}]
|
||||
(forms/input-error errors :old-password)
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :password-1)
|
||||
:value (:password-1 form "")
|
||||
:on-change (partial on-field-change :password-1)
|
||||
:placeholder "New password"}]
|
||||
(forms/input-error errors :password-1)
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :password-2)
|
||||
:value (:password-2 form "")
|
||||
:on-change (partial on-field-change :password-2)
|
||||
:placeholder "Confirm password"}]
|
||||
(forms/input-error errors :password-2)
|
||||
[:input.btn-primary
|
||||
{:type "button"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:on-click on-submit
|
||||
:value "Update settings"}]]))))
|
||||
|
||||
(def password-form
|
||||
(mx/component
|
||||
{:render password-form-render
|
||||
:name "password-form"
|
||||
:mixins [mx/static (mx/local) mx/reactive]}))
|
||||
(println "on-submit" data)
|
||||
#_(rs/emit! (udu/update-password form)))]
|
||||
(println "password-form" data)
|
||||
[:form.password-form
|
||||
[:span.user-settings-label "Change password"]
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :old-password)
|
||||
:value (:old-password data "")
|
||||
:on-change (partial on-change :old-password)
|
||||
:placeholder "Old password"}]
|
||||
(forms/input-error errors :old-password)
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :password-1)
|
||||
:value (:password-1 data "")
|
||||
:on-change (partial on-change :password-1)
|
||||
:placeholder "New password"}]
|
||||
(forms/input-error errors :password-1)
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
:class (forms/error-class errors :password-2)
|
||||
:value (:password-2 data "")
|
||||
:on-change (partial on-change :password-2)
|
||||
:placeholder "Confirm password"}]
|
||||
(forms/input-error errors :password-2)
|
||||
[:input.btn-primary
|
||||
{:type "button"
|
||||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:on-click on-submit
|
||||
:value "Update settings"}]])))
|
||||
|
||||
;; --- Password Page
|
||||
|
||||
(defn password-page-render
|
||||
[own]
|
||||
(html
|
||||
[:main.dashboard-main
|
||||
(header)
|
||||
(uum/messages)
|
||||
[:section.dashboard-content.user-settings
|
||||
[:section.user-settings-content
|
||||
(password-form)]]]))
|
||||
|
||||
(def password-page
|
||||
(mx/component
|
||||
{:render password-page-render
|
||||
:name "password-page"
|
||||
:mixins [mx/static]}))
|
||||
(mx/defc password-page
|
||||
{:mixins [mx/static]}
|
||||
[]
|
||||
[:main.dashboard-main
|
||||
(header)
|
||||
(uum/messages)
|
||||
[:section.dashboard-content.user-settings
|
||||
[:section.user-settings-content
|
||||
(password-form)]]])
|
||||
|
|
|
@ -6,100 +6,102 @@
|
|||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.settings.profile
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[cuerdas.core :as str]
|
||||
(:require [cuerdas.core :as str]
|
||||
[lentes.core :as l]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.util.interop :refer (iterable->seq)]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.settings.header :refer (header)]
|
||||
[uxbox.main.ui.messages :as uum]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.util.interop :refer (iterable->seq)]
|
||||
[uxbox.util.dom :as dom]))
|
||||
[uxbox.main.data.users :as udu]))
|
||||
|
||||
;; --- Constants
|
||||
(def form-data (forms/focus-data :profile st/state))
|
||||
(def form-errors (forms/focus-errors :profile st/state))
|
||||
(def set-value! (partial forms/set-value! :profile))
|
||||
(def set-error! (partial forms/set-error! :profile))
|
||||
|
||||
(def formdata
|
||||
(-> (l/in [:forms :profile/main])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def formerrors
|
||||
(-> (l/in [:errors :profile/main])
|
||||
(l/derive st/state)))
|
||||
|
||||
(def assign-field-value
|
||||
(partial udf/assign-field-value :profile/main))
|
||||
|
||||
(def ^:private profile-ref
|
||||
(def profile-ref
|
||||
(-> (l/key :profile)
|
||||
(l/derive st/state)))
|
||||
|
||||
(def +profile-form+
|
||||
{:fullname [forms/required forms/string]
|
||||
:email [forms/required forms/email]
|
||||
:username [forms/required forms/string]})
|
||||
|
||||
;; --- Profile Form
|
||||
|
||||
(defn profile-form-render
|
||||
[own]
|
||||
(let [form (merge (mx/react profile-ref)
|
||||
(mx/react formdata))
|
||||
errors (mx/react formerrors)
|
||||
valid? (sc/valid? form udu/update-profile-schema)
|
||||
theme (get-in form [:metadata :theme] "light")]
|
||||
|
||||
(mx/defc profile-form
|
||||
{:mixins [mx/static mx/reactive]
|
||||
:will-unmount (forms/cleaner-fn :profile)}
|
||||
[]
|
||||
;; TODO: properly persist theme
|
||||
(let [data (merge {:theme "light"}
|
||||
(mx/react profile-ref)
|
||||
(mx/react form-data))
|
||||
errors (mx/react form-errors)
|
||||
valid? (forms/valid? data +profile-form+)
|
||||
theme (:theme data)]
|
||||
(letfn [(on-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(rs/emit! (assign-field-value field value))))
|
||||
(set-value! field value)))
|
||||
(on-error [{:keys [code] :as payload}]
|
||||
(case code
|
||||
:uxbox.services.users/email-already-exists
|
||||
(set-error! :email "Email already exists")
|
||||
:uxbox.services.users/username-already-exists
|
||||
(set-error! :username "Username already exists")))
|
||||
(on-success []
|
||||
(forms/clear! :profile))
|
||||
(on-submit [event]
|
||||
(rs/emit! (udu/update-profile form)))]
|
||||
(html
|
||||
[:form.profile-form
|
||||
[:span.user-settings-label "Name, username and email"]
|
||||
[:input.input-text
|
||||
{:type "text"
|
||||
:on-change (partial on-change :fullname)
|
||||
:value (:fullname form "")
|
||||
:placeholder "Your name"}]
|
||||
(forms/input-error errors :fullname)
|
||||
[:input.input-text
|
||||
{:type "text"
|
||||
:on-change (partial on-change :username)
|
||||
:value (:username form "")
|
||||
:placeholder "Your username"}]
|
||||
(rs/emit! (udu/update-profile data on-success on-error)))]
|
||||
[:form.profile-form
|
||||
[:span.user-settings-label "Name, username and email"]
|
||||
[:input.input-text
|
||||
{:type "text"
|
||||
:on-change (partial on-change :fullname)
|
||||
:value (:fullname data "")
|
||||
:placeholder "Your name"}]
|
||||
[:input.input-text
|
||||
{:type "text"
|
||||
:on-change (partial on-change :username)
|
||||
:value (:username data "")
|
||||
:placeholder "Your username"}]
|
||||
(forms/input-error errors :username)
|
||||
|
||||
[:input.input-text
|
||||
{:type "email"
|
||||
:on-change (partial on-change :email)
|
||||
:value (:email form "")
|
||||
:value (:email data "")
|
||||
:placeholder "Your email"}]
|
||||
(forms/input-error errors :email)
|
||||
|
||||
[:span.user-settings-label "Choose a color theme"]
|
||||
[:div.input-radio.radio-primary
|
||||
[:input {:type "radio"
|
||||
:checked (= theme "light")
|
||||
:on-change (partial on-change [:metadata :theme])
|
||||
:checked (when (= theme "light") "checked")
|
||||
:on-change (partial on-change :theme)
|
||||
:id "light-theme"
|
||||
:name "theme"
|
||||
:value "light"}]
|
||||
[:label {:for "light-theme"} "Light theme"]
|
||||
|
||||
[:input {:type "radio"
|
||||
:checked (= theme "dark")
|
||||
:on-change (partial on-change [:metadata :theme])
|
||||
:checked (when (= theme "dark") "checked")
|
||||
:on-change (partial on-change :theme)
|
||||
:id "dark-theme"
|
||||
:name "theme"
|
||||
:value "dark"}]
|
||||
[:label {:for "dark-theme"} "Dark theme"]
|
||||
|
||||
[:input {:type "radio"
|
||||
:checked (= theme "high-contrast")
|
||||
:on-change (partial on-change [:metadata :theme])
|
||||
:checked (when (= theme "high-contrast") "checked")
|
||||
:on-change (partial on-change :theme)
|
||||
:id "high-contrast-theme"
|
||||
:name "theme"
|
||||
:value "high-contrast"}]
|
||||
|
@ -110,18 +112,13 @@
|
|||
:class (when-not valid? "btn-disabled")
|
||||
:disabled (not valid?)
|
||||
:on-click on-submit
|
||||
:value "Update settings"}]]))))
|
||||
|
||||
(def profile-form
|
||||
(mx/component
|
||||
{:render profile-form-render
|
||||
:name "profile-form"
|
||||
:mixins [(mx/local) mx/reactive mx/static]}))
|
||||
:value "Update settings"}]])))
|
||||
|
||||
;; --- Profile Photo Form
|
||||
|
||||
(defn- profile-photo-form-render
|
||||
[own]
|
||||
(mx/defc profile-photo-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[]
|
||||
(letfn [(on-change [event]
|
||||
(let [target (dom/get-target event)
|
||||
file (-> (dom/get-files target)
|
||||
|
@ -133,36 +130,22 @@
|
|||
photo (if (or (str/empty? photo) (nil? photo))
|
||||
"images/avatar.jpg"
|
||||
photo)]
|
||||
(html
|
||||
[:form.avatar-form
|
||||
[:img {:src photo :border "0"}]
|
||||
[:input {:type "file"
|
||||
:value ""
|
||||
:on-change on-change}]]))))
|
||||
|
||||
(def profile-photo-form
|
||||
(mx/component
|
||||
{:render profile-photo-form-render
|
||||
:name profile-photo-form
|
||||
:mixins [mx/static mx/reactive]}))
|
||||
[:form.avatar-form
|
||||
[:img {:src photo}]
|
||||
[:input {:type "file"
|
||||
:value ""
|
||||
:on-change on-change}]])))
|
||||
|
||||
;; --- Profile Page
|
||||
|
||||
(defn profile-page-render
|
||||
[own]
|
||||
(html
|
||||
[:main.dashboard-main
|
||||
(header)
|
||||
(uum/messages)
|
||||
[:section.dashboard-content.user-settings
|
||||
[:section.user-settings-content
|
||||
[:span.user-settings-label "Your avatar"]
|
||||
(profile-photo-form)
|
||||
(profile-form)
|
||||
]]]))
|
||||
|
||||
(def profile-page
|
||||
(mx/component
|
||||
{:render profile-page-render
|
||||
:name "profile-page"
|
||||
:mixins [mx/static]}))
|
||||
(mx/defc profile-page
|
||||
{:mixins [mx/static]}
|
||||
[]
|
||||
[:main.dashboard-main
|
||||
(header)
|
||||
(uum/messages)
|
||||
[:section.dashboard-content.user-settings
|
||||
[:section.user-settings-content
|
||||
[:span.user-settings-label "Your avatar"]
|
||||
(profile-photo-form)
|
||||
(profile-form)]]])
|
||||
|
|
|
@ -61,8 +61,8 @@
|
|||
|
||||
(def alignment-ref
|
||||
(letfn [(getter [flags]
|
||||
(and (contains? flags :grid/indexed)
|
||||
(contains? flags :grid/alignment)
|
||||
(and (contains? flags :grid-indexed)
|
||||
(contains? flags :grid-alignment)
|
||||
(contains? flags :grid)))]
|
||||
(-> (l/lens getter)
|
||||
(l/derive flags-ref))))
|
||||
|
|
|
@ -21,16 +21,16 @@
|
|||
(defn- grid-render
|
||||
[own]
|
||||
(let [options (:options (mx/react wb/page-ref))
|
||||
color (:grid/color options "#cccccc")
|
||||
color (:grid-color options "#cccccc")
|
||||
width c/viewport-width
|
||||
height c/viewport-height
|
||||
x-ticks (range (- 0 c/canvas-start-x)
|
||||
(- width c/canvas-start-x)
|
||||
(:grid/x-axis options 10))
|
||||
(:grid-x-axis options 10))
|
||||
|
||||
y-ticks (range (- 0 c/canvas-start-x)
|
||||
(- height c/canvas-start-x)
|
||||
(:grid/y-axis options 10))
|
||||
(:grid-y-axis options 10))
|
||||
|
||||
path (as-> [] $
|
||||
(reduce (partial vertical-line height) $ x-ticks)
|
||||
|
|
|
@ -6,119 +6,114 @@
|
|||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.settings
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
[rum.core :as rum]
|
||||
(:require [lentes.core :as l]
|
||||
[uxbox.main.constants :as c]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.main.data.forms :as udf]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.data.lightbox :as udl]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.main.ui.forms :as forms]
|
||||
[uxbox.main.ui.lightbox :as lbx]
|
||||
[uxbox.main.ui.colorpicker :as uucp]
|
||||
[uxbox.main.ui.workspace.base :as wb]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.data :refer (parse-int)]))
|
||||
|
||||
;; --- Lentes
|
||||
|
||||
(def formdata (udf/focus-form-data :workspace/settings))
|
||||
(def formerrors (udf/focus-form-errors :workspace/settings))
|
||||
(def assign-field-value (partial udf/assign-field-value :workspace/settings))
|
||||
(def form-data (forms/focus-data :workspace-settings st/state))
|
||||
(def form-errors (forms/focus-errors :workspace-settings st/state))
|
||||
(def set-value! (partial forms/set-value! :workspace-settings))
|
||||
(def set-errors! (partial forms/set-errors! :workspace-settings))
|
||||
(def page-ref wb/page-ref)
|
||||
|
||||
;; --- Form Component
|
||||
|
||||
(def settings-form-defaults
|
||||
{:grid/x-axis c/grid-x-axis
|
||||
:grid/y-axis c/grid-y-axis
|
||||
:grid/color "#b5bdb9"
|
||||
:grid/alignment false})
|
||||
(def +settings-defaults+
|
||||
{:grid-x-axis c/grid-x-axis
|
||||
:grid-y-axis c/grid-y-axis
|
||||
:grid-color "#b5bdb9"
|
||||
:grid-alignment false})
|
||||
|
||||
(defn- settings-form-render
|
||||
[own]
|
||||
(let [page (mx/react wb/page-ref)
|
||||
form (merge settings-form-defaults
|
||||
(:options page)
|
||||
(mx/react formdata))
|
||||
errors (mx/react formerrors)]
|
||||
(def +settings-form+
|
||||
{:grid-y-axis [forms/required forms/integer [forms/in-range 2 100]]
|
||||
:grid-x-axis [forms/required forms/integer [forms/in-range 2 100]]
|
||||
:grid-alignment [forms/boolean]
|
||||
:grid-color [forms/required forms/color]})
|
||||
|
||||
(mx/defc settings-form
|
||||
{:mixins [mx/reactive]}
|
||||
[]
|
||||
(let [{:keys [id] :as page} (mx/react page-ref)
|
||||
errors (mx/react form-errors)
|
||||
data (merge +settings-defaults+
|
||||
(:metadata page)
|
||||
(mx/react form-data))]
|
||||
(letfn [(on-field-change [field event]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value "")]
|
||||
(rs/emit! (assign-field-value field value))))
|
||||
(set-value! field value)))
|
||||
(on-color-change [color]
|
||||
(rs/emit! (assign-field-value :grid/color color)))
|
||||
(set-value! :grid-color color))
|
||||
(on-align-change [event]
|
||||
(let [checked? (-> (dom/get-target event)
|
||||
(dom/checked?))]
|
||||
(rs/emit! (assign-field-value :grid/alignment checked?))))
|
||||
(set-value! :grid-alignment checked?)))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(rs/emit! (udw/submit-workspace-settings (:id page) form)))]
|
||||
(html
|
||||
[:form {:on-submit on-submit}
|
||||
[:span.lightbox-label "Grid size"]
|
||||
[:div.project-size
|
||||
[:div.input-element.pixels
|
||||
[:input#grid-x.input-text
|
||||
{:placeholder "X"
|
||||
:type "number"
|
||||
:class (forms/error-class errors :grid/x-axis)
|
||||
:value (:grid/x-axis form "")
|
||||
:on-change (partial on-field-change :grid/x-axis)
|
||||
:min 1
|
||||
:max 100}]]
|
||||
[:div.input-element.pixels
|
||||
[:input#grid-y.input-text
|
||||
{:placeholder "Y"
|
||||
:type "number"
|
||||
:class (forms/error-class errors :grid/y-axis)
|
||||
:value (:grid/y-axis form "")
|
||||
:on-change (partial on-field-change :grid/y-axis)
|
||||
:min 1
|
||||
:max 100}]]]
|
||||
[:span.lightbox-label "Grid color"]
|
||||
(uucp/colorpicker
|
||||
:value (:grid/color form)
|
||||
:on-change on-color-change)
|
||||
[:span.lightbox-label "Grid magnet option"]
|
||||
[:div.input-checkbox.check-primary
|
||||
[:input
|
||||
{:type "checkbox"
|
||||
:on-change on-align-change
|
||||
:checked (:grid/alignment form)
|
||||
:id "magnet"
|
||||
:value "Yes"}]
|
||||
[:label {:for "magnet"} "Activate magnet"]]
|
||||
[:input.btn-primary
|
||||
{:type "submit"
|
||||
:value "Save"}]]))))
|
||||
(let [[errors data] (forms/validate data +settings-form+)]
|
||||
(if errors
|
||||
(set-errors! errors)
|
||||
(rs/emit! (udw/update-metadata id data)
|
||||
(forms/clear :workspace-settings)
|
||||
(udl/hide-lightbox)))))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:span.lightbox-label "Grid size"]
|
||||
[:div.project-size
|
||||
[:div.input-element.pixels
|
||||
[:input#grid-x.input-text
|
||||
{:placeholder "X"
|
||||
:type "number"
|
||||
:class (forms/error-class errors :grid-x-axis)
|
||||
:value (:grid-x-axis data "")
|
||||
:on-change (partial on-field-change :grid-x-axis)
|
||||
:min 2
|
||||
:max 100}]]
|
||||
[:div.input-element.pixels
|
||||
[:input#grid-y.input-text
|
||||
{:placeholder "Y"
|
||||
:type "number"
|
||||
:class (forms/error-class errors :grid-y-axis)
|
||||
:value (:grid-y-axis data "")
|
||||
:on-change (partial on-field-change :grid-y-axis)
|
||||
:min 2
|
||||
:max 100}]]]
|
||||
[:span.lightbox-label "Grid color"]
|
||||
(uucp/colorpicker
|
||||
:value (:grid-color data)
|
||||
:on-change on-color-change)
|
||||
[:span.lightbox-label "Grid magnet option"]
|
||||
[:div.input-checkbox.check-primary
|
||||
[:input
|
||||
{:type "checkbox"
|
||||
:on-change on-align-change
|
||||
:checked (:grid-alignment data)
|
||||
:id "magnet"
|
||||
:value "Yes"}]
|
||||
[:label {:for "magnet"} "Activate magnet"]]
|
||||
[:input.btn-primary
|
||||
{:type "submit"
|
||||
:value "Save"}]])))
|
||||
|
||||
(def settings-form
|
||||
(mx/component
|
||||
{:render settings-form-render
|
||||
:name "settings-form"
|
||||
:mixins [(mx/local) mx/reactive mx/static]}))
|
||||
|
||||
(defn- settings-dialog-render
|
||||
(mx/defc settings-dialog
|
||||
[own]
|
||||
(html
|
||||
[:div.lightbox-body.settings
|
||||
[:h3 "Grid settings"]
|
||||
(settings-form)
|
||||
[:a.close {:href "#"
|
||||
:on-click #(do (dom/prevent-default %)
|
||||
(udl/close!))} i/close]]))
|
||||
|
||||
(def settings-dialog
|
||||
(mx/component
|
||||
{:render settings-dialog-render
|
||||
:name "settings-dialog"
|
||||
:mixins []}))
|
||||
[:div.lightbox-body.settings
|
||||
[:h3 "Grid settings"]
|
||||
(settings-form)
|
||||
[:a.close {:href "#"
|
||||
:on-click #(do (dom/prevent-default %)
|
||||
(udl/close!))} i/close]])
|
||||
|
||||
(defmethod lbx/render-lightbox :settings
|
||||
[_]
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.data.lightbox :as udl]
|
||||
[uxbox.main.ui.dashboard.projects :refer (+layouts+)]
|
||||
[uxbox.main.ui.workspace.base :as wb]
|
||||
[uxbox.main.ui.workspace.sidebar.sitemap-pageform]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.main.ui.lightbox :as lbx]
|
||||
[uxbox.util.data :refer (deep-merge parse-int)]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
||||
;; --- Refs
|
||||
|
@ -87,103 +86,3 @@
|
|||
:let [active? (= (:id page) (:id current))]]
|
||||
(-> (page-item page (count pages) active?)
|
||||
(mx/with-key (:id page))))]]]))
|
||||
|
||||
;; --- Lightbox
|
||||
|
||||
(def +page-defaults+
|
||||
{:width 1920
|
||||
:height 1080
|
||||
:layout :desktop})
|
||||
|
||||
(mx/defc layout-input
|
||||
[local page id]
|
||||
(let [layout (get +layouts+ id)
|
||||
metadata (:metadata page)
|
||||
size (select-keys layout [:width :height])
|
||||
change #(swap! local update :metadata merge {:layout id} size)]
|
||||
[:div
|
||||
[:input {:type "radio"
|
||||
:key id :id id
|
||||
:name "project-layout"
|
||||
:value (:id layout)
|
||||
:checked (= id (:layout metadata))
|
||||
:on-change change}]
|
||||
[:label {:value (:id layout) :for id} (:name layout)]]))
|
||||
|
||||
(mx/defcs page-form-lightbox
|
||||
{:mixins [(mx/local)]}
|
||||
[own page]
|
||||
(let [local (:rum/local own)
|
||||
page (deep-merge page @local {:data nil})
|
||||
metadata (:metadata page)
|
||||
edition? (:id page)
|
||||
valid? (and (not (str/empty? (str/trim (:name page ""))))
|
||||
(pos? (:width metadata))
|
||||
(pos? (:height metadata)))]
|
||||
(letfn [(update-size [field e]
|
||||
(let [value (dom/event->value e)
|
||||
value (parse-int value)]
|
||||
(swap! local assoc-in [:metadata field] value)))
|
||||
(update-name [e]
|
||||
(let [value (dom/event->value e)]
|
||||
(swap! local assoc :name value)))
|
||||
(toggle-sizes []
|
||||
(let [width (get-in page [:metadata :width])
|
||||
height (get-in page [:metadata :height])]
|
||||
(swap! local update :metadata merge {:width height
|
||||
:height width})))
|
||||
(cancel [e]
|
||||
(dom/prevent-default e)
|
||||
(udl/close!))
|
||||
(persist [e]
|
||||
(dom/prevent-default e)
|
||||
(udl/close!)
|
||||
(if edition?
|
||||
(rs/emit! (udp/update-page-metadata page))
|
||||
(rs/emit! (udp/create-page page))))]
|
||||
[:div.lightbox-body
|
||||
(if edition?
|
||||
[:h3 "Edit page"]
|
||||
[:h3 "New page"])
|
||||
[:form
|
||||
[:input#project-name.input-text
|
||||
{:placeholder "Page name"
|
||||
:type "text"
|
||||
:value (:name page "")
|
||||
:auto-focus true
|
||||
:on-change update-name}]
|
||||
[:div.project-size
|
||||
[:div.input-element.pixels
|
||||
[:input#project-witdh.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 4000
|
||||
:value (:width metadata)
|
||||
:on-change #(update-size :width %)}]]
|
||||
[:a.toggle-layout {:on-click toggle-sizes} i/toggle]
|
||||
[:div.input-element.pixels
|
||||
[:input#project-height.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 4000
|
||||
:value (:height metadata)
|
||||
:on-change #(update-size :height %)}]]]
|
||||
|
||||
[:div.input-radio.radio-primary
|
||||
(layout-input local page "mobile")
|
||||
(layout-input local page "tablet")
|
||||
(layout-input local page "notebook")
|
||||
(layout-input local page "desktop")]
|
||||
|
||||
(when valid?
|
||||
[:input#project-btn.btn-primary
|
||||
{:value "Go go go!"
|
||||
:on-click persist
|
||||
:type "button"}])]
|
||||
[:a.close {:on-click cancel} i/close]])))
|
||||
|
||||
(defmethod lbx/render-lightbox :page-form
|
||||
[{:keys [page]}]
|
||||
(page-form-lightbox page))
|
||||
|
|
145
src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs
Normal file
145
src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs
Normal file
|
@ -0,0 +1,145 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.sitemap-pageform
|
||||
(:require [lentes.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.main.state :as st]
|
||||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.data.lightbox :as udl]
|
||||
[uxbox.main.ui.dashboard.projects :refer (+layouts+)]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.lightbox :as lbx]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.router :as r]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.forms :as forms]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.data :refer (deep-merge parse-int)]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
||||
(def form-data (forms/focus-data :workspace-page-form st/state))
|
||||
(def set-value! (partial forms/set-value! :workspace-page-form))
|
||||
|
||||
;; --- Lightbox
|
||||
|
||||
(def +page-defaults+
|
||||
{:width 1920
|
||||
:height 1080
|
||||
:layout :desktop})
|
||||
|
||||
(def +page-form+
|
||||
{:name [forms/required forms/string]
|
||||
:width [forms/required forms/number]
|
||||
:height [forms/required forms/number]
|
||||
:layout [forms/required forms/string]})
|
||||
|
||||
(mx/defc layout-input
|
||||
[data id]
|
||||
(let [{:keys [id name width height]} (get +layouts+ id)]
|
||||
(letfn [(on-change [event]
|
||||
(set-value! :layout id)
|
||||
(set-value! :width width)
|
||||
(set-value! :height height))]
|
||||
[:div
|
||||
[:input {:type "radio"
|
||||
:id id
|
||||
:name "project-layout"
|
||||
:value id
|
||||
:checked (when (= id (:layout data)) "checked")
|
||||
:on-change on-change}]
|
||||
[:label {:value id :for id} name]])))
|
||||
|
||||
(mx/defc page-form
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[{:keys [metadata id] :as page}]
|
||||
(let [data (merge +page-defaults+
|
||||
(select-keys page [:name])
|
||||
(select-keys metadata [:width :height :layout])
|
||||
(mx/react form-data))
|
||||
valid? (forms/valid? data +page-form+)]
|
||||
(letfn [(update-size [field e]
|
||||
(let [value (dom/event->value e)
|
||||
value (parse-int value)]
|
||||
(set-value! field value)))
|
||||
(update-name [e]
|
||||
(let [value (dom/event->value e)]
|
||||
(set-value! :name value)))
|
||||
(toggle-sizes []
|
||||
(let [{:keys [width height]} data]
|
||||
(set-value! :width height)
|
||||
(set-value! :height width)))
|
||||
(on-cancel [e]
|
||||
(dom/prevent-default e)
|
||||
(udl/close!))
|
||||
(on-save [e]
|
||||
(dom/prevent-default e)
|
||||
(udl/close!)
|
||||
(if (nil? id)
|
||||
(rs/emit! (udp/create-page data))
|
||||
(rs/emit! (udp/update-page id data))))]
|
||||
[:form
|
||||
[:input#project-name.input-text
|
||||
{:placeholder "Page name"
|
||||
:type "text"
|
||||
:value (:name data "")
|
||||
:auto-focus true
|
||||
:on-change update-name}]
|
||||
[:div.project-size
|
||||
[:div.input-element.pixels
|
||||
[:input#project-witdh.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 4000
|
||||
:value (:width data)
|
||||
:on-change #(update-size :width %)}]]
|
||||
[:a.toggle-layout {:on-click toggle-sizes} i/toggle]
|
||||
[:div.input-element.pixels
|
||||
[:input#project-height.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 4000
|
||||
:value (:height data)
|
||||
:on-change #(update-size :height %)}]]]
|
||||
|
||||
[:div.input-radio.radio-primary
|
||||
(layout-input data "mobile")
|
||||
(layout-input data "tablet")
|
||||
(layout-input data "notebook")
|
||||
(layout-input data "desktop")]
|
||||
|
||||
(when valid?
|
||||
[:input#project-btn.btn-primary
|
||||
{:value "Go go go!"
|
||||
:on-click on-save
|
||||
:type "button"}])])))
|
||||
|
||||
(mx/defc page-form-lightbox
|
||||
{:mixins [mx/static]
|
||||
:will-unmount (fn [own]
|
||||
(forms/clear! :workspace-page-form)
|
||||
own)}
|
||||
[{:keys [id] :as page}]
|
||||
(letfn [(on-cancel [event]
|
||||
(dom/prevent-default event)
|
||||
(udl/close!))]
|
||||
(let [creation? (nil? id)]
|
||||
[:div.lightbox-body
|
||||
(if creation?
|
||||
[:h3 "New page"]
|
||||
[:h3 "Edit page"])
|
||||
(page-form page)
|
||||
[:a.close {:on-click on-cancel} i/close]])))
|
||||
|
||||
(defmethod lbx/render-lightbox :page-form
|
||||
[{:keys [page]}]
|
||||
(page-form-lightbox page))
|
||||
|
||||
|
264
src/uxbox/util/forms.cljs
Normal file
264
src/uxbox/util/forms.cljs
Normal file
|
@ -0,0 +1,264 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.util.forms
|
||||
(:refer-clojure :exclude [keyword uuid vector boolean map set])
|
||||
(:require [struct.core :as st]
|
||||
[lentes.core :as l]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.i18n :refer (tr)]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Form Validation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Form Validators
|
||||
|
||||
(def required
|
||||
(assoc st/required :message "errors.form.required"))
|
||||
|
||||
(def string
|
||||
(assoc st/string :message "errors.form.string"))
|
||||
|
||||
(def number
|
||||
(assoc st/number :message "errors.form.number"))
|
||||
|
||||
(def integer
|
||||
(assoc st/integer :message "errors.form.integer"))
|
||||
|
||||
(def boolean
|
||||
(assoc st/boolean :message "errors.form.bool"))
|
||||
|
||||
(def identical-to
|
||||
(assoc st/identical-to :message "errors.form.identical-to"))
|
||||
|
||||
(def in-range st/in-range)
|
||||
;; (def uuid-like st/uuid-like)
|
||||
(def uuid st/uuid)
|
||||
(def keyword st/keyword)
|
||||
(def integer-str st/integer-str)
|
||||
(def number-str st/number-str)
|
||||
;; (def boolean-like st/boolean-like)
|
||||
(def email st/email)
|
||||
;; (def function st/function)
|
||||
(def positive st/positive)
|
||||
;; (def validate st/validate)
|
||||
;; (def validate! st/validate!)
|
||||
|
||||
(def max-len
|
||||
{:message "errors.form.max-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(let [len (count v)]
|
||||
(>= len v)))})
|
||||
|
||||
(def min-len
|
||||
{:message "errors.form.min-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(>= (count v) n))})
|
||||
|
||||
(def color
|
||||
{:message "errors.form.color"
|
||||
:optional true
|
||||
:validate #(not (nil? (re-find #"^#[0-9A-Fa-f]{6}$" %)))})
|
||||
|
||||
;; --- Public Validation Api
|
||||
|
||||
(defn validate
|
||||
([data schema]
|
||||
(validate data schema nil))
|
||||
([data schema opts]
|
||||
(st/validate data schema opts)))
|
||||
|
||||
(defn validate!
|
||||
([data schema]
|
||||
(validate! data schema nil))
|
||||
([data schema opts]
|
||||
(let [[errors data] (validate data schema opts)]
|
||||
(if errors
|
||||
(throw (ex-info "Invalid data" errors))
|
||||
data))))
|
||||
|
||||
(defn valid?
|
||||
[data schema]
|
||||
(let [[errors data] (validate data schema)]
|
||||
(not errors)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Form Events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Set Error
|
||||
|
||||
(defrecord SetError [type field error]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:errors type field] error)))
|
||||
|
||||
(defn set-error
|
||||
([type field]
|
||||
(set-error type field nil))
|
||||
([type field error]
|
||||
{:pre [(keyword? type)
|
||||
(keyword? field)
|
||||
(any? error)]}
|
||||
(SetError. type field error)))
|
||||
|
||||
(defn set-error!
|
||||
[& args]
|
||||
(rs/emit! (apply set-error args)))
|
||||
|
||||
;; --- Set Errors
|
||||
|
||||
(defrecord SetErrors [type errors]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:errors type] errors)))
|
||||
|
||||
(defn set-errors
|
||||
([type]
|
||||
(set-errors type nil))
|
||||
([type errors]
|
||||
{:pre [(keyword? type)
|
||||
(or (map? errors)
|
||||
(nil? errors))]}
|
||||
(SetErrors. type errors)))
|
||||
|
||||
(defn set-errors!
|
||||
[& args]
|
||||
(rs/emit! (apply set-errors args)))
|
||||
|
||||
;; --- Set Value
|
||||
|
||||
(defrecord SetValue [type field value]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [form-path (into [:forms type] (if (coll? field) field [field]))
|
||||
errors-path (into [:errors type] (if (coll? field) field [field]))]
|
||||
(-> state
|
||||
(assoc-in form-path value)
|
||||
(update-in (butlast errors-path) dissoc (last errors-path))))))
|
||||
|
||||
(defn set-value
|
||||
[type field value]
|
||||
{:pre [(keyword? type)
|
||||
(keyword? field)
|
||||
(any? value)]}
|
||||
(SetValue. type field value))
|
||||
|
||||
(defn set-value!
|
||||
[type field value]
|
||||
(rs/emit! (set-value type field value)))
|
||||
|
||||
;; --- Validate Form
|
||||
|
||||
;; (defrecord ValidateForm [type form data on-success]
|
||||
;; rs/WatchEvent
|
||||
;; (-apply-watch [_ state stream]
|
||||
;; (let [[errors data] (validate data form)]
|
||||
;; (if errors
|
||||
;; (rx/of (set-errors type errors))
|
||||
;; (do
|
||||
;; (on-success data)
|
||||
;; (rx/empty))))))
|
||||
|
||||
;; (defn validate-form
|
||||
;; [& {:keys [type form data on-success]}]
|
||||
;; {:pre [(keyword? type)
|
||||
;; (map? form)
|
||||
;; (map? data)
|
||||
;; (fn? on-success)]}
|
||||
;; (ValidateForm. type form data on-success))
|
||||
|
||||
;; (defn validate-form!
|
||||
;; [& args]
|
||||
;; (rs/emit! (apply validate-form args)))
|
||||
|
||||
;; --- Clear Form
|
||||
|
||||
(defrecord ClearForm [type]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:forms type] nil)))
|
||||
|
||||
(defn clear-form
|
||||
[type]
|
||||
{:pre [(keyword? type)]}
|
||||
(ClearForm. type))
|
||||
|
||||
(defn clear-form!
|
||||
[type]
|
||||
(rs/emit! (clear-form type)))
|
||||
|
||||
;; --- Clear Form
|
||||
|
||||
(defrecord ClearErrors [type]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:errors type] nil)))
|
||||
|
||||
(defn clear-errors
|
||||
[type]
|
||||
{:pre [(keyword? type)]}
|
||||
(ClearErrors. type))
|
||||
|
||||
(defn clear-errors!
|
||||
[type]
|
||||
(rs/emit! (clear-errors type)))
|
||||
|
||||
;; --- Clear
|
||||
|
||||
(defrecord Clear [type]
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/of (clear-form type)
|
||||
(clear-errors type))))
|
||||
|
||||
(defn clear
|
||||
[type]
|
||||
(Clear. type))
|
||||
|
||||
(defn clear!
|
||||
[type]
|
||||
(rs/emit! (clear type)))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn focus-data
|
||||
[type state]
|
||||
(-> (l/in [:forms type])
|
||||
(l/derive state)))
|
||||
|
||||
(defn focus-errors
|
||||
[type state]
|
||||
(-> (l/in [:errors type])
|
||||
(l/derive state)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Form UI
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mx/defc input-error
|
||||
[errors field]
|
||||
(when-let [error (get errors field)]
|
||||
[:ul.form-errors
|
||||
[:li {:key error} (tr error)]]))
|
||||
|
||||
(defn error-class
|
||||
[errors field]
|
||||
(when (get errors field)
|
||||
"invalid"))
|
||||
|
||||
(defn cleaner-fn
|
||||
[type]
|
||||
{:pre [(keyword? type)]}
|
||||
(fn [own]
|
||||
(clear! type)
|
||||
own))
|
|
@ -37,10 +37,7 @@
|
|||
(defrecord Navigate [id params]
|
||||
rs/EffectEvent
|
||||
(-apply-effect [_ state]
|
||||
(let [loc (merge {:handler id}
|
||||
(when params
|
||||
{:route-params params}))]
|
||||
(r/navigate! +router+ id params))))
|
||||
(r/navigate! +router+ id {})))
|
||||
|
||||
(defn navigate
|
||||
([id] (navigate id nil))
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.util.schema
|
||||
(:refer-clojure :exclude [keyword uuid vector boolean map set])
|
||||
(:require [struct.core :as st]
|
||||
[uxbox.util.i18n :refer (tr)]))
|
||||
|
||||
;; (def datetime
|
||||
;; {:message "must be an instant"
|
||||
;; :optional true
|
||||
;; :validate #(instance? Instant %)})
|
||||
|
||||
(def required
|
||||
(assoc st/required :message "errors.form.required"))
|
||||
|
||||
(def string
|
||||
(assoc st/string :message "errors.form.string"))
|
||||
|
||||
(def number
|
||||
(assoc st/number :message "errors.form.number"))
|
||||
|
||||
(def integer
|
||||
(assoc st/integer :message "errors.form.integer"))
|
||||
|
||||
(def boolean
|
||||
(assoc st/boolean :message "errors.form.bool"))
|
||||
|
||||
(def identical-to
|
||||
(assoc st/identical-to :message "errors.form.identical-to"))
|
||||
|
||||
(def in-range st/in-range)
|
||||
;; (def uuid-like st/uuid-like)
|
||||
(def uuid st/uuid)
|
||||
(def keyword st/keyword)
|
||||
(def integer-str st/integer-str)
|
||||
(def number-str st/number-str)
|
||||
;; (def boolean-like st/boolean-like)
|
||||
(def email st/email)
|
||||
;; (def function st/function)
|
||||
;; (def positive st/positive)
|
||||
;; (def validate st/validate)
|
||||
;; (def validate! st/validate!)
|
||||
|
||||
(def max-len
|
||||
{:message "errors.form.max-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(let [len (count v)]
|
||||
(>= len v)))})
|
||||
|
||||
(def min-len
|
||||
{:message "errors.form.min-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(>= (count v) n))})
|
||||
|
||||
(def color
|
||||
{:message "errors.form.color"
|
||||
:optional true
|
||||
:validate #(not (nil? (re-find #"^#[0-9A-Fa-f]{6}$" %)))})
|
||||
|
||||
(defn validate
|
||||
([data schema]
|
||||
(validate data schema nil))
|
||||
([data schema opts]
|
||||
(st/validate data schema opts)))
|
||||
|
||||
(defn validate!
|
||||
([data schema]
|
||||
(validate! data schema nil))
|
||||
([data schema opts]
|
||||
(let [[errors data] (validate data schema opts)]
|
||||
(if errors
|
||||
(throw (ex-info "Invalid data" errors))
|
||||
data))))
|
||||
|
||||
(defn valid?
|
||||
[data schema]
|
||||
(let [[errors data] (validate data schema)]
|
||||
(not errors)))
|
47
src/uxbox/util/spec.cljs
Normal file
47
src/uxbox/util/spec.cljs
Normal file
|
@ -0,0 +1,47 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.util.spec
|
||||
(:require [cljs.spec :as s]))
|
||||
|
||||
;; --- Constants
|
||||
|
||||
(def email-rx
|
||||
#"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
||||
|
||||
(def uuid-rx
|
||||
#"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$")
|
||||
|
||||
;; --- Predicates
|
||||
|
||||
(defn email?
|
||||
[v]
|
||||
(and string?
|
||||
(re-matches email-rx v)))
|
||||
|
||||
(defn color?
|
||||
[v]
|
||||
(and (string? v)
|
||||
(re-matches #"^#[0-9A-Fa-f]{6}$" v)))
|
||||
|
||||
(defn file?
|
||||
[v]
|
||||
(instance? js/File v))
|
||||
|
||||
;; --- Default Specs
|
||||
|
||||
(s/def ::uuid uuid?)
|
||||
(s/def ::email email?)
|
||||
(s/def ::color color?)
|
||||
|
||||
;; --- Public Api
|
||||
|
||||
(defn valid?
|
||||
[spec data]
|
||||
(let [valid (s/valid? spec data)]
|
||||
(when-not valid
|
||||
(js/console.error (str "Spec validation error:\n" (s/explain-str spec data))))
|
||||
valid))
|
|
@ -8,7 +8,7 @@
|
|||
(:require [beicon.core :as rx]
|
||||
[uxbox.util.rstore :as rs]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.schema :as sc]
|
||||
[uxbox.util.forms :as sc]
|
||||
[uxbox.util.data :refer (parse-int)]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.data.pages :as udpg]
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
|
||||
(defonce tree (kd/create))
|
||||
|
||||
(defmethod impl/handler :grid/init
|
||||
(defmethod impl/handler :grid-init
|
||||
[{:keys [sender width height x-axis y-axis] :as opts}]
|
||||
(time
|
||||
(kd/setup! tree width height (or x-axis 10) (or y-axis 10)))
|
||||
(impl/reply! sender nil))
|
||||
|
||||
(defmethod impl/handler :grid/align
|
||||
(defmethod impl/handler :grid-align
|
||||
[{:keys [sender point] :as message}]
|
||||
(let [point [(:x point) (:y point)]
|
||||
results (kd/nearest tree point 1)
|
||||
|
|
Loading…
Add table
Reference in a new issue