Persist in-app feature flags (dev-only feature) (#19619)
This commit improves in-app feature flags to persist what is currently only stored in a Reagent atom by using RN Async Storage https://reactnative.dev/docs/asyncstorage. This should make them more convenient to use, which is a good thing overall for developers. Additionally, there's now a top-right button in screen Settings > Feature Flags that will reset the flags to the initial values obtained from environment variables. These in-app feature flags are exclusively available in debug builds in Settings > Feature Flags, and only visible when flag ENABLE_QUO_PREVIEW is enabled. There's no impact whatsoever in prod builds. A reminder that they are not meant to be used by users (yet). It's worth noting that RN has deprecated Async Storage and now recommends other community solutions, but for a dev-only feature, I think it's fine.
This commit is contained in:
parent
ab191407ed
commit
8ad58bb364
|
@ -15,7 +15,9 @@
|
||||||
:text-align :left
|
:text-align :left
|
||||||
:title "Features Flags"
|
:title "Features Flags"
|
||||||
:icon-name :i/arrow-left
|
:icon-name :i/arrow-left
|
||||||
:on-press #(rf/dispatch [:navigate-back])}]
|
:on-press #(rf/dispatch [:navigate-back])
|
||||||
|
:right-side [{:icon-name :i/rotate
|
||||||
|
:on-press #(ff/reset-flags)}]}]
|
||||||
(doall
|
(doall
|
||||||
(for [context-name ff/feature-flags-categories
|
(for [context-name ff/feature-flags-categories
|
||||||
:let [context-flags (filter (fn [[k]]
|
:let [context-flags (filter (fn [[k]]
|
||||||
|
@ -36,4 +38,3 @@
|
||||||
:container-style {:margin-right 8}
|
:container-style {:margin-right 8}
|
||||||
:on-change #(ff/toggle flag)}]
|
:on-change #(ff/toggle flag)}]
|
||||||
[quo/text (second (string/split (name flag) "."))]]))]))])
|
[quo/text (second (string/split (name flag) "."))]]))]))])
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
[status-im.contexts.profile.push-notifications.events :as notifications]
|
[status-im.contexts.profile.push-notifications.events :as notifications]
|
||||||
[status-im.contexts.shell.jump-to.state :as shell.state]
|
[status-im.contexts.shell.jump-to.state :as shell.state]
|
||||||
[status-im.contexts.shell.jump-to.utils :as shell.utils]
|
[status-im.contexts.shell.jump-to.utils :as shell.utils]
|
||||||
|
[status-im.feature-flags :as ff]
|
||||||
[status-im.navigation.core :as navigation]
|
[status-im.navigation.core :as navigation]
|
||||||
status-im.contexts.wallet.signals
|
status-im.contexts.wallet.signals
|
||||||
status-im.events
|
status-im.events
|
||||||
status-im.navigation.core
|
|
||||||
[status-im.setup.dev :as dev]
|
[status-im.setup.dev :as dev]
|
||||||
[status-im.setup.global-error :as global-error]
|
[status-im.setup.global-error :as global-error]
|
||||||
[status-im.setup.interceptors :as interceptors]
|
[status-im.setup.interceptors :as interceptors]
|
||||||
|
@ -63,6 +63,9 @@
|
||||||
(async-storage/get-item :selected-stack-id #(shell.utils/change-selected-stack-id % nil nil))
|
(async-storage/get-item :selected-stack-id #(shell.utils/change-selected-stack-id % nil nil))
|
||||||
(async-storage/get-item :screen-height #(reset! shell.state/screen-height %))
|
(async-storage/get-item :screen-height #(reset! shell.state/screen-height %))
|
||||||
|
|
||||||
|
(when config/quo-preview-enabled?
|
||||||
|
(ff/load-flags))
|
||||||
|
|
||||||
(dev/setup)
|
(dev/setup)
|
||||||
(log/info "hermesEnabled ->" (is-hermes))
|
(log/info "hermesEnabled ->" (is-hermes))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns status-im.feature-flags
|
(ns status-im.feature-flags
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
|
[react-native.async-storage :as async-storage]
|
||||||
[react-native.config :as config]
|
[react-native.config :as config]
|
||||||
[reagent.core :as reagent]))
|
[reagent.core :as reagent]))
|
||||||
|
|
||||||
|
@ -8,8 +9,7 @@
|
||||||
[k]
|
[k]
|
||||||
(= "1" (config/get-config k)))
|
(= "1" (config/get-config k)))
|
||||||
|
|
||||||
(defonce ^:private feature-flags-config
|
(def ^:private initial-flags
|
||||||
(reagent/atom
|
|
||||||
{::community.edit-account-selection (enabled-in-env? :FLAG_EDIT_ACCOUNT_SELECTION_ENABLED)
|
{::community.edit-account-selection (enabled-in-env? :FLAG_EDIT_ACCOUNT_SELECTION_ENABLED)
|
||||||
::wallet.activities (enabled-in-env? :FLAG_WALLET_ACTIVITY_ENABLED)
|
::wallet.activities (enabled-in-env? :FLAG_WALLET_ACTIVITY_ENABLED)
|
||||||
::wallet.assets-modal-hide (enabled-in-env? :FLAG_ASSETS_MODAL_HIDE)
|
::wallet.assets-modal-hide (enabled-in-env? :FLAG_ASSETS_MODAL_HIDE)
|
||||||
|
@ -21,7 +21,10 @@
|
||||||
::wallet.import-private-key (enabled-in-env? :FLAG_IMPORT_PRIVATE_KEY_ENABLED)
|
::wallet.import-private-key (enabled-in-env? :FLAG_IMPORT_PRIVATE_KEY_ENABLED)
|
||||||
::wallet.long-press-watch-only-asset (enabled-in-env? :FLAG_LONG_PRESS_WATCH_ONLY_ASSET_ENABLED)
|
::wallet.long-press-watch-only-asset (enabled-in-env? :FLAG_LONG_PRESS_WATCH_ONLY_ASSET_ENABLED)
|
||||||
::wallet.swap (enabled-in-env? :FLAG_SWAP_ENABLED)
|
::wallet.swap (enabled-in-env? :FLAG_SWAP_ENABLED)
|
||||||
::wallet.wallet-connect (enabled-in-env? :FLAG_WALLET_CONNECT_ENABLED)}))
|
::wallet.wallet-connect (enabled-in-env? :FLAG_WALLET_CONNECT_ENABLED)})
|
||||||
|
|
||||||
|
(defonce ^:private feature-flags-config
|
||||||
|
(reagent/atom initial-flags))
|
||||||
|
|
||||||
(defn feature-flags [] @feature-flags-config)
|
(defn feature-flags [] @feature-flags-config)
|
||||||
|
|
||||||
|
@ -37,7 +40,28 @@
|
||||||
|
|
||||||
(defn toggle
|
(defn toggle
|
||||||
[flag]
|
[flag]
|
||||||
(swap! feature-flags-config update flag not))
|
(let [new-flags (update @feature-flags-config flag not)]
|
||||||
|
(async-storage/set-item!
|
||||||
|
:feature-flags
|
||||||
|
new-flags
|
||||||
|
(fn []
|
||||||
|
(reset! feature-flags-config new-flags)))))
|
||||||
|
|
||||||
|
(defn load-flags
|
||||||
|
[]
|
||||||
|
(async-storage/get-item
|
||||||
|
:feature-flags
|
||||||
|
(fn [flags]
|
||||||
|
(when flags
|
||||||
|
(reset! feature-flags-config flags)))))
|
||||||
|
|
||||||
|
(defn reset-flags
|
||||||
|
[]
|
||||||
|
(async-storage/set-item!
|
||||||
|
:feature-flags
|
||||||
|
initial-flags
|
||||||
|
(fn []
|
||||||
|
(reset! feature-flags-config initial-flags))))
|
||||||
|
|
||||||
(defn alert
|
(defn alert
|
||||||
[flag action]
|
[flag action]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const { NativeModules } = require('react-native');
|
const { NativeModules } = require('react-native');
|
||||||
|
|
||||||
require('@react-native-async-storage/async-storage/jest/async-storage-mock');
|
mockAsyncStorage = require('@react-native-async-storage/async-storage/jest/async-storage-mock');
|
||||||
require('react-native-gesture-handler/jestSetup');
|
require('react-native-gesture-handler/jestSetup');
|
||||||
require('react-native-reanimated/src/reanimated2/jestUtils').setUpTests();
|
require('react-native-reanimated/src/reanimated2/jestUtils').setUpTests();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue