0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-07 06:48:19 -05:00

🎉 Add themes infraestructure.

This commit is contained in:
Vitaly Kornilov 2020-04-08 11:57:29 +03:00 committed by Andrey Antukh
parent cd61269cd5
commit ea3e17f7fe
20 changed files with 207 additions and 33 deletions

View file

@ -11,6 +11,7 @@ CREATE TABLE profile (
password text NOT NULL,
lang text NULL,
theme text NULL,
is_demo boolean NOT NULL DEFAULT false
);

View file

@ -46,6 +46,7 @@
(s/def ::profile-id ::us/uuid)
(s/def ::password ::us/string)
(s/def ::old-password ::us/string)
(s/def ::theme ::us/string)
;; --- Mutation: Login
@ -96,20 +97,21 @@
(def ^:private sql:update-profile
"update profile
set fullname = $2,
lang = $3
lang = $3,
theme = $4
where id = $1
and deleted_at is null
returning *")
(defn- update-profile
[conn {:keys [id fullname lang] :as params}]
(let [sqlv [sql:update-profile id fullname lang]]
[conn {:keys [id fullname lang theme] :as params}]
(let [sqlv [sql:update-profile id fullname lang theme]]
(-> (db/query-one conn sqlv)
(p/then' su/raise-not-found-if-nil)
(p/then' profile/strip-private-attrs))))
(s/def ::update-profile
(s/keys :req-un [::id ::fullname ::lang]))
(s/keys :req-un [::id ::fullname ::lang ::theme]))
(sm/defmutation ::update-profile
[params]

View file

@ -30,6 +30,7 @@
(s/def ::path ::us/string)
(s/def ::user ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::theme ::us/string)
;; --- Query: Profile (own)
@ -93,4 +94,4 @@
(defn strip-private-attrs
"Only selects a publicy visible profile attrs."
[profile]
(select-keys profile [:id :fullname :lang :email :created-at :photo]))
(select-keys profile [:id :fullname :lang :email :created-at :photo :theme]))

View file

@ -87,7 +87,9 @@
[^Row row]
(reduce (fn [acc index]
(let [cname (.getColumnName row index)]
(assoc acc cname (.getValue row ^int index))))
(if-some [value (.getValue row ^int index)]
(assoc acc cname value)
acc)))
{}
(range (.size row))))

View file

@ -51,8 +51,8 @@
out (th/try-on! (sm/handle event))]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(t/is (= (:id profile) (get-in out [:result :id])))))
))
(t/is (= (:id profile) (get-in out [:result :id])))))))
(t/deftest profile-query-and-manipulation
(let [profile @(th/create-profile db/pool 1)]
@ -75,7 +75,8 @@
::sm/type :update-profile
:fullname "Full Name"
:name "profile222"
:lang "en")
:lang "en"
:theme "dark")
out (th/try-on! (sm/handle data))]
;; (th/print-result! out)
@ -84,6 +85,7 @@
(let [result (:result out)]
(t/is (= (:fullname data) (:fullname result)))
(t/is (= (:email data) (:email result)))
(t/is (= (:theme data) (:theme result)))
(t/is (not (contains? result :password))))))
(t/testing "update photo"
@ -99,8 +101,8 @@
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (= (:id profile) (:id result))))))
))
(t/is (= (:id profile) (:id result))))))))
(t/deftest profile-deletion
(let [prof @(th/create-profile db/pool 1)
@ -189,8 +191,8 @@
out (th/try-on! (sq/handle data))]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(t/is (= 0 (count (:result out))))))
))
(t/is (= 0 (count (:result out))))))))
(t/deftest registration-domain-whitelist
(let [whitelist "gmail.com, hey.com, ya.ru"]

View file

@ -39,6 +39,8 @@ services:
- UXBOX_DATABASE_URI=postgresql://postgres/uxbox
- UXBOX_DATABASE_USERNAME=uxbox
- UXBOX_DATABASE_PASSWORD=uxbox
- UXBOX_THEME=light
#- UXBOX_THEME=dark
smtp:
container_name: 'uxbox-devenv-smtp'

View file

@ -24,6 +24,6 @@ tmux send-keys -t uxbox './bin/start-dev' enter
tmux rename-window -t uxbox:0 'gulp'
tmux select-window -t uxbox:0
tmux send-keys -t uxbox 'cd uxbox/frontend' enter C-l
tmux send-keys -t uxbox 'npx gulp watch' enter
tmux send-keys -t uxbox 'npx gulp --theme=${UXBOX_THEME} watch' enter
tmux -2 attach-session -t uxbox

View file

@ -2,6 +2,13 @@
**TODO**
## Frontend configuration parameters ##
**Only available at build time!**
- `-e UXBOX_PUBLIC_URL=...` (defaults to `http://localhost:6060`)
- `-e UXBOX_DEMO_WARNING=...` (defaults to `true`)
- `-e UXBOX_THEME=...` (defaults to `light`, accepts `dark` to enable UXBOX dark theme)
## Backend configuration parameters ##

View file

@ -123,14 +123,18 @@ function templatePipeline(options) {
const input = options.input;
const output = options.output;
const ts = Math.floor(new Date());
const th = process.env.UXBOX_THEME || 'light';
const themes = ['light', 'dark'];
const locales = readLocales();
const config = readConfig();
const tmpl = mustache({
ts: ts,
th: th,
config: JSON.stringify(config),
translations: JSON.stringify(locales),
themes: JSON.stringify(themes),
});
return gulp.src(input)
@ -144,12 +148,17 @@ function templatePipeline(options) {
* Generic
***********************************************/
gulp.task("scss:main", scssPipeline({
input: paths.resources + "styles/main.scss",
output: paths.output + "css/main.css"
gulp.task("scss:main-light", scssPipeline({
input: paths.resources + "styles/main-light.scss",
output: paths.output + "css/main-light.css"
}));
gulp.task("scss", gulp.parallel("scss:main"));
gulp.task("scss:main-dark", scssPipeline({
input: paths.resources + "styles/main-dark.scss",
output: paths.output + "css/main-dark.css"
}));
gulp.task("scss", gulp.parallel("scss:main-light", "scss:main-dark"));
gulp.task("svg:sprite", function() {
return gulp.src(paths.resources + "images/icons/*.svg")

View file

@ -634,6 +634,13 @@
"fr" : "Langue par défaut"
}
},
"settings.profile.section-theme-data" : {
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:102" ],
"translations" : {
"en" : "Default theme",
"fr" : "Thème par défaut"
}
},
"settings.profile.profile-saved" : {
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:47" ],
"translations" : {

View file

@ -0,0 +1,68 @@
// 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>
// Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
// UXBOX MAIN STYLES
//#################################################
//
//#################################################
@import 'common/dependencies/colors';
//@import 'common/dependencies/uxbox-light';
@import 'common/dependencies/uxbox-dark';
@import 'common/dependencies/helpers';
@import 'common/dependencies/mixin';
@import 'common/dependencies/fonts';
@import 'common/dependencies/reset';
@import 'common/dependencies/animations';
@import 'common/dependencies/z-index';
//#################################################
// Layouts
//#################################################
@import 'common/base';
@import 'main/layouts/main-layout';
@import 'main/layouts/login';
//#################################################
// Commons
//#################################################
@import 'common/framework';
//#################################################
// Partials
//#################################################
@import 'main/partials/main-bar';
@import 'main/partials/workspace-bar';
@import 'main/partials/workspace';
@import 'main/partials/tool-bar';
@import 'main/partials/project-bar';
@import 'main/partials/sidebar';
@import 'main/partials/sidebar-tools';
@import 'main/partials/sidebar-element-options';
@import 'main/partials/sidebar-icons';
@import 'main/partials/sidebar-layers';
@import 'main/partials/sidebar-sitemap';
@import 'main/partials/sidebar-document-history';
@import 'main/partials/dashboard-bar';
@import 'main/partials/dashboard-grid';
@import 'main/partials/user-settings';
@import 'main/partials/activity-bar';
@import 'main/partials/library-bar';
@import 'main/partials/lightbox';
@import 'main/partials/color-palette';
@import 'main/partials/colorpicker';
@import 'main/partials/forms';
@import 'main/partials/loader';
//#################################################
// Resources
//#################################################
@import 'collection/font-collection';

View file

@ -11,7 +11,7 @@
//#################################################
@import 'common/dependencies/colors';
//@import 'common/dependencies/uxbox-light';
@import 'common/dependencies/uxbox-light';
//@import 'common/dependencies/uxbox-dark';
@import 'common/dependencies/helpers';
@import 'common/dependencies/mixin';

View file

@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>UXBOX - The Open-Source prototyping tool</title>
<link href="/css/main.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
<link id="theme" href="/css/main-{{& th}}.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
<link rel="icon" href="/images/favicon.png" />
<!-- <link rel="preload" as="image" type="image/svg+xml" -->
<!-- href="/images/svg-sprite/symbol/svg/sprite.symbol.svg" /> -->
@ -16,6 +16,7 @@
<script>
window.uxboxConfig = JSON.parse({{& config }});
window.uxboxTranslations = JSON.parse({{& translations }});
window.uxboxThemes = {{& themes }};
</script>
<script src="/js/main.js?ts={{& ts}}"></script>
<script>uxbox.main.init()</script>

View file

@ -3,8 +3,9 @@
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UXBOX View</title>
<link href="/css/view.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
<link href="/css/view-{{& th}}.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
<link rel="icon" href="/images/favicon.png" />
</head>
<body>

View file

@ -20,4 +20,5 @@
(def default-language "en")
(def demo-warning (gobj/get config "demoWarning" true))
(def url public-url))
(def url public-url)
(def default-theme "light"))

View file

@ -22,6 +22,7 @@
[uxbox.util.dom :as dom]
[uxbox.util.html.history :as html-history]
[uxbox.util.i18n :as i18n]
[uxbox.util.theme :as theme]
[uxbox.util.router :as rt]
[uxbox.util.storage :refer [storage]]
[uxbox.util.timers :as ts]))
@ -65,8 +66,10 @@
(defn ^:export init
[]
(let [translations (gobj/get goog.global "uxboxTranslations")]
(let [translations (gobj/get goog.global "uxboxTranslations")
themes (gobj/get goog.global "uxboxThemes")]
(i18n/init! translations)
(theme/init! themes)
(unchecked-set js/window app-sym "main")
(st/init)
(init-ui)))

View file

@ -11,9 +11,11 @@
[cuerdas.core :as str]
[potok.core :as ptk]
[uxbox.common.spec :as us]
[uxbox.config :as cfg]
[uxbox.main.repo :as rp]
[uxbox.util.i18n :as i18n :refer [tr]]
[uxbox.util.storage :refer [storage]]))
[uxbox.util.storage :refer [storage]]
[uxbox.util.theme :as theme]))
;; --- Common Specs
@ -21,13 +23,13 @@
(s/def ::fullname ::us/string)
(s/def ::email ::us/email)
(s/def ::password ::us/string)
(s/def ::language ::us/string)
(s/def ::lang ::us/string)
(s/def ::theme ::us/string)
(s/def ::photo ::us/string)
(s/def ::created-at ::us/inst)
(s/def ::password-1 ::us/string)
(s/def ::password-2 ::us/string)
(s/def ::password-old ::us/string)
(s/def ::lang (s/nilable ::us/string))
(s/def ::profile
(s/keys :req-un [::id]
@ -35,7 +37,8 @@
::fullname
::photo
::email
::lang]))
::lang
::theme]))
;; --- Profile Fetched
@ -45,13 +48,16 @@
(ptk/reify ::profile-fetched
ptk/UpdateEvent
(update [_ state]
(assoc state :profile data))
(assoc state :profile (cond-> data
(nil? (:land data)) (assoc :lang cfg/default-language)
(nil? (:theme data)) (assoc :theme cfg/default-theme))))
ptk/EffectEvent
(effect [_ state stream]
(swap! storage assoc :profile data)
(when-let [lang (:lang data)]
(i18n/set-current-locale! lang)))))
(let [profile (:profile state)]
(swap! storage assoc :profile profile)
(i18n/set-current-locale! (:lang profile))
(theme/set-current-theme! (:theme profile))))))
;; --- Fetch Profile

View file

@ -22,10 +22,11 @@
(s/def ::fullname ::fm/not-empty-string)
(s/def ::lang (s/nilable ::fm/not-empty-string))
(s/def ::theme ::fm/not-empty-string)
(s/def ::email ::fm/email)
(s/def ::profile-form
(s/keys :req-un [::fullname ::lang ::email]))
(s/keys :req-un [::fullname ::lang ::theme ::email]))
(defn- on-error
[error form]
@ -86,7 +87,7 @@
:field :email}]
[:span.settings-label (t locale "settings.profile.lang")]
[:select.input-select {:value (or (:lang data) "en")
[:select.input-select {:value (:lang data)
:name "lang"
:class (fm/error-class form :lang)
:on-blur (fm/on-input-blur form :lang)
@ -94,6 +95,15 @@
[:option {:value "en"} "English"]
[:option {:value "fr"} "Français"]]
[:span.user-settings-label (tr "settings.profile.section-theme-data")]
[:select.input-select {:value (:theme data)
:name "theme"
:class (fm/error-class form :theme)
:on-blur (fm/on-input-blur form :theme)
:on-change (fm/on-input-change form :theme)}
[:option {:value "light"} "Light"]
[:option {:value "dark"} "Dark"]]
[:input.btn-primary
{:type "submit"
:class (when-not (:valid form) "btn-disabled")

View file

@ -0,0 +1,50 @@
;; 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 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2019-2020 Mathieu BRUNOT <mathieu.brunot@monogramm.io>
(ns uxbox.util.theme
"A theme manager."
(:require
[cuerdas.core :as str]
[rumext.alpha :as mf]
[beicon.core :as rx]
[goog.object :as gobj]
[uxbox.config :as cfg]
[uxbox.util.dom :as dom]
[uxbox.util.transit :as t]
[uxbox.util.storage :refer [storage]]))
(defonce theme (get storage ::theme cfg/default-theme))
(defonce theme-sub (rx/subject))
(defonce themes #js {})
(defn init!
[data]
(set! themes data))
(defn set-current-theme!
[v]
(when (not= theme v)
(when-some [el (dom/get-element "theme")]
(set! (.-href el) (str "css/main-" v ".css")))
(swap! storage assoc ::theme v)
(set! theme v)
(rx/push! theme-sub v)))
(defn set-default-theme!
[]
(set-current-theme! cfg/default-theme))
(defn use-theme
[]
(let [[theme set-theme] (mf/useState theme)]
(mf/useEffect (fn []
(let [sub (rx/sub! theme-sub #(set-theme %))]
#(rx/dispose! sub)))
#js [])
theme))

View file

@ -59,6 +59,7 @@ function build-frontend {
-e UXBOX_DEMO_WARNING=${UXBOX_DEMO_WARNING} \
-e UXBOX_DEPLOY_DATE=${UXBOX_DEPLOY_DATE} \
-e UXBOX_DEPLOY_COMMIT=${UXBOX_DEPLOY_COMMIT} \
-e UXBOX_THEME=${UXBOX_THEME} \
$IMAGE ./scripts/build-app.sh
}