🎉 Add newsletter subscription modal

This commit is contained in:
Eva 2022-03-22 18:27:06 +01:00 committed by Andrey Antukh
parent b7e0619e9a
commit 5e7e055539
14 changed files with 270 additions and 19 deletions

View file

@ -8,6 +8,7 @@
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)
## 1.13.0-beta ## 1.13.0-beta
### :boom: Breaking changes ### :boom: Breaking changes

View file

@ -342,13 +342,26 @@
;; --- MUTATION: Update Profile (own) ;; --- 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 (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 (let [profile (db/update! conn :profile
{:fullname fullname {:fullname fullname
:lang lang :lang lang
:theme theme} :theme theme}
{:id id})] {:id id})
profile (if (some? newsletter-subscribed)
(update-profile-newsletter-subscribed conn id newsletter-subscribed)
profile)]
(-> profile (-> profile
(profile/decode-profile-row) (profile/decode-profile-row)
(profile/strip-private-attrs)))) (profile/strip-private-attrs))))

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -109,6 +109,38 @@
flex-direction: column; flex-direction: column;
max-width: 368px; max-width: 368px;
width: 100%; 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, .options-form,

View file

@ -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 { .deco {
@ -1004,6 +1055,23 @@
top: -18px; top: -18px;
width: 60px; width: 60px;
&.top {
width: 183px;
top: -106px;
left: 161px;
}
&.newsletter-right {
left: 515px;
top: 50px;
}
&.newsletter-left {
width: 26px;
left: -15px;
top: -15px;
}
&.right { &.right {
left: 590px; left: 590px;
top: 0; top: 0;

View file

@ -304,7 +304,7 @@
(let [mdata (meta data) (let [mdata (meta data)
on-success (:on-success mdata identity) on-success (:on-success mdata identity)
on-error (:on-error mdata #(rx/throw %))] on-error (:on-error mdata #(rx/throw %))]
(->> (rp/mutation :update-profile data) (->> (rp/mutation :update-profile (dissoc data :props))
(rx/catch on-error) (rx/catch on-error)
(rx/mapcat (rx/mapcat
(fn [_] (fn [_]
@ -392,7 +392,6 @@
(->> (rp/mutation :update-profile-props {:props props}) (->> (rp/mutation :update-profile-props {:props props})
(rx/map (constantly (fetch-profile))))))))) (rx/map (constantly (fetch-profile)))))))))
(defn mark-questions-as-answered (defn mark-questions-as-answered
[] []
(ptk/reify ::mark-questions-as-answered (ptk/reify ::mark-questions-as-answered

View file

@ -59,15 +59,15 @@
klass (str more-classes " " klass (str more-classes " "
(dom/classnames (dom/classnames
:focus @focus? :focus @focus?
:valid (and touched? (not error)) :valid (and touched? (not error))
:invalid (and touched? error) :invalid (and touched? error)
:disabled disabled :disabled disabled
:empty (and is-text? (str/empty? value)) :empty (and is-text? (str/empty? value))
:with-icon (not (nil? help-icon')) :with-icon (not (nil? help-icon'))
:custom-input is-text? :custom-input is-text?
:input-radio is-radio? :input-radio is-radio?
:input-checkbox is-checkbox?)) :input-checkbox is-checkbox?))
swap-text-password swap-text-password
(fn [] (fn []
@ -97,6 +97,7 @@
:placeholder label :placeholder label
:on-change on-change :on-change on-change
:type @type') :type @type')
(cond-> (and value is-checkbox?) (assoc :default-checked value))
(obj/clj->props))] (obj/clj->props))]
[:div [:div
@ -210,7 +211,7 @@
(let [form (or form (mf/use-ctx form-ctx))] (let [form (or form (mf/use-ctx form-ctx))]
[:input.btn-primary.btn-large [:input.btn-primary.btn-large
{:name "submit" {: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)) :disabled (or (not (:valid @form)) (true? disabled))
:on-click on-click :on-click on-click
:value label :value label

View file

@ -10,6 +10,7 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.users :as du] [app.main.data.users :as du]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.onboarding.newsletter]
[app.main.ui.onboarding.questions] [app.main.ui.onboarding.questions]
[app.main.ui.onboarding.team-choice] [app.main.ui.onboarding.team-choice]
[app.main.ui.onboarding.templates] [app.main.ui.onboarding.templates]
@ -154,7 +155,7 @@
skip skip
(mf/use-callback (mf/use-callback
(st/emitf (modal/hide) (st/emitf (modal/hide)
(modal/show {:type :onboarding-choice}) (modal/show {:type :onboarding-newsletter-modal})
(du/mark-onboarding-as-viewed)))] (du/mark-onboarding-as-viewed)))]
(mf/use-layout-effect (mf/use-layout-effect

View file

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

View file

@ -43,13 +43,17 @@
[{:keys [locale] :as props}] [{:keys [locale] :as props}]
(let [profile (mf/deref refs/profile) (let [profile (mf/deref refs/profile)
form (fm/use-form :spec ::profile-form 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 [:& fm/form {:on-submit on-submit
:form form :form form
:class "profile-form"} :class "profile-form"}
[:div.fields-row [:div.fields-row
[:& fm/input [:& fm/input
{:type "text" {:type "text"
:on-key-down activate-btn
:name :fullname :name :fullname
:label (t locale "dashboard.your-name")}]] :label (t locale "dashboard.your-name")}]]
@ -66,8 +70,19 @@
[:a {:on-click #(modal/show! :change-email {})} [:a {:on-click #(modal/show! :change-email {})}
(t locale "dashboard.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 [:& fm/submit-button
{:label (t locale "dashboard.update-settings")}] {:label (t locale "dashboard.save-settings")
:disabled @disabled-button}]
[:div.links [:div.links
[:div.link-item [:div.link-item

View file

@ -581,7 +581,19 @@ msgstr "Search results"
msgid "dashboard.type-something" msgid "dashboard.type-something"
msgstr "Type to search results" 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" msgid "dashboard.update-settings"
msgstr "Update settings" msgstr "Update settings"
@ -1855,6 +1867,30 @@ msgstr ""
msgid "onboarding.welcome.title" msgid "onboarding.welcome.title"
msgstr "Welcome to Penpot" 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 #: src/app/main/ui/auth/recovery.cljs
msgid "profile.recovery.go-to-login" msgid "profile.recovery.go-to-login"
msgstr "Go to login" msgstr "Go to login"

View file

@ -587,7 +587,20 @@ msgstr "Resultados de búsqueda"
msgid "dashboard.type-something" msgid "dashboard.type-something"
msgstr "Escribe algo para buscar" 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" msgid "dashboard.update-settings"
msgstr "Actualizar opciones" msgstr "Actualizar opciones"
@ -1876,6 +1889,31 @@ msgstr ""
msgid "onboarding.welcome.title" msgid "onboarding.welcome.title"
msgstr "Te damos la bienvenida a Penpot" 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 #: src/app/main/ui/auth/recovery.cljs
msgid "profile.recovery.go-to-login" msgid "profile.recovery.go-to-login"
msgstr "Ir al login" msgstr "Ir al login"