From f7013d6e8b3e87b0134e7e95d5a73ad8fbfd5670 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 2 Apr 2016 11:17:51 +0300 Subject: [PATCH] Update profile form integration. --- src/uxbox/data/auth.cljs | 32 +----- src/uxbox/data/users.cljs | 78 ++++++++++++++ src/uxbox/repo/auth.cljs | 7 ++ src/uxbox/ui.cljs | 4 +- src/uxbox/ui/settings.cljs | 112 ++------------------ src/uxbox/ui/settings/notifications.cljs | 49 +++++++++ src/uxbox/ui/settings/password.cljs | 86 +++++++++++++++ src/uxbox/ui/settings/profile.cljs | 127 +++++++++++++++++++++++ src/uxbox/util/data.cljs | 8 ++ 9 files changed, 365 insertions(+), 138 deletions(-) create mode 100644 src/uxbox/data/users.cljs create mode 100644 src/uxbox/ui/settings/notifications.cljs create mode 100644 src/uxbox/ui/settings/password.cljs create mode 100644 src/uxbox/ui/settings/profile.cljs diff --git a/src/uxbox/data/auth.cljs b/src/uxbox/data/auth.cljs index 5e942c46c6..327b52d98b 100644 --- a/src/uxbox/data/auth.cljs +++ b/src/uxbox/data/auth.cljs @@ -16,36 +16,9 @@ [uxbox.schema :as sc] [uxbox.locales :refer (tr)] [uxbox.data.projects :as dp] + [uxbox.data.users :as udu] [uxbox.ui.messages :as uum])) -;; --- Profile Fetched - -(defrecord ProfileFetched [data] - rs/UpdateEvent - (-apply-update [this state] - (assoc state :profile data))) - -(defn profile-fetched - [data] - (ProfileFetched. data)) - -;; --- Fetch Profile - -(defrecord FetchProfile [] - rs/WatchEvent - (-apply-watch [_ state s] - (letfn [(on-error [err] - (uum/error (tr "errors.profile-fetch")) - (rx/empty))] - (->> (rp/do :fetch/profile) - (rx/catch on-error) - (rx/map :payload) - (rx/map profile-fetched))))) - -(defn fetch-profile - [] - (FetchProfile.)) - ;; --- Logged In (defrecord LoggedIn [data] @@ -85,8 +58,7 @@ (rx/map :payload) (rx/mapcat #(rx/of (logged-in %) (dp/fetch-projects) - (fetch-profile)))))))) - + (udu/fetch-profile)))))))) (def ^:const ^:private +login-schema+ {:username [sc/required sc/string] diff --git a/src/uxbox/data/users.cljs b/src/uxbox/data/users.cljs new file mode 100644 index 0000000000..c14f02374f --- /dev/null +++ b/src/uxbox/data/users.cljs @@ -0,0 +1,78 @@ +;; 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 + +(ns uxbox.data.users + (:require [beicon.core :as rx] + [promesa.core :as p] + [uxbox.repo :as rp] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.schema :as sc] + [uxbox.locales :refer (tr)] + [uxbox.ui.messages :as uum])) + +;; --- Profile Fetched + +(defrecord ProfileFetched [data] + rs/UpdateEvent + (-apply-update [this state] + (assoc state :profile data))) + +(defn profile-fetched + [data] + (ProfileFetched. data)) + +;; --- Fetch Profile + +(defrecord FetchProfile [] + rs/WatchEvent + (-apply-watch [_ state s] + (letfn [(on-error [err] + (uum/error (tr "errors.profile-fetch")) + (rx/empty))] + (->> (rp/do :fetch/profile) + (rx/catch on-error) + (rx/map :payload) + (rx/map profile-fetched))))) + +(defn fetch-profile + [] + (FetchProfile.)) + +;; --- Update Profile + +(defrecord UpdateProfile [data] + rs/WatchEvent + (-apply-watch [_ state s] + (letfn [(on-error [err] + (uum/error (tr "errors.update-profile")) + (rx/empty))] + (->> (rp/do :update/profile data) + (rx/catch on-error) + (rx/map :payload) + (rx/map profile-fetched))))) + +(defn update-profile + [data] + (UpdateProfile. data)) + +;; --- Update Password + +(defrecord UpdatePassword [password] + rs/WatchEvent + (-apply-watch [_ state s] + ;; (letfn [(on-error [err] + ;; (uum/error (tr "errors.update-password")) + ;; (rx/empty))] + ;; (->> (rp/do :update/password data) + ;; (rx/catch on-error))))) + (js/alert "Not implemented") + (rx/empty))) + +(defn update-password + [password] + {:pre [(string? password)]} + (UpdatePassword. password)) diff --git a/src/uxbox/repo/auth.cljs b/src/uxbox/repo/auth.cljs index 068d54bd20..8a4f6027fe 100644 --- a/src/uxbox/repo/auth.cljs +++ b/src/uxbox/repo/auth.cljs @@ -23,3 +23,10 @@ :method :post :auth false :body data}))) + +(defmethod -do :update/profile + [type data] + (let [params {:url (str url "/profile/me") + :method :put + :body data}] + (send! params))) diff --git a/src/uxbox/ui.cljs b/src/uxbox/ui.cljs index 8b863031ee..54b94b6405 100644 --- a/src/uxbox/ui.cljs +++ b/src/uxbox/ui.cljs @@ -15,7 +15,7 @@ [uxbox.router :as r] [uxbox.rstore :as rs] [uxbox.data.projects :as dp] - [uxbox.data.auth :as uda] + [uxbox.data.users :as udu] [uxbox.ui.lightbox :as ui-lightbox] [uxbox.ui.auth :as ui-auth] [uxbox.ui.dashboard :as ui-dashboard] @@ -60,7 +60,7 @@ (defn app-will-mount [own] (when @auth-data - (rs/emit! (uda/fetch-profile) + (rs/emit! (udu/fetch-profile) (dp/fetch-projects))) own) diff --git a/src/uxbox/ui/settings.cljs b/src/uxbox/ui/settings.cljs index 403cfc2fd6..649e32ee4c 100644 --- a/src/uxbox/ui/settings.cljs +++ b/src/uxbox/ui/settings.cljs @@ -15,111 +15,11 @@ [uxbox.ui.mixins :as mx] [uxbox.util.dom :as dom] [uxbox.data.dashboard :as dd] + [uxbox.ui.settings.profile :as profile] + [uxbox.ui.settings.password :as password] + [uxbox.ui.settings.notifications :as notifications] [uxbox.ui.dashboard.header :refer (header)])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Page: Profile -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn profile-page-render - [own] - (html - [:main.dashboard-main - (header) - [:section.dashboard-content.user-settings - [:div.user-settings-nav - [:ul.user-settings-nav-inside - [:li.current {:on-click #(r/go :settings/profile)} "Profile"] - [:li {:on-click #(r/go :settings/password)} "Password"] - [:li {:on-click #(r/go :settings/notifications)} "Notifications"]]] - - [:section.user-settings-content - [:span.user-settings-label "Your avatar"] - [:form.avatar-form - [:img {:src "images/favicon.png" :border "0"}] - [:input {:type "file"}]] - [:span.user-settings-label "Name, username and email"] - [:input.input-text {:type "text" :placeholder "Your name"}] - [:input.input-text {:type "text" :placeholder "Your username"}] - [:input.input-text {:type "email" :placeholder "Your email"}] - [:span.user-settings-label "Choose a color theme"] - [:div.input-radio.radio-primary - [:input {:type "radio" :id "light-theme" :name "light theme" :value "none"}] - [:label {:for "light-theme" :value "None"} "Light theme"] - [:input {:type "radio" :id "dark-theme" :name "dark theme" :value "every-hour"}] - [:label {:for "dark-theme" :value "Every hour"} "Dark theme"] - [:input {:type "radio" :id "contrast-theme" :name "contrast theme" :value "every-day"}] - [:label {:for "contrast-theme" :value "Every day"} "High-contrast theme"]] - [:input.btn-primary {:type "submit" :value "Update settings"}] - ]]])) - -(def ^:static profile-page - (mx/component - {:render profile-page-render - :name "profile-page" - :mixins [mx/static]})) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Page: password -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn password-page-render - [own] - (html - [:main.dashboard-main - (header) - [:section.dashboard-content.user-settings - [:div.user-settings-nav - [:ul.user-settings-nav-inside - [:li {:on-click #(r/go :settings/profile)} "Profile"] - [:li.current {:on-click #(r/go :settings/password)} "Password"] - [:li {:on-click #(r/go :settings/notifications)} "Notifications"]]] - - [:section.user-settings-content - [:span.user-settings-label "Change password"] - [:input.input-text {:type "password" :placeholder "Old password"}] - [:input.input-text {:type "password" :placeholder "New password"}] - [:input.input-text {:type "password" :placeholder "Confirm password"}] - [:input.btn-primary {:type "submit" :value "Update settings"}] - ]]])) - -(def ^:static password-page - (mx/component - {:render password-page-render - :name "password-page" - :mixins [mx/static]})) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Page: notifications -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn notifications-page-render - [own] - (html - [:main.dashboard-main - (header) - [:section.dashboard-content.user-settings - [:div.user-settings-nav - [:ul.user-settings-nav-inside - [:li {:on-click #(r/go :settings/profile)} "Profile"] - [:li {:on-click #(r/go :settings/password)} "Password"] - [:li.current {:on-click #(r/go :settings/notifications)} "Notifications"]]] - - [:section.user-settings-content - [:span.user-settings-label "Prototype notifications"] - [:p "Get a roll up of prototype changes in your inbox."] - [:div.input-radio.radio-primary - [:input {:type "radio" :id "notification-1" :name "notification-1" :value "none"}] - [:label {:for "notification-1" :value "None"} "None"] - [:input {:type "radio" :id "notification-2" :name "notification-2" :value "every-hour"}] - [:label {:for "notification-2" :value "Every hour"} "Every hour"] - [:input {:type "radio" :id "notification-3" :name "notification-3" :value "every-day"}] - [:label {:for "notification-3" :value "Every day"} "Every day"]] - [:input.btn-primary {:type "submit" :value "Update settings"}] - ]]])) - -(def ^:static notifications-page - (mx/component - {:render notifications-page-render - :name "notifications-page" - :mixins [mx/static]})) +(def profile-page profile/profile-page) +(def password-page password/password-page) +(def notifications-page notifications/notifications-page) diff --git a/src/uxbox/ui/settings/notifications.cljs b/src/uxbox/ui/settings/notifications.cljs new file mode 100644 index 0000000000..867a4f7b06 --- /dev/null +++ b/src/uxbox/ui/settings/notifications.cljs @@ -0,0 +1,49 @@ +;; 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 +;; Copyright (c) 2016 Juan de la Cruz + +(ns uxbox.ui.settings.notifications + (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [cuerdas.core :as str] + [uxbox.router :as r] + [uxbox.rstore :as rs] + [uxbox.ui.icons :as i] + [uxbox.ui.mixins :as mx] + [uxbox.util.dom :as dom] + [uxbox.data.dashboard :as dd] + [uxbox.ui.dashboard.header :refer (header)])) + +(defn notifications-page-render + [own] + (html + [:main.dashboard-main + (header) + [:section.dashboard-content.user-settings + [:div.user-settings-nav + [:ul.user-settings-nav-inside + [:li {:on-click #(r/go :settings/profile)} "Profile"] + [:li {:on-click #(r/go :settings/password)} "Password"] + [:li.current {:on-click #(r/go :settings/notifications)} "Notifications"]]] + + [:section.user-settings-content + [:span.user-settings-label "Prototype notifications"] + [:p "Get a roll up of prototype changes in your inbox."] + [:div.input-radio.radio-primary + [:input {:type "radio" :id "notification-1" :name "notification-1" :value "none"}] + [:label {:for "notification-1" :value "None"} "None"] + [:input {:type "radio" :id "notification-2" :name "notification-2" :value "every-hour"}] + [:label {:for "notification-2" :value "Every hour"} "Every hour"] + [:input {:type "radio" :id "notification-3" :name "notification-3" :value "every-day"}] + [:label {:for "notification-3" :value "Every day"} "Every day"]] + [:input.btn-primary {:type "submit" :value "Update settings"}] + ]]])) + +(def ^:static notifications-page + (mx/component + {:render notifications-page-render + :name "notifications-page" + :mixins [mx/static]})) diff --git a/src/uxbox/ui/settings/password.cljs b/src/uxbox/ui/settings/password.cljs new file mode 100644 index 0000000000..9dd18a07a3 --- /dev/null +++ b/src/uxbox/ui/settings/password.cljs @@ -0,0 +1,86 @@ +;; 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 +;; Copyright (c) 2016 Juan de la Cruz + +(ns uxbox.ui.settings.password + (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [cuerdas.core :as str] + [uxbox.router :as r] + [uxbox.rstore :as rs] + [uxbox.ui.icons :as i] + [uxbox.ui.mixins :as mx] + [uxbox.util.dom :as dom] + [uxbox.data.users :as udu] + [uxbox.ui.dashboard.header :refer (header)])) + +;; --- Password Form + +(defn password-form-render + [own] + (let [local (:rum/local own) + valid? (and (not (str/empty? (:password-1 @local))) + (not (str/empty? (:password-2 @local))) + (= 6 (count (:password-1 @local ""))) + (= (:password-1 @local) + (:password-2 @local)))] + (println "valid?" valid?) + (letfn [(on-field-change [field event] + (let [value (dom/event->value event)] + (swap! local assoc field value))) + (on-submit [event] + (let [password (:password-1 @local)] + (rs/emit! (udu/update-password password))))] + + (html + [:form.password-form + [:span.user-settings-label "Change password"] + #_[:input.input-text {:type "password" :placeholder "Old password"}] + [:input.input-text + {:type "password" + :value (:password-1 @local "") + :on-change (partial on-field-change :password-1) + :placeholder "New password"}] + [:input.input-text + {:type "password" + :value (:password-2 @local "") + :on-change (partial on-field-change :password-2) + :placeholder "Confirm password"}] + [:input.btn-primary + {:type "button" + :class (when-not valid? "btn-disabled") + :disabled (not valid?) + :value "Update settings"}]])))) + +(def password-form + (mx/component + {:render password-form-render + :name "password-form" + :mixins [mx/static (mx/local)]})) + +;; --- Password Page + +(defn password-page-render + [own] + (html + [:main.dashboard-main + (header) + [:section.dashboard-content.user-settings + [:div.user-settings-nav + [:ul.user-settings-nav-inside + [:li {:on-click #(r/go :settings/profile)} "Profile"] + [:li.current {:on-click #(r/go :settings/password)} "Password"] + [:li {:on-click #(r/go :settings/notifications)} "Notifications"]]] + + [:section.user-settings-content + (password-form)]]])) + +(def password-page + (mx/component + {:render password-page-render + :name "password-page" + :mixins [mx/static]})) + diff --git a/src/uxbox/ui/settings/profile.cljs b/src/uxbox/ui/settings/profile.cljs new file mode 100644 index 0000000000..ddee0b41d6 --- /dev/null +++ b/src/uxbox/ui/settings/profile.cljs @@ -0,0 +1,127 @@ +;; 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 +;; Copyright (c) 2015-2016 Juan de la Cruz + +(ns uxbox.ui.settings.profile + (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [cuerdas.core :as str] + [lentes.core :as l] + [uxbox.router :as r] + [uxbox.state :as st] + [uxbox.rstore :as rs] + [uxbox.ui.icons :as i] + [uxbox.ui.mixins :as mx] + [uxbox.ui.dashboard.header :refer (header)] + [uxbox.data.users :as udu] + [uxbox.util.dom :as dom])) + +;; --- Lentes + +(def ^:const ^:private profile-l + (-> (l/key :profile) + (l/focus-atom st/state))) + +;; --- Profile Form + +(defn profile-form-render + [own] + (let [local (:rum/local own) + profile (merge (rum/react profile-l) + (deref local)) + theme (get-in profile [:metadata :theme] "light")] + (letfn [(on-theme-change [event] + (let [value (dom/event->value event)] + (println "on-theme-change" value) + (swap! local assoc-in [:metadata :theme] value))) + (on-field-change [field event] + (let [value (dom/event->value event)] + (swap! local assoc field value))) + (on-submit [event] + (rs/emit! (udu/update-profile profile)))] + (html + [:form.profile-form + [:span.user-settings-label "Name, username and email"] + [:input.input-text + {:type "text" + :on-change (partial on-field-change :fullname) + :value (:fullname profile "") + :placeholder "Your name"}] + [:input.input-text + {:type "text" + :on-change (partial on-field-change :username) + :value (:username profile "") + :placeholder "Your username"}] + [:input.input-text + {:type "email" + :on-change (partial on-field-change :email) + :value (:email profile "") + :placeholder "Your email"}] + + [:span.user-settings-label "Choose a color theme"] + [:div.input-radio.radio-primary + [:input {:type "radio" + :checked (= theme "light") + :on-change on-theme-change + :id "light-theme" + :name "theme" + :value "light"}] + [:label {:for "light-theme"} "Light theme"] + + [:input {:type "radio" + :checked (= theme "dark") + :on-change on-theme-change + :id "dark-theme" + :name "theme" + :value "dark"}] + [:label {:for "dark-theme"} "Dark theme"] + + [:input {:type "radio" + :checked (= theme "high-contrast") + :on-change on-theme-change + :id "high-contrast-theme" + :name "theme" + :value "high-contrast"}] + [:label {:for "high-contrast-theme"} "High-contrast theme"]] + + [:input.btn-primary + {:type "button" + :on-click on-submit + :value "Update settings"}]])))) + +(def profile-form + (mx/component + {:render profile-form-render + :name "profile-form" + :mixins [(mx/local) rum/reactive mx/static]})) + +;; --- Profile Page + +(defn profile-page-render + [own] + (html + [:main.dashboard-main + (header) + [:section.dashboard-content.user-settings + [:div.user-settings-nav + [:ul.user-settings-nav-inside + [:li.current {:on-click #(r/go :settings/profile)} "Profile"] + [:li {:on-click #(r/go :settings/password)} "Password"] + [:li {:on-click #(r/go :settings/notifications)} "Notifications"]]] + [:section.user-settings-content + [:span.user-settings-label "Your avatar"] + [:form.avatar-form + [:img {:src "images/favicon.png" :border "0"}] + [:input {:type "file"}]] + (profile-form) + ]]])) + +(def profile-page + (mx/component + {:render profile-page-render + :name "profile-page" + :mixins [mx/static]})) + diff --git a/src/uxbox/util/data.cljs b/src/uxbox/util/data.cljs index b3b90d03dc..9712b9cc01 100644 --- a/src/uxbox/util/data.cljs +++ b/src/uxbox/util/data.cljs @@ -53,6 +53,14 @@ value item)) coll)) + +(defn deep-merge + "Like merge, but merges maps recursively." + [& maps] + (if (every? map? maps) + (apply merge-with deep-merge maps) + (last maps))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Numbers Parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;