diff --git a/frontend/src/uxbox/main.cljs b/frontend/src/uxbox/main.cljs index cefdca49e..5f0098767 100644 --- a/frontend/src/uxbox/main.cljs +++ b/frontend/src/uxbox/main.cljs @@ -22,8 +22,6 @@ [uxbox.util.router :as rt] [uxbox.util.timers :as ts])) - - ;; --- i18n (declare reinit) diff --git a/frontend/src/uxbox/main/locales/en.cljs b/frontend/src/uxbox/main/locales/en.cljs index c65387243..0d27b8629 100644 --- a/frontend/src/uxbox/main/locales/en.cljs +++ b/frontend/src/uxbox/main/locales/en.cljs @@ -119,7 +119,8 @@ "settings.exit" "EXIT" "settings.profile.profile-saved" "Profile saved successfully!" - "settings.profile.profile.profile-saved" "Name, username and email" + "settings.profile.section-basic-data" "Name, username and email" + "settings.profile.section-i18n-data" "Default language" "settings.profile.your-name" "Your name" "settings.profile.your-username" "Your username" "settings.profile.your-email" "Your email" diff --git a/frontend/src/uxbox/main/locales/fr.cljs b/frontend/src/uxbox/main/locales/fr.cljs index b0d093394..4d5092c54 100644 --- a/frontend/src/uxbox/main/locales/fr.cljs +++ b/frontend/src/uxbox/main/locales/fr.cljs @@ -119,7 +119,9 @@ "settings.exit" "QUITTER" "settings.profile.profile-saved" "Profil enregistré avec succès !" - "settings.profile.profile.profile-saved" "Nom, nom d'utilisateur et adresse email" + "settings.profile.section-basic-data" "Nom, nom d'utilisateur et adresse email" + "settings.profile.section-i18n-data" nil ;; TODO + "settings.profile.your-name" "Votre nom complet" "settings.profile.your-username" "Votre nom d'utilisateur" "settings.profile.your-email" "Votre adresse email" diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index 668676846..a067bdf0b 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -91,9 +91,9 @@ (uuid-str? id) (uuid id) :else nil) type (when (str/alpha? type) (keyword type))] - {:section section - :id id - :type type})) + #js {:section section + :id id + :type type})) (mf/def app @@ -105,9 +105,10 @@ :render (fn [own props] - (let [route (mx/react (::route-ref own))] - (case (get-in route [:data :name]) - :auth/login (mf/element auth/login-page) + (let [route (mx/react (::route-ref own)) + route-id (get-in route [:data :name])] + (case route-id + :auth/login (mf/elem auth/login-page) :auth/register (auth/register-page) :auth/recovery-request (auth/recovery-request-page) @@ -115,17 +116,22 @@ (let [token (get-in route [:params :path :token])] (auth/recovery-page token)) - :settings/profile (mf/element settings/profile-page) - :settings/password (settings/password-page) - :settings/notifications (settings/notifications-page) + (:settings/profile + :settings/password + :settings/notifications) + (mf/elem settings/settings {:route route}) - :dashboard/projects (dashboard/dashboard {:section :projects}) - :dashboard/icons (-> (parse-dashboard-params route :icons) - (dashboard/dashboard)) - :dashboard/images (-> (parse-dashboard-params route :images) - (dashboard/dashboard)) - :dashboard/colors (-> (parse-dashboard-params route :colors) - (dashboard/dashboard)) + ;; :settings/profile (mf/elem settings/settings {:section :profile}) + ;; :settings/password (mf/elem settings/settings {:section :password}) + ;; :settings/notifications (mf/elem settings/notifications-page) + + :dashboard/projects (mf/elem dashboard/dashboard {:section :projects}) + :dashboard/icons (->> (parse-dashboard-params route :icons) + (mf/element dashboard/dashboard)) + :dashboard/images (->> (parse-dashboard-params route :images) + (mf/element dashboard/dashboard)) + :dashboard/colors (->> (parse-dashboard-params route :colors) + (mf/element dashboard/dashboard)) :workspace/page (let [project (uuid (get-in route [:params :path :project])) page (uuid (get-in route [:params :path :page]))] diff --git a/frontend/src/uxbox/main/ui/auth/login.cljs b/frontend/src/uxbox/main/ui/auth/login.cljs index ae03e7da5..af2f5cfc2 100644 --- a/frontend/src/uxbox/main/ui/auth/login.cljs +++ b/frontend/src/uxbox/main/ui/auth/login.cljs @@ -57,7 +57,7 @@ " the projects will be periodicaly wiped."]]) (mf/defc login-form - {:wrap [mf/reactive]} + {:wrap [mf/reactive*]} [] (let [data (mf/react form-data) valid? (fm/valid? ::login-form data)] diff --git a/frontend/src/uxbox/main/ui/settings.cljs b/frontend/src/uxbox/main/ui/settings.cljs index 71255cf37..9e45314a4 100644 --- a/frontend/src/uxbox/main/ui/settings.cljs +++ b/frontend/src/uxbox/main/ui/settings.cljs @@ -6,17 +6,29 @@ ;; Copyright (c) 2015-2016 Juan de la Cruz (ns uxbox.main.ui.settings - (:require [cuerdas.core :as str] - [potok.core :as ptk] - [rumext.core :as mx :include-macros true] - [uxbox.builtins.icons :as i] - [uxbox.util.dom :as dom] - [uxbox.util.router :as r] - [uxbox.main.ui.settings.profile :as profile] - [uxbox.main.ui.settings.password :as password] - [uxbox.main.ui.settings.notifications :as notifications] - [uxbox.main.ui.dashboard.header :refer (header)])) + (:require + [cuerdas.core :as str] + [potok.core :as ptk] + [rumext.alpha :as mf] + [uxbox.builtins.icons :as i] + [uxbox.main.ui.messages :refer [messages-widget]] + [uxbox.main.ui.settings.header :refer [header]] + [uxbox.main.ui.settings.notifications :as notifications] + [uxbox.main.ui.settings.password :as password] + [uxbox.main.ui.settings.profile :as profile])) + +(mf/defc settings + {:wrap [mf/memo*]} + [{:keys [route] :as props}] + (let [section (get-in route [:data :name])] + [:main.dashboard-main + (messages-widget) + [:& header {:section section}] + (case section + :settings/profile (mf/elem profile/profile-page) + :settings/password (mf/elem password/password-page) + :settings/notifications (mf/elem notifications/notifications-page))])) + + + -(def profile-page profile/profile-page) -(def password-page password/password-page) -(def notifications-page notifications/notifications-page) diff --git a/frontend/src/uxbox/main/ui/settings/header.cljs b/frontend/src/uxbox/main/ui/settings/header.cljs index 3103187af..e811d9dd5 100644 --- a/frontend/src/uxbox/main/ui/settings/header.cljs +++ b/frontend/src/uxbox/main/ui/settings/header.cljs @@ -18,39 +18,33 @@ [uxbox.util.i18n :refer [tr]] [uxbox.util.router :as rt])) -(def ^:private section-ref - (-> (l/in [:route :id]) - (l/derive st/state))) - (mf/defc header-link [{:keys [section content] :as props}] (let [on-click #(st/emit! (rt/nav section))] [:a {:on-click on-click} content])) -(mf/def header - :mixins [mf/static mf/reactive] - :render - (fn [own props] - (let [section (mf/react section-ref) - profile? (= section :settings/profile) - password? (= section :settings/password) - notifications? (= section :settings/notifications)] - [:header#main-bar.main-bar - [:div.main-logo - [:& header-link {:section :dashboard/projects - :content i/logo}]] - [:ul.main-nav - [:li {:class (when profile? "current")} - [:& header-link {:setion :settings/profile - :content (tr "settings.profile")}]] - [:li {:class (when password? "current")} - [:& header-link {:section :settings/password - :content (tr "settings.password")}]] - [:li {:class (when notifications? "current")} - [:& header-link {:section :settings/notifications - :content (tr "settings.notifications")}]] - [:li {:on-click #(st/emit! (da/logout))} - [:& header-link {:section :auth/login - :content (tr "settings.exit")}]]] - (user)]))) +(mf/defc header + {:wrap [mf/memo*]} + [{:keys [section] :as props}] + (let [profile? (= section :settings/profile) + password? (= section :settings/password) + notifications? (= section :settings/notifications)] + [:header#main-bar.main-bar + [:div.main-logo + [:& header-link {:section :dashboard/projects + :content i/logo}]] + [:ul.main-nav + [:li {:class (when profile? "current")} + [:& header-link {:section :settings/profile + :content (tr "settings.profile")}]] + [:li {:class (when password? "current")} + [:& header-link {:section :settings/password + :content (tr "settings.password")}]] + [:li {:class (when notifications? "current")} + [:& header-link {:section :settings/notifications + :content (tr "settings.notifications")}]] + #_[:li {:on-click #(st/emit! (da/logout))} + [:& header-link {:section :auth/login + :content (tr "settings.exit")}]]] + [:& user]])) diff --git a/frontend/src/uxbox/main/ui/settings/notifications.cljs b/frontend/src/uxbox/main/ui/settings/notifications.cljs index ef59a65f1..3662c836f 100644 --- a/frontend/src/uxbox/main/ui/settings/notifications.cljs +++ b/frontend/src/uxbox/main/ui/settings/notifications.cljs @@ -6,45 +6,38 @@ ;; Copyright (c) 2016 Juan de la Cruz (ns uxbox.main.ui.settings.notifications - (:require [cuerdas.core :as str] - [uxbox.util.router :as r] - [potok.core :as ptk] - [uxbox.builtins.icons :as i] - [rumext.core :as mx :include-macros true] - [uxbox.util.i18n :refer [tr]] - [uxbox.util.dom :as dom] - [uxbox.main.ui.settings.header :refer (header)])) + (:require + [cuerdas.core :as str] + [rumext.alpha :as mf] + [uxbox.util.i18n :refer [tr]])) -(mx/defc notifications-page - {:mixins [mx/static]} - [own] - [:main.dashboard-main - (header) - [:section.dashboard-content.user-settings - [:section.user-settings-content - [:span.user-settings-label (tr "settings.notifications.notifications-saved")] - [:p (tr "settings.notifications.description")] - [:div.input-radio.radio-primary - [:input {:type "radio" - :id "notification-1" - :name "notification" - :value "none"}] - [:label {:for "notification-1" - :value (tr "settings.notifications.none")} (tr "settings.notifications.none")] - [:input {:type "radio" - :id "notification-2" - :name "notification" - :value "every-hour"}] - [:label {:for "notification-2" - :value (tr "settings.notifications.every-hour")} (tr "settings.notifications.every-hour")] - [:input {:type "radio" - :id "notification-3" - :name "notification" - :value "every-day"}] - [:label {:for "notification-3" - :value (tr "settings.notifications.every-day")} (tr "settings.notifications.every-day")]] - [:input.btn-primary {:type "submit" - :class "btn-disabled" - :disabled true - :value (tr "settings.update-settings")}] - ]]]) +(mf/defc notifications-page + [] + [:section.dashboard-content.user-settings + [:section.user-settings-content + [:span.user-settings-label (tr "settings.notifications.notifications-saved")] + [:p (tr "settings.notifications.description")] + [:div.input-radio.radio-primary + [:input {:type "radio" + :id "notification-1" + :name "notification" + :value "none"}] + [:label {:for "notification-1" + :value (tr "settings.notifications.none")} (tr "settings.notifications.none")] + [:input {:type "radio" + :id "notification-2" + :name "notification" + :value "every-hour"}] + [:label {:for "notification-2" + :value (tr "settings.notifications.every-hour")} (tr "settings.notifications.every-hour")] + [:input {:type "radio" + :id "notification-3" + :name "notification" + :value "every-day"}] + [:label {:for "notification-3" + :value (tr "settings.notifications.every-day")} (tr "settings.notifications.every-day")]] + [:input.btn-primary {:type "submit" + :class "btn-disabled" + :disabled true + :value (tr "settings.update-settings")}] + ]]) diff --git a/frontend/src/uxbox/main/ui/settings/password.cljs b/frontend/src/uxbox/main/ui/settings/password.cljs index 63ab1b032..8eaf2eb06 100644 --- a/frontend/src/uxbox/main/ui/settings/password.cljs +++ b/frontend/src/uxbox/main/ui/settings/password.cljs @@ -94,11 +94,7 @@ ;; --- Password Page (mx/defc password-page - {:mixins [mx/static]} [] - [:main.dashboard-main - (messages-widget) - (header) - [:section.dashboard-content.user-settings - [:section.user-settings-content - (password-form)]]]) + [:section.dashboard-content.user-settings + [:section.user-settings-content + (password-form)]]) diff --git a/frontend/src/uxbox/main/ui/settings/profile.cljs b/frontend/src/uxbox/main/ui/settings/profile.cljs index 9342f0a8f..4d5c8d19b 100644 --- a/frontend/src/uxbox/main/ui/settings/profile.cljs +++ b/frontend/src/uxbox/main/ui/settings/profile.cljs @@ -19,11 +19,11 @@ [uxbox.main.ui.messages :refer [messages-widget]] [uxbox.main.ui.settings.header :refer [header]] [uxbox.util.dom :as dom] + [uxbox.util.data :refer [read-string]] [uxbox.util.forms :as fm] - [uxbox.util.i18n :refer [tr]] + [uxbox.util.i18n :as i18n :refer [tr]] [uxbox.util.interop :refer [iterable->seq]] - [uxbox.util.router :as r] -)) + [uxbox.util.router :as r])) (def form-data (fm/focus-data :profile st/state)) @@ -63,7 +63,7 @@ ;; --- Profile Form (mf/def profile-form - :mixins [mf/static mf/reactive mf/sync-render (fm/clear-mixin st/store :profile)] + :mixins [mf/memo mf/reactive mf/sync-render (fm/clear-mixin st/store :profile)] :render (fn [own props] (let [data (merge {:theme "light"} @@ -73,9 +73,13 @@ valid? (fm/valid? ::profile-form data) theme (:theme data) on-success #(st/emit! (clear-form)) - on-submit #(st/emit! (udu/update-profile data on-success on-error))] + on-submit #(st/emit! (udu/update-profile data on-success on-error)) + on-lang-change (fn [event] + (let [lang (read-string (dom/event->value event))] + (prn "on-lang-change" lang) + (i18n/set-current-locale! lang)))] [:form.profile-form - [:span.user-settings-label (tr "settings.profile.profile.profile-saved")] + [:span.user-settings-label (tr "settings.profile.section-basic-data")] [:input.input-text {:type "text" :on-change #(on-field-change % :fullname) @@ -87,7 +91,6 @@ :value (:username data "") :placeholder (tr "settings.profile.your-username")}] (fm/input-error errors :username) - [:input.input-text {:type "email" :on-change #(on-field-change % :email) @@ -95,6 +98,12 @@ :placeholder (tr "settings.profile.your-email")}] (fm/input-error errors :email) + [:span.user-settings-label (tr "settings.profile.section-i18n-data")] + [:select.input-select {:value (pr-str (mf/deref i18n/locale)) + :on-change on-lang-change} + [:option {:value ":en"} "English"] + [:option {:value ":fr"} "Français"]] + [:input.btn-primary {:type "button" :class (when-not valid? "btn-disabled") @@ -105,7 +114,7 @@ ;; --- Profile Photo Form (mf/defc profile-photo-form - {:wrap [mf/reactive]} + {:wrap [mf/reactive*]} [] (letfn [(on-change [event] (let [target (dom/get-target event) @@ -128,11 +137,8 @@ (mf/defc profile-page [] - [:main.dashboard-main - (messages-widget) - (header) - [:section.dashboard-content.user-settings - [:section.user-settings-content - [:span.user-settings-label (tr "settings.profile.your-avatar")] - [:& profile-photo-form] - (profile-form)]]]) + [:section.dashboard-content.user-settings + [:section.user-settings-content + [:span.user-settings-label (tr "settings.profile.your-avatar")] + [:& profile-photo-form] + (profile-form)]]) diff --git a/frontend/src/uxbox/main/ui/users.cljs b/frontend/src/uxbox/main/ui/users.cljs index 2261194ef..0cca1f47a 100644 --- a/frontend/src/uxbox/main/ui/users.cljs +++ b/frontend/src/uxbox/main/ui/users.cljs @@ -15,6 +15,7 @@ [uxbox.main.data.lightbox :as udl] [uxbox.main.store :as st] [uxbox.main.ui.navigation :as nav] + [uxbox.util.dom :as dom] [uxbox.util.i18n :refer (tr)] [uxbox.util.router :as rt])) @@ -22,19 +23,24 @@ (mf/defc user-menu [props] - [:ul.dropdown #_{:class (when-not open? "hide")} - [:li {:on-click #(st/emit! (rt/nav :settings/profile))} - i/user - [:span (tr "ds.user.profile")]] - [:li {:on-click #(st/emit! (rt/nav :settings/password))} - i/lock - [:span (tr "ds.user.password")]] - [:li {:on-click #(st/emit! (rt/nav :settings/notifications))} - i/mail - [:span (tr "ds.user.notifications")]] - [:li {:on-click #(st/emit! (da/logout))} - i/exit - [:span (tr "ds.user.exit")]]]) + (letfn [(on-click [event section] + (dom/stop-propagation event) + (if (keyword? section) + (st/emit! (rt/nav section)) + (st/emit! section)))] + [:ul.dropdown + [:li {:on-click #(on-click % :settings/profile)} + i/user + [:span (tr "ds.user.profile")]] + [:li {:on-click #(on-click % :settings/password)} + i/lock + [:span (tr "ds.user.password")]] + [:li {:on-click #(on-click % :settings/notifications)} + i/mail + [:span (tr "ds.user.notifications")]] + [:li {:on-click #(on-click % (da/logout))} + i/exit + [:span (tr "ds.user.exit")]]])) ;; --- User Widget @@ -42,19 +48,18 @@ (-> (l/key :profile) (l/derive st/state))) -(mf/def user - :mixins [mf/static mf/reactive (mf/local false)] - - :render - (fn [{:keys [::mf/local] :as own} props] - (let [profile (mf/react profile-ref) - photo (if (str/empty? (:photo profile "")) - "/images/avatar.jpg" - (:photo profile))] - [:div.user-zone {:on-click #(st/emit! (rt/navigate :settings/profile)) - :on-mouse-enter #(reset! local true) - :on-mouse-leave #(reset! local false)} - [:span (:fullname profile)] - [:img {:src photo}] - (when @local - [:& user-menu])]))) +(mf/defc user + {:wrap [mf/reactive*]} + [_] + (let [open (mf/use-state false) + profile (mf/react profile-ref) + photo (if (str/empty? (:photo profile "")) + "/images/avatar.jpg" + (:photo profile))] + [:div.user-zone {:on-click #(st/emit! (rt/navigate :settings/profile)) + :on-mouse-enter #(reset! open true) + :on-mouse-leave #(reset! open false)} + [:span (:fullname profile)] + [:img {:src photo}] + (when @open + [:& user-menu])])) diff --git a/frontend/src/uxbox/util/i18n.cljs b/frontend/src/uxbox/util/i18n.cljs index e0cda4911..6b4793d26 100644 --- a/frontend/src/uxbox/util/i18n.cljs +++ b/frontend/src/uxbox/util/i18n.cljs @@ -10,24 +10,23 @@ (:require [cuerdas.core :as str] [uxbox.util.storage :refer (storage)])) -(defonce state (atom {:current-locale (get storage ::locale :en)})) +(defonce locale (atom (get storage ::locale :en))) +(defonce state (atom {})) (defn update-locales! [callback] (swap! state callback)) (defn set-current-locale! - [locale] - (swap! storage assoc ::locale locale) - (swap! state assoc :current-locale locale)) + [v] + (swap! storage assoc ::locale v) + (reset! locale v)) (defn on-locale-change! [callback] - (add-watch state ::main (fn [_ _ old new] - (let [old-locale (:current-locale old) - new-locale (:current-locale new)] - (when (not= old-locale new-locale) - (callback new-locale old-locale)))))) + (add-watch locale ::main (fn [_ _ old-locale new-locale] + (when (not= old-locale new-locale) + (callback new-locale old-locale))))) ;; A marker type that is used just for mark ;; a parameter that reprsentes the counter. @@ -50,13 +49,14 @@ "Translate the string." ([t] (let [default (name t) - locale (get @state :current-locale) - value (get-in @state [locale t] default)] + locale (deref locale) + value (or (get-in @state [locale t]) + default)] (if (vector? value) (or (second value) default) value))) ([t & args] - (let [locale (get @state :current-locale) + (let [locale (deref locale) value (get-in @state [locale t] (name t)) plural (first (filter c? args)) args (mapv #(if (c? %) @% %) args)