🎉 Enable Nitrate renewal with a manual activation code

Add a manual activation code flow to renew Nitrate when automatic activation is not available.
This commit is contained in:
Juanfran 2026-05-22 13:19:03 +02:00
parent 57d47f8e5e
commit 6b3d4e38b0
5 changed files with 58 additions and 30 deletions

View File

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.data.profile :as dprof]
[app.main.repo :as rp]
[app.main.store :as st]
@ -23,7 +24,7 @@
(mf/defc nitrate-code-activation-modal*
{::mf/register modal/components
::mf/register-as :nitrate-code-activation}
[_props]
[{:keys [renew?]}]
(let [value* (mf/use-state "")
error* (mf/use-state nil)
@ -35,7 +36,7 @@
on-accept
(mf/use-fn
(mf/deps value*)
(mf/deps value* renew?)
(fn [_]
(let [code (str/trim @value*)]
(when (seq code)
@ -43,9 +44,13 @@
(rx/subs!
(fn [_]
(modal/hide!)
(st/emit!
(modal/show {:type :nitrate-activation-success})
(dprof/refresh-profile)))
(if renew?
(st/emit!
(dprof/refresh-profile)
(ntf/success (tr "nitrate.subscription.renew-success")))
(st/emit!
(modal/show {:type :nitrate-activation-success})
(dprof/refresh-profile))))
(fn [error]
;; TODO: "Already used" is not yet detectable (CC upserts on reuse).
(let [code (-> error ex-data :code)]
@ -69,28 +74,32 @@
:icon i/close}]
[:div {:class (stl/css :modal-header)}
[:h2 {:class (stl/css :modal-title)} (tr "nitrate.code-activation.title")]]
[:h2 {:class (stl/css :modal-title)}
(if renew?
(tr "nitrate.code-activation.renew-title")
(tr "nitrate.code-activation.title"))]]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css-case :code-field true :invalid (some? @error*))}
[:label {:class (stl/css :code-label)}
(tr "nitrate.code-activation.input-label")]
[:textarea {:class (stl/css :code-textarea)
:auto-focus true
:value @value*
:placeholder (tr "nitrate.code-activation.placeholder")
:on-change on-change
:on-key-down on-key-down}]
(when @error*
[:span {:class (stl/css :error-msg)} @error*])]
[:div {:class (stl/css :content-stack)}
[:div {:class (stl/css-case :code-field true :invalid (some? @error*))}
[:label {:class (stl/css :code-label)}
(tr "nitrate.code-activation.input-label")]
[:textarea {:class (stl/css :code-textarea)
:auto-focus true
:value @value*
:placeholder (tr "nitrate.code-activation.placeholder")
:on-change on-change
:on-key-down on-key-down}]
(when @error*
[:span {:class (stl/css :error-msg)} @error*])]
[:input
{:type "button"
:class (stl/css-case :accept-btn true
:global/disabled (empty? (str/trim @value*)))
:disabled (empty? (str/trim @value*))
:value (tr "nitrate.code-activation.submit")
:on-click on-accept}]
[:input
{:type "button"
:class (stl/css-case :accept-btn true
:global/disabled (empty? (str/trim @value*)))
:disabled (empty? (str/trim @value*))
:value (tr "nitrate.code-activation.submit")
:on-click on-accept}]]
[:div {:class (stl/css :footer-text)}
(tr "nitrate.code-activation.footer") " "
[:a {:class (stl/css :link)

View File

@ -31,7 +31,7 @@
}
.modal-title {
@include t.use-typography("title-large");
@include t.use-typography("headline-large");
color: var(--modal-title-foreground-color);
margin-block-end: var(--sp-xxxl);
@ -40,10 +40,15 @@
.modal-content {
display: flex;
flex-direction: column;
gap: var(--sp-m);
color: var(--color-foreground-secondary);
}
.content-stack {
display: flex;
flex-direction: column;
gap: var(--sp-m);
}
.accept-btn {
@extend %modal-accept-btn;
@ -59,7 +64,7 @@
.code-label {
@include t.use-typography("body-medium");
color: var(--color-foreground-secondary);
color: var(--color-foreground-primary);
}
.code-textarea {

View File

@ -95,9 +95,11 @@
:activate-by-code (= code-action :activate)
:renew-by-code (= code-action :renovate)
:bottom-link (= code-action :renovate))
;; TODO add renovation modal
:on-click (when (= code-action :activate)
#(st/emit! (modal/show {:type :nitrate-code-activation})))}
:on-click (cond
(= code-action :activate)
#(st/emit! (modal/show {:type :nitrate-code-activation}))
(= code-action :renovate)
#(st/emit! (modal/show :nitrate-code-activation {:renew? true})))}
(if (= code-action :activate)
(tr "subscription.settings.activate-by-code")
(tr "nitrate.subscription.settings.renew-with-code"))])

View File

@ -9457,6 +9457,9 @@ msgstr "Field %s is invalid: %s"
msgid "nitrate.code-activation.title"
msgstr "Activate Nitrate"
msgid "nitrate.code-activation.renew-title"
msgstr "RENEW NITRATE"
msgid "nitrate.code-activation.input-label"
msgstr "Enter your activation code"
@ -9562,6 +9565,9 @@ msgstr "Cancel subscription"
msgid "nitrate.subscription.settings.renew-with-code"
msgstr "Renew with activation code"
msgid "nitrate.subscription.renew-success"
msgstr "Your Nitrate subscription has been renewed."
msgid "subscription.settings.activate-by-code"
msgstr "Enter activation code"

View File

@ -9128,6 +9128,9 @@ msgstr "¿Estás seguro de que deseas eliminar este equipo que forma parte de la
msgid "nitrate.code-activation.title"
msgstr "Activar Nitrate"
msgid "nitrate.code-activation.renew-title"
msgstr "RENOVAR NITRATE"
msgid "nitrate.code-activation.input-label"
msgstr "Introduce tu código de activación"
@ -9234,6 +9237,9 @@ msgstr "Cancelar subscripción"
msgid "nitrate.subscription.settings.renew-with-code"
msgstr "Renovar con código de activación"
msgid "nitrate.subscription.renew-success"
msgstr "Tu suscripción de Nitrate se ha renovado."
msgid "subscription.settings.more-information"
msgstr "Más información"