diff --git a/.env b/.env index 6ff5e710bf..2b9812ffbb 100644 --- a/.env +++ b/.env @@ -28,3 +28,4 @@ MAX_IMAGES_BATCH=5 APN_TOPIC=im.status.ethereum.pr COMMUNITIES_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1 +METRICS_ENABLED=0 \ No newline at end of file diff --git a/.env.e2e b/.env.e2e index f4f7d0916f..3e6179f7c2 100644 --- a/.env.e2e +++ b/.env.e2e @@ -28,3 +28,4 @@ APN_TOPIC=im.status.ethereum.pr VERIFY_TRANSACTION_CHAIN_ID=3 COMMUNITIES_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1 +METRICS_ENABLED=0 \ No newline at end of file diff --git a/.env.jenkins b/.env.jenkins index 9d04d16308..1d461a17d2 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -28,3 +28,4 @@ MAX_IMAGES_BATCH=5 GOOGLE_FREE=0 COMMUNITIES_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1 +METRICS_ENABLED=0 \ No newline at end of file diff --git a/.env.nightly b/.env.nightly index 6e9693b1c8..3d1d3ced0c 100644 --- a/.env.nightly +++ b/.env.nightly @@ -22,3 +22,4 @@ MAX_IMAGES_BATCH=5 BLANK_PREVIEW=0 COMMUNITIES_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1 +METRICS_ENABLED=0 \ No newline at end of file diff --git a/.env.release b/.env.release index be3001d2dd..7e878eca47 100644 --- a/.env.release +++ b/.env.release @@ -18,3 +18,4 @@ PARTITIONED_TOPIC=0 ENABLE_ROOT_ALERT=1 MAX_IMAGES_BATCH=1 ENABLE_REFERRAL_INVITE=0 +METRICS_ENABLED=0 \ No newline at end of file diff --git a/resources/images/ui/graph@2x.png b/resources/images/ui/graph@2x.png new file mode 100644 index 0000000000..d1da4b4aeb Binary files /dev/null and b/resources/images/ui/graph@2x.png differ diff --git a/resources/images/ui/graph@3x.png b/resources/images/ui/graph@3x.png new file mode 100644 index 0000000000..f80b6ceeed Binary files /dev/null and b/resources/images/ui/graph@3x.png differ diff --git a/src/status_im/anon_metrics/core.cljs b/src/status_im/anon_metrics/core.cljs index bf537d111f..3772b4fa5a 100644 --- a/src/status_im/anon_metrics/core.cljs +++ b/src/status_im/anon_metrics/core.cljs @@ -1,12 +1,15 @@ (ns status-im.anon-metrics.core - (:require [status-im.ethereum.json-rpc :as json-rpc] - [taoensso.timbre :as log] + (:require [taoensso.timbre :as log] [re-frame.core :as re-frame] [re-frame.interceptor :refer [->interceptor]] + [status-im.async-storage.core :as async-storage] + [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.utils.async :refer [async-periodic-exec async-periodic-stop!]] + [status-im.ethereum.json-rpc :as json-rpc] [status-im.utils.platform :as platform] [status-im.utils.build :as build] [status-im.utils.fx :as fx] + [status-im.utils.config :as config] [status-im.anon-metrics.transformers :as txf])) (defonce events-foyer (atom [])) @@ -20,13 +23,13 @@ (when (seq outstanding-events) (reset! events-foyer []) (json-rpc/call - {:method "appmetrics_saveAppMetrics" - :params [outstanding-events] + {:method "appmetrics_saveAppMetrics" + :params [outstanding-events] :on-success #() - :on-error (fn [err] - (log/error {:error err - :events outstanding-events}) - (log/warn "All outstanding events will be rejected"))})))) + :on-error (fn [err] + (log/error {:error err + :events outstanding-events}) + (log/warn "The logged events will be rejected"))})))) (re-frame/reg-fx ::transfer-data @@ -42,11 +45,10 @@ (async-periodic-exec onboard-events 4000 5000))) (do (log/info "[anon-metrics] Stop collection service") - (async-periodic-stop! @periodic-tasks-chan) - (reset! periodic-tasks-chan nil) - - ;; final onboard, will save and clear any pending events - (onboard-events))))) + (when @periodic-tasks-chan + (async-periodic-stop! @periodic-tasks-chan) + (onboard-events) ; final onboard, will save and clear any pending events + (reset! periodic-tasks-chan nil)))))) (fx/defn start-transferring [_] @@ -57,14 +59,25 @@ {::transfer-data false}) (defn transform-and-log [context] - (when-let [transformed-payload (txf/transform context)] - (swap! - events-foyer - conj - {:event (-> context :coeffects :event first) - :value transformed-payload - :app_version build/version - :os platform/os}))) + (let [transformed-payload (txf/transform context) + should-send? (get-in context + [:coeffects + :db + :multiaccount + :anon-metrics/should-send?] + false)] + (when (and config/metrics-enabled? ;metrics are enabled + should-send? ; user opted-in + transformed-payload); we were able to transform metrics-shape + (swap! + events-foyer + conj + {:event (-> context :coeffects :event first) + :value transformed-payload + :app_version build/version + :os platform/os})))) + +(re-frame/reg-fx ::transform-and-log transform-and-log) (defn catch-events-before [context] (transform-and-log context) @@ -75,8 +88,6 @@ :id :catch-events :before catch-events-before)) -(re-frame/reg-fx ::transform-and-log transform-and-log) - (fx/defn hoax-capture-event "Due to usage of fx/defn with fx/merge, it might not be able to intercept some events (like navigate-to-cofx). In cases like that, @@ -86,6 +97,79 @@ ;; re-shape event to look like a context object {::transform-and-log {:coeffects {:event og-event}}}) +(fx/defn fetch-local-metrics-success + {:events [::fetch-local-metrics-success]} + [{:keys [db]} {:keys [metrics total-count clear-existing?]}] + {:db (-> db + (as-> db + (if clear-existing? + (assoc db + :anon-metrics/events metrics + :anon-metrics/total-count total-count) + (update db :anon-metrics/events concat metrics))) + (dissoc :anon-metrics/fetching?))}) + +(fx/defn fetch-local-metrics + {:events [::fetch-local-metrics]} + [{:keys [db]} {:keys [limit offset clear-existing?]}] + {::json-rpc/call [{:method "appmetrics_getAppMetrics" + :params [(or limit 3) (or offset 0)] + :on-success #(re-frame/dispatch + [::fetch-local-metrics-success + {:metrics (get % :AppMetrics []) + :total-count (get % :TotalCount 0) + :clear-existing? clear-existing?}])}] + :db (assoc db :anon-metrics/fetching? true)}) + +(fx/defn set-opt-in-screen-displayed-flag + [{:keys [db]}] + {::async-storage/set! {:anon-metrics/opt-in-screen-displayed? true} + :db (assoc db :anon-metrics/opt-in-screen-displayed? true)}) + +(fx/defn set-show-thank-you + {:events [::set-show-thank-you]} + [{:keys [db]} show?] + {:db (assoc db :anon-metrics/show-thank-you? show?)}) + +(defn on-opt-in-success [enabled?] + (if enabled? + (do + ;; animate + (re-frame/dispatch [::set-show-thank-you true]) + ;; and redirect after 1 second + (js/setTimeout + (fn [] + (re-frame/dispatch [:navigate-reset {:index 0 + :routes [{:name :tabs}]}]) + (re-frame/dispatch [::set-show-thank-you false])) + 1000)) + (re-frame/dispatch [:navigate-reset {:index 0 + :routes [{:name :tabs}]}]))) + +(fx/defn fetch-opt-in-displayed-success + {:events [::fetch-opt-in-displayed-success]} + [{:keys [db]} [k v]] + {:db (assoc db k v)}) + +(fx/defn fetch-opt-in-screen-displayed? + {:events [::fetch-opt-in-screen-displayed?]} + [cofx] + {::async-storage/get + {:keys [:anon-metrics/opt-in-screen-displayed?] + :cb #(re-frame/dispatch [::fetch-opt-in-displayed-success (first %)])}}) + +(fx/defn opt-in + {:events [::opt-in]} + [cofx enabled?] + (fx/merge cofx + (set-opt-in-screen-displayed-flag) + (if enabled? + (start-transferring) + (stop-transferring)) + (multiaccounts.update/multiaccount-update + :anon-metrics/should-send? enabled? + {:on-success #(on-opt-in-success enabled?)}))) + (comment ;; read the database (def events-in-db (atom nil)) @@ -95,3 +179,4 @@ (json-rpc/call {:method "appmetrics_getAppMetrics" :params [1000 0] ; limit, offset :on-success #(reset! events-in-db %)})) + diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 109845a807..07f9e8b7b8 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -145,3 +145,6 @@ (def ^:const three-days (* one-day 3)) (def ^:const one-week (* one-day 7)) (def ^:const one-month (* one-day 31)) + +(def ^:const metrics-github-link + "https://github.com/status-im/status-go/blob/develop/_docs/app-metrics.md") diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index 703faa9843..e08d4e39cc 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -2,6 +2,7 @@ (:require [clojure.string :as string] [re-frame.core :as re-frame] [status-im.multiaccounts.login.core :as multiaccounts.login] + [status-im.anon-metrics.core :as anon-metrics] [status-im.native-module.core :as status] [status-im.network.net-info :as network] [status-im.db :refer [app-db]] @@ -53,7 +54,8 @@ (assoc :multiaccounts/loading false)) ;; NOTE: Try to dispatch later navigation because of that https://github.com/react-navigation/react-navigation/issues/6879 :dispatch-later [{:dispatch [::initialize-view {:logout? logout?}] - :ms 100}]}))) + :ms 100}] + :dispatch [::anon-metrics/fetch-opt-in-screen-displayed?]}))) (fx/defn start-app {:events [:init/app-started]} diff --git a/src/status_im/multiaccounts/core.cljs b/src/status_im/multiaccounts/core.cljs index a7d6c1fdd2..17f0cb1734 100644 --- a/src/status_im/multiaccounts/core.cljs +++ b/src/status_im/multiaccounts/core.cljs @@ -3,6 +3,7 @@ [status-im.ethereum.stateofus :as stateofus] [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.anon-metrics.core :as anon-metrics] [status-im.native-module.core :as native-module] [status-im.ethereum.json-rpc :as json-rpc] [status-im.utils.fx :as fx] @@ -138,6 +139,17 @@ :default-sync-period value {})) +(fx/defn switch-share-anonymous-usage-data + {:events [:multiaccounts.ui/share-anonymous-usage-data-switched]} + [cofx opted-in?] + (fx/merge cofx + (multiaccounts.update/multiaccount-update + :anon-metrics/should-send? (boolean opted-in?) + {}) + (if opted-in? + (anon-metrics/start-transferring) + (anon-metrics/stop-transferring)))) + (fx/defn switch-preview-privacy-mode-flag [{:keys [db]}] (let [private? (get-in db [:multiaccount :preview-privacy?])] diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 5a8ff86840..9660b25f4e 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -350,12 +350,14 @@ (let [{:keys [key-uid password save-password? creating?]} (:multiaccounts/login db) - multiaccounts (:multiaccounts/multiaccounts db) - recovered-account? (get db :recovered-account?) - login-only? (not (or creating? - recovered-account? - (keycard-setup? cofx))) - nodes nil] + multiaccounts (:multiaccounts/multiaccounts db) + recovered-account? (get db :recovered-account?) + login-only? (not (or creating? + recovered-account? + (keycard-setup? cofx))) + nodes nil + should-send-metrics? (get-in db [:multiaccount :anon-metrics/should-send?]) + opt-in-screen-displayed? (get db :anon-metrics/opt-in-screen-displayed?)] (log/debug "[multiaccount] multiaccount-login-success" "login-only?" login-only? "recovered-account?" recovered-account?) @@ -373,7 +375,8 @@ [{:method "web3_clientVersion" :on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]} ;; Start tasks to save usage data locally - (anon-metrics/start-transferring) + (when should-send-metrics? + (anon-metrics/start-transferring)) ;;FIXME (when nodes (fleet/set-nodes :eth.contract nodes)) @@ -382,7 +385,11 @@ (wallet/set-initial-blocks-range)) (if login-only? (login-only-events key-uid password save-password?) - (create-only-events))))) + (create-only-events)) + (when (and login-only? + (not opt-in-screen-displayed?) + config/metrics-enabled?) + (navigation/navigate-to :anon-metrics-opt-in {}))))) ;; FIXME(Ferossgp): We should not copy keys as we denormalize the database, ;; this create desync between actual accounts and the one on login causing broken state @@ -514,8 +521,10 @@ (fx/merge cofx (when first-account? (acquisition/create)) - (navigation/navigate-reset {:index 0 - :routes [{:name :tabs}]})))) + (if config/metrics-enabled? + (navigation/navigate-to :anon-metrics-opt-in {}) + (navigation/navigate-reset {:index 0 + :routes [{:name :tabs}]}))))) (fx/defn multiaccount-selected {:events [:multiaccounts.login.ui/multiaccount-selected]} diff --git a/src/status_im/multiaccounts/logout/core.cljs b/src/status_im/multiaccounts/logout/core.cljs index 87fff71b9d..a7fef36fdd 100644 --- a/src/status_im/multiaccounts/logout/core.cljs +++ b/src/status_im/multiaccounts/logout/core.cljs @@ -13,15 +13,17 @@ (fx/defn logout-method [{:keys [db] :as cofx} {:keys [auth-method logout?]}] - (let [key-uid (get-in db [:multiaccount :key-uid])] + (let [key-uid (get-in db [:multiaccount :key-uid]) + should-send-metrics? (get-in db [:multiaccount :anon-metrics/should-send?])] (fx/merge cofx {::logout nil ::multiaccounts/webview-debug-changed false - ::disable-local-notifications nil + ::disable-local-notifications nil :keychain/clear-user-password key-uid ::init/open-multiaccounts #(re-frame/dispatch [::init/initialize-multiaccounts % {:logout? logout?}])} (notifications/logout-disable) - (anon-metrics/stop-transferring) + (when should-send-metrics? + (anon-metrics/stop-transferring)) (keychain/save-auth-method key-uid auth-method) (transport/stop-whisper) (wallet/clear-timeouts) diff --git a/src/status_im/navigation.cljs b/src/status_im/navigation.cljs index 5a140324e2..c605877e27 100644 --- a/src/status_im/navigation.cljs +++ b/src/status_im/navigation.cljs @@ -47,7 +47,7 @@ ::anon-metrics/transform-and-log {:coeffects {:event [:navigate-to go-to-view-id screen-params]}}}) (fx/defn navigate-to - {:events [:navigate-to]} + {:events [:navigate-to]} [cofx go-to-view-id screen-params] (navigate-to-cofx cofx go-to-view-id screen-params)) diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index 551b79bcb5..140516cb78 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -42,7 +42,8 @@ :theme-dark (js/require "../resources/images/ui/theme-dark.png") :theme-light (js/require "../resources/images/ui/theme-light.png") :theme-system (js/require "../resources/images/ui/theme-system.png") - :notifications (js/require "../resources/images/ui/notifications.png")}) + :notifications (js/require "../resources/images/ui/notifications.png") + :graph (js/require "../resources/images/ui/graph.png")}) (defn get-theme-image [k] (get ui (when (colors/dark?) (keyword (str (name k) "-dark"))) (get ui k))) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 62394d6f9c..57f59a2ae7 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -220,6 +220,15 @@ (reg-root-key-sub :activity.center/notifications :activity.center/notifications) (reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count) +;; anon. metrics +(reg-root-key-sub :anon-metrics/opt-in-screen-displayed? :anon-metrics/opt-in-screen-displayed?) +(reg-root-key-sub :anon-metrics/show-thank-you? :anon-metrics/show-thank-you?) +(reg-root-key-sub :anon-metrics/events :anon-metrics/events) +(reg-root-key-sub :anon-metrics/total-count :anon-metrics/total-count) +(reg-root-key-sub :anon-metrics/fetching? :anon-metrics/fetching?) +(reg-root-key-sub :anon-metrics/data-visible? :anon-metrics/data-visible?) +(reg-root-key-sub :anon-metrics/learn-more-visible? :anon-metrics/learn-more-visible?) + (re-frame/reg-sub :communities :<- [:raw-communities] diff --git a/src/status_im/ui/components/accordion.cljs b/src/status_im/ui/components/accordion.cljs index fe71dcd05f..34c9cf2c8f 100644 --- a/src/status_im/ui/components/accordion.cljs +++ b/src/status_im/ui/components/accordion.cljs @@ -5,31 +5,32 @@ [status-im.ui.components.react :as react] [status-im.ui.components.icons.icons :as icons])) +(defn drop-down-icon [opened?] + [react/view {:flex-direction :row :align-items :center} + [icons/icon (if opened? :main-icons/dropdown-up :main-icons/dropdown) + {:container-style {:align-items :center + :margin-left 8 + :justify-content :center} + :resize-mode :center + :color colors/black}]]) + (defn section "Render collapsible section" [_props] (let [opened? (reagent/atom false)] - (fn [{:keys [title cnt content icon]}] + (fn [{:keys [title content icon]}] [react/view {:padding-vertical 8} - [quo/list-item - {:title title - :icon icon - :on-press #(swap! opened? not) - :accessory - [react/view {:flex-direction :row :align-items :center} - (when (pos? cnt) - [react/text {:style {:color colors/gray}} cnt]) - [icons/icon (if @opened? :main-icons/dropdown-up :main-icons/dropdown) - {:container-style {:align-items :center - :margin-left 8 - :justify-content :center} - :resize-mode :center - :color colors/black}]]}] + (if (string? title) + [quo/list-item + {:title title + :icon icon + :on-press #(swap! opened? not) + :accessory [drop-down-icon @opened?]}] + [react/touchable-opacity {:on-press #(swap! opened? not)} + [react/view {:flex-direction :row + :justify-content :space-between} + title + [drop-down-icon @opened?]]]) (when @opened? content)]))) -(defn accordion - "List of collapseable sections" - []) - ;; TODO(shivekkhurana): Extract status-im.ui.screens.wallet.recipient.views/accordion component here - diff --git a/src/status_im/ui/components/invite/views.cljs b/src/status_im/ui/components/invite/views.cljs index 387cddd225..435cff2319 100644 --- a/src/status_im/ui/components/invite/views.cljs +++ b/src/status_im/ui/components/invite/views.cljs @@ -105,7 +105,7 @@ [rn/view {:style (styles/invite-instructions)} [rn/view {:style (styles/invite-instructions-title)} [quo/text {:color :secondary} - (i18n/label :t/invite-instruction)]] + (i18n/label :t/how-it-works)]] [rn/view {:style (styles/invite-warning)} [quo/text (i18n/label :t/invite-warning)]] [rn/view {:style (styles/invite-instructions-content)} diff --git a/src/status_im/ui/components/status_bar/view.cljs b/src/status_im/ui/components/status_bar/view.cljs index 81ed3e2db1..3ab0e569f8 100644 --- a/src/status_im/ui/components/status_bar/view.cljs +++ b/src/status_im/ui/components/status_bar/view.cljs @@ -3,11 +3,15 @@ [status-im.ui.components.status-bar.styles :as styles] [status-im.utils.platform :as platform])) -(def route->bar-type (merge {:qr-scanner {:type :black} +(def route->bar-type (merge {:qr-scanner {:type :black} :image-preview {:type :black}} (when platform/ios? - {:new-chat {:type :black} - :new-public-chat {:type :black}}))) + {:new-chat {:type :black} + :contact-toggle-list {:type :black} + :new-group {:type :black} + :anon-metrics-view-data {:type :black} + :anon-metrics-learn-more {:type :black} + :new-public-chat {:type :black}}))) ;; TODO: Integrate into navigation (defn get-config [view-id] diff --git a/src/status_im/ui/screens/anonymous_metrics_settings/views.cljs b/src/status_im/ui/screens/anonymous_metrics_settings/views.cljs new file mode 100644 index 0000000000..edc86e6b8e --- /dev/null +++ b/src/status_im/ui/screens/anonymous_metrics_settings/views.cljs @@ -0,0 +1,301 @@ +(ns status-im.ui.screens.anonymous-metrics-settings.views + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [re-frame.core :as re-frame] + [status-im.ui.components.animation :as animation] + [status-im.react-native.resources :as resources] + [status-im.anon-metrics.core :as anon-metrics] + [status-im.ui.components.accordion :as accordion] + [status-im.ui.components.react :as react] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.icons.icons :as icons] + [status-im.i18n.i18n :as i18n] + [status-im.constants :refer [metrics-github-link]] + [quo.design-system.spacing :as spacing] + [status-im.ui.components.topbar :as topbar] + [quo.core :as quo])) + +(defn graphic-and-desc [{:keys [show-title?]}] + [:<> + [react/view {:align-items :center + :margin-vertical 25} + [react/image {:source (resources/get-image :graph)}]] + (when show-title? + [quo/text {:size :x-large + :weight :bold + :align :center + :style {:margin-bottom 16}} + (i18n/label :t/help-improve-status)]) + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:navigate-to :anon-metrics-learn-more]) + :accessibility-label :learn-more-anchor} + [react/nested-text + {:style (merge {:color colors/gray + :text-align :center} + (:large spacing/padding-horizontal))} + (i18n/label :t/anonymous-usage-data-subtitle) + [{:style {:color colors/blue + ;; :font-weight key doesn't work + :fontWeight :bold}} + (str " " (i18n/label :t/learn-more))]]]]) + +(defn setting-switch [enabled? on-press] + [quo/list-item {:title (i18n/label :t/share-anonymous-usage-data) + :active enabled? + :accessibility-label :opt-in-switch + :accessory :switch + :subtitle-max-lines 2 + :on-press on-press}]) + +(defn icon-list-item + ([icon label] + (icon-list-item icon {} label)) + ([icon icon-opts label] + [react/view {:flex-direction :row + :margin-horizontal (:base spacing/spacing) + :margin-vertical (:tiny spacing/spacing)} + [icons/icon icon (into icon-opts + {:container-style {:margin-top 1.2 + :margin-right (:tiny spacing/spacing)}})] + [react/view {:style {:padding-right (:xx-large spacing/spacing)}} + (if (string? label) + [react/text label] + label)]])) + +(defn what-is-shared [] + [:<> + [quo/list-header (i18n/label :t/what-is-shared)] + (for [label [(i18n/label :t/anon-metrics-your-interactions) + (i18n/label :t/anon-metrics-bg-activity) + (i18n/label :t/anon-metrics-settings-and-prefs)]] + ^{:key label} + [icon-list-item :main-icons/info {:color colors/blue} label])]) + +(defn event-item [event] + [accordion/section + {:title [react/view {:flex 1 + :flex-direction :row + :margin-bottom (:base spacing/spacing)} + [react/view {:style {:padding-right (:base spacing/spacing)}} + (for [label ["event" "time" "os"]] + ^{:key label} + [react/text {:style {:color colors/gray}} + label])] + + [react/view + [react/text (:event event)] + [react/text (:created_at event)] + [react/text (:os event)]]] + :content [react/view {:style {:background-color colors/gray-lighter + :padding (:small spacing/spacing) + :border-radius 14}} + [react/text (:value event)]]}]) + +(defn data-sheet-header-and-desc [] + [:<> + [quo/text {:size :x-large + :weight :bold + :align :center + :style {:margin-vertical 16}} + (i18n/label :t/data-collected)] + [react/touchable-highlight + {:on-press #(.openURL ^js react/linking metrics-github-link)} + [react/nested-text + {:style (:base spacing/padding-horizontal)} + (i18n/label :t/data-collected-subtitle) + [{:style {:color colors/blue}} + (str " " (i18n/label :t/view-rules))]]]]) + +(defview view-data [] + (letsubs [events [:anon-metrics/events] + total [:anon-metrics/total-count] + fetching? [:anon-metrics/fetching?]] + {:component-did-mount #(re-frame/dispatch [::anon-metrics/fetch-local-metrics + {:clear-existing? true + :limit 10 + :offset 0}])} + [:<> + [topbar/topbar {:modal? true + :border-bottom false}] + [react/scroll-view + [data-sheet-header-and-desc] + [quo/list-header (i18n/label :t/count-metrics-collected {:count total})] + (when (pos? total) + [react/view + {:style (merge + {:border-width 1 + :border-radius 8 + :border-color colors/gray-lighter + :padding-top 16 + :padding-bottom 20 + :margin 16} + (:base spacing/padding-horizontal))} + (doall + (map-indexed + (fn [index event] + ^{:key index} + [event-item event]) + events)) + (if fetching? + [react/activity-indicator {:size :large + :animating true}] + [quo/button {:type :primary + :disabled (= (count events) total) + :accessibility-label :show-more-button + :on-press #(re-frame/dispatch [::anon-metrics/fetch-local-metrics + {:clear-existing? false + :limit 10 + :offset (count events)}])} + (i18n/label :t/show-more)])])]])) + +(defn view-data-button [] + [react/view {:flex 1 + :align-items :center + :style {:margin-top (:x-large spacing/spacing)}} + [quo/button {:type :primary + :theme :main + :accessibility-label :view-data-button + :on-press #(re-frame/dispatch [:navigate-to :anon-metrics-view-data])} + (i18n/label :t/view-data)]]) + +(defn desc-point-with-link [desc link-text link-href accessibility-label] + [react/touchable-highlight {:on-press #(.openURL ^js react/linking link-href) + :accessibility-label accessibility-label} + [react/view + [react/text desc] + [react/view {:flex-direction :row} + [react/text {:style {:text-align :center + :color colors/blue}} + link-text] + [icons/tiny-icon :tiny-icons/tiny-external + {:color colors/blue + :container-style {:margin-left 4 + :margin-top 4}}]]]]) + +(defn learn-more [] + [:<> + [topbar/topbar {:modal? true + :border-bottom false}] + [react/scroll-view + [quo/text {:size :x-large + :weight :bold + :align :center + :style {:margin-top 16}} + (i18n/label :t/about-sharing-data)] + [react/text {:style (merge + (:base spacing/padding-horizontal) + (:small spacing/padding-vertical))} + (i18n/label :t/about-sharing-data-subtitle)] + [what-is-shared] + [view-data-button] + [quo/separator {:style {:margin-vertical (:base spacing/spacing)}}] + [quo/list-header (i18n/label :t/how-it-works)] + (for [label [[desc-point-with-link + (i18n/label :t/sharing-data-desc-1) + (i18n/label :t/view-rules) + metrics-github-link + :view-rules-anchor] + (i18n/label :t/sharing-data-desc-2) + (i18n/label :t/sharing-data-desc-3) + (i18n/label :t/sharing-data-desc-4) + ;; TODO (shivekkhurana): Enable this link when the public + ;; dashboard is live + ;; [desc-point-with-link + ;; (i18n/label :t/sharing-data-desc-5) + ;; (i18n/label :t/view-public-dashboard) + ;; "https://github.com/" + ;; :view-dashboad-anchor] + (i18n/label :t/sharing-data-desc-6)]] + ^{:key label} + [icon-list-item :main-icons/arrow-right {:color colors/blue} label]) + [react/view {:style {:margin-bottom (:xx-large spacing/spacing)}}]]]) + +(defview settings [] + (letsubs [{:keys [:anon-metrics/should-send?]} [:multiaccount]] + [:<> + [topbar/topbar {:title (i18n/label :t/anonymous-usage-data)}] + [react/scroll-view {:flex 1} + [graphic-and-desc] + [setting-switch + should-send? + #(re-frame/dispatch [:multiaccounts.ui/share-anonymous-usage-data-switched (not should-send?)])] + [quo/separator] + [what-is-shared] + [react/view {:padding-bottom 80} + [view-data-button]]]])) + +(defn allow-or-not-actions [] + [react/view {:align-items :center + :style {:padding-bottom 80}} + [quo/button {:type :primary + :theme :main + :accessibility-label :opt-in-button + :on-press #(re-frame/dispatch [::anon-metrics/opt-in true])} + (i18n/label :t/allow-and-send)] + [react/view {:style {:margin-top (:base spacing/spacing)}}] + [quo/button {:type :primary + :theme :main + :accessibility-label :opt-out-button + :on-press #(re-frame/dispatch [::anon-metrics/opt-in false])} + (i18n/label :t/no-thanks)]]) + +(defn thank-you-animation [overlay-opacity box-opacity] + (fn [] + (animation/start + (animation/parallel + [(animation/spring + overlay-opacity + {:toValue 0.72 + :useNativeDriver true}) + (animation/spring + box-opacity + {:toValue 1 + :useNativeDriver true})])))) + +(defview floating-thank-you [] + (letsubs [overlay-opacity (animation/create-value 1) + box-opacity (animation/create-value 0.2)] + {:component-did-mount (thank-you-animation overlay-opacity box-opacity)} + [:<> + [react/animated-view ; thank you container + {:style {:position :absolute + :opacity box-opacity + :z-index 2 + :top "40%" + :padding (:x-large spacing/spacing) + :background-color colors/white + :align-items :center + :align-self :center}} + [react/text {:style {:font-size 32}} "\uD83C\uDF89"] + [quo/text {:size :x-large + :weight :bold} + (i18n/label :t/thank-you)]] + [react/animated-view ; translucent overlay + {:style {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :z-index 1 + :background-color colors/white + :opacity overlay-opacity}}]])) + +(defview new-account-opt-in [] + (letsubs [show-thank-you? [:anon-metrics/show-thank-you?]] + [react/scroll-view {:flex 1 + :padding-vertical (:large spacing/spacing) + :flex-direction :column} + (when show-thank-you? [floating-thank-you]) + [graphic-and-desc {:show-title? true}] + [react/view {:style {:margin-bottom (:small spacing/spacing) + :margin-top (:large spacing/spacing)}} + [quo/separator]] + [what-is-shared] + [react/view {:style (:large spacing/padding-vertical)} + [quo/separator]] + [allow-or-not-actions]])) + +(comment + (re-frame/dispatch [:navigate-back]) + (re-frame/dispatch [:navigate-to :metrics-learn-more]) + (re-frame/dispatch [:navigate-to :metrics-view-data]) + (re-frame/dispatch [:navigate-to :anon-metrics-opt-in {}])) diff --git a/src/status_im/ui/screens/intro/views.cljs b/src/status_im/ui/screens/intro/views.cljs index 124b27f5be..cf41549d2e 100644 --- a/src/status_im/ui/screens/intro/views.cljs +++ b/src/status_im/ui/screens/intro/views.cljs @@ -24,8 +24,8 @@ (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defview intro [] - (letsubs [{window-height :height} [:dimensions/window] - view-id [:view-id]] + (letsubs [{window-height :height} [:dimensions/window] + view-id [:view-id]] [react/view {:style styles/intro-view} [carousel/viewer [{:image (resources/get-theme-image :chat) :title :intro-title1 diff --git a/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs b/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs index d8684c0256..f01135e236 100644 --- a/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs @@ -61,7 +61,6 @@ [accordion/section {:title name :icon [chat-icon.screen/contact-icon-contacts-tab (multiaccounts/displayed-photo multiaccount)] - :count 0 :content [accordion-content]}] [react/view {:flex 1 :flex-direction :column diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index 7fba3bf636..cdcd1b35f9 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -5,6 +5,7 @@ [status-im.ui.components.colors :as colors] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.react :as react] + [status-im.utils.config :as config] [status-im.multiaccounts.biometric.core :as biometric] [status-im.ui.components.topbar :as topbar] [status-im.utils.platform :as platform]) @@ -65,30 +66,36 @@ :on-press #(re-frame/dispatch [:multiaccounts.ui/preview-privacy-mode-switched ((complement boolean) preview-privacy?)])}] - [quo/list-item {:size :small - :title (i18n/label :t/chat-link-previews) - :chevron true - :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) - :accessibility-label :chat-link-previews}] - [quo/list-item {:size :small - :title (i18n/label :t/accept-new-chats-from) - :chevron true - :accessory :text - :accessory-text (i18n/label (if messages-from-contacts-only - :t/contacts - :t/anyone)) - :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) - :accessibility-label :accept-new-chats-from}] + [quo/list-item {:size :small + :title (i18n/label :t/chat-link-previews) + :chevron true + :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) + :accessibility-label :chat-link-previews}] + [quo/list-item {:size :small + :title (i18n/label :t/accept-new-chats-from) + :chevron true + :accessory :text + :accessory-text (i18n/label (if messages-from-contacts-only + :t/contacts + :t/anyone)) + :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) + :accessibility-label :accept-new-chats-from}] + (when config/metrics-enabled? + [quo/list-item {:size :small + :title (i18n/label :t/anonymous-usage-data) + :chevron true + :on-press #(re-frame/dispatch [:navigate-to :anonymous-metrics-settings]) + :accessibility-label :anonymous-usage-data}]) (when platform/android? - [quo/list-item {:size :small - :title (i18n/label :t/webview-camera-permission-requests) - :active webview-allow-permission-requests? - :accessory :switch - :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) - :subtitle-max-lines 2 - :on-press #(re-frame/dispatch - [:multiaccounts.ui/webview-permission-requests-switched - ((complement boolean) webview-allow-permission-requests?)])}]) + [quo/list-item {:size :small + :title (i18n/label :t/webview-camera-permission-requests) + :active webview-allow-permission-requests? + :accessory :switch + :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) + :subtitle-max-lines 2 + :on-press #(re-frame/dispatch + [:multiaccounts.ui/webview-permission-requests-switched + ((complement boolean) webview-allow-permission-requests?)])}]) [separator] [quo/list-item {:size :small diff --git a/src/status_im/ui/screens/routing/main.cljs b/src/status_im/ui/screens/routing/main.cljs index fd677eff3c..04d5a55962 100644 --- a/src/status_im/ui/screens/routing/main.cljs +++ b/src/status_im/ui/screens/routing/main.cljs @@ -6,6 +6,7 @@ [status-im.ui.screens.stickers.views :as stickers] [status-im.ui.screens.home.views :as home] [status-im.ui.screens.add-new.new-chat.views :as new-chat] + [status-im.ui.screens.anonymous-metrics-settings.views :as anon-metrics-settings] [status-im.add-new.core :as new-chat.events] [status-im.ui.screens.routing.intro-login-stack :as intro-login-stack] [status-im.ui.screens.routing.chat-stack :as chat-stack] @@ -80,6 +81,9 @@ {:name :welcome :back-handler :noop :component home/welcome} + {:name :anon-metrics-opt-in + :back-handler :noop + :component anon-metrics-settings/new-account-opt-in} {:name :new-chat :on-focus [::new-chat.events/new-chat-focus] :transition :presentation-ios @@ -171,7 +175,13 @@ :component wallet.buy-crypto/website} {:name :invite-people-community :component communities.invite/invite - :insets {:bottom true}}] + :insets {:bottom true}} + {:name :anon-metrics-learn-more + :transition :presentation-ios + :component anon-metrics-settings/learn-more} + {:name :anon-metrics-view-data + :transition :presentation-ios + :component anon-metrics-settings/view-data}] (when config/quo-preview-enabled? [{:name :quo-preview diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index 11068e1524..f8c003a39e 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -15,6 +15,7 @@ offline-messaging-settings] [status-im.ui.screens.dapps-permissions.views :as dapps-permissions] [status-im.ui.screens.link-previews-settings.views :as link-previews-settings] + [status-im.ui.screens.anonymous-metrics-settings.views :as anonymous-metrics-settings] [status-im.ui.screens.privacy-and-security-settings.views :as privacy-and-security] [status-im.ui.screens.privacy-and-security-settings.messages-from-contacts-only :as messages-from-contacts-only] [status-im.ui.screens.sync-settings.views :as sync-settings] @@ -84,6 +85,8 @@ :component dapps-permissions/dapps-permissions} {:name :link-previews-settings :component link-previews-settings/link-previews-settings} + {:name :anonymous-metrics-settings + :component anonymous-metrics-settings/settings} {:name :privacy-and-security :component privacy-and-security/privacy-and-security} {:name :messages-from-contacts-only diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index 0b5fcb3049..ee9a22f0d2 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -48,6 +48,7 @@ (def communities-enabled? (enabled? (get-config :COMMUNITIES_ENABLED "0"))) (def database-management-enabled? (enabled? (get-config :DATABASE_MANAGEMENT_ENABLED "0"))) (def debug-webview? (enabled? (get-config :DEBUG_WEBVIEW "0"))) +(def metrics-enabled? (enabled? (get-config :METRICS_ENABLED "0"))) ;; CONFIG VALUES (def log-level @@ -80,6 +81,7 @@ :profile-pictures-visibility 1 :log-level log-level :webview-allow-permission-requests? false + :anon-metrics/should-send? false :link-previews-enabled-sites #{} :link-preview-request-enabled true}) diff --git a/translations/ar.json b/translations/ar.json index 6b8e9f9b2c..b69653e4b3 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -768,7 +768,7 @@ "invite-chat-rule": "سيؤدي القبول أيضًا إلى مكافأة صديقك بمكافأة إحالة بالعملة الرقمية", "invite-chat-starter-pack": "حزمة المبتدئين", "invite-friends": "دعوه الأصدقاء", - "invite-instruction": "كيف يعمل", + "how-it-works": "كيف يعمل", "invite-instruction-fifth": "يمكنك اختيار استرداد مكافأة الإحالة الخاصة بك في أي وقت.", "invite-instruction-first": "يمكنك إرسال رابط دعوة فريد إلى صديقك لتنزيل Status والانضمام إلينا", "invite-instruction-fourth": "تتلقى مكافأة الإحالة الخاصة بك وصديقك حزمة المبتدئين", diff --git a/translations/bn.json b/translations/bn.json index f7481cd5e2..dcd5420dc2 100644 --- a/translations/bn.json +++ b/translations/bn.json @@ -645,7 +645,7 @@ "invite-chat-rule": "গ্রহণ করলে আপনার বন্ধুকে ক্রিপ্টো রেফারেল বোনাস প্রদান করা হবে।", "invite-chat-starter-pack": "স্টার্টার প্যাক", "invite-friends": "বন্ধুদের আমন্ত্রণ করুন", - "invite-instruction": "এটা কিভাবে কাজ করে", + "how-it-works": "এটা কিভাবে কাজ করে", "invite-instruction-fifth": "আপনি যে কোন সময় আপনার রেফারেল বোনাস পরিশোধ করতে পারেন।", "invite-instruction-first": "Status ডাউনলোড এবং যোগদান করতে আপনি আপনার বন্ধুকে একটি অনন্য আমন্ত্রণ লিংক পাঠান", "invite-instruction-fourth": "আপনি আপনার রেফারেল বোনাস এবং আপনার বন্ধু স্টার্টার প্যাক পাবেন", diff --git a/translations/de.json b/translations/de.json index 0a888c2b17..0b6edfcf45 100644 --- a/translations/de.json +++ b/translations/de.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Die Annahme belohnt Ihren Freund auch mit einem Krypto-Empfehlungsbonus", "invite-chat-starter-pack": "Starter Pack", "invite-friends": "Freunde einladen", - "invite-instruction": "Wie es funktioniert", + "how-it-works": "Wie es funktioniert", "invite-instruction-fifth": "Du kannst deinen Empfehlungsbonus jederzeit einlösen.", "invite-instruction-first": "Du sendest einem Freund einen eindeutigen Einladungslink zum Herunterladen und nutzen von Status", "invite-instruction-fourth": "Du erhältst einen Empfehlungsbonus und dein Freund das Starter Pack", diff --git a/translations/el.json b/translations/el.json index 2f82a3da7a..cb414f1ad9 100644 --- a/translations/el.json +++ b/translations/el.json @@ -655,7 +655,7 @@ "invite-chat-rule": "Η αποδοχή θα ανταμείψει επίσης το φίλο σας με ένα μπόνους παραπομπής κρυπτογράφησης", "invite-chat-starter-pack": "Πακέτο εκκίνησης", "invite-friends": "Πρόσκληση φίλων", - "invite-instruction": "Πώς λειτουργεί", + "how-it-works": "Πώς λειτουργεί", "invite-instruction-fifth": "Μπορείτε να επιλέξετε να εξαργυρώσετε το μπόνους παραπομπής σας ανά πάσα στιγμή.", "invite-instruction-first": "Στέλνετε έναν μοναδικό σύνδεσμο πρόσκλησης στον φίλο σας για λήψη και συμμετοχή στο Status", "invite-instruction-fourth": "Λαμβάνετε το μπόνους παραπομπής και ο φίλος σας το Πακέτο Εκκίνησης", diff --git a/translations/en.json b/translations/en.json index 66a997dcf9..59021d4ebd 100644 --- a/translations/en.json +++ b/translations/en.json @@ -661,7 +661,7 @@ "invited": "invited", "invite-button": "Invite", "invite-receive-account": "Account to receive your referral bonus", - "invite-instruction": "How it works", + "how-it-works": "How it works", "invite-warning": "This promotion is only valid for users of an Android device, who aren't residents of US. Friend needs to confirm referral within 7 days", "invite-instruction-first": "You send a unique invite link to your friend to download and join Status", "invite-instruction-second": "Your friend downloads Status and creates an account (on Android)", @@ -1535,5 +1535,37 @@ "one-month": "One month", "my-profile": "My profile", "default-sync-period": "Sync history for", - "bip39-password-placeholder": "BIP39 password" + "bip39-password-placeholder": "BIP39 password", + "anonymous-usage-data": "Anonymous usage data", + "anonymous-usage-data-subtitle": "Share anonymous, end-to-end ecrypted data about how you use Status.", + "share-anonymous-usage-data": "Share anonymous usage data", + "what-is-shared": "What is shared", + "anon-metrics-your-interactions": "Your interactions with the app like clicks and screen views", + "anon-metrics-bg-activity": "Background activity and internal processes", + "anon-metrics-settings-and-prefs": "Settings and preferences", + "anon-metrics-data-is-validated": "Data is validated against public validation rules to ensure no sensitive data is sent.", + "anon-metrics-usage-data-sent": "Usage data is sent end-to-end encrypted over Status’ peer-to-peer network", + "anon-metrics-ephemeral-key": "Instead of your regular chat key, a single use key is used", + "anon-metrics-usage-not-linked-to-ip": "Usage data cannot be associated with your IP address", + "anon-metrics-removed-from-phone": "The data is removed from your phone after they are sent", + "anon-metrics-public-dashboard": "Cumulative data of all users is publicaly available View public dashboard", + "view-data": "View data", + "data-collected": "Data collected", + "data-collected-subtitle": "The table below shows the exact data that is stored and will be sent. Data is validated against public rules to ensure no sensitive data is sent. Don’t trust, verify.", + "view-rules": "View rules", + "expand-all": "Expand all", + "about-sharing-data": "About sharing data", + "about-sharing-data-subtitle": "As open-source community project, Status asks you to share anonymous, non-invasive data to improve the app. This is strictly opt-in, and can be changed at any time in Settings.", + "sharing-data-desc-1": "Data is validated against public rules to ensure no sensitive data is sent. Don’t trust, verify.", + "sharing-data-desc-2": "Usage data is sent end-to-end encrypted over Status’ peer-to-peer network", + "sharing-data-desc-3": "Instead of your regular chat key, a single use key is used", + "sharing-data-desc-4": "Usage data cannot be associated with your IP address", + "sharing-data-desc-5": "Cumulative data of all users is publicaly available", + "view-public-dashboard": "View public dashboard", + "sharing-data-desc-6": "The data is removed from your phone after they are sent", + "allow-and-send": "Allow and send", + "no-thanks": "No thanks", + "help-improve-status": "Help improve Status", + "thank-you": "Thank you", + "count-metrics-collected": "{{count}} metrics collected" } diff --git a/translations/es.json b/translations/es.json index cba29bd605..dc735b185f 100644 --- a/translations/es.json +++ b/translations/es.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Si aceptás también recompensarás a tu amigo con un bono de referencia criptográfica", "invite-chat-starter-pack": "Paquete de Bienvenida", "invite-friends": "Invitar a amigos", - "invite-instruction": "Cómo funciona", + "how-it-works": "Cómo funciona", "invite-instruction-fifth": "Puede elegir canjear su bono de referencia en cualquier momento.", "invite-instruction-first": "Envías un enlace de invitación único a tu amigo para descargar y unirse a Status", "invite-instruction-fourth": "Vos recibís tu bono de referencia y tu amigo el Paquete de Bienvenida", diff --git a/translations/es_419.json b/translations/es_419.json index cba29bd605..dc735b185f 100644 --- a/translations/es_419.json +++ b/translations/es_419.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Si aceptás también recompensarás a tu amigo con un bono de referencia criptográfica", "invite-chat-starter-pack": "Paquete de Bienvenida", "invite-friends": "Invitar a amigos", - "invite-instruction": "Cómo funciona", + "how-it-works": "Cómo funciona", "invite-instruction-fifth": "Puede elegir canjear su bono de referencia en cualquier momento.", "invite-instruction-first": "Envías un enlace de invitación único a tu amigo para descargar y unirse a Status", "invite-instruction-fourth": "Vos recibís tu bono de referencia y tu amigo el Paquete de Bienvenida", diff --git a/translations/es_AR.json b/translations/es_AR.json index 583917652f..405f3c7b99 100644 --- a/translations/es_AR.json +++ b/translations/es_AR.json @@ -710,7 +710,7 @@ "invite-chat-rule": "Si aceptás también recompensarás a tu amigo con un bono de referencia criptográfica", "invite-chat-starter-pack": "Paquete de Bienvenida", "invite-friends": "Invitar a amigos", - "invite-instruction": "Cómo funciona", + "how-it-works": "Cómo funciona", "invite-instruction-fifth": "Puede elegir canjear su bono de referencia en cualquier momento.", "invite-instruction-first": "Envías un enlace de invitación único a tu amigo para descargar y unirse a Status", "invite-instruction-fourth": "Vos recibís tu bono de referencia y tu amigo el Paquete de Bienvenida", diff --git a/translations/fil.json b/translations/fil.json index 0ed38dfc4a..84572a5ebc 100644 --- a/translations/fil.json +++ b/translations/fil.json @@ -768,7 +768,7 @@ "invite-chat-rule": "Ang pagtanggap ay gagantimpalaan din ang iyong kaibigan ng isang crypto referral bonus", "invite-chat-starter-pack": "Starter Pack", "invite-friends": "Mag-imbita ng mga kaibigan", - "invite-instruction": "Paano ito gumagana", + "how-it-works": "Paano ito gumagana", "invite-instruction-fifth": "Maaari kang pumili para i-redeem ang iyong referral bonus anumang oras.", "invite-instruction-first": "Nagpadala ka ng isang natatanging link ng imbitasyon sa iyong kaibigan upang i-download at sumali sa Status", "invite-instruction-fourth": "Ikaw ay makakatanggap ng referral bonus at ang iyong kaibigan ng Starter Pack", diff --git a/translations/fr.json b/translations/fr.json index 4a0c89e6d0..c6a72f5a0f 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -710,7 +710,7 @@ "invite-chat-rule": "Accepter récompensera également votre ami avec un bonus de parrainage crypto", "invite-chat-starter-pack": "Kit de départ", "invite-friends": "Inviter des amis", - "invite-instruction": "Comment ça marche", + "how-it-works": "Comment ça marche", "invite-instruction-fifth": "Vous pouvez utiliser votre bonus de parrainage quand vous le souhaité", "invite-instruction-first": "Vous envoyez un lien d'invitation unique à votre ami pour télécharger et rejoindre Status", "invite-instruction-fourth": "Vous recevez votre bonus de référence et votre ami le Pack Starter", diff --git a/translations/id.json b/translations/id.json index 320ae7afb0..0082074547 100644 --- a/translations/id.json +++ b/translations/id.json @@ -641,7 +641,7 @@ "invite-chat-rule": "Menerima juga akan memberi teman Anda bonus rujukan crypto", "invite-chat-starter-pack": "Paket awal", "invite-friends": "Mengundang teman-teman", - "invite-instruction": "Bagaimana itu bekerja", + "how-it-works": "Bagaimana itu bekerja", "invite-instruction-fifth": "Anda dapat memilih untuk menebus bonus referensi Anda kapan saja.", "invite-instruction-first": "Anda mengirim tautan undangan unik ke teman Anda untuk mengunduh dan bergabung dengan Status", "invite-instruction-fourth": "Anda menerima bonus referensi dan teman Anda Paket Pemula", diff --git a/translations/it.json b/translations/it.json index 4cffedbc96..c80ecad659 100644 --- a/translations/it.json +++ b/translations/it.json @@ -710,7 +710,7 @@ "invite-chat-rule": "L'accettazione ricompenserà anche il tuo amico con un bonus in token", "invite-chat-starter-pack": "Starter Pack", "invite-friends": "Invita amici", - "invite-instruction": "Come funziona", + "how-it-works": "Come funziona", "invite-instruction-fifth": "Puoi scegliere di riscattare il tuo bonus per la segnalazione in qualsiasi momento.", "invite-instruction-first": "Invia un link univoco di invito al tuo amico per scaricare e iscriverti a Status", "invite-instruction-fourth": "Ricevi il tuo bonus di segnalazione e il tuo amico lo Starter Pack", diff --git a/translations/ja.json b/translations/ja.json index 7ffb516b3e..26fdbba153 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -713,7 +713,7 @@ "invite-chat-rule": "受け入れると友達にも紹介ボーナスがもらえます", "invite-chat-starter-pack": "スターターパック", "invite-friends": "友達を招待", - "invite-instruction": "友達招待の手順", + "how-it-works": "友達招待の手順", "invite-instruction-fifth": "紹介ボーナスはいつでも受け取ることができます。", "invite-instruction-first": "友達に招待リンクを送って、友達がStatusをダウンロードして参加します。", "invite-instruction-fourth": "あなたは紹介ボーナスを受け取り、友達はスターターパックを受け取ります", diff --git a/translations/ko.json b/translations/ko.json index 382b90e318..67f993ce59 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -713,7 +713,7 @@ "invite-chat-rule": "수락하면 사용자의 친구는 초대 보너스를 받게 됩니다", "invite-chat-starter-pack": "스타터 팩", "invite-friends": "친구 초대", - "invite-instruction": "어떻게 시작하나요?", + "how-it-works": "어떻게 시작하나요?", "invite-instruction-fifth": "보너스는 언제든지 환급 가능합니다.", "invite-instruction-first": "친구가 스테이터스를 다운로드하고 사용할 수 있는 특별한 초대 링크를 보내세요", "invite-instruction-fourth": "사용자는 초대 보너스를, 친구는 스타터 팩을 받게 됩니다", diff --git a/translations/ms.json b/translations/ms.json index 6aede8b6d2..55cf868a12 100644 --- a/translations/ms.json +++ b/translations/ms.json @@ -634,7 +634,7 @@ "invite-chat-rule": "Penerimaan juga akan menawarkan ganjaran rujukan bonus kripto kepada rakan anda", "invite-chat-starter-pack": "Pek permulaan", "invite-friends": "Menjemput kawan-kawan", - "invite-instruction": "Bagaimana ia berfungsi", + "how-it-works": "Bagaimana ia berfungsi", "invite-instruction-fifth": "Anda boleh memilih untuk menebus rujukan bonus anda pada bila-bila masa.", "invite-instruction-first": "Anda menghantar pautan jemputan unik kepada rakan anda untuk memuat turun dan menyertai Status", "invite-instruction-fourth": "Anda menerima bonus rujukan dan rakan anda Starter Pack", diff --git a/translations/nl.json b/translations/nl.json index 76b694b9b5..af38d70992 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Als je accepteert, wordt je vriend ook beloond met een crypto-doorverwijsbonus", "invite-chat-starter-pack": "Starterspakket", "invite-friends": "Vrienden uitnodigen", - "invite-instruction": "Hoe het werkt", + "how-it-works": "Hoe het werkt", "invite-instruction-fifth": "Je kunt er op ieder moment voor kiezen om je doorverwijsbonus in te wisselen.", "invite-instruction-first": "Je stuurt een unieke uitnodigingslink naar je vriend om status te downloaden en te installeren", "invite-instruction-fourth": "Je ontvangt je doorverwijsbonus en je vriend het starterspakket", diff --git a/translations/pl.json b/translations/pl.json index dd63553b99..71182aa191 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -746,7 +746,7 @@ "invite-chat-rule": "Akceptacja wynagrodzi również znajomego premią w postaci bonusu kryptowalutowego", "invite-chat-starter-pack": "Pakiet startowy", "invite-friends": "Zaproś znajomych", - "invite-instruction": "Jak to działa?", + "how-it-works": "Jak to działa?", "invite-instruction-fifth": "Bonus za polecenie możesz zrealizować w dowolnym momencie.", "invite-instruction-first": "Wysyłasz znajomemu unikalny link z zaproszeniem do pobrania i dołączenia do platformy Status", "invite-instruction-fourth": "Otrzymasz premię za polecenie, a twój znajomy pakiet startowy", diff --git a/translations/pt.json b/translations/pt.json index f8b4533543..eb86c38bac 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Aceitar também recompensará o seu amigo com um bônus de indicação criptografado", "invite-chat-starter-pack": "Pacote Inicial", "invite-friends": "Convide amigos", - "invite-instruction": "Como funciona", + "how-it-works": "Como funciona", "invite-instruction-fifth": "Você pode optar por resgatar seu bônus de indicação a qualquer momento.", "invite-instruction-first": "Você envia um link de convite exclusivo para seu amigo fazer o download e entrar na Status", "invite-instruction-fourth": "Você recebe seu bônus de indicação e seu amigo o Pacote inicial", diff --git a/translations/pt_BR.json b/translations/pt_BR.json index f8b4533543..eb86c38bac 100644 --- a/translations/pt_BR.json +++ b/translations/pt_BR.json @@ -724,7 +724,7 @@ "invite-chat-rule": "Aceitar também recompensará o seu amigo com um bônus de indicação criptografado", "invite-chat-starter-pack": "Pacote Inicial", "invite-friends": "Convide amigos", - "invite-instruction": "Como funciona", + "how-it-works": "Como funciona", "invite-instruction-fifth": "Você pode optar por resgatar seu bônus de indicação a qualquer momento.", "invite-instruction-first": "Você envia um link de convite exclusivo para seu amigo fazer o download e entrar na Status", "invite-instruction-fourth": "Você recebe seu bônus de indicação e seu amigo o Pacote inicial", diff --git a/translations/ru.json b/translations/ru.json index eec613f267..e5080b2906 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -757,7 +757,7 @@ "invite-chat-rule": "Принятие также вознаградит вашего друга крипто-реферальным бонусом", "invite-chat-starter-pack": "Стартовый пак", "invite-friends": "Пригласить друзей", - "invite-instruction": "Как это работает", + "how-it-works": "Как это работает", "invite-instruction-fifth": "Вы можете в любой момент использовать свой реферальный бонус.", "invite-instruction-first": "Вы отправляете своему другу уникальную ссылку-приглашение на скачивание и присоединение к Status", "invite-instruction-fourth": "Вы получите реферальный бонус, а ваш друг – стартовый пак", diff --git a/translations/tl.json b/translations/tl.json index 91e3a3d31d..d83ab719fa 100644 --- a/translations/tl.json +++ b/translations/tl.json @@ -748,7 +748,7 @@ "invite-chat-rule": "Ang pagtanggap ay gagantimpalaan din ang iyong kaibigan ng isang crypto referral bonus", "invite-chat-starter-pack": "Starter Pack", "invite-friends": "Mag-imbita ng mga kaibigan", - "invite-instruction": "Paano ito gumagana", + "how-it-works": "Paano ito gumagana", "invite-instruction-fifth": "Maaari kang pumili para i-redeem ang iyong referral bonus anumang oras.", "invite-instruction-first": "Nagpadala ka ng isang natatanging link ng imbitasyon sa iyong kaibigan upang i-download at sumali sa Status", "invite-instruction-fourth": "Ikaw ay makakatanggap ng referral bonus at ang iyong kaibigan ng Starter Pack", diff --git a/translations/tr.json b/translations/tr.json index ff5f71a16b..10e6bb3938 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -707,7 +707,7 @@ "invite-chat-rule": "Kabul ettiğinizde,arkadaşınızı bir kripto tavsiye bonusu ile de ödüllendirilecek", "invite-chat-starter-pack": "Başlangıç paketi", "invite-friends": "Arkadaşlarınızı davet edin", - "invite-instruction": "Nasıl çalışır", + "how-it-works": "Nasıl çalışır", "invite-instruction-fifth": "Tavsiye bonusunuzu istediğiniz zaman kullanmayı seçebilirsiniz.", "invite-instruction-first": "Statusu indirmek ve katılmak için arkadaşınıza benzersiz bir davet bağlantısı gönderirsiniz", "invite-instruction-fourth": "Tavsiye bonusu ve arkadaşınıza Başlangıç Paketini alırsınız", diff --git a/translations/vi.json b/translations/vi.json index 13ceba2423..419eb281ee 100644 --- a/translations/vi.json +++ b/translations/vi.json @@ -633,7 +633,7 @@ "invite-chat-rule": "Việc chấp nhận cũng sẽ thưởng cho bạn bè của bạn một phần thưởng giới thiệu tiền điện tử", "invite-chat-starter-pack": "Gói khởi động", "invite-friends": "Mời bạn bè", - "invite-instruction": "Làm thế nào nó hoạt động", + "how-it-works": "Làm thế nào nó hoạt động", "invite-instruction-fifth": "Bạn có thể chọn đổi phần thưởng giới thiệu của mình bất cứ lúc nào.", "invite-instruction-first": "Bạn gửi một liên kết mời duy nhất đến bạn bè của mình để tải xuống và tham gia Trạng thái", "invite-instruction-fourth": "Bạn nhận được phần thưởng giới thiệu của mình và bạn bè của bạn Gói Khởi đầu", diff --git a/translations/zh.json b/translations/zh.json index 638d478d89..47fdd4ac89 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -713,7 +713,7 @@ "invite-chat-rule": "接受将让您的朋友也获得推荐奖金", "invite-chat-starter-pack": "入门包", "invite-friends": "邀请朋友", - "invite-instruction": "如何运作", + "how-it-works": "如何运作", "invite-instruction-fifth": "您随时都可以兑换推荐奖金。", "invite-instruction-first": "您向您的朋友发送一个专属的邀请链接,以下载并加入Status", "invite-instruction-fourth": "您将获得推荐奖金,您的朋友将获得入门包", diff --git a/translations/zh_TW.json b/translations/zh_TW.json index f205c32f39..7368a411dd 100644 --- a/translations/zh_TW.json +++ b/translations/zh_TW.json @@ -713,7 +713,7 @@ "invite-chat-rule": "接受將讓您的朋友也獲得推薦獎勵", "invite-chat-starter-pack": "入門包", "invite-friends": "邀請朋友", - "invite-instruction": "如何運作", + "how-it-works": "如何運作", "invite-instruction-fifth": "您隨時都可以來兌換推薦獎勵。", "invite-instruction-first": "向您的朋友發送一個專屬的邀請,用來下載並加入Status", "invite-instruction-fourth": "您的朋友取得入門包時,您可以獲得推薦獎勵",