mirror of
https://github.com/penpot/penpot.git
synced 2025-05-31 00:46:11 +02: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>
|
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.data.auth
|
(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.main.repo :as rp]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.schema :as us]
|
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
|
[uxbox.main.state :as st]
|
||||||
[uxbox.main.data.projects :as udp]
|
[uxbox.main.data.projects :as udp]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.main.data.users :as udu]
|
||||||
[uxbox.main.data.messages :as udm]
|
[uxbox.main.data.messages :as udm]
|
||||||
[uxbox.main.data.forms :as udf]
|
|
||||||
[uxbox.util.storage :refer (storage)]))
|
[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
|
;; --- Logged In
|
||||||
|
|
||||||
(defrecord LoggedIn [data]
|
(defrecord LoggedIn [data]
|
||||||
|
@ -58,8 +64,12 @@
|
||||||
(rx/map logged-in)
|
(rx/map logged-in)
|
||||||
(rx/catch rp/client-error? on-error)))))
|
(rx/catch rp/client-error? on-error)))))
|
||||||
|
|
||||||
|
(s/def ::login-event
|
||||||
|
(s/keys :req-un [::username ::password]))
|
||||||
|
|
||||||
(defn login
|
(defn login
|
||||||
[params]
|
[params]
|
||||||
|
{:pre [(us/valid? ::login-event params)]}
|
||||||
(map->Login params))
|
(map->Login params))
|
||||||
|
|
||||||
;; --- Logout
|
;; --- Logout
|
||||||
|
@ -80,40 +90,33 @@
|
||||||
|
|
||||||
;; --- Register
|
;; --- Register
|
||||||
|
|
||||||
(defrecord Register [data]
|
;; TODO: clean form on success
|
||||||
|
|
||||||
|
(defrecord Register [data on-error]
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [_ state stream]
|
(-apply-watch [_ state stream]
|
||||||
(letfn [(on-error [{payload :payload}]
|
(letfn [(handle-error [{payload :payload}]
|
||||||
(->> (:payload payload)
|
(on-error payload)
|
||||||
(udf/assign-errors :register)
|
(rx/empty))]
|
||||||
(rx/of)))]
|
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(->> (rp/req :auth/register data)
|
(->> (rp/req :auth/register data)
|
||||||
(rx/map :payload)
|
(rx/map :payload)
|
||||||
(rx/map (constantly ::registered))
|
(rx/map (constantly ::registered))
|
||||||
(rx/catch rp/client-error? on-error))
|
(rx/catch rp/client-error? handle-error))
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter #(= % ::registered))
|
(rx/filter #(= % ::registered))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map #(login data)))
|
(rx/map #(login data)))))))
|
||||||
(->> stream
|
|
||||||
(rx/filter logged-in?)
|
|
||||||
(rx/take 1)
|
|
||||||
(rx/map #(udf/clean :register)))))))
|
|
||||||
|
|
||||||
(def register-schema
|
(s/def ::register-event
|
||||||
{:username [us/required us/string]
|
(s/keys :req-un [::fullname ::username ::email ::password]))
|
||||||
:fullname [us/required us/string]
|
|
||||||
:email [us/required us/email]
|
|
||||||
:password [us/required us/string]})
|
|
||||||
|
|
||||||
(defn register
|
(defn register
|
||||||
"Create a register event instance."
|
"Create a register event instance."
|
||||||
[data]
|
[data on-error]
|
||||||
(let [[errors data] (us/validate data register-schema)]
|
{:pre [(us/valid? ::register-event data)
|
||||||
(if errors
|
(fn? on-error)]}
|
||||||
(udf/assign-errors :register errors)
|
(Register. data on-error))
|
||||||
(Register. data))))
|
|
||||||
|
|
||||||
;; --- Recovery Request
|
;; --- Recovery Request
|
||||||
|
|
||||||
|
@ -122,9 +125,7 @@
|
||||||
(-apply-watch [_ state stream]
|
(-apply-watch [_ state stream]
|
||||||
(letfn [(on-error [{payload :payload}]
|
(letfn [(on-error [{payload :payload}]
|
||||||
(println "on-error" payload)
|
(println "on-error" payload)
|
||||||
(->> (:payload payload)
|
(rx/empty))]
|
||||||
(udf/assign-errors :recovery-request)
|
|
||||||
(rx/of)))]
|
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(->> (rp/req :auth/recovery-request data)
|
(->> (rp/req :auth/recovery-request data)
|
||||||
(rx/map (constantly ::recovery-requested))
|
(rx/map (constantly ::recovery-requested))
|
||||||
|
@ -132,11 +133,14 @@
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter #(= % ::recovery-requested))
|
(rx/filter #(= % ::recovery-requested))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/do #(udm/info! (tr "auth.message.recovery-token-sent")))
|
(rx/do #(udm/info! (tr "auth.message.recovery-token-sent"))))))))
|
||||||
(rx/map #(udf/clean :recovery-request)))))))
|
|
||||||
|
(s/def ::recovery-request-event
|
||||||
|
(s/keys :req-un [::username]))
|
||||||
|
|
||||||
(defn recovery-request
|
(defn recovery-request
|
||||||
[data]
|
[data]
|
||||||
|
{:pre [(us/valid? ::recovery-request-event data)]}
|
||||||
(RecoveryRequest. data))
|
(RecoveryRequest. data))
|
||||||
|
|
||||||
;; --- Check Recovery Token
|
;; --- Check Recovery Token
|
||||||
|
@ -153,8 +157,9 @@
|
||||||
(rx/catch rp/client-error? on-error)))))
|
(rx/catch rp/client-error? on-error)))))
|
||||||
|
|
||||||
(defn validate-recovery-token
|
(defn validate-recovery-token
|
||||||
[data]
|
[token]
|
||||||
(ValidateRecoveryToken. data))
|
{:pre [(string? token)]}
|
||||||
|
(ValidateRecoveryToken. token))
|
||||||
|
|
||||||
;; --- Recovery (Password)
|
;; --- Recovery (Password)
|
||||||
|
|
||||||
|
@ -171,8 +176,10 @@
|
||||||
(rx/mapcat on-success)
|
(rx/mapcat on-success)
|
||||||
(rx/catch rp/client-error? on-error)))))
|
(rx/catch rp/client-error? on-error)))))
|
||||||
|
|
||||||
|
(s/def ::recovery-event
|
||||||
|
(s/keys :req-un [::username ::token]))
|
||||||
|
|
||||||
(defn recovery
|
(defn recovery
|
||||||
[{:keys [token password]}]
|
[{:keys [token password] :as data}]
|
||||||
|
{:pre [(us/valid? ::recovery-event data)]}
|
||||||
(Recovery. token password))
|
(Recovery. token password))
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.main.data.projects :as dp]
|
[uxbox.main.data.projects :as dp]
|
||||||
[uxbox.main.data.colors :as dc]
|
[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.util.router :as r]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]
|
||||||
[uxbox.util.datetime :as dt]
|
[uxbox.util.datetime :as dt]
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
(rs/emit! (fetch-page-history id)
|
(rs/emit! (fetch-page-history id)
|
||||||
(fetch-pinned-page-history id)))]
|
(fetch-pinned-page-history id)))]
|
||||||
(as-> rs/stream $
|
(as-> rs/stream $
|
||||||
(rx/filter udp/page-synced? $)
|
(rx/filter udp/page-persisted? $)
|
||||||
(rx/delay 500 $)
|
(rx/delay 500 $)
|
||||||
(rx/map (comp :id :page) $)
|
(rx/map (comp :id :page) $)
|
||||||
(rx/on-value $ on-value))))
|
(rx/on-value $ on-value))))
|
||||||
|
@ -137,7 +137,7 @@
|
||||||
|
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [_ state s]
|
(-apply-watch [_ state s]
|
||||||
(rx/of (udp/update-page id))))
|
(rx/of (udp/persist-page id))))
|
||||||
|
|
||||||
(defn apply-selected-history
|
(defn apply-selected-history
|
||||||
[id]
|
[id]
|
||||||
|
|
|
@ -3,21 +3,43 @@
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
;; 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 Andrey Antukh <niwi@niwi.nz>
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(ns uxbox.main.data.pages
|
(ns uxbox.main.data.pages
|
||||||
(:require [cuerdas.core :as str]
|
(:require [cljs.spec :as s]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[lentes.core :as l]
|
[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.rstore :as rs]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.main.repo :as rp]
|
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.main.state :as st]
|
|
||||||
[uxbox.util.datetime :as dt]
|
[uxbox.util.datetime :as dt]
|
||||||
[uxbox.util.data :refer (without-keys replace-by-id)]))
|
[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
|
;; --- Protocols
|
||||||
|
|
||||||
|
@ -99,75 +121,84 @@
|
||||||
[projectid]
|
[projectid]
|
||||||
(FetchPages. 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
|
;; --- Create Page
|
||||||
|
|
||||||
(defrecord CreatePage [name project metadata]
|
(defrecord CreatePage [name project width height layout]
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [this state s]
|
(-apply-watch [this state s]
|
||||||
(letfn [(on-created [{page :payload}]
|
|
||||||
(rx/of
|
|
||||||
#(assoc-page % page)
|
|
||||||
#(assoc-packed-page % page)))]
|
|
||||||
(let [params {:name name
|
(let [params {:name name
|
||||||
:project project
|
:project project
|
||||||
:data {}
|
:data {}
|
||||||
:metadata metadata}]
|
:metadata {:width width
|
||||||
|
:height height
|
||||||
|
:layout layout}}]
|
||||||
(->> (rp/req :create/page params)
|
(->> (rp/req :create/page params)
|
||||||
(rx/mapcat on-created))))))
|
(rx/map :payload)
|
||||||
|
(rx/map page-created)))))
|
||||||
|
|
||||||
(def ^:private create-page-schema
|
(s/def ::create-page-event
|
||||||
{:name [sc/required sc/string]
|
(s/keys :req-un [::name ::project ::width ::height ::layout]))
|
||||||
:metadata [sc/required]
|
|
||||||
:project [sc/required sc/uuid]})
|
|
||||||
|
|
||||||
(defn create-page
|
(defn create-page
|
||||||
[data]
|
[data]
|
||||||
(-> (sc/validate! data create-page-schema)
|
{:pre [(us/valid? ::create-page-event data)]}
|
||||||
(map->CreatePage)))
|
(map->CreatePage data))
|
||||||
|
|
||||||
;; --- Page Synced
|
;; --- Page Persisted
|
||||||
|
|
||||||
(defrecord PageSynced [page]
|
(defrecord PagePersisted [data]
|
||||||
rs/UpdateEvent
|
rs/UpdateEvent
|
||||||
(-apply-update [this state]
|
(-apply-update [_ state]
|
||||||
(-> state
|
(assoc-page state data)))
|
||||||
(assoc-in [:pages (:id page) :version] (:version page))
|
|
||||||
(assoc-page page))))
|
|
||||||
|
|
||||||
(defn- page-synced?
|
(defn- page-persisted?
|
||||||
[event]
|
[event]
|
||||||
(instance? PageSynced event))
|
(instance? PagePersisted event))
|
||||||
|
|
||||||
;; --- Sync Page
|
;; TODO: add specs
|
||||||
|
|
||||||
(defrecord SyncPage [id]
|
(defn page-persisted
|
||||||
rs/WatchEvent
|
[data]
|
||||||
(-apply-watch [this state s]
|
{:pre [(map? data)]}
|
||||||
(let [page (pack-page state id)]
|
(PagePersisted. data))
|
||||||
(->> (rp/req :update/page page)
|
|
||||||
(rx/map (comp ->PageSynced :payload))))))
|
|
||||||
|
|
||||||
(defn sync-page
|
;; --- Persist Page
|
||||||
[id]
|
|
||||||
(SyncPage. id))
|
|
||||||
|
|
||||||
;; --- Update Page
|
(defrecord PersistPage [id]
|
||||||
|
|
||||||
(defrecord UpdatePage [id]
|
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [this state s]
|
(-apply-watch [this state s]
|
||||||
(let [page (get-in state [:pages id])]
|
(let [page (get-in state [:pages id])]
|
||||||
(if (:history page)
|
(if (:history page)
|
||||||
(rx/empty)
|
(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]
|
[v]
|
||||||
(instance? UpdatePage v))
|
(instance? PersistPage v))
|
||||||
|
|
||||||
(defn update-page
|
(defn persist-page
|
||||||
[id]
|
[id]
|
||||||
(UpdatePage. id))
|
(PersistPage. id))
|
||||||
|
|
||||||
(defn watch-page-changes
|
(defn watch-page-changes
|
||||||
"A function that starts watching for `IPageUpdate`
|
"A function that starts watching for `IPageUpdate`
|
||||||
|
@ -182,59 +213,82 @@
|
||||||
(as-> rs/stream $
|
(as-> rs/stream $
|
||||||
(rx/filter #(satisfies? IPageUpdate %) $)
|
(rx/filter #(satisfies? IPageUpdate %) $)
|
||||||
(rx/debounce 1000 $)
|
(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
|
;; that does not sends the heavyweiht `:data` attribute
|
||||||
;; and only serves for update other page data.
|
;; and only serves for update other page data.
|
||||||
|
|
||||||
;; TODO: sync also with the pagedata-by-id index.
|
(defrecord PersistMetadata [id]
|
||||||
|
|
||||||
(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)))
|
|
||||||
|
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [this state s]
|
(-apply-watch [_ state stream]
|
||||||
(letfn [(on-success [{page :payload}]
|
(let [page (get-in state [:pages id])]
|
||||||
#(assoc-in % [:pages id :version] (:version page)))]
|
(->> (rp/req :update/page-metadata page)
|
||||||
(->> (rp/req :update/page-metadata (into {} this))
|
(rx/map :payload)
|
||||||
(rx/map on-success)))))
|
(rx/map metadata-persisted)))))
|
||||||
|
|
||||||
(def ^:private update-page-schema
|
(defn persist-metadata
|
||||||
{:id [sc/required]
|
[id]
|
||||||
:project [sc/required]
|
{:pre [(uuid? id)]}
|
||||||
:version [sc/required]
|
(PersistMetadata. id))
|
||||||
: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)))
|
|
||||||
|
|
||||||
;; --- Update Page Options
|
;; --- 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
|
rs/WatchEvent
|
||||||
(-apply-watch [this state s]
|
(-apply-watch [this state s]
|
||||||
(let [page (get-in state [:pages id])
|
(rx/of (persist-metadata id))))
|
||||||
page (assoc page :options options)]
|
|
||||||
(rx/of (map->UpdatePageMetadata page)))))
|
|
||||||
|
|
||||||
(defn update-page-options
|
(defn update-metadata
|
||||||
[id options]
|
[id metadata]
|
||||||
(UpdatePageOptions. id options))
|
{: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)
|
;; --- Delete Page (by id)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.main.data.pages :as udp]))
|
[uxbox.main.data.pages :as udp]))
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
(let [page {:id (:page-id project)
|
(let [page {:id (:page-id project)
|
||||||
:name (:page-name project)
|
:name (:page-name project)
|
||||||
:version (:page-version project)
|
:version (:page-version project)
|
||||||
|
:project (:id project)
|
||||||
:data (:page-data project)
|
:data (:page-data project)
|
||||||
:created-at (:page-created-at project)
|
:created-at (:page-created-at project)
|
||||||
:modified-at (:page-modified-at project)
|
:modified-at (:page-modified-at project)
|
||||||
|
@ -176,7 +177,6 @@
|
||||||
:width [sc/required sc/integer]
|
:width [sc/required sc/integer]
|
||||||
:height [sc/required sc/integer]
|
:height [sc/required sc/integer]
|
||||||
:layout [sc/required sc/string]})
|
:layout [sc/required sc/string]})
|
||||||
|
|
||||||
(defn create-project
|
(defn create-project
|
||||||
[params]
|
[params]
|
||||||
(-> (sc/validate! params create-project-schema)
|
(-> (sc/validate! params create-project-schema)
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
[uxbox.util.uuid :as uuid]
|
[uxbox.util.uuid :as uuid]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.util.workers :as uw]
|
[uxbox.util.workers :as uw]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.geom :as geom]
|
[uxbox.main.geom :as geom]
|
||||||
|
@ -594,6 +594,6 @@
|
||||||
|
|
||||||
(defn align-point
|
(defn align-point
|
||||||
[point]
|
[point]
|
||||||
(let [message {:cmd :grid/align :point point}]
|
(let [message {:cmd :grid-align :point point}]
|
||||||
(->> (uw/ask! worker message)
|
(->> (uw/ask! worker message)
|
||||||
(rx/map :point))))
|
(rx/map :point))))
|
||||||
|
|
|
@ -5,15 +5,20 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.data.users
|
(ns uxbox.main.data.users
|
||||||
(:require [beicon.core :as rx]
|
(:require [cljs.spec :as s]
|
||||||
[uxbox.main.repo :as rp]
|
[beicon.core :as rx]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.schema :as sc]
|
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[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]))
|
[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
|
;; --- Profile Fetched
|
||||||
|
|
||||||
(defrecord ProfileFetched [data]
|
(defrecord ProfileFetched [data]
|
||||||
|
@ -55,37 +60,31 @@
|
||||||
|
|
||||||
;; --- Update Profile
|
;; --- Update Profile
|
||||||
|
|
||||||
(defrecord UpdateProfile [data]
|
(defrecord UpdateProfile [data on-success on-error]
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [_ state s]
|
(-apply-watch [_ state s]
|
||||||
(letfn [(on-error [{payload :payload}]
|
(letfn [(handle-error [{payload :payload}]
|
||||||
(->> (:payload payload)
|
(on-error payload)
|
||||||
(udf/assign-errors :profile/main)
|
(rx/empty))]
|
||||||
(rx/of)))]
|
|
||||||
(->> (rp/req :update/profile data)
|
(->> (rp/req :update/profile data)
|
||||||
(rx/map :payload)
|
(rx/map :payload)
|
||||||
|
(rx/do on-success)
|
||||||
(rx/map profile-updated)
|
(rx/map profile-updated)
|
||||||
(rx/catch rp/client-error? on-error)))))
|
(rx/catch rp/client-error? handle-error)))))
|
||||||
|
|
||||||
(def update-profile-schema
|
(s/def ::update-profile-event
|
||||||
{:fullname [sc/required sc/string]
|
(s/keys :req-un [::fullname ::email ::username ::theme]))
|
||||||
:email [sc/required sc/email]
|
|
||||||
:username [sc/required sc/string]})
|
|
||||||
|
|
||||||
(defn update-profile
|
(defn update-profile
|
||||||
[data]
|
[data on-success on-error]
|
||||||
(let [[errors data] (sc/validate data update-profile-schema)]
|
{:pre [(us/valid? ::update-profile-event data)
|
||||||
(if errors
|
(fn? on-error)
|
||||||
(udf/assign-errors :profile/main errors)
|
(fn? on-success)]}
|
||||||
(UpdateProfile. data))))
|
(UpdateProfile. data on-success on-error))
|
||||||
|
|
||||||
;; --- Password Updated
|
;; --- Password Updated
|
||||||
|
|
||||||
(defrecord PasswordUpdated []
|
(defrecord PasswordUpdated []
|
||||||
rs/UpdateEvent
|
|
||||||
(-apply-update [_ state]
|
|
||||||
(assoc-in state [:forms :profile/password] {}))
|
|
||||||
|
|
||||||
rs/EffectEvent
|
rs/EffectEvent
|
||||||
(-apply-effect [_ state]
|
(-apply-effect [_ state]
|
||||||
(udm/info! (tr "settings.password-saved"))))
|
(udm/info! (tr "settings.password-saved"))))
|
||||||
|
@ -99,28 +98,22 @@
|
||||||
(defrecord UpdatePassword [data]
|
(defrecord UpdatePassword [data]
|
||||||
rs/WatchEvent
|
rs/WatchEvent
|
||||||
(-apply-watch [_ state s]
|
(-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)
|
(let [params {:old-password (:old-password data)
|
||||||
:password (:password-1 data)}]
|
:password (:password-1 data)}]
|
||||||
(->> (rp/req :update/profile-password params)
|
(->> (rp/req :update/profile-password params)
|
||||||
(rx/map password-updated)
|
(rx/map password-updated)))))
|
||||||
(rx/catch rp/client-error? on-error))))))
|
|
||||||
|
|
||||||
(def update-password-schema
|
(s/def ::password-1 string?)
|
||||||
[[:password-1 sc/required sc/string [sc/min-len 6]]
|
(s/def ::password-2 string?)
|
||||||
[:password-2 sc/required sc/string
|
(s/def ::old-password string?)
|
||||||
[sc/identical-to :password-1 :message "errors.form.password-not-match"]]
|
|
||||||
[:old-password sc/required sc/string]])
|
(s/def ::update-password-event
|
||||||
|
(s/keys :req-un [::password-1 ::password-2 ::old-password]))
|
||||||
|
|
||||||
(defn update-password
|
(defn update-password
|
||||||
[data]
|
[data]
|
||||||
(let [[errors data] (sc/validate data update-password-schema)]
|
{:pre [(us/valid? ::update-password-event data)]}
|
||||||
(if errors
|
(UpdatePassword. data))
|
||||||
(udf/assign-errors :profile/password errors)
|
|
||||||
(UpdatePassword. data))))
|
|
||||||
|
|
||||||
;; --- Update Photo
|
;; --- Update Photo
|
||||||
|
|
||||||
|
@ -132,7 +125,7 @@
|
||||||
(rx/map fetch-profile))))
|
(rx/map fetch-profile))))
|
||||||
|
|
||||||
(defn update-photo
|
(defn update-photo
|
||||||
([file]
|
([file] (update-photo file (constantly nil)))
|
||||||
(UpdatePhoto. file (constantly nil)))
|
|
||||||
([file done]
|
([file done]
|
||||||
|
{:pre [(us/file? file) (fn? done)]}
|
||||||
(UpdatePhoto. file done)))
|
(UpdatePhoto. file done)))
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.data.workspace
|
(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.util.uuid :as uuid]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.util.rstore :as rs]
|
[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.geom.point :as gpt]
|
||||||
[uxbox.util.workers :as uw]
|
[uxbox.util.workers :as uw]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]
|
||||||
|
@ -18,7 +20,6 @@
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.data.shapes-impl :as shimpl]
|
[uxbox.main.data.shapes-impl :as shimpl]
|
||||||
[uxbox.main.data.forms :as udf]
|
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.main.data.history :as udh]
|
[uxbox.main.data.history :as udh]
|
||||||
[uxbox.util.datetime :as dt]
|
[uxbox.util.datetime :as dt]
|
||||||
|
@ -210,44 +211,32 @@
|
||||||
(-apply-watch [_ state s]
|
(-apply-watch [_ state s]
|
||||||
(let [page (get-in state [:pages id])
|
(let [page (get-in state [:pages id])
|
||||||
opts (:options page)
|
opts (:options page)
|
||||||
message {:cmd :grid/init
|
message {:cmd :grid-init
|
||||||
:width c/viewport-width
|
:width c/viewport-width
|
||||||
:height c/viewport-height
|
:height c/viewport-height
|
||||||
:x-axis (:grid/x-axis opts c/grid-x-axis)
|
:x-axis (:grid-x-axis opts c/grid-x-axis)
|
||||||
:y-axis (:grid/y-axis opts c/grid-y-axis)}]
|
:y-axis (:grid-y-axis opts c/grid-y-axis)}]
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(->> (uw/send! worker message)
|
(->> (uw/send! worker message)
|
||||||
(rx/map #(activate-flag :grid/indexed)))
|
(rx/map #(activate-flag :grid-indexed)))
|
||||||
(when (:grid/alignment opts)
|
(when (:grid-alignment opts)
|
||||||
(rx/of (activate-flag :grid/alignment)))))))
|
(rx/of (activate-flag :grid-alignment)))))))
|
||||||
|
|
||||||
(defn initialize-alignment-index
|
(defn initialize-alignment-index
|
||||||
[id]
|
[id]
|
||||||
(InitializeAlignmentIndex. 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
|
rs/WatchEvent
|
||||||
(-apply-watch [_ state s]
|
(-apply-watch [_ state s]
|
||||||
(rx/of (udp/update-page-options id options)
|
(rx/of (udp/update-metadata id metadata)
|
||||||
(initialize-alignment-index id)
|
(initialize-alignment-index id))))
|
||||||
(udf/clean :workspace/settings)))
|
|
||||||
|
|
||||||
rs/EffectEvent
|
(defn update-metadata
|
||||||
(-apply-effect [_ state]
|
[id metadata]
|
||||||
(udl/close!)))
|
{:pre [(uuid? id) (us/valid? ::udp/metadata metadata)]}
|
||||||
|
(UpdateMetadata. id metadata))
|
||||||
(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))))
|
|
||||||
|
|
|
@ -11,38 +11,23 @@
|
||||||
[uxbox.main.repo.impl :refer (request send!)]
|
[uxbox.main.repo.impl :refer (request send!)]
|
||||||
[uxbox.util.transit :as t]))
|
[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
|
(defmethod request :fetch/pages
|
||||||
[type data]
|
[type data]
|
||||||
(let [params {:url (str url "/pages")
|
(let [params {:url (str url "/pages")
|
||||||
:method :get}]
|
:method :get}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :fetch/pages-by-project
|
(defmethod request :fetch/pages-by-project
|
||||||
[type {:keys [project] :as params}]
|
[type {:keys [project] :as params}]
|
||||||
(let [url (str url "/projects/" project "/pages")]
|
(let [url (str url "/projects/" project "/pages")]
|
||||||
(->> (send! {:method :get :url url})
|
(send! {:method :get :url url})))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :fetch/page-history
|
(defmethod request :fetch/page-history
|
||||||
[type {:keys [page] :as params}]
|
[type {:keys [page] :as params}]
|
||||||
(let [url (str url "/pages/" page "/history")
|
(let [url (str url "/pages/" page "/history")
|
||||||
query (select-keys params [:max :since :pinned])
|
query (select-keys params [:max :since :pinned])
|
||||||
params {:method :get :url url :query query}]
|
params {:method :get :url url :query query}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :delete/page
|
(defmethod request :delete/page
|
||||||
[_ id]
|
[_ id]
|
||||||
|
@ -51,41 +36,30 @@
|
||||||
:method :delete})))
|
:method :delete})))
|
||||||
|
|
||||||
(defmethod request :create/page
|
(defmethod request :create/page
|
||||||
[type {:keys [data metadata] :as body}]
|
[type body]
|
||||||
(let [body (assoc body
|
(let [params {:url (str url "/pages")
|
||||||
:data (t/encode data)
|
|
||||||
:metadata (t/encode metadata))
|
|
||||||
params {:url (str url "/pages")
|
|
||||||
:method :post
|
:method :post
|
||||||
:body body}]
|
:body body}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :update/page
|
(defmethod request :update/page
|
||||||
[type {:keys [id data metadata] :as body}]
|
[type {:keys [id] :as body}]
|
||||||
(let [body (assoc body
|
(let [params {:url (str url "/pages/" id)
|
||||||
:data (t/encode data)
|
|
||||||
:metadata (t/encode metadata))
|
|
||||||
params {:url (str url "/pages/" id)
|
|
||||||
:method :put
|
:method :put
|
||||||
:body body}]
|
:body body}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :update/page-history
|
(defmethod request :update/page-history
|
||||||
[type {:keys [id page] :as data}]
|
[type {:keys [id page] :as data}]
|
||||||
(let [params {:url (str url "/pages/" page "/history/" id)
|
(let [params {:url (str url "/pages/" page "/history/" id)
|
||||||
:method :put
|
:method :put
|
||||||
:body data}]
|
:body data}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :update/page-metadata
|
(defmethod request :update/page-metadata
|
||||||
[type {:keys [id metadata] :as body}]
|
[type {:keys [id metadata] :as body}]
|
||||||
(let [body (dissoc body :data)
|
(let [body (dissoc body :data)
|
||||||
body (assoc body :metadata (t/encode metadata))
|
|
||||||
params {:url (str url "/pages/" id "/metadata")
|
params {:url (str url "/pages/" id "/metadata")
|
||||||
:method :put
|
:method :put
|
||||||
:body body}]
|
:body body}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
|
@ -14,27 +14,15 @@
|
||||||
|
|
||||||
(defmethod request :fetch/projects
|
(defmethod request :fetch/projects
|
||||||
[type data]
|
[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
|
;; Obtain the list of projects and decode the embedded
|
||||||
;; page data in order to have it usable.
|
;; page data in order to have it usable.
|
||||||
(->> (send! {:url (str url "/projects")
|
(send! {:url (str url "/projects")
|
||||||
:method :get})
|
:method :get}))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :fetch/project-by-token
|
(defmethod request :fetch/project-by-token
|
||||||
[_ token]
|
[_ token]
|
||||||
(letfn [(decode-pages [response]
|
(send! {:url (str url "/projects-by-token/" token)
|
||||||
(let [pages (->> (get-in response [:payload :pages])
|
:method :get}))
|
||||||
(mapv pages/decode-page))]
|
|
||||||
(assoc-in response [:payload :pages] pages)))]
|
|
||||||
(->> (send! {:url (str url "/projects-by-token/" token)
|
|
||||||
:method :get})
|
|
||||||
(rx/map decode-pages))))
|
|
||||||
|
|
||||||
(defmethod request :create/project
|
(defmethod request :create/project
|
||||||
[_ data]
|
[_ data]
|
||||||
|
|
|
@ -11,27 +11,18 @@
|
||||||
[uxbox.main.repo.impl :refer (request send!)]
|
[uxbox.main.repo.impl :refer (request send!)]
|
||||||
[uxbox.util.transit :as t]))
|
[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
|
(defmethod request :fetch/profile
|
||||||
[type _]
|
[type _]
|
||||||
(let [url (str url "/profile/me")
|
(let [url (str url "/profile/me")
|
||||||
params {:method :get :url url}]
|
params {:method :get :url url}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :update/profile
|
(defmethod request :update/profile
|
||||||
[type {:keys [metadata] :as body}]
|
[type body]
|
||||||
(let [body (assoc body :metadata (t/encode metadata))
|
(let [params {:url (str url "/profile/me")
|
||||||
params {:url (str url "/profile/me")
|
|
||||||
:method :put
|
:method :put
|
||||||
:body body}]
|
:body body}]
|
||||||
(->> (send! params)
|
(send! params)))
|
||||||
(rx/map decode-payload))))
|
|
||||||
|
|
||||||
(defmethod request :update/profile-password
|
(defmethod request :update/profile-password
|
||||||
[type data]
|
[type data]
|
||||||
|
|
|
@ -138,6 +138,8 @@
|
||||||
|
|
||||||
(def routes
|
(def routes
|
||||||
[["/auth/login" :auth/login]
|
[["/auth/login" :auth/login]
|
||||||
|
["/auth/register" :auth/register]
|
||||||
|
["/auth/recovery/request" :auth/recovery-request]
|
||||||
["/auth/recovery/token/:token" :auth/recovery]
|
["/auth/recovery/token/:token" :auth/recovery]
|
||||||
["/settings/profile" :settings/profile]
|
["/settings/profile" :settings/profile]
|
||||||
["/settings/password" :settings/password]
|
["/settings/password" :settings/password]
|
||||||
|
@ -145,7 +147,6 @@
|
||||||
["/dashboard/projects" :dashboard/projects]
|
["/dashboard/projects" :dashboard/projects]
|
||||||
["/dashboard/elements" :dashboard/elements]
|
["/dashboard/elements" :dashboard/elements]
|
||||||
|
|
||||||
|
|
||||||
["/dashboard/icons" :dashboard/icons]
|
["/dashboard/icons" :dashboard/icons]
|
||||||
["/dashboard/icons/:type/:id" :dashboard/icons]
|
["/dashboard/icons/:type/:id" :dashboard/icons]
|
||||||
["/dashboard/icons/:type" :dashboard/icons]
|
["/dashboard/icons/:type" :dashboard/icons]
|
||||||
|
|
|
@ -5,74 +5,22 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.login
|
(ns uxbox.main.ui.auth.login
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[lentes.core :as l]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rum.core :as rum]
|
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.rstore :as rs]
|
[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.auth :as da]
|
||||||
[uxbox.main.data.messages :as udm]
|
[uxbox.main.data.messages :as udm]
|
||||||
[uxbox.util.dom :as dom]
|
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]))
|
||||||
[uxbox.util.mixins :as mx :include-macros true]))
|
|
||||||
|
|
||||||
(defn- login-submit
|
(def form-data (forms/focus-data :login st/state))
|
||||||
[event local]
|
(def set-value! (partial forms/set-value! :login))
|
||||||
(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?"]]]]]])))
|
|
||||||
|
|
||||||
(defn- login-page-will-mount
|
(defn- login-page-will-mount
|
||||||
[own]
|
[own]
|
||||||
|
@ -80,9 +28,54 @@
|
||||||
(rt/go :dashboard/projects))
|
(rt/go :dashboard/projects))
|
||||||
own)
|
own)
|
||||||
|
|
||||||
(def login-page
|
(def +login-form+
|
||||||
(mx/component
|
{:email [forms/required forms/string]
|
||||||
{:render #(login-page-render % (:rum/local %))
|
:password [forms/required forms/string]})
|
||||||
:will-mount login-page-will-mount
|
|
||||||
:name "login-page"
|
(mx/defc login-form
|
||||||
:mixins [(mx/local)]}))
|
{: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,65 +5,49 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.recovery
|
(ns uxbox.main.ui.auth.recovery
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[lentes.core :as l]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rum.core :as rum]
|
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]
|
||||||
[uxbox.util.rstore :as rs]
|
[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.auth :as uda]
|
||||||
[uxbox.main.data.messages :as udm]
|
[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.icons :as i]
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]))
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.util.dom :as dom]))
|
|
||||||
|
|
||||||
;; --- Constants
|
;; --- Recovery Form
|
||||||
|
|
||||||
(def form-data
|
(def form-data (forms/focus-data :recovery st/state))
|
||||||
(-> (l/in [:forms :recovery])
|
(def set-value! (partial forms/set-value! :recovery))
|
||||||
(l/derive st/state)))
|
|
||||||
|
|
||||||
(def form-errors
|
(def +recovery-form+
|
||||||
(-> (l/in [:errors :recovery])
|
{:password [forms/required forms/string]})
|
||||||
(l/derive st/state)))
|
|
||||||
|
|
||||||
(def set-value!
|
(mx/defc recovery-form
|
||||||
(partial udf/assign-field-value :recovery))
|
{:mixins [mx/static mx/reactive]}
|
||||||
|
[token]
|
||||||
;; --- Recovery Request Form
|
(let [data (merge (mx/react form-data)
|
||||||
|
{:token token})
|
||||||
(def schema
|
valid? (forms/valid? data +recovery-form+)]
|
||||||
{: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)]
|
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(rs/emit! (set-value! field value))))
|
(set-value! field value)))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(rs/emit! (uda/recovery (assoc form :token token))))]
|
(rs/emit! (uda/recovery data)
|
||||||
(html
|
(forms/clear :recovery)))]
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "password"
|
{:name "password"
|
||||||
:value (:password form "")
|
:value (:password data "")
|
||||||
:on-change (partial on-change :password)
|
:on-change (partial on-change :password)
|
||||||
:placeholder "Password"
|
:placeholder "Password"
|
||||||
:type "password"}]
|
:type "password"}]
|
||||||
(forms/input-error errors :password)
|
|
||||||
|
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:name "login"
|
{:name "login"
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
|
@ -71,15 +55,9 @@
|
||||||
:value "Recover password"
|
:value "Recover password"
|
||||||
:type "submit"}]
|
:type "submit"}]
|
||||||
[:div.login-links
|
[:div.login-links
|
||||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]]))))
|
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]])))
|
||||||
|
|
||||||
(def form
|
;; --- Recovery Page
|
||||||
(mx/component
|
|
||||||
{:render form-render
|
|
||||||
:name "form"
|
|
||||||
:mixins [mx/static mx/reactive]}))
|
|
||||||
|
|
||||||
;; --- Recovery Request Page
|
|
||||||
|
|
||||||
(defn- recovery-page-will-mount
|
(defn- recovery-page-will-mount
|
||||||
[own]
|
[own]
|
||||||
|
@ -87,18 +65,13 @@
|
||||||
(rs/emit! (uda/validate-recovery-token token))
|
(rs/emit! (uda/validate-recovery-token token))
|
||||||
own))
|
own))
|
||||||
|
|
||||||
(defn- recovery-page-render
|
(mx/defc recovery-page
|
||||||
[own token]
|
{:mixins [mx/static]
|
||||||
(html
|
:will-mount recovery-page-will-mount
|
||||||
|
:will-unmount (forms/cleaner-fn :recovery)}
|
||||||
|
[token]
|
||||||
[:div.login
|
[:div.login
|
||||||
[:div.login-body
|
[:div.login-body
|
||||||
(uum/messages)
|
(uum/messages)
|
||||||
[:a i/logo]
|
[:a i/logo]
|
||||||
(form token)]]))
|
(recovery-form token)]])
|
||||||
|
|
||||||
(def recovery-page
|
|
||||||
(mx/component
|
|
||||||
{:render recovery-page-render
|
|
||||||
:will-mount recovery-page-will-mount
|
|
||||||
:name "recovery-page"
|
|
||||||
:mixins [mx/static]}))
|
|
||||||
|
|
|
@ -5,64 +5,46 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.recovery-request
|
(ns uxbox.main.ui.auth.recovery-request
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[lentes.core :as l]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rum.core :as rum]
|
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.main.state :as st]
|
|
||||||
[uxbox.util.rstore :as rs]
|
[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.auth :as uda]
|
||||||
[uxbox.main.data.messages :as udm]
|
[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.icons :as i]
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]))
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.util.dom :as dom]))
|
|
||||||
|
|
||||||
;; --- 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
|
(def +recovery-request-form+
|
||||||
(-> (l/in [:forms :recovery-request])
|
{:username [forms/required forms/string]})
|
||||||
(l/derive st/state)))
|
|
||||||
|
|
||||||
(def form-errors
|
(mx/defc recovery-request-form
|
||||||
(-> (l/in [:errors :recovery-request])
|
{:mixins [mx/static mx/reactive]}
|
||||||
(l/derive st/state)))
|
[]
|
||||||
|
(let [data (mx/react form-data)
|
||||||
(def set-value!
|
valid? (forms/valid? data +recovery-request-form+)]
|
||||||
(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)]
|
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(rs/emit! (set-value! field value))))
|
(set-value! field value)))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(rs/emit! (uda/recovery-request form)))]
|
(rs/emit! (uda/recovery-request data)
|
||||||
(html
|
(forms/clear :recovery-request)))]
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "username"
|
{:name "username"
|
||||||
:value (:username form "")
|
:value (:username data "")
|
||||||
:on-change (partial on-change :username)
|
:on-change (partial on-change :username)
|
||||||
:placeholder "username or email address"
|
:placeholder "username or email address"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
(forms/input-error errors :username)
|
|
||||||
|
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:name "login"
|
{:name "login"
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
|
@ -70,27 +52,16 @@
|
||||||
:value "Recover password"
|
:value "Recover password"
|
||||||
:type "submit"}]
|
:type "submit"}]
|
||||||
[:div.login-links
|
[:div.login-links
|
||||||
[:a {:on-click #(rt/go :auth/login)} "Go back!"]]]]))))
|
[: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 Request Page
|
||||||
|
|
||||||
(defn- recovery-request-page-render
|
(mx/defc recovery-request-page
|
||||||
[own]
|
{:mixins [mx/static]
|
||||||
(html
|
:will-unmount (forms/cleaner-fn :recovery-request)}
|
||||||
|
[]
|
||||||
[:div.login
|
[:div.login
|
||||||
[:div.login-body
|
[:div.login-body
|
||||||
(uum/messages)
|
(uum/messages)
|
||||||
[:a i/logo]
|
[:a i/logo]
|
||||||
(form)]]))
|
(recovery-request-form)]])
|
||||||
|
|
||||||
(def recovery-request-page
|
|
||||||
(mx/component
|
|
||||||
{:render recovery-request-page-render
|
|
||||||
:name "recovery-request-page"
|
|
||||||
:mixins [mx/static]}))
|
|
||||||
|
|
|
@ -5,56 +5,56 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.register
|
(ns uxbox.main.ui.auth.register
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[lentes.core :as l]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rum.core :as rum]
|
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.main.state :as st]
|
|
||||||
[uxbox.util.rstore :as rs]
|
[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.auth :as uda]
|
||||||
[uxbox.main.data.messages :as udm]
|
[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.icons :as i]
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[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))
|
|
||||||
|
|
||||||
;; --- Register Form
|
;; --- Register Form
|
||||||
|
|
||||||
(defn- register-form-render
|
(def form-data (forms/focus-data :register st/state))
|
||||||
[own]
|
(def form-errors (forms/focus-errors :register st/state))
|
||||||
(let [form (mx/react form-data)
|
(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)
|
errors (mx/react form-errors)
|
||||||
valid? (us/valid? form uda/register-schema)]
|
valid? (forms/valid? data +register-form+)]
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value 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]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(rs/emit! (uda/register form)))]
|
(rs/emit! (uda/register data on-error)))]
|
||||||
(html
|
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "fullname"
|
{:name "fullname"
|
||||||
:value (:fullname form "")
|
:value (:fullname data "")
|
||||||
:on-change (partial on-change :fullname)
|
:on-change (partial on-change :fullname)
|
||||||
:placeholder "Full Name"
|
:placeholder "Full Name"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "username"
|
{:name "username"
|
||||||
:value (:username form "")
|
:value (:username data "")
|
||||||
:on-change (partial on-change :username)
|
:on-change (partial on-change :username)
|
||||||
:placeholder "Username"
|
:placeholder "Username"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "email"
|
{:name "email"
|
||||||
:ref "email"
|
:ref "email"
|
||||||
:value (:email form "")
|
:value (:email data "")
|
||||||
:on-change (partial on-change :email)
|
:on-change (partial on-change :email)
|
||||||
:placeholder "Email"
|
:placeholder "Email"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "password"
|
{:name "password"
|
||||||
:ref "password"
|
:ref "password"
|
||||||
:value (:password form "")
|
:value (:password data "")
|
||||||
:on-change (partial on-change :password)
|
:on-change (partial on-change :password)
|
||||||
:placeholder "Password"
|
:placeholder "Password"
|
||||||
:type "password"}]
|
:type "password"}]
|
||||||
|
@ -94,27 +94,15 @@
|
||||||
:type "submit"}]
|
:type "submit"}]
|
||||||
[:div.login-links
|
[:div.login-links
|
||||||
;; [:a {:on-click #(rt/go :auth/recover-password)} "Forgot your password?"]
|
;; [:a {:on-click #(rt/go :auth/recover-password)} "Forgot your password?"]
|
||||||
[:a {:on-click #(rt/go :auth/login)} "Already have an account?"]]]]))))
|
[: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]}))
|
|
||||||
|
|
||||||
;; --- Register Page
|
;; --- Register Page
|
||||||
|
|
||||||
(defn- register-page-render
|
(mx/defc register-page
|
||||||
|
{:mixins [mx/static]}
|
||||||
[own]
|
[own]
|
||||||
(html
|
|
||||||
[:div.login
|
[:div.login
|
||||||
[:div.login-body
|
[:div.login-body
|
||||||
(uum/messages)
|
(uum/messages)
|
||||||
[:a i/logo]
|
[:a i/logo]
|
||||||
(register-form)]]))
|
(register-form)]])
|
||||||
|
|
||||||
(def register-page
|
|
||||||
(mx/component
|
|
||||||
{:render register-page-render
|
|
||||||
:name "register-page"
|
|
||||||
:mixins [mx/static]}))
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
[rum.core :as rum]
|
[rum.core :as rum]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.math :as mth]
|
[uxbox.util.math :as mth]
|
||||||
[uxbox.util.data :as data]
|
[uxbox.util.data :as data]
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.ui.colorpicker :refer (colorpicker)]
|
[uxbox.main.ui.colorpicker :refer (colorpicker)]
|
||||||
[uxbox.main.ui.dashboard.header :refer (header)]
|
[uxbox.main.ui.dashboard.header :refer (header)]
|
||||||
[uxbox.main.ui.forms :as form]
|
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
[uxbox.main.ui.keyboard :as k]
|
[uxbox.main.ui.keyboard :as k]
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
|
@ -24,8 +23,7 @@
|
||||||
[uxbox.util.i18n :as t :refer (tr)]
|
[uxbox.util.i18n :as t :refer (tr)]
|
||||||
[uxbox.util.lens :as ul]
|
[uxbox.util.lens :as ul]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]))
|
||||||
[uxbox.util.schema :as sc]))
|
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.datetime :as dt]
|
[uxbox.util.datetime :as dt]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.util.lens :as ul]
|
[uxbox.util.lens :as ul]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.dom :as dom]))
|
[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,69 +6,64 @@
|
||||||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.password
|
(ns uxbox.main.ui.settings.password
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[rum.core :as rum]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[uxbox.util.schema :as sc]
|
|
||||||
[uxbox.main.state :as st]
|
|
||||||
[uxbox.util.i18n :as t :refer (tr)]
|
[uxbox.util.i18n :as t :refer (tr)]
|
||||||
[uxbox.util.router :as r]
|
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.util.forms :as forms]
|
||||||
[uxbox.main.data.forms :as udf]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.main.ui.icons :as i]
|
|
||||||
[uxbox.main.ui.forms :as forms]
|
|
||||||
[uxbox.main.ui.messages :as uum]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.main.ui.settings.header :refer (header)]
|
[uxbox.main.state :as st]
|
||||||
[uxbox.util.dom :as dom]))
|
[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
|
(def +password-form+
|
||||||
(-> (l/in [:forms :profile/password])
|
[[:password-1 forms/required forms/string [forms/min-len 6]]
|
||||||
(l/derive st/state)))
|
[: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
|
(mx/defc password-form
|
||||||
(-> (l/in [:errors :profile/password])
|
{:mixins [mx/reactive mx/static]}
|
||||||
(l/derive st/state)))
|
[]
|
||||||
|
(let [data (mx/react form-data)
|
||||||
(def assign-field-value
|
errors (mx/react form-errors)
|
||||||
(partial udf/assign-field-value :profile/password))
|
valid? (forms/valid? data +password-form+)]
|
||||||
|
(letfn [(on-change [field event]
|
||||||
(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]
|
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(rs/emit! (assign-field-value field value))))
|
(set-value! field value)))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(rs/emit! (udu/update-password form)))]
|
(println "on-submit" data)
|
||||||
(html
|
#_(rs/emit! (udu/update-password form)))]
|
||||||
|
(println "password-form" data)
|
||||||
[:form.password-form
|
[:form.password-form
|
||||||
[:span.user-settings-label "Change password"]
|
[:span.user-settings-label "Change password"]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :old-password)
|
:class (forms/error-class errors :old-password)
|
||||||
:value (:old-password form "")
|
:value (:old-password data "")
|
||||||
:on-change (partial on-field-change :old-password)
|
:on-change (partial on-change :old-password)
|
||||||
:placeholder "Old password"}]
|
:placeholder "Old password"}]
|
||||||
(forms/input-error errors :old-password)
|
(forms/input-error errors :old-password)
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :password-1)
|
:class (forms/error-class errors :password-1)
|
||||||
:value (:password-1 form "")
|
:value (:password-1 data "")
|
||||||
:on-change (partial on-field-change :password-1)
|
:on-change (partial on-change :password-1)
|
||||||
:placeholder "New password"}]
|
:placeholder "New password"}]
|
||||||
(forms/input-error errors :password-1)
|
(forms/input-error errors :password-1)
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :password-2)
|
:class (forms/error-class errors :password-2)
|
||||||
:value (:password-2 form "")
|
:value (:password-2 data "")
|
||||||
:on-change (partial on-field-change :password-2)
|
:on-change (partial on-change :password-2)
|
||||||
:placeholder "Confirm password"}]
|
:placeholder "Confirm password"}]
|
||||||
(forms/input-error errors :password-2)
|
(forms/input-error errors :password-2)
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
|
@ -76,28 +71,16 @@
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
:disabled (not valid?)
|
:disabled (not valid?)
|
||||||
:on-click on-submit
|
:on-click on-submit
|
||||||
:value "Update settings"}]]))))
|
:value "Update settings"}]])))
|
||||||
|
|
||||||
(def password-form
|
|
||||||
(mx/component
|
|
||||||
{:render password-form-render
|
|
||||||
:name "password-form"
|
|
||||||
:mixins [mx/static (mx/local) mx/reactive]}))
|
|
||||||
|
|
||||||
;; --- Password Page
|
;; --- Password Page
|
||||||
|
|
||||||
(defn password-page-render
|
(mx/defc password-page
|
||||||
[own]
|
{:mixins [mx/static]}
|
||||||
(html
|
[]
|
||||||
[:main.dashboard-main
|
[:main.dashboard-main
|
||||||
(header)
|
(header)
|
||||||
(uum/messages)
|
(uum/messages)
|
||||||
[:section.dashboard-content.user-settings
|
[:section.dashboard-content.user-settings
|
||||||
[:section.user-settings-content
|
[:section.user-settings-content
|
||||||
(password-form)]]]))
|
(password-form)]]])
|
||||||
|
|
||||||
(def password-page
|
|
||||||
(mx/component
|
|
||||||
{:render password-page-render
|
|
||||||
:name "password-page"
|
|
||||||
:mixins [mx/static]}))
|
|
||||||
|
|
|
@ -6,100 +6,102 @@
|
||||||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.profile
|
(ns uxbox.main.ui.settings.profile
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [cuerdas.core :as str]
|
||||||
[rum.core :as rum]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as forms]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.main.state :as st]
|
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.main.ui.icons :as i]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[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.settings.header :refer (header)]
|
||||||
[uxbox.main.ui.messages :as uum]
|
[uxbox.main.ui.messages :as uum]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.main.data.users :as udu]))
|
||||||
[uxbox.main.data.forms :as udf]
|
|
||||||
[uxbox.util.interop :refer (iterable->seq)]
|
|
||||||
[uxbox.util.dom :as dom]))
|
|
||||||
|
|
||||||
;; --- 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
|
(def profile-ref
|
||||||
(-> (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
|
|
||||||
(-> (l/key :profile)
|
(-> (l/key :profile)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
|
(def +profile-form+
|
||||||
|
{:fullname [forms/required forms/string]
|
||||||
|
:email [forms/required forms/email]
|
||||||
|
:username [forms/required forms/string]})
|
||||||
|
|
||||||
;; --- Profile Form
|
;; --- Profile Form
|
||||||
|
|
||||||
(defn profile-form-render
|
(mx/defc profile-form
|
||||||
[own]
|
{:mixins [mx/static mx/reactive]
|
||||||
(let [form (merge (mx/react profile-ref)
|
:will-unmount (forms/cleaner-fn :profile)}
|
||||||
(mx/react formdata))
|
[]
|
||||||
errors (mx/react formerrors)
|
;; TODO: properly persist theme
|
||||||
valid? (sc/valid? form udu/update-profile-schema)
|
(let [data (merge {:theme "light"}
|
||||||
theme (get-in form [:metadata :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]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value 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]
|
(on-submit [event]
|
||||||
(rs/emit! (udu/update-profile form)))]
|
(rs/emit! (udu/update-profile data on-success on-error)))]
|
||||||
(html
|
|
||||||
[:form.profile-form
|
[:form.profile-form
|
||||||
[:span.user-settings-label "Name, username and email"]
|
[:span.user-settings-label "Name, username and email"]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "text"
|
{:type "text"
|
||||||
:on-change (partial on-change :fullname)
|
:on-change (partial on-change :fullname)
|
||||||
:value (:fullname form "")
|
:value (:fullname data "")
|
||||||
:placeholder "Your name"}]
|
:placeholder "Your name"}]
|
||||||
(forms/input-error errors :fullname)
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "text"
|
{:type "text"
|
||||||
:on-change (partial on-change :username)
|
:on-change (partial on-change :username)
|
||||||
:value (:username form "")
|
:value (:username data "")
|
||||||
:placeholder "Your username"}]
|
:placeholder "Your username"}]
|
||||||
(forms/input-error errors :username)
|
(forms/input-error errors :username)
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "email"
|
{:type "email"
|
||||||
:on-change (partial on-change :email)
|
:on-change (partial on-change :email)
|
||||||
:value (:email form "")
|
:value (:email data "")
|
||||||
:placeholder "Your email"}]
|
:placeholder "Your email"}]
|
||||||
(forms/input-error errors :email)
|
(forms/input-error errors :email)
|
||||||
|
|
||||||
[:span.user-settings-label "Choose a color theme"]
|
[:span.user-settings-label "Choose a color theme"]
|
||||||
[:div.input-radio.radio-primary
|
[:div.input-radio.radio-primary
|
||||||
[:input {:type "radio"
|
[:input {:type "radio"
|
||||||
:checked (= theme "light")
|
:checked (when (= theme "light") "checked")
|
||||||
:on-change (partial on-change [:metadata :theme])
|
:on-change (partial on-change :theme)
|
||||||
:id "light-theme"
|
:id "light-theme"
|
||||||
:name "theme"
|
:name "theme"
|
||||||
:value "light"}]
|
:value "light"}]
|
||||||
[:label {:for "light-theme"} "Light theme"]
|
[:label {:for "light-theme"} "Light theme"]
|
||||||
|
|
||||||
[:input {:type "radio"
|
[:input {:type "radio"
|
||||||
:checked (= theme "dark")
|
:checked (when (= theme "dark") "checked")
|
||||||
:on-change (partial on-change [:metadata :theme])
|
:on-change (partial on-change :theme)
|
||||||
:id "dark-theme"
|
:id "dark-theme"
|
||||||
:name "theme"
|
:name "theme"
|
||||||
:value "dark"}]
|
:value "dark"}]
|
||||||
[:label {:for "dark-theme"} "Dark theme"]
|
[:label {:for "dark-theme"} "Dark theme"]
|
||||||
|
|
||||||
[:input {:type "radio"
|
[:input {:type "radio"
|
||||||
:checked (= theme "high-contrast")
|
:checked (when (= theme "high-contrast") "checked")
|
||||||
:on-change (partial on-change [:metadata :theme])
|
:on-change (partial on-change :theme)
|
||||||
:id "high-contrast-theme"
|
:id "high-contrast-theme"
|
||||||
:name "theme"
|
:name "theme"
|
||||||
:value "high-contrast"}]
|
:value "high-contrast"}]
|
||||||
|
@ -110,18 +112,13 @@
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
:disabled (not valid?)
|
:disabled (not valid?)
|
||||||
:on-click on-submit
|
:on-click on-submit
|
||||||
:value "Update settings"}]]))))
|
:value "Update settings"}]])))
|
||||||
|
|
||||||
(def profile-form
|
|
||||||
(mx/component
|
|
||||||
{:render profile-form-render
|
|
||||||
:name "profile-form"
|
|
||||||
:mixins [(mx/local) mx/reactive mx/static]}))
|
|
||||||
|
|
||||||
;; --- Profile Photo Form
|
;; --- Profile Photo Form
|
||||||
|
|
||||||
(defn- profile-photo-form-render
|
(mx/defc profile-photo-form
|
||||||
[own]
|
{:mixins [mx/static mx/reactive]}
|
||||||
|
[]
|
||||||
(letfn [(on-change [event]
|
(letfn [(on-change [event]
|
||||||
(let [target (dom/get-target event)
|
(let [target (dom/get-target event)
|
||||||
file (-> (dom/get-files target)
|
file (-> (dom/get-files target)
|
||||||
|
@ -133,24 +130,17 @@
|
||||||
photo (if (or (str/empty? photo) (nil? photo))
|
photo (if (or (str/empty? photo) (nil? photo))
|
||||||
"images/avatar.jpg"
|
"images/avatar.jpg"
|
||||||
photo)]
|
photo)]
|
||||||
(html
|
|
||||||
[:form.avatar-form
|
[:form.avatar-form
|
||||||
[:img {:src photo :border "0"}]
|
[:img {:src photo}]
|
||||||
[:input {:type "file"
|
[:input {:type "file"
|
||||||
:value ""
|
:value ""
|
||||||
:on-change on-change}]]))))
|
:on-change on-change}]])))
|
||||||
|
|
||||||
(def profile-photo-form
|
|
||||||
(mx/component
|
|
||||||
{:render profile-photo-form-render
|
|
||||||
:name profile-photo-form
|
|
||||||
:mixins [mx/static mx/reactive]}))
|
|
||||||
|
|
||||||
;; --- Profile Page
|
;; --- Profile Page
|
||||||
|
|
||||||
(defn profile-page-render
|
(mx/defc profile-page
|
||||||
[own]
|
{:mixins [mx/static]}
|
||||||
(html
|
[]
|
||||||
[:main.dashboard-main
|
[:main.dashboard-main
|
||||||
(header)
|
(header)
|
||||||
(uum/messages)
|
(uum/messages)
|
||||||
|
@ -158,11 +148,4 @@
|
||||||
[:section.user-settings-content
|
[:section.user-settings-content
|
||||||
[:span.user-settings-label "Your avatar"]
|
[:span.user-settings-label "Your avatar"]
|
||||||
(profile-photo-form)
|
(profile-photo-form)
|
||||||
(profile-form)
|
(profile-form)]]])
|
||||||
]]]))
|
|
||||||
|
|
||||||
(def profile-page
|
|
||||||
(mx/component
|
|
||||||
{:render profile-page-render
|
|
||||||
:name "profile-page"
|
|
||||||
:mixins [mx/static]}))
|
|
||||||
|
|
|
@ -61,8 +61,8 @@
|
||||||
|
|
||||||
(def alignment-ref
|
(def alignment-ref
|
||||||
(letfn [(getter [flags]
|
(letfn [(getter [flags]
|
||||||
(and (contains? flags :grid/indexed)
|
(and (contains? flags :grid-indexed)
|
||||||
(contains? flags :grid/alignment)
|
(contains? flags :grid-alignment)
|
||||||
(contains? flags :grid)))]
|
(contains? flags :grid)))]
|
||||||
(-> (l/lens getter)
|
(-> (l/lens getter)
|
||||||
(l/derive flags-ref))))
|
(l/derive flags-ref))))
|
||||||
|
|
|
@ -21,16 +21,16 @@
|
||||||
(defn- grid-render
|
(defn- grid-render
|
||||||
[own]
|
[own]
|
||||||
(let [options (:options (mx/react wb/page-ref))
|
(let [options (:options (mx/react wb/page-ref))
|
||||||
color (:grid/color options "#cccccc")
|
color (:grid-color options "#cccccc")
|
||||||
width c/viewport-width
|
width c/viewport-width
|
||||||
height c/viewport-height
|
height c/viewport-height
|
||||||
x-ticks (range (- 0 c/canvas-start-x)
|
x-ticks (range (- 0 c/canvas-start-x)
|
||||||
(- width 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)
|
y-ticks (range (- 0 c/canvas-start-x)
|
||||||
(- height c/canvas-start-x)
|
(- height c/canvas-start-x)
|
||||||
(:grid/y-axis options 10))
|
(:grid-y-axis options 10))
|
||||||
|
|
||||||
path (as-> [] $
|
path (as-> [] $
|
||||||
(reduce (partial vertical-line height) $ x-ticks)
|
(reduce (partial vertical-line height) $ x-ticks)
|
||||||
|
|
|
@ -6,60 +6,68 @@
|
||||||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.workspace.settings
|
(ns uxbox.main.ui.workspace.settings
|
||||||
(:require [sablono.core :as html :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[lentes.core :as l]
|
|
||||||
[rum.core :as rum]
|
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.data.forms :as udf]
|
|
||||||
[uxbox.main.data.workspace :as udw]
|
[uxbox.main.data.workspace :as udw]
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
|
[uxbox.util.forms :as forms]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.main.ui.forms :as forms]
|
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
[uxbox.main.ui.colorpicker :as uucp]
|
[uxbox.main.ui.colorpicker :as uucp]
|
||||||
[uxbox.main.ui.workspace.base :as wb]
|
[uxbox.main.ui.workspace.base :as wb]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.data :refer (parse-int)]))
|
[uxbox.util.data :refer (parse-int)]))
|
||||||
|
|
||||||
;; --- Lentes
|
(def form-data (forms/focus-data :workspace-settings st/state))
|
||||||
|
(def form-errors (forms/focus-errors :workspace-settings st/state))
|
||||||
(def formdata (udf/focus-form-data :workspace/settings))
|
(def set-value! (partial forms/set-value! :workspace-settings))
|
||||||
(def formerrors (udf/focus-form-errors :workspace/settings))
|
(def set-errors! (partial forms/set-errors! :workspace-settings))
|
||||||
(def assign-field-value (partial udf/assign-field-value :workspace/settings))
|
(def page-ref wb/page-ref)
|
||||||
|
|
||||||
;; --- Form Component
|
;; --- Form Component
|
||||||
|
|
||||||
(def settings-form-defaults
|
(def +settings-defaults+
|
||||||
{:grid/x-axis c/grid-x-axis
|
{:grid-x-axis c/grid-x-axis
|
||||||
:grid/y-axis c/grid-y-axis
|
:grid-y-axis c/grid-y-axis
|
||||||
:grid/color "#b5bdb9"
|
:grid-color "#b5bdb9"
|
||||||
:grid/alignment false})
|
:grid-alignment false})
|
||||||
|
|
||||||
(defn- settings-form-render
|
(def +settings-form+
|
||||||
[own]
|
{:grid-y-axis [forms/required forms/integer [forms/in-range 2 100]]
|
||||||
(let [page (mx/react wb/page-ref)
|
:grid-x-axis [forms/required forms/integer [forms/in-range 2 100]]
|
||||||
form (merge settings-form-defaults
|
:grid-alignment [forms/boolean]
|
||||||
(:options page)
|
:grid-color [forms/required forms/color]})
|
||||||
(mx/react formdata))
|
|
||||||
errors (mx/react formerrors)]
|
(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]
|
(letfn [(on-field-change [field event]
|
||||||
(let [value (dom/event->value event)
|
(let [value (dom/event->value event)
|
||||||
value (parse-int value "")]
|
value (parse-int value "")]
|
||||||
(rs/emit! (assign-field-value field value))))
|
(set-value! field value)))
|
||||||
(on-color-change [color]
|
(on-color-change [color]
|
||||||
(rs/emit! (assign-field-value :grid/color color)))
|
(set-value! :grid-color color))
|
||||||
(on-align-change [event]
|
(on-align-change [event]
|
||||||
(let [checked? (-> (dom/get-target event)
|
(let [checked? (-> (dom/get-target event)
|
||||||
(dom/checked?))]
|
(dom/checked?))]
|
||||||
(rs/emit! (assign-field-value :grid/alignment checked?))))
|
(set-value! :grid-alignment checked?)))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(rs/emit! (udw/submit-workspace-settings (:id page) form)))]
|
(let [[errors data] (forms/validate data +settings-form+)]
|
||||||
(html
|
(if errors
|
||||||
|
(set-errors! errors)
|
||||||
|
(rs/emit! (udw/update-metadata id data)
|
||||||
|
(forms/clear :workspace-settings)
|
||||||
|
(udl/hide-lightbox)))))]
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:span.lightbox-label "Grid size"]
|
[:span.lightbox-label "Grid size"]
|
||||||
[:div.project-size
|
[:div.project-size
|
||||||
|
@ -67,58 +75,45 @@
|
||||||
[:input#grid-x.input-text
|
[:input#grid-x.input-text
|
||||||
{:placeholder "X"
|
{:placeholder "X"
|
||||||
:type "number"
|
:type "number"
|
||||||
:class (forms/error-class errors :grid/x-axis)
|
:class (forms/error-class errors :grid-x-axis)
|
||||||
:value (:grid/x-axis form "")
|
:value (:grid-x-axis data "")
|
||||||
:on-change (partial on-field-change :grid/x-axis)
|
:on-change (partial on-field-change :grid-x-axis)
|
||||||
:min 1
|
:min 2
|
||||||
:max 100}]]
|
:max 100}]]
|
||||||
[:div.input-element.pixels
|
[:div.input-element.pixels
|
||||||
[:input#grid-y.input-text
|
[:input#grid-y.input-text
|
||||||
{:placeholder "Y"
|
{:placeholder "Y"
|
||||||
:type "number"
|
:type "number"
|
||||||
:class (forms/error-class errors :grid/y-axis)
|
:class (forms/error-class errors :grid-y-axis)
|
||||||
:value (:grid/y-axis form "")
|
:value (:grid-y-axis data "")
|
||||||
:on-change (partial on-field-change :grid/y-axis)
|
:on-change (partial on-field-change :grid-y-axis)
|
||||||
:min 1
|
:min 2
|
||||||
:max 100}]]]
|
:max 100}]]]
|
||||||
[:span.lightbox-label "Grid color"]
|
[:span.lightbox-label "Grid color"]
|
||||||
(uucp/colorpicker
|
(uucp/colorpicker
|
||||||
:value (:grid/color form)
|
:value (:grid-color data)
|
||||||
:on-change on-color-change)
|
:on-change on-color-change)
|
||||||
[:span.lightbox-label "Grid magnet option"]
|
[:span.lightbox-label "Grid magnet option"]
|
||||||
[:div.input-checkbox.check-primary
|
[:div.input-checkbox.check-primary
|
||||||
[:input
|
[:input
|
||||||
{:type "checkbox"
|
{:type "checkbox"
|
||||||
:on-change on-align-change
|
:on-change on-align-change
|
||||||
:checked (:grid/alignment form)
|
:checked (:grid-alignment data)
|
||||||
:id "magnet"
|
:id "magnet"
|
||||||
:value "Yes"}]
|
:value "Yes"}]
|
||||||
[:label {:for "magnet"} "Activate magnet"]]
|
[:label {:for "magnet"} "Activate magnet"]]
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:type "submit"
|
{:type "submit"
|
||||||
:value "Save"}]]))))
|
:value "Save"}]])))
|
||||||
|
|
||||||
(def settings-form
|
(mx/defc settings-dialog
|
||||||
(mx/component
|
|
||||||
{:render settings-form-render
|
|
||||||
:name "settings-form"
|
|
||||||
:mixins [(mx/local) mx/reactive mx/static]}))
|
|
||||||
|
|
||||||
(defn- settings-dialog-render
|
|
||||||
[own]
|
[own]
|
||||||
(html
|
|
||||||
[:div.lightbox-body.settings
|
[:div.lightbox-body.settings
|
||||||
[:h3 "Grid settings"]
|
[:h3 "Grid settings"]
|
||||||
(settings-form)
|
(settings-form)
|
||||||
[:a.close {:href "#"
|
[:a.close {:href "#"
|
||||||
:on-click #(do (dom/prevent-default %)
|
:on-click #(do (dom/prevent-default %)
|
||||||
(udl/close!))} i/close]]))
|
(udl/close!))} i/close]])
|
||||||
|
|
||||||
(def settings-dialog
|
|
||||||
(mx/component
|
|
||||||
{:render settings-dialog-render
|
|
||||||
:name "settings-dialog"
|
|
||||||
:mixins []}))
|
|
||||||
|
|
||||||
(defmethod lbx/render-lightbox :settings
|
(defmethod lbx/render-lightbox :settings
|
||||||
[_]
|
[_]
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.data.workspace :as dw]
|
[uxbox.main.data.workspace :as dw]
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.main.ui.dashboard.projects :refer (+layouts+)]
|
|
||||||
[uxbox.main.ui.workspace.base :as wb]
|
[uxbox.main.ui.workspace.base :as wb]
|
||||||
|
[uxbox.main.ui.workspace.sidebar.sitemap-pageform]
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
[uxbox.util.data :refer (deep-merge parse-int)]
|
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
@ -87,103 +86,3 @@
|
||||||
:let [active? (= (:id page) (:id current))]]
|
:let [active? (= (:id page) (:id current))]]
|
||||||
(-> (page-item page (count pages) active?)
|
(-> (page-item page (count pages) active?)
|
||||||
(mx/with-key (:id page))))]]]))
|
(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]
|
(defrecord Navigate [id params]
|
||||||
rs/EffectEvent
|
rs/EffectEvent
|
||||||
(-apply-effect [_ state]
|
(-apply-effect [_ state]
|
||||||
(let [loc (merge {:handler id}
|
(r/navigate! +router+ id {})))
|
||||||
(when params
|
|
||||||
{:route-params params}))]
|
|
||||||
(r/navigate! +router+ id params))))
|
|
||||||
|
|
||||||
(defn navigate
|
(defn navigate
|
||||||
([id] (navigate id nil))
|
([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]
|
(:require [beicon.core :as rx]
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.util.schema :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
[uxbox.util.data :refer (parse-int)]
|
[uxbox.util.data :refer (parse-int)]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.main.data.pages :as udpg]
|
[uxbox.main.data.pages :as udpg]
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
|
|
||||||
(defonce tree (kd/create))
|
(defonce tree (kd/create))
|
||||||
|
|
||||||
(defmethod impl/handler :grid/init
|
(defmethod impl/handler :grid-init
|
||||||
[{:keys [sender width height x-axis y-axis] :as opts}]
|
[{:keys [sender width height x-axis y-axis] :as opts}]
|
||||||
(time
|
(time
|
||||||
(kd/setup! tree width height (or x-axis 10) (or y-axis 10)))
|
(kd/setup! tree width height (or x-axis 10) (or y-axis 10)))
|
||||||
(impl/reply! sender nil))
|
(impl/reply! sender nil))
|
||||||
|
|
||||||
(defmethod impl/handler :grid/align
|
(defmethod impl/handler :grid-align
|
||||||
[{:keys [sender point] :as message}]
|
[{:keys [sender point] :as message}]
|
||||||
(let [point [(:x point) (:y point)]
|
(let [point [(:x point) (:y point)]
|
||||||
results (kd/nearest tree point 1)
|
results (kd/nearest tree point 1)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue