mirror of
https://github.com/status-im/status-react.git
synced 2025-02-12 10:57:48 +00:00
feat(onboarding): Present Terms to users upgrading from v1 (#21124)
We now show the onboarding intro requesting the user to accept the Terms of Use & Privacy Policy with the new button "Explore the new Status" if the user had installed any version of Status older than the one from this PR and had at least one profile. Fixes https://github.com/status-im/status-mobile/issues/21113 status-go PR https://github.com/status-im/status-go/pull/5766 In practice, this means: - Users coming from Status v1 who had at least one profile will see the modified onboarding intro screen and will need to accept the terms to proceed. - Users who already installed v2 and are upgrading to this PR build (devs & QAs mostly) and who had at least one profile will also see the modified intro screen and will need to accept the terms to proceed. Areas that may be impacted - Onboarding Steps to test: The criteria used during development: 1. Given that user Alice had installed v1 and had one or more profiles. 2. When she installs v2 and opens it, she sees the new onboarding intro and must agree to the terms to enable the button "Explore the new Status". 3. After pressing the button, she can login as usual in any of her profiles. 1. Given that user Alice already upgraded from v1 and accepted the terms. 2. When she reopens the app she does not need to accept terms again and can immediately sign-in with any of her profiles. 1. Given that user Alice already upgraded from v1 and accepted the terms. 2. When she deletes all profiles, she sees the onboarding intro for users who have not upgraded, i.e. she has to agree to terms and she sees the usual two buttons "Create profile" and "Sync or recover profile". 1. Given that user Alice never installed Status. 2. When she installs v2, she sees the normal onboarding intro screen, where she has to accept the terms and she sees two buttons "Create profile" and "Sync or recover profile". 3. When she reopens the app, she doesn't see anymore the screen to accept terms.
This commit is contained in:
parent
b1c9077565
commit
d45eb5ec20
@ -253,6 +253,11 @@ class AccountManager(private val reactContext: ReactApplicationContext) : ReactC
|
||||
utils.executeRunnableStatusGoMethod({ Statusgo.initializeApplication(request) }, callback)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
private fun acceptTerms(callback: Callback) {
|
||||
Log.d(TAG, "acceptTerms")
|
||||
utils.executeRunnableStatusGoMethod({ Statusgo.acceptTerms() }, callback)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun logout() {
|
||||
|
@ -204,6 +204,14 @@ RCT_EXPORT_METHOD(initializeApplication:(NSString *)request
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(acceptTerms:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"acceptTerms() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoAcceptTerms();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"OpenAccounts() method called");
|
||||
|
@ -761,6 +761,26 @@ void _Logout(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
}
|
||||
|
||||
void _AcceptTerms(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
if (args.Length() != 0) {
|
||||
// Throw an Error that is passed back to JavaScript
|
||||
isolate->ThrowException(Exception::TypeError(
|
||||
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for AcceptTerms")));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the argument types
|
||||
|
||||
// Call exported Go function, which returns a C string
|
||||
char *c = AcceptTerms();
|
||||
|
||||
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
|
||||
args.GetReturnValue().Set(ret);
|
||||
delete c;
|
||||
}
|
||||
|
||||
void _HashMessage(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
@ -1998,6 +2018,7 @@ void init(Local<Object> exports) {
|
||||
NODE_SET_METHOD(exports, "multiAccountStoreAccount", _MultiAccountStoreAccount);
|
||||
NODE_SET_METHOD(exports, "initKeystore", _InitKeystore);
|
||||
NODE_SET_METHOD(exports, "initializeApplication", _InitializeApplication);
|
||||
NODE_SET_METHOD(exports, "acceptTerms", _AcceptTerms);
|
||||
NODE_SET_METHOD(exports, "fleets", _Fleets);
|
||||
NODE_SET_METHOD(exports, "stopCPUProfiling", _StopCPUProfiling);
|
||||
NODE_SET_METHOD(exports, "encodeTransfer", _EncodeTransfer);
|
||||
|
@ -79,6 +79,12 @@
|
||||
(types/clj->json request)
|
||||
#(callback (types/json->clj %))))
|
||||
|
||||
(defn accept-terms
|
||||
([]
|
||||
(native-utils/promisify-native-module-call accept-terms))
|
||||
([callback]
|
||||
(.acceptTerms ^js (account-manager) callback)))
|
||||
|
||||
(defn prepare-dir-and-update-config
|
||||
[key-uid config callback]
|
||||
(log/debug "[native-module] prepare-dir-and-update-config")
|
||||
|
@ -11,66 +11,92 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- show-terms-of-use
|
||||
[]
|
||||
(rf/dispatch [:show-bottom-sheet {:content terms/terms-of-use :shell? true}]))
|
||||
|
||||
(defn- show-privacy-policy
|
||||
[]
|
||||
(rf/dispatch [:show-bottom-sheet {:content privacy/privacy-statement :shell? true}]))
|
||||
|
||||
(defn- terms
|
||||
[terms-accepted? set-terms-accepted?]
|
||||
[rn/view {:style style/terms-privacy-container}
|
||||
[rn/view
|
||||
{:accessibility-label :terms-privacy-checkbox-container}
|
||||
[quo/selectors
|
||||
{:type :checkbox
|
||||
:blur? true
|
||||
:checked? terms-accepted?
|
||||
:on-change #(set-terms-accepted? not)}]]
|
||||
[rn/view {:style style/text-container}
|
||||
[quo/text
|
||||
{:style style/plain-text
|
||||
:size :paragraph-2}
|
||||
(str (i18n/label :t/accept-status-tos-prefix) " ")]
|
||||
[quo/text
|
||||
{:on-press show-terms-of-use
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
:weight :medium}
|
||||
(i18n/label :t/terms-of-service)]
|
||||
[quo/text
|
||||
{:style style/plain-text
|
||||
:size :paragraph-2}
|
||||
" " (i18n/label :t/and) " "]
|
||||
[quo/text
|
||||
{:on-press show-privacy-policy
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
:weight :medium}
|
||||
(i18n/label :t/intro-privacy-policy)]]])
|
||||
|
||||
(defn- explore-new-status
|
||||
[]
|
||||
(rf/dispatch [:profile/explore-new-status]))
|
||||
|
||||
(defn- sync-or-recover-profile
|
||||
[]
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch [:open-modal :screen/onboarding.sync-or-recover-profile]))
|
||||
|
||||
(defn- create-profile
|
||||
[]
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch [:open-modal :screen/onboarding.new-to-status]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [[terms-accepted? set-terms-accepted?] (rn/use-state false)]
|
||||
(let [[terms-accepted? set-terms-accepted?] (rn/use-state false)
|
||||
from-v1-without-terms-accepted? (rf/sub [:profile/from-status-v1-without-terms-accepted?])]
|
||||
[rn/view {:style style/page-container}
|
||||
[background/view false]
|
||||
[quo/bottom-actions
|
||||
{:container-style (style/bottom-actions-container (safe-area/get-bottom))
|
||||
:actions :two-vertical-actions
|
||||
:description :top
|
||||
:description-top-text [rn/view
|
||||
{:style style/terms-privacy-container}
|
||||
[rn/view
|
||||
{:accessibility-label :terms-privacy-checkbox-container}
|
||||
[quo/selectors
|
||||
{:type :checkbox
|
||||
:blur? true
|
||||
:checked? terms-accepted?
|
||||
:on-change #(set-terms-accepted? not)}]]
|
||||
[rn/view {:style style/text-container}
|
||||
[quo/text
|
||||
{:style style/plain-text
|
||||
:size :paragraph-2}
|
||||
(str (i18n/label :t/accept-status-tos-prefix) " ")]
|
||||
[quo/text
|
||||
{:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content terms/terms-of-use
|
||||
:shell? true}])
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
:weight :medium}
|
||||
(i18n/label :t/terms-of-service)]
|
||||
[quo/text
|
||||
{:style style/plain-text
|
||||
:size :paragraph-2}
|
||||
" " (i18n/label :t/and) " "]
|
||||
[quo/text
|
||||
{:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content privacy/privacy-statement
|
||||
:shell? true}])
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
:weight :medium}
|
||||
(i18n/label :t/intro-privacy-policy)]]]
|
||||
:button-one-label (i18n/label :t/sync-or-recover-profile)
|
||||
:button-one-props {:type :dark-grey
|
||||
:disabled? (not terms-accepted?)
|
||||
:accessibility-label :already-use-status-button
|
||||
:on-press (fn []
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch
|
||||
[:open-modal
|
||||
:screen/onboarding.sync-or-recover-profile]))}
|
||||
:button-two-label (i18n/label :t/create-profile)
|
||||
:button-two-props {:accessibility-label :new-to-status-button
|
||||
:disabled? (not terms-accepted?)
|
||||
:on-press
|
||||
(fn []
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch
|
||||
[:open-modal :screen/onboarding.new-to-status]))}}]
|
||||
(cond->
|
||||
{:container-style (style/bottom-actions-container (safe-area/get-bottom))
|
||||
:actions :two-vertical-actions
|
||||
:description :top
|
||||
:description-top-text [terms terms-accepted? set-terms-accepted?]}
|
||||
from-v1-without-terms-accepted?
|
||||
(assoc
|
||||
:actions :one-action
|
||||
:button-one-label (i18n/label :t/explore-the-new-status)
|
||||
:button-one-props {:disabled? (not terms-accepted?)
|
||||
:accessibility-label :explore-new-status
|
||||
:on-press explore-new-status})
|
||||
|
||||
(not from-v1-without-terms-accepted?)
|
||||
(assoc
|
||||
:actions :two-vertical-actions
|
||||
:button-one-label (i18n/label :t/sync-or-recover-profile)
|
||||
:button-one-props {:type :dark-grey
|
||||
:disabled? (not terms-accepted?)
|
||||
:accessibility-label :already-use-status-button
|
||||
:on-press sync-or-recover-profile}
|
||||
:button-two-label (i18n/label :t/create-profile)
|
||||
:button-two-props {:accessibility-label :new-to-status-button
|
||||
:disabled? (not terms-accepted?)
|
||||
:on-press create-profile}))]
|
||||
[overlay/view]]))
|
||||
|
5
src/status_im/contexts/profile/data_store.cljs
Normal file
5
src/status_im/contexts/profile/data_store.cljs
Normal file
@ -0,0 +1,5 @@
|
||||
(ns status-im.contexts.profile.data-store)
|
||||
|
||||
(defn accepted-terms?
|
||||
[accounts]
|
||||
(some #(:hasAcceptedTerms %) accounts))
|
14
src/status_im/contexts/profile/effects.cljs
Normal file
14
src/status_im/contexts/profile/effects.cljs
Normal file
@ -0,0 +1,14 @@
|
||||
(ns status-im.contexts.profile.effects
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[promesa.core :as promesa]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-fx :effects.profile/accept-terms
|
||||
(fn [{:keys [on-success]}]
|
||||
(-> (native-module/accept-terms)
|
||||
(promesa/then (fn []
|
||||
(rf/call-continuation on-success)))
|
||||
(promesa/catch (fn [error]
|
||||
(log/error "Failed to accept terms" {:error error}))))))
|
@ -4,10 +4,12 @@
|
||||
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[native-module.core :as native-module]
|
||||
[status-im.config :as config]
|
||||
[status-im.contexts.profile.data-store :as profile.data-store]
|
||||
[status-im.contexts.profile.edit.accent-colour.events]
|
||||
[status-im.contexts.profile.edit.bio.events]
|
||||
[status-im.contexts.profile.edit.header.events]
|
||||
[status-im.contexts.profile.edit.name.events]
|
||||
status-im.contexts.profile.effects
|
||||
status-im.contexts.profile.login.events
|
||||
[status-im.contexts.profile.rpc :as profile.rpc]
|
||||
[utils.re-frame :as rf]))
|
||||
@ -42,26 +44,27 @@
|
||||
(rf/reg-event-fx
|
||||
:profile/get-profiles-overview-success
|
||||
(fn [{:keys [db]} [{:keys [accounts] {:keys [userConfirmed enabled]} :centralizedMetricsInfo}]]
|
||||
(let [db-with-settings (assoc db
|
||||
:centralized-metrics/user-confirmed? userConfirmed
|
||||
:centralized-metrics/enabled? enabled)]
|
||||
(if (seq accounts)
|
||||
(let [profiles (reduce-profiles accounts)
|
||||
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))]
|
||||
{:db (if key-uid
|
||||
(-> db-with-settings
|
||||
(assoc :profile/profiles-overview profiles)
|
||||
(update :profile/login #(select-profile % key-uid)))
|
||||
db-with-settings)
|
||||
:fx [[:dispatch [:update-theme-and-init-root :screen/profile.profiles]]
|
||||
(when (and key-uid userConfirmed)
|
||||
[:effects.biometric/check-if-available
|
||||
{:key-uid key-uid
|
||||
:on-success (fn [auth-method]
|
||||
(rf/dispatch [:profile.login/check-biometric-success key-uid
|
||||
auth-method]))}])]})
|
||||
{:db db-with-settings
|
||||
:fx [[:dispatch [:update-theme-and-init-root :screen/onboarding.intro]]]}))))
|
||||
(let [db-with-settings (assoc db
|
||||
:centralized-metrics/user-confirmed? userConfirmed
|
||||
:centralized-metrics/enabled? enabled)
|
||||
profiles (reduce-profiles accounts)
|
||||
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))
|
||||
new-db (cond-> db-with-settings
|
||||
(seq profiles)
|
||||
(assoc :profile/profiles-overview profiles)
|
||||
|
||||
key-uid
|
||||
(update :profile/login #(select-profile % key-uid)))]
|
||||
{:db new-db
|
||||
:fx (if (profile.data-store/accepted-terms? accounts)
|
||||
[[:dispatch [:update-theme-and-init-root :screen/profile.profiles]]
|
||||
(when (and key-uid userConfirmed)
|
||||
[:effects.biometric/check-if-available
|
||||
{:key-uid key-uid
|
||||
:on-success (fn [auth-method]
|
||||
(rf/dispatch [:profile.login/check-biometric-success key-uid
|
||||
auth-method]))}])]
|
||||
[[:dispatch [:update-theme-and-init-root :screen/onboarding.intro]]])})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:profile/update-setting-from-backup
|
||||
@ -82,3 +85,8 @@
|
||||
:messages-from-contacts-only
|
||||
(not (get-in db [:profile/profile :messages-from-contacts-only]))
|
||||
{})))
|
||||
|
||||
(rf/reg-event-fx :profile/explore-new-status
|
||||
(fn []
|
||||
{:fx [[:effects.profile/accept-terms
|
||||
{:on-success [:navigate-to :screen/profile.profiles]}]]}))
|
||||
|
@ -9,6 +9,7 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.pixel-ratio :as pixel-ratio]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.profile.data-store :as profile.data-store]
|
||||
[status-im.contexts.profile.utils :as profile.utils]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
@ -18,6 +19,18 @@
|
||||
(fn [{:keys [customization-color]}]
|
||||
(or customization-color constants/profile-default-color)))
|
||||
|
||||
(re-frame/reg-sub :profile/accepted-terms?
|
||||
:<- [:profile/profile]
|
||||
(fn [{:keys [hasAcceptedTerms]}]
|
||||
hasAcceptedTerms))
|
||||
|
||||
;; A profile can only be created without accepting terms in Status v1.
|
||||
(re-frame/reg-sub :profile/from-status-v1-without-terms-accepted?
|
||||
:<- [:profile/profiles-overview]
|
||||
(fn [profiles-overview]
|
||||
(and (seq profiles-overview)
|
||||
(not (profile.data-store/accepted-terms? (vals profiles-overview))))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:profile/currency
|
||||
(fn []
|
||||
|
@ -70,6 +70,9 @@
|
||||
{:initializeApplication
|
||||
(fn [request callback]
|
||||
(callback (.initializeApplication native-status request)))
|
||||
:acceptTerms
|
||||
(fn [callback]
|
||||
(callback (.acceptTerms native-status)))
|
||||
:createAccountAndLogin
|
||||
(fn [request] (.createAccountAndLogin native-status request))
|
||||
:restoreAccountAndLogin
|
||||
|
@ -4,6 +4,6 @@
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "release/0.182.x",
|
||||
"commit-sha1": "4a18c85c3c1d58ea6c8493c46bfd2ed5772b1386",
|
||||
"src-sha256": "04fgykwk44r7f16bfxlkpl9kgnl7yssfyycqnddwszinnnlnfmpl"
|
||||
"commit-sha1": "14c996158cf1d651d44808d51686fdbfb2eb3b39",
|
||||
"src-sha256": "02dnz3327kz8cnhqp6cgcmvqvhcdc941iic7jz2v35hck221vbkg"
|
||||
}
|
||||
|
@ -986,6 +986,7 @@
|
||||
"expand-all": "Expand all",
|
||||
"experienced-web3": "Experienced in Web3?",
|
||||
"explore-the-decentralized-web": "Explore and interact with the decentralized web",
|
||||
"explore-the-new-status": "Explore the new Status",
|
||||
"export-account": "Export account",
|
||||
"export-key": "Export private key",
|
||||
"external-link": "External link",
|
||||
|
Loading…
x
Reference in New Issue
Block a user