Compare commits

..
10 Commits
Author SHA1 Message Date
Omar Basem 2303b59bae updates 2023-01-19 10:39:07 +04:00
Omar Basem 703f4c3a42 updates 2023-01-19 10:14:47 +04:00
Omar Basem c787ea4173 updates 2023-01-19 10:13:28 +04:00
Omar Basem 92a464ca72 updates 2023-01-19 09:52:46 +04:00
Omar Basem eacab643b6 updates 2023-01-19 09:52:02 +04:00
Omar Basem 66ed4b36d7 updates 2023-01-19 09:34:09 +04:00
Omar Basem 1fbb1f1f64 updats 2023-01-19 09:31:57 +04:00
Omar Basem fb2dd25866 updates 2023-01-19 09:25:48 +04:00
Omar Basem 49c1ad3891 updates 2023-01-19 09:24:56 +04:00
Omar Basem 0966cb7829 updates
https://github.com/status-im/status-go/compare/d40290a6...d60c1d00
2023-01-19 09:20:38 +04:00
2978 changed files with 53196 additions and 91410 deletions
+14 -10
View File
@@ -12,11 +12,11 @@ status-im.chat.models.message-content/actions
status-im.chat.models.message-content/blank-string
status-im.chat.models.message-content/sorted-ranges
status-im.ethereum.mnemonic/words->passphrase
native-module.core/listener
native-module.core/multiaccount-reset
native-module.core/extract-group-membership-signatures
native-module.core/sign-group-membership
native-module.core/update-mailservers
status-im.native-module.core/listener
status-im.native-module.core/multiaccount-reset
status-im.native-module.core/extract-group-membership-signatures
status-im.native-module.core/sign-group-membership
status-im.native-module.core/update-mailservers
status-im.ethereum.abi-spec/bytes-to-hex
status-im.android.core/init
status-im.chat.models.message/transport-keys
@@ -95,11 +95,11 @@ status-im.hardwallet.simulated-keycard/sign-typed-data
status-im.utils.core/safe-read-message-content
status-im.ui.components.react/native-modules
status-im.ui.components.react/progress-bar
react-native.fs/move-file
react-native.fs/read-dir
react-native.fs/mkdir
react-native.fs/unlink
react-native.fs/file-exists?
status-im.utils.fs/move-file
status-im.utils.fs/read-dir
status-im.utils.fs/mkdir
status-im.utils.fs/unlink
status-im.utils.fs/file-exists?
quo.animated/code
quo.animated/eq
quo.animated/neq
@@ -196,6 +196,10 @@ 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
status-im.transport.db/create-chat
status-im.utils.priority-map/priority-map
+9 -43
View File
@@ -9,49 +9,15 @@
status-im.utils.styles/defn clojure.core/defn
test-helpers.unit/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:clj-kondo-config {:level :error}
:cond-else {:level :error}
:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:docstring-blank {:level :error}
:inline-def {:level :error}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
:loop-without-recur {: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}
:quoted-case-test-constant {: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}
:unknown-require-option {:level :error}
:unreachable-code {:level :error}
:unresolved-namespace {:level :error}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}
:unresolved-var {:level :error}
: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}}
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}}
:config-in-ns {mocks.js-dependencies {:linters {:clojure-lsp/unused-public-var {:level :off}}}}}
+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 -1
View File
@@ -22,6 +22,7 @@ KEYCARD_TEST_MENU=0
QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=1
ENABLE_QUO_PREVIEW=1
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr
COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1
@@ -32,4 +33,3 @@ TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
TEST_STATEOFUS=1
+1 -1
View File
@@ -21,6 +21,7 @@ COMMANDS_ENABLED=1
KEYCARD_TEST_MENU=1
QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=0
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr
VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5
@@ -32,4 +33,3 @@ DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
STICKERS_TEST_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
TEST_STATEOFUS=1
BLANK_PREVIEW=0
MAX_IMAGES_BATCH=5
DATABASE_MANAGEMENT_ENABLED=1
COMMUNITIES_ENABLED=1
COMMUNITIES_MANAGEMENT_ENABLED=1
@@ -34,4 +35,3 @@ TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_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
CONTRACT_NODES=1
ENABLE_ROOT_ALERT=1
MAX_IMAGES_BATCH=5
BLANK_PREVIEW=0
COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1
DELETE_MESSAGE_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=0
+3 -3
View File
@@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: '@react-native-community',
};
root: true,
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)
```
...
```
```
+5 -4
View File
@@ -1,8 +1,8 @@
---
name: MVPBug Report
about: MVPBug Report
name: Bug Report
about: Bug Report
title: ''
labels: 'E:Mobile Bug MVP'
labels: bug
assignees: ''
---
@@ -22,7 +22,8 @@ assignees: ''
### Reproduction
1.
1) Open Status
2)
### 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)
+1
View File
@@ -5,6 +5,7 @@ project-board:
min-reviewers: 1
tested-pr-label-name: 'Tested - Issues'
contributor-column-name: 'CONTRIBUTOR'
to-rebase-column-name: 'TO REBASE'
review-column-name: 'REVIEW'
test-column-name: 'E2E Tests'
+3 -9
View File
@@ -7,7 +7,6 @@
# Xcode
#
/ios/.xcode.env.local
/component-spec
result/
build/
@@ -101,8 +100,9 @@ Statusgo.xcframework
#ios
/ios/Pods/
/ios/logs/
/ios/StatusIm.xcworkspace
/ios/react-native-xcode.log
/ios/set_xcode_version.log
.ruby-version
status-e2e/
@@ -183,10 +183,4 @@ test/appium/tests/users.py
/.calva/
## component-tests
*.log
## local xcode env introduced in react-native 0.69
.xcode.env.local
## git hooks
lefthook.yml
*.log
-9
View File
@@ -1,9 +0,0 @@
*
!*.js
!*/
*.clj-kondo
*.shadow-cljs
modules
result
target
component-spec
+5 -7
View File
@@ -1,9 +1,7 @@
module.exports = {
arrowParens: 'always',
printWidth: 120,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
useTabs: false,
bracketSpacing: false,
bracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
tabWidth: 4,
};
+1 -6
View File
@@ -16,11 +16,7 @@
;; 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]
:respect-bl]
:fn-map
{"reg-sub" :arg1-pair
"h/describe" :arg1-body
@@ -30,7 +26,6 @@
"list-comp" :binding
"defview" :arg1-body
"letsubs" :binding
"with-let" "let"
"testing" :arg1-body
"deftest-sub" :arg1-body
"wait-for" :arg1-body
+45 -66
View File
@@ -14,17 +14,17 @@ WHITE := $(shell tput -Txterm setaf 7)
YELLOW := $(shell tput -Txterm setaf 3)
RESET := $(shell tput -Txterm sgr0)
HELP_FUN = \
%help; \
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"; \
for (sort keys %help) { \
print "${WHITE}$$_:${RESET}\n"; \
for (@{$$help{$$_}}) { \
$$sep = " " x (32 - length $$_->[0]); \
print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
}; \
print "\n"; \
}
%help; \
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"; \
for (sort keys %help) { \
print "${WHITE}$$_:${RESET}\n"; \
for (@{$$help{$$_}}) { \
$$sep = " " x (32 - length $$_->[0]); \
print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
}; \
print "\n"; \
}
HOST_OS := $(shell uname | tr '[:upper:]' '[:lower:]')
# This can come from Jenkins
@@ -42,14 +42,11 @@ export NODE_OPTIONS += --openssl-legacy-provider
export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
# 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
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
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,\
BUILD_TYPE,BUILD_NUMBER,COMMIT_HASH,\
ANDROID_GRADLE_OPTS,ANDROID_ABI_SPLIT,ANDROID_ABI_INCLUDE,\
STATUS_GO_SRC_OVERRIDE,STATUS_GO_IPFS_GATEWAY_URL
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,STATUS_GO_SRC_OVERRIDE
# Useful for Android release builds
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)" && \
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: nix-gc-protected ##@nix Garbage collect all packages older than 20 days from /nix/store
nix-store --gc
@@ -224,20 +217,24 @@ release-android: keystore build-android ##@build Build signed Android APK
@scripts/sign-android.sh result/app-release-unsigned.apk
release-ios: export TARGET := ios
release-ios: export IOS_STATUS_GO_TARGETS := ios/arm64
release-ios: export BUILD_ENV ?= prod
release-ios: watchman-clean ios-clean jsbundle ##@build Build release for iOS release
xcodebuild \
-scheme StatusIm \
-configuration Release \
-workspace ios/StatusIm.xcworkspace \
-destination 'generic/platform=iOS' \
-UseModernBuildSystem=N clean archive
release-ios: watchman-clean ##@build Build release for iOS release
@git clean -dxf -f target/ios && \
$(MAKE) jsbundle-ios && \
xcodebuild -workspace ios/StatusIm.xcworkspace -scheme StatusIm -configuration Release -destination 'generic/platform=iOS' -UseModernBuildSystem=N clean archive
jsbundle: SHELL := /bin/sh
jsbundle: export BUILD_ENV ?= prod
jsbundle: ##@build Build JavaScript and Clojurescript bundle for iOS and Android
nix/scripts/build.sh targets.mobile.jsbundle
jsbundle-android: SHELL := /bin/sh
jsbundle-android: export TARGET := android
jsbundle-android: export BUILD_ENV ?= prod
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
@@ -273,14 +270,11 @@ 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
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_arch.sh)
run-android: ##@run Build Android APK and start it on the device
npx react-native run-android --appIdSuffix debug
SIMULATOR=iPhone 11 Pro
SIMULATOR=iPhone 13
run-ios: export TARGET := ios
run-ios: export IOS_STATUS_GO_TARGETS := iossimulator/amd64
run-ios: ##@run Build iOS app and start it in a simulator/device
ifneq ("$(SIMULATOR)", "")
npx react-native run-ios --simulator="$(SIMULATOR)"
@@ -288,17 +282,6 @@ else
npx react-native run-ios
endif
show-ios-devices: ##@other shows connected ios device and its name
xcrun xctrace list devices
run-ios-device: export TARGET := ios
run-ios-device: export IOS_STATUS_GO_TARGETS := ios/arm64
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
#--------------
@@ -308,23 +291,20 @@ define find_all_clojure_files
$$(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
lint: export TARGET := clojure
lint: export TARGET := default
lint: ##@test Run code style checks
@sh scripts/lint-re-frame-in-quo-components.sh && \
clj-kondo --config .clj-kondo/config.edn --cache false --fail-level error --lint src && \
clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
sh scripts/lint-trailing-newline.sh && \
yarn prettier
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES
# 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
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
sh scripts/lint-trailing-newline.sh --fix && \
yarn prettier
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES
shadow-server: export TARGET := clojure
shadow-server:##@ Start shadow-cljs in server mode for watching
@@ -351,10 +331,13 @@ test: ##@test Run tests once in NodeJS
yarn shadow-cljs compile test && \
node --require ./test-resources/override.js target/test/test.js
android-test: jsbundle
android-test: export TARGET := android
android-test:
cd android && ./gradlew test
run-visual-test-ios: export TARGET := clojure
run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/DerivedData
run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
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 COMPONENT_TEST := true
@@ -366,12 +349,12 @@ component-test-watch: ##@ Watch tests and re-run no changes to cljs files
component-test: export TARGET := clojure
component-test: export COMPONENT_TEST := true
component-test: export BABEL_ENV := test
component-test: ##@test Run component tests once in NodeJS
component-test: ##@test Run tests once in NodeJS
# Here we create the gyp bindings for nodejs
yarn install
yarn shadow-cljs compile component-test && \
jest --config=test/jest/jest.config.js
#--------------
# Other
#--------------
@@ -380,10 +363,6 @@ geth-connect: ##@other Connect to Geth on the device
adb forward tcp:8545 tcp: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: ##@prepare Clean Gradle state
git clean -dxf -f ./android/app/build; \
+17 -12
View File
@@ -2,9 +2,7 @@
[<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/)
[Get it on Github](https://github.com/status-im/status-mobile/releases)
[<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.
@@ -22,31 +20,31 @@ If this interests you, **help us make Status a reality** - anyone can 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
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
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
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
Status](doc/starting-guide.md), which talks about managing
project dependencies, coding guidelines and testing procedures.
Check out our [coding guidelines](doc/new-guidelines.md).
Status](https://status.im/technical/build_status/), which talks about managing
project dependencies, coding guidelines and testing procedures. The [doc/](doc/)
directory also has valuable information for contributors.
- **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 long lasting relationships you form will be well worth it, check out our [Mission and Community Principles](https://status.im/about)
- **Specification / Documentation**
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, 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**
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**
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 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!
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**
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!
@@ -54,6 +52,9 @@ Status is a visual interface to make permanent changes on the Blockchain, it han
- **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)
## 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!
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/).
@@ -67,8 +68,12 @@ Without the dedication of these outstanding individuals, Status would not exist.
## 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
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
@@ -1 +1 @@
1.25.0
1.20.2
+23 -114
View File
@@ -94,10 +94,6 @@ project.ext.react = [
enableHermes: !disableHermes,
/* Disable 'The first definition was here.' warnings */
hermesFlagsRelease: ["-w"],
/* FIXME: Workaround for crash caused by missing libhermes-executor-release.so.
* https://github.com/facebook/react-native/issues/32928 */
deleteDebugFilesForVariant: { false },
enableVmCleanup: false,
bundleInPr: true,
inputExcludes: ["android/**", "ios/**", "react-native/**", "src/**", "test/**"]
]
@@ -136,14 +132,6 @@ def jscFlavor = 'org.webkit:android-jsc:+'
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
def getCommitHash = { ->
if (project.hasProperty("commitHash")) {
return project.commitHash
@@ -168,13 +156,12 @@ def getVersionCode = { ->
def getVersionName = { ->
new ByteArrayOutputStream().withStream { stdOut ->
// TODO: probably not used, cleanup
if (project.hasProperty("releaseVersion")) {
return project.releaseVersion
}
/* Necessary because Android Studio uses wrong PWD.
* Is actually absolute directory path of this file. */
def configDir = project.projectDir.toString()
def configDir = project.projectDir.toString()
version = new File(configDir + '/../../VERSION').text
return version.replaceAll("\\s","")
}
@@ -196,13 +183,11 @@ def getEnvOrConfig = { varName ->
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
@@ -220,70 +205,7 @@ android {
abiFilters getEnvOrConfig('ANDROID_ABI_INCLUDE').split(";")
}
}
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=$buildDir/generated/source",
"PROJECT_BUILD_DIR=$buildDir",
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
"NODE_MODULES_DIR=$rootDir/../node_modules"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
// Make sure this target name is the same you specify inside the
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
targets "rndiffapp_appmodules"
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
path "$projectDir/src/main/jni/Android.mk"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureNdkBuild* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureNdkBuildRelease.dependsOn(preReleaseBuild)
configureNdkBuildDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
/**
* Arbitrary project metadata
* https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
@@ -386,15 +308,26 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+" // From node_modules
implementation 'com.facebook.soloader:soloader:0.10.3+'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "androidx.core:core-splashscreen:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
// 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) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group: 'com.facebook.fbjni' }
} else {
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
}
@@ -412,25 +345,9 @@ dependencies {
implementation project(':react-native-status')
implementation project(':react-native-status-keycard')
implementation 'com.github.status-im:function:0.0.1'
implementation 'com.facebook.fresco:fresco:2.5.0'
implementation 'com.facebook.fresco:animated-gif:2.5.0'
implementation "com.squareup.okhttp3:okhttp-tls:4.9.2"
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
implementation 'com.facebook.fresco:fresco:2.2.0'
implementation 'com.facebook.fresco:animated-gif:2.2.0'
implementation "com.squareup.okhttp3:okhttp-tls:3.12.12"
}
def getLocalNDKDir = { ->
@@ -466,11 +383,3 @@ task hemroidBuild(type: Exec) {
preBuild.dependsOn hemroidBuild
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
+1 -3
View File
@@ -7,7 +7,5 @@
<!-- 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"/>
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" >
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
</application>
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" />
</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());
}
}
}
}
+3 -7
View File
@@ -29,22 +29,18 @@
android:allowBackup="false"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/Theme.AppSplash"
android:theme="@style/AppTheme"
android:name=".MainApplication"
android:largeHeap="true"
android:usesCleartextTraffic="true"
android:extractNativeLibs="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`-->
android:usesCleartextTraffic="true">
<meta-data android:name="commitHash" android:value="${commitHash}"/>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AppSplash"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:resizeableActivity="false"
android:launchMode="singleTask">
<intent-filter>
@@ -21,7 +21,6 @@ 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;
@@ -29,10 +28,10 @@ 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 com.facebook.react.ReactActivity;
import org.devio.rn.splashscreen.SplashScreen;
import java.util.Properties;
import im.status.ethereum.module.StatusThreadPoolExecutor;
@@ -43,8 +42,6 @@ public class MainActivity extends NavigationActivity
@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();
@@ -124,8 +121,21 @@ public class MainActivity extends NavigationActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
setTheme(R.style.DarkTheme);
switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
case Configuration.UI_MODE_NIGHT_YES:
setTheme(R.style.DarkTheme);
SplashScreen.show(this, R.style.DarkTheme, R.id.lottie);
break;
case Configuration.UI_MODE_NIGHT_NO:
setTheme(R.style.LightTheme);
SplashScreen.show(this, R.style.LightTheme, R.id.lottie);
break;
default:
setTheme(R.style.LightTheme);
SplashScreen.show(this, R.style.LightTheme, R.id.lottie);
}
SplashScreen.setAnimationFinished(true);
// Make sure we get an Alert for every uncaught exceptions
registerUncaughtExceptionHandler(MainActivity.this);
@@ -187,11 +197,6 @@ public class MainActivity extends NavigationActivity
}
};
splashScreen.setKeepOnScreenCondition(() -> keepSplash);
Handler handler = new Handler();
handler.postDelayed(() -> keepSplash = false, SPLASH_DELAY);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@@ -1,9 +1,9 @@
package im.status.ethereum;
import androidx.multidex.MultiDexApplication;
import android.util.Log;
import android.content.Context;
import android.webkit.WebView;
import im.status.ethereum.newarchitecture.MainApplicationReactNativeHost;
import java.lang.reflect.InvocationTargetException;
import com.facebook.react.PackageList;
@@ -14,10 +14,9 @@ import com.facebook.react.ReactNativeHost;
import com.reactnativenavigation.NavigationApplication;
import com.reactnativenavigation.react.NavigationReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.reactnativecommunity.blurview.BlurViewPackage;
import com.cmcewen.blurview.BlurViewPackage;
import java.util.List;
@@ -39,7 +38,6 @@ public class MainApplication extends NavigationApplication {
@Override
protected List<ReactPackage> getPackages() {
StatusPackage statusPackage = new StatusPackage(RootUtil.isDeviceRooted());
List<ReactPackage> packages = new PackageList(this).getPackages();
@@ -62,26 +60,48 @@ public class MainApplication extends NavigationApplication {
}
};
private final ReactNativeHost mNewArchitectureNativeHost = new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
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();
}
}
}
}
@@ -20,58 +20,25 @@ import java.security.cert.X509Certificate;
import im.status.ethereum.module.StatusPackage;
class StatusOkHttpClientFactory implements OkHttpClientFactory {
private static final String TAG = "StatusOkHttpClientFactory";
public OkHttpClient createNewNetworkModuleClient() {
X509Certificate cert = null;
HandshakeCertificates clientCertificates;
String certPem = "";
String certPem = StatusPackage.getImageTLSCert();
X509Certificate cert;
// Get TLS PEM certificate from status-go
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(500);
certPem = StatusPackage.getImageTLSCert();
} catch(Exception e) {
Log.e(TAG, "Could not getImageTLSCert",e);
}
// Convert PEM certificate string to X509Certificate object
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
// java.lang.RuntimeException: Could not invoke WebSocketModule.connect
Thread.sleep(500);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certPem.getBytes()));
} catch(Exception e) {
Log.e(TAG, "Could not parse certificate",e);
}
// Create HandshakeCertificates object with our certificate
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
// java.lang.RuntimeException: Could not invoke WebSocketModule.connect
Thread.sleep(500);
clientCertificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addTrustedCertificate(cert)
.build();
} catch(Exception e) {
Log.e(TAG, "Could not build HandshakeCertificates", e);
return null;
Log.e("StatusOkHttpClientFactory", "Could not parse certificate");
cert = null;
}
// Create OkHttpClient with custom SSL socket factory and trust manager
try {
return OkHttpClientProvider.createClientBuilder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
} catch(Exception e) {
Log.e(TAG, "Could not create OkHttpClient", e);
return null;
}
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addTrustedCertificate(cert)
.build();
return OkHttpClientProvider.createClientBuilder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
}
}
@@ -1,103 +0,0 @@
package im.status.ethereum.newarchitecture;
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.fabric.ReactNativeConfig;
import com.facebook.react.uimanager.ViewManagerRegistry;
import im.status.ethereum.BuildConfig;
import im.status.ethereum.newarchitecture.components.MainComponentsRegistry;
import im.status.ethereum.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
* TurboModule delegates and the Fabric Renderer.
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationReactNativeHost extends ReactNativeHost {
public MainApplicationReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
// packages.add(new TurboReactPackage() { ... });
// If you have custom Fabric Components, their ViewManagers should also be loaded here
// inside a ReactPackage.
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@NonNull
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
// for the new architecture and to use TurboModules correctly.
return new MainApplicationTurboModuleManagerDelegate.Builder();
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
// Here we provide a new JSIModuleSpec that will be responsible of providing the
// custom Fabric Components.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Here we register a Components Registry.
// The one that is generated with the template contains no components
// and just provides you the one from React Native core.
MainComponentsRegistry.register(componentFactory);
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry);
}
});
return specs;
}
};
}
}
@@ -1,30 +0,0 @@
package im.status.ethereum.newarchitecture.components;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
/**
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
@DoNotStrip
public class MainComponentsRegistry {
static {
SoLoader.loadLibrary("fabricjni");
}
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MainComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory);
}
@DoNotStrip
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
return new MainComponentsRegistry(componentFactory);
}
}
@@ -1,40 +0,0 @@
package im.status.ethereum.newarchitecture.modules;
import com.facebook.jni.HybridData;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.soloader.SoLoader;
import java.util.List;
/**
* Class responsible to load the TurboModules. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationTurboModuleManagerDelegate
extends ReactPackageTurboModuleManagerDelegate {
private static volatile boolean sIsSoLibraryLoaded;
protected MainApplicationTurboModuleManagerDelegate(
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
super(reactApplicationContext, packages);
}
protected native HybridData initHybrid();
native boolean canCreateTurboModule(String moduleName);
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
protected MainApplicationTurboModuleManagerDelegate build(
ReactApplicationContext context, List<ReactPackage> packages) {
return new MainApplicationTurboModuleManagerDelegate(context, packages);
}
}
@Override
protected synchronized void maybeLoadOtherSoLibraries() {
if (!sIsSoLibraryLoaded) {
// If you change the name of your application .so file in the Android.mk file,
// make sure you update the name here as well.
SoLoader.loadLibrary("status-im_appmodules");
sIsSoLibraryLoaded = true;
}
}
}
-39
View File
@@ -1,39 +0,0 @@
THIS_DIR := $(call my-dir)
include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
# If you wish to add a custom TurboModule or Fabric component in your app you
# will have to include the following autogenerated makefile.
# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
include $(CLEAR_VARS)
LOCAL_PATH := $(THIS_DIR)
# You can customize the name of your application .so file here.
LOCAL_MODULE := rndiffapp_appmodules
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
# If you wish to add a custom TurboModule or Fabric component in your app you
# will have to uncomment those lines to include the generated source
# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
#
# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
# Here you should add any native library you wish to depend on.
LOCAL_SHARED_LIBRARIES := \
libfabricjni \
libfbjni \
libfolly_runtime \
libglog \
libjsi \
libreact_codegen_rncore \
libreact_debug \
libreact_nativemodule_core \
libreact_render_componentregistry \
libreact_render_core \
libreact_render_debug \
libreact_render_graphics \
librrc_view \
libruntimeexecutor \
libturbomodulejsijni \
libyoga
LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
include $(BUILD_SHARED_LIBRARY)
@@ -1,20 +0,0 @@
#include "MainApplicationModuleProvider.h"
#include <rncore.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`:
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
return rncore_ModuleProvider(moduleName, params);
}
} // namespace react
} // namespace facebook
@@ -1,11 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <ReactCommon/JavaTurboModule.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params);
} // namespace react
} // namespace facebook
@@ -1,38 +0,0 @@
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainApplicationModuleProvider.h"
namespace facebook {
namespace react {
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
MainApplicationTurboModuleManagerDelegate::initHybrid(
jni::alias_ref<jhybridobject>) {
return makeCxxInstance();
}
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
makeNativeMethod(
"canCreateTurboModule",
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
});
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) {
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) {
return MainApplicationModuleProvider(name, params);
}
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
std::string name) {
return getTurboModule(name, nullptr) != nullptr ||
getTurboModule(name, {.moduleName = name}) != nullptr;
}
} // namespace react
} // namespace facebook
@@ -1,30 +0,0 @@
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook {
namespace react {
class MainApplicationTurboModuleManagerDelegate
: public jni::HybridClass<
MainApplicationTurboModuleManagerDelegate,
TurboModuleManagerDelegate> {
public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor =
"Lcom/rndiffapp/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) override;
/**
* Test-only method. Allows user to verify whether a TurboModule can be
* created by instances of this class.
*/
bool canCreateTurboModule(std::string name);
};
} // namespace react
} // namespace facebook
@@ -1,48 +0,0 @@
#include "MainComponentsRegistry.h"
#include <CoreComponentsRegistry.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>
namespace facebook {
namespace react {
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
std::shared_ptr<ComponentDescriptorProviderRegistry const>
MainComponentsRegistry::sharedProviderRegistry() {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
return providerRegistry;
}
jni::local_ref<MainComponentsRegistry::jhybriddata>
MainComponentsRegistry::initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate) {
auto instance = makeCxxInstance(delegate);
auto buildRegistryFunction =
[](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto registry = MainComponentsRegistry::sharedProviderRegistry()
->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
auto mutableRegistry =
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{
eventDispatcher, contextContainer, nullptr}));
return registry;
};
delegate->buildRegistryFunction = buildRegistryFunction;
return instance;
}
void MainComponentsRegistry::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
});
}
} // namespace react
} // namespace facebook
@@ -1,24 +0,0 @@
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MainComponentsRegistry
: public facebook::jni::HybridClass<MainComponentsRegistry> {
public:
// Adapt it to the package you used for your Java class.
constexpr static auto kJavaDescriptor =
"Lcom/rndiffapp/newarchitecture/components/MainComponentsRegistry;";
static void registerNatives();
MainComponentsRegistry(ComponentFactory *delegate);
private:
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate);
};
} // namespace react
} // namespace facebook
-10
View File
@@ -1,10 +0,0 @@
#include <fbjni/fbjni.h>
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainComponentsRegistry.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::MainApplicationTurboModuleManagerDelegate::
registerNatives();
facebook::react::MainComponentsRegistry::registerNatives();
});
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

@@ -2,6 +2,4 @@
<resources>
<color name="alert_background">#ffffff</color>
<color name="alert_text">#000000</color>
<color name="splash_background">#09101C</color>
<color name="splash_status_bar_color">#ffffff</color>
</resources>
@@ -1,13 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.AppSplash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splash_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_logo</item>
<item name="windowSplashScreenAnimationDuration">1000</item>
<!-- Status bar and Nav bar configs -->
<item name="android:statusBarColor" tools:targetApi="l">@color/splash_background</item>
<item name="android:windowLightStatusBar">false</item>
<item name="postSplashScreenTheme">@style/DarkTheme</item>
</style>
</resources>
+3 -34
View File
@@ -1,30 +1,16 @@
import org.apache.tools.ant.taskdefs.condition.Os
// Top-level build file where you can add configuration options common to all sub-projects/modules.
/**
* Project-wide gradle configuration properties for use by all modules
*/
ext {
// kotlin_version is needed for react-native-camera-kit library
kotlin_version = project.kotlinVersion
RNNKotlinVersion = "1.6.20"
RNGH_kotlinVersion = "1.6.20"
RNNKotlinVersion = "1.4.31"
buildToolsVersion = project.buildToolsVersion
minSdkVersion = Integer.valueOf(project.minSdkVersion)
compileSdkVersion = Integer.valueOf(project.compileSdkVersion)
targetSdkVersion = Integer.valueOf(project.targetSdkVersion)
supportLibVersion = project.supportLibVersion
gradlePluginVersion = project.gradlePluginVersion
kotlinPluginVersion = project.kotlinPluginVersion
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
}
buildscript {
@@ -34,10 +20,8 @@ buildscript {
mavenCentral()
}
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:${project.ext.gradlePluginVersion}"
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:5.0.1")
// WARNING: Do not place your application dependencies here!
// They belong in the individual module build.gradle files.
}
@@ -66,20 +50,6 @@ allprojects {
}
repositories {
exclusiveContent {
// We get React Native's Android binaries exclusively through npm,
// from a local Maven repo inside node_modules/react-native/.
// (The use of exclusiveContent prevents looking elsewhere like Maven Central
// and potentially getting a wrong version.)
filter {
includeGroup "com.facebook.react"
}
forRepository {
maven {
url "$rootDir/../node_modules/react-native/android"
}
}
}
mavenLocal()
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
maven { url("$rootDir/../node_modules/react-native/android") }
@@ -91,7 +61,6 @@ allprojects {
google()
jcenter() // Required for @react-native-community/blur:3.6.1, can be removed after updating to 3.6.1+
mavenCentral()
// maven { url "https://www.jitpack.io" }
maven { url 'https://mvnrepository.com/artifact/com.github.gundy/semver4j'}
maven { url "https://www.jitpack.io" }
}
}
+4 -9
View File
@@ -21,15 +21,14 @@
# $keytool -genkey -v -keystore ./status-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias status
# Version requirements used throughout the Gradle scripts
kotlinVersion=1.6.20
kotlinVersion=1.3.50
minSdkVersion=23
compileSdkVersion=31
targetSdkVersion=31
buildToolsVersion=31.0.0
supportLibVersion=28.0.0
# This should match version from nix/mobile/android/maven-and-npm-deps/maven/default.nix
gradlePluginVersion=7.2.2
kotlinPluginVersion=1.6.20
gradlePluginVersion=3.5.4
android.useAndroidX=true
android.enableJetifier=true
@@ -49,9 +48,5 @@ org.gradle.jvmargs=-Xmx8704M
versionCode=9999
commitHash=unknown
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# 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
# Flipper
FLIPPER_VERSION=0.54.0
-7
View File
@@ -20,10 +20,3 @@ include ':react-native-status-keycard'
project(':react-native-status-keycard').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-status-keycard/android')
include ':react-native-blur'
project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/blur/android')
includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
}
+1 -1
View File
@@ -1,4 +1,4 @@
{
"name": "StatusIm",
"displayName": "StatusIm"
}
}
+23 -18
View File
@@ -1,19 +1,24 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin', '@babel/plugin-transform-named-capturing-groups-regex'],
env: {
test: {
presets: [
'@babel/preset-react',
[
'@babel/preset-env',
{
targets: {
node: '14',
},
},
],
],
},
},
};
"presets": [
"module:metro-react-native-babel-preset"
],
"plugins": [
"react-native-reanimated/plugin"
],
"env": {
"test": {
"presets": [
'@babel/preset-react',
[
'@babel/preset-env',
{
targets: {
node: '14',
},
},
],
],
}
}
}
+16 -5
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.14' }
agent { label 'linux && x86_64 && nix-2.11' }
options {
timestamps()
@@ -41,12 +40,16 @@ pipeline {
BUILD_ENV = 'prod'
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1
/* coverage report identification */
COVERALLS_SERVICE_NAME = "jenkins"
COVERALLS_SERVICE_JOB_ID = "${JOB_NAME}#${BUILD_NUMBER}"
}
stages {
stage('Prep') {
steps {
script {
utils.doGitRebasePR()
utils.symlinkEnv()
println("Build Number: ${utils.genBuildNumber()}")
}
@@ -72,12 +75,20 @@ pipeline {
stage('Upload') {
steps { script {
def urls = apks.collect { s3.uploadArtifact(it) }
if (urls.size() > 1) { /* Return only the universal APK. */
/* return only the universal APK */
if (urls.size() > 1) {
env.PKG_URL = urls.find { it.contains('universal') }
} else { /* If no universal is available pick first. */
} else { /* if no universal is available pick first */
env.PKG_URL = urls.first()
}
jenkins.setBuildDesc(APK: env.PKG_URL)
/* e2e builds get tested in SauceLabs */
if (utils.isE2EBuild()) {
env.SAUCE_URL = android.uploadToSauceLabs()
}
if (utils.isNightlyBuild()) {
env.DIAWI_URL = android.uploadToDiawi()
}
} }
}
}
+20 -5
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
pipeline {
agent { label 'linux' }
@@ -18,7 +17,7 @@ pipeline {
}
/* WARNING: Defining parameters here with the ?: trick causes them to remember last value. */
parameters {
parameters {
choice(
name: 'BUILD_TYPE',
description: 'Makefile target to build. Optional Parameter.',
@@ -47,6 +46,9 @@ pipeline {
stage('Android') { steps { script {
apk = jenkins.Build('status-mobile/platforms/android')
} } }
stage('Android e2e') { steps { script {
apke2e = jenkins.Build('status-mobile/platforms/android-e2e')
} } }
stage('Tests') { steps { script {
jenkins.Build('status-mobile/platforms/tests')
} } }
@@ -56,7 +58,9 @@ pipeline {
steps { script {
sh('rm -f pkg/*')
jenkins.copyArts(ios)
//jenkins.copyArts(iose2e)
jenkins.copyArts(apk)
jenkins.copyArts(apke2e)
sha = "pkg/${utils.pkgFilename(ext: 'sha256')}"
dir('pkg') {
/* generate sha256 checksums for upload */
@@ -70,8 +74,8 @@ pipeline {
/* object for easier URLs handling */
urls = [
/* mobile */
Apk: utils.pkgUrl(apk),
iOS: utils.pkgUrl(ios),
Apk: utils.pkgUrl(apk), Apke2e: utils.pkgUrl(apke2e),
iOS: utils.pkgUrl(ios), /*iOSe2e: utils.pkgUrl(iose2e),*/
Diawi: utils.getEnv(ios, 'DIAWI_URL'),
/* upload the sha256 checksums file too */
SHA: s3.uploadArtifact(sha),
@@ -95,6 +99,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_NAME', value: e2eApk)],
wait: false
)
} }
}
}
}
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
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),
]
)
}
}
}
}
+10 -11
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@set_ios_status_go_targets'
library 'status-jenkins-lib@v1.6.3'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'macos && arm64 && nix-2.14 && xcode-14.3' }
agent { label 'macos && arm64 && nix-2.11 && xcode-14.2' }
parameters {
string(
@@ -49,6 +48,7 @@ pipeline {
stage('Prep') {
steps {
script {
utils.doGitRebasePR()
utils.symlinkEnv()
println("Build Number: ${utils.genBuildNumber()}")
}
@@ -63,11 +63,6 @@ pipeline {
steps {
script { api = ios.bundle() }
}
post {
failure {
archiveArtifacts 'ios/logs/*'
}
}
}
stage('Parallel Upload') {
parallel {
@@ -77,11 +72,15 @@ pipeline {
}
}
stage('Upload') {
when { expression { !utils.isReleaseBuild() } }
steps { script {
env.DIAWI_URL = ios.uploadToDiawi()
env.PKG_URL = env.DIAWI_URL
env.PKG_URL = s3.uploadArtifact(api)
jenkins.setBuildDesc(IPA: env.PKG_URL)
/* e2e builds get tested in SauceLabs */
if (utils.isE2EBuild()) {
env.SAUCE_URL = ios.uploadToSauceLabs()
} else if (!utils.isReleaseBuild()) {
env.DIAWI_URL = ios.uploadToDiawi()
}
} }
}
}
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
pipeline {
agent { label params.AGENT_LABEL }
@@ -29,7 +28,7 @@ pipeline {
timestamps()
disableConcurrentBuilds()
/* Prevent Jenkins jobs from running forever */
timeout(time: 120, unit: 'MINUTES')
timeout(time: 300, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '20',
@@ -42,19 +41,18 @@ pipeline {
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)
arch = sh(script: 'arch', returnStdout: true)
uname = sh(script: 'uname', returnStdout: true)
} }
}
stage('Build status-go') {
steps { script {
def platforms = ['mobile.android', 'mobile.ios', 'library']
if (os != 'Darwin') { platforms.removeAll { it == 'mobile.ios' } }
/* FIXME: Remove this when #16237 is merged. */
if (arch == 'arm64') { platforms.removeAll { it == 'mobile.android' } }
def platforms = ['mobile.android', 'mobile.ios']
if (uname != "Darwin") {
platforms.removeAll { it == "ios" }
}
platforms.each { os ->
nix.build(
attr: "targets.status-go.${os}",
attr: "targets.status-go.${os}.buildInputs",
sandbox: false,
link: false
)
@@ -63,9 +61,10 @@ pipeline {
}
stage('Build android jsbundle') {
steps { script {
/* Build/fetch deps required for jsbundle build. */
/* build/fetch things required to produce a js-bundle for android
* (e.g. maven and node repos) */
nix.build(
attr: 'targets.mobile.jsbundle',
attr: 'targets.mobile.android.jsbundle',
sandbox: false,
pure: false,
link: false
@@ -74,7 +73,7 @@ pipeline {
}
stage('Build android deps') {
steps { script {
/* Build/fetch deps required to build android release. */
/* build/fetch things required to build jsbundle and android */
nix.build(
attr: 'targets.mobile.android.release.buildInputs',
sandbox: false,
@@ -85,16 +84,12 @@ pipeline {
}
stage('Build nix shell deps') {
steps { script {
def shells = ['android', 'ios', 'fastlane', 'keytool', 'clojure', 'gradle']
if (os != "Darwin") { shells.removeAll { it == 'ios' } }
/* Build/fetch deps required to start default Nix shell. */
shells.each { shell ->
nix.build(
attr: "shells.${shell}.buildInputs",
sandbox: false,
link: false
)
}
/* build/fetch things required to instantiate shell.nix for TARGET=all */
nix.build(
attr: 'shells.default.buildInputs',
sandbox: false,
link: false
)
} }
}
stage('Upload') {
+10 -12
View File
@@ -1,16 +1,15 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.14' }
agent { label 'linux && x86_64 && nix-2.11' }
options {
timestamps()
/* Prevent Jenkins jobs from running forever */
timeout(time: 15, unit: 'MINUTES')
timeout(time: 10, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '10',
@@ -42,6 +41,13 @@ pipeline {
}
stages {
stage('Prep') {
steps {
script {
utils.doGitRebasePR()
}
}
}
stage('Checks') {
parallel {
stage('Lint') {
@@ -62,14 +68,6 @@ pipeline {
}
}
}
stage('Component Tests') {
steps {
sh """#!/bin/bash
set -eo pipefail
make component-test 2>&1 | tee -a ${LOG_FILE}
"""
}
}
stage('Upload') {
steps {
script {
+5 -33
View File
@@ -1,49 +1,25 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
pipeline {
agent { label 'linux' }
parameters {
string(
name: 'APK_BUILD_NUMBER',
description: 'platform/e2e build number for apk artifact',
name: 'APK_NAME',
description: 'Filename of APK uploaded to SauceLabs.',
)
string(
name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)',
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 {
disableConcurrentBuilds()
}
stages {
stage('Fetch') {
steps { script {
copyArtifacts(
projectName: "status-mobile/platforms/android-e2e",
filter: 'result/*-x86.apk',
selector: specific(env.APK_BUILD_NUMBER),
)
apk_path = "${env.WORKSPACE}/${utils.findFile('result/*-x86.apk')}"
} }
}
stage('Setup') {
steps { script {
dir('test/appium') {
@@ -51,7 +27,6 @@ pipeline {
}
} }
}
stage('Test') {
steps {
withCredentials([
@@ -83,13 +58,13 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 8 \
--numprocesses 4 \
--rerun_count=2 \
--testrail_report=True \
-m testrail_id \
-m \"new_ui_critical or new_ui_medium\" \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_URL ?: apk_path}
--apk=${params.APK_NAME}
"""
}
}
@@ -113,8 +88,5 @@ pipeline {
)
}
}
cleanup {
sh 'make purge'
}
}
}
+17 -46
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
pipeline {
@@ -11,14 +10,20 @@ pipeline {
description: 'Name of the branch to checkout and build.',
defaultValue: 'develop',
)
/* Commented to use TEST_MARKERS values from job params
string(
name: 'TEST_MARKERS',
description: 'Marker expression for matching tests to run.',
defaultValue: 'new_ui_critical',
) */
string(
name: 'APK_NAME',
description: 'Filename of APK uploaded to SauceLabs, path, or URL.',
)
string(
name: 'PR_ID',
description: 'ID of the Pull Request triggering this build.',
)
string(
name: 'APK_URL',
description: 'Optional, set if job require APK to be downloaded from URL.',
)
string(
name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)',
@@ -29,48 +34,19 @@ pipeline {
description: 'IDs of the TestRail case, separated by a comma (Optional)',
defaultValue: '',
)
/* FIXME: Remove this no longer relevant argument */
string(
name: 'APK_NAME',
description: 'OBSOLETE ARGUMENT TO BE REMOVED',
defaultValue: 'DUMMY',
)
/* Commented to use TEST_MARKERS values from job params
string(
name: 'TEST_MARKERS',
description: 'Marker expression for matching tests to run.',
defaultValue: 'new_ui_critical',
)
*/
}
options {
disableConcurrentBuilds()
timeout(time: 90, unit: 'MINUTES')
}
stages {
stage('Prep') {
stage('Checks') {
steps { script {
currentBuild.displayName = "PR-${params.PR_ID}"
if (params.PR_ID == null) {
error("PR_ID parameter not set!")
}
if (params.APK_NAME == null) { error("APK_NAME parameter not set!") }
if (params.PR_ID == null) { error("PR_ID parameter not set!") }
} }
}
stage('Fetch') {
when { expression { !params.APK_URL } }
steps { script {
copyArtifacts(
projectName: "status-mobile/prs/android-e2e/PR-${params.PR_ID}",
/* WARNING: This copies the latest available artifact. */
selector: lastWithArtifacts(),
)
apk_path = "${env.WORKSPACE}/${utils.findFile('result/*.apk')}"
} }
}
stage('Setup') {
steps { script {
dir('test/appium') {
@@ -78,9 +54,9 @@ pipeline {
}
} }
}
stage('Test') {
steps { script {
currentBuild.displayName = "PR-${params.PR_ID}"
/* for managing optional arguments */
def extraPytestOpts = ''
if (params.TR_CASE_IDS != '') {
@@ -123,11 +99,11 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 8 \
--numprocesses 4 \
--rerun_count=2 \
--testrail_report=True \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_URL ?: apk_path} \
--apk=${params.APK_NAME} \
--build=PR-${params.PR_ID}-${utils.timestamp()} \
--pr_number=${params.PR_ID} \
${extraPytestOpts}
@@ -137,9 +113,4 @@ pipeline {
} }
}
}
post {
cleanup {
sh 'make purge'
}
}
}
+6 -24
View File
@@ -1,18 +1,15 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
pipeline {
agent { label 'linux' }
parameters {
string(
name: 'APK_URL',
description: 'URL of APK to be tested(base for upgrade, usually release build)',
name: 'APK_NAME',
description: 'Filename of APK uploaded to SauceLabs (base for upgrade, usually release build)',
)
string(
name: 'APK_URL_UPGRADE',
description: 'URL of APK of upgraded application (installed on top of base)',
name: 'APK_NAME_UPGRADE',
description: 'Filename of APK of upgraded application (installed on top of base)',
)
string(
name: 'KEYWORD_EXPRESSION',
@@ -32,17 +29,6 @@ pipeline {
stages {
stage('Prep') {
steps { script {
if (params.APK_URL == null) {
error("APK_URL parameter not set!")
}
if (params.APK_URL_UPGRADE == null) {
error("APK_URL_UPGRADE parameter not set!")
}
} }
}
stage('Setup') {
steps { script {
dir('test/appium') {
@@ -50,7 +36,6 @@ pipeline {
}
} }
}
stage('Test') {
steps { script {
/* for managing optional arguments */
@@ -92,8 +77,8 @@ pipeline {
--numprocesses 4 \
--rerun_count=2 \
--testrail_report=True \
--apk=${params.APK_URL} \
--apk_upgrade=${params.APK_URL_UPGRADE} \
--apk=${params.APK_NAME} \
--apk_upgrade=${params.APK_NAME_UPGRADE} \
${extraPytestOpts}
"""
}
@@ -118,8 +103,5 @@ pipeline {
)
}
}
cleanup {
sh 'make purge'
}
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
pipeline {
agent { label 'macos' }
+1 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
library 'status-jenkins-lib@v1.6.3'
pipeline {
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 is done to avoid issues with out of date repo causing errors like:
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.14'
pipeline {
agent {
label 'linux'
}
triggers {
cron('H 5 * * *')
}
options {
timestamps()
/* Prevent Jenkins jobs from running forever */
timeout(time: 15, unit: 'MINUTES')
/* Disable concurrent jobs */
disableConcurrentBuilds()
/* Don't keep more than 50 builds */
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Get Nodes') {
steps { script {
stagePerNode = nodesByLabel('macos').collectEntries {
["${it}" : generateNodeCleanupStage(it)]
}
} }
}
stage('Clean Xcode') {
steps { script {
parallel stagePerNode
} }
}
}
}
def generateNodeCleanupStage(nodeLabel) {
return { stage(nodeLabel) {
node(nodeLabel) {
dir('/Users/jenkins/Library/Developer/Xcode') {
sh 'rm -fr Archives DerivedData'
}
}
} }
}
View File
+1 -1
View File
@@ -2,7 +2,7 @@
## Export icons
![](images/export-icons/export-icons.gif)
![](./export-icons.gif)
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"`.
+13 -13
View File
@@ -33,13 +33,13 @@ See https://cursive-ide.com/userguide/index.html
- https://gist.github.com/Samyoul/f71a0593ba7a12d24dd0d5ef986ebbec
- Right click and "add as leiningen project"
<img src="images/ide-setup/1_fake_project_file.png" width=75% />
<img src="images/IDE_SETUP/1_fake_project_file.png" width=75% />
## I get a lot of `cannot be resolved`
Are you getting problems where you get a lot of `cannot be resolved` on everything?
<img src="images/ide-setup/2_resolve.jpeg" width=75% />
<img src="images/IDE_SETUP/2_resolve.jpeg" width=75% />
See https://cursive-ide.com/userguide/macros.html
@@ -56,21 +56,21 @@ I had a number of problems connecting to REPL, the solution is as follows:
At the top of IntelliJ IDEA click on the `Add Configuration...` option:
<img src="images/ide-setup/3_REPL_1.png" width=75% />
<img src="images/IDE_SETUP/3_REPL_1.png" width=75% />
This will load the following menu:
<img src="images/ide-setup/4_REPL_2.png" width=75% />
<img src="images/IDE_SETUP/4_REPL_2.png" width=75% />
Click on the `+` icon in the top left corner of the menu.
Select `Clojure REPL > Remote`
<img src="images/ide-setup/5_REPL_3.png" width=75% />
<img src="images/IDE_SETUP/5_REPL_3.png" width=75% />
Which will load the following menu
<img src="images/ide-setup/6_REPL_4.png" width=75% />
<img src="images/IDE_SETUP/6_REPL_4.png" width=75% />
Enter the below options:
@@ -81,14 +81,14 @@ Enter the below options:
- Host = 127.0.0.1
- Port = 7888
<img src="images/ide-setup/7_REPL_5.png" width=75% />
<img src="images/IDE_SETUP/7_REPL_5.png" width=75% />
Press `OK`
Now the below option will be visible.
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 an dialog with the following message:
@@ -113,7 +113,7 @@ Which should output
See below:
<img src="images/ide-setup/9_REPL_7.png" width=75% />
<img src="images/IDE_SETUP/9_REPL_7.png" width=75% />
#### Connecting REPL and IntelliJ to `status-mobile`
@@ -138,7 +138,7 @@ Next go back to the REPL input and enter the following commands:
See Below
<img src="images/ide-setup/10_REPL_8.png" width="75%" />
<img src="images/IDE_SETUP/10_REPL_8.png" width="75%" />
Which should switch the clj file type target to cljs as shown above
@@ -146,13 +146,13 @@ Finally you are ready to test REPL.
Create a sample function to evaluate something simple like `(prn "I'm working")`, move your cursor to one of the outer parentheses. Right or `control` click and select the `REPL` option. From there select `Sync files in REPL` and then `Send '...' to REPL'`.
<img src="images/ide-setup/11_REPL_9.png" width="75%" />
<img src="images/IDE_SETUP/11_REPL_9.png" width="75%" />
Alternatively you can use the shortcut commands `⇧⌘M` to sync your files and `⇧⌘P` to send the statement to REPL. You may also need to switch the REPL namespace to match the current file, which can be done manually from the dialogue box or using the `⇧⌘N` shortcut key.
Following the above should give you the below result:
<img src="images/ide-setup/12_REPL_10.png" width="75%" />
<img src="images/IDE_SETUP/12_REPL_10.png" width="75%" />
🎉 Tada! Working! 🎉
@@ -169,7 +169,7 @@ For VS Code users.
### Start and connect the REPL
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. 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`
-54
View File
@@ -1,54 +0,0 @@
## Getting Started
[Starting Guide](starting-guide.md)
[IDE Setup](ide-setup.md)
## Development Process
[Coding guidelines](new-guidelines.md)
[Release Checklist](release-checklist.md)
[Release Guide](release-guide.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)
[Debugging](debugging.md)
[Patching](patching.md)
[Creating a pixel perfect UI](pixel-perfection.md)
## Testing
[How to run local tests](testing.md)
[End-to-end tests (e2e) overview](how-to-launch-e2e.md)
[Component tests (jest) overview](component-tests-overview.md)
## Misc
[Importing icons from Figma into project](export-icons.md)
[Updating Status APK builds for the F-Droid Android application catalogue](fdroid.md)
[Troubleshooting for known errors](troubleshooting.md)
## Outdated:
[Old guidelines](codebase-structure-and-guidelines.md)
[Post mortem analysis](post-mortem.md)
@@ -28,30 +28,11 @@ You need to have an emulator like [AVD](https://developer.android.com/studio/run
### iOS
#### Running on a simulator
We highly recommend using the `iPhone 11 Pro` simulator as its screen dimensions match with our design.
If you have Xcode `v12.x` (and above) installed in your system, you need to follow the below steps to add `iPhone 11 Pro` simulator:
1. Open Xcode
2. Menu `>` Window `>` Devices and Simulators
3. Tap `+` button on bottom left
4. Select **Device Type** as `iPhone 11 Pro`
5. Leave the **Simulator Name** empty and tap on **Create**
##### NOTE ⚠️
Running `make run-ios` will target `iPhone 11 Pro` by default.
If you need to run on any other simulator, you can specify the simulator type by adding the `SIMULATOR` flag:
You can specify the simulator type by adding the `SIMULATOR` flag:
```sh
make run-ios SIMULATOR="iPhone 11 Pro"
```
#### Running on a physical device
Some manual steps are necessary for [developing on a physical iOS Device](#physical-ios-device).
Some manual steps are necesary for [developing on a physical iOS Device](#physical-ios-device).
# Build release
+40
View File
@@ -0,0 +1,40 @@
# Testing
### Unit & integration tests
To run tests:
```
make test
```
To watch the tests:
```
make test-watch
```
To run test in REPL
```
make test
yarn shadow-cljs cljs-repl test # or start the REPL in your editor
```
Then start the test process with
```
node --require ./test-resources/override.js target/test/test.js --repl
```
You can run single test in REPL like this
```clojure
(require 'cljs.test)
(cljs.test/test-var #'status-im.data-store.chats-test/normalize-chat-test)
```
Tests will use the bindings in `modules/react-native-status/nodejs`, if you make any changes to these you will need to restart the watcher.
@@ -1,27 +1,6 @@
## `yarn add` is not working
# [DEPRECATED] Undefined is not an object evaluating `register_handler_fx`
While running any yarn add command like `yarn add react-native-share@7.0.2`, it is showing error
```
error status-mobile/node_modules/better-sqlite3: Command failed.
```
### Cause
Local `node` version can be different from the one needed by Status project.
### Solution
Before running `yarn add`, nix shell should be started:
```
make shell
```
## [DEPRECATED] Undefined is not an object evaluating `register_handler_fx`
### Deprecation note
## Deprecation note
This type of error should not occur anymore now that we require the namespace in the `fx.cljs` file.
@@ -35,7 +14,7 @@ That way you don't need to use any magical call like `find-ns` or inline `requir
You also want to make sure users are using the macro by using a aliased namespace defined in require statement rather than require-macro and refer to the macro directly. Otherwise it won't require the cljs file and the require statement of the namespace in the macroexpension might not be there.
### Stacktrace
## Stacktrace
```
13:25:22, Requiring: hi-base32
@@ -55,19 +34,19 @@ _callTimer@http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false
_callImmediatesPass@http://localhost:8081/index.bundle?pla<…>
```
### Cause
## Cause
- stacktrace mentions `register_handler_fx`,
- common cause is when requires have been cleaned up and a require of `status-im.utils.handlers` namespace was removed because it looked like it was unused but was actually used through a fx/defn macro
### Solution
## Solution
go through known faulty commit looking for deleted requires
## Git "unable to access" errors during `yarn install`
# Git "unable to access" errors during `yarn install`
### Description
## Description
Developer updates `package.json` file with a new dependency using a GitHub URL. So it looks like this:
```
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#feature/exportKeyWithPath",
@@ -81,10 +60,10 @@ fatal: unable to access 'https://github.com/status-im/react-native-status-keycar
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
```
### Cause
## Cause
`yarn.lock` is not updated to be in sync with `package.json`.
### Solution
## Solution
Update yarn.lock file. In order to do this, perform the following steps on a clean `status-mobile` repo:
```
cd status-mobile
@@ -93,9 +72,9 @@ yarn install
and don't forget to commit updated `yarn.lock` together with `package.json`.
## adb server/client version mismatch errors
# adb server/client version mismatch errors
### Description
## Description
Running some adb commands, e.g. `adb devices` or `make android-ports` (in turn invokes `adb reverse`/`adb forward` commands) may display the following message:
```
adb server version (40) doesn't match this client (41); killing...
@@ -111,10 +90,10 @@ This might cause all kinds of difficult-to-debug errors, e.g.:
- `make run-android` throwing `- Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081 Unable to install /status-mobile/android/app/build/outputs/apk/debug/app-debug.apk com.android.ddmlib.InstallException: EOF`
- dropped CLJS repl connections (that have been enabled previously with the help of `make android-ports`)
### Cause
## Cause
System's local adb and Nix's adb differ. As adb include of server/client processes, this can cause subtle version errors that cause adb to kill mismatching server processes.
### Solution
## Solution
Always use respective `make` commands, e.g. `make android-ports`, `make android-devices`, etc.
Alternatively, run adb commands only from `make shell TARGET=android` shell. Don't forget the `TARGET=android` env var setting - otherwise `adb` will still be selected from the system's default location. You can double-check this by running `which adb`.
@@ -146,4 +125,4 @@ For x86 CPU architecture Android Devices, Hermes is creating the issue and the a
### Solution
Disable Hermes while building the app
`make run-android DISABLE_HERMES=true`
`make run-android DISABLE_HERMES=true`
+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.
## 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](./log-settings.png)
## 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.
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
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).
-55
View File
@@ -1,55 +0,0 @@
# Component Tests
The component tests are using React Native Testing Library - https://callstack.github.io/react-native-testing-library/
and Jest - https://jestjs.io/
It is highly recommended to read some advice from Kent C.Dodds on how to write tests and use these tools correctly.
https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
https://www.youtube.com/watch?v=ahrvE062Kv4
Both of these links are showing it for React-Testing-Library (not Native) however the approach is for the most part considered the same.
## Running the tests
To run these tests there are two methods.
`make component-test`
setups and runs the test suite once.
`make component-test-watch`
setups and runs the test suite and watches for code changes will then retrigger the test suite.
## Writing Tests
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 shadow-cljs config discussed below.
### Best practices
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.
### 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.
## Configuration
Status Mobile has a bespoke tech stack, as such there is more complexities to configuring the tests.
### Shadow-CLJS
the configuration for compiling our tests are defined in the "shadow-cljs.edn" file.
The three main parts of this are
`:target :npm-module`
Needed for the configuration we are using
`:entries`
a vector of entry points for the test files.
and the `ns-regexp` to specify what tests to find. Since we have multiple forms of tests we decided that "component-spec" is the least likely to detect the wrong file type.
It's worth knowing that our tests are compiled to JS and then run in the temporary folder `component-tests`.
### 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 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
-64
View File
@@ -1,64 +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)
## 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'
```
+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 |
| 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.
-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

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

-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
-103
View File
@@ -1,103 +0,0 @@
How to Launch E2E
===
## Overview for how automated test structured for Status app
As a part of CI for Status mobile app and in order to ensure there are no regressions appear after the changes in code (bug fix, new/updated feature) we are using automated tests (e2e tests).
- Automated tests written on Python 3.9 and pytest.
- Appium (server) and Selenium WebDriver (protocol) are the base of test automation framework.
TestRail is a test case management system tool where we have test cases.
Each of the test case gets a priority (Critical/High/Medium)
**SauceLabs** - is a cloud based mobile application test platform. We are using Android emulators (Android 10.0) for test script execution there. We have 16 session be running at the same time max.
For now we support e2e for Android only.
## 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.
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).
In that case we save results in TestRail as well and push a comment with test results in a respective PR.
For example: https://github.com/status-im/status-react/pull/9147#issuecomment-540008770
![](images/how-to-launch-e2e/how-to-launch-e2e-1.png)
The test_send_stt_from_wallet opens link in TestRail https://ethstatus.testrail.net/index.php?/tests/view/890885 where performed steps could be found
List of all runs performed by test jobs could be found here https://ethstatus.testrail.net/index.php?/runs/overview/14
**For credentials for TestRail to see results ping Chu in DM**:
Opening any test run navigates you to list of test cases with results:
![](images/how-to-launch-e2e/how-to-launch-e2e-2.png)
## What about launching e2e manually
To manage e2e there are several jobs in https://ci.status.im/job/status-mobile/job/e2e :
1) [nightly](https://ci.status.im/job/status-mobile/job/e2e/job/status-app-nightly/) - running automatically after building nightly apk e2e build. QA running it manually for results of e2e when testing release.
2) [upgrade](https://ci.status.im/job/status-mobile/job/e2e/job/status-app-upgrade/) - running manually by QA in release testing for smoke upgrade tests.
3) [prs](https://ci.status.im/job/status-mobile/job/e2e/job/status-app-prs/) - running **only automatically** when PR moves into `e2e column`
4) [prs-rerun](https://ci.status.im/job/status-mobile/job/e2e/job/status-app-prs-rerun/) - for manual run, can be run on request by anyone. **If you need to launch e2e against your build, use this job.**
Params to specify:
- apk: [url_to_apk_build_here]
- pr_id: pull request number (e.g. 1234)
- 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`, 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 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)
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.
![](images/how-to-launch-e2e/how-to-launch-e2e-3.png)
And then hit Build.
Once the job starts it picks up specified tests, runs them against provided apk and sends results to pull request.
Even we have 16 parallel sessions for testing its a time consuming operation (whole test suite we have automated at the moment takes ~140 minutes to finish).
So for PRs we pick only set of `critical or high` (you can also use this in TEST_MARKS param for job)
tests (otherwise some PRs could wait their turn of the scheduled Jenkins job till the next day).
## 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 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:
- Test clicked on element which should load new screen (or pop-up) and awaits some element on this screen. But test did not wait enough allowing the new screen to appear and so it fails with “Could not find element XYZ” (this case is more app issue in our opinion rather then test issue, but we just can not spend our and dev time with too specific random places which happens once causing app lags in different moments)
- Test sent transaction to address but it was not mined in time (we have a limit to wait until balance is changed on recipient side up to ~6 mins now). We classify this as False Fail, because its not the app issue but more network issue.
- Test infrastructure issues, - anything related to infrastructure including SauceLabs side issues (apk failed to install - rare case, or LTE connection was set by default instead WiFi or unexpected pop-up appeared preventing test to going further)
- Failure due to changed feature which has not been taken into account in some test after code merge (for instance: some element on screen has been removed, and we want to locate another element on this screen via XPath which is different now)
- **Valid issue in the automated test scripts** - that's what we're looking for
Example: here is the test results https://github.com/status-im/status-react/pull/13015#issuecomment-1016495043 where one test failed.
1. Open the test in TestRail and open session recorded for this test in SauceLabs
![](images/how-to-launch-e2e/how-to-launch-e2e-4.png)
In TestRail you may find all the steps performed by the test.
In SauceLabs testrun page you may find useful: video of the session, step logs, logcat.log of the session
2. Analyze step where test was failed
For particular example it was failed on `Recover access(password:qwerty, keycard:False)` and unexpected error appeared.
## Limits for e2e tests coverage
Not all features of the app could be covered by e2e at the moment:
- Colours or place of an element on UI.
- Real ETH/token transactions. Thats the main reason we have separate .apk build for automation needs - it defaults to Goerli network. Also it has enabled keycard test menu, ENS names and chat commands are also on Goerli network (the same in PR builds, but not in nightlies / release)
- Autologin/Biometric related actions (autologin available when device meets certain conditions like the it has set unlock password and device is not rooted: all emulators are rooted in SauceLabs)
## Brief flow for test to be automated
Whenever there is a need to have a new test:
1) Create a test scenario in TestRail.
2) If certain item could be checked in scope of existing test case we update existing one (otherwise we may have thousands of test cases which is overkill to manage in TestRail as well as in automated test scripts). And also complex autotests increase probability to not catch regressions by stopping test execution (due to valid bug or changed feature) keeping the rest test steps uncovered. So here we need to balance when it makes sense to update existing test case with more checks.
3) Then we create test script based on the test case, ensure test passes for the build and pushing the changes to repo.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Before

Width:  |  Height:  |  Size: 439 KiB

After

Width:  |  Height:  |  Size: 439 KiB

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Before

Width:  |  Height:  |  Size: 290 KiB

After

Width:  |  Height:  |  Size: 290 KiB

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 655 KiB

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Before

Width:  |  Height:  |  Size: 583 KiB

After

Width:  |  Height:  |  Size: 583 KiB

Before

Width:  |  Height:  |  Size: 585 KiB

After

Width:  |  Height:  |  Size: 585 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 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

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