mirror of
https://github.com/penpot/penpot.git
synced 2025-03-13 00:01:51 -05:00
Refactor settings pages and add tha ability to change current locale.
This commit is contained in:
parent
76726b6cd2
commit
14d97511e6
12 changed files with 182 additions and 169 deletions
|
@ -22,8 +22,6 @@
|
|||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.timers :as ts]))
|
||||
|
||||
|
||||
|
||||
;; --- i18n
|
||||
|
||||
(declare reinit)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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]))]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -6,17 +6,29 @@
|
|||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(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)
|
||||
|
|
|
@ -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]]))
|
||||
|
||||
|
|
|
@ -6,45 +6,38 @@
|
|||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(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")}]
|
||||
]])
|
||||
|
|
|
@ -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)]])
|
||||
|
|
|
@ -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)]])
|
||||
|
|
|
@ -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])]))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue