diff --git a/CHANGES.md b/CHANGES.md index 473c1adb6..a1381635d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) + ## 1.13.0-beta ### :boom: Breaking changes diff --git a/backend/src/app/rpc/mutations/profile.clj b/backend/src/app/rpc/mutations/profile.clj index 5834b0588..2749c2b4a 100644 --- a/backend/src/app/rpc/mutations/profile.clj +++ b/backend/src/app/rpc/mutations/profile.clj @@ -342,13 +342,26 @@ ;; --- MUTATION: Update Profile (own) +(defn- update-profile-newsletter-subscribed + [conn profile-id newsletter-subscribed] + (let [profile (profile/retrieve-profile-data conn profile-id) + props (:props profile) + props (assoc props :newsletter-subscribed newsletter-subscribed) + ] + (db/update! conn :profile + {:props (db/tjson props)} + {:id profile-id}))) + (defn- update-profile - [conn {:keys [id fullname lang theme] :as params}] + [conn {:keys [id fullname lang theme newsletter-subscribed] :as params}] (let [profile (db/update! conn :profile {:fullname fullname :lang lang :theme theme} - {:id id})] + {:id id}) + profile (if (some? newsletter-subscribed) + (update-profile-newsletter-subscribed conn id newsletter-subscribed) + profile)] (-> profile (profile/decode-profile-row) (profile/strip-private-attrs)))) diff --git a/frontend/resources/images/deco-news-left.png b/frontend/resources/images/deco-news-left.png new file mode 100644 index 000000000..a705b2640 Binary files /dev/null and b/frontend/resources/images/deco-news-left.png differ diff --git a/frontend/resources/images/deco-news-right.png b/frontend/resources/images/deco-news-right.png new file mode 100644 index 000000000..f8feec261 Binary files /dev/null and b/frontend/resources/images/deco-news-right.png differ diff --git a/frontend/resources/images/deco-newsletter.png b/frontend/resources/images/deco-newsletter.png new file mode 100644 index 000000000..d322c62b8 Binary files /dev/null and b/frontend/resources/images/deco-newsletter.png differ diff --git a/frontend/resources/styles/main/partials/dashboard-settings.scss b/frontend/resources/styles/main/partials/dashboard-settings.scss index c5c19597b..d45d1661e 100644 --- a/frontend/resources/styles/main/partials/dashboard-settings.scss +++ b/frontend/resources/styles/main/partials/dashboard-settings.scss @@ -109,6 +109,38 @@ flex-direction: column; max-width: 368px; width: 100%; + + .newsletter-subs { + border-bottom: 1px solid $color-gray-20; + border-top: 1px solid $color-gray-20; + padding: 30px 0; + margin-bottom: 31px; + + .newsletter-title { + font-family: "worksans", sans-serif; + color: $color-gray-30; + font-size: $fs14; + } + + label { + font-family: "worksans", sans-serif; + color: $color-gray-60; + font-size: $fs12; + margin-right: -17px; + margin-bottom: 13px; + } + + .info { + font-family: "worksans", sans-serif; + color: $color-gray-30; + font-size: $fs12; + margin-bottom: 8px; + } + + .input-checkbox label { + align-items: flex-start; + } + } } .options-form, diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index 26f352471..b5a8f3ce2 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -996,6 +996,57 @@ } } } + + &.newsletter { + padding: $size-5 0 0 0; + flex-direction: column; + min-width: 555px; + .modal-top { + padding: 87px 40px 0 40px; + color: $color-gray-60; + display: flex; + flex-direction: column; + + h1 { + font-family: sourcesanspro; + font-weight: bold; + font-size: $fs36; + margin-bottom: 0.75rem; + } + + p { + font-family: sourcesanspro; + font-weight: 500; + font-size: $fs16; + margin-bottom: 1.5rem; + } + } + + .modal-bottom { + margin: 0 32px; + padding: 32px 0; + color: $color-gray-60; + display: flex; + flex-direction: column; + border-top: 1px solid $color-gray-10; + + p { + font-family: "worksans", sans-serif; + text-align: left; + color: $color-gray-30; + } + } + + .modal-footer { + padding: 17px; + display: flex; + justify-content: flex-end; + + .btn-secondary { + margin-right: 16px; + } + } + } } .deco { @@ -1004,6 +1055,23 @@ top: -18px; width: 60px; + &.top { + width: 183px; + top: -106px; + left: 161px; + } + + &.newsletter-right { + left: 515px; + top: 50px; + } + + &.newsletter-left { + width: 26px; + left: -15px; + top: -15px; + } + &.right { left: 590px; top: 0; diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index cd5ca770e..4bc1355d2 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -304,7 +304,7 @@ (let [mdata (meta data) on-success (:on-success mdata identity) on-error (:on-error mdata #(rx/throw %))] - (->> (rp/mutation :update-profile data) + (->> (rp/mutation :update-profile (dissoc data :props)) (rx/catch on-error) (rx/mapcat (fn [_] @@ -392,7 +392,6 @@ (->> (rp/mutation :update-profile-props {:props props}) (rx/map (constantly (fetch-profile))))))))) - (defn mark-questions-as-answered [] (ptk/reify ::mark-questions-as-answered diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 3c61dbce5..e7b2bab00 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -59,15 +59,15 @@ klass (str more-classes " " (dom/classnames - :focus @focus? - :valid (and touched? (not error)) - :invalid (and touched? error) - :disabled disabled - :empty (and is-text? (str/empty? value)) - :with-icon (not (nil? help-icon')) - :custom-input is-text? - :input-radio is-radio? - :input-checkbox is-checkbox?)) + :focus @focus? + :valid (and touched? (not error)) + :invalid (and touched? error) + :disabled disabled + :empty (and is-text? (str/empty? value)) + :with-icon (not (nil? help-icon')) + :custom-input is-text? + :input-radio is-radio? + :input-checkbox is-checkbox?)) swap-text-password (fn [] @@ -97,6 +97,7 @@ :placeholder label :on-change on-change :type @type') + (cond-> (and value is-checkbox?) (assoc :default-checked value)) (obj/clj->props))] [:div @@ -210,7 +211,7 @@ (let [form (or form (mf/use-ctx form-ctx))] [:input.btn-primary.btn-large {:name "submit" - :class (when-not (:valid @form) "btn-disabled") + :class (when (or (not (:valid @form)) (true? disabled)) "btn-disabled") :disabled (or (not (:valid @form)) (true? disabled)) :on-click on-click :value label diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index 76fb13755..c5caf63d6 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -10,6 +10,7 @@ [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] + [app.main.ui.onboarding.newsletter] [app.main.ui.onboarding.questions] [app.main.ui.onboarding.team-choice] [app.main.ui.onboarding.templates] @@ -154,7 +155,7 @@ skip (mf/use-callback (st/emitf (modal/hide) - (modal/show {:type :onboarding-choice}) + (modal/show {:type :onboarding-newsletter-modal}) (du/mark-onboarding-as-viewed)))] (mf/use-layout-effect diff --git a/frontend/src/app/main/ui/onboarding/newsletter.cljs b/frontend/src/app/main/ui/onboarding/newsletter.cljs new file mode 100644 index 000000000..bf72d5639 --- /dev/null +++ b/frontend/src/app/main/ui/onboarding/newsletter.cljs @@ -0,0 +1,47 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.onboarding.newsletter + (:require + [app.main.data.messages :as dm] + [app.main.data.modal :as modal] + [app.main.data.users :as du] + [app.main.store :as st] + [app.util.i18n :as i18n :refer [tr]] + [rumext.alpha :as mf])) + +(mf/defc onboarding-newsletter-modal + {::mf/register modal/components + ::mf/register-as :onboarding-newsletter-modal} + [] + (let [message (tr "onboarding.newsletter.acceptance-message") + accept + (mf/use-callback + (fn [] + (st/emit! (dm/success message) + (modal/show {:type :onboarding-choice}) + (du/update-profile-props {:newsletter-subscribed true})))) + + decline + (mf/use-callback + (fn [] + (st/emit! (modal/show {:type :onboarding-choice}) + (du/update-profile-props {:newsletter-subscribed false}))))] + + [:div.modal-overlay + [:div.modal-container.onboarding.newsletter.animated.fadeInUp + [:div.modal-top + [:h1.newsletter-title {:data-test "onboarding-newsletter-title"} (tr "onboarding.newsletter.title")] + [:p (tr "onboarding.newsletter.desc")]] + [:div.modal-bottom + [:p (tr "onboarding.newsletter.privacy1") [:a {:target "_blank" :href "https://penpot.app/privacy.html"} (tr "onboarding.newsletter.policy")]] + [:p (tr "onboarding.newsletter.privacy2")]] + [:div.modal-footer + [:button.btn-secondary {:on-click decline} (tr "onboarding.newsletter.decline")] + [:button.btn-primary {:on-click accept} (tr "onboarding.newsletter.accept")]] + [:img.deco.top {:src "images/deco-newsletter.png" :border "0"}] + [:img.deco.newsletter-left {:src "images/deco-news-left.png" :border "0"}] + [:img.deco.newsletter-right {:src "images/deco-news-right.png" :border "0"}]]])) diff --git a/frontend/src/app/main/ui/settings/profile.cljs b/frontend/src/app/main/ui/settings/profile.cljs index e92a71eaa..2919180e3 100644 --- a/frontend/src/app/main/ui/settings/profile.cljs +++ b/frontend/src/app/main/ui/settings/profile.cljs @@ -43,13 +43,17 @@ [{:keys [locale] :as props}] (let [profile (mf/deref refs/profile) form (fm/use-form :spec ::profile-form - :initial profile)] + :initial profile) + disabled-button (mf/use-state true) + activate-btn #(reset! disabled-button false)] + [:& fm/form {:on-submit on-submit :form form :class "profile-form"} [:div.fields-row [:& fm/input {:type "text" + :on-key-down activate-btn :name :fullname :label (t locale "dashboard.your-name")}]] @@ -66,8 +70,19 @@ [:a {:on-click #(modal/show! :change-email {})} (t locale "dashboard.change-email")]]]] + [:div.newsletter-subs + [:p.newsletter-title (tr "dashboard.newsletter-title")] + [:& fm/input {:name :newsletter-subscribed + :class "check-primary" + :type "checkbox" + :label (tr "dashboard.newsletter-msg") + :on-click activate-btn}] + [:p.info (tr "onboarding.newsletter.privacy1") [:a {:target "_blank" :href "https://penpot.app/privacy.html"} (tr "onboarding.newsletter.policy")]] + [:p.info (tr "onboarding.newsletter.privacy2")]] + [:& fm/submit-button - {:label (t locale "dashboard.update-settings")}] + {:label (t locale "dashboard.save-settings") + :disabled @disabled-button}] [:div.links [:div.link-item diff --git a/frontend/translations/en.po b/frontend/translations/en.po index ce9150436..46081d3e3 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -581,7 +581,19 @@ msgstr "Search results" msgid "dashboard.type-something" msgstr "Type to search results" -#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.save-settings" +msgstr "Save settings" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.newsletter-title" +msgstr "Newsletter subscription" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.newsletter-msg" +msgstr "Send me news, product updates and recommendations about Penpot." + +#: src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs msgid "dashboard.update-settings" msgstr "Update settings" @@ -1855,6 +1867,30 @@ msgstr "" msgid "onboarding.welcome.title" msgstr "Welcome to Penpot" +msgid "onboarding.newsletter.title" +msgstr "Want to receive Penpot news?" + +msgid "onboarding.newsletter.desc" +msgstr "Subscribe to our newsletter to stay up to date with product development progress and news." + +msgid "onboarding.newsletter.privacy1" +msgstr "Because we care about privacy, here's our " + +msgid "onboarding.newsletter.policy" +msgstr "Privacy Policy." + +msgid "onboarding.newsletter.privacy2" +msgstr "We will only send relevant emails to you. You can unsubscribe at any time in your user profile or via the unsubscribe link in any of our newsletters." + +msgid "onboarding.newsletter.accept" +msgstr "Yes, subscribe" + +msgid "onboarding.newsletter.decline" +msgstr "No, thanks" + +msgid "onboarding.newsletter.acceptance-message" +msgstr "Your subscription request has been sent, we will send you an email to confirm it." + #: src/app/main/ui/auth/recovery.cljs msgid "profile.recovery.go-to-login" msgstr "Go to login" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index c09e0e91b..0aa4de77c 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -587,7 +587,20 @@ msgstr "Resultados de búsqueda" msgid "dashboard.type-something" msgstr "Escribe algo para buscar" -#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.save-settings" +msgstr "Guardar opciones" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.newsletter-title" +msgstr "Suscripción a newsletter" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.newsletter-msg" +msgstr "Envíame noticias, actualizaciones de producto y recomendaciones sobre Penpot." + + +#: src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs msgid "dashboard.update-settings" msgstr "Actualizar opciones" @@ -1876,6 +1889,31 @@ msgstr "" msgid "onboarding.welcome.title" msgstr "Te damos la bienvenida a Penpot" +msgid "onboarding.newsletter.title" +msgstr "¿Quieres recibir noticias sobre Penpot?" + +msgid "onboarding.newsletter.desc" +msgstr "Suscríbete a nuestra newsletter para estar al día de los progresos del producto y noticias." + +msgid "onboarding.newsletter.privacy1" +msgstr "Porque nos importa la privacidad, aquí puedes ver nuestra " + +msgid "onboarding.newsletter.policy" +msgstr "Política de Privacidad." + +msgid "onboarding.newsletter.privacy2" +msgstr "Sólo te enviaremos emails relevantes para ti. Puedes desuscribirte en cualquier momento desde tu perfil o usando el vínculo de desuscripción en cualquiera de nuestras newsletters." + +msgid "onboarding.newsletter.accept" +msgstr "Si, suscribirme" + +msgid "onboarding.newsletter.decline" +msgstr "No, gracias" + +msgid "onboarding.newsletter.acceptance-message" +msgstr "Tu solicitud de suscripción ha sido enviada, te haremos una confirmación a tu email" + + #: src/app/main/ui/auth/recovery.cljs msgid "profile.recovery.go-to-login" msgstr "Ir al login"