Compare commits

..
Author SHA1 Message Date
Omar Basem 502718440c draft 2023-03-21 14:09:06 +04:00
Omar Basem f0cbfd7155 updates 2023-03-21 13:40:14 +04:00
Omar Basem 9c49991bae draft 2023-03-21 13:26:12 +04:00
Omar Basem 1b4c65ea97 draft 2023-03-21 13:03:37 +04:00
Omar Basem 41547a70a5 draft 2023-03-21 12:49:00 +04:00
Omar Basem a030046401 draft 2023-03-21 12:32:56 +04:00
Omar Basem 7557e885d0 draft 2023-03-21 12:12:02 +04:00
Omar Basem 06fa981954 draft 2023-03-21 11:38:02 +04:00
Omar Basem 38522e5180 draft 2023-03-21 11:33:58 +04:00
Omar Basem ecba5ade66 lint 2023-03-21 10:54:53 +04:00
Omar Basem c94508e559 remove image delay 2023-03-21 09:05:33 +04:00
4386 changed files with 115535 additions and 160458 deletions
+203 -152
View File
@@ -1,55 +1,62 @@
legacy.status-im.utils.build/warning-handler status-im.utils.build/warning-handler
legacy.status-im.utils.build/get-current-sha status-im.utils.build/get-current-sha
status-im.constants/spacing-char status-im2.constants/spacing-char
status-im.constants/arg-wrapping-char status-im2.constants/arg-wrapping-char
legacy.status-im.ios.core/init status-im.ios.core/init
legacy.status-im.ui.components.camera/aspects status-im.ui.components.camera/aspects
legacy.status-im.ui.components.camera/capture-targets status-im.ui.components.camera/capture-targets
legacy.status-im.ui.components.camera/set-torch status-im.ui.components.camera/set-torch
legacy.status-im.ui.components.camera/request-access-ios status-im.ui.components.camera/request-access-ios
legacy.status-im.chat.models.message-content/stylings status-im.chat.models.message-content/stylings
legacy.status-im.chat.models.message-content/actions status-im.chat.models.message-content/actions
legacy.status-im.chat.models.message-content/blank-string status-im.chat.models.message-content/blank-string
legacy.status-im.chat.models.message-content/sorted-ranges status-im.chat.models.message-content/sorted-ranges
legacy.status-im.ethereum.mnemonic/words->passphrase status-im.ethereum.mnemonic/words->passphrase
native-module.core/listener status-im.native-module.core/listener
native-module.core/multiaccount-reset status-im.native-module.core/multiaccount-reset
native-module.core/extract-group-membership-signatures status-im.native-module.core/extract-group-membership-signatures
native-module.core/sign-group-membership status-im.native-module.core/sign-group-membership
native-module.core/update-mailservers status-im.native-module.core/update-mailservers
legacy.status-im.ethereum.abi-spec/bytes-to-hex status-im.ethereum.abi-spec/bytes-to-hex
legacy.status-im.android.core/init status-im.android.core/init
legacy.status-im.chat.models.message/transport-keys status-im.chat.models.message/transport-keys
legacy.status-im.utils.datetime/week status-im.utils.datetime/week
legacy.status-im.utils.datetime/time-ago status-im.utils.datetime/time-ago
legacy.status-im.utils.datetime/format-date status-im.utils.datetime/format-date
legacy.status-im.utils.datetime/get-ordinal-date status-im.utils.datetime/get-ordinal-date
reagent.dom/render reagent.dom/render
reagent.dom/unmount-component-at-node reagent.dom/unmount-component-at-node
reagent.dom/dom-node reagent.dom/dom-node
reagent.dom/force-update-all reagent.dom/force-update-all
legacy.status-im.multiaccounts.create.core/get-new-key-code status-im.multiaccounts.create.core/get-new-key-code
legacy.status-im.ethereum.decode/string status-im.ethereum.decode/string
legacy.status-im.ui.screens.network.views/mainnet? status-im.ui.screens.network.views/mainnet?
legacy.status-im.data-store.chats/event->string status-im.data-store.chats/event->string
legacy.status-im.mailserver.constants/seven-days status-im.mailserver.constants/seven-days
legacy.status-im.mailserver.constants/connection-timeout status-im.mailserver.constants/connection-timeout
legacy.status-im.hardwallet.card/install-applet status-im.hardwallet.card/install-applet
legacy.status-im.hardwallet.card/install-cash-applet status-im.hardwallet.card/install-cash-applet
legacy.status-im.multiaccount.login.data-test/all-contacts status-im.multiaccount.login.data-test/all-contacts
legacy.status-im.multiaccount.login.data-test/multiaccounts status-im.multiaccount.login.data-test/multiaccounts
legacy.status-im.multiaccount.login.data-test/get-chats status-im.multiaccount.login.data-test/get-chats
legacy.status-im.multiaccount.login.data-test/transport status-im.multiaccount.login.data-test/transport
legacy.status-im.multiaccount.login.data-test/topics status-im.multiaccount.login.data-test/topics
legacy.status-im.utils.utils/clear-timeout status-im.utils.utils/clear-timeout
legacy.status-im.utils.handlers/logged-in status-im.utils.handlers/logged-in
legacy.status-im.multiaccounts.model/credentials status-im.multiaccounts.model/credentials
legacy.status-im.multiaccounts.login.core/contract-fleet? status-im.multiaccounts.login.core/contract-fleet?
legacy.status-im.multiaccounts.login.core/fetch-nodes status-im.multiaccounts.login.core/fetch-nodes
status-im.config/rpc-networks-only? status-im2.config/rpc-networks-only?
status-im.config/waku-enabled? status-im2.config/waku-enabled?
legacy.status-im.utils.pairing/has-paired-installations? status-im.utils.pairing/has-paired-installations?
legacy.status-im.tribute-to-talk.core-test/user-cofx status-im.tribute-to-talk.core-test/user-cofx
quo.gesture-handler/tap-gesture-handler
quo.gesture-handler/pan-gesture-handler
quo.gesture-handler/long-press-gesture-handler
quo.gesture-handler/pure-native-button
quo.gesture-handler/createNativeWrapper
quo.gesture-handler/animated-raw-button
quo.gesture-handler/states
mocks.js-dependencies/action-button mocks.js-dependencies/action-button
mocks.js-dependencies/camera mocks.js-dependencies/camera
mocks.js-dependencies/dismiss-keyboard mocks.js-dependencies/dismiss-keyboard
@@ -78,108 +85,152 @@ mocks.js-dependencies/react-navigation-bottom-tabs
mocks.js-dependencies/react-native-haptic-feedback mocks.js-dependencies/react-native-haptic-feedback
mocks.js-dependencies/mock mocks.js-dependencies/mock
quo.previews.main/init quo.previews.main/init
legacy.status-im.chat.models.message-list-test/descending-range status-im.chat.models.message-list-test/descending-range
legacy.status-im.chat.models.message-list-test/random-range status-im.chat.models.message-list-test/random-range
legacy.status-im.ethereum.eip165/supports? status-im.ethereum.eip165/supports?
legacy.status-im.utils.hex/valid-hex? status-im.utils.hex/valid-hex?
legacy.status-im.multiaccounts.core/chat-send? status-im.multiaccounts.core/chat-send?
legacy.status-im.hardwallet.simulated-keycard/install-cash-applet status-im.hardwallet.simulated-keycard/install-cash-applet
legacy.status-im.hardwallet.simulated-keycard/sign-typed-data status-im.hardwallet.simulated-keycard/sign-typed-data
legacy.status-im.utils.core/safe-read-message-content status-im.utils.core/safe-read-message-content
legacy.status-im.ui.components.react/native-modules status-im.ui.components.react/native-modules
legacy.status-im.ui.components.react/progress-bar status-im.ui.components.react/progress-bar
react-native.fs/move-file status-im.utils.fs/move-file
react-native.fs/read-dir status-im.utils.fs/read-dir
react-native.fs/mkdir status-im.utils.fs/mkdir
react-native.fs/unlink status-im.utils.fs/unlink
react-native.fs/file-exists? status-im.utils.fs/file-exists?
legacy.status-im.ui.components.colors/white quo.animated/code
legacy.status-im.ui.components.colors/black quo.animated/eq
legacy.status-im.ui.components.core/animated-header quo.animated/neq
legacy.status-im.ui.components.core/safe-area-provider quo.animated/greater-or-eq
legacy.status-im.ui.components.core/safe-area-consumer quo.animated/not*
legacy.status-im.ui.components.core/safe-area-view quo.animated/or*
legacy.status-im.utils.universal-links.core/open! quo.animated/and*
legacy.status-im.transport.filters.core-test/member-2 quo.animated/add
legacy.status-im.ui.screens.chat.ttt/one-to-one-chat-description-container quo.animated/sub
legacy.status-im.ethereum.ens/ttl quo.animated/multiply
legacy.status-im.ethereum.ens/addr-hash quo.animated/abs
legacy.status-im.ethereum.ens/name-hash quo.animated/min*
legacy.status-im.ethereum.ens/ABI-hash quo.animated/max*
legacy.status-im.ethereum.ens/pubkey-hash quo.animated/set
legacy.status-im.network.core/get-network quo.animated/start-clock
status-im.constants/desktop-content-types quo.animated/stop-clock
status-im.constants/blocks-per-hour quo.animated/bezier
status-im.constants/one-earth-day quo.animated/linear
status-im.constants/left-pane-min-width quo.animated/set-value
status-im.constants/system quo.animated/clock
status-im.constants/contact-discovery quo.animated/debug
status-im.constants/send-transaction-failed-parse-response quo.animated/log
status-im.constants/send-transaction-failed-parse-params quo.animated/event
status-im.constants/send-transaction-no-account-selected quo.animated/on-change
status-im.constants/send-transaction-invalid-tx-sender quo.animated/cond*
status-im.constants/web3-get-logs quo.animated/block
status-im.constants/web3-transaction-receipt quo.animated/call*
status-im.constants/web3-new-filter quo.animated/timing
status-im.constants/web3-new-pending-transaction-filter quo.animated/spring
status-im.constants/web3-new-block-filter quo.animated/clamp
status-im.constants/web3-uninstall-filter quo.animated/with-spring
status-im.constants/web3-get-filter-changes quo.animated/with-timing
status-im.constants/web3-shh-post quo.animated/re-timing
status-im.constants/web3-shh-new-identity quo.design-system.colors/white
status-im.constants/web3-shh-has-identity quo.design-system.colors/black
status-im.constants/web3-shh-new-group status-im.transport.core-test/messages
status-im.constants/web3-shh-add-to-group status-im.ethereum.core/sidechain?
status-im.constants/web3-shh-new-filter status-im.ethereum.core/network-with-upstream-rpc?
status-im.constants/web3-shh-uninstall-filter status-im.ethereum.core/current-address
status-im.constants/web3-shh-get-filter-changes status-im.ethereum.core/network->network-name
status-im.constants/web3-shh-get-messages status-im.ethereum.core/estimate-gas
status-im.constants/status-create-address quo.core/animated-header
status-im.constants/event-transfer-hash quo.core/safe-area-provider
status-im.constants/regx-rtl-characters quo.core/safe-area-consumer
status-im.constants/desktop-msg-chars-hard-limit quo.core/safe-area-view
status-im.constants/debug-metrics status-im.utils.universal-links.core/open!
status-im.constants/scan-qr-code status-im.transport.filters.core-test/member-2
status-im.constants/ipfs-proto-code status-im.ui.screens.chat.ttt/one-to-one-chat-description-container
status-im.constants/swarm-proto-code status-im.ethereum.ens/ttl
legacy.status-im.multiaccounts.update.publisher/publish-update! status-im.ethereum.ens/addr-hash
legacy.status-im.utils.async/task-queue status-im.ethereum.ens/name-hash
legacy.status-im.utils.async/async-periodic-run! status-im.ethereum.ens/ABI-hash
legacy.status-im.desktop.core/app-root status-im.ethereum.ens/pubkey-hash
legacy.status-im.desktop.core/init status-im.network.core/get-network
legacy.status-im.chat.models-test/test-db status-im2.constants/desktop-content-types
legacy.status-im.ui.components.spacing/padding-vertical status-im2.constants/blocks-per-hour
legacy.status-im.utils.money/percent-change status-im2.constants/one-earth-day
status-im2.constants/left-pane-min-width
status-im2.constants/system
status-im2.constants/contact-discovery
status-im2.constants/send-transaction-failed-parse-response
status-im2.constants/send-transaction-failed-parse-params
status-im2.constants/send-transaction-no-account-selected
status-im2.constants/send-transaction-invalid-tx-sender
status-im2.constants/web3-get-logs
status-im2.constants/web3-transaction-receipt
status-im2.constants/web3-new-filter
status-im2.constants/web3-new-pending-transaction-filter
status-im2.constants/web3-new-block-filter
status-im2.constants/web3-uninstall-filter
status-im2.constants/web3-get-filter-changes
status-im2.constants/web3-shh-post
status-im2.constants/web3-shh-new-identity
status-im2.constants/web3-shh-has-identity
status-im2.constants/web3-shh-new-group
status-im2.constants/web3-shh-add-to-group
status-im2.constants/web3-shh-new-filter
status-im2.constants/web3-shh-uninstall-filter
status-im2.constants/web3-shh-get-filter-changes
status-im2.constants/web3-shh-get-messages
status-im2.constants/status-create-address
status-im2.constants/event-transfer-hash
status-im2.constants/regx-rtl-characters
status-im2.constants/desktop-msg-chars-hard-limit
status-im2.constants/debug-metrics
status-im2.constants/scan-qr-code
status-im2.constants/ipfs-proto-code
status-im2.constants/swarm-proto-code
status-im.multiaccounts.update.publisher/publish-update!
status-im.utils.async/task-queue
status-im.utils.async/async-periodic-run!
status-im.desktop.core/app-root
status-im.desktop.core/init
status-im.chat.models-test/test-db
quo.design-system.spacing/padding-vertical
status-im.utils.money/percent-change
status-im.utils.platform/isMacOs?
status-im.utils.platform/isNix?
status-im.utils.platform/isWin?
status-im.utils.platform/android-version>=
utils.debounce/clear-all utils.debounce/clear-all
legacy.status-im.transport.db/create-chat status-im.transport.db/create-chat
legacy.status-im.utils.priority-map/priority-map status-im.utils.priority-map/priority-map
legacy.status-im.utils.priority-map/priority-map-by status-im.utils.priority-map/priority-map-by
legacy.status-im.utils.priority-map/priority-map-keyfn status-im.utils.priority-map/priority-map-keyfn
legacy.status-im.utils.priority-map/empty-message-map status-im.utils.priority-map/empty-message-map
legacy.status-im.wallet.choose-recipient.core/use-default-eth-gas status-im.wallet.choose-recipient.core/use-default-eth-gas
legacy.status-im.ui.components.animation/decay status-im.ui.components.animation/decay
legacy.status-im.ui.components.animation/remove-all-listeners status-im.ui.components.animation/remove-all-listeners
legacy.status-im.ui.components.animation/stop-animation status-im.ui.components.animation/stop-animation
legacy.status-im.ui.components.animation/animated status-im.ui.components.animation/animated
legacy.status-im.ui.components.animation/add status-im.ui.components.animation/add
legacy.status-im.ui.components.animation/subtract status-im.ui.components.animation/subtract
legacy.status-im.ui.components.animation/get-layout status-im.ui.components.animation/get-layout
legacy.status-im.wallet.transactions-test/uri-query-data status-im.wallet.transactions-test/uri-query-data
legacy.status-im.utils.name/shortened-name quo.react-native/touchable-highlight
legacy.status-im.ui.components.button.haptic/trigger status-im.utils.name/shortened-name
legacy.status-im.contact.db/filter-dapps status-im.ui.components.button.haptic/trigger
legacy.status-im.contact.db/filter-group-contacts status-im.contact.db/filter-dapps
legacy.status-im.contact.db/legacy-pending? status-im.contact.db/filter-group-contacts
legacy.status-im.utils.transducers-test/preview-call-1 status-im.contact.db/legacy-pending?
legacy.status-im.utils.transducers-test/preview-call-2 status-im.utils.transducers-test/preview-call-1
legacy.status-im.utils.types/to-string status-im.utils.transducers-test/preview-call-2
legacy.status-im.chat.db/datemark? status-im.utils.types/to-string
legacy.status-im.chat.db/gap? status-im.chat.db/datemark?
legacy.status-im.chat.db/map->sorted-seq status-im.chat.db/gap?
legacy.status-im.utils.snoopy/status-module-filter status-im.chat.db/map->sorted-seq
legacy.status-im.utils.snoopy/websocket-filter status-im.utils.snoopy/status-module-filter
legacy.status-im.utils.snoopy/touches-filter status-im.utils.snoopy/websocket-filter
legacy.status-im.utils.snoopy/native-animation-filter status-im.utils.snoopy/touches-filter
legacy.status-im.utils.snoopy/keyboard-observer-filter status-im.utils.snoopy/native-animation-filter
legacy.status-im.utils.multihash/hex status-im.utils.snoopy/keyboard-observer-filter
legacy.status-im.utils.multihash/decode-array status-im.utils.multihash/hex
status-im.utils.multihash/decode-array
-1
View File
@@ -1 +0,0 @@
{:lint-as {babashka.fs/with-temp-dir clojure.core/let}}
-1
View File
@@ -1 +0,0 @@
{:hooks {:macroexpand {sci.core/copy-ns sci.core/copy-ns}}}
-9
View File
@@ -1,9 +0,0 @@
(ns sci.core)
(defmacro copy-ns
([ns-sym sci-ns]
`(copy-ns ~ns-sym ~sci-ns nil))
([ns-sym sci-ns opts]
`[(quote ~ns-sym)
~sci-ns
(quote ~opts)]))
+20 -73
View File
@@ -1,76 +1,23 @@
{:config-paths ["status-im"] {:lint-as {status-im.utils.views/defview clojure.core/defn
:lint-as {legacy.status-im.utils.views/defview clojure.core/defn status-im.utils.views/letsubs clojure.core/let
legacy.status-im.utils.views/letsubs clojure.core/let reagent.core/with-let clojure.core/let
reagent.core/with-let clojure.core/let status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
legacy.status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all utils.re-frame/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all quo.react/with-deps-check clojure.core/fn
quo.react/with-deps-check clojure.core/fn quo.previews.preview/list-comp clojure.core/for
quo.previews.preview/list-comp clojure.core/for status-im.utils.styles/def clojure.core/def
legacy.status-im.utils.styles/def clojure.core/def status-im.utils.styles/defn clojure.core/defn
legacy.status-im.utils.styles/defn clojure.core/defn test-helpers.unit/deftest-sub clojure.core/defn
test-helpers.unit/deftest-sub clojure.core/defn taoensso.tufte/defnp clojure.core/defn}
taoensso.tufte/defnp clojure.core/defn} :linters {:consistent-alias {:level :error
:linters {:case-duplicate-test {:level :error} :aliases {clojure.string string
:case-quoted-test {:level :error} clojure.set set
:case-symbol-test {:level :error} clojure.walk walk
:clj-kondo-config {:level :error} taoensso.timbre log}}
:cond-else {:level :error} :invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
:consistent-alias {:level :error
:aliases {clojure.set set
clojure.string string
clojure.walk walk
malli.core malli
malli.dev.pretty malli.pretty
malli.dev.virhe malli.virhe
malli.error malli.error
malli.generator malli.generator
malli.transform malli.transform
malli.util malli.util
schema.core schema
taoensso.timbre log}}
:deprecated-namespace {:level :warning}
:docstring-blank {:level :error}
:equals-true {:level :error}
:inline-def {:level :error}
:invalid-arity {:skip-args [legacy.status-im.utils.fx/defn
utils.re-frame/defn]}
:loop-without-recur {:level :error}
:minus-one {:level :error}
:misplaced-docstring {:level :error}
:missing-body-in-when {:level :error}
:missing-clause-in-try {:level :error}
:missing-else-branch {:level :error}
:not-empty? {:level :error}
:plus-one {:level :error}
:redundant-do {:level :error}
:redundant-let {:level :error}
:refer-all {:level :error}
:shadowed-var {:level :error
;; We temporarily use :include to define an
;; allowlist of core Clojure vars. In the
;; future, as we progressively fix shadowed
;; vars, we should be able to delete this
;; option and lint all vars.
:exclude [type name]}
:single-operand-comparison {:level :error}
:syntax {:level :error}
:unbound-destructuring-default {:level :error}
:uninitialized-var {:level :error}
:unknown-require-option {:level :error}
:unreachable-code {:level :error}
:unresolved-namespace {:level :error}
;; TODO remove number when this is fixed ;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867 ;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY :unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number number
legacy.status-im.test-helpers/restore-app-db]} status-im.test-helpers/restore-app-db]}}
:unresolved-var {:level :error}
:unsorted-required-namespaces {:level :error}
:unused-alias {:level :warning}
:unused-binding {:level :error}
:unused-import {:level :error}
:unused-namespace {:level :error}
:unused-private-var {:level :error}
:unused-referred-var {:level :error}
:use {:level :error}}
:config-in-ns {mocks.js-dependencies {:linters {:clojure-lsp/unused-public-var {:level :off}}}}} :config-in-ns {mocks.js-dependencies {:linters {:clojure-lsp/unused-public-var {:level :off}}}}}
-2
View File
@@ -1,2 +0,0 @@
{:lint-as {malli.experimental/defn schema.core/defn}
:linters {:unresolved-symbol {:exclude [(malli.core/=>)]}}}
@@ -1,4 +0,0 @@
{:linters
{:unresolved-symbol
{:exclude [(cljs.test/is [match? thrown-match?])
(clojure.test/is [match? thrown-match?])]}}}
@@ -1,5 +0,0 @@
{:lint-as
{rewrite-clj.zip/subedit-> clojure.core/->
rewrite-clj.zip/subedit->> clojure.core/->>
rewrite-clj.zip/edit-> clojure.core/->
rewrite-clj.zip/edit->> clojure.core/->>}}
-8
View File
@@ -1,8 +0,0 @@
{:hooks
{:analyze-call
{utils.i18n/label utils.i18n/label
test-helpers.component/get-all-by-translation-text utils.i18n/label
test-helpers.component/get-by-translation-text utils.i18n/label
test-helpers.component/query-all-by-translation-text utils.i18n/label
test-helpers.component/query-by-translation-text utils.i18n/label}}
:linters {:status-im.linter/invalid-translation-keyword {:level :error}}}
-26
View File
@@ -1,26 +0,0 @@
(ns utils.i18n
(:require [clj-kondo.hooks-api :as hooks]))
(defn label
"Verify call to `utils.i18n/label` pass the translation keyword qualified with `t`."
[{:keys [node]}]
(let [[_ translation-key-node & _] (:children node)]
(when (and (hooks/keyword-node? translation-key-node)
(not= "t" (-> translation-key-node hooks/sexpr namespace)))
(hooks/reg-finding! (assoc (meta translation-key-node)
:message "Translation keyword should be qualified with \"t\""
:type :status-im.linter/invalid-translation-keyword)))))
(comment
;; Valid
(label {:node (hooks/parse-string "(i18n/label :t/foo {:var \"hello\"})")
:cljc false
:lang :cljs
:filename ""
:config {}
:ns ""
:context nil})
;; Invalid
(label {:node (hooks/parse-string "(i18n/label :foo)")})
)
-1
View File
@@ -1 +0,0 @@
{:hooks {:analyze-call {taoensso.encore/defalias taoensso.encore/defalias}}}
@@ -1,16 +0,0 @@
(ns taoensso.encore
(:require
[clj-kondo.hooks-api :as hooks]))
(defn defalias [{:keys [node]}]
(let [[sym-raw src-raw] (rest (:children node))
src (if src-raw src-raw sym-raw)
sym (if src-raw
sym-raw
(symbol (name (hooks/sexpr src))))]
{:node (with-meta
(hooks/list-node
[(hooks/token-node 'def)
(hooks/token-node (hooks/sexpr sym))
(hooks/token-node (hooks/sexpr src))])
(meta src))}))
+37
View File
@@ -0,0 +1,37 @@
module.exports = {
"testRunner": "jest",
"testRegex": "\\.visual\\.js$",
"runner-config": "visual-test/config.json",
"devices": {
"simulator": {
"type": "ios.simulator",
"device": {
"type": "iPhone 11 Pro"
}
}
},
"apps": {
"ios.release": {
"name": "StatusIm",
"type": "ios.app",
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/StatusIm.app",
"build": "make release-ios"
},
"ios.debug": {
"name": "StatusIm",
"type": "ios.app",
"binaryPath": process.env.TEST_BINARY_PATH,
"build": "make run-ios SIMULATOR='iPhone 11 Pro'"
}
},
"configurations": {
"ios.sim.release": {
"device": "simulator",
"app": "ios.release"
},
"ios.sim.debug": {
"device": "simulator",
"app": "ios.debug"
}
}
}
+1 -2
View File
@@ -22,6 +22,7 @@ KEYCARD_TEST_MENU=0
QR_READ_TEST_MENU=1 QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=1 ENABLE_ROOT_ALERT=1
ENABLE_QUO_PREVIEW=1 ENABLE_QUO_PREVIEW=1
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr APN_TOPIC=im.status.ethereum.pr
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1
@@ -32,5 +33,3 @@ TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1 SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1 STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1 LOCAL_PAIRING_ENABLED=1
TEST_STATEOFUS=1
FAST_CREATE_COMMUNITY_ENABLED=1
+1 -1
View File
@@ -21,6 +21,7 @@ COMMANDS_ENABLED=1
KEYCARD_TEST_MENU=1 KEYCARD_TEST_MENU=1
QR_READ_TEST_MENU=1 QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=0 ENABLE_ROOT_ALERT=0
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr APN_TOPIC=im.status.ethereum.pr
VERIFY_TRANSACTION_CHAIN_ID=5 VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5 VERIFY_ENS_CHAIN_ID=5
@@ -32,4 +33,3 @@ DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1 TWO_MINUTES_SYNCING=1
STICKERS_TEST_ENABLED=1 STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1 LOCAL_PAIRING_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=1
+1 -1
View File
@@ -26,6 +26,7 @@ VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5 VERIFY_ENS_CHAIN_ID=5
TEST_STATEOFUS=1 TEST_STATEOFUS=1
BLANK_PREVIEW=0 BLANK_PREVIEW=0
MAX_IMAGES_BATCH=5
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
COMMUNITIES_MANAGEMENT_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1
@@ -34,4 +35,3 @@ TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1 ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1 STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1 LOCAL_PAIRING_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=1
+1 -1
View File
@@ -17,8 +17,8 @@ RPC_NETWORKS_ONLY=0
PARTITIONED_TOPIC=0 PARTITIONED_TOPIC=0
CONTRACT_NODES=1 CONTRACT_NODES=1
ENABLE_ROOT_ALERT=1 ENABLE_ROOT_ALERT=1
MAX_IMAGES_BATCH=5
BLANK_PREVIEW=0 BLANK_PREVIEW=0
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1
DELETE_MESSAGE_ENABLED=1 DELETE_MESSAGE_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=0
-1
View File
@@ -19,4 +19,3 @@ ENABLE_ROOT_ALERT=1
MAX_IMAGES_BATCH=1 MAX_IMAGES_BATCH=1
DELETE_MESSAGE_ENABLED=1 DELETE_MESSAGE_ENABLED=1
COLLECTIBLES_ENABLED=1 COLLECTIBLES_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=0
+3 -3
View File
@@ -1,4 +1,4 @@
module.exports = { module.exports = {
root: true, root: true,
extends: '@react-native', extends: '@react-native-community',
}; };
+1 -1
View File
@@ -43,4 +43,4 @@ As a <user|developer|...>, I want to <task> so that <goal>.
[comment]: # (if on Android please replicate bug whilst running adb logcat) [comment]: # (if on Android please replicate bug whilst running adb logcat)
``` ```
... ...
``` ```
+5 -4
View File
@@ -1,8 +1,8 @@
--- ---
name: MVPBug Report name: Bug Report
about: MVPBug Report about: Bug Report
title: '' title: ''
labels: 'E:Mobile Bug MVP' labels: bug
assignees: '' assignees: ''
--- ---
@@ -22,7 +22,8 @@ assignees: ''
### Reproduction ### Reproduction
1. 1) Open Status
2)
### Additional Information ### Additional Information
-30
View File
@@ -1,30 +0,0 @@
---
name: Wont fix Report
about: to facilitate future handling, identify and address non-urgent issues that won't be prioritized in the near future
title: ''
labels: likely_wont_fix
assignees: ''
---
## Problem
#### Expected behavior
#### Actual behavior
### Reproduction
1.
### Additional Information
- Status version: release
- Operating System: Android, iOS
[comment]: # (Please, add logs/notes if necessary)
-6
View File
@@ -63,10 +63,4 @@ Documentation change PR (review please): https://github.com/status-im/status.im/
<!-- (PRs will only be accepted if squashed into single commit.) --> <!-- (PRs will only be accepted if squashed into single commit.) -->
### Before and after screenshots comparison
| Figma (if available) | iOS (if available) | Android (if available)
| --- | --- | --- |
| Please embed Image/Video here of the before and after. | Please embed Image/Video here of the before and after. | Please embed Image/Video here of the before and after. |
status: ready <!-- Can be ready or wip --> status: ready <!-- Can be ready or wip -->
+1
View File
@@ -5,6 +5,7 @@ project-board:
min-reviewers: 1 min-reviewers: 1
tested-pr-label-name: 'Tested - Issues' tested-pr-label-name: 'Tested - Issues'
contributor-column-name: 'CONTRIBUTOR' contributor-column-name: 'CONTRIBUTOR'
to-rebase-column-name: 'TO REBASE'
review-column-name: 'REVIEW' review-column-name: 'REVIEW'
test-column-name: 'E2E Tests' test-column-name: 'E2E Tests'
-11
View File
@@ -7,7 +7,6 @@
# Xcode # Xcode
# #
/ios/.xcode.env.local
/component-spec /component-spec
result/ result/
build/ build/
@@ -36,10 +35,6 @@ project.xcworkspace
**/android/.project **/android/.project
**/android/.settings/org.eclipse.buildship.core.prefs **/android/.settings/org.eclipse.buildship.core.prefs
local.properties local.properties
*.hprof
.cxx/
*.keystore
!debug.keystore
# Atom # Atom
.tags* .tags*
@@ -188,9 +183,3 @@ test/appium/tests/users.py
## component-tests ## component-tests
*.log *.log
## local xcode env introduced in react-native 0.69
.xcode.env.local
## git hooks
lefthook.yml
-1
View File
@@ -1 +0,0 @@
{:clean {:ns-inner-blocks-indentation :keep}}
+4 -4
View File
@@ -1,10 +1,10 @@
{ {
"include": [ "include": [
"target/test/legacy/status_im/**" "target/test/status_im/**"
], ],
"exclude": [ "exclude": [
"target/test/legacy/status_im/test/**", "target/test/status_im/test/**",
"target/test/legacy/status_im/ui/**" "target/test/status_im/ui/**"
], ],
"report-dir": "./coverage-report", "report-dir": "./coverage-report",
"all": true, "all": true,
@@ -14,4 +14,4 @@
"exclude-after-remap": false, "exclude-after-remap": false,
"extension": [".js"], "extension": [".js"],
"reporter": ["lcovonly"] "reporter": ["lcovonly"]
} }
-10
View File
@@ -1,10 +0,0 @@
*
!*.js
!*/
*.clj-kondo
*.shadow-cljs
modules
result
target
component-spec
/app
+5 -7
View File
@@ -1,9 +1,7 @@
module.exports = { module.exports = {
arrowParens: 'always', bracketSpacing: false,
printWidth: 120, bracketSameLine: true,
semi: true, singleQuote: true,
singleQuote: true, trailingComma: 'all',
tabWidth: 2, tabWidth: 4,
trailingComma: 'all',
useTabs: false,
}; };
-12
View File
@@ -1,12 +0,0 @@
{
"editor.formatOnSave": true,
"clojure.format.enable": false,
"[clojure]": {
"editor.defaultFormatter": "rflagreca.vscode-clj-zprint"
},
"vscode-clj-zprint.IgnoreExternalFiles": false,
"vscode-clj-zprint.CommunityFormatting": false,
"vscode-clj-zprint.OptionsMap": "",
"vscode-clj-zprint.width": "",
"vscode-clj-zprint.Styles.UseOnlyTheseStyles": false
}
+18 -25
View File
@@ -1,47 +1,40 @@
;; -*- mode: clojure -*- ;; -*- mode: clojure -*-
;; vi: ft=clojure ;; vi: ft=clojure
{:width 105 {:width 105
:remove {:fn-force-nl #{:noarg1-body}} :style [;; community style
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#community
:community
:style ;; no comma in map
[;; community style https://github.com/kkinnear/zprint/blob/main/doc/reference.md#community :no-comma
:community
;; no comma in map :custom-justify
:no-comma
:custom-justify ;; respect all newlines
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-nl
:respect-nl
;; respect all newlines https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-nl ;; respect blank line
:respect-nl ;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
:respect-bl
;; respect blank line https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
:respect-bl
;; hang multiline left-hand-thing https://github.com/kkinnear/zprint/issues/273
:multi-lhs-hang]
;; hang multiline left-hand-thing
;; https://github.com/kkinnear/zprint/issues/273
:multi-lhs-hang]
:fn-map :fn-map
{"reg-sub" :arg1-pair {"reg-sub" :arg1-pair
"h/describe" :arg1-body "h/describe" :arg1-body
"h/describe-skip" :arg1-body
"h/describe-only" :arg1-body
"h/test" :arg1-body "h/test" :arg1-body
"h/test-skip" :arg1-body
"h/test-only" :arg1-body
"global.describe" :arg1-body "global.describe" :arg1-body
"global.test" :arg1-body "global.test" :arg1-body
"list-comp" :binding "list-comp" :binding
"defview" :arg1-body "defview" :arg1-body
"letsubs" :binding "letsubs" :binding
"with-let" "let" "with-let" "let"
"reg-event-fx" :arg1-pair
"reg-fx" :arg1-pair
"testing" :arg1-body "testing" :arg1-body
"deftest-sub" :arg1-body "deftest-sub" :arg1-body
"wait-for" :arg1-body "wait-for" :arg1-body
"with-deps-check" :arg1-body "with-deps-check" :arg1-body
"schema/=>" :arg1-body
"->" [:noarg1-body "->" [:noarg1-body
{:list {:constant-pair? false :force-nl? false} {:list {:constant-pair? false :force-nl? false}
:next-inner-restore [[:list :constant-pair?]]}] :next-inner-restore [[:list :constant-pair?]]}]
@@ -50,9 +43,9 @@
"assoc-some" "assoc" "assoc-some" "assoc"
"conj-when" "conj" "conj-when" "conj"
"conj-some" "conj"} "conj-some" "conj"}
:remove {:fn-force-nl #{:noarg1-body}}
:style-map :style-map
{:no-comma {:map {:comma? false}} {:no-comma {:map {:comma? false}}
:custom-justify :custom-justify
{:doc "Justify everything using pre-1.1.2 approach" {:doc "Justify everything using pre-1.1.2 approach"
:binding {:justify? true :justify {:max-variance 1000}} :binding {:justify? true :justify {:max-variance 1000}}
+65 -116
View File
@@ -14,17 +14,17 @@ WHITE := $(shell tput -Txterm setaf 7)
YELLOW := $(shell tput -Txterm setaf 3) YELLOW := $(shell tput -Txterm setaf 3)
RESET := $(shell tput -Txterm sgr0) RESET := $(shell tput -Txterm sgr0)
HELP_FUN = \ HELP_FUN = \
%help; \ %help; \
while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \ while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \
print "Usage: make [target]\n\nSee STARTING_GUIDE.md for more info.\n\n"; \ print "Usage: make [target]\n\nSee STARTING_GUIDE.md for more info.\n\n"; \
for (sort keys %help) { \ for (sort keys %help) { \
print "${WHITE}$$_:${RESET}\n"; \ print "${WHITE}$$_:${RESET}\n"; \
for (@{$$help{$$_}}) { \ for (@{$$help{$$_}}) { \
$$sep = " " x (32 - length $$_->[0]); \ $$sep = " " x (32 - length $$_->[0]); \
print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \ print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
}; \ }; \
print "\n"; \ print "\n"; \
} }
HOST_OS := $(shell uname | tr '[:upper:]' '[:lower:]') HOST_OS := $(shell uname | tr '[:upper:]' '[:lower:]')
# This can come from Jenkins # This can come from Jenkins
@@ -42,14 +42,11 @@ export NODE_OPTIONS += --openssl-legacy-provider
export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
# Our custom config is located in nix/nix.conf # Our custom config is located in nix/nix.conf
export NIX_USER_CONF_FILES = $(PWD)/nix/nix.conf export NIX_CONF_DIR = $(PWD)/nix
# Location of symlinks to derivations that should not be garbage collected # Location of symlinks to derivations that should not be garbage collected
export _NIX_GCROOTS = /nix/var/nix/gcroots/per-user/$(USER)/status-mobile export _NIX_GCROOTS = /nix/var/nix/gcroots/per-user/$(USER)/status-mobile
# Defines which variables will be kept for Nix pure shell, use semicolon as divider # Defines which variables will be kept for Nix pure shell, use semicolon as divider
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,\ export _NIX_KEEP ?= TMPDIR,BUILD_ENV,STATUS_GO_SRC_OVERRIDE
BUILD_TYPE,BUILD_NUMBER,COMMIT_HASH,\
ANDROID_GRADLE_OPTS,ANDROID_ABI_SPLIT,ANDROID_ABI_INCLUDE,\
STATUS_GO_SRC_OVERRIDE,STATUS_GO_IPFS_GATEWAY_URL
# Useful for Android release builds # Useful for Android release builds
TMP_BUILD_NUMBER := $(shell ./scripts/version/gen_build_no.sh | cut -c1-10) TMP_BUILD_NUMBER := $(shell ./scripts/version/gen_build_no.sh | cut -c1-10)
@@ -84,10 +81,6 @@ nix-gc-protected:
@echo -e "$(YELLOW)The following paths are protected:$(RESET)" && \ @echo -e "$(YELLOW)The following paths are protected:$(RESET)" && \
ls -1 $(_NIX_GCROOTS) | sed 's/^/ - /' ls -1 $(_NIX_GCROOTS) | sed 's/^/ - /'
nix-upgrade: SHELL := /bin/sh
nix-upgrade: ##@nix Upgrade Nix interpreter to current version.
nix/scripts/upgrade.sh
nix-gc: export TARGET := nix nix-gc: export TARGET := nix
nix-gc: nix-gc-protected ##@nix Garbage collect all packages older than 20 days from /nix/store nix-gc: nix-gc-protected ##@nix Garbage collect all packages older than 20 days from /nix/store
nix-store --gc nix-store --gc
@@ -108,7 +101,7 @@ nix-update-clojure: export TARGET := clojure
nix-update-clojure: ##@nix Update maven Nix expressions based on current clojure setup nix-update-clojure: ##@nix Update maven Nix expressions based on current clojure setup
nix/deps/clojure/generate.sh nix/deps/clojure/generate.sh
nix-update-gems: export TARGET := fastlane nix-update-gems: export TARGET := default
nix-update-gems: ##@nix Update Ruby gems in fastlane/Gemfile.lock and fastlane/gemset.nix nix-update-gems: ##@nix Update Ruby gems in fastlane/Gemfile.lock and fastlane/gemset.nix
fastlane/update.sh fastlane/update.sh
@@ -224,20 +217,24 @@ release-android: keystore build-android ##@build Build signed Android APK
@scripts/sign-android.sh result/app-release-unsigned.apk @scripts/sign-android.sh result/app-release-unsigned.apk
release-ios: export TARGET := ios release-ios: export TARGET := ios
release-ios: export IOS_STATUS_GO_TARGETS := ios/arm64
release-ios: export BUILD_ENV ?= prod release-ios: export BUILD_ENV ?= prod
release-ios: watchman-clean ios-clean jsbundle ##@build Build release for iOS release release-ios: watchman-clean ##@build Build release for iOS release
xcodebuild \ @git clean -dxf -f target/ios && \
-scheme StatusIm \ $(MAKE) jsbundle-ios && \
-configuration Release \ xcodebuild -workspace ios/StatusIm.xcworkspace -scheme StatusIm -configuration Release -destination 'generic/platform=iOS' -UseModernBuildSystem=N clean archive
-workspace ios/StatusIm.xcworkspace \
-destination 'generic/platform=iOS' \
-UseModernBuildSystem=N clean archive
jsbundle: SHELL := /bin/sh jsbundle-android: SHELL := /bin/sh
jsbundle: export BUILD_ENV ?= prod jsbundle-android: export TARGET := android
jsbundle: ##@build Build JavaScript and Clojurescript bundle for iOS and Android jsbundle-android: export BUILD_ENV ?= prod
nix/scripts/build.sh targets.mobile.jsbundle jsbundle-android: ##@jsbundle Compile JavaScript and Clojurescript into app directory
# Call nix-build to build the 'targets.mobile.android.jsbundle' attribute and copy the.js files to the project root
nix/scripts/build.sh targets.mobile.android.jsbundle && \
mv result/*.js ./
jsbundle-ios: export TARGET := ios
jsbundle-ios: export BUILD_ENV ?= prod
jsbundle-ios: ##@jsbundle Compile JavaScript and Clojure into index.ios.js
yarn shadow-cljs release mobile
#-------------- #--------------
# status-go lib # status-go lib
@@ -273,134 +270,90 @@ run-re-frisk: ##@run Start re-frisk server
# TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix # TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix
run-android: export TARGET := android run-android: export TARGET := android
# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set.
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh)
run-android: ##@run Build Android APK and start it on the device run-android: ##@run Build Android APK and start it on the device
npx react-native run-android --appIdSuffix debug npx react-native run-android --appIdSuffix debug
SIMULATOR=iPhone 13 SIMULATOR=iPhone 13
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
run-ios: export TARGET := ios run-ios: export TARGET := ios
run-ios: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
run-ios: ##@run Build iOS app and start it in a simulator/device run-ios: ##@run Build iOS app and start it in a simulator/device
ifneq ("$(SIMULATOR)", "") ifneq ("$(SIMULATOR)", "")
npx react-native run-ios --simulator="$(SIMULATOR)" | xcbeautify npx react-native run-ios --simulator="$(SIMULATOR)"
else else
npx react-native run-ios | xcbeautify npx react-native run-ios
endif endif
show-ios-devices: ##@other shows connected ios device and its name
xcrun xctrace list devices
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
run-ios-device: export TARGET := ios
run-ios-device: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
run-ios-device: ##@run iOS app and start it on a connected device by its name
ifndef DEVICE_NAME
$(error Usage: make run-ios-device DEVICE_NAME=your-device-name)
endif
react-native run-ios --device "$(DEVICE_NAME)"
#-------------- #--------------
# Tests # Tests
#-------------- #--------------
# Get all clojure files, including untracked, excluding removed # Get all clojure files, including untracked, excluding removed
define find_all_clojure_files define find_all_clojure_files
$$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -E '((\.clj-kondo\/status-im)|(src).*\.clj[sc]?$$)|((\.clj-kondo\/(status-im|config))|(src).*\.edn$$)|shadow-cljs\.edn') $$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn)
endef endef
lint: export TARGET := clojure lint: export TARGET := default
lint: export CLJ_LINTER_PRINT_WARNINGS ?= false
lint: ##@test Run code style checks lint: ##@test Run code style checks
@sh scripts/lint/re-frame-in-quo-components.sh && \ @sh scripts/lint-re-frame-in-quo-components.sh && \
sh scripts/lint/direct-require-component-outside-quo.sh && \ clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \
sh scripts/lint/require-i18n-resource-first.sh && \
clj-kondo --config .clj-kondo/config.edn --cache false --fail-level error --lint src $(if $(filter $(CLJ_LINTER_PRINT_WARNINGS),true),,| grep -v ': warning: ') && \
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \ ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
scripts/lint/translations.clj && \ zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
sh scripts/lint/trailing-newline.sh && \
node_modules/.bin/prettier --write .
# NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271 # NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271
lint-fix: export TARGET := clojure lint-fix: export TARGET := default
lint-fix: ##@test Run code style checks and fix issues lint-fix: ##@test Run code style checks and fix issues
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \ ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES
clojure-lsp --ns-exclude-regex ".*/\.clj-kondo/.*|.*/src/status_im/core\.cljs|.*/src/test_helpers/component_tests_preload\.cljs$$" clean-ns && \
sh scripts/lint/trailing-newline.sh --fix && \
node_modules/.bin/prettier --write .
shadow-server: export TARGET := clojure shadow-server: export TARGET := clojure
shadow-server:##@ Start shadow-cljs in server mode for watching shadow-server:##@ Start shadow-cljs in server mode for watching
yarn shadow-cljs server yarn shadow-cljs server
_test-clojure: export TARGET := clojure test-watch: export TARGET := clojure
_test-clojure: export WATCH ?= false test-watch: ##@ Watch tests and re-run no changes to cljs files
_test-clojure: yarn install
ifeq ($(WATCH), true) nodemon --exec 'yarn shadow-cljs compile mocks && yarn shadow-cljs compile test && node --require ./test-resources/override.js target/test/test.js' -e cljs
yarn install && \
yarn shadow-cljs compile mocks && \
nodemon --exec "yarn shadow-cljs compile test && node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO" -e cljs
else
yarn install && \
yarn shadow-cljs compile mocks && \
yarn shadow-cljs compile test && \
node --require ./test-resources/override.js "$$SHADOW_OUTPUT_TO"
endif
test: export SHADOW_OUTPUT_TO := target/test/test.js test-watch-for-repl: export TARGET := clojure
test: export SHADOW_NS_REGEXP := .*-test$$ test-watch-for-repl: ##@ Watch tests and support REPL connections
test: ##@test Run all Clojure tests
test: _test-clojure
test-watch-for-repl: export SHADOW_OUTPUT_TO := target/test/test.js
test-watch-for-repl: export SHADOW_NS_REGEXP := .*-test$$
test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connections
yarn install yarn install
rm -f target/test/test.js rm -f target/test/test.js
yarn shadow-cljs compile mocks && \
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \ concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
'yarn shadow-cljs watch test --verbose' \ 'yarn shadow-cljs compile mocks && yarn shadow-cljs watch test --verbose' \
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl" 'until [ -f ./target/test/test.js ] ; do sleep 1 ; done ; node --require ./test-resources/override.js ./target/test/test.js --repl'
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js test: export TARGET := clojure
test-unit: export SHADOW_NS_REGEXP := ^(?!status-im\.integration-test).*-test$$ test: ##@test Run tests once in NodeJS
test-unit: ##@test Run unit tests # Here we create the gyp bindings for nodejs
test-unit: _test-clojure yarn install
yarn shadow-cljs compile mocks && \
yarn shadow-cljs compile test && \
node --require ./test-resources/override.js target/test/test.js
test-integration: export SHADOW_OUTPUT_TO := target/integration_test/test.js
test-integration: export SHADOW_NS_REGEXP := ^status-im\.integration-test.*$$
test-integration: ##@test Run integration tests
test-integration: _test-clojure
android-test: jsbundle run-visual-test-ios: export TARGET := clojure
android-test: export TARGET := android run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/DerivedData
android-test: run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
cd android && ./gradlew test run-visual-test-ios: export TEST_BINARY_PATH := $(XCODE_DERIVED_DATA)/$(APPLICATION_NAME)/Build/Products/Debug-iphonesimulator/StatusIm.app
run-visual-test-ios: ##@test Run tests once in NodeJS
detox test --configuration ios.sim.debug
component-test-watch: export TARGET := clojure component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true component-test-watch: export COMPONENT_TEST := true
component-test-watch: export BABEL_ENV := test component-test-watch: export BABEL_ENV := test
component-test-watch: export JEST_USE_SILENT_REPORTER := false
component-test-watch: ##@ Watch tests and re-run no changes to cljs files component-test-watch: ##@ Watch tests and re-run no changes to cljs files
@scripts/check-metro-shadow-process.sh yarn install
rm -rf ./component-spec nodemon --exec 'yarn shadow-cljs compile component-test && jest --config=test/jest/jest.config.js' -e cljs
nodemon --exec 'yarn shadow-cljs compile component-test && jest --config=test/jest/jest.config.js --testEnvironment node ' -e cljs
component-test: export TARGET := clojure component-test: export TARGET := clojure
component-test: export COMPONENT_TEST := true component-test: export COMPONENT_TEST := true
component-test: export BABEL_ENV := test component-test: export BABEL_ENV := test
component-test: export JEST_USE_SILENT_REPORTER := false
component-test: ##@test Run component tests once in NodeJS component-test: ##@test Run component tests once in NodeJS
@scripts/check-metro-shadow-process.sh yarn install
rm -rf ./component-spec
yarn shadow-cljs compile component-test && \ yarn shadow-cljs compile component-test && \
jest --clearCache && jest --config=test/jest/jest.config.js --testEnvironment node jest --config=test/jest/jest.config.js
#--------------
# Other # Other
#-------------- #--------------
@@ -409,10 +362,6 @@ geth-connect: ##@other Connect to Geth on the device
adb forward tcp:8545 tcp:8545 && \ adb forward tcp:8545 tcp:8545 && \
build/bin/geth attach http://localhost:8545 build/bin/geth attach http://localhost:8545
ios-clean: SHELL := /bin/sh
ios-clean: ##@prepare Clean iOS build artifacts
git clean -dxf -f target/ios
android-clean: export TARGET := gradle android-clean: export TARGET := gradle
android-clean: ##@prepare Clean Gradle state android-clean: ##@prepare Clean Gradle state
git clean -dxf -f ./android/app/build; \ git clean -dxf -f ./android/app/build; \
+23 -18
View File
@@ -2,11 +2,9 @@
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80"/>](https://play.google.com/store/apps/details?id=im.status.ethereum) [<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80"/>](https://play.google.com/store/apps/details?id=im.status.ethereum)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80"/>](https://f-droid.org/packages/im.status.ethereum/) [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80"/>](https://f-droid.org/packages/im.status.ethereum/)
[<img src="doc/github_badge.png" alt="Get it on Github" height="80"/>](https://github.com/status-im/status-mobile/releases)
Join us in creating a browser, messenger, and gateway to a decentralized world. Status is a free (libre) open source mobile client targeting Android & iOS built entirely on [Ethereum](https://ethereum.org/) technologies. That's right, no middle men and `go-ethereum` running directly on your device.
[Get it on Github](https://github.com/status-im/status-mobile/releases)
Join us in creating a browser, messenger, and gateway to a decentralized world. Status is a free (libre) open source mobile client targeting Android & iOS built entirely on [Ethereum](https://ethereum.org/) technologies. That's right, no middlemen and `go-ethereum` running directly on your device.
[![Getting started with Status](https://i.imgur.com/C0aZZEL.jpg)](https://www.youtube.com/watch?v=oDCSEmS9c3o "Getting started with Status") [![Getting started with Status](https://i.imgur.com/C0aZZEL.jpg)](https://www.youtube.com/watch?v=oDCSEmS9c3o "Getting started with Status")
@@ -14,49 +12,52 @@ Join us in creating a browser, messenger, and gateway to a decentralized world.
We believe in a medium of pure free trade, economies with fair, permission-less access and a world without intermediaries. We want to create policies that can exist between friends or scale globally, we want to communicate securely and be uninhibited by legacy systems. We believe in a medium of pure free trade, economies with fair, permission-less access and a world without intermediaries. We want to create policies that can exist between friends or scale globally, we want to communicate securely and be uninhibited by legacy systems.
We want to take responsibility for our data, and the way we conduct ourselves privately and promote this way of life to a mass audience. We want to take responsibility for our data, the way we conduct ourselves privately and promote this way of life to a mass audience.
We want deep insights into our own economies so we can make informed, data-driven decisions on how to make our lives better. The Ethereum blockchain, Smart Contracts, Swarm and Whisper provide us with a path forward. We want deep insights into our own economies so we can make informed, data-driven decisions on how to make our lives better. The Ethereum blockchain, Smart Contracts, Swarm and Whisper provides us a path forward.
If this interests you, **help us make Status a reality** - anyone can contribute and we need everyone at any skill level to participate. If this interests you, **help us make Status a reality** - anyone can contribute and we need everyone at any skill level to participate.
## How to Contribute? ## How to Contribute?
Go straight to the [docs](doc/) and choose what interests you: Go straight to the [docs](https://status.im/docs) or [join our chat](https://join.status.im/chat/public/status) and choose what interests you:
- **Developer** Developers are the heart of software and to keep Status beating - **Developer** Developers are the heart of software and to keep Status beating
we need all the help we can get! If you're looking to code in ClojureScript or we need all the help we can get! If you're looking to code in ClojureScript or
Golang then Status is the project for you! We use React Native and there is even Golang then Status is the project for you! We use React Native and there is even
some Java/Objective-C too! Want to learn more about it? Start by reading our some Java/Objective-C too! Want to learn more about it? Start by reading our
[Developer Introduction](doc/starting-guide.md) which guides you [Developer Introduction](https://status.im/developer_tools/) which guides you
through the technology stack and start browsing [beginner through the technology stack and start browsing [beginner
issues](https://github.com/status-im/status-mobile/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20). issues](https://github.com/status-im/status-mobile/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20).
Then you can read how to [Build Then you can read how to [Build
Status](doc/starting-guide.md), which talks about managing Status](https://status.im/technical/build_status/), which talks about managing
project dependencies, coding guidelines and testing procedures. project dependencies, coding guidelines and testing procedures. The [doc/](doc/)
Check out our [coding guidelines](doc/new-guidelines.md). directory also has valuable information for contributors.
- **Community Management** - **Community Management**
Metcalfe's law states that the value of a network is proportional to the square of the number of connected users of the system - without community Status is meaningless. We're looking to create a positive, fun environment to explore new ideas, experiment and grow the Status community. Building a community takes a lot of work but the people you'll meet and the long-lasting relationships you form will be well worth it, check out our [Mission and Community Principles](https://status.im/about) Metcalfe's law states that the value of a network is proportional to the square of the number of connected users of the system - without community Status is meaningless. We're looking to create a positive, fun environment to explore new ideas, experiment and grow the Status community. Building a community takes a lot of work but the people you'll meet and long lasting relationships you form will be well worth it, check out our [Mission and Community Principles](https://status.im/about)
- **Specification / Documentation** - **Specification / Documentation**
John Dewey once said, "Education is not preparation for life; education is life *itself* ". Developers and Designers need guidance and it all starts from documentation and specifications. Our software is only as good as its documentation, check out our [docs](doc/) and see how you can improve what we have. John Dewey once said "Education is not preparation for life; education is life *itself* ". Developers & Designers need guidance and it all starts from documentation and specifications. Our software is only as good as its documentation, head over to our [docs](https://status.im/docs) and see how you can improve what we have.
- **Blog Writing** - **Blog Writing**
Content is King, keeping our blog up to date and informing the community of news helps keep everyone on the same page. Content is King, keeping our blog up to date and informing the community of news helps keep everyone on the same page. [Jump into our chat](https://join.status.im/chat/public/status) and discuss with the team how you can contribute!
- **Testers** - **Testers**
It's bug-hunting season! Status is currently under active development and there is sure to be a bunch of learning, [build status from scratch](doc/starting-guide.md) or if an android user checks out our [nightly builds](https://status.im/nightly). You can shake your phone to submit bug reports, or start browsing our [Github Issues](https://github.com/status-im/status-mobile/issues). Every bug you find brings Status closer to stable, usable software for everyone to enjoy! It's bug hunting season! Status is currently under active development and there is sure to be a bunch of learning, [build status from scratch](https://status.im/technical/build_status/) or if an android user check out our [nightly builds](https://status.im/nightly). You can shake your phone to submit bug reports, or start browsing our [Github Issues](https://github.com/status-im/status-mobile/issues). Every bug you find brings Status closer to stable, usable software for everyone to enjoy!
- **Security** - **Security**
Status is a visual interface to make permanent changes on the Blockchain, it handles crypto-tokens that have real value and allows 3rd party code execution. Security is paramount to its success. You are given permission to break Status as hard as you can, as long as you share your findings with the community! Status is a visual interface to make permanent changes on the Blockchain, it handles crypto-tokens that have real value and allows 3rd party code execution. Security is paramount to its success. You are given permission to break Status as hard as you can, as long as you share your findings with the community!
- **Evangelism** - **Evangelism**
Help us spread the word! Tell a friend *right now*, in fact, tell **everyone** - yell from a mountain if you have to, every person counts! If you've got a great story to tell or have some interesting way you've spread the word about Status let us know about it in our [chat](https://join.status.im/chat/public/status) Help us spread the word! Tell a friend *right now*, in fact tell **everyone** - yell from a mountain if you have to, every person counts! If you've got a great story to tell or have some interesting way you've spread the word about Status let us know about it in our [chat](https://join.status.im/chat/public/status)
## Status API
View our [API Docs](https://status.im/developer_tools/status_web_api.html) and learn how to integrate your DApp into Status. You can read more about how to add your DApp to Status [here](https://status.im/developer_tools/add_your_dapp.html).
## Give me Binaries! ## Give me Binaries!
You can get our Beta builds for both Android and iOS on our [website](https://status.im), through our [nightly builds](https://status.im/nightly/), or by [building it yourself](https://status.im/technical/build_status/). You can get our Beta builds for both Android and iOS on our [website](https://status.im), through our [nightly builds](https://status.im/nightly/) or by [building it yourself](https://status.im/technical/build_status/).
## Core Contributors ## Core Contributors
@@ -67,8 +68,12 @@ Without the dedication of these outstanding individuals, Status would not exist.
## Contact us ## Contact us
Feel free to email us at [support@status.im](mailto:support@status.im). Feel free to email us at [support@status.im](mailto:support@status.im) or better yet, [join our chat](https://join.status.im/chat/public/status).
## License ## License
Licensed under the [Mozilla Public License v2.0](https://github.com/status-im/status-mobile/blob/develop/LICENSE.md) Licensed under the [Mozilla Public License v2.0](https://github.com/status-im/status-mobile/blob/develop/LICENSE.md)
## Testing Supported by
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=SFBWQ3k5R2tnT2t6UGsxU1h1U0g3VFlCNnoxVTRHYWNoTDhsb1laaTJZWT0tLUZMUm5sSVFBSU9qSWM1RUJvK1I3Z3c9PQ==--ad15ae7992b432b33743749d40318952415bd98f)](https://www.browserstack.com/automate/public-build/SFBWQ3k5R2tnT2t6UGsxU1h1U0g3VFlCNnoxVTRHYWNoTDhsb1laaTJZWT0tLUZMUm5sSVFBSU9qSWM1RUJvK1I3Z3c9PQ==--ad15ae7992b432b33743749d40318952415bd98f)
+1 -1
View File
@@ -25,7 +25,7 @@
### Redesign ### Redesign
* quo, Switcher Navigation by @Parveshdhull in https://github.com/status-im/status-mobile/pull/13167 * Quo2, Switcher Navigation by @Parveshdhull in https://github.com/status-im/status-mobile/pull/13167
* Switcher and Bottom Tabs Animations and UI Performance Improvements by @Parveshdhull in https://github.com/status-im/status-mobile/pull/13470 * Switcher and Bottom Tabs Animations and UI Performance Improvements by @Parveshdhull in https://github.com/status-im/status-mobile/pull/13470
* feat: add token tag component (#13599) by @J-Son89 in https://github.com/status-im/status-mobile/pull/13644 * feat: add token tag component (#13599) by @J-Son89 in https://github.com/status-im/status-mobile/pull/13644
* [13565] icon-avatar component by @ibrkhalil in https://github.com/status-im/status-mobile/pull/13692 * [13565] icon-avatar component by @ibrkhalil in https://github.com/status-im/status-mobile/pull/13692
+1 -1
View File
@@ -1 +1 @@
1.25.0 1.21.0
+135 -74
View File
@@ -1,75 +1,124 @@
apply plugin: "com.android.application" apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
import com.android.build.OutputFile import com.android.build.OutputFile
import com.sun.org.apache.xalan.internal.xsltc.compiler.Copy import com.sun.org.apache.xalan.internal.xsltc.compiler.Copy
/* /**
* This is the configuration block to customize your React Native Android app. * The react.gradle file registers a task for each build variant:
* By default you don't need to apply any configuration, just uncomment the lines you need. * - bundlePrJsAndAssets
* - bundleDebugJsAndAssets
* - bundleReleaseJsAndAssets
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node", "--max-old-space-size=8192"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/ */
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
/* Variants */ /*
// The list of variants to that are debuggable. For those we're going to Enabling Hermes on x86 devices is crashing the app after a few reloads,
// skip the bundling of the JS bundle and the assets. By default is just 'debug'. this flag can be used to disable Hermes while building app.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. https://github.com/status-im/status-mobile/issues/14031
// debuggableVariants = ["liteDebug", "prodDebug"] */
/* Bundling */ def disableHermes = System.getenv('DISABLE_HERMES') == 'true'
// A list containing the node command and its flags. Default is just 'node'.
nodeExecutableAndArgs = ["node", "--max-old-space-size=16384" ]
//
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */ project.ext.react = [
// The hermes compiler command to run. By default it is 'hermesc' nodeExecutableAndArgs: ["node", "--max-old-space-size=16384"],
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" entryFile: "index.js",
// /* NOTE: Hermes engine is required for Android 64-bit builds running on 64 devices,
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" * to guard against a hang in the UI thread after invoking status-go.
// hermesFlags = ["-O", "-output-source-map"] * Also a clean and rebuild is required when changing this. */
} enableHermes: !disableHermes,
/* Disable 'The first definition was here.' warnings */
hermesFlagsRelease: ["-w"],
bundleInPr: true,
inputExcludes: ["android/**", "ios/**", "react-native/**", "src/**", "test/**"]
]
// NOTE: Uncomment if building RN from a fork
// configurations.all {
// exclude group: 'com.facebook.react', module: 'react-native'
// }
apply from: "../../node_modules/react-native/react.gradle"
/** /**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode. * Run Proguard to shrink the Java bytecode in release builds.
*/ */
def enableProguardInReleaseBuilds = false def enableProguardInReleaseBuilds = false
/** /**
* The preferred build flavor of JavaScriptCore (JSC) * The preferred build flavor of JavaScriptCore.
* *
* For example, to use the international variant, you can use: * For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
* *
* The international variant includes ICU i18n library and necessary data * The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that * give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default. * this variant is about 6MiB larger per architecture than default.
*/ */
def jscFlavor = 'org.webkit:android-jsc:+' def jscFlavor = 'org.webkit:android-jsc:+'
@@ -81,8 +130,7 @@ def jscFlavor = 'org.webkit:android-jsc:+'
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced. * and the benefits of using Hermes will therefore be sharply reduced.
*/ */
def enableHermes = hermesEnabled.toBoolean(); def enableHermes = project.ext.react.get("enableHermes", false);
def getCommitHash = { -> def getCommitHash = { ->
if (project.hasProperty("commitHash")) { if (project.hasProperty("commitHash")) {
@@ -108,13 +156,12 @@ def getVersionCode = { ->
def getVersionName = { -> def getVersionName = { ->
new ByteArrayOutputStream().withStream { stdOut -> new ByteArrayOutputStream().withStream { stdOut ->
// TODO: probably not used, cleanup
if (project.hasProperty("releaseVersion")) { if (project.hasProperty("releaseVersion")) {
return project.releaseVersion return project.releaseVersion
} }
/* Necessary because Android Studio uses wrong PWD. /* Necessary because Android Studio uses wrong PWD.
* Is actually absolute directory path of this file. */ * Is actually absolute directory path of this file. */
def configDir = project.projectDir.toString() def configDir = project.projectDir.toString()
version = new File(configDir + '/../../VERSION').text version = new File(configDir + '/../../VERSION').text
return version.replaceAll("\\s","") return version.replaceAll("\\s","")
} }
@@ -136,16 +183,13 @@ def getEnvOrConfig = { varName ->
} }
android { android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_1_8
} }
namespace "im.status.ethereum"
defaultConfig { defaultConfig {
applicationId "im.status.ethereum" applicationId "im.status.ethereum"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
@@ -162,7 +206,6 @@ android {
} }
} }
} }
/** /**
* Arbitrary project metadata * Arbitrary project metadata
* https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html * https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
@@ -233,11 +276,10 @@ android {
versionNameSuffix ".pr" versionNameSuffix ".pr"
debuggable false debuggable false
matchingFallbacks = ["release"] matchingFallbacks = ["release"]
// necessary to make react-native-config's code generation work // necessary to make react-native-config's code generation work
resValue "string", "build_config_package", "im.status.ethereum" resValue "string", "build_config_package", "im.status.ethereum"
} }
} }
// applicationVariants are e.g. debug, release // applicationVariants are e.g. debug, release
applicationVariants.all { variant -> applicationVariants.all { variant ->
variant.outputs.each { output -> variant.outputs.each { output ->
@@ -253,7 +295,6 @@ android {
} }
} }
aaptOptions { aaptOptions {
// disable PNG optimization as for some reason it cannot be trusted to provide deterministic output (see https://f-droid.org/en/docs/Reproducible_Builds/) // disable PNG optimization as for some reason it cannot be trusted to provide deterministic output (see https://f-droid.org/en/docs/Reproducible_Builds/)
cruncherEnabled = false cruncherEnabled = false
@@ -265,31 +306,51 @@ android {
} }
dependencies { dependencies {
// The version of react-native is set by the React Native Gradle Plugin implementation fileTree(dir: "libs", include: ["*.jar"])
implementation("com.facebook.react:react-android") implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "androidx.core:core-splashscreen:1.0.0" implementation "androidx.core:core-splashscreen:1.0.0"
if (hermesEnabled.toBoolean()) { debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
implementation("com.facebook.react:hermes-android") exclude group:'com.facebook.fbjni'
} else { }
// TODO Uncomment once Flipper does not cause webview crashes in Debug mode
// debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
// exclude group:'com.facebook.flipper'
// }
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
prImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor implementation jscFlavor
} }
// react-native-screens // react-native-screens
implementation 'androidx.appcompat:appcompat:1.1.0-rc01' implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
implementation 'androidx.multidex:multidex:2.0.1' // required by status-mobile/android/app/src/main/java/im/status/ethereum/MainApplication.java
implementation 'androidx.multidex:multidex:2.0.1' // only needed if minSdkVersion is less than 21 (https://developer.android.com/studio/build/multidex)
implementation project(':react-native-dialogs')
implementation project(':react-native-blur') implementation project(':react-native-blur')
// Force using exact RN version instead of relying on gradle dependency resolution
// https://docs.gradle.org/current/userguide/introduction_dependency_management.html#sec:dependency_resolution
// NOTE: Uncomment if building RN from a fork
//compile ("com.facebook.react:react-native:0.55.4") { force = true } // From node_modules
implementation project(':react-native-status') implementation project(':react-native-status')
implementation project(':react-native-status-keycard') implementation project(':react-native-status-keycard')
implementation 'com.github.status-im:function:0.0.1' implementation 'com.github.status-im:function:0.0.1'
implementation 'com.facebook.fresco:fresco:2.5.0' implementation 'com.facebook.fresco:fresco:2.2.0'
implementation 'com.facebook.fresco:animated-gif:2.5.0' implementation 'com.facebook.fresco:animated-gif:2.2.0'
implementation "com.squareup.okhttp3:okhttp-tls:4.9.2" implementation "com.squareup.okhttp3:okhttp-tls:3.12.12"
implementation("com.google.prefab:cli:2.0.0")
} }
def getLocalNDKDir = { -> def getLocalNDKDir = { ->
def rootDir = project.rootDir def rootDir = project.rootDir
def localProperties = new File(rootDir, "local.properties") def localProperties = new File(rootDir, "local.properties")
+1 -3
View File
@@ -7,7 +7,5 @@
<!-- Remove licensing permission since we don't license our app and it blocks F-Droid submissions. --> <!-- Remove licensing permission since we don't license our app and it blocks F-Droid submissions. -->
<uses-permission tools:node="remove" android:name="com.android.vending.CHECK_LICENSE"/> <uses-permission tools:node="remove" android:name="com.android.vending.CHECK_LICENSE"/>
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" > <application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" />
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
</application>
</manifest> </manifest>
@@ -0,0 +1,73 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package im.status.ethereum;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
// import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
// import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
// TODO Uncomment once Flipper does not cause webview crashes in Debug mode
// NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
// NetworkingModule.setCustomClientBuilder(
// new NetworkingModule.CustomClientBuilder() {
// @Override
// public void apply(OkHttpClient.Builder builder) {
// builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
// }
// });
// client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}
+10 -13
View File
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto"> android:installLocation="auto"
package="im.status.ethereum">
<!-- non-dangerous permissions --> <!-- non-dangerous permissions -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -16,11 +17,11 @@
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<application <application
@@ -31,11 +32,7 @@
android:theme="@style/Theme.AppSplash" android:theme="@style/Theme.AppSplash"
android:name=".MainApplication" android:name=".MainApplication"
android:largeHeap="true" android:largeHeap="true"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true">
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true">
<!-- After upgrading Android Gradle Plugin to 4.2.0 and above we must get rid of `extractNativeLibs="true"`
and use`useLegacyPackaging` flag in our app's `build.gradle`-->
<meta-data android:name="commitHash" android:value="${commitHash}"/> <meta-data android:name="commitHash" android:value="${commitHash}"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@@ -44,7 +41,7 @@
android:theme="@style/Theme.AppSplash" android:theme="@style/Theme.AppSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:resizeableActivity="false" android:resizeableActivity="false"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
@@ -62,14 +59,14 @@
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="status.app" /> <data android:scheme="http" android:host="join.status.im" />
<data android:scheme="https" android:host="status.app" /> <data android:scheme="https" android:host="join.status.im" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true"> <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="status-app" /> <data android:scheme="status-im" />
<data android:scheme="ethereum" /> <data android:scheme="ethereum" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -0,0 +1,260 @@
package im.status.ethereum;
import android.content.Context;
import android.annotation.TargetApi;
import androidx.annotation.Nullable;
import android.app.AlertDialog;
import android.app.ActivityManager;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
import android.preference.PreferenceManager;
import androidx.core.app.ActivityCompat;
import android.util.Log;
import android.view.WindowManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.provider.Settings;
import android.os.Bundle;
import android.os.Handler;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import com.facebook.react.ReactFragmentActivity;
import com.reactnativenavigation.NavigationActivity;
import com.facebook.react.modules.core.PermissionListener;
import androidx.core.splashscreen.SplashScreen;
import java.util.Properties;
import im.status.ethereum.module.StatusThreadPoolExecutor;
import im.status.ethereum.MainApplication;
public class MainActivity extends NavigationActivity
implements ActivityCompat.OnRequestPermissionsResultCallback{
@Nullable private PermissionListener mPermissionListener;
private boolean keepSplash = true;
private final int SPLASH_DELAY = 3200;
private static void registerUncaughtExceptionHandler(final Context context) {
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread thread, final Throwable t) {
// High priority, so don't use StatusThreadPoolExecutor
new Thread() {
@Override
public void run() {
Looper.prepare();
new AlertDialog.Builder(context)
.setTitle("Error")
.setMessage(t.toString())
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int id) {
dialog.dismiss();
defaultUncaughtExceptionHandler.uncaughtException(thread, t);
}
}).show();
Looper.loop();
}
}.start();
}
});
}
private ActivityManager getActivityManager() {
return (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
}
private ActivityManager.MemoryInfo getAvailableMemory(final ActivityManager activityManager) {
final ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo;
}
protected void configureStatus() {
// Required because of crazy APN settings redirecting localhost (found in GB)
Properties properties = System.getProperties();
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
}
private Intent createNotificationSettingsIntent() {
final Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
} else {
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
}
return intent;
}
private void tryToEmit(String eventName, WritableMap event) {
try {
((MainApplication) getApplication()).getReactNativeHost()
.getReactInstanceManager()
.getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("url", event);
} catch(Exception e) {/* we expect NPE on first start, which is OK because we have a fallback */}
}
@Override
public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
if (intent.getDataString() != null && intent.getData().getScheme().startsWith("app-settings")) {
startActivity(createNotificationSettingsIntent());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
setTheme(R.style.DarkTheme);
// Make sure we get an Alert for every uncaught exceptions
registerUncaughtExceptionHandler(MainActivity.this);
// Report memory details for this application
final ActivityManager activityManager = getActivityManager();
Log.v("RNBootstrap", "Available system memory "+getAvailableMemory(activityManager).availMem + ", maximum usable application memory " + activityManager.getLargeMemoryClass()+"M");
setSecureFlag();
// NOTE: Try to not restore the state https://github.com/software-mansion/react-native-screens/issues/17
super.onCreate(null);
if (!shouldShowRootedNotification()) {
configureStatus();
} else {
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
.setMessage(getResources().getString(R.string.root_warning))
.setPositiveButton(getResources().getString(R.string.root_okay), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
rejectRootedNotification();
dialog.dismiss();
configureStatus();
}
})
.setNegativeButton(getResources().getString(R.string.root_cancel), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
MainActivity.this.finishAffinity();
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
MainActivity.this.finishAffinity();
}
})
.create();
dialog.show();
}
Runnable r = new Runnable() {
@Override
public void run() {
System.loadLibrary("status-logs");
// when app is started but the Activity has been destroyed, the deep linking url event is
// not emitted when coming back to foreground. This is a workaround. If the problem is
// resolved in react-native this code should be removed
if (getIntent().getData() != null) {
WritableMap event = Arguments.createMap();
event.putString("url", getIntent().getDataString());
// on first start emit will (silently) fail, but the regular deep linking handler will work
tryToEmit("url", event);
}
}
};
splashScreen.setKeepOnScreenCondition(() -> keepSplash);
Handler handler = new Handler();
handler.postDelayed(() -> keepSplash = false, SPLASH_DELAY);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
this.sendBroadcast(intent);
}
private static final String REJECTED_ROOTED_NOTIFICATION = "rejectedRootedNotification";
private static final Integer FREQUENCY_OF_REMINDER_IN_PERCENT = 5;
private boolean shouldShowRootedNotification() {
if (RootUtil.isDeviceRooted() && BuildConfig.ENABLE_ROOT_ALERT == "1") {
if (userRejectedRootedNotification()) {
return ((Math.random() * 100) < FREQUENCY_OF_REMINDER_IN_PERCENT);
} else return true;
} else {
return false;
}
}
private boolean userRejectedRootedNotification() {
SharedPreferences preferences = getPreferences(0);
return preferences.getBoolean(REJECTED_ROOTED_NOTIFICATION, false);
}
private void rejectRootedNotification() {
SharedPreferences preferences = getPreferences(0);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(REJECTED_ROOTED_NOTIFICATION, true);
editor.commit();
}
private void setSecureFlag() {
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
final boolean setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
@TargetApi(Build.VERSION_CODES.M)
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
mPermissionListener = listener;
requestPermissions(permissions, requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
mPermissionListener = null;
}
}
}
@@ -1,236 +0,0 @@
package im.status.ethereum
import android.annotation.TargetApi
import android.app.ActivityManager
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.preference.PreferenceManager
import android.provider.Settings
import android.util.Log
import android.view.WindowManager
import androidx.annotation.Nullable
import androidx.core.app.ActivityCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.defaults.DefaultReactActivityDelegate
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.facebook.react.modules.core.PermissionListener
import com.reactnativenavigation.NavigationActivity
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView
import im.status.ethereum.MainApplication
import im.status.ethereum.module.StatusThreadPoolExecutor
import java.util.Properties
class MainActivity : NavigationActivity(), ActivityCompat.OnRequestPermissionsResultCallback {
@Nullable
private var mPermissionListener: PermissionListener? = null
private var keepSplash = true
private val SPLASH_DELAY = 3200
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
protected fun getMainComponentName(): String {
return "StatusIm"
}
private fun registerUncaughtExceptionHandler(context: Context) {
val defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
// High priority, so don't use StatusThreadPoolExecutor
Thread.setDefaultUncaughtExceptionHandler { thread, t ->
Thread {
Looper.prepare()
AlertDialog.Builder(context)
.setTitle("Error")
.setMessage(t.toString())
.setNegativeButton("Exit") { dialog, id ->
dialog.dismiss()
defaultUncaughtExceptionHandler.uncaughtException(thread, t)
}.show()
Looper.loop()
}.start()
}
}
private fun getActivityManager(): ActivityManager {
return getSystemService(ACTIVITY_SERVICE) as ActivityManager
}
private fun getAvailableMemory(activityManager: ActivityManager): ActivityManager.MemoryInfo {
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
return memoryInfo
}
protected fun configureStatus() {
// Required because of crazy APN settings redirecting localhost (found in GB)
val properties = System.getProperties()
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1")
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1")
}
private fun createNotificationSettingsIntent(): Intent {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
} else {
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.data = Uri.parse("package:$packageName")
}
return intent
}
private fun tryToEmit(eventName: String, event: WritableMap) {
try {
(getApplication() as MainApplication).getReactNativeHost()
.getReactInstanceManager()
.getCurrentReactContext()
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit("url", event)
} catch (e: Exception) { // we expect NPE on first start, which is OK because we have a fallback
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.dataString != null && intent.data!!.scheme!!.startsWith("app-settings")) {
startActivity(createNotificationSettingsIntent())
}
}
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen();
setTheme(R.style.DarkTheme)
// Make sure we get an Alert for every uncaught exceptions
registerUncaughtExceptionHandler(this)
// Report memory details for this application
val activityManager = getActivityManager()
Log.v("RNBootstrap", "Available system memory " + getAvailableMemory(activityManager).availMem + ", maximum usable application memory " + activityManager.largeMemoryClass + "M")
setSecureFlag()
// NOTE: Try to not restore the state https://github.com/software-mansion/react-native-screens/issues/17
super.onCreate(null)
if (!shouldShowRootedNotification()) {
configureStatus()
} else {
val dialog = AlertDialog.Builder(this)
.setMessage(resources.getString(R.string.root_warning))
.setPositiveButton(resources.getString(R.string.root_okay)) { dialog, which ->
rejectRootedNotification()
dialog.dismiss()
configureStatus()
}
.setNegativeButton(resources.getString(R.string.root_cancel)) { dialog, which ->
dialog.dismiss()
finishAffinity()
}
.setOnCancelListener {
it.dismiss()
finishAffinity()
}
.create()
dialog.show()
}
val r = Runnable {
System.loadLibrary("status-logs")
// when app is started but the Activity has been destroyed, the deep linking url event is
// not emitted when coming back to foreground. This is a workaround. If the problem is
// resolved in react-native this code should be removed
if (intent.data != null) {
val event = Arguments.createMap()
event.putString("url", intent.dataString)
// on first start emit will (silently) fail, but the regular deep linking handler will work
tryToEmit("url", event)
}
}
splashScreen.setKeepOnScreenCondition { keepSplash }
val handler = Handler()
handler.postDelayed({ keepSplash = false }, SPLASH_DELAY.toLong())
StatusThreadPoolExecutor.getInstance().execute(r)
}
override fun onDestroy() {
super.onDestroy()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val intent = Intent("onConfigurationChanged")
intent.putExtra("newConfig", newConfig)
sendBroadcast(intent)
}
private val REJECTED_ROOTED_NOTIFICATION = "rejectedRootedNotification"
private val FREQUENCY_OF_REMINDER_IN_PERCENT = 5
private fun shouldShowRootedNotification(): Boolean {
if (RootUtil.isDeviceRooted() && BuildConfig.ENABLE_ROOT_ALERT == "1") {
return if (userRejectedRootedNotification()) {
(Math.random() * 100) < FREQUENCY_OF_REMINDER_IN_PERCENT
} else true
} else {
return false
}
}
private fun userRejectedRootedNotification(): Boolean {
val preferences = getPreferences(0)
return preferences.getBoolean(REJECTED_ROOTED_NOTIFICATION, false)
}
private fun rejectRootedNotification() {
val preferences = getPreferences(0)
val editor = preferences.edit()
editor.putBoolean(REJECTED_ROOTED_NOTIFICATION, true)
editor.commit()
}
private fun setSecureFlag() {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
val setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
@TargetApi(Build.VERSION_CODES.M)
override fun requestPermissions(permissions: Array<String>, requestCode: Int, listener: PermissionListener) {
mPermissionListener = listener
super.requestPermissions(permissions, requestCode)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (mPermissionListener != null && mPermissionListener!!.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
mPermissionListener = null
}
}
}
@@ -0,0 +1,107 @@
package im.status.ethereum;
import androidx.multidex.MultiDexApplication;
import android.util.Log;
import android.content.Context;
import android.webkit.WebView;
import java.lang.reflect.InvocationTargetException;
import com.facebook.react.PackageList;
import com.aakashns.reactnativedialogs.ReactNativeDialogsPackage;
import com.facebook.react.ReactApplication;
import cl.json.RNSharePackage;
import com.facebook.react.ReactNativeHost;
import com.reactnativenavigation.NavigationApplication;
import com.reactnativenavigation.react.NavigationReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.reactnativecommunity.blurview.BlurViewPackage;
import java.util.List;
import im.status.ethereum.keycard.RNStatusKeycardPackage;
import im.status.ethereum.module.StatusPackage;
import im.status.ethereum.pushnotifications.PushNotificationPackage;
import im.status.ethereum.StatusOkHttpClientFactory;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
public class MainApplication extends NavigationApplication {
private final ReactNativeHost mReactNativeHost = new NavigationReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
StatusPackage statusPackage = new StatusPackage(RootUtil.isDeviceRooted());
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(statusPackage);
packages.add(new ReactNativeDialogsPackage());
packages.add(new RNStatusKeycardPackage());
packages.add(new PushNotificationPackage());
packages.add(new BlurViewPackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage();
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
OkHttpClientProvider.setOkHttpClientFactory(new StatusOkHttpClientFactory());
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG_WEBVIEW == "1");
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("im.status.ethereum.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
@@ -1,61 +0,0 @@
package im.status.ethereum
import android.webkit.WebView
import androidx.multidex.MultiDexApplication
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JSIModulePackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.modules.network.OkHttpClientProvider
import com.reactnativenavigation.NavigationApplication
import com.reactnativenavigation.react.NavigationReactNativeHost
import cl.json.RNSharePackage
import com.reactnativecommunity.blurview.BlurViewPackage
import im.status.ethereum.keycard.RNStatusKeycardPackage
import im.status.ethereum.module.StatusPackage
import im.status.ethereum.pushnotifications.PushNotificationPackage
import im.status.ethereum.StatusOkHttpClientFactory
class MainApplication : NavigationApplication() {
private val mReactNativeHost = object : NavigationReactNativeHost(this) {
override fun getUseDeveloperSupport(): Boolean {
return BuildConfig.DEBUG
}
override fun getPackages(): List<ReactPackage> {
val statusPackage = StatusPackage(RootUtil.isDeviceRooted())
val packages = PackageList(this).getPackages()
packages.add(statusPackage)
packages.add(RNStatusKeycardPackage())
packages.add(PushNotificationPackage())
packages.add(BlurViewPackage())
return packages
}
override fun getJSMainModuleName(): String = "index"
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override fun getReactNativeHost(): ReactNativeHost {
return mReactNativeHost
}
override fun onCreate() {
super.onCreate()
OkHttpClientProvider.setOkHttpClientFactory(StatusOkHttpClientFactory())
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG_WEBVIEW == "1")
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load()
}
}
}
@@ -1,88 +0,0 @@
package im.status.ethereum
import android.util.Log
import com.facebook.react.modules.network.OkHttpClientFactory
import com.facebook.react.modules.network.OkHttpClientProvider
import okhttp3.OkHttpClient
import okhttp3.tls.HandshakeCertificates
import java.io.ByteArrayInputStream
import java.lang.RuntimeException
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import im.status.ethereum.module.StatusPackage
class StatusOkHttpClientFactory : OkHttpClientFactory {
companion object {
private const val TAG = "StatusOkHttpClientFactory"
private const val SLEEP_DURATION = 500L // milliseconds
}
override fun createNewNetworkModuleClient(): OkHttpClient? {
val certPem = getCertificatePem().takeIf { it.isNotEmpty() }
?: return logAndReturnNull("Certificate is empty, cannot create OkHttpClient without a valid certificate")
val cert = try {
induceSleep()
// Convert PEM certificate string to X509Certificate object
CertificateFactory.getInstance("X.509")
.generateCertificate(ByteArrayInputStream(certPem.toByteArray())) as? X509Certificate
?: return logAndReturnNull("Certificate could not be parsed as non-null")
} catch (e: Exception) {
return logAndReturnNull("Could not parse certificate", e)
}
val clientCertificates = try {
induceSleep()
// Create HandshakeCertificates object with our certificate
HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addTrustedCertificate(cert)
.build()
} catch (e: Exception) {
return logAndReturnNull("Could not build HandshakeCertificates", e)
}
return try {
OkHttpClientProvider.createClientBuilder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)
.build()
} catch (e: Exception) {
logAndReturnNull("Could not create OkHttpClient", e)
}
}
private fun getCertificatePem(): String {
return try {
// Create OkHttpClient with custom SSL socket factory and trust manager
StatusPackage.getImageTLSCert().takeIf { !it.isNullOrBlank() }
?: logAndReturnEmpty("Certificate PEM string is null or empty")
} catch (e: Exception) {
logAndReturnEmpty("Could not getImageTLSCert", e)
}
}
private fun induceSleep() {
try {
// induce half second sleep because sometimes a cert is not immediately available
// TODO : remove sleep if App no longer crashes on Android 10 devices with
// java.lang.RuntimeException: Could not invoke WebSocketModule.connect
Thread.sleep(SLEEP_DURATION)
} catch (e: InterruptedException) {
Log.e(TAG, "Sleep interrupted", e)
}
}
private fun logAndReturnNull(message: String, e: Exception? = null): Nothing? {
Log.e(TAG, message, e)
return null
}
private fun logAndReturnEmpty(message: String, e: Exception? = null): String {
Log.e(TAG, message, e)
return ""
}
}
@@ -0,0 +1,44 @@
package im.status.ethereum;
import android.util.Log;
import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;
import okhttp3.Interceptor;
import okhttp3.tls.HandshakeCertificates;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.RuntimeException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import im.status.ethereum.module.StatusPackage;
class StatusOkHttpClientFactory implements OkHttpClientFactory {
public OkHttpClient createNewNetworkModuleClient() {
String certPem = StatusPackage.getImageTLSCert();
X509Certificate cert;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certPem.getBytes()));
} catch(Exception e) {
Log.e("StatusOkHttpClientFactory", "Could not parse certificate");
cert = null;
}
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addTrustedCertificate(cert)
.build();
return OkHttpClientProvider.createClientBuilder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
}
}
+11 -10
View File
@@ -4,18 +4,13 @@
* Project-wide gradle configuration properties for use by all modules * Project-wide gradle configuration properties for use by all modules
*/ */
ext { ext {
// kotlin_version is needed for react-native-camera-kit library RNNKotlinVersion = "1.4.31"
kotlin_version = project.kotlinPluginVersion
RNNKotlinVersion = project.kotlinPluginVersion
RNGH_kotlinVersion = project.kotlinPluginVersion
buildToolsVersion = project.buildToolsVersion buildToolsVersion = project.buildToolsVersion
minSdkVersion = Integer.valueOf(project.minSdkVersion) minSdkVersion = Integer.valueOf(project.minSdkVersion)
compileSdkVersion = Integer.valueOf(project.compileSdkVersion) compileSdkVersion = Integer.valueOf(project.compileSdkVersion)
targetSdkVersion = Integer.valueOf(project.targetSdkVersion) targetSdkVersion = Integer.valueOf(project.targetSdkVersion)
supportLibVersion = project.supportLibVersion supportLibVersion = project.supportLibVersion
gradlePluginVersion = project.gradlePluginVersion gradlePluginVersion = project.gradlePluginVersion
kotlinVersion = project.kotlinPluginVersion
ndkVersion = "25.2.9519653"
} }
buildscript { buildscript {
@@ -25,9 +20,10 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${project.ext.kotlinPluginVersion}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31"
classpath("com.android.tools.build:gradle") classpath "com.android.tools.build:gradle:${project.ext.gradlePluginVersion}"
classpath("com.facebook.react:react-native-gradle-plugin") // WARNING: Do not place your application dependencies here!
// They belong in the individual module build.gradle files.
} }
} }
@@ -52,14 +48,19 @@ allprojects {
throw new GradleException('STATUS_GO_ANDROID_LIBDIR environment variable is not valid!') throw new GradleException('STATUS_GO_ANDROID_LIBDIR environment variable is not valid!')
} }
} }
repositories { repositories {
mavenLocal() mavenLocal()
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
maven { url("$rootDir/../node_modules/react-native/android") }
// Android JSC is installed from npm // Android JSC is installed from npm
maven { url("$rootDir/../node_modules/jsc-android/dist") } maven { url("$rootDir/../node_modules/jsc-android/dist") }
// For geth, function, and status-go // For geth, function, and status-go
flatDir { dirs "libs", System.env.STATUS_GO_ANDROID_LIBDIR } flatDir { dirs "libs", System.env.STATUS_GO_ANDROID_LIBDIR }
// Everything Else
google() google()
jcenter() // Required for @react-native-community/blur:3.6.1, can be removed after updating to 3.6.1+
mavenCentral() mavenCentral()
maven { url 'https://mvnrepository.com/artifact/com.github.gundy/semver4j'} maven { url "https://www.jitpack.io" }
} }
} }
+10 -18
View File
@@ -21,14 +21,14 @@
# $keytool -genkey -v -keystore ./status-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias status # $keytool -genkey -v -keystore ./status-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias status
# Version requirements used throughout the Gradle scripts # Version requirements used throughout the Gradle scripts
minSdkVersion=24 kotlinVersion=1.3.50
compileSdkVersion=33 minSdkVersion=23
targetSdkVersion=33 compileSdkVersion=31
buildToolsVersion=33.0.0 targetSdkVersion=31
buildToolsVersion=31.0.0
supportLibVersion=28.0.0 supportLibVersion=28.0.0
# This should match version from nix/pkgs/aapt2/default.nix # This should match version from nix/mobile/android/maven-and-npm-deps/maven/default.nix
gradlePluginVersion=7.4.2 gradlePluginVersion=3.5.4
kotlinPluginVersion=1.9.0
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
@@ -41,20 +41,12 @@ KEYSTORE_KEY_PASSWORD=password
# By default we build a mostly universal APK # By default we build a mostly universal APK
ANDROID_ABI_SPLIT=false ANDROID_ABI_SPLIT=false
# Some platforms are excluded though # Some platforms are excluded though
ANDROID_ABI_INCLUDE=armeabi-v7a;arm64-v8a;x86;x86_64 ANDROID_ABI_INCLUDE=armeabi-v7a;arm64-v8a;x86
org.gradle.jvmargs=-Xmx8704M org.gradle.jvmargs=-Xmx8704M
versionCode=9999 versionCode=9999
commitHash=unknown commitHash=unknown
# Use this property to enable support to the new architecture. # Flipper
# This will allow you to use TurboModules and the Fabric render in FLIPPER_VERSION=0.54.0
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
+2 -1
View File
@@ -12,10 +12,11 @@ apply from: file("../node_modules/@react-native-community/cli-platform-android/n
include ':react-native-nfc-manager' include ':react-native-nfc-manager'
project(':react-native-nfc-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-nfc-manager/android') project(':react-native-nfc-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-nfc-manager/android')
include ':app' include ':app'
include ':react-native-dialogs'
project(':react-native-dialogs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dialogs/android')
include ':react-native-status' include ':react-native-status'
project(':react-native-status').projectDir = new File(rootProject.projectDir, '../modules/react-native-status/android') project(':react-native-status').projectDir = new File(rootProject.projectDir, '../modules/react-native-status/android')
include ':react-native-status-keycard' include ':react-native-status-keycard'
project(':react-native-status-keycard').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status-keycard/android') project(':react-native-status-keycard').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status-keycard/android')
include ':react-native-blur' include ':react-native-blur'
project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/blur/android') project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/blur/android')
includeBuild('../node_modules/@react-native/gradle-plugin')
+1 -1
View File
@@ -1,4 +1,4 @@
{ {
"name": "StatusIm", "name": "StatusIm",
"displayName": "StatusIm" "displayName": "StatusIm"
} }
+23 -18
View File
@@ -1,19 +1,24 @@
module.exports = { module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin', '@babel/plugin-transform-named-capturing-groups-regex'], "presets": [
env: { "module:metro-react-native-babel-preset"
test: { ],
presets: [ "plugins": [
'@babel/preset-react', "react-native-reanimated/plugin"
[ ],
'@babel/preset-env', "env": {
{ "test": {
targets: { "presets": [
node: 'current', '@babel/preset-react',
}, [
}, '@babel/preset-env',
], {
], targets: {
}, node: '14',
}, },
}; },
],
],
}
}
}
+6 -3
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.8.4'
/* Options section can't access functions in objects. */ /* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild() def isPRBuild = utils.isPRBuild()
pipeline { pipeline {
agent { label 'linux && x86_64 && nix-2.14' } agent { label 'linux && x86_64 && nix-2.11' }
options { options {
timestamps() timestamps()
@@ -41,12 +40,16 @@ pipeline {
BUILD_ENV = 'prod' BUILD_ENV = 'prod'
NIX_CONF_DIR = "${env.WORKSPACE}/nix" NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1 FASTLANE_DISABLE_COLORS = 1
/* coverage report identification */
COVERALLS_SERVICE_NAME = "jenkins"
COVERALLS_SERVICE_JOB_ID = "${JOB_NAME}#${BUILD_NUMBER}"
} }
stages { stages {
stage('Prep') { stage('Prep') {
steps { steps {
script { script {
utils.doGitRebasePR()
utils.symlinkEnv() utils.symlinkEnv()
println("Build Number: ${utils.genBuildNumber()}") println("Build Number: ${utils.genBuildNumber()}")
} }
+22 -5
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.8.4'
pipeline { pipeline {
agent { label 'linux' } agent { label 'linux' }
@@ -9,6 +8,8 @@ pipeline {
disableConcurrentBuilds() disableConcurrentBuilds()
/* Prevent Jenkins jobs from running forever */ /* Prevent Jenkins jobs from running forever */
timeout(time: 40, unit: 'MINUTES') timeout(time: 40, unit: 'MINUTES')
/* Allow copying of artifacts from this job. */
copyArtifactPermission('/status-mobile/e2e/*')
/* Limit builds retained */ /* Limit builds retained */
buildDiscarder(logRotator( buildDiscarder(logRotator(
numToKeepStr: '10', numToKeepStr: '10',
@@ -18,7 +19,7 @@ pipeline {
} }
/* WARNING: Defining parameters here with the ?: trick causes them to remember last value. */ /* WARNING: Defining parameters here with the ?: trick causes them to remember last value. */
parameters { parameters {
choice( choice(
name: 'BUILD_TYPE', name: 'BUILD_TYPE',
description: 'Makefile target to build. Optional Parameter.', description: 'Makefile target to build. Optional Parameter.',
@@ -47,6 +48,9 @@ pipeline {
stage('Android') { steps { script { stage('Android') { steps { script {
apk = jenkins.Build('status-mobile/platforms/android') apk = jenkins.Build('status-mobile/platforms/android')
} } } } } }
stage('Android e2e') { steps { script {
apke2e = jenkins.Build('status-mobile/platforms/android-e2e')
} } }
stage('Tests') { steps { script { stage('Tests') { steps { script {
jenkins.Build('status-mobile/platforms/tests') jenkins.Build('status-mobile/platforms/tests')
} } } } } }
@@ -56,7 +60,9 @@ pipeline {
steps { script { steps { script {
sh('rm -f pkg/*') sh('rm -f pkg/*')
jenkins.copyArts(ios) jenkins.copyArts(ios)
//jenkins.copyArts(iose2e)
jenkins.copyArts(apk) jenkins.copyArts(apk)
jenkins.copyArts(apke2e)
sha = "pkg/${utils.pkgFilename(ext: 'sha256')}" sha = "pkg/${utils.pkgFilename(ext: 'sha256')}"
dir('pkg') { dir('pkg') {
/* generate sha256 checksums for upload */ /* generate sha256 checksums for upload */
@@ -70,8 +76,8 @@ pipeline {
/* object for easier URLs handling */ /* object for easier URLs handling */
urls = [ urls = [
/* mobile */ /* mobile */
Apk: utils.pkgUrl(apk), Apk: utils.pkgUrl(apk), Apke2e: utils.pkgUrl(apke2e),
iOS: utils.pkgUrl(ios), iOS: utils.pkgUrl(ios), /*iOSe2e: utils.pkgUrl(iose2e),*/
Diawi: utils.getEnv(ios, 'DIAWI_URL'), Diawi: utils.getEnv(ios, 'DIAWI_URL'),
/* upload the sha256 checksums file too */ /* upload the sha256 checksums file too */
SHA: s3.uploadArtifact(sha), SHA: s3.uploadArtifact(sha),
@@ -95,6 +101,17 @@ pipeline {
} }
} } } }
} }
stage('Run e2e') {
when { expression { btype == 'nightly' && params.PUBLISH } }
steps { script {
e2eApk = utils.getEnv(apke2e, 'SAUCE_URL')
build(
job: 'status-mobile/e2e/status-app-nightly',
parameters: [string(name: 'APK_URL', value: e2eApk)],
wait: false
)
} }
}
} }
} }
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
pipeline {
agent { label 'linux' }
triggers {
// Nightly at 2am
cron 'H 2 * * *'
}
parameters {
string(
name: 'BRANCH',
description: 'Name of the branch to checkout and build.',
defaultValue: 'develop',
)
}
options {
timestamps()
disableConcurrentBuilds()
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
))
}
stages {
stage('Build') {
steps {
script {
apk_build = jenkins.Build('status-mobile/platforms/android-e2e')
apk_build_number = apk_build.getNumber().toString()
}
}
}
stage('Run e2e') {
steps {
build(
job: 'status-mobile/e2e/status-app-nightly',
parameters: [
string(name: 'APK_BUILD_NUMBER', value: apk_build_number),
string(name: 'BRANCH', value: env.BRANCH),
]
)
}
}
}
}
+6 -9
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.8.4'
/* Options section can't access functions in objects. */ /* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild() def isPRBuild = utils.isPRBuild()
pipeline { pipeline {
agent { label 'macos && arm64 && nix-2.14 && xcode-15.1' } agent { label 'macos && arm64 && nix-2.11 && xcode-14.2' }
parameters { parameters {
string( string(
@@ -49,6 +48,7 @@ pipeline {
stage('Prep') { stage('Prep') {
steps { steps {
script { script {
utils.doGitRebasePR()
utils.symlinkEnv() utils.symlinkEnv()
println("Build Number: ${utils.genBuildNumber()}") println("Build Number: ${utils.genBuildNumber()}")
} }
@@ -64,9 +64,7 @@ pipeline {
script { api = ios.bundle() } script { api = ios.bundle() }
} }
post { post {
failure { failure { archiveArtifacts 'ios/logs/*' }
archiveArtifacts 'ios/logs/*'
}
} }
} }
stage('Parallel Upload') { stage('Parallel Upload') {
@@ -77,10 +75,9 @@ pipeline {
} }
} }
stage('Upload') { stage('Upload') {
when { expression { !utils.isReleaseBuild() } }
steps { script { steps { script {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { env.DIAWI_URL = ios.uploadToDiawi()
env.DIAWI_URL = ios.uploadToDiawi()
}
env.PKG_URL = env.DIAWI_URL env.PKG_URL = env.DIAWI_URL
jenkins.setBuildDesc(IPA: env.PKG_URL) jenkins.setBuildDesc(IPA: env.PKG_URL)
} } } }
+116
View File
@@ -0,0 +1,116 @@
library 'status-jenkins-lib@v1.6.9'
pipeline {
agent { label params.AGENT_LABEL }
parameters {
string(
name: 'NIX_CACHE_HOST',
description: 'FQDN of Nix binary cache host.',
defaultValue: params.NIX_CACHE_HOST ?: 'cache-01.do-ams3.nix.ci.statusim.net'
)
string(
name: 'NIX_CACHE_USER',
description: 'Username for Nix binary cache host.',
defaultValue: params.NIX_CACHE_USER ?: 'nix-cache'
)
}
environment {
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
/* we source .bash_profile to be able to use nix-store */
NIX_SSHOPTS = "-o StrictHostKeyChecking=no source .profile;"
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
}
options {
timestamps()
disableConcurrentBuilds()
/* Prevent Jenkins jobs from running forever */
timeout(time: 300, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '20',
daysToKeepStr: '30',
))
}
stages {
stage('Setup') {
steps { script {
nix.shell('nix-env -i openssh', sandbox: false, pure: false)
/* some build targets don't build on MacOS */
uname = sh(script: 'uname', returnStdout: true)
} }
}
stage('Build status-go') {
steps { script {
def platforms = ['mobile.android', 'mobile.ios']
if (uname != "Darwin") {
platforms.removeAll { it == "ios" }
}
platforms.each { os ->
nix.build(
attr: "targets.status-go.${os}.buildInputs",
sandbox: false,
link: false
)
}
} }
}
stage('Build android jsbundle') {
steps { script {
/* build/fetch things required to produce a js-bundle for android
* (e.g. maven and node repos) */
nix.build(
attr: 'targets.mobile.android.jsbundle',
sandbox: false,
pure: false,
link: false
)
} }
}
stage('Build android deps') {
steps { script {
/* build/fetch things required to build jsbundle and android */
nix.build(
attr: 'targets.mobile.android.release.buildInputs',
sandbox: false,
pure: false,
link: false
)
} }
}
stage('Build nix shell deps') {
steps { script {
/* build/fetch things required to instantiate shell.nix for TARGET=all */
nix.build(
attr: 'shells.default.buildInputs',
sandbox: false,
link: false
)
} }
}
stage('Upload') {
steps { script {
sshagent(credentials: ['nix-cache-ssh']) {
nix.shell("""
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
-not -name '*.links' -and -not -name '*-status-mobile-*' \
| xargs nix copy \
--to ssh-ng://${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}
""",
pure: false
)
}
} }
}
}
post {
always { script {
nix.shell('nix-store --optimize', pure: false)
nix.shell('nix/scripts/clean.sh', pure: false)
} }
}
}
+13 -15
View File
@@ -1,16 +1,15 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.8.4'
/* Options section can't access functions in objects. */ /* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild() def isPRBuild = utils.isPRBuild()
pipeline { pipeline {
agent { label 'linux && x86_64 && nix-2.14' } agent { label 'linux && x86_64 && nix-2.11' }
options { options {
timestamps() timestamps()
/* Prevent Jenkins jobs from running forever */ /* Prevent Jenkins jobs from running forever */
timeout(time: 15, unit: 'MINUTES') timeout(time: 10, unit: 'MINUTES')
/* Limit builds retained */ /* Limit builds retained */
buildDiscarder(logRotator( buildDiscarder(logRotator(
numToKeepStr: '10', numToKeepStr: '10',
@@ -42,34 +41,33 @@ pipeline {
} }
stages { stages {
stage('Prep') {
steps {
script {
utils.doGitRebasePR()
}
}
}
stage('Checks') { stage('Checks') {
parallel { parallel {
stage('Lint') { stage('Lint') {
steps { steps {
sh """#!/bin/bash sh """#!/bin/bash
set -eo pipefail set -eo pipefail
make lint CLJ_LINTER_PRINT_WARNINGS=true 2>&1 | tee ${LOG_FILE} make lint 2>&1 | tee ${LOG_FILE}
""" """
} }
} }
stage('Unit Tests') { stage('Tests') {
steps { steps {
sh """#!/bin/bash sh """#!/bin/bash
set -eo pipefail set -eo pipefail
make test-unit 2>&1 | tee -a ${LOG_FILE} make test 2>&1 | tee -a ${LOG_FILE}
""" """
} }
} }
} }
} }
stage('Integration Tests') {
steps {
sh """#!/bin/bash
set -eo pipefail
make test-integration 2>&1 | tee -a ${LOG_FILE}
"""
}
}
stage('Component Tests') { stage('Component Tests') {
steps { steps {
sh """#!/bin/bash sh """#!/bin/bash
+10 -20
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
@@ -7,24 +6,14 @@ pipeline {
parameters { parameters {
string( string(
name: 'APK_BUILD_NUMBER', name: 'APK_URL',
description: 'platform/e2e build number for apk artifact', description: 'URL of APK uploaded to SauceLabs.',
) )
string( string(
name: 'KEYWORD_EXPRESSION', name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)', description: 'This will run tests which contain names that match the given string expression (Optional)',
defaultValue: '', defaultValue: '',
) )
string(
name: 'BRANCH',
description: 'Name of the branch to checkout and build.',
defaultValue: 'develop',
)
string(
name: 'TR_CASE_IDS',
description: 'IDs of the TestRail case, separated by a comma (Optional)',
defaultValue: '',
)
} }
options { options {
@@ -33,14 +22,15 @@ pipeline {
stages { stages {
stage('Fetch') { stage('Fetch') {
when { expression { !params.APK_URL } }
steps { script { steps { script {
copyArtifacts( copyArtifacts(
projectName: "status-mobile/platforms/android-e2e", projectName: "status-mobile/nightly",
filter: 'result/*-x86_64.apk', filter: '*-x86.apk',
selector: specific(env.APK_BUILD_NUMBER), /* WARNING: This copies the latest available artifact. */
selector: lastWithArtifacts(),
) )
apk_path = "${env.WORKSPACE}/${utils.findFile('result/*-x86_64.apk')}" apk_path = "${env.WORKSPACE}/${utils.findFile('*-x86.apk')}"
} } } }
} }
@@ -83,7 +73,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py' sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """ sh """
python3 -m pytest \ python3 -m pytest \
--numprocesses 8 \ --numprocesses 4 \
--rerun_count=2 \ --rerun_count=2 \
--testrail_report=True \ --testrail_report=True \
-m testrail_id \ -m testrail_id \
+2 -4
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
@@ -46,7 +45,6 @@ pipeline {
options { options {
disableConcurrentBuilds() disableConcurrentBuilds()
timeout(time: 90, unit: 'MINUTES')
} }
stages { stages {
@@ -123,7 +121,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py' sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """ sh """
python3 -m pytest \ python3 -m pytest \
--numprocesses 8 \ --numprocesses 4 \
--rerun_count=2 \ --rerun_count=2 \
--testrail_report=True \ --testrail_report=True \
-k \"${params.KEYWORD_EXPRESSION}\" \ -k \"${params.KEYWORD_EXPRESSION}\" \
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
agent { label 'macos' } agent { label 'macos' }
-138
View File
@@ -1,138 +0,0 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
pipeline {
agent { label params.AGENT_LABEL }
parameters {
string(
name: 'NIX_CACHE_HOST',
description: 'FQDN of Nix binary cache host.',
defaultValue: params.NIX_CACHE_HOST ?: 'cache-01.do-ams3.nix.ci.statusim.net'
)
string(
name: 'NIX_CACHE_USER',
description: 'Username for Nix binary cache host.',
defaultValue: params.NIX_CACHE_USER ?: 'nix-cache'
)
}
environment {
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
/* we source .bash_profile to be able to use nix-store */
NIX_SSHOPTS = "-oStrictHostKeyChecking=no"
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
NIX_STORE_CMD = '/nix/var/nix/profiles/default/bin/nix-store'
NIX_SSH_REMOTE = "ssh://${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}?remote-program=${env.NIX_STORE_CMD}"
}
options {
timestamps()
disableConcurrentBuilds()
/* Prevent Jenkins jobs from running forever */
timeout(time: 120, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '20',
daysToKeepStr: '30',
))
}
stages {
stage('Setup') {
steps { script {
nix.shell('nix-env -i openssh', sandbox: false, pure: false)
/* some build targets don't build on MacOS */
os = sh(script: 'uname', returnStdout: true).trim()
arch = sh(script: 'arch', returnStdout: true).trim()
} }
}
stage('Build status-go') {
steps { script {
def platforms = ['mobile.android', 'mobile.ios', 'library']
if (os != 'Darwin') { platforms.removeAll { it == 'mobile.ios' } }
/* FIXME: "'x86_64-darwin' with features {} is required to build" */
if (arch == 'arm64') { platforms.removeAll { it == 'mobile.android' } }
platforms.each { platform ->
/* Allow for Android builds on Apple ARM. */
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, platform)
nix.build(
attr: "targets.status-go.${platform}",
sandbox: false,
link: false
)
}
} }
}
stage('Build android jsbundle') {
steps { script {
/* Build/fetch deps required for jsbundle build. */
nix.build(
attr: 'targets.mobile.jsbundle',
sandbox: false,
pure: false,
link: false
)
} }
}
stage('Build android deps') {
steps { script {
/* Allow for Android builds on Apple ARM. */
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, 'android')
/* Build/fetch deps required to build android release. */
nix.build(
attr: 'targets.mobile.android.release.buildInputs',
sandbox: false,
pure: false,
link: false
)
} }
}
stage('Build nix shell deps') {
steps { script {
def shells = ['android', 'ios', 'fastlane', 'keytool', 'clojure', 'gradle']
if (os != 'Darwin') { shells.removeAll { it == 'ios' } }
/* FIXME: "'x86_64-darwin' with features {} is required to build" */
if (arch == 'arm64') { shells.removeAll { it == 'android' } }
/* Build/fetch deps required to start default Nix shell. */
shells.each { shell ->
/* Allow for Android builds on Apple ARM. */
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, shell)
nix.build(
attr: "shells.${shell}.buildInputs",
sandbox: false,
link: false
)
}
} }
}
stage('Upload') {
steps { script {
sshagent(credentials: ['nix-cache-ssh']) {
nix.shell("""
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
-not -name '*.links' -and -not -name '*-status-mobile-*' \
| xargs nix copy --to ${NIX_SSH_REMOTE}
""",
pure: false
)
}
} }
}
}
post {
always { script {
nix.shell('nix-store --optimize', pure: false)
nix.shell('nix/scripts/clean.sh', pure: false)
} }
}
}
def nixSysOverride(os, arch, target='android') {
return (
os == 'Darwin' &&
arch == 'arm64' &&
target =~ /.*android$/
) ? 'x86_64-darwin' : ''
}
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
agent { label 'linux' } agent { label 'linux' }
-1
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env groovy
/** /**
* This job runs daily and executes `pod repo update` on MacOS * This job runs daily and executes `pod repo update` on MacOS
* This is done to avoid issues with out of date repo causing errors like: * This is done to avoid issues with out of date repo causing errors like:
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy library 'status-jenkins-lib@v1.6.9'
library 'status-jenkins-lib@v1.7.15'
pipeline { pipeline {
agent { agent {
-15
View File
@@ -16,17 +16,9 @@
[Merging PR process](merging-pr-process.md) [Merging PR process](merging-pr-process.md)
[PR Review Policy](pr-review-policy.md)
[Working on PR together with QA team](pipeline_process.md) [Working on PR together with QA team](pipeline_process.md)
[Debugging](debugging.md)
[Patching](patching.md)
[Creating a pixel perfect UI](pixel-perfection.md)
[Contributing to status-go](status-go-changes.md)
## Testing ## Testing
@@ -37,13 +29,6 @@
[Component tests (jest) overview](component-tests-overview.md) [Component tests (jest) overview](component-tests-overview.md)
## Project details
[status-go introduction (recorded meeting)](https://drive.google.com/file/d/1B7TljmTZ8fHkqJH8ChU1Cp4FGDFM03gq/view)
[re-frame usage (recorded meeting)](https://drive.google.com/file/d/1qv_E0CEGzQpu_zGXD0gCTU5EvhC2k8Jy/view)
[status app functionality demo](https://drive.google.com/file/u/0/d/1PmwsMLTLDNNIdv5_6wvMOwoj2PfT50c6/view)
## Misc ## Misc
+19
View File
@@ -91,11 +91,30 @@ These guidelines make db.cljs namespaces the place to go when making changes to
- If manual QA is not needed but all tests don't pass, you can ping @churik or @Serhy to confirm that failed E2E tests are not unrelated. - If manual QA is not needed but all tests don't pass, you can ping @churik or @Serhy to confirm that failed E2E tests are not unrelated.
## Enabling debug logs
Calls to `log/debug` will not be printed to the console by default. It can be enabled under "Advanced settings" in the app:
![Enable Debug Logs](images/codebase-structure-and-guidelines/log-settings.png)
## Translations ## Translations
The app relies on system locale to select a language from the [list of supported languages](https://github.com/status-im/status-mobile/blob/bda73867471cf2bb8a68b1cc27c9f94b92d9a58b/src/status_im/i18n_resources.cljs#L9). It falls back to English in cash the system locale is not supported. The app relies on system locale to select a language from the [list of supported languages](https://github.com/status-im/status-mobile/blob/bda73867471cf2bb8a68b1cc27c9f94b92d9a58b/src/status_im/i18n_resources.cljs#L9). It falls back to English in cash the system locale is not supported.
We use Lokalise App to manage [translations](https://translate.status.im/). In case you need to add/remove a key to translations, you only need to change `en.json`. Missing keys fallback to `en.json`. The actual translations will be added by Lokalise. We use Lokalise App to manage [translations](https://translate.status.im/). In case you need to add/remove a key to translations, you only need to change `en.json`. Missing keys fallback to `en.json`. The actual translations will be added by Lokalise.
## re-frisk
re-frisk is a state visualization tool written by our very own Andrey (@flexsurfer). To start re-frisk, execute the following command:
```bash
$ yarn shadow-cljs run re-frisk-remote.core/start
```
or you can also use make:
```bash
$ make run-re-frisk
```
A server will be started at http://localhost:4567. It might show "not connected" at first. Don't worry and just start using the app. The events and state will populate.
## Merging approved PRs ## Merging approved PRs
We don't Github's UI to merge. Instead `./scripts/merge-pr.sh` is used to sign and merge PR to `develop`. You first need to enable [GPG signing on you commits](https://github.com/status-im/status-mobile/blob/develop/STARTING_GUIDE.md#configure-gpg-keys-for-signing-commits). We don't Github's UI to merge. Instead `./scripts/merge-pr.sh` is used to sign and merge PR to `develop`. You first need to enable [GPG signing on you commits](https://github.com/status-im/status-mobile/blob/develop/STARTING_GUIDE.md#configure-gpg-keys-for-signing-commits).
+3 -3
View File
@@ -23,13 +23,13 @@ setups and runs the test suite once.
setups and runs the test suite and watches for code changes will then retrigger the test suite. setups and runs the test suite and watches for code changes will then retrigger the test suite.
## Writing Tests ## Writing Tests
New test files will need their namespace added to either the file "src/quo/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadow-cljs config discussed below. New test files will need their namespace added to either the file "src/quo2/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadowcljs config discussed below.
### Best practices ### Best practices
For the moment we will keep best practices for tests in our other guidelines document: For the moment we will keep best practices for tests in our other guidelines document:
To that point these guidelines will follow the conventions of Jest and React Native Testing Library recommendations and Status mobile will just stack their preferences on top. To that point these guidelines will follow the conventions of Jest and React Native Testing Library recomendations and Status mobile will just stack their preferences on top.
### Utilities ### Utilities
There is a file of utility functions defined in "src/test_helpers/component.cljs" and "src/test_helpers/component.clj". It will be great to use these utilities and to add any common testing tools to these files as it should make writing tests easier and faster. There is a file of utility functions defined in "src/test_helpers/component.cljs" and "src/test_helpers/component.clj". It will be great to use these utilities and to add any common testing tools to these files as it should make writing tests easier and faster.
@@ -52,4 +52,4 @@ It's worth knowing that our tests are compiled to JS and then run in the tempora
### Jest ### Jest
There is also further configuration for Jest in "test/jest". There is a jest config file which has some mostly standard configuration pieces, where the tests live, what environment variables are set etc. This is documented by Jest here: https://jestjs.io/docs/configuration There is also further configuration for Jest in "test/jest". There is a jest config file which has some mostly standard configuration pieces, where the tests live, what environment variables are set etc. This is documented by Jest here: https://jestjs.io/docs/configuration
There is also a setup file which is used to set some global and default values. Additionally this file is used to mock some of the react native (among other) dependencies There is also a setup file which is used to set some global and default values. Additionally this file is used to mock some of the react native (among other) dependencies
-136
View File
@@ -1,136 +0,0 @@
# Debugging
## Inspecting re-frame with re-frisk
`re-frisk` is a state visualization tool written by our very own Andrey (@flexsurfer). With its help you can inspect the current state of app-db, watch event, etc.
![re-frisk](images/debugging/re-frisk.png)
To start `re-frisk`, execute the following command:
```bash
$ yarn shadow-cljs run re-frisk-remote.core/start
```
or you can also use make:
```bash
$ make run-re-frisk
```
A server will be started at http://localhost:4567. It might show "not connected" at first. Don't worry and just start using the app. The events and state will populate.
More details about re-frisk are on the [project page](https://github.com/flexsurfer/re-frisk).
## Enabling debug logs
Calls to `log/debug` will not be printed to the console by default. It can be enabled under "Advanced settings" in the app:
![Enable Debug Logs](images/debugging/log-settings.png)
## Checking status-go logs
While status mobile works it saves logs from `status-go` to `geth.log` file.
### Checking logs from physical device
To obtain `geth.log` from physical device you need to shake it and in an opened menu select "Share logs".
![Share logs](images/debugging/share-logs.jpeg)
### Checking logs from iOS Simulator
When developing with iOS simulator it is more convenient to see the `geth.log` updates in real-time.
To do this:
- open Activity Monitor
- find the "StatusIm" app and doubleclick it
- in the opened window select "Open files and ports" and find the full path to `geth.log` (note that it won't appear until you login to Status app)
![geth.log path](images/debugging/geth-path.png)
## Inspecting database content
Encrypted database can be found using commands:
```
cd ~/Library/Developer/CoreSimulator/Devices
find ./ -name accounts.sql
```
To get unencrypted database you need to export it first:
- open the status app in simulator
- on login screen enter the correct password without logging in
- using repl execute statement to export db:
```
(re-frame.core/dispatch [:multiaccounts.login.ui/export-db-submitted])
```
- save generated `export.db` file
Now you can locate the `export.db` and open it with preferred db viewer.
**Android:**
```sh
adb root
adb pull /storage/emulated/0/Android/data/im.status.ethereum.debug/files/Download/export.db /path/to/store/export.db
```
If you're using a release build, change the path to `/storage/emulated/0/Android/data/im.status.ethereum/files/Download`
**iOS**
```
cd ~/Library/Developer/CoreSimulator/Devices
find ./ -name export.db
```
## Tips
### From @ilmotta:
Something I find extremely convenient for Android is to use `adb` to tail logs. I don't use macOS so I don't know if the iOS simulator offers a CLI interface with the same capabilities.
But here's what I use for example:
```
adb shell tail -n 10 -f /storage/emulated/0/Android/data/im.status.ethereum.debug/files/Download/geth.log | grep 'waku.relay'
```
Also to inspect logs in a more flexible manner, instead of the strict output from `make run-metro`, I prefer `adb logcat`. Combined with enabling status-mobile logs in debug by default plus filtering the logs to only what I care during development, I find this helps me inspect the app without running re-frisk because with the debug log level I can already see which events are dispatched (one of the features I like the most from re-frisk).
```
adb logcat | grep 'ReactNativeJS\|StatusModule\|GoLog'
```
### From @clauxx (visual debugging):
When building/fixing/refactoring components, it can be annoying to set-up the component for instant visual feedback (i.e. finding it in the UI when fixing/refactoring; finding a place for it when building from scratch). Very often if the location of the screen containing your component nested deeply in the app, you might have to do a lot of repeated navigation, nevermind logging in every time the app reloads (even with fast refresh disabled this happens quite often).
To alleviate this discomfort and make the development cycle more *pleasant*, you can use one of the following options (*_both of which involve the ... REPL ༼ つ ╹ ╹ ༽つ, so make sure you have that set-up with your IDE/editor_*)
#### REPL-ing to a screen
This one is straightforward, just evaluate a navigation dispatch form from anywhere:
```cljs
(comment (rf/dispatch [:navigate-to :your-favorite-buggy-screen]))
```
> TIP: you can run this from an untracked user namespace `(ns user)`, from which you can experiment or interact with the REPL. This is where I usually keep my development navigation forms.
#### REPL-ing a component
This is also straighforward, but there are some small differences. Just like above, we only have to evaluate a dispatch form, as follows:
```cljs
;; example debugging a quo component, although it could be any other component e.g. from the `status-im2` ns.
(comment (re-frame/dispatch [:dev/preview-component
[quo/slide-button
{:track-icon :face-id
:track-text "preview component example"
:customization-color :blue
:on-complete identity}]]))
```
When evaluated, a full-screen bottom sheet will appear with your component inside. You can make changes to the props or the component internals and re-evaluate the changed forms, after which the component will be re-mounted without running a reload (unless you save the file). Other times it's quicker to just hit save and the component will be reloaded as usual (except for the props you evaluated, which have to be re-evaluated when changed).
> TIP: You can have multiple versions of the component as separate comments to check for the component variations
> TIP: You can leave `comment` blocks at the end of component files (other than quo, since can be previewed in other ways) with usages of components, which other developers can use for visualisation/testing.
@@ -18,7 +18,7 @@ While neat in theory, it has some serious downsides:
As a result of that, more dynamic/state requiring things (like the live tx detail in `/send` command messages) were very hard to do, As a result of that, more dynamic/state requiring things (like the live tx detail in `/send` command messages) were very hard to do,
so instead of "eating our own dogfood", we decided to side-step the API and implement such things as hard-coded logic in the app, while partly so instead of "eating our own dogfood", we decided to side-step the API and implement such things as hard-coded logic in the app, while partly
retaining the js code for "easier" things (like parameter declaration). retaining the js code for "easier" things (like parameter declaration).
Needles to say, such efforts produced code of very poor quality, riddling our app with hard-coded "magic" everywhere in the codebase, completely Needles to say, such efforts produced code of very poor quality, riddling our app with hard-coded "magic" everywhere in the codebase, completly
defeating the point of "dogfooding" while still requiring more effort and being much more error prone (no way to unit test jail logic) because defeating the point of "dogfooding" while still requiring more effort and being much more error prone (no way to unit test jail logic) because
of the need to asynchronously communicate with jail for leftover logic in command messages (the parts not hardcoded in app). of the need to asynchronously communicate with jail for leftover logic in command messages (the parts not hardcoded in app).
+1 -1
View File
@@ -13,7 +13,7 @@ accepted
There was a generally dissatisfaction with our PR flow process from multiple stakeholders, including devs, QA and design. These largely centered around size, speed of integration and quality of PRs. There was a generally dissatisfaction with our PR flow process from multiple stakeholders, including devs, QA and design. These largely centered around size, speed of integration and quality of PRs.
For more details, please see pain points in the meeting notes held end of February here: https://notes.status.im/C5pj8g7gQOu9Wo8PtDZsMw?edit# as well as the preceding Discuss thread: https://discuss.status.im/t/better-pull-requests-process/1044 For more details, please see pain points in the meeting notes held end of February here: https://notes.status.im/C5pj8g7gQOu9Wo8PtDZsMw?edit# as well as the preceeding Discuss thread: https://discuss.status.im/t/better-pull-requests-process/1044
Also see conversations in Core Dev Call #12 and #13: https://github.com/status-im/pm/ Also see conversations in Core Dev Call #12 and #13: https://github.com/status-im/pm/
+1 -1
View File
@@ -45,4 +45,4 @@ The `State` field in the Pivotal story is used to track the progress of a Pivota
| 5 points | 2 - 3 days | | 5 points | 2 - 3 days |
| 8 points | ~1 week | | 8 points | ~1 week |
We should avoid 8 point stories by breaking them down into smaller stories as much as possible. We should avoid 8 point stories by breaking them down into smaller stories as much as possible.
+3 -3
View File
@@ -18,7 +18,7 @@ The whitepaper also proposes that the deposit is only forfeited to the recipient
Considering: Considering:
- the absence of efficient ways to perform anonymous transactions (zk-snarks could be used in the future for that) - the absence of efficient ways to perform anonymous transactions (zk-snarks could be used in the future for that)
- the impossibility to prove that a recipient has made an actual reply and not some kind of automated reply (captcha solution was proposed, but wouldn't be practical until we can use a solution such as swarm feeds that allow users to make free updates to their captcha without on-chain transactions) - the impossibility to prove that a recipient has made an actual reply and not some kind of automated reply (captcha solution was proposed, but wouldn't be pratical until we can use a solution such as swarm feeds that allow users to make free updates to their captcha without on-chain transactions)
- the limited time to develop the feature - the limited time to develop the feature
We opted for a solution that: We opted for a solution that:
@@ -28,8 +28,8 @@ We opted for a solution that:
## Manifests ## Manifests
Since TtT related information aren't stored on chain, they need to be stored somewhere else. Since TtT related informations aren't stored on chain, they need to be stored somewhere else.
For the first iteration of TtT we opted for IPFS, which is already used across the app for other forms of content, through Infura IPFS gateway. For the first iteration of TtT we opted for IPFS, which is already used accross the app for other forms of content, through Infura IPFS gateway.
On IPFS we store what we call a ttt manifest, which is a json file with the following format: On IPFS we store what we call a ttt manifest, which is a json file with the following format:
-30
View File
@@ -1,30 +0,0 @@
# 0014. Team-structure
| Date | Tags |
|---|---|
| 2023-06-16 | wallet team |
## Status
Proposed
## Context:
In order to try out vertical team, which is a structure that is wanted by the company, we will be creating a wallet team within the mobile team.
Some member of the mobile team will join the current wallet team, which is composed by developers from desktop and lead by @alaibe, on a voluntary basis.
The responsibilities of the team are to implement the new wallet UI, with the support of the current wallet team, which will be assisting with the status-go work.
## Decision
The team members for the initial wallet team are:
- @rasom
- @OmarBasem
- @vkjr
- @J-Son89
- @briansztamfater
- @smohamedjavid
- @ulisesmac
- @erikseppanen
- @mmilad75
- @tumanov-alex
-46
View File
@@ -1,46 +0,0 @@
# 0015. Team-structure
| Date | Tags |
|---|---|
| 2023-06-16 | changes in team structure |
## Status
Proposed/partially implemented
## Context:
Currently, there is only a single team lead in the mobile development team, and after a meeting with the design team, it has been identified that the mobile app lacks polishness. One of the main issues identified is a lack of oversight in certain areas. To address this problem, it has been decided to create an additional role within the team. For each section of the mobile app (communities, syncing & onboarding, activity center & shell, wallet, messaging), a person will be selected to fulfill the responsibilities outlined below.
## Decision
The decision is to create an additional role within the mobile development team for each section. The individuals selected for these roles will have the following responsibilities:
- Ensure GitHub issues are created for new features/designs of the section.
- Ensure issues are adequately described and linked as best as possible.
- Ensure issues have a grooming session where other developers can provide their feedback before working on the set of issues.
- Ensure all relevant parties are invited, such as designers, QA, Product Owner, leads, etc., as needed (including the designer who created the feature).
- Ensure there is consensus among the developers working on the section about the decided strategy approach. Ideally, this consensus should be documented.
- Ensure UI quality by organizing designer reviews and guaranteeing pixel perfection.
- Ensure overall implementation is satisfactory based on the predefined requirements.
- Ensure alignment between desktop, web, and mobile versions for the respective section in collaboration with the mobile developers and design team.
- Act as a point of contact for the design team with respect to the section.
- Ensure that someone goes through the designs once they are created with the respective designer.
- Assist the design team with technical feasibility analysis and address any product concerns.
- Facilitate the growth of the next team member into the role.
The people identified for each sections are as follow:
- Messaging @flexsurfer, followed by @alwx
- Syncing & Onboarding @siddarthkay
- Communities @ilmotta, followed by @ajayesivan
- Activity Center & Shell @J-Son89, followed by @Parveshdhull
- Wallet (once the vertical team is created) @J-Son89, followed by @briansztamfater
This will be a rotating post, every 4 months it will go to the next in line.
The wallet team might have longer times for rotation, as stability might be more important. That's still to be decided.
# Follow-up Actions
- Find suitable next in line for Messaging, Onboarding, Communities
@@ -1,31 +0,0 @@
### How should the paste button behave when clipboard is empty?
This question first came up as a review comment to this PR
https://github.com/status-im/status-mobile/pull/16852
There were 2 considerations :
- Either keep the paste button disabled when there is nothing in the clipboard OR
- Always keep the paste button enabled
There were positives and negatives for both approaches.
Positives of keeping paste button disabled when there is nothing in clipboard would require us to
check the value of clipboard as soon as the component is mounted (i.e when the user first sees
the screen). In iOS this means a native permissions dialog would appear requesting for permissions
to paste from the clipboard.
Negatives of this approach is that as soon as any user navigates to this screen they are greeted with
this popup which can be annoying sometimes.
Positives of keeping paste button always enabled is that we can trigger a request to the clipboard on
tap of the paste button which would trigger the native permissions dialog requesting for permissions
to paste from the clipboard.
In this case seeing this dialog is okay because the user has initiated a paste action.
Negatives of this approach is that in the event the clipboard is empty the user will still see the
system dialog and on approving nothing will be pasted (because the clipboard was empty).
This behaviour can be confusing.
On consulting the Design Team via discord it was concluded that out of the two approaches
having the paste button always enabled is a better UX overall.
+2 -2
View File
@@ -4,7 +4,7 @@
![](images/export-icons/export-icons.gif) ![](images/export-icons/export-icons.gif)
1. Export from figma 2 pngs 2x and 3x put them in `./resources/images/icons2` 1. Export from figma 2 pngs 2x and 3x put them in `./resources/images/icons`
2. if necessary, rename file so that filename contains only lower case chars, e.g. `"Icon-Name@2x.png"` should be renamed to `"icon_name@2x.png"`. 2. if necessary, rename file so that filename contains only lower case chars, e.g. `"Icon-Name@2x.png"` should be renamed to `"icon_name@2x.png"`.
3. In the app `icon_name.png` still can be accessed as `icon-name`, so in order to use can add the next code: 3. In the app `icon_name.png` still can be accessed as `icon-name`, so in order to use can add the next code:
```clojure ```clojure
@@ -12,4 +12,4 @@
[vector-icons/icon :icon-name {:color ...}] [vector-icons/icon :icon-name {:color ...}]
``` ```
2x@2x 2x@2x
4. If you want platform specific icon use `.android` or `.ios` suffixes. Example `icon_name@2x.android.png`.:w 4. If you want paltform specific icon use `.android` or `.ios` suffixes. Example `icon_name@2x.android.png`.:w
-40
View File
@@ -1,40 +0,0 @@
# How to catch crash on ios use xcode
## Overview
Sometimes, we can't reproduce crash with local dev build, probably because your built version differs from the one QA team used.
The QA team mostly used the PR build version. In this guide we will try to make a build as close as possible to the PR build for iOS.
You may notice various configuration files starting with .env (such as .env, .env.e2e, .env.jenkins) in the project.
The usage of these files and environment variables injected at build time on CI will not be covered in this guide.
This guide covers how to configure Xcode to capture a crash stack trace with a physical iOS device. This allows debugging crashes by inspecting the stack trace.
I will use [issue #17255](https://github.com/status-im/status-mobile/issues/17255) as an example to reproduce and analyze a crash.
## Prerequisites
- A physical ios device (simulator is not considered in this doc)
- Xcode installed
## Steps
1. Prepare Status Mobile
1. Open Terminal, cd to project root
2. Run `make run-clojure` in one tab
3. Run `make run-metro` in a second tab
4. Run `make pod-install` in a third tab
2. Configure Xcode Project
1. Open Xcode
2. Open a project or file, navigate to folder `path_to_root_directory_of_status_mobile/ios`
3. Set Scheme `StatusImPR` as current scheme ![](images/how-to-catch-crash-on-ios/choose-scheme-StatusImPR.png)
4. Edit scheme ![](images/how-to-catch-crash-on-ios/edit-scheme.png)
5. Change `Info` ![](images/how-to-catch-crash-on-ios/config-release.png)
6. Change `Diagnostics` ![](images/how-to-catch-crash-on-ios/change-diagnostics.png)
7. Set automatically manage signing ![](images/how-to-catch-crash-on-ios/automatically-manage-signing.png)
3. Update Code
1. Edit `src/status_im2/setup/dev.cljs`, comment out lines 47-52 ![](images/how-to-catch-crash-on-ios/edit-dev.png)
2. Edit `shadow-cljs.edn`, comment out `re-frisk-remote.preload`, otherwise it will try to connect `localhost:4567` on physical device which could slow down performance ![](images/how-to-catch-crash-on-ios/edit-shadow-cljs.png)
4. Reproduce Crash And Analyze
1. Choose your physical ios device as target device and Run
2. Follow reproduce steps from [issue #17255](https://github.com/status-im/status-mobile/issues/17255) mentioned and xcode will halt at crash point ![](images/how-to-catch-crash-on-ios/located-stack-trace.png)
3. Inspect stack trace to identify root cause. From the stack trace, we can locate the crash happened in `api/geth_backend.go:1048`(createTempDBFile) clearly! ![](images/how-to-catch-crash-on-ios/located-go-code.png)
## Links
- [changes](https://github.com/status-im/status-mobile/commit/4306d419de15e737e8ded3a749cc5e8d6a06e627) to source code
+4 -4
View File
@@ -19,7 +19,7 @@ For now we support e2e for Android only.
## What's happening when any e2e job is running ## What's happening when any e2e job is running
Whenever we need to push set of test scripts we create 16 parallel sessions (max, but depending on amount of cases that are included in job) and each thread: 1) uploads Android .apk file to SauceLabs -> 2) runs through the test steps -> 3) receives results whether test failed on particular step or succeeded with no errors -> 3) Parse test results and push them as a Github comment (if the suite ran against respective PR) and into TestRail. Whenever we need to push set of test scripts we create 16 parallel sessions (max, but depending on amount of cases that are included in job) and each thread: 1) uploads Android .apk file to SauceLabs -> 2) runs through the test steps -> 3) receives results whether test failed on particular step or succeeded with no errors -> 3) Parse test results and push them as a Github comment (if the suite ran against respective PR) and into TestRail.
We push **whole automation test suite (currently 155, amount is changing)** against each nightly build (if the nightly builds job succeeded). Results of the test run are saved in TestRail. We push **whole automation test suite (currently 155, amout is changing)** against each nightly build (if the nightly builds job succeeded). Results of the test run are saved in TestRail.
And also we push set of autotests whenever PR with successful builds got moved in to `E2E Tests` column from [Pipeline for QA dashboard ](https://github.com/status-im/status-react/projects/7). And also we push set of autotests whenever PR with successful builds got moved in to `E2E Tests` column from [Pipeline for QA dashboard ](https://github.com/status-im/status-react/projects/7).
In that case we save results in TestRail as well and push a comment with test results in a respective PR. In that case we save results in TestRail as well and push a comment with test results in a respective PR.
@@ -49,8 +49,8 @@ Params to specify:
- apk: [url_to_apk_build_here] - apk: [url_to_apk_build_here]
- pr_id: pull request number (e.g. 1234) - pr_id: pull request number (e.g. 1234)
- branch: branch name from which the test are taken (in most of cases `develop`) - branch: branch name from which the test are taken (in most of cases `develop`)
- keyword expression: tests by area (let's say `ens` or `chat`, they can be combined`ens or chat or send_tx`. All keywords can be found in testrail, ping Chu for details) - keyword expression: tests by area (let's say `ens` or `chat`, thay can be combined`ens or chat or send_tx`. All keywords can be found in testrail, ping Chu for details)
- test_marks: tests by priorities (by default: `critical or high or medium`, which corresponds the whole suite; to launch the same suite as in PRs, use `critical or high`) - test_marks: tests by priorities (by default: `critical or high or medium`, which corresponds the whole suite; to lauch the same suite as in PRs, use `critical or high`)
- testrail_case_id: here is the list of test cases which you may find in test rail (4-digit value) - testrail_case_id: here is the list of test cases which you may find in test rail (4-digit value)
For easier access you can hit `Rerun tests` in GH comment and testrail_case_id/ apk_name/ pr_id will be filled automatically. For making sure that tests are being rerun on most recent e2e build it is recommended to paste link to the last e2e build in apk_name field. The list of PR builds can be found in Jenkins Builds block on PR page. For easier access you can hit `Rerun tests` in GH comment and testrail_case_id/ apk_name/ pr_id will be filled automatically. For making sure that tests are being rerun on most recent e2e build it is recommended to paste link to the last e2e build in apk_name field. The list of PR builds can be found in Jenkins Builds block on PR page.
@@ -64,7 +64,7 @@ tests (otherwise some PRs could wait their turn of the scheduled Jenkins job til
## Analysing test results (and why test fails to pass) ## Analysing test results (and why test fails to pass)
After automated test run finished test results could be found in GH comment (if the test suite ran against PR) and TestRail. There are two states of the test: Passed and Failed. Test failure happens when certain condition of test step has not met or automated test can not proceed execution because it can not find the respective element on screen it expects should be there. After automated test run finished test results could be found in GH comment (if the test suite ran agaist PR) and TestRail. There are two states of the test: Passed and Failed. Test failure happens when certain condition of test step has not met or automated test can not proceed execution because it can not find the respective element on screen it expects should be there.
Several examples of when test fails to succeed: Several examples of when test fails to succeed:
+23 -48
View File
@@ -17,10 +17,9 @@ You can use any Clojure enabled editor. Here are instructions for developing the
- [REPL!](#repl) - [REPL!](#repl)
- [Connecting to REPL to IntelliJ](#connecting-to-repl-to-intellij) - [Connecting to REPL to IntelliJ](#connecting-to-repl-to-intellij)
- [Connecting REPL and IntelliJ to `status-mobile`](#connecting-repl-and-intellij-to-status-mobile) - [Connecting REPL and IntelliJ to `status-mobile`](#connecting-repl-and-intellij-to-status-mobile)
- [Visual Studio Code](#visual-studio-code) - [Using Calva](#using-calva)
- [Set up the REPL](#set-up-the-repl) - [Start and connect the REPL](#start-and-connect-the-repl)
- [Auto-format files on save](#auto-format-files-on-save) - [Use the REPL](#use-the-repl)
- [Using Emacs with CIDER](#using-emacs-with-cider)
## Using Cursive ## Using Cursive
@@ -91,7 +90,7 @@ Press the green run button
<img src="images/ide-setup/8_REPL_6.png" width=75% /> <img src="images/ide-setup/8_REPL_6.png" width=75% />
You should now see a dialog with the following message: You should now see an dialog with the following message:
```shell ```shell
Connecting to remote nREPL server... Connecting to remote nREPL server...
@@ -161,24 +160,24 @@ Following the above should give you the below result:
For additional details on issues you may face when setting up REPL with Cursive [see this document](https://notes.status.im/9Gr7kqF8SzC_SmYK0eB7uQ?view#Connecting-Cursive--IntelliJ-IDEA-to-REPL-Problems) For additional details on issues you may face when setting up REPL with Cursive [see this document](https://notes.status.im/9Gr7kqF8SzC_SmYK0eB7uQ?view#Connecting-Cursive--IntelliJ-IDEA-to-REPL-Problems)
## Visual Studio Code ## Using Calva
### Set up the REPL For VS Code users.
1. Install Calva extension. 0. Install Calva.
2. Open the `status-mobile` folder.
3. Start [Status development](starting-guide.md#development) (Starting the
`run-clojure` and `run-metro` jobs in split view in the VS Code integrated
terminal works great).
4. Run the VS Code command: **Calva: Connect to a running REPL Server in the
project**.
- Select the project type `shadow-cljs`.
- Accept the suggested connection `host:port`.
- Select to connect to the `:mobile` build.
Open any `.cljs` file in the project and evaluate forms in it. See ### Start and connect the REPL
https://calva.io/try-first/ for some starter tips and links. Confirm that your
REPL is connected to the app by evaluating: 1. Open the `status-mobile` folder.
1. Start [Status development](STARTING_GUIDE.md#development) (Starting the `run-clojure` and `run-metro` jobs in split view in the VS Code integrated terminal works great.)
1. Run the VS Code command: **Calva: Connect to a running REPL Server in the project**
1. Select the project type `shadow-cljs`
1. Accept the suggested connection `host:port`
1. Select to connect to the `:mobile` build
### Use the REPL
Open any `.cljs` file in the project and evaluate forms in it. See https://calva.io/try-first/ for some starter tips and links. Confirm that your REPL is connected to the app by evaluating:
```clojure ```clojure
(js/alert "Hello from Status App!") (js/alert "Hello from Status App!")
@@ -186,35 +185,9 @@ REPL is connected to the app by evaluating:
🎉 Tada! You are ready to use the REPL to improve Status.im! 🎉 🎉 Tada! You are ready to use the REPL to improve Status.im! 🎉
Consider bookmarking [calva.io](https://calva.io/) for quick access to the Calva Please consider bookmarking [calva.io](https://calva.io/) for quick access to the Calva documentation.
documentation.
### Auto-format files on save ## Using Emacs/Cider
We format Clojure code with [zprint](https://github.com/kkinnear/zprint). We
recommend configuring VSCode to auto-format Clojure files on every save,
otherwise you'll need to constantly rely on the much slower `make lint-fix`
command.
1. Create a file `~/.zprintrc` and enable the `:search-config?` option. This is
necessary so the VSCode extension finds the project's `.zprintrc`.
```bash
echo "{:search-config? true}" > ~/.zprintrc
```
2. Install the extension
[vscode-clj-zprint](https://github.com/rflagreca/vscode-clj-zprint). By the
way, did you know this extension is written in ClojureScript?
3. Copy the recommended settings from our repository in
[.vscode/settings.example.json](/.vscode/settings.example.json) and merge it
with your workspace settings in `.vscode/settings.json`.
The extension `vscode-clj-zprint` is only available for VSCode. If you are using
VSCodium, you'll need to build the `vsix` (extension) yourself (it's easy, only
`yarn` and `vsce` are required).
## Using Emacs with CIDER
1. Install Emacs/Cider/etc. (there is a lot of variability in how to manage things in emacs, so please google for help with this) 1. Install Emacs/Cider/etc. (there is a lot of variability in how to manage things in emacs, so please google for help with this)
2. Add a local ~/.shadow-cljs/config.edn file like below (corresponding to the version numbers of the packages you are using): 2. Add a local ~/.shadow-cljs/config.edn file like below (corresponding to the version numbers of the packages you are using):
@@ -225,3 +198,5 @@ VSCodium, you'll need to build the `vsix` (extension) yourself (it's easy, only
[cider/cider-nrepl "0.28.4"] [cider/cider-nrepl "0.28.4"]
[cider/piggieback "0.5.2"]]} [cider/piggieback "0.5.2"]]}
``` ```

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

+3 -7
View File
@@ -4,11 +4,9 @@
2) Add some reviewers to the PR and wait for feedback 2) Add some reviewers to the PR and wait for feedback
3) Address feedback 3) Address feedback
4) Make sure builds and tests are green (run `make test` locally, `make lint-fix` to fix any indentation issue and `make lint`) 4) Make sure builds and tests are green (run `make test` locally, `make lint-fix` to fix any indentation issue and `make lint`)
5) Once the PR has been reviewed by the dev team, run e2e tests on it by going to https://github.com/status-im/status-mobile/projects/7 and move the pr under the column E2E tests. This will trigger tests. **Running E2E tests is required for all PRs.** 5) Once the PR has been reviewed by the dev team you can run e2e tests on it by going to https://github.com/status-im/status-mobile/projects/7 and move the pr under the column E2E tests. This will trigger tests.
1) Read our [PR Review Policy](pr-review-policy.md) for more details on our approach to PR reviews.
2) Learn more about our [Pipeline for QA](pipeline_process.md) process
6) Once e2e tests have run, they will report the result on the PR, if it's less than 100%, ask QA to take a look to make sure everything is in order (some might fail for legitimate reasons) 6) Once e2e tests have run, they will report the result on the PR, if it's less than 100%, ask QA to take a look to make sure everything is in order (some might fail for legitimate reasons)
7) Ask QA for manual testing if the PR requires it (more details [here](pipeline_process.md)) 7) Ask QA for manual testing if the PR requires it
8) Once it has been tested successfully, squash everything into one commit. rebase and merge. The commands we use: 8) Once it has been tested successfully, squash everything into one commit. rebase and merge. The commands we use:
``` ```
git checkout develop git checkout develop
@@ -41,9 +39,7 @@ In practice, this means that sometimes they need to be merged in lockstep.
git push origin vx.y.z git push origin vx.y.z
``` ```
5) Update status-mobile with the new status-go version, using the new tag `scripts/update-status-go.sh "vx.y.z"` 5) Update status-mobile with the new status-go version, using the new tag `scripts/update-status-go.sh "vx.y.z"`
6) If you had to rebase status-go to include new changes, e2e test MUST be re-run. If there's any issue you will 6) In status-mobile, push, rebase against `develop` and merge it 🚀
have to fix in status-go with another PR and follow the same process.
7) In status-mobile, push, rebase against `develop` and merge it 🚀
important note : make sure your status-go PRs get a tested-ok by QA before merging them in. important note : make sure your status-go PRs get a tested-ok by QA before merging them in.
+63 -364
View File
@@ -1,9 +1,8 @@
# Code Style Guidelines # Code Style Guidelines
> [!IMPORTANT] >The goal of this document is to help all contributors (core and external) to
> The goal of this document is to help all contributors (core and external) to >write code in _unison_ and help establish good practices that serve the Status
> write code in _unison_ and help establish good practices that serve the Status >Mobile contributors well.
> Mobile contributors well.
We don't want to turn this document into an exhaustive list of rules to be We don't want to turn this document into an exhaustive list of rules to be
followed that nobody will read. As much as possible, we'll try to document only followed that nobody will read. As much as possible, we'll try to document only
@@ -11,130 +10,37 @@ what we consider important for Status Mobile. In other words, we don't want to
maintain a general Clojure convention/style guide, nor do we want to turn this maintain a general Clojure convention/style guide, nor do we want to turn this
document into a long tutorial. document into a long tutorial.
> [!WARNING] This is a **work in progress**, and not all conventions are properly implemented
> This is a **work in progress**, and not all conventions are properly in the codebase yet. The project structure is also going over major changes (as
> implemented in the codebase yet. The project structure is also undergoing of Nov/2022), and it'll take a considerable amount of time until we migrate the
> major changes, and it will take a considerable amount of time until we migrate existing code to the new structure.
> the existing code to the new structure.
If you find out anything is outdated or missing, please, share with us or even If you find out anything is outdated or missing, please, share with us or even
better, create a pull-request! 🤸 better, create a pull-request! 🤸
## Style guide ## Style guide
We follow the [Clojure Style We mostly follow the [Clojure Style
Guide](https://github.com/bbatsov/clojure-style-guide) and we use Guide](https://github.com/bbatsov/clojure-style-guide), so it's recommended to
[zprint](https://github.com/kkinnear/zprint) to format Clojure code. Running get familiar with it.
`make lint-fix` should fix most formatting issues, but not all of them.
As of Nov/2022, running `make lint` should fix the most basic formatting issues,
and we are in the process of integrating a tool to format non-trivial code
according to the Clojure Style Guide. This should greatly reduce noise in
pull-request reviews and it'll simplify the life of all contributors.
Pay special attention to:
- Align let bindings https://github.com/bbatsov/clojure-style-guide#bindings-alignment
- Align map keys https://github.com/bbatsov/clojure-style-guide#map-keys-alignment
## Dos and don'ts ## Dos and don'ts
### Hiccup
Never use anonymous inline function in hiccup, this will lead to
reinitialization of component on each render of parent component.
```clojure
;; bad
(defn checkbox-view
[{:keys [size]}]
[rn/view
[(fn [] [rn/view])]])
;; good
(defn comp []
[rn/view])
(defn checkbox-view
[{:keys [size]}]
[rn/view
[comp]])
```
This mistake mostly happens with functional components.
```clojure
;; bad
(fn []
(let [atom (rf/sub [:sub])]
(fn []
[:f>
(fn []
[rn/text atom]
;; good
(defn f-comp [atom]
[rn/text atom])
(fn []
(let [atom (rf/sub [:sub])]
(fn []
[:f> f-comp atom])))
```
It's important to name functional components with `f-` prefix.
### Component props and API scheme to match Figma as closely as possible
Ideally, the prop names for components (particularly in quo Design System)
should match the Figma properties as best as possible. This makes it easier for
the developer using that component to configure it correctly for the screen it
is being used on and avoids unnecessary overwrites and adjustments being made.
#### Avoid unnecessarily grouping categories to reduce the number of props
For example in Figma if there is a component and it has the following variants:
|theme: "light" blur: "False"|theme: "dark" blur: "False"|theme: "light" blur: "True"|theme: "dark" blur: "True"|
|----------------------------|---------------------------|---------------------------|---------------------------|
| type :neutral label "ABC" | type :neutral label "ABC" | | |
| type :active label "ABC" | type :active label "ABC" | | |
| type :danger label "ABC" | type :danger label "ABC" | type :danger label "ABC" | type :danger label "ABC" |
```clojure
;; bad
"theme - :light or :dark
type - can be :neutral :active :danger :danger-blur"
(defn my-component [{:keys [theme type]} label])
;; good
"theme - :light or :dark
type - can be :neutral :active :danger
blur? - boolean
"
(defn my-component [{:keys [theme blur? type]} label])
```
Please note this is only for the external API of the component and there should
be no restriction of how the component manages its internal API as that will not
affect the developer using the component with the issues described above.
In some cases this is not always possible or does not make sense. However the
thought process should be how easy will it be for another developer to use this
component with the correct configuration given the screen designs for Figma.
#### Avoid unnecessarily renaming props
In general it can be helpful to avoid renaming props from their counterpart in
Figma.
For example if Figma has sizes `:small`, `:medium` and `:large`
```clojure
;; bad
":size - :little, :default or :big"
(defn my-component [{:keys [size]}])
;; good
":size - :small, :medium or :large"
(defn my-component [{:keys [size]}])
```
### Component styles ### Component styles
Prefer to define styles in a separate file named `style.cljs`, colocated with Prefer to define styles in a separate file named `style.cljs`, colocated with
the source file. For a real example, see the source file. For a real example, see
[src/quo/components/record_audio/record_audio/style.cljs](../src/quo/components/record_audio/record_audio/style.cljs). [src/quo2/components/record_audio/record_audio/style.cljs](../src/quo2/components/record_audio/record_audio/style.cljs).
```clojure ```clojure
;; bad ;; bad
@@ -155,112 +61,6 @@ the source file. For a real example, see
[rn/view (do-something)]]) [rn/view (do-something)]])
``` ```
### Always add styles inside the `:style` key
Although when compiling ReactNative for mobile some components are able work with
their styles in the top-level of the properties map, prefer to add them inside the
`:style` key in order to separate styles from properties:
```clojure
;; bad
[rn/button {:flex 1
:padding-vertical 10
:padding-horizontal 20
:on-press #(js/alert "Hi!")
:title "Button"}]
;; good
[rn/button {:style {:flex 1
:padding-vertical 10
:padding-horizontal 20}
:on-press #(js/alert "Hi!")
:title "Button"}]
;; better
;; (define them in a style ns & place them inside `:style` key)
[rn/button {:style (style/button)
:on-press #(js/alert "Hi!")
:title "Button"}]
```
Also its fine to keep one liner styles in view
```clojure
;; ok
[rn/view {:style {:flex 1 :padding-top 5}}]
```
### Don't define properties in styles ns
Properties must be set on view level
```clojure
;; bad
{:style {:position :absolute
:left 0
:right 0
:bottom 0}
:blur-amount 30
:blur-radius 25
:blur-type :transparent
:overlay-color :transparent}
;; good
{:position :absolute
:left 0
:right 0
:bottom 0}
```
### Animated styles in the style file
```clojure
;; bad
(defn circle
[]
(let [opacity (reanimated/use-shared-value 1)]
[reanimated/view {:style [{:opacity opacity}
style/circle-container]}]))
;; good
(defn circle
[]
(let [opacity (reanimated/use-shared-value 1)]
[reanimated/view {:style (style/circle-container opacity)}]))
```
### Pass a vector of styles to Reanimated view
Prefer to pass a vector of styles to `react-native.reanimated/view` `:style`
prop instead of using `apply-animations-to-style` directly. For more details, check out Reanimated docs about [inline
styles](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/your-first-animation/#defining-a-shared-value)
and [useAnimatedStyle](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/animating-styles-and-props/#animating-styles).
```clojure
(defn f-view []
(let [scroll-x (reanimated/use-shared-value 0)
opacity (reanimated/interpolate scroll-x [0 45 50] [1 1 0])]
[reanimated/view
;; bad
{:style (reanimated/apply-animations-to-style
{:opacity opacity
:transform [{:translate-x scroll-x}]}
{:flex-direction :row})}
;; good
{:style [{:opacity opacity
:transform [{:translate-x scroll-x}]}
{:flex-direction :row}]}
;; other valid and good variants
{:style [{:opacity opacity}
{:transform [{:translate-x scroll-x}]}
{:flex-direction :row}]}
{:style {:opacity opacity
:transform [{:translate-x scroll-x}]
:flex-direction :row}}]))
```
### Don't use percents to define width/height ### Don't use percents to define width/height
In ReactNative, all layouts use the [flexbox In ReactNative, all layouts use the [flexbox
@@ -277,37 +77,24 @@ Joshua Comeau.
[rn/view {:style {:padding-horizontal 20}}] [rn/view {:style {:padding-horizontal 20}}]
``` ```
### Use a question mark to convey the value is a boolean ### Don't prepend booleans with is-
The Clojure Style Guide suggests using a question mark only in [predicate It is a common practice in JavaScript and other languages to prepend boolean variable names with `is-*`.
functions](https://guide.clojure.style/#naming-predicates), but nothing is In ClojureScript it is common practice to suffix boolean variable names with a `?`.
mentioned about other symbols and keywords. We prefer to extend the convention There is no need for both of these and so it is preferable to stick with the latter.
to all boolean references.
```clojure ```clojure
;; bad ;; bad
(let [is-open? true] ...) (let [is-open? true] ...)
(def flag-is-enabled false)
;; good ;; good
(let [open? true] ...) (let [open? true] ...)
(def flag-enabled? false)
```
And for keywords too:
```clojure
;; bad
[some-component {:logged-in true}]
;; good
[some-component {:logged-in? true}]
``` ```
### Styles def vs defn ### Styles def vs defn
Always use `def` over `defn` if there are no dynamic values. This helps cut the Always use `def` over `defn`, unless the style relies on dynamic values, such as
cost of function calls. deref'ed atoms.
```clojure ```clojure
;; bad ;; bad
@@ -331,18 +118,15 @@ cost of function calls.
### Custom Colors ### Custom Colors
The Status designs have a lot of customization of user and group colors. For The Status designs have a lot of customization of user and group colors with components and pages. For consistency it is best to use `customization-color` as the prop key on pages and components. This will help easily identify what pages and components in the application are using customized colors.
consistency it is best to use `customization-color` as the prop key on pages and
components. This will help easily identify what pages and components in the
application are using customized colors.
```clojure ```clojure
;; bad ;; bad
(defn community-card [{keys [custom-color]}] (defn community-card [{keys [custom-color]}]
...) ...)
;; good ;; good
(defn community-card [{keys [customization-color]}] (defn community-card [{keys [customization-color]}]
...) ...)
``` ```
@@ -459,67 +243,23 @@ dispatch.
(str "Hello " username)]]) (str "Hello " username)]])
``` ```
### Registering effects
When registering re-frame effects (`reg-fx`), prefer to expose a data-only
interface because that will allow event handlers to stay pure.
For instance, if an effect needs a `on-success` callback, allow it to receive a
*re-frame event vector*. This approach is used by us in the [json-rpc/call
effect](src/status_im2/common/json_rpc/events.cljs), but also by third-party
effects, such as https://github.com/Day8/re-frame-http-fx. For the complete
rationale, see [PR #15936](https://github.com/status-im/status-mobile/pull/15936).
### Using the effect `:json-rpc/call`
Prefer the pure version of `:json-rpc/call` (no callbacks).
```clojure
;; not as good
(rf/defn accept-contact-request
{:events [:activity-center.contact-requests/accept]}
[_ contact-id]
{:json-rpc/call
[{:method "wakuext_acceptContactRequest"
:params [{:id contact-id}]
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(rf/dispatch [:activity-center.contact-requests/accept-error contact-id %])}]})
;; better
(rf/defn accept-contact-request
{:events [:activity-center.contact-requests/accept]}
[_ contact-id]
{:json-rpc/call
[{:method "wakuext_acceptContactRequest"
:params [{:id contact-id}]
:on-success [:sanitize-messages-and-process-response]
:on-error [:activity-center.contact-requests/accept-error contact-id]}]})
```
### Registering event handlers ### Registering event handlers
Register events with `utils.re-frame/reg-event-fx` and follow [re-frame's best Events must always be declared with the `utils.fx/defn` macro. Also, don't use
practice](https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/FAQs/BestPractice.md#use-the-fx-effect)
so use only `:db` and `:fx` effects. `utils.re-frame/merge` and `utils.re-frame/defn` are deprecated and should not be
used in the new code in `src/status_im2/`. Don't use
`re-frame.core/reg-event-db`. `re-frame.core/reg-event-db`.
```clojure ```clojure
;; bad ;; bad
(rf/defn invite-people-pressed (re-frame/reg-event-fx
{:events [:communities/invite-people-pressed]} :wakuv2.ui/save-all-confirmed
[cofx id] (fn [{:keys [db] :as cofx}]
(rf/merge cofx ...))
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {:invite? true})))
;; good ;; good
(re-frame/reg-event-fx :communities/invite-people-pressed (fx/defn save-all
(fn [{:keys [db]} [id]] {:events [:wakuv2.ui/save-all-confirmed]}
{:db (assoc db :communities/community-id-input id) [{:keys [db] :as cofx}]
:fx [[:dispatch [:hide-bottom-sheet]] ...)
[:dispatch [:open-modal :invite-people-community {:invite? true}]]]}))
``` ```
### Registering top-level re-frame subscriptions ### Registering top-level re-frame subscriptions
@@ -559,27 +299,27 @@ due to performance constraints.
(:preferred-name multiaccount))) (:preferred-name multiaccount)))
``` ```
### Requiring quo components ### Requiring quo2 components
Consume `quo` components from `quo.core`, unless the namespace is also inside Consume `quo2` components from `quo2.core`, unless the namespace is also inside
the `quo/` directory. the `quo2/` directory.
```clojure ```clojure
;; bad ;; bad
(ns my-namespace (ns my-namespace
(:require [quo.components.icon :as icon])) (:require [quo2.components.icon :as icon]))
(icon/icon :i/verified) (icon/icon :i/verified)
;; good ;; good
(ns my-namespace (ns my-namespace
(:require [quo.core :as quo])) (:require [quo2.core :as quo2]))
(quo/icon :i/verified) (quo2/icon :i/verified)
;; also good because both namespaces are inside quo/ ;; also good because both namespaces are inside quo2/
(ns quo.components.tabs.account-selector (ns quo2.components.tabs.account-selector
(:require [quo.components.markdown.text :as text])) (:require [quo2.components.markdown.text :as text]))
``` ```
### Require/import ### Require/import
@@ -642,16 +382,17 @@ indices.
### Icons ### Icons
Use the appropriate keyword qualification/namespace. Use the appropriate keyword qualification/namespace and don't directly require
namespaces inside `quo2/components/`.
```clojure ```clojure
;; bad ;; bad
(require '[quo.components.icon :as icons]) (require '[quo2.components.icon :as icons])
(icons/icon :main-icons2/verified) (icons/icon :main-icons2/verified)
;; good ;; good
(require '[quo.core :as quo]) (require '[quo2.core :as quo2])
(quo/icon :i/verified) (quo2/icon :i/verified)
``` ```
### Translations ### Translations
@@ -672,26 +413,6 @@ keywords and concatenating them into a single string.
``` ```
### Tests ### Tests
#### Prefer `match?` over `=` when comparing data structures
Prefer the `match?` directive over `=` when comparing data structures, otherwise
when the check fails the output can be too difficult to read. `match?` is
defined by library https://github.com/nubank/matcher-combinators.
```clojure
;; bad
(deftest some-test
(let [expected {...}
actual {...}]
(is (= expected actual))))
;; good
(deftest some-test
(let [expected {...}
actual {...}]
(is (match? expected actual))))
```
#### Subscription tests #### Subscription tests
Test [layer-3 subscriptions](https://day8.github.io/re-frame/subscriptions/) by Test [layer-3 subscriptions](https://day8.github.io/re-frame/subscriptions/) by
@@ -746,7 +467,7 @@ First, the bird's-eye view with some example ClojureScript files:
src src
├── js/ ├── js/
├── mocks/ ├── mocks/
├── quo ├── quo2
│ ├── components/ │ ├── components/
│ ├── foundations/ │ ├── foundations/
│ └── theme.cljs │ └── theme.cljs
@@ -767,7 +488,7 @@ src
- `src/js`: Raw Javascript files, e.g. React Native Reanimated worklets. - `src/js`: Raw Javascript files, e.g. React Native Reanimated worklets.
- `src/mocks`: Plumbing configuration to be able to run tests. - `src/mocks`: Plumbing configuration to be able to run tests.
- `src/quo/`: The component library for Status Mobile. [Read more...](../src/quo/README.md) - `src/quo2/`: The component library for Status Mobile.
- `src/react_native/`: Contains only low-level constructs to help React Native - `src/react_native/`: Contains only low-level constructs to help React Native
work in tandem with Clojure(Script). work in tandem with Clojure(Script).
- `src/status_im2/`: Directory where we try to be as strict as possible about - `src/status_im2/`: Directory where we try to be as strict as possible about
@@ -779,7 +500,7 @@ src
of the directory tree. Just like directories named `utils`, their directory of the directory tree. Just like directories named `utils`, their directory
nesting level communicates their applicable limits. nesting level communicates their applicable limits.
- `src/status_im2/common/components/`: Contains reusable components that are not - `src/status_im2/common/components/`: Contains reusable components that are not
part of the design system (quo). part of the design system (quo2).
- `src/status_im2/contexts/`: Contains [bounded contexts](#glossary), like - `src/status_im2/contexts/`: Contains [bounded contexts](#glossary), like
`browser/`, `messaging/`, etc. As much as possible, _bounded contexts_ should `browser/`, `messaging/`, etc. As much as possible, _bounded contexts_ should
not directly require each other's namespaces. not directly require each other's namespaces.
@@ -794,9 +515,9 @@ directory nesting level precisely indicates its boundaries. For example, a
`contexts/user_settings/utils/datetime.cljs` file communicates that it should `contexts/user_settings/utils/datetime.cljs` file communicates that it should
only be used in the `user_settings` context. only be used in the `user_settings` context.
### src/quo ### src/quo2
The `src/quo/` directory holds all components for the new design system. As The `src/quo2/` directory holds all components for the new design system. As
much as possible, its sub-directories and component names should reflect the much as possible, its sub-directories and component names should reflect the
same language used by designers. same language used by designers.
@@ -804,14 +525,14 @@ Even though the directory lives alongside the rest of the codebase, we should
think of it as an external entity that abstracts away particular Status domain think of it as an external entity that abstracts away particular Status domain
knowledge. knowledge.
Components inside `src/quo/` should not rely on re-frame, i.e. they should not Components inside `src/quo2/` should not rely on re-frame, i.e. they should not
dispatch events or use subscriptions. dispatch events or use subscriptions.
Example structure: Example structure:
``` ```
src src
└── quo └── quo2
├── components ├── components
│ └── dropdown │ └── dropdown
│ ├── style.cljs │ ├── style.cljs
@@ -842,28 +563,6 @@ src
└── view.cljs └── view.cljs
``` ```
## Deprecation process
To deprecate a var, add the `:deprecated` metadata and, if necessary, suggest an
alternative.
```clojure
;; Good if there's no better alternative yet, but we want to deprecate it anyway.
(defn ^:deprecated foo
[]
(bar))
;; Good
(defn foo
{:deprecated "Use some.namespace/var-name instead."}
[]
(bar))
```
Please check the [Clojure Style](https://guide.clojure.style/#deprecated) documentation
To reduce visual clutter from deprecated methods in your text editor, consult this [example](https://rider-support.jetbrains.com/hc/en-us/community/posts/4419728641810-How-to-disable-the-the-strike-thru-for-deprecated-methods-in-Javascript-#:~:text=Try%20disabling%20%22Preferences%20%7C%20Editor%20%7C,It%20works%20for%20me). The approach can be adapted for settings in VSCode, Emacs, VIM, and others.
### Test structure ### Test structure
[Unit tests](#glossary) should be created alongside their respective source [Unit tests](#glossary) should be created alongside their respective source
@@ -878,9 +577,9 @@ Clojure (JVM) codebases which mirror the sources in a top-level test directory.
└── models_test.cljs └── models_test.cljs
``` ```
Component tests should be created in the same directory as the source component, Component tests should be created in a separate directory `__tests__`, colocated
and named as `component_spec.cljs`. with the source. When the entire component implementation is isolated under a
single directory, create a test file named `component_spec.cljs` instead.
``` ```
└── filter └── filter
-13
View File
@@ -1,13 +0,0 @@
# Patching
## Libraries
If 3rd party library has an issue and fix is not yet released (or we can't switch to a new release), we use forks. Fix should be committed to the fork, tagged and referenced from package.json.
Example: [`react-native-hole-view`](https://github.com/status-im/react-native-hole-view#refs/tags/v2.1.1-status)
## React Native
When patch need to be applied to React Native itself Status does patching with Nix instead of doing it nodejs-way.
Patches should be added to [this file](https://github.com/status-im/status-mobile/blob/develop/nix/deps/nodejs-patched/default.nix).
Example: [patching `react-native/Yoga` to build app with XCode 14.3](https://github.com/status-im/status-mobile/pull/15589)
+21 -33
View File
@@ -1,6 +1,6 @@
# Pipeline process # Pipeline process
[Pipeline for QA](https://github.com/status-im/status-mobile/projects/7) is a project board for developers and testers used to track the status of a pull request, get reviews and manual testing, and run autotests. [Pipeline for QA](https://github.com/status-im/status-mobile/projects/7) is a project board for developers and testers used to track the status of a pull request, get reviews and manual testing, _and run autotests_ (_temporary disabled_).
The generally accepted recommendations for its use are described below: The generally accepted recommendations for its use are described below:
@@ -30,7 +30,7 @@ Ready for testing PR should meet the following criteria:
3. Wait for the review. 3. Wait for the review.
4. Make sure that after review and before requesting manual QA your PR is rebased to current develop. 4. Make sure that after review and before requesting manual QA your PR is rebased to current develop.
5. Once the PR has been approved by reviewer(s) - label `request-manual-qa` should be applied to the PR 5. Once the PR has been approved by reviewer(s) - label `request-manual-qa` should be applied to the PR
6. Move PR to the E2E column when it is ready for testing (**mandatory for all PRs**). That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test. 6. Move PR to the E2E column when it is ready for testing. That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test.
After that - PR will be taken into manual testing by the QA team. After that - PR will be taken into manual testing by the QA team.
@@ -41,50 +41,38 @@ After that - PR will be taken into manual testing by the QA team.
- QA engineer picks up one of PRs with the ```request-manual-qa``` label, drags the item to the ```IN TESTING``` column and assigns it to themselves. - QA engineer picks up one of PRs with the ```request-manual-qa``` label, drags the item to the ```IN TESTING``` column and assigns it to themselves.
- During testing, QA will add comments describing the issues found, and also review automation tests results. - During testing, QA will add comments describing the issues found, and also review automation tests results.
Usually found issues are numbered as "Issue 1, Issue 2", etc. Usually found issues are numbered as "Issue 1, Issue 2", etc.
When the first round of testing is completed and all issues for this stage are found, the QA can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional. When the first round of testing is completed and all issues for this stage are found, tester can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional.
- When manual testing of the PR is fully completed and all the issues are fixed, the QA adds the ```Tested - OK``` label and drags the card to the ```Design review``` column (the cases when design review is necessary are described below). - When manual testing of PR is fully completed and all issues are fixed, QA adds the ```Tested - OK``` label and drags the card to the ```MERGE``` column, after which developer merges PR into develop.
- If design review is not required, the QA drags the PR to the ```MERGE``` column. After that the developer merges PR into develop. If manual testing was not carried out, developer drags PR to the ```MERGE``` column themselves.
- If design review has been done, the designer (```@Francesca-G```) drags the PR to the ```MERGE``` column.
After that the developer merges PR into develop.
- If manual testing/design review was not carried out, the developer drags PR to the ```MERGE``` column themselves.
### Design review
_**How do I know if a design review is needed?**_
There are three cases here depending on the changes in the PR:
1. **Functional PRs with UI changes:** after the ```Tested - OK``` label is added, the QA moves the PR to the ```Design review``` column + mentions ```@Francesca-G``` in comments.
2. **Component PRs:** once the PR has received a review from developers and e2e tests results, it can be moved directly to the ```Design review``` column by the developer (manual testing step can be skipped) + the developer mentions ```@Francesca-G``` in comments.
3. **Functional PRs changes in which are not related to UI (e.g. a crash fix):** skip the ```Design review``` step (the PR should only be manually tested by QA).
There are three possible scenarios when the design review is completed:
1. **Approved by design** > the PR is moved to the ```MERGE``` column by Francesca
2. **Some changes requested, but can be fixed as followups** > need to be negotiated with Francesca > Francesca moves PR to the ```MERGE``` column and adds the ```Follow-up required``` label > follow-ups should be added **by the dev** right after the PR is merged
3. **Some changes requested, they should be fixed before merge** > Francesca reviews the PR again after the fixes and moves PR to the ```MERGE``` column from ```Design review```
---
**Notes:**
- If your PR has a long story and started from `develop` branch several days ago, please rebase it to current develop before adding label
- if PR can be tested by developer (in case of small changes) and/or developer is sure that the changes made cannot introduce a regression, then PR can be merged without manual testing. Also, currently, PRs are not manually tested if the changes relate only the design (creation of components, etc.) and do not affect the functionality.
---
**Notes:**
- If your PR has a long story and started from `develop` branch several days ago, please rebase it to current develop before adding label
- if PR can be tested by developer (in case of small changes) and/or developer is sure that the changes made cannot introduce a regression, then PR can be merged without manual testing. Also, currently, PRs are not manually tested if the changes relate only the design (creation of components, etc.) and do not affect the functionality.
#### Why my PR is in `Contributor` column? #### Why my PR is in `Contributor` column?
PR can be moved to this column by the ```status-github-bot``` or by QA engineer with label `Tested-issues`. PR can be moved to this column by the ```status-github-bot``` or by QA engineer with label `Tested-issues`.
In the first case most often this happens due to conflicting files in PR. In the first case most often this happens due to conflicting files in PR.
In the second case - after fixing of all found issues, the developer should ping the QA in the PR comments for retesting. In the second case - after fixing of all found issues, the developer should ping the QA in the PR comments for retesting.
#### Why is my PR in `To Rebase` column?
PR is moved to the "To Rebase" column in two cases:
- automatically by github bot if PR branch has conflicts that should be resolved
- manually (by QAs) if PR branch is out-of-date with the base branch and requires rebasing to the latest develop
If PR appeared in the "To Rebase" column dev who is working on the PR should resolve conflicts/rebase branch to the latest develop. After resolving conflicts/rebasing PR should be moved by developer to the right column depending on PR work progress.
## Merging a PR ## Merging a PR
**Merge conditions:** **Merge conditions:**
1. Required number of reviews received 1. Required number of reviews received
2. E2E results are received and reviewed 2. All commits are squashed into one.
3. All commits are squashed into one. 3. No conflicting files in PR
4. No conflicting files in PR 4. No issues from lint
5. No issues from lint 5. Pay attention to automation checks (some of them are not blockers, best to check before merge anyway)
6. Pay attention to automation checks (some of them are not blockers, best to check before merge anyway)
![](images/pipeline-process/automation-checks.png) ![](images/pipeline-process/automation-checks.png)
6. In case of manual testing - the label ```Tested - OK``` from QA 6. In case of manual testing - the label ```Tested - OK``` from QA
7. In case of design review - the approval from the designer
You can merge your PR into develop - some useful clues you can find [here](https://notes.status.im/setup-e2e#3-Merging-PR) You can merge your PR into develop - some useful clues you can find [here](https://notes.status.im/setup-e2e#3-Merging-PR)
-23
View File
@@ -1,23 +0,0 @@
# Pixel Perfection
The Status Mobile team aims to align the design implementation perfectly according to a given screens respective Figma design spec.
Currently, the Figma Designs match up with both "iPhone 11 Pro" and "iPhone 13" screen size.
The Dimension reference for iPhone 11 Pro is 375 x 812 (width x height) and for iPhone 13 is 390 x 844.
To test your implementation is correct you can take the following steps
- Open a new Figma file so that you have write privileges.
- Copy in the component or screen from the Figma file.
- Using the "iPhone 11 Pro" or the "iPhone 13" simulator (it is the default now) you take a screenshot of your component and paste it into your Figma file.
- To get the right size, set your screenshot to width according to your simulator:
- for iPhone 13 set 390
- for iPhone 11 Pro set 375
![](images/pixel-perfection/layer-width.png)
- Lower the opacity so you can see it sitting on top of the Figma mock ups.

Some files were not shown because too many files have changed in this diff Show More