0
Fork 0
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:
Andrey Antukh 2019-07-21 19:09:37 +02:00
parent 76726b6cd2
commit 14d97511e6
12 changed files with 182 additions and 169 deletions

View file

@ -22,8 +22,6 @@
[uxbox.util.router :as rt]
[uxbox.util.timers :as ts]))
;; --- i18n
(declare reinit)

View file

@ -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"

View file

@ -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"

View file

@ -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]))]

View file

@ -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)]

View file

@ -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)

View file

@ -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]]))

View file

@ -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")}]
]])

View file

@ -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)]])

View file

@ -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)]])

View file

@ -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])]))

View file

@ -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)