diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 581f8f6b3f8a..000000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,124 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## [0.9.28 - Unreleased] -### Added -- Allow numbers in chat topics -### Fixed -Fix app freeze on recover with wrong password -### Changed - -## [0.9.27] -### Added -- Added BRLN coin #5582 -- Added SuperRare token support #5496 -- Setting to remember login passwords iOS -- ENS username resolution in Chat for stateofus.eth usernames (testnet only) - -### Fixed -- Improve contact code validation #5612 #5613 -- Frequent logouts on iOS -- Peepeth incompatibilities on Android #5726 -- Realm vulnerability PR -- Fixed how node version is shown on the about screen #5688 -- Transaction fixes #5694 #5709 - -### Changed -- Updated Bounties.network address -- Update status-go to v0.13.1 - -## [0.9.26] -### Added -- Allow user to remove custom avatar -### Fixed - -### Changed -- Improved validation in account recovery. We now show a warning if any words are not from our mnemonic dictionary. - -## [0.9.25] -### Added -- Add MOKSHA token on Rinkeby - -### Changed -- Update REP contract address -- Update mailservers on Rinkeby network -- Update url of Bounties Network DApp - -## [0.9.24 - 2018-08-01] -### Fixed -- Security patch for DApp browser - -## [0.9.23 - 2018-07-23] -### Added -- Added dismiss button to "Add to contacts" bar - -### Fixed -- Partially fixed issue with 0 fiat in main Wallet screen. We are now explicitly showing an error when app can't fetch -asset prices, instead of silently failing. - -### Changed - -## 0.9.23 - 2018-07-23 -### Added -- iPad support. Status is now displayed at full native resolution on iPad's -- Deep links support -- Persist browser history -- Added refresh button -- Added more dapps -- Allow for setting custom network id -- Sound for Push Notifications, tap on PN opens corresponding chat - -### Fixed -- Fixed Sign in: Cannot paste text within password field [#3931] -- Fixed chat message layout for right-to-left languages -- Fixed parsing of messages containing multiple dots (elipsis) -- Fixed Webview: Screen cut off when using ERC dEX DApp [#3131] -- Fixed links targeting new tabs [#4413] -- Fixed blinking token amounts in transaction history -- Fixed/corrected fiat values in /send and /request messages -- Automatically converts recovery phrase to lowercase during account recovery -- Fixed unread messages counter -- Fixed incoming messages timestamp -- Fixed gas validation: Showing message when total amount being sent plus the max gas cost exceed the balance [#3441] - -### Changed -- Removed Mixpanel integration - -## [0.9.22] - 2018-07-09 -### Added -- Added Farsi public #status channel -- Spam moderation -- Collectibles support (CryptoKitties, CryptoStrikers and Etheremon) -- Added more dapps -- Universal and deep links for public chats, browsing dapps, viewing profiles - -### Fixed -- Fixed mailservers connectivity issue -- Clear chat action correctly clear the unread messages counter -- Gracefully handle realm decryption failures by showing a pop up asking the user to reset the data - -### Changed -- Downgraded React Native to 0.53.3 for improved performance and decreased battery consumption -- When joining a chat only download one day of history - -## [0.9.21] - 2018-06-25 -### Added -- Mainnet is enabled by default on new installations. Upgrades will need to manually switch. -- Chat now supports sending SNT and other tokens in 1-1 chats -- New chat UI for /send and /receive commands -- Updated chat message styling -- Updated toolbar style -- Updated DApp list -- New help links on the Profile tab. Read our Beta FAQ, file a bug, or suggest and vote on features. -- Performance improvements when fetching old messages -- Added 5 new default public chats for our friends speaking Korean, Chinese, Japanese, Russian and Spanish. - -### Fixed -- Prevented empty commands from being sent in chat -- Fixed minimum amounts in transactions -- Fixed an issue where contacts names may not be shown -- Removed log files from release builds -- Fixed an issue with transactions when navigating back diff --git a/ICONS.md b/ICONS.md new file mode 100644 index 000000000000..e9a4c243077d --- /dev/null +++ b/ICONS.md @@ -0,0 +1,24 @@ +# new icons + +## android +1. copy files to corresponding directories at `/Users/romanvolosovskyi/clj/status-react/android/app/src/main/res` (one of `drawable-hdpi`, `drawable-mdpi`, `drawable-xhdpi`, `drawable-xxhdpi`, `drawable-xxxhdpi` for corresponding resolution) +If you only have 3 pngs 1x, 2x and 3x put them in mdpi, xhdpi and xxhdpi +3. if necessary, rename file so that filename contains only lower case chars, and dashes instead of hyphens, e.g. `"Icon-Name.png"` should be renamed to `"icon_name.png"`. +4. In the app `icon_name.png` still can be accessed as `icon-name`, so in order to use can add the next code: + ```clojure + ;; icon_name.png + [vector-icons/icon :icon-name {:color ...}] + ``` + +## ios +1. open xcode (on macos run `open ios/StatusIm.xcworkspace` from project's root dir) +2. go to `StatusIm/StatusIm/Images.xcassets` in xcode +![](https://notes.status.im/uploads/upload_be25e49db97cb114ff4aa0c9d94422fa.png) +3. add images there +4. if necessary, rename file so that filename contains only lower case chars, e.g. `"Icon-Name.png"` should be renamed to `"icon-name"`. +5. **IMPORTANT** there is no need to replace hyphens with dashes, and if you do so you will need to use names with dashes in both android and ios versions. So use dashes for android resources names and hyphens for ios. +6. And now `"icon-name"` can be added in app the same way as it was added for android version + ```clojure + ;; icon-name + [vector-icons/icon :icon-name {:color ...}] + ``` diff --git a/android/app/src/main/res/drawable-mdpi/faceid.png b/android/app/src/main/res/drawable-mdpi/faceid.png new file mode 100644 index 000000000000..9f817b0addcd Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/faceid.png differ diff --git a/android/app/src/main/res/drawable-mdpi/print.png b/android/app/src/main/res/drawable-mdpi/print.png new file mode 100644 index 000000000000..9556c28bba5b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/print.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/faceid.png b/android/app/src/main/res/drawable-xhdpi/faceid.png new file mode 100644 index 000000000000..cf0cae9d2a18 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/faceid.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/print.png b/android/app/src/main/res/drawable-xhdpi/print.png new file mode 100644 index 000000000000..6103fe4ee09b Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/print.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/faceid.png b/android/app/src/main/res/drawable-xxhdpi/faceid.png new file mode 100644 index 000000000000..6c4dd55ac695 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/faceid.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/print.png b/android/app/src/main/res/drawable-xxhdpi/print.png new file mode 100644 index 000000000000..874c7babc3b8 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/print.png differ diff --git a/desktop/js_files/package.json b/desktop/js_files/package.json index 8c23f2d55151..d9b8c7d00874 100644 --- a/desktop/js_files/package.json +++ b/desktop/js_files/package.json @@ -6,6 +6,61 @@ "start": "node node_modules/react-native/local-cli/cli.js start", "prepare": "patch-package" }, + "dependencies": { + "assert": "^1.4.1", + "bignumber.js": "git+https://github.com/status-im/bignumber.js.git#v4.0.2-status", + "buffer": "^3.6.0", + "chance": "^1.0.12", + "create-react-class": "^15.6.2", + "emojilib": "^2.2.9", + "eth-phishing-detect": "^1.1.13", + "events": "^1.1.1", + "google-breakpad": "git+https://github.com/status-im/google-breakpad.git#v0.9.0", + "hi-base32": "^0.5.0", + "i18n-js": "^3.1.0", + "identicon.js": "git+https://github.com/status-im/identicon.js.git#v1.2.1-status", + "metro": "^0.48.1", + "nfc-react-native": "git+https://github.com/status-im/nfc-react-native.git#v0.3.8-status", + "qrcode": "^1.4.1", + "react": "^16.6.1", + "react-dom": "^16.4.2", + "react-native": "git+https://github.com/status-im/react-native-desktop.git#v0.57.8_11", + "react-native-background-timer": "^2.0.0", + "react-native-camera": "^0.10.0", + "react-native-config": "git+https://github.com/status-im/react-native-config.git#v0.11.2-status", + "react-native-dialogs": "^0.0.20", + "react-native-fetch-polyfill": "^1.1.2", + "react-native-fs": "git+https://github.com/status-im/react-native-fs.git#v2.9.7-status", + "react-native-gesture-handler": "^1.3.0", + "react-native-image-crop-picker": "^0.18.1", + "react-native-image-resizer": "^1.0.0", + "react-native-keychain": "git+https://github.com/status-im/react-native-keychain.git#v.3.0.0-4-status", + "react-native-languages": "git+https://github.com/status-im/react-native-languages.git#v0.1.1-status", + "react-native-navigation-twopane": "git+https://github.com/status-im/react-native-navigation-twopane.git#v0.0.2-status", + "react-native-os": "^1.1.0", + "react-native-splash-screen": "^3.0.6", + "react-native-webview-bridge": "git+https://github.com/status-im/react-native-webview-bridge.git#0.33.16-status-rn049-desktop", + "react-navigation": "^3.11.0", + "status-conan": "git+https://github.com/status-im/status-conan.git#v1.0.0", + "web3-utils": "^1.0.0-beta.36" + }, + "devDependencies": { + "@babel/core": "7.0.1", + "@babel/generator": "7.0.0", + "@babel/helper-builder-react-jsx": "7.0.0", + "@babel/plugin-transform-block-scoping": "7.0.0", + "@babel/preset-env": "7.1.0", + "@babel/register": "7.0.0", + "babel-preset-react-native": "^5.0.2", + "metro-react-native-babel-preset": "^0.45.6", + "coveralls": "^3.0.4", + "nyc": "^14.1.1", + "patch-package": "^5.1.1", + "rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#v2.0.2-status" + }, + "optionalDependencies": { + "appdmg": "^0.5.2" + }, "desktopExternalModules": [ "node_modules/react-native-languages/desktop", "node_modules/react-native-config/desktop", @@ -41,61 +96,6 @@ "../../../../../resources/fonts/Inter-Thin-BETA.otf", "../../../../../resources/fonts/Inter-ThinItalic-BETA.otf" ], - "dependencies": { - "assert": "1.4.1", - "babel-preset-react-native": "5.0.2", - "bignumber.js": "git+https://github.com/status-im/bignumber.js.git#v4.0.2-status", - "buffer": "3.6.0", - "chance": "1.0.12", - "create-react-class": "15.6.2", - "emojilib": "2.2.9", - "eth-phishing-detect": "1.1.13", - "events": "1.1.1", - "google-breakpad": "git+https://github.com/status-im/google-breakpad.git#v0.9.0", - "hi-base32": "0.5.0", - "i18n-js": "^3.1.0", - "identicon.js": "git+https://github.com/status-im/identicon.js.git#v1.2.1-status", - "metro": "^0.48.1", - "metro-react-native-babel-preset": "0.45.6", - "nfc-react-native": "git+https://github.com/status-im/nfc-react-native.git#v0.3.8-status", - "qrcode": "^1.4.1", - "react": "16.6.1", - "react-dom": "16.4.2", - "react-native": "git+https://github.com/status-im/react-native-desktop.git#v0.57.8_11", - "react-native-background-timer": "2.0.0", - "react-native-camera": "0.10.0", - "react-native-config": "git+https://github.com/status-im/react-native-config.git#v0.11.2-status", - "react-native-dialogs": "0.0.20", - "react-native-fetch-polyfill": "1.1.2", - "react-native-fs": "git+https://github.com/status-im/react-native-fs.git#v2.9.7-status", - "react-native-gesture-handler": "^1.3.0", - "react-native-image-crop-picker": "0.18.1", - "react-native-image-resizer": "1.0.0", - "react-native-keychain": "git+https://github.com/status-im/react-native-keychain.git#v.3.0.0-4-status", - "react-native-languages": "git+https://github.com/status-im/react-native-languages.git#v0.1.1-status", - "react-native-navigation-twopane": "git+https://github.com/status-im/react-native-navigation-twopane.git#v0.0.2-status", - "react-native-os": "1.1.0", - "react-native-splash-screen": "3.0.6", - "react-native-webview-bridge": "git+https://github.com/status-im/react-native-webview-bridge.git#0.33.16-status-rn049-desktop", - "react-navigation": "^3.11.0", - "status-conan": "git+https://github.com/status-im/status-conan.git#v1.0.0", - "web3-utils": "1.0.0-beta.36" - }, - "devDependencies": { - "@babel/core": "7.0.1", - "@babel/generator": "7.0.0", - "@babel/helper-builder-react-jsx": "7.0.0", - "@babel/plugin-transform-block-scoping": "7.0.0", - "@babel/preset-env": "7.1.0", - "@babel/register": "7.0.0", - "coveralls": "^3.0.4", - "nyc": "^14.1.1", - "patch-package": "^5.1.1", - "rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#v2.0.2-status" - }, - "optionalDependencies": { - "appdmg": "^0.5.2" - }, "desktopImages": [ "../../../../../desktop/resources/add.png", "../../../../../desktop/resources/address.png", diff --git a/desktop/js_files/yarn.lock b/desktop/js_files/yarn.lock index 6d738d4bf759..ce8d286ef0a5 100644 --- a/desktop/js_files/yarn.lock +++ b/desktop/js_files/yarn.lock @@ -1624,7 +1624,7 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert@1.4.1: +assert@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= @@ -1730,7 +1730,7 @@ babel-preset-fbjs@^3.0.1, babel-preset-fbjs@^3.2.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-react-native@5.0.2: +babel-preset-react-native@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/babel-preset-react-native/-/babel-preset-react-native-5.0.2.tgz#dfed62379712a9c017ff99ce4fbeac1e11d42285" integrity sha512-Ua5JeQ1yGK8UoydMPzE2Ghq5raOKxXzpyApYDuHi4etIbXi5+GnCin19Nu+1obLQCf2Dxy9Y/GZwI0rnNOjggA== @@ -1994,7 +1994,7 @@ buffer-to-arraybuffer@^0.0.5: resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= -buffer@3.6.0: +buffer@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" integrity sha1-pyyTb3e5a/UvX357RnGAYoVR3vs= @@ -2114,7 +2114,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chance@1.0.12: +chance@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.12.tgz#6764c9cb7b4f34856fb780d07f0e17a6601c6c08" integrity sha512-W99uMuboG5CT1iToDmizEH6yQYqICzZnrSRbbXPuJErzFWLPaoiEDvwnKbESjDo/8st1n3pyh70VBMmfqPmf+Q== @@ -2464,7 +2464,7 @@ create-error-class@^3.0.0: dependencies: capture-stack-trace "^1.0.0" -create-react-class@15.6.2: +create-react-class@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a" integrity sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co= @@ -2767,7 +2767,7 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== -emojilib@2.2.9: +emojilib@^2.2.9: version "2.2.9" resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.2.9.tgz#ec5722689fc148f56422c14b0dc16a901d446b75" integrity sha512-RbYMz3G8OFBZYsqHQp8kiM/Hsg/0bSRWG1iRPqvQ04s4RUQH+iBLFjybSM6fv2rV/vQrywOEL5m31BjnfyDUlw== @@ -2899,7 +2899,7 @@ eth-lib@0.1.27: ws "^3.0.0" xhr-request-promise "^0.1.2" -eth-phishing-detect@1.1.13: +eth-phishing-detect@^1.1.13: version "1.1.13" resolved "https://registry.yarnpkg.com/eth-phishing-detect/-/eth-phishing-detect-1.1.13.tgz#ed718b933c8a69fef0cefa6604538824b472dbea" integrity sha512-1KQcKvAQIjJgFMVwxaw2+BlzM9Momzl0e+/torPdMjg7WGq6LmCIS7ddg84diH5zIQp9quGyRVIEawCCuErgVQ== @@ -2924,7 +2924,7 @@ eventemitter3@^3.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@1.1.1: +events@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= @@ -3625,6 +3625,11 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= + handlebars@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.2.0.tgz#57ce8d2175b9bbb3d8b3cf3e4217b1aec8ddcb2e" @@ -3736,7 +3741,7 @@ hasha@^3.0.0: dependencies: is-stream "^1.0.1" -hi-base32@0.5.0: +hi-base32@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.0.tgz#61329f76a31f31008533f1c36f2473e259d64571" integrity sha512-DDRmxSyoYuvjUb9EnXdoiMChBZ7ZcUVJsK5Frd3kqMhuBxvmZdnBeynAVfj7/ECbn++CekcoprvC/rprHPAtow== @@ -4920,7 +4925,7 @@ metro-minify-uglify@0.48.5: dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.45.6: +metro-react-native-babel-preset@^0.45.6: version "0.45.6" resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.45.6.tgz#077ce4039d6461a1b699b215612985415f9816a9" integrity sha512-qh+iXlV2tDfvHYbhh1meihxnzXXXB8nF1fi8z2HFxqYDkFBM48XewXO6mLz97PL8lmuTGvX/2dYVuFtriENw1w== @@ -6006,7 +6011,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6146,7 +6151,7 @@ react-devtools-core@^3.4.2: shell-quote "^1.6.1" ws "^3.3.1" -react-dom@16.4.2: +react-dom@^16.4.2: version "16.4.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4" integrity sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw== @@ -6166,12 +6171,12 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-native-background-timer@2.0.0: +react-native-background-timer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-native-background-timer/-/react-native-background-timer-2.0.0.tgz#afdac10b7441952d570acea4a8dbb065107b5461" integrity sha512-vLNJIedXQZN4p3ChFsAgVHacnJqQMnLl+wBsnZuliRkmsjEHo8kQOA9fnLih/OoiDi1O3eHQvXC5L8f+RYiKgw== -react-native-camera@0.10.0: +react-native-camera@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/react-native-camera/-/react-native-camera-0.10.0.tgz#5d973c9a8aab95f68aede395ba310eae0c3cecdd" integrity sha512-qum5zdJ3GQZ/mB+SYdT8EhQVnIfmyLzwyIK9Ss4vsTGLKxKmt9tf7oGigT2t52aOy6tNglKcgYuU0cZREKGDDA== @@ -6182,12 +6187,12 @@ react-native-camera@0.10.0: version "0.11.2" resolved "git+https://github.com/status-im/react-native-config.git#e705dac8ab459614906c060dcf699e5fe5c05d9d" -react-native-dialogs@0.0.20: +react-native-dialogs@^0.0.20: version "0.0.20" resolved "https://registry.yarnpkg.com/react-native-dialogs/-/react-native-dialogs-0.0.20.tgz#f3fb48eadba9d83fb9d4e6d7eca573494a8cefaa" integrity sha512-HeoU9d7wWUUAtxjcIJLaeIs/eYS2ZHTNh3kboeCKngLvfvgptd/7vZXwXdQ+xeOEek+cm95kDfMJQd2xnb+4wA== -react-native-fetch-polyfill@1.1.2: +react-native-fetch-polyfill@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/react-native-fetch-polyfill/-/react-native-fetch-polyfill-1.1.2.tgz#256b5a0abd78cc4992f7a7cf82543da2f2124a73" integrity sha1-JWtaCr14zEmS96fPglQ9ovISSnM= @@ -6200,20 +6205,21 @@ react-native-fetch-polyfill@1.1.2: utf8 "^2.1.1" react-native-gesture-handler@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.3.0.tgz#d0386f565928ccc1849537f03f2e37fd5f6ad43f" - integrity sha512-ASRFIXBuKRvqlmwkWJhV8yP2dTpvcqVrLNpd7FKVBFHYWr6SAxjGyO9Ik8w1lAxDhMlRP2IcJ9p9eq5X2WWeLQ== + version "1.4.1" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.4.1.tgz#c38d9e57637b95e221722a79f2bafac62e3aeb21" + integrity sha512-Ffcs+SbEbkGaal0CClYL+v7A9y4OA5G5lW01qrzjxaqASp5C8BfnWSKuqYKE00s6bLwE5L4xnlHkG0yIxAtbrQ== dependencies: + hammerjs "^2.0.8" hoist-non-react-statics "^2.3.1" - invariant "^2.2.2" - prop-types "^15.5.10" + invariant "^2.2.4" + prop-types "^15.7.2" -react-native-image-crop-picker@0.18.1: +react-native-image-crop-picker@^0.18.1: version "0.18.1" resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.18.1.tgz#ebdbe72def5afb6c69aa6e2ad72fd3a7de8ee408" integrity sha512-63Yr+Mzl/CzDze1eqMg/GGFOODgGrixSMO+28TVGCX3982f76w/op0JKoL7zuSKz/P/mVsbi9RnHA0z6Ih/RHQ== -react-native-image-resizer@1.0.0: +react-native-image-resizer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/react-native-image-resizer/-/react-native-image-resizer-1.0.0.tgz#d47e14943c37938e287fbd639e4db7ceb7fd8917" integrity sha1-1H4UlDw3k44of71jnk23zrf9iRc= @@ -6230,7 +6236,7 @@ react-native-image-resizer@1.0.0: version "0.0.2" resolved "git+https://github.com/status-im/react-native-navigation-twopane.git#04ed5fddfb46a6a3ee30776987acb4d3b11c27d4" -react-native-os@1.1.0: +react-native-os@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-native-os/-/react-native-os-1.1.0.tgz#bfbe1c44d8a5b14a6f3a3a405d8ada6f547a516e" integrity sha1-v74cRNilsUpvOjpAXYrab1R6UW4= @@ -6249,7 +6255,7 @@ react-native-safe-area-view@^0.14.1: dependencies: debounce "^1.2.0" -react-native-splash-screen@3.0.6: +react-native-splash-screen@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.0.6.tgz#c0bbf2c8ae40a313c4c7044f55e569414ff68332" integrity sha512-yaTnGAHRyhduLSfD85gP3Vsf0BRePHW3aNNtDXbkbUhwIIeafu2cJH86U/qKFuKLMYLnFOXteOkP80gaYVGAYg== @@ -6381,7 +6387,7 @@ react-transform-hmr@^1.0.4: global "^4.3.0" react-proxy "^1.1.7" -react@16.6.1: +react@^16.6.1: version "16.6.1" resolved "https://registry.yarnpkg.com/react/-/react-16.6.1.tgz#ee2aef4f0a09e494594882029821049772f915fe" integrity sha512-OtawJThYlvRgm9BXK+xTL7BIlDx8vv21j+fbQDjRRUyok6y7NyjlweGorielTahLZHYIdKUoK2Dp9ByVWuMqxw== @@ -7730,7 +7736,7 @@ watch@~0.18.0: exec-sh "^0.2.0" minimist "^1.2.0" -web3-utils@1.0.0-beta.36: +web3-utils@^1.0.0-beta.36: version "1.0.0-beta.36" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.36.tgz#dc19c9aeec009b1816cc91ef64d7fe9f8ee344c9" integrity sha512-7ri74lG5fS2Th0fhYvTtiEHMB1Pmf2p7dQx1COQ3OHNI/CHNEMjzoNMEbBU6FAENrywfoFur40K4m0AOmEUq5A== diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 14930a5c6087..86fee9eaf838 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -181,7 +181,7 @@ PODS: - React - RNFS (2.14.1): - React - - RNGestureHandler (1.3.0): + - RNGestureHandler (1.4.1): - React - RNImageCropPicker (0.25.0): - QBImagePickerController @@ -398,7 +398,7 @@ SPEC CHECKSUMS: React-RCTWebSocket: 2e7f6e98fd6d2bf447d145e499c3a235cc13e350 RNFirebase: ca408d6bee0ca9383bd0cfe84f40b01370b8ad4d RNFS: 6a2bfb5d0b14cf0ad72667f76202358abba4aacf - RNGestureHandler: 7ccf2f3f60458e084f9ada01fbaf610f6fef073c + RNGestureHandler: 311e3b1cef021a7c9ef31e97e7dc31970cc6288d RNImageCropPicker: c406db73f02f11bf3a887f856ff04ee50af173f4 RNKeychain: 627c6095cef215dd3d9804a9a9cf45ab96aa3997 RNLanguages: e3ae05ef105937645218272429dac0c3f7633451 diff --git a/ios/StatusIm/Images.xcassets/faceid.imageset/Contents.json b/ios/StatusIm/Images.xcassets/faceid.imageset/Contents.json new file mode 100644 index 000000000000..a982e9218663 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/faceid.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "faceid@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "faceid@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "faceid@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@1x.png b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@1x.png new file mode 100644 index 000000000000..9f817b0addcd Binary files /dev/null and b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@1x.png differ diff --git a/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@2x.png b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@2x.png new file mode 100644 index 000000000000..cf0cae9d2a18 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@3x.png b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@3x.png new file mode 100644 index 000000000000..6c4dd55ac695 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/faceid.imageset/faceid@3x.png differ diff --git a/ios/StatusIm/Images.xcassets/print.imageset/Contents.json b/ios/StatusIm/Images.xcassets/print.imageset/Contents.json new file mode 100644 index 000000000000..dc19ddb7088f --- /dev/null +++ b/ios/StatusIm/Images.xcassets/print.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "print@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "print@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "print@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/print.imageset/print@1x.png b/ios/StatusIm/Images.xcassets/print.imageset/print@1x.png new file mode 100644 index 000000000000..9556c28bba5b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/print.imageset/print@1x.png differ diff --git a/ios/StatusIm/Images.xcassets/print.imageset/print@2x.png b/ios/StatusIm/Images.xcassets/print.imageset/print@2x.png new file mode 100644 index 000000000000..6103fe4ee09b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/print.imageset/print@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/print.imageset/print@3x.png b/ios/StatusIm/Images.xcassets/print.imageset/print@3x.png new file mode 100644 index 000000000000..874c7babc3b8 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/print.imageset/print@3x.png differ diff --git a/mobile/js_files/package.json b/mobile/js_files/package.json index 50ca9594851d..50544906693a 100644 --- a/mobile/js_files/package.json +++ b/mobile/js_files/package.json @@ -10,39 +10,39 @@ "bignumber.js": "git+https://github.com/status-im/bignumber.js.git#v4.0.2-status", "buffer": "^5.4.2", "chance": "^1.1.0", - "create-react-class": "15.6.2", + "create-react-class": "^15.6.2", "emojilib": "^2.4.0", "eth-phishing-detect": "^1.1.13", "hermes-engine": "0.2.1", "hi-base32": "^0.5.0", "i18n-js": "^3.3.0", "qrcode": "^1.4.1", - "react": "16.8.6", - "react-dom": "16.4.2", - "react-native": "0.60.5", + "react": "^16.8.6", + "react-dom": "^16.4.2", + "react-native": "^0.60.5", "react-native-background-timer": "^2.1.1", - "react-native-camera": "3.3.3", + "react-native-camera": "^3.3.3", "react-native-config": "git+https://github.com/status-im/react-native-config.git#0.11.2-1-status", - "react-native-dialogs": "1.0.4", - "react-native-fetch-polyfill": "1.1.2", - "react-native-firebase": "5.5.6", - "react-native-fs": "2.14.1", - "react-native-gesture-handler": "1.3.0", - "react-native-image-crop-picker": "0.25.0", + "react-native-dialogs": "^1.0.4", + "react-native-fetch-polyfill": "^1.1.2", + "react-native-firebase": "^5.5.6", + "react-native-fs": "^2.14.1", + "react-native-gesture-handler": "^1.3.0", + "react-native-image-crop-picker": "^0.25.0", "react-native-image-resizer": "git+https://github.com/status-im/react-native-image-resizer.git#1.0.0-1-status", "react-native-keychain": "git+https://github.com/status-im/react-native-keychain.git#v.3.0.0-status", "react-native-languages": "^3.0.2", "react-native-mail": "git+https://github.com/status-im/react-native-mail.git#v4.0.0-status", "react-native-navigation-twopane": "git+https://github.com/status-im/react-native-navigation-twopane.git#v0.0.2-status", - "react-native-screens": "1.0.0-alpha.22", + "react-native-screens": "^1.0.0-alpha.22", "react-native-shake": "^3.3.1", "react-native-splash-screen": "^3.2.0", "react-native-status-keycard": "^2.5.12", - "react-native-svg": "9.8.4", + "react-native-svg": "^9.8.4", "react-native-touch-id": "^4.4.1", - "react-native-webview": "6.11.1", + "react-native-webview": "^6.11.1", "react-native-webview-bridge": "git+https://github.com/status-im/react-native-webview-bridge.git#fix/community-webview", - "react-navigation": "3.11.0", + "react-navigation": "^3.11.0", "web3-utils": "^1.2.1" }, "devDependencies": { diff --git a/mobile/js_files/yarn.lock b/mobile/js_files/yarn.lock index e800fea1eefc..9f089da53a9d 100644 --- a/mobile/js_files/yarn.lock +++ b/mobile/js_files/yarn.lock @@ -1871,16 +1871,7 @@ cp-file@^6.2.0: pify "^4.0.1" safe-buffer "^5.0.1" -create-react-class@15.6.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a" - integrity sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co= - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - object-assign "^4.1.1" - -create-react-class@^15.6.3: +create-react-class@^15.6.2, create-react-class@^15.6.3: version "15.6.3" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== @@ -2688,6 +2679,11 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= + handlebars@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.2.0.tgz#57ce8d2175b9bbb3d8b3cf3e4217b1aec8ddcb2e" @@ -4668,7 +4664,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -4771,7 +4767,7 @@ react-devtools-core@^3.6.1: shell-quote "^1.6.1" ws "^3.3.1" -react-dom@16.4.2: +react-dom@^16.4.2: version "16.4.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4" integrity sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw== @@ -4781,11 +4777,16 @@ react-dom@16.4.2: object-assign "^4.1.1" prop-types "^15.6.0" -react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: +react-is@^16.7.0, react-is@^16.8.4, react-is@^16.8.6: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-is@^16.8.1: + version "16.10.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.1.tgz#0612786bf19df406502d935494f0450b40b8294f" + integrity sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw== + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -4796,7 +4797,7 @@ react-native-background-timer@^2.1.1: resolved "https://registry.yarnpkg.com/react-native-background-timer/-/react-native-background-timer-2.1.1.tgz#9a2489681ab2f8033c213c73272e9d4c47572cd5" integrity sha512-cuXIIv+dcG8a8qkTD8pMzeqOEZCO+UGKglZWIe1osve+yJslmCowYQff+bI9xa7NOt2w+Vtd4L3d9JonlSqODg== -react-native-camera@3.3.3: +react-native-camera@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/react-native-camera/-/react-native-camera-3.3.3.tgz#57b56947f067b749e15baecfe8764d122bef1251" integrity sha512-eP4TdOqva/NxLC/LFiMSFs75nclic+7ZVO9lTASQWvyCz/WFkTZYr3ai/ftuQ7QmVC7svR4QRA0zdZ0zKWlExA== @@ -4807,17 +4808,17 @@ react-native-camera@3.3.3: version "0.11.2" resolved "git+https://github.com/status-im/react-native-config.git#f48d41012f812e1fc9dd6a78e874e3271e5e385e" -react-native-dialogs@1.0.4: +react-native-dialogs@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/react-native-dialogs/-/react-native-dialogs-1.0.4.tgz#c3bcee4a8a4ca2e97131bd5ffaed8db6ab1dec48" integrity sha512-Q3e4bAPp13jnPkStKYzyE/pb6xVY1spSlGHzq3zljV7uMXqFHjuTBRiM/qfm2bSZplR/znzVS/y2TtcW+1aufQ== -react-native-fetch-polyfill@1.1.2: +react-native-fetch-polyfill@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/react-native-fetch-polyfill/-/react-native-fetch-polyfill-1.1.2.tgz#256b5a0abd78cc4992f7a7cf82543da2f2124a73" integrity sha1-JWtaCr14zEmS96fPglQ9ovISSnM= -react-native-firebase@5.5.6: +react-native-firebase@^5.5.6: version "5.5.6" resolved "https://registry.yarnpkg.com/react-native-firebase/-/react-native-firebase-5.5.6.tgz#17b34ec0d5dc39afaaf0e159fd160f6339e0f707" integrity sha512-AdbpGwKEEiMFgaRar9WOPc8Li4sfxR//WOyNQzxYsQ1fpARC908j7feqy2gu73SLqk4wzIrnlfJWakOe0/Bclg== @@ -4825,7 +4826,7 @@ react-native-firebase@5.5.6: opencollective-postinstall "^2.0.0" prop-types "^15.7.2" -react-native-fs@2.14.1: +react-native-fs@^2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.14.1.tgz#61c70a865b5b5bcb020dd4e4befd60a2c20c836f" integrity sha512-ZcfiwNP+FBgvv2eRk0B62/NI58mbjszjjYvQlP352HLkUqVsK4Ld6X8fdBO1lZAz6SgitUk8WEc9NEciRIt31g== @@ -4833,16 +4834,17 @@ react-native-fs@2.14.1: base-64 "^0.1.0" utf8 "^2.1.1" -react-native-gesture-handler@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.3.0.tgz#d0386f565928ccc1849537f03f2e37fd5f6ad43f" - integrity sha512-ASRFIXBuKRvqlmwkWJhV8yP2dTpvcqVrLNpd7FKVBFHYWr6SAxjGyO9Ik8w1lAxDhMlRP2IcJ9p9eq5X2WWeLQ== +react-native-gesture-handler@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.4.1.tgz#c38d9e57637b95e221722a79f2bafac62e3aeb21" + integrity sha512-Ffcs+SbEbkGaal0CClYL+v7A9y4OA5G5lW01qrzjxaqASp5C8BfnWSKuqYKE00s6bLwE5L4xnlHkG0yIxAtbrQ== dependencies: + hammerjs "^2.0.8" hoist-non-react-statics "^2.3.1" - invariant "^2.2.2" - prop-types "^15.5.10" + invariant "^2.2.4" + prop-types "^15.7.2" -react-native-image-crop-picker@0.25.0: +react-native-image-crop-picker@^0.25.0: version "0.25.0" resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.25.0.tgz#858fa6fdc857255fe5250252a38d58db347f9067" integrity sha512-SpT8/zRUW+d/girR78Rc9FzqCOwuARRTJX91hENKJ8LPzVos1fthNOJO0LGGvVFYggbtLsLugrPRDRP2MiuGlQ== @@ -4875,7 +4877,7 @@ react-native-safe-area-view@^0.14.1: dependencies: hoist-non-react-statics "^2.3.1" -react-native-screens@1.0.0-alpha.22: +react-native-screens@^1.0.0-alpha.22: version "1.0.0-alpha.22" resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-1.0.0-alpha.22.tgz#7a120377b52aa9bbb94d0b8541a014026be9289b" integrity sha512-kSyAt0AeVU6N7ZonfV6dP6iZF8B7Bce+tk3eujXhzBGsLg0VSLnU7uE9VqJF0xdQrHR91ZjGgVMieo/8df9KTA== @@ -4904,7 +4906,7 @@ react-native-status-keycard@^2.5.12: resolved "https://registry.yarnpkg.com/react-native-status-keycard/-/react-native-status-keycard-2.5.12.tgz#96cf98f955d3a354d0c4e9324e25fd359b99206c" integrity sha512-QCGiL5Sir8Gm53dG+PXB7fFGWLozej3GPt12dj9aU5EdNUPL7qG5gYCKFRlKMV/nPlpvKBikY4h1dDxwoCEcBg== -react-native-svg@9.8.4: +react-native-svg@^9.8.4: version "9.8.4" resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-9.8.4.tgz#6fd28198ae991b30cfbea10bb445b55870f0457c" integrity sha512-s0kCURCQyupf9qL3ZPccZ43Hxkvmccf+tZ1/Tq2bAiORLNhVCI0gtI57zqqHsKk5PnTKmnqT8aTWfp5TzYgZWw== @@ -4929,7 +4931,7 @@ react-native-touch-id@^4.4.1: keymirror "0.1.1" react-native-webview "^6.11.1" -react-native-webview@6.11.1, react-native-webview@^6.11.1: +react-native-webview@^6.11.1: version "6.11.1" resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-6.11.1.tgz#51fab0838ef9bdf3efd0d3f27147dddda5119f94" integrity sha512-0OaNCEzdyywJZ70y6Z4fmmuAd2AHYq2AEByIr15z3YetpMXhm9lXUe2V/8BXQIZXeC9FJosj2DAKu48lpZ0nEg== @@ -4937,7 +4939,7 @@ react-native-webview@6.11.1, react-native-webview@^6.11.1: escape-string-regexp "1.0.5" invariant "2.2.4" -react-native@0.60.5: +react-native@^0.60.5: version "0.60.5" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.60.5.tgz#3c1d9c06a0fbab9807220b6acac09488d39186ee" integrity sha512-cZwI0XzzihACN+7an1Dy46A83FRaAe2Xyd7laCalFFAppZIYeMVphZQWrVljJk5kIZBNtYG35TY1VsghQ0Oc2Q== @@ -4993,7 +4995,7 @@ react-navigation-tabs@~1.1.4: react-lifecycles-compat "^3.0.4" react-native-tab-view "^1.4.1" -react-navigation@3.11.0: +react-navigation@^3.11.0: version "3.11.0" resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-3.11.0.tgz#2c82217c452d07d8b9b0929bc7e77e2bcfaf9388" integrity sha512-wlPcDtNiIdPeYxNQ/MN4arY5Xe9EphD2QVpRuvvuPWW+BamF3AJaIy060r3Yz59DODAoWllscabat/yqnih8Tg== @@ -5020,7 +5022,7 @@ react-transform-hmr@^1.0.4: global "^4.3.0" react-proxy "^1.1.7" -react@16.8.6: +react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== diff --git a/src/status_im/biometric_auth/core.cljs b/src/status_im/biometric_auth/core.cljs deleted file mode 100644 index 82a443d1762a..000000000000 --- a/src/status_im/biometric_auth/core.cljs +++ /dev/null @@ -1,93 +0,0 @@ -(ns status-im.biometric-auth.core - (:require [re-frame.core :as re-frame] - [status-im.utils.platform :as platform] - [status-im.i18n :as i18n] - [status-im.utils.fx :as fx] - [status-im.ui.components.colors :as colors] - [status-im.native-module.core :as status] - [status-im.react-native.js-dependencies :as rn])) - -;; currently, for android, react-native-touch-id -;; is not returning supported biometric type -;; defaulting to :fingerprint -(def android-default-support :fingerprint) - -;;; android blacklist based on device info: - -(def deviceinfo (status/get-device-model-info)) - -;; {:model ? -;; :brand "Xiaomi" -;; :build-id "13D15" -;; :device-id "goldfish" -;; more info on https://github.com/react-native-community/react-native-device-info - -(def android-device-blacklisted? - (cond - (= (:brand deviceinfo) "bannedbrand") true - :else false)) - -;; biometric auth config -;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config -(defn- authenticate-options [ios-fallback-label] - (clj->js (merge - {:unifiedErrors true} - (when platform/ios? - {:passcodeFallback false - :fallbackLabel (or ios-fallback-label "")}) - (when platform/android? - {:title (i18n/label :t/biometric-auth-android-title) - :imageColor colors/blue - :imageErrorColor colors/red - :sensorDescription (i18n/label :t/biometric-auth-android-sensor-desc) - :sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc) - :cancelText (i18n/label :cancel)})))) - -(defn- get-error-message - "must return an error message for the user" - [touchid-error-code] - (cond - ;; no message if user canceled or falled back to password - (= touchid-error-code "USER_CANCELED") nil - (= touchid-error-code "USER_FALLBACK") nil - ;; add here more specific errors if needed - ;; https://github.com/naoufal/react-native-touch-id#unified-errors - :else (i18n/label :t/biometric-auth-error {:code touchid-error-code}))) - -(def success-result - {:bioauth-success true}) - -(defn- generate-error-result [touchid-error-obj] - (let [code (aget touchid-error-obj "code")] - {:bioauth-success false - :bioauth-code code - :bioauth-message (get-error-message code)})) - -(defn- do-get-supported [callback] - (-> (.isSupported rn/touchid) - (.then #(callback (or (keyword %) android-default-support))) - (.catch #(callback nil)))) - -(defn get-supported [callback] - (cond platform/ios? (do-get-supported callback) - platform/android? (if android-device-blacklisted? - (callback nil) - (do-get-supported callback)) - :else (callback nil))) - -(defn authenticate - ([cb] - (authenticate cb nil)) - ([cb {:keys [reason ios-fallback-label]}] - (-> (.authenticate rn/touchid reason (authenticate-options ios-fallback-label)) - (.then #(cb success-result)) - (.catch #(cb (generate-error-result %)))))) - -(fx/defn authenticate-fx - [_ cb options] - {:biometric-auth/authenticate [cb options]}) - -(re-frame/reg-fx - :biometric-auth/authenticate - (fn [[cb options]] - (authenticate #(cb %) options))) \ No newline at end of file diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 57366f34b71f..c7c90b580e12 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -237,12 +237,19 @@ (def ^:const status-create-address "status_createaddress") -(def ^:const path-root "m/44'/60'/0'/0") -(def ^:const path-default-wallet "m/44'/60'/0'/0/0") -(def ^:const path-whisper "m/43'/60'/1581'/0'/0") +; BIP44 Wallet Root Key, the extended key from which any wallet can be derived +(def ^:const path-wallet-root "m/44'/60'/0'/0") +; EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived +(def ^:const path-eip1581 "m/43'/60'/1581'") +; BIP44-0 Wallet key, the default wallet key +(def ^:const path-default-wallet (str path-wallet-root "/0")) +; EIP1581 Chat Key 0, the default whisper key +(def ^:const path-whisper (str path-eip1581 "/0'/0")) (def ^:const path-default-wallet-keyword (keyword path-default-wallet)) (def ^:const path-whisper-keyword (keyword path-whisper)) +(def ^:const path-wallet-root-keyword (keyword path-wallet-root)) +(def ^:const path-eip1581-keyword (keyword path-eip1581)) ;; (ethereum/sha3 "Transfer(address,address,uint256)") (def ^:const event-transfer-hash "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 1d5e3928fc81..2de2165ecebd 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -7,7 +7,7 @@ [status-im.multiaccounts.logout.core :as multiaccounts.logout] [status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.biometric-auth.core :as biomentric-auth] + status-im.multiaccounts.biometric.core [status-im.bootnodes.core :as bootnodes] [status-im.browser.core :as browser] [status-im.browser.permissions :as browser.permissions] @@ -120,20 +120,6 @@ :on-accept open-chaos-unicorn-day-link}}) (multiaccounts/switch-chaos-mode chaos-mode?))))) -(handlers/register-handler-fx - :multiaccounts.ui/biometric-auth-switched - (fn [cofx [_ biometric-auth?]] - (if biometric-auth? - (biomentric-auth/authenticate-fx - cofx - (fn [{:keys [bioauth-success bioauth-message]}] - (when bioauth-success - (re-frame/dispatch [:multiaccounts.ui/switch-biometric-auth true])) - (when bioauth-message - (utils/show-popup (i18n/label :t/biometric-auth-reason-verify) bioauth-message))) - {:reason (i18n/label :t/biometric-auth-reason-verify)}) - (multiaccounts/switch-biometric-auth cofx false)))) - (handlers/register-handler-fx :multiaccounts.ui/notifications-enabled (fn [cofx [_ desktop-notifications?]] @@ -175,14 +161,6 @@ (fn [cofx [_ address photo-path name public-key]] (multiaccounts.login/open-login cofx address photo-path name public-key))) -(handlers/register-handler-fx - :multiaccounts.login.callback/get-user-password-success - (fn [{:keys [db] :as cofx} [_ password address]] - (let [biometric-auth? (get-in db [:multiaccounts/multiaccounts address :settings :biometric-auth?])] - (if (and password biometric-auth?) - (multiaccounts.login/do-biometric-auth cofx password) - (multiaccounts.login/open-login-callback cofx password {:bioauth-notrequired true}))))) - ;; multiaccounts logout module (handlers/register-handler-fx diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index d489f8aff899..4d5ae7476c8a 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -1,6 +1,5 @@ (ns status-im.init.core (:require [re-frame.core :as re-frame] - [status-im.biometric-auth.core :as biometric-auth] [status-im.multiaccounts.login.core :as multiaccounts.login] [status-im.native-module.core :as status] [status-im.network.net-info :as network] @@ -33,11 +32,6 @@ :view-id view-id :push-notifications/stored stored)}) -(fx/defn set-supported-biometric-auth - {:events [:init.callback/get-supported-biometric-auth-success]} - [{:keys [db]} supported-biometric-auth] - {:db (assoc db :supported-biometric-auth supported-biometric-auth)}) - (fx/defn initialize-views [cofx] (let [{{:multiaccounts/keys [multiaccounts] :as db} :db} cofx] @@ -75,7 +69,7 @@ (fx/defn start-app [cofx] (fx/merge cofx - {::get-supported-biometric-auth nil + {:get-supported-biometric-auth nil ::init-keystore nil ::restore-native-settings nil ::open-multiaccounts #(re-frame/dispatch [::initialize-multiaccounts %]) @@ -100,9 +94,4 @@ (re-frame/reg-fx ::init-keystore (fn [] - (status/init-keystore))) - -(re-frame/reg-fx - ::get-supported-biometric-auth - (fn [] - (biometric-auth/get-supported #(re-frame/dispatch [:init.callback/get-supported-biometric-auth-success %])))) + (status/init-keystore))) \ No newline at end of file diff --git a/src/status_im/multiaccounts/biometric/core.cljs b/src/status_im/multiaccounts/biometric/core.cljs new file mode 100644 index 000000000000..bb0834330672 --- /dev/null +++ b/src/status_im/multiaccounts/biometric/core.cljs @@ -0,0 +1,160 @@ +(ns status-im.multiaccounts.biometric.core + (:require + [status-im.utils.fx :as fx] + [status-im.utils.types :as types] + [clojure.string :as string] + [status-im.popover.core :as popover] + [status-im.native-module.core :as status] + [status-im.utils.platform :as platform] + [status-im.ui.components.colors :as colors] + [status-im.i18n :as i18n] + [status-im.react-native.js-dependencies :as js-dependencies] + [re-frame.core :as re-frame] + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.utils.keychain.core :as keychain])) + +;; currently, for android, react-native-touch-id +;; is not returning supported biometric type +;; defaulting to :fingerprint +(def android-default-support :fingerprint) + +;;; android blacklist based on device info: + +(def deviceinfo (status/get-device-model-info)) + +;; {:model ? +;; :brand "Xiaomi" +;; :build-id "13D15" +;; :device-id "goldfish" +;; more info on https://github.com/react-native-community/react-native-device-info + +(def android-device-blacklisted? + (cond + (= (:brand deviceinfo) "bannedbrand") true + :else false)) + +;; biometric auth config +;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config +(defn- authenticate-options [ios-fallback-label] + (clj->js (merge + {:unifiedErrors true} + (when platform/ios? + {:passcodeFallback false + :fallbackLabel (or ios-fallback-label "")}) + (when platform/android? + {:title (i18n/label :t/biometric-auth-android-title) + :imageColor colors/blue + :imageErrorColor colors/red + :sensorDescription (i18n/label :t/biometric-auth-android-sensor-desc) + :sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc) + :cancelText (i18n/label :cancel)})))) + +(defn get-label [supported-biometric-auth] + (case supported-biometric-auth + :fingerprint "Fingerprint" + :FaceID "Face ID" + "Touch ID")) + +(defn- get-error-message + "must return an error message for the user" + [touchid-error-code] + (cond + ;; no message if user canceled or falled back to password + (= touchid-error-code "USER_CANCELED") nil + (= touchid-error-code "USER_FALLBACK") nil + ;; add here more specific errors if needed + ;; https://github.com/naoufal/react-native-touch-id#unified-errors + :else (i18n/label :t/biometric-auth-error {:code touchid-error-code}))) + +(def success-result + {:bioauth-success true}) + +(defn- generate-error-result [touchid-error-obj] + (let [code (aget touchid-error-obj "code")] + {:bioauth-success false + :bioauth-code code + :bioauth-message (get-error-message code)})) + +(defn- do-get-supported [callback] + (-> (.isSupported js-dependencies/touchid) + (.then #(callback (or (keyword %) android-default-support))) + (.catch #(callback nil)))) + +(defn get-supported [callback] + (cond platform/ios? (do-get-supported callback) + platform/android? (if android-device-blacklisted? + (callback nil) + (do-get-supported callback)) + :else (callback nil))) + +(defn authenticate-fx + ([cb] + (authenticate-fx cb nil)) + ([cb {:keys [reason ios-fallback-label]}] + (-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label)) + (.then #(cb success-result)) + (.catch #(cb (generate-error-result %)))))) + +(re-frame/reg-fx + :get-supported-biometric-auth + (fn [] + (get-supported #(re-frame/dispatch [:init.callback/get-supported-biometric-auth-success %])))) + +(fx/defn set-supported-biometric-auth + {:events [:init.callback/get-supported-biometric-auth-success]} + [{:keys [db]} supported-biometric-auth] + {:db (assoc db :supported-biometric-auth supported-biometric-auth)}) + +(fx/defn authenticate + [_ cb options] + {:biometric-auth/authenticate [cb options]}) + +(re-frame/reg-fx + :biometric-auth/authenticate + (fn [[cb options]] + (authenticate-fx #(cb %) options))) + +(fx/defn update-biometric [{db :db :as cofx} biometric-auth?] + (let [address (get-in db [:multiaccount :address])] + (fx/merge cofx + (keychain/save-auth-method address (if biometric-auth? "biometric" "none")) + #(when-not biometric-auth? + {:keychain/clear-user-password address})))) + +(fx/defn biometric-auth-switched + {:events [:multiaccounts.ui/biometric-auth-switched]} + [cofx biometric-auth?] + (if biometric-auth? + (authenticate + cofx + #(re-frame/dispatch [:biometric-init-done %]) + {}) + (update-biometric cofx false))) + +(fx/defn show-message + [cofx bioauth-message bioauth-code] + (let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code) + (i18n/label :t/grant-face-id-permissions)) + bioauth-message)] + (when content + {:utils/show-popup + {:title (i18n/label :t/biometric-auth-login-error-title) + :content content}}))) + +(fx/defn biometric-init-done + {:events [:biometric-init-done]} + [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] + (if bioauth-success + (if (= "password" (get-in cofx [:db :auth-method])) + (update-biometric cofx true) + (popover/show-popover cofx {:view :enable-biometric})) + (show-message cofx bioauth-message bioauth-code))) + +(fx/defn biometric-auth + {:events [:biometric-authenticate]} + [cofx] + (authenticate + cofx + #(re-frame/dispatch [:biometric-auth-done %]) + {:reason (i18n/label :t/biometric-auth-reason-login) + :ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)})) \ No newline at end of file diff --git a/src/status_im/multiaccounts/core.cljs b/src/status_im/multiaccounts/core.cljs index 317f3e8f0982..cebd0c3505f5 100644 --- a/src/status_im/multiaccounts/core.cljs +++ b/src/status_im/multiaccounts/core.cljs @@ -66,15 +66,6 @@ (multiaccounts.update/multiaccount-update {:chaos-mode? chaos-mode?} {})))) -(fx/defn switch-biometric-auth - {:events [:multiaccounts.ui/switch-biometric-auth]} - [{:keys [db] :as cofx} biometric-auth?] - (when (:multiaccount db) - (let [settings (get-in db [:multiaccount :settings])] - (multiaccounts.update/update-settings cofx - (assoc settings :biometric-auth? biometric-auth?) - {})))) - (fx/defn enable-notifications [cofx desktop-notifications?] (multiaccounts.update/multiaccount-update cofx {:desktop-notifications? desktop-notifications?} diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index 104774ca1555..afa8c63e9541 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -52,7 +52,7 @@ (let [{:keys [selected-id address key-code]} (:intro-wizard db) {:keys [address]} (get-selected-multiaccount cofx) hashed-password (ethereum/sha3 (security/safe-unmask-data key-code)) - callback #(re-frame/dispatch [::store-multiaccount-success key-code])] + callback #(re-frame/dispatch [::store-multiaccount-success key-code %])] {::store-multiaccount [selected-id address hashed-password callback]})) (fx/defn intro-wizard @@ -184,11 +184,16 @@ name (gfycat/generate-gfy publicKey) photo-path (identicon/identicon publicKey) multiaccount-data {:name name :address address :photo-path photo-path} - new-multiaccount (cond-> {:address address + new-multiaccount (cond-> {; address of the master key + :address address + ;; The address from which we derive any wallet + :wallet-root-address (get-in multiaccount [:derived constants/path-wallet-root-keyword :address]) + ;; The address from which we derive any chat account/encryption keys + :eip1581-address (get-in multiaccount [:derived constants/path-eip1581-keyword :address]) :name name :photo-path photo-path + ; public key of the chat account :public-key publicKey - :latest-derived-path 0 :accounts [wallet-account] :signing-phrase signing-phrase @@ -296,26 +301,26 @@ {:events [::store-multiaccount-success] :interceptors [(re-frame/inject-cofx :random-guid-generator) (re-frame/inject-cofx ::get-signing-phrase)]} - [cofx password] - (on-multiaccount-created cofx (get-selected-multiaccount cofx) password {:seed-backed-up? false})) + [cofx password derived] + (on-multiaccount-created cofx + (assoc + (get-selected-multiaccount cofx) + :derived + (types/json->clj derived)) + password + {:seed-backed-up? false})) (re-frame/reg-fx ::store-multiaccount (fn [[id address hashed-password callback]] - (status/multiaccount-store-account + (status/multiaccount-store-derived id + [constants/path-wallet-root + constants/path-eip1581 + constants/path-whisper + constants/path-default-wallet] hashed-password - (fn [] - (status/multiaccount-load-account - address - hashed-password - (fn [value] - (let [{:keys [id]} (types/json->clj value)] - (status/multiaccount-store-derived - id - [constants/path-whisper constants/path-default-wallet] - hashed-password - callback)))))))) + callback))) (re-frame/reg-fx ::save-account-and-login diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 52d75ff04de7..5f7eeb2bb9d7 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -1,6 +1,5 @@ (ns status-im.multiaccounts.login.core (:require [re-frame.core :as re-frame] - [status-im.biometric-auth.core :as biometric-auth] [status-im.chaos-mode.core :as chaos-mode] [status-im.chat.models :as chat-model] [status-im.chat.models.loading :as chat.loading] @@ -27,7 +26,9 @@ [status-im.utils.universal-links.core :as universal-links] [status-im.utils.utils :as utils] [status-im.wallet.core :as wallet] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.ui.screens.db :refer [app-db]] + [status-im.multiaccounts.biometric.core :as biometric])) (def rpc-endpoint "https://goerli.infura.io/v3/f315575765b14720b32382a61a89341a") (def contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf") @@ -49,7 +50,11 @@ (resolve default-nodes))))) (resolve default-nodes)))) -;;;; Handlers +(re-frame/reg-fx + ::login + (fn [[account-data hashed-password]] + (status/login account-data hashed-password))) + (fx/defn initialize-wallet [cofx] (fx/merge cofx (wallet/initialize-tokens) @@ -98,10 +103,6 @@ [{:keys [db]} node-version] {:db (assoc db :web3-node-version node-version)}) -(fx/defn save-user-password - [cofx address password] - {:keychain/save-user-password [address password]}) - (fx/defn handle-close-app-confirmed {:events [::close-app-confirmed]} [_] @@ -156,25 +157,32 @@ (fx/defn login-only-events [{:keys [db] :as cofx} address password save-password?] - (let [stored-pns (:push-notifications/stored db)] + (let [stored-pns (:push-notifications/stored db) + auth-method (:auth-method db) + new-auth-method (if save-password? + (when-not (or (= "biometric" auth-method) (= "password" auth-method)) + (if (= auth-method "biometric-prepare") "biometric" "password")) + (when (and auth-method (not= auth-method "none")) "none"))] (fx/merge cofx {:db (assoc db :chats/loading? true) ::json-rpc/call - [{:method "mailservers_getMailserverTopics" + [{:method "mailservers_getMailserverTopics" :on-success #(re-frame/dispatch [::protocol/initialize-protocol {:mailserver-topics (or % {})}])} - {:method "mailservers_getChatRequestRanges" + {:method "mailservers_getChatRequestRanges" :on-success #(re-frame/dispatch [::protocol/initialize-protocol {:mailserver-ranges (or % {})}])} - {:method "browsers_getBrowsers" + {:method "browsers_getBrowsers" :on-success #(re-frame/dispatch [::initialize-browsers %])} - {:method "permissions_getDappPermissions" + {:method "permissions_getDappPermissions" :on-success #(re-frame/dispatch [::initialize-dapp-permissions %])} - {:method "mailservers_getMailservers" + {:method "mailservers_getMailservers" :on-success #(re-frame/dispatch [::protocol/initialize-protocol {:mailservers (or % [])}])} - {:method "settings_getConfigs" - :params [["multiaccount" "current-network" "networks"]] + {:method "settings_getConfigs" + :params [["multiaccount" "current-network" "networks"]] :on-success #(re-frame/dispatch [::get-config-callback % stored-pns])}]} (when save-password? - (save-user-password address password)) + (keychain/save-user-password address password)) + (when new-auth-method + (keychain/save-auth-method address new-auth-method)) (navigation/navigate-to-cofx :home nil) (when platform/desktop? (chat-model/update-dock-badge-label))))) @@ -263,12 +271,6 @@ (navigation/navigate-to-cofx :multiaccounts nil) (navigation/navigate-to-cofx :keycard-login-pin nil))))) -(fx/defn get-user-password - [_ address] - {:keychain/can-save-user-password? nil - :keychain/get-user-password [address - #(re-frame/dispatch [:multiaccounts.login.callback/get-user-password-success % address])]}) - (fx/defn open-login [{:keys [db] :as cofx} address photo-path name public-key] (let [keycard-multiaccount? (get-in db [:multiaccounts/multiaccounts address :keycard-key-uid])] @@ -284,31 +286,41 @@ :password))} (if keycard-multiaccount? (open-keycard-login) - (get-user-password address))))) + (keychain/get-auth-method address))))) (fx/defn open-login-callback - {:events [::biometric-auth-done]} - [{:keys [db] :as cofx} password {:keys [bioauth-success bioauth-notrequired bioauth-message]}] - (if (and password - (or bioauth-success bioauth-notrequired)) + {:events [:multiaccounts.login.callback/get-user-password-success]} + [{:keys [db] :as cofx} password] + (if password (fx/merge cofx - {:db (assoc-in db [:multiaccounts/login :password] password)} + {:db (update-in db [:multiaccounts/login] assoc :password password :save-password? true)} (navigation/navigate-to-cofx :progress nil) login) + (navigation/navigate-to-cofx cofx :login nil))) + +(fx/defn get-auth-method-success + "Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\"" + {:events [:multiaccounts.login/get-auth-method-success]} + [{:keys [db] :as cofx} auth-method] + (let [address (get-in db [:multiaccounts/login :address])] (fx/merge cofx - (when bioauth-message - {:utils/show-popup {:title (i18n/label :t/biometric-auth-login-error-title) :content bioauth-message}}) - (navigation/navigate-to-cofx :login nil)))) + {:db (assoc db :auth-method auth-method)} + #(case auth-method + "biometric" + (biometric/biometric-auth %) + "password" + (keychain/get-user-password % address) -(fx/defn do-biometric-auth - [{:keys [db] :as cofx} password] - (biometric-auth/authenticate-fx cofx - #(re-frame/dispatch [::biometric-auth-done password %]) - {:reason (i18n/label :t/biometric-auth-reason-login) - :ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)})) + ;;nil or "none" or "biometric-prepare" + (open-login-callback % nil))))) -(re-frame/reg-fx - ::login - (fn [[account-data hashed-password]] - (status/login account-data - hashed-password))) +(fx/defn biometric-auth-done + {:events [:biometric-auth-done]} + [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] + (let [address (get-in db [:multiaccounts/login :address])] + (if bioauth-success + (keychain/get-user-password cofx address) + (fx/merge cofx + {:db (assoc-in db [:multiaccounts/login :save-password?] true)} + (biometric/show-message bioauth-message bioauth-code) + (open-login-callback nil))))) \ No newline at end of file diff --git a/src/status_im/multiaccounts/logout/core.cljs b/src/status_im/multiaccounts/logout/core.cljs index ee25e7ca81b7..3406e91fc8fc 100644 --- a/src/status_im/multiaccounts/logout/core.cljs +++ b/src/status_im/multiaccounts/logout/core.cljs @@ -6,19 +6,23 @@ [status-im.native-module.core :as status] [status-im.transport.core :as transport] [status-im.utils.fx :as fx] - [clojure.string :as string])) + [status-im.utils.keychain.core :as keychain])) + +(fx/defn logout-method [{:keys [db] :as cofx} auth-method] + (let [address (get-in db [:multiaccount :address])] + (fx/merge cofx + {::logout nil + :keychain/clear-user-password address + ::init/open-multiaccounts #(re-frame/dispatch [::init/initialize-multiaccounts %])} + (keychain/save-auth-method address auth-method) + (transport/stop-whisper) + (chaos-mode/stop-checking) + (init/initialize-app-db)))) (fx/defn logout {:events [:logout]} - [{:keys [db] :as cofx}] - (fx/merge cofx - {::logout nil - ;;TODO sort out this mess with lower case addresses - :keychain/clear-user-password (string/lower-case (get-in db [:multiaccount :address])) - ::init/open-multiaccounts #(re-frame/dispatch [::init/initialize-multiaccounts %])} - (transport/stop-whisper) - (chaos-mode/stop-checking) - (init/initialize-app-db))) + [cofx] + (logout-method cofx "none")) (fx/defn show-logout-confirmation [_] {:ui/show-confirmation @@ -28,6 +32,14 @@ :on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed]) :on-cancel nil}}) +(fx/defn biometric-logout + {:events [:biometric-logout]} + [{:keys [db] :as cofx}] + (fx/merge cofx + (logout-method "biometric-prepare") + (fn [{:keys [db]}] + {:db (assoc-in db [:multiaccounts/login :save-password?] true)}))) + (re-frame/reg-fx ::logout (fn [] diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index 04520f8c998d..7ce0a24cbad3 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -127,7 +127,10 @@ (let [{:keys [id] :as root-data} (types/json->clj result)] (status-im.native-module.core/multiaccount-derive-addresses id - [constants/path-default-wallet constants/path-whisper] + [constants/path-wallet-root + constants/path-eip1581 + constants/path-whisper + constants/path-default-wallet] (fn [result] (let [derived-data (types/json->clj result)] (re-frame/dispatch [::import-multiaccount-success diff --git a/src/status_im/popover/core.cljs b/src/status_im/popover/core.cljs index 9d8a79ce9284..424f390f3492 100644 --- a/src/status_im/popover/core.cljs +++ b/src/status_im/popover/core.cljs @@ -1,6 +1,5 @@ (ns status-im.popover.core - (:require [status-im.utils.fx :as fx] - [status-im.utils.handlers :as handlers])) + (:require [status-im.utils.fx :as fx])) (fx/defn show-popover {:events [:show-popover]} diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index cde20315e368..4af8ea51b523 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -183,6 +183,8 @@ (reg-root-key-sub :keycard :hardwallet) +(reg-root-key-sub :auth-method :auth-method) + ;;GENERAL ============================================================================================================== (re-frame/reg-sub diff --git a/src/status_im/ui/components/button.cljs b/src/status_im/ui/components/button.cljs index 555f305d53ea..f77bae494d12 100644 --- a/src/status_im/ui/components/button.cljs +++ b/src/status_im/ui/components/button.cljs @@ -45,9 +45,9 @@ Spec: https://www.figma.com/file/cb4p8AxLtTF3q1L6JYDnKN15/Index?node-id=858%3A0" - [{:keys [label type disabled? on-press accessibility-label] :or {type :main}}] + [{:keys [label type disabled? on-press accessibility-label style] :or {type :main}}] (let [label (utils.label/stringify label)] - [react/touchable-opacity (merge {:on-press on-press :disabled disabled? :active-pacity 0.5} + [react/touchable-opacity (merge {:on-press on-press :disabled disabled? :active-pacity 0.5 :style style} (when accessibility-label {:accessibility-label accessibility-label})) [react/view {:style (style-container type disabled?)} diff --git a/src/status_im/ui/screens/advanced_settings/views.cljs b/src/status_im/ui/screens/advanced_settings/views.cljs index ae1001289893..b94f03c0e8cc 100644 --- a/src/status_im/ui/screens/advanced_settings/views.cljs +++ b/src/status_im/ui/screens/advanced_settings/views.cljs @@ -57,7 +57,7 @@ :disabled false}]]} {:type :divider}]) -(defn- dev-mode-settings-data [settings chaos-mode? supported-biometric-auth] +(defn- dev-mode-settings-data [settings chaos-mode?] [{:container-margin-top 8 :type :section-header :title :t/dev-mode-settings} @@ -117,47 +117,25 @@ #(re-frame/dispatch [:multiaccounts.ui/chaos-mode-switched (not chaos-mode?)]) :disabled false}]]} - {:type :small - :title :t/biometric-auth-setting-label - :container-margin-bottom 8 - :accessibility-label :biometric-auth-settings-switch - :disabled? (not (some? supported-biometric-auth)) - :accessories - [[react/switch - {:track-color #js {:true colors/blue :false nil} - :value (boolean (:biometric-auth? settings)) - :on-value-change - #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %]) - :disabled (not (some? supported-biometric-auth))}]] - :on-press - #(re-frame/dispatch - [:multiaccounts.ui/biometric-auth-switched - ((complement boolean) (:biometric-auth? settings))])} [react/view {:height 24}]]) (defn- flat-list-data [network-name current-log-level current-fleet - dev-mode? settings chaos-mode? supported-biometric-auth] + dev-mode? settings chaos-mode?] (if dev-mode? (into (normal-mode-settings-data network-name current-log-level current-fleet dev-mode?) (dev-mode-settings-data - settings chaos-mode? supported-biometric-auth)) + settings chaos-mode?)) ;; else (normal-mode-settings-data network-name current-log-level current-fleet dev-mode?))) (views/defview advanced-settings [] - (views/letsubs [{:keys - [chaos-mode? - dev-mode? - settings] - :as current-multiaccount} [:multiaccount] - settings [:multiaccount-settings] - network-name [:network-name] - current-log-level [:settings/current-log-level] - current-fleet [:settings/current-fleet] - supported-biometric-auth [:supported-biometric-auth]] + (views/letsubs [{:keys [chaos-mode? dev-mode? settings]} [:multiaccount] + network-name [:network-name] + current-log-level [:settings/current-log-level] + current-fleet [:settings/current-fleet]] [react/view {:flex 1 :background-color colors/white} [status-bar/status-bar] [toolbar/simple-toolbar @@ -166,7 +144,7 @@ {:data (flat-list-data network-name current-log-level current-fleet dev-mode? settings - chaos-mode? supported-biometric-auth) + chaos-mode?) :key-fn (fn [_ i] (str i)) :render-fn list/flat-list-generic-render-fn}]])) diff --git a/src/status_im/ui/screens/biometric/views.cljs b/src/status_im/ui/screens/biometric/views.cljs new file mode 100644 index 000000000000..d820b218aa32 --- /dev/null +++ b/src/status_im/ui/screens/biometric/views.cljs @@ -0,0 +1,24 @@ +(ns status-im.ui.screens.biometric.views + (:require-macros [status-im.utils.views :as views]) + (:require [status-im.ui.components.react :as react] + [status-im.ui.components.button :as button] + [re-frame.core :as re-frame] + [status-im.multiaccounts.biometric.core :as biometric] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.icons.vector-icons :as icons] + [status-im.i18n :as i18n])) + +(views/defview enable-biometric-popover [] + (views/letsubs [supported-biometric-auth [:supported-biometric-auth]] + (let [bio-type-label (biometric/get-label supported-biometric-auth)] + [react/view {:padding 24 :align-items :center} + [react/view {:margin-bottom 16 :width 32 :height 32 :background-color colors/blue-light + :border-radius 16 :align-items :center :justify-content :center} + [icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]] + [react/text {:style {:typography :title-bold}} (str (i18n/label :t/enable) " " bio-type-label)] + [react/text {:style {:margin-bottom 25 :margin-top 10 :text-align :center}} + (i18n/label :t/to-enable-biometric {:bio-type-label bio-type-label})] + [button/button {:label (i18n/label :t/ok-save-pass) :style {:margin-bottom 16} + :on-press #(re-frame/dispatch [:biometric-logout])}] + [button/button {:label :t/cancel :type :secondary + :on-press #(re-frame/dispatch [:hide-popover])}]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index a25df245101a..a8fc9d82668f 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -281,6 +281,7 @@ ::app-state ::app-in-background-since ::hardwallet + ::auth-method :multiaccount/multiaccount :navigation/view-id :navigation/navigation-stack diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index defafa7ec7f1..dc4a16d19aa9 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -20,8 +20,8 @@ [status-im.utils.http :as http] [status-im.utils.utils :as utils] [status-im.i18n :as i18n] - [status-im.biometric-auth.core :as biometric-auth] - [status-im.constants :as const])) + [status-im.constants :as const] + [status-im.multiaccounts.biometric.core :as biometric])) (defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator timeout-ms]}] (let [on-success #(re-frame/dispatch (success-event-creator %)) @@ -131,13 +131,13 @@ :content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message)) :confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again) :cancel-button-text (i18n/label :t/biometric-auth-confirm-logout) - :on-accept #(biometric-auth/authenticate on-biometric-auth-result authentication-options) + :on-accept #(biometric/authenticate on-biometric-auth-result authentication-options) :on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))) (fx/defn on-return-from-background [{:keys [db now] :as cofx}] (let [app-in-background-since (get db :app-in-background-since) signed-up? (get-in db [:multiaccount :signed-up?]) - biometric-auth? (get-in db [:multiaccount :settings :biometric-auth?]) + biometric-auth? (= (:auth-method db) "biometric") requires-bio-auth (and signed-up? biometric-auth? @@ -149,7 +149,7 @@ (mailserver/process-next-messages-request) (hardwallet/return-back-from-nfc-settings) #(when requires-bio-auth - (biometric-auth/authenticate-fx % on-biometric-auth-result authentication-options))))) + (biometric/authenticate % on-biometric-auth-result authentication-options))))) (fx/defn on-going-in-background [{:keys [db now] :as cofx}] (fx/merge cofx diff --git a/src/status_im/ui/screens/multiaccounts/login/styles.cljs b/src/status_im/ui/screens/multiaccounts/login/styles.cljs index cd13468f4c49..1b04d501314f 100644 --- a/src/status_im/ui/screens/multiaccounts/login/styles.cljs +++ b/src/status_im/ui/screens/multiaccounts/login/styles.cljs @@ -68,3 +68,11 @@ :text-align :center :flex-direction :row :align-items :center}) + +(def biometric-button + {:justify-content :center + :align-items :center + :height 40 + :width 40 + :border-radius 20 + :margin-left 16}) diff --git a/src/status_im/ui/screens/multiaccounts/login/views.cljs b/src/status_im/ui/screens/multiaccounts/login/views.cljs index 84b34f7626f4..594160a36cd9 100644 --- a/src/status_im/ui/screens/multiaccounts/login/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/login/views.cljs @@ -15,7 +15,8 @@ [status-im.ui.screens.multiaccounts.styles :as ast] [status-im.utils.platform :as platform] [status-im.utils.security :as security] - [status-im.utils.utils :as utils]) + [status-im.utils.utils :as utils] + [status-im.ui.components.icons.vector-icons :as icons]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn login-toolbar [can-navigate-back?] @@ -46,11 +47,13 @@ (utils/get-shortened-address public-key)]]]) (defview login [] - (letsubs [{:keys [error processing save-password? can-save-password?] :as multiaccount} [:multiaccounts/login] + (letsubs [{:keys [error processing save-password?] :as multiaccount} [:multiaccounts/login] can-navigate-back? [:can-navigate-back?] password-text-input (atom nil) sign-in-enabled? [:sign-in-enabled?] - view-id [:view-id]] + auth-method [:auth-method] + view-id [:view-id] + supported-biometric-auth [:supported-biometric-auth]] [react/keyboard-avoiding-view {:style ast/multiaccounts-view} [status-bar/status-bar] [login-toolbar can-navigate-back?] @@ -59,32 +62,38 @@ [multiaccount-login-badge multiaccount] [react/view {:style styles/password-container :important-for-accessibility :no-hide-descendants} - [text-input/text-input-with-label - {:placeholder (i18n/label :t/enter-your-password) - :ref #(reset! password-text-input %) - :auto-focus (= view-id :login) - :accessibility-label :password-input - :on-submit-editing (when sign-in-enabled? - #(login-multiaccount @password-text-input)) - :on-change-text #(do - (re-frame/dispatch [:set-in [:multiaccounts/login :password] - (security/mask-data %)]) - (re-frame/dispatch [:set-in [:multiaccounts/login :error] ""])) - :secure-text-entry true - :error (when (not-empty error) error)}]] - + [react/view {:flex-direction :row :align-items :center} + [react/view {:flex 1} + [text-input/text-input-with-label + {:placeholder (i18n/label :t/enter-your-password) + :ref #(reset! password-text-input %) + :auto-focus (= view-id :login) + :accessibility-label :password-input + :on-submit-editing (when sign-in-enabled? + #(login-multiaccount @password-text-input)) + :on-change-text #(do + (re-frame/dispatch [:set-in [:multiaccounts/login :password] + (security/mask-data %)]) + (re-frame/dispatch [:set-in [:multiaccounts/login :error] ""])) + :secure-text-entry true + :error (when (not-empty error) error)}]] + (when (and supported-biometric-auth (= auth-method "biometric")) + [react/touchable-highlight {:on-press #(re-frame/dispatch [:biometric-authenticate])} + [react/view {:style styles/biometric-button} + [icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]]])]] (when-not platform/desktop? ;; saving passwords is unavailable on Desktop - (if (and platform/android? (not can-save-password?)) + (if (and platform/android? (not auth-method)) ;; on Android, there is much more reasons for the password save to be unavailable, ;; so we don't show the checkbox whatsoever but put a label explaining why it happenned. [react/i18n-text {:style styles/save-password-unavailable-android - :key :save-password-unavailable-android}] + :key :save-password-unavailable-android}] [react/view {:style {:flex-direction :row :align-items :center - :justify-content :flex-start}} - [checkbox/checkbox {:checked? save-password? - :style {:padding-left 0 :padding-right 10} + :justify-content :flex-start + :margin-top 19}} + [checkbox/checkbox {:checked? save-password? + :style {:margin-left 3 :margin-right 10} :on-value-change #(re-frame/dispatch [:set-in [:multiaccounts/login :save-password?] %])}] [react/text (i18n/label :t/save-password)]]))]] (when processing diff --git a/src/status_im/ui/screens/popover/views.cljs b/src/status_im/ui/screens/popover/views.cljs index caf23e9db050..bb7a074f538c 100644 --- a/src/status_im/ui/screens/popover/views.cljs +++ b/src/status_im/ui/screens/popover/views.cljs @@ -7,7 +7,8 @@ [status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase] [status-im.ui.screens.wallet.request.views :as request] [status-im.ui.screens.profile.user.views :as profile.user] - [status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover])) + [status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover] + [status-im.ui.screens.biometric.views :as biometric])) (defn hide-panel-anim [bottom-anim-value alpha-value window-height] @@ -102,6 +103,9 @@ (= :custom-seed-phrase view) [multiaccounts.recover/custom-seed-phrase] + (= :enable-biometric view) + [biometric/enable-biometric-popover] + :else [view])]]]]])))}))) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index 1f1e77dbcbc9..79833638f538 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -8,10 +8,11 @@ [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar] - [status-im.ui.components.toolbar.view :as toolbar]) + [status-im.ui.components.toolbar.view :as toolbar] + [status-im.multiaccounts.biometric.core :as biometric]) (:require-macros [status-im.utils.views :as views])) -(defn- list-data [show-backup-seed? settings] +(defn- list-data [show-backup-seed? settings supported-biometric-auth biometric-auth? keycard?] [{:type :section-header :title :t/security :container-margin-top 6} @@ -26,6 +27,18 @@ :accessories [(when show-backup-seed? [components.common/counter {:size 22} 1]) :chevron]} + {:type :small + :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) + :container-margin-bottom 8 + :accessibility-label :biometric-auth-settings-switch + :disabled? (or (not (some? supported-biometric-auth)) keycard?) + :accessories [[react/switch + {:track-color #js {:true colors/blue :false nil} + :value (boolean biometric-auth?) + :on-value-change #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %]) + :disabled (or (not (some? supported-biometric-auth)) keycard?)}]] + :on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched + ((complement boolean) biometric-auth?)])} ;; TODO - uncomment when implemented ;; {:type :small ;; :title :t/change-password @@ -69,7 +82,10 @@ (views/defview privacy-and-security [] (views/letsubs [{:keys [seed-backed-up? mnemonic]} [:multiaccount] - settings [:multiaccount-settings]] + settings [:multiaccount-settings] + supported-biometric-auth [:supported-biometric-auth] + auth-method [:auth-method] + {:keys [keycard-key-uid]} [:multiaccount]] (let [show-backup-seed? (and (not seed-backed-up?) (not (string/blank? mnemonic)))] [react/view {:flex 1 :background-color colors/white} @@ -77,6 +93,7 @@ [toolbar/simple-toolbar (i18n/label :t/privacy-and-security)] [list/flat-list - {:data (list-data show-backup-seed? settings) + {:data (list-data show-backup-seed? settings supported-biometric-auth + (= auth-method "biometric") (boolean keycard-key-uid)) :key-fn (fn [_ i] (str i)) - :render-fn list/flat-list-generic-render-fn}]]))) + :render-fn list/flat-list-generic-render-fn}]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/signing/views.cljs b/src/status_im/ui/screens/signing/views.cljs index 5e723f1fb49b..a61743664e87 100644 --- a/src/status_im/ui/screens/signing/views.cljs +++ b/src/status_im/ui/screens/signing/views.cljs @@ -46,11 +46,6 @@ (defn separator [] [react/view {:height 1 :background-color colors/gray-lighter}]) -(defn acc-text [txt1 txt2] - [react/nested-text nil - txt1 " " - [{:style {:color colors/gray}} txt2]]) - (defn displayed-name [contact] (if (or (:preferred-name contact) (:name contact)) (multiaccounts/displayed-name contact) @@ -218,9 +213,13 @@ sign [:signing/sign] chain [:ethereum/chain-keyword] {:keys [amount-error gas-error]} [:signing/amount-errors (:address from)] - keycard-multiaccount? [:keycard-multiaccount?]] + keycard-multiaccount? [:keycard-multiaccount?] + prices [:prices] + wallet-currency [:wallet/currency]] (let [display-symbol (wallet.utils/display-symbol token) - fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))] + fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain)) + converted-value (* amount (get-in prices [(keyword display-symbol) (keyword (:code wallet-currency)) :price])) + converted-fee-value (* fee (get-in prices [(keyword fee-display-symbol) (keyword (:code wallet-currency)) :price]))] [react/view styles/sheet [header sign tx display-symbol fee fee-display-symbol] [separator] @@ -239,24 +238,43 @@ {:type :small :title :t/send-request-amount :error amount-error - :accessories [[acc-text (if amount (str amount) "0") - (or display-symbol fee-display-symbol)]]}] - [separator]]) - [list-item/list-item - {:type :small - :title :t/network-fee - :error gas-error - :accessories [[acc-text fee fee-display-symbol] :chevron] - :on-press #(re-frame/dispatch - [:signing.ui/open-fee-sheet - {:content (fn [] [sheets/fee-bottom-sheet fee-display-symbol]) - :content-height 270}])}] - [react/view {:align-items :center :margin-top 16 :margin-bottom 40} - (if keycard-multiaccount? - [sign-with-keycard-button amount-error gas-error] - [button/button {:on-press #(re-frame/dispatch [:set :signing/sign {:type :password}]) - :disabled? (or amount-error gas-error) - :label :t/sign-with-password}])]])]))) + :accessories [[react/nested-text {:style {:color colors/gray}} + [{:style {:color colors/black}} (if amount (str amount) "0")] + " " + (or display-symbol fee-display-symbol) + " • " + [{:style {:color colors/black}} (if converted-value (str "~" (i18n/format-currency converted-value (:code wallet-currency))) + [react/activity-indicator {:color :colors/gray + :ios {:size :small} + :android {:size :16}}])] + " " + (str (:code wallet-currency))]]}] + [separator] + [list-item/list-item + {:type :small + :title :t/network-fee + :error gas-error + :accessories [[react/nested-text {:style {:color colors/gray}} + [{:style {:color colors/black}} fee] + " " + fee-display-symbol + " • " + [{:style {:color colors/black}} (if converted-fee-value (str "~" (i18n/format-currency converted-fee-value (:code wallet-currency))) + [react/activity-indicator {:color :colors/gray + :ios {:size :small} + :android {:size :16}}])] + " " + (str (:code wallet-currency))] :chevron] + :on-press #(re-frame/dispatch + [:signing.ui/open-fee-sheet + {:content (fn [] [sheets/fee-bottom-sheet fee-display-symbol]) + :content-height 270}])}] + [react/view {:align-items :center :margin-top 16 :margin-bottom 40} + (if keycard-multiaccount? + [sign-with-keycard-button amount-error gas-error] + [button/button {:on-press #(re-frame/dispatch [:set :signing/sign {:type :password}]) + :disabled? (or amount-error gas-error) + :label :t/sign-with-password}])]])])]))) (defn signing-view [tx window-height] (let [bottom-anim-value (anim/create-value window-height) diff --git a/src/status_im/utils/keychain/core.cljs b/src/status_im/utils/keychain/core.cljs index f994e07ed73f..36e431148f81 100644 --- a/src/status_im/utils/keychain/core.cljs +++ b/src/status_im/utils/keychain/core.cljs @@ -5,7 +5,9 @@ [status-im.utils.platform :as platform] [status-im.utils.security :as security] [status-im.native-module.core :as status] - [status-im.utils.handlers :as handlers])) + [status-im.utils.fx :as fx] + [goog.object :as object] + [clojure.string :as string])) (defn- check-conditions [callback & checks] (if (= (count checks) 0) @@ -28,7 +30,9 @@ ;; to an address (`server`) property. (defn enum-val [enum-name value-name] - (get-in (js->clj rn/keychain) [enum-name value-name])) + (-> rn/keychain + (object/get enum-name) + (object/get value-name))) ;; We need a more strict access mode for keychain entries that save user password. ;; iOS @@ -48,9 +52,9 @@ ;; > you might choose kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. ;; That is exactly what we use there. ;; Note that the password won't be stored if the device isn't locked by a passcode. - {:accessible (enum-val "ACCESSIBLE" "WHEN_PASSCODE_SET_THIS_DEVICE_ONLY")}) + #js {:accessible (enum-val "ACCESSIBLE" "WHEN_PASSCODE_SET_THIS_DEVICE_ONLY")}) -(def keychain-secure-hardware +(def ^:const keychain-secure-hardware ;; (Android) Requires storing the encryption key for the entry in secure hardware ;; or StrongBox (see https://developer.android.com/training/articles/keystore#ExtractionPrevention) "SECURE_HARDWARE") @@ -70,55 +74,54 @@ (defn- device-encrypted? [callback] (-> (.canImplyAuthentication rn/keychain - (clj->js - {:authenticationType - (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")})) + #js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}) (.then callback))) -;; Stores the password for the address to the Keychain -(defn save-user-password [address password callback] - (-> (.setInternetCredentials rn/keychain address address password keychain-secure-hardware (clj->js keychain-restricted-availability)) - (.then callback))) - -(defn handle-callback [callback result] - (if result - (callback (security/mask-data (.-password result))) - (callback nil))) +(defn can-save-user-password? [callback] + (cond + platform/ios? + (check-conditions callback device-encrypted?) -;; Gets the password for a specified address from the Keychain -(defn get-user-password [address callback] - (if (or platform/ios? platform/android?) - (-> (.getInternetCredentials rn/keychain address) - (.then (partial handle-callback callback))) - (callback))) ;; no-op for Desktop + platform/android? + (check-conditions callback secure-hardware-available? device-not-rooted?) -;; Clears the password for a specified address from the Keychain -;; (example of usage is logout or signing in w/o "save-password") -(defn clear-user-password [address callback] - (if (or platform/ios? platform/android?) - (-> (.resetInternetCredentials rn/keychain address) - (.then callback)) - (callback true))) ;; no-op for Desktop + :else + (callback false))) -;; Resolves to `false` if the device doesn't have neither a passcode nor a biometry auth. -(defn can-save-user-password? [callback] - (cond - platform/ios? (check-conditions callback - device-encrypted?) +(defn save-credentials + "Stores the credentials for the address to the Keychain" + [server username password callback] + (-> (.setInternetCredentials rn/keychain (string/lower-case server) username password + keychain-secure-hardware keychain-restricted-availability) + (.then callback))) - platform/android? (check-conditions - callback - secure-hardware-available? - device-not-rooted?) +(defn get-credentials + "Gets the credentials for a specified server from the Keychain" + [server callback] + (if platform/mobile? + (-> (.getInternetCredentials rn/keychain (string/lower-case server)) + (.then callback)) + (callback))) ;; no-op for Desktop - :else (callback false))) +(re-frame/reg-fx + :keychain/get-auth-method + (fn [[address callback]] + (can-save-user-password? + (fn [can-save?] + (if can-save? + (get-credentials (str address "-auth") #(callback (if % (.-password %) "none"))) + (callback nil)))))) -;;;; Effects +(re-frame/reg-fx + :keychain/get-user-password + (fn [[address callback]] + (get-credentials address #(if % (callback (security/mask-data (.-password %))) (callback nil))))) (re-frame/reg-fx :keychain/save-user-password (fn [[address password]] - (save-user-password + (save-credentials + address address (security/safe-unmask-data password) #(when-not % @@ -129,26 +132,41 @@ "but you will have to login again next time you launch it.")))))) (re-frame/reg-fx - :keychain/get-user-password - (fn [[address callback]] - (get-user-password address callback))) - -(re-frame/reg-fx - :keychain/clear-user-password - (fn [address] - (clear-user-password + :keychain/save-auth-method + (fn [[address method]] + (save-credentials + (str address "-auth") address + method #(when-not % - (log/error (str "Error while clearing saved password.")))))) + (log/error + (str "Error while saving auth method." + " " + "The app will continue to work normally, " + "but you will have to login again next time you launch it.")))))) (re-frame/reg-fx - :keychain/can-save-user-password? - (fn [_] - (can-save-user-password? #(re-frame/dispatch [:keychain.callback/can-save-user-password?-success %])))) - -(handlers/register-handler-fx - :keychain.callback/can-save-user-password?-success - (fn [{:keys [db]} [_ can-save-user-password?]] - {:db (assoc-in db - [:multiaccounts/login :can-save-password?] - can-save-user-password?)})) + :keychain/clear-user-password + (fn [address] + (when platform/mobile? + (-> (.resetInternetCredentials rn/keychain (string/lower-case address)) + (.then #(when-not % (log/error (str "Error while clearing saved password.")))))))) + +(fx/defn get-auth-method + [_ address] + {:keychain/get-auth-method + [address #(re-frame/dispatch [:multiaccounts.login/get-auth-method-success % address])]}) + +(fx/defn get-user-password + [_ address] + {:keychain/get-user-password + [address #(re-frame/dispatch [:multiaccounts.login.callback/get-user-password-success % address])]}) + +(fx/defn save-user-password + [cofx address password] + {:keychain/save-user-password [address password]}) + +(fx/defn save-auth-method + [{:keys [db]} address method] + {:db (assoc db :auth-method method) + :keychain/save-auth-method [address method]}) \ No newline at end of file diff --git a/src/status_im/wallet/accounts/core.cljs b/src/status_im/wallet/accounts/core.cljs index fcb1bf56e9dd..ea7914c64f1d 100644 --- a/src/status_im/wallet/accounts/core.cljs +++ b/src/status_im/wallet/accounts/core.cljs @@ -21,15 +21,15 @@ (re-frame/reg-fx ::generate-account - (fn [{:keys [address hashed-password path-num]}] - (status/multiaccount-load-account - address - hashed-password - (fn [value] - (let [{:keys [id error]} (types/json->clj value)] - (if error - (re-frame/dispatch [::generate-new-account-error]) - (let [path (str constants/path-root "/" path-num)] + (fn [{:keys [derivation-info hashed-password path-num]}] + (let [{:keys [address path]} derivation-info] + (status/multiaccount-load-account + address + hashed-password + (fn [value] + (let [{:keys [id error]} (types/json->clj value)] + (if error + (re-frame/dispatch [::generate-new-account-error]) (status/multiaccount-derive-addresses id [path] @@ -45,7 +45,7 @@ {:name (str "Account " path-num) :address address :public-key public-key - :path path + :path (str constants/path-wallet-root "/" path-num) :color (rand-nth colors/account-colors)}]))))))))))))) (fx/defn set-symbol-request @@ -56,11 +56,20 @@ (fx/defn generate-new-account {:events [:wallet.accounts/generate-new-account]} [{:keys [db]} password] - (when-not (get-in db [:generate-account :step]) - {:db (assoc-in db [:generate-account :step] :generating) - ::generate-account {:address (get-in db [:multiaccount :address]) - :path-num (inc (get-in db [:multiaccount :latest-derived-path])) - :hashed-password (ethereum/sha3 password)}})) + (let [wallet-root-address (get-in db [:multiaccount :wallet-root-address]) + path-num (inc (get-in db [:multiaccount :latest-derived-path]))] + (when-not (get-in db [:generate-account :step]) + {:db (assoc-in db [:generate-account :step] :generating) + ::generate-account {:derivation-info (if wallet-root-address + ;; Use the walllet-root-address for stored on disk keys + ;; This needs to be the RELATIVE path to the key used to derive + {:path (str "m/" path-num) + :address wallet-root-address} + ;; Fallback on the master account for keycards, use the absolute path + {:path (str constants/path-wallet-root "/" path-num) + :address (get-in db [:multiaccount :address])}) + :path-num path-num + :hashed-password (ethereum/sha3 password)}}))) (fx/defn generate-new-account-error {:events [::generate-new-account-error]} diff --git a/test/appium/support/api/network_api.py b/test/appium/support/api/network_api.py index f8c0fa73d8e4..5bc666890ac0 100644 --- a/test/appium/support/api/network_api.py +++ b/test/appium/support/api/network_api.py @@ -5,6 +5,7 @@ import requests import time from json import JSONDecodeError +from decimal import Decimal class NetworkApi(object): @@ -130,3 +131,10 @@ def start_chat_bot(self, chat_name: str, messages_number: int, interval: int = 1 url = '%s/ping/%s?count=%s&interval=%s' % (self.chat_bot_url, chat_name, messages_number, interval) text = requests.request('GET', url).text return [i.split(maxsplit=5)[-1].strip('*') for i in text.splitlines()] + + def get_rounded_balance(self, fetched_balance, actual_balance): + fetched_balance, actual_balance = str(fetched_balance), str(actual_balance) + # get actual number of decimals on account balance + decimals = abs(Decimal(fetched_balance).as_tuple().exponent) + rounded_balance = round(float(actual_balance), decimals) + return rounded_balance \ No newline at end of file diff --git a/test/appium/tests/atomic/account_management/test_profile.py b/test/appium/tests/atomic/account_management/test_profile.py index 737ac673aa6a..09aaa9cd01fd 100644 --- a/test/appium/tests/atomic/account_management/test_profile.py +++ b/test/appium/tests/atomic/account_management/test_profile.py @@ -28,15 +28,88 @@ def test_set_profile_picture(self): @marks.testrail_id(5741) @marks.high - def test_mobile_data_usage_popup(self): + def test_mobile_data_usage_popup_continue_syncing(self): sign_in_view = SignInView(self.driver) sign_in_view.create_user() + sign_in_view.just_fyi("Enable mobile network to see popup and enable syncing") sign_in_view.toggle_mobile_data() - if not sign_in_view.find_text_part("Sync using Mobile data"): + if not sign_in_view.element_by_text_part("Sync using Mobile data").is_element_displayed(): self.driver.fail('No popup about Mobile data is shown') - # TODO: add steps after 8973 fix + sign_in_view.wait_for_element_starts_with_text('Continue syncing').click() - @marks.testrail_id(5454) + sign_in_view.just_fyi("Check that selected option is stored in Profile") + profile_view = sign_in_view.profile_button.click() + profile_view.sync_settings_button.click() + profile_view.element_by_text('Mobile data').click() + for toggle in profile_view.use_mobile_data, profile_view.ask_me_when_on_mobile_network: + if not toggle.attribute_value('checked'): + self.errors.append("Toggles in Mobile settings are not enabled") + + sign_in_view.just_fyi("Check that can join public chat and send message") + chat_name = sign_in_view.get_public_chat_name() + home = profile_view.get_back_to_home_view() + chat = home.join_public_chat(chat_name) + message = 'test message' + chat.chat_message_input.send_keys(message) + chat.send_message_button.click() + if not chat.chat_element_by_text(message).is_element_displayed(): + self.errors.append("Message was not sent!") + self.verify_no_errors() + + @marks.testrail_id(5741) + @marks.high + def test_mobile_data_usage_popup_stop_syncing(self): + sign_in_view = SignInView(self.driver) + sign_in_view.create_user() + offline_banner_text = "History syncing offline" + + sign_in_view.just_fyi("Enable mobile network to see popup and stop syncing") + sign_in_view.toggle_mobile_data() + sign_in_view.wait_for_element_starts_with_text('Stop syncing').click() + if not sign_in_view.wait_for_element_starts_with_text(offline_banner_text, 60): + self.driver.fail('No popup about offline history is shown') + sign_in_view.element_by_text_part(offline_banner_text).click() + for item in "Offline, waiting for Wi-Fi", "Start syncing", "Go to settings": + if not sign_in_view.element_by_text(item).is_element_displayed(): + self.driver.fail("%s is not shown" % item) + + sign_in_view.just_fyi("Start syncing in offline popup") + sign_in_view.element_by_text("Start syncing").click() + if sign_in_view.element_by_text_part(offline_banner_text).is_element_displayed(): + self.driver.fail("Popup about offline history is shown") + + @marks.testrail_id(6228) + @marks.high + def test_mobile_data_usage_settings(self): + sign_in_view = SignInView(self.driver) + sign_in_view.create_user() + profile_view = sign_in_view.profile_button.click() + + sign_in_view.just_fyi("Check default preferences") + profile_view.sync_settings_button.click() + profile_view.element_by_text('Mobile data').click() + + if profile_view.use_mobile_data.attribute_value("checked"): + self.errors.append("Mobile data is enabled by default") + if not profile_view.ask_me_when_on_mobile_network.attribute_value("checked"): + self.errors.append("'Ask me when on mobile network' is not enabled by default") + + sign_in_view.just_fyi("Disable 'ask me when on mobile network' and check that it is not shown") + profile_view.ask_me_when_on_mobile_network.click() + sign_in_view.toggle_mobile_data() + if sign_in_view.element_by_text("Start syncing").is_element_displayed(20): + self.errors.append("Popup is shown, but 'ask me when on mobile network' is disabled") + + sign_in_view.just_fyi("Check 'Restore default' setting") + profile_view.element_by_text('Restore Defaults').click() + if profile_view.use_mobile_data.attribute_value("checked"): + self.errors.append("Mobile data is enabled by default") + if not profile_view.ask_me_when_on_mobile_network.attribute_value("checked"): + self.errors.append("'Ask me when on mobile network' is not enabled by default") + self.verify_no_errors() + + + @marks.testrail_id(6229) @marks.critical def test_user_can_remove_profile_picture(self): signin_view = SignInView(self.driver) diff --git a/test/appium/tests/atomic/account_management/test_wallet_management.py b/test/appium/tests/atomic/account_management/test_wallet_management.py index 0eb095e23b1c..e2ee111ec97b 100644 --- a/test/appium/tests/atomic/account_management/test_wallet_management.py +++ b/test/appium/tests/atomic/account_management/test_wallet_management.py @@ -226,6 +226,24 @@ def test_user_can_see_all_own_assets_after_account_recovering(self): if not wallet_view.element_by_text('1').is_element_displayed(): self.driver.fail('User collectibles amount does not match') + @marks.testrail_id(5346) + @marks.high + def test_collectible_from_wallet_opens_in_browser_view(self): + passphrase = wallet_users['F']['passphrase'] + signin_view = SignInView(self.driver) + home_view = signin_view.recover_access(passphrase=passphrase) + profile = home_view.profile_button.click() + profile.switch_network('Mainnet with upstream RPC') + wallet_view = profile.wallet_button.click() + wallet_view.set_up_wallet() + wallet_view.collectibles_button.click() + wallet_view.cryptokitties_in_collectibles_button.click() + web_view = wallet_view.view_in_cryptokitties_button.click() + web_view.element_by_text('cryptokitties.co').click() + cryptokitty_link = 'https://www.cryptokitties.co/kitty/1338226' + if not web_view.element_by_text(cryptokitty_link).is_element_displayed(): + self.driver.fail('Cryptokitty detail page not opened') + @marks.testrail_id(6208) @marks.high def test_add_custom_token(self): diff --git a/test/appium/tests/atomic/chats/test_chats_management.py b/test/appium/tests/atomic/chats/test_chats_management.py index 0002910bde7c..dd7fc87bbb11 100644 --- a/test/appium/tests/atomic/chats/test_chats_management.py +++ b/test/appium/tests/atomic/chats/test_chats_management.py @@ -242,9 +242,9 @@ def test_block_user_from_public_chat(self): message_before_block_1 = "Before block from %s" % device_1.driver.number message_before_block_2 = "Before block from %s" % device_2.driver.number message_after_block_2 = "After block from %s" % device_2.driver.number - home_1, home_2 = device_1.create_user(), device_2.recover_access(basic_user['passphrase']) + home_1, home_2 = device_1.create_user(), device_2.create_user() - # device 1, device 2: join to public chat and send several messages + device_1.just_fyi('both devices joining the same public chat and send messages') chat_name = device_1.get_public_chat_name() for home in home_1, home_2: home.join_public_chat(chat_name) @@ -253,27 +253,25 @@ def test_block_user_from_public_chat(self): chat.chat_message_input.send_keys("Before block from %s" % chat.driver.number) chat.send_message_button.click() - # device 1: block user from public chat + device_1.just_fyi('block user') chat_element = chat_public_1.chat_element_by_text(message_before_block_2) chat_element.find_element() chat_element.member_photo.click() chat_public_1.profile_block_contact.click() chat_public_1.block_button.click() - # device 1: check that messages from blocked user are hidden in public chat + device_1.just_fyi('messages from blocked user are hidden in public chat and close app') if chat_public_1.chat_element_by_text(message_before_block_2).is_element_displayed(): self.errors.append( "Messages from blocked user %s are not cleared in public chat '%s'" % ( device_2.driver.number, chat_name)) - - # device 1: close app self.drivers[0].close_app() - # device 2: send message to public chat while device 1 is offline + device_2.just_fyi('send message to public chat while device 1 is offline') chat_public_2.chat_message_input.send_keys(message_after_block_2) chat_public_2.send_message_button.click() - # device 1: check that new messages from blocked user are not delivered + device_1.just_fyi('check that new messages from blocked user are not delivered') self.drivers[0].launch_app() device_1.accept_agreements() device_1.sign_in() @@ -292,12 +290,14 @@ def test_block_user_from_one_to_one_header(self): message_before_block_1 = "Before block from %s" % device_1.driver.number message_before_block_2 = "Before block from %s" % device_2.driver.number message_after_block_2 = "After block from %s" % device_2.driver.number - home_1, home_2 = device_1.create_user(), device_2.recover_access(basic_user['passphrase']) + home_1, home_2 = device_1.create_user(), device_2.create_user() profile_1 = home_1.profile_button.click() + device_2_public_key = home_2.get_public_key() + home_2.get_back_to_home_view() default_username_1 = profile_1.default_username_text.text profile_1.get_back_to_home_view() - # device 1, device 2: join to public chat and send several messages + device_1.just_fyi('both devices joining the same public chat and send messages') chat_name = device_1.get_public_chat_name() for home in home_1, home_2: home.join_public_chat(chat_name) @@ -309,8 +309,8 @@ def test_block_user_from_one_to_one_header(self): chat_public_1.get_back_to_home_view() chat_public_2.get_back_to_home_view() - # device 1, device 2: create 1-1 chat and send there several messages - chat_1 = home_1.add_contact(basic_user['public_key']) + device_1.just_fyi('both devices joining 1-1 chat and exchanging several messages') + chat_1 = home_1.add_contact(device_2_public_key) for _ in range(2): chat_1.chat_message_input.send_keys(message_before_block_1) chat_1.send_message_button.click() @@ -320,24 +320,22 @@ def test_block_user_from_one_to_one_header(self): chat_2.chat_message_input.send_keys(message_before_block_2) chat_2.send_message_button.click() - # device 1: block user from chat header + device_1.just_fyi('block user') chat_1.chat_options.click() chat_1.view_profile_button.click() chat_1.profile_block_contact.click() chat_1.block_button.click() - # device 1: check that chat with blocked user was deleted - # and messages from blocked user are hidden in public chat + device_1.just_fyi('no 1-1, messages from blocked user are hidden in public chat') if home_1.get_chat_with_user(basic_user['username']).is_element_displayed(): home_1.driver.fail("Chat with blocked user '%s' is not deleted" % device_2.driver.number) - public_chat_after_block = home_1.join_public_chat(chat_name) if public_chat_after_block.chat_element_by_text(message_before_block_2).is_element_displayed(): self.errors.append( "Messages from blocked user '%s' are not cleared in public chat '%s'" % (device_2.driver.number, chat_name)) - # device 2: send messages to 1-1 and public chat + device_2.just_fyi('send messages to 1-1 and public chat') for _ in range(2): chat_2.chat_message_input.send_keys(message_after_block_2) chat_2.send_message_button.click() @@ -349,18 +347,15 @@ def test_block_user_from_one_to_one_header(self): chat_public_2.chat_message_input.send_keys(message_after_block_2) chat_public_2.send_message_button.click() - # device 1: check that new messages sent from device 2 are not shown + device_1.just_fyi("check that new messages didn't arrived from blocked user") if public_chat_after_block.chat_element_by_text(message_after_block_2).is_element_displayed(): self.errors.append("Message from blocked user '%s' is received" % device_2.driver.number) public_chat_after_block.get_back_to_home_view() - if home_1.get_chat_with_user(basic_user['username']).is_element_displayed(): device_2.driver.fail("Chat with blocked user is reappeared after receiving new messages") - - # device 1: close app self.drivers[0].close_app() - # device 2: send message to 1-1 and public chat while device 1 is offline + device_2.just_fyi("send messages when device 1 is offline") for _ in range(2): chat_public_2.chat_message_input.send_keys(message_after_block_2) chat_public_2.send_message_button.click() @@ -370,7 +365,7 @@ def test_block_user_from_one_to_one_header(self): chat_2.chat_message_input.send_keys(message_after_block_2) chat_2.send_message_button.click() - # device 1: check that messages from blocked user are not fetched from offline + device_1.just_fyi("reopen app and check that messages from blocked user are not fetched") self.drivers[0].launch_app() device_1.accept_agreements() device_1.sign_in() diff --git a/test/appium/tests/atomic/chats/test_one_to_one.py b/test/appium/tests/atomic/chats/test_one_to_one.py index c55fd84f01f1..4825a4af33a8 100644 --- a/test/appium/tests/atomic/chats/test_one_to_one.py +++ b/test/appium/tests/atomic/chats/test_one_to_one.py @@ -69,7 +69,7 @@ def test_offline_messaging_1_1_chat(self): home_1.airplane_mode_button.click() # airplane mode on primary device chat_2.element_by_text('Connecting to peers...').wait_for_invisibility_of_element(60) - chat_2.connection_status.wait_for_invisibility_of_element(30) + chat_2.connection_status.wait_for_invisibility_of_element(60) message_2 = 'one more message' chat_2.chat_message_input.send_keys(message_2) chat_2.send_message_button.click() diff --git a/test/appium/tests/atomic/dapps_and_browsing/test_dapps.py b/test/appium/tests/atomic/dapps_and_browsing/test_dapps.py index 4e5de434ab0a..12afe0f9374f 100644 --- a/test/appium/tests/atomic/dapps_and_browsing/test_dapps.py +++ b/test/appium/tests/atomic/dapps_and_browsing/test_dapps.py @@ -28,12 +28,12 @@ def test_request_public_key_status_test_daap(self): sign_in_view = SignInView(self.driver) home_view = sign_in_view.recover_access(passphrase=user['passphrase']) status_test_dapp = home_view.open_status_test_dapp(allow_all=False) - status_test_dapp.status_api_button.click() - status_test_dapp.request_contact_code_button.click() + status_test_dapp.status_api_button.click_until_presence_of_element(status_test_dapp.request_contact_code_button) + status_test_dapp.request_contact_code_button.click_until_presence_of_element(status_test_dapp.deny_button) status_test_dapp.deny_button.click() if status_test_dapp.element_by_text(user['public_key']).is_element_displayed(): pytest.fail('Public key is returned but access was not allowed') - status_test_dapp.request_contact_code_button.click() + status_test_dapp.request_contact_code_button.click_until_presence_of_element(status_test_dapp.deny_button) status_test_dapp.allow_button.click() if not status_test_dapp.element_by_text(user['public_key']).is_element_displayed(): pytest.fail('Public key is not returned') diff --git a/test/appium/tests/atomic/transactions/test_wallet.py b/test/appium/tests/atomic/transactions/test_wallet.py index 21a384295662..a622de20365f 100644 --- a/test/appium/tests/atomic/transactions/test_wallet.py +++ b/test/appium/tests/atomic/transactions/test_wallet.py @@ -1,5 +1,4 @@ import random - import pytest from support.utilities import get_merged_txs_list @@ -461,7 +460,8 @@ def test_send_funds_between_accounts_in_multiaccount_instance(self): wallet_view.send_transaction_button.click() wallet_view.back_button.click() balance_after_receiving_tx = float(wallet_view.eth_asset_value.text) - if balance_after_receiving_tx != float(transaction_amount): + expected_balance = self.network_api.get_rounded_balance(balance_after_receiving_tx, transaction_amount) + if balance_after_receiving_tx != expected_balance: self.driver.fail('New account balance %s does not match expected %s after receiving a transaction' % ( balance_after_receiving_tx, transaction_amount)) @@ -484,12 +484,16 @@ def test_send_funds_between_accounts_in_multiaccount_instance(self): self.network_api.verify_balance_is_updated(updated_balance, status_account_address) wallet_view.just_fyi("Verify total ETH on main wallet view") + self.network_api.wait_for_confirmation_of_transaction(status_account_address, transaction_amount_1) + self.network_api.verify_balance_is_updated((updated_balance + transaction_amount_1), status_account_address) send_transaction.back_button.click() balance_of_sub_account = float(self.network_api.get_balance(sub_account_address)) / 1000000000000000000 balance_of_status_account = float(self.network_api.get_balance(status_account_address)) / 1000000000000000000 - expected_balance = str(float(balance_after_receiving_tx) - transaction_amount_1 - float(total_fee)) total_eth_from_two_accounts = float(wallet_view.eth_asset_value.text) - if total_eth_from_two_accounts != (balance_of_status_account + balance_of_sub_account): + expected_balance = self.network_api.get_rounded_balance(total_eth_from_two_accounts, + (balance_of_status_account + balance_of_sub_account)) + + if total_eth_from_two_accounts != expected_balance: self.driver.fail('Total wallet balance %s != of Status account (%s) + SubAccount (%s)' % ( total_eth_from_two_accounts, balance_of_status_account, balance_of_sub_account)) diff --git a/test/appium/tests/users.py b/test/appium/tests/users.py index 6b93b38f8d65..da3d174a1d86 100644 --- a/test/appium/tests/users.py +++ b/test/appium/tests/users.py @@ -49,6 +49,14 @@ wallet_users['E']['public_key'] = "0x044cf0620ec3ea0aba9fb0e19cb42a6fbd6b4e74f234f0da82580564817b238cc6434745d31" \ "fa1649927ba48adfa7c95991fd51940bc00a71e80b095db5b107f1b" +wallet_users['F'] = dict() +wallet_users['F']['passphrase'] = "jazz human replace save wreck merry evolve oval black expose clutch sword" +wallet_users['F']['username'] = "Dual Sour Galapagostortoise" +wallet_users['F']['address'] = "0x8A4339aE98df2B2e51E37631C8B4F853048D4556" +wallet_users['F']['public_key'] = "0x049d0b39d95b20114fac79c3173a36c60a126c060dce52bba4128ab9a3885f0f058f2af9c92099" \ + "315eb564412b718d8bfe697a4425e4bc603063abd4f5c45f8e57" + + # Users used in chats. E.g. as members of a group chat chat_users = dict() diff --git a/test/appium/views/base_element.py b/test/appium/views/base_element.py index 16eaa13c2c1c..725db847bb07 100644 --- a/test/appium/views/base_element.py +++ b/test/appium/views/base_element.py @@ -157,6 +157,9 @@ def template(self, value): def image(self): return Image.open(BytesIO(base64.b64decode(self.find_element().screenshot_as_base64))) + def attribute_value(self, value): + return self.find_element().get_attribute(value) + def is_element_image_equals_template(self, file_name: str = ''): if file_name: self.template = file_name diff --git a/test/appium/views/base_view.py b/test/appium/views/base_view.py index e9239897e414..ae9b00ff7be4 100644 --- a/test/appium/views/base_view.py +++ b/test/appium/views/base_view.py @@ -386,6 +386,7 @@ def confirm_until_presence_of_element(self, desired_element, attempts=3): counter += 1 def just_fyi(self, string): + self.driver.info('=========================================================================') self.driver.info(string) def click_system_back_button(self): diff --git a/test/appium/views/dapps_view.py b/test/appium/views/dapps_view.py index cc41b45b263c..5dae1114a975 100644 --- a/test/appium/views/dapps_view.py +++ b/test/appium/views/dapps_view.py @@ -3,6 +3,7 @@ from views.home_view import ChatElement + class OpenDAppButton(BaseButton): def __init__(self, driver): super(OpenDAppButton, self).__init__(driver) @@ -30,26 +31,31 @@ def __init__(self, driver, name): super(BrowserEntry, self).__init__(driver, name) self.locator = self.Locator.xpath_selector('//*[@text="%s"]/..' % name) + class EnsName(BaseEditBox): def __init__(self, driver): super(EnsName, self).__init__(driver) self.locator = self.Locator.xpath_selector('//android.widget.EditText') + class EnsCheckName(BaseButton): def __init__(self, driver): super(EnsCheckName, self).__init__(driver) self.locator = self.Locator.xpath_selector('//android.widget.EditText//following-sibling::android.view.ViewGroup[1]') + class RemoveDappButton(BaseButton): def __init__(self, driver): super(RemoveDappButton, self).__init__(driver) self.locator = self.Locator.accessibility_id('remove-dapp-from-list') + class ClearAllDappButton(BaseButton): def __init__(self, driver): super(ClearAllDappButton, self).__init__(driver) self.locator = self.Locator.accessibility_id('clear-all-dapps') + class DappsView(BaseView): def __init__(self, driver): diff --git a/test/appium/views/profile_view.py b/test/appium/views/profile_view.py index 62cad8fc4f53..a64e93389c87 100644 --- a/test/appium/views/profile_view.py +++ b/test/appium/views/profile_view.py @@ -503,6 +503,19 @@ def __init__(self, driver): self.locator = self.Locator.xpath_selector( "//*[@text='Show my ENS username in chats']/following-sibling::*[1][name()='android.widget.Switch'] ") +class UseMobileDataToggle(BaseButton): + def __init__(self, driver): + super(UseMobileDataToggle, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//*[@text='Use mobile data']/../*[name()='android.widget.Switch']") + +class AskMeWhenOnMobileNetworkToggle(BaseButton): + def __init__(self, driver): + super(AskMeWhenOnMobileNetworkToggle, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//*[@text='Ask me when on mobile network']/../*[name()='android.widget.Switch']") + + class ProfileView(BaseView): @@ -585,8 +598,13 @@ def __init__(self, driver): self.advertise_device_button = AdvertiseDeviceButton(self.driver) self.sync_all_button = SyncAllButton(self.driver) + # ENS self.show_ens_name_in_chats = ShowENSNameInChatsToggle(self.driver) + # Mobile Data + self.use_mobile_data = UseMobileDataToggle(self.driver) + self.ask_me_when_on_mobile_network = AskMeWhenOnMobileNetworkToggle(self.driver) + def switch_network(self, network): self.advanced_button.click() self.debug_mode_toggle.click() diff --git a/test/appium/views/wallet_view.py b/test/appium/views/wallet_view.py index 49010e1edca5..b510cffa5a0d 100644 --- a/test/appium/views/wallet_view.py +++ b/test/appium/views/wallet_view.py @@ -171,6 +171,27 @@ def __init__(self, driver): self.locator = self.Locator.text_selector('Collectibles') +class CryptoKittiesInCollectiblesButton(BaseButton): + def __init__(self, driver): + super(CryptoKittiesInCollectiblesButton, self).__init__(driver) + self.locator = self.Locator.text_selector('CryptoKitties') + + +class ViewInCryptoKittiesButton(BaseButton): + def __init__(self, driver): + super(ViewInCryptoKittiesButton, self).__init__(driver) + self.locator = self.Locator.accessibility_id('open-collectible-button') + + def navigate(self): + from views.web_views.base_web_view import BaseWebView + return BaseWebView(self.driver) + + def click(self): + self.wait_for_element(30).click() + self.driver.info('Tap on View in CryptoKitties') + return self.navigate() + + class BackupRecoveryPhrase(BaseButton): def __init__(self, driver): super(BackupRecoveryPhrase, self).__init__(driver) @@ -330,6 +351,8 @@ def __init__(self, driver): self.multiaccount_more_options = MultiaccountMoreOptions(self.driver) self.accounts_status_account = AccountElementButton(self.driver, account_name="Status account") self.collectibles_button = CollectiblesButton(self.driver) + self.cryptokitties_in_collectibles_button = CryptoKittiesInCollectiblesButton(self.driver) + self.view_in_cryptokitties_button = ViewInCryptoKittiesButton(self.driver) self.set_currency_button = SetCurrencyButton(self.driver) self.add_account_button = AddAccountButton(self.driver) self.add_an_account_button = AddAnAccountButton(self.driver) diff --git a/test/cljs/status_im/react_native/js_dependencies.cljs b/test/cljs/status_im/react_native/js_dependencies.cljs index 0a11242d5e4d..89a8d326ae23 100644 --- a/test/cljs/status_im/react_native/js_dependencies.cljs +++ b/test/cljs/status_im/react_native/js_dependencies.cljs @@ -44,7 +44,9 @@ :clearTimeout js/clearTimeout :clearInterval js/clearInterval}) -(def keychain #js {:setGenericPassword (constantly (.resolve js/Promise true))}) +(def keychain #js {:setGenericPassword (constantly (.resolve js/Promise true)) + "ACCESSIBLE" {} + "ACCESS_CONTROL" {}}) (def react-navigation #js {:NavigationActions #js {}}) (def desktop-menu #js {}) (def desktop-config #js {}) diff --git a/translations/en.json b/translations/en.json index fbf6962afb7a..4fb0d33d0a41 100644 --- a/translations/en.json +++ b/translations/en.json @@ -341,7 +341,7 @@ "empty-chat-description-one-to-one": "Any messages you send here are encrypted and can only be read by you and ", "empty-chat-description-public": "It's been quiet here for the last {{quiet-hours}}. Start the conversation or ", "empty-chat-description-public-share-this": "share this chat.", - "enable": "enable", + "enable": "Enable", "encrypt-with-password": "Encrypt with password", "ens-10-SNT": "10 SNT", "ens-add-username": "Add username", @@ -1100,5 +1100,9 @@ "custom-seed-phrase-text-1": "This looks like a custom seed phrase and doesn't match the Status dictionary. This could also mean ", "custom-seed-phrase-text-2": "some words are misspelled.", "custom-seed-phrase-text-3": " If so, you'll end up creating a", - "custom-seed-phrase-text-4": " new account" + "custom-seed-phrase-text-4": " new account", + "to-enable-biometric": "To enable {{bio-type-label}}, your must save your password on the unlock screen", + "ok-save-pass": "OK, save password", + "lock-app-with": "Lock app with", + "grant-face-id-permissions": "To grant the required Face ID permission, please go to your system settings and make sure that Status > Face ID is selected" } diff --git a/translations/ko.json b/translations/ko.json index 011a2d24402e..2b83f1bf8903 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -26,6 +26,7 @@ "add-members": "멤버 추가", "add-network": "네트워크 추가", "add-to-contacts": "연락처에 추가", + "add-to-contacts-text": "특정 사용자를 연락처에 추가하면, 사용자의 지갑 주소를 공유하게 됩니다.", "address": "주소", "advanced": "고급 설정", "advanced-settings": "고급 설정", @@ -69,7 +70,7 @@ "blank-keycard-text": "키와 이름을 생성하면 키카드로 계속 진행할 수 있습니다.", "blank-keycard-title": "비어있는 키카드를\n사용하셨습니다", "block": "블록", - "block-contact": "연락처 차단", + "block-contact": "사용자 차단", "block-contact-details": "차단 기능은 해당 사용자의 이전 메시지를 모두 삭제하고, 새 메시지가 귀하에게 전송되지 못하도록 합니다", "blocked-users": "차단된 사용자", "bootnode-address": "Bootnode 주소", @@ -126,6 +127,7 @@ "check-your-recovery-phrase": "시드 구문 확인", "choose-authentication-method": "계정을 인증할 방법을 선택해주세요", "clear": "지우기", + "clear-all": "모두 지우기", "clear-history": "대화 내역 삭제", "clear-history-action": "지우기", "clear-history-confirmation": "대화내역 삭제", @@ -174,6 +176,7 @@ "create-multiaccount": "계정 만들기", "create-new-key": "신규 키 생성", "create-pin": "패스코드 만들기", + "create-pin-description": "로그인하고 거래를 확인하려면 카드와 패스코드가 필요합니다", "created-group-chat-description": "{{group-name}} 그룹을 생성했습니다", "creating-your-multiaccount": "계정 생성 중...", "cryptokitty-name": "CryptoKitty # {{id}}", @@ -262,6 +265,11 @@ "current-pin-description": "계속 진행하려면 패스코드를 입력하세요", "custom": "사용자 정의", "custom-networks": "사용자 정의 네트워크", + "custom-seed-phrase": "커스텀 시드 구문", + "custom-seed-phrase-text-1": "커스텀 시드 구문이 스테이터스 딕셔너리와 일치하지 않습니다. 또한", + "custom-seed-phrase-text-2": "일부 단어의 철자가 잘못되었을 수 있습니다.", + "custom-seed-phrase-text-3": " 이 경우", + "custom-seed-phrase-text-4": "새 계정을 생성하게 됩니다.", "dapp": "디앱", "dapp-would-like-to-connect-wallet": "지갑에 연결합니다", "dapps": "디앱", @@ -547,6 +555,7 @@ "keycard-cancel-setup-text": "키카드 설치가 취소됩니다. 키카드를 사용하려면 설치를 완료해야 합니다. 설치를 취소하시겠습니까?", "keycard-cancel-setup-title": "위험 작업", "keycard-desc": "안드로이드 전용. 먼저 키카드가 있어야 합니다", + "keycard-existing-multiaccount": "이미 기기에 계정이 존재하여, 해당 계정을 복구할 수 없습니다.", "keycard-has-multiaccount-on-it": "이 카드에는 이미 등록된 계정이 있습니다. 정보를 수정하려면, 먼저 로그인하고 카드를 재설정해주세요", "keycard-onboarding-finishing-header": "마무리 작업 진행 중", "keycard-onboarding-intro-header": "키카드에 키 저장하기", @@ -699,6 +708,7 @@ "no-tokens-found": "토큰 없음", "node-info": "노드 정보", "node-unavailable": "사용 가능한 이더리움 노드가 없습니다.", + "node-version": "노드 버전", "nonce": "Nonce", "none": "없음", "not-applicable": "서명되지 않은 거래에 사용할 수 없음", @@ -770,6 +780,7 @@ "processing": "처리 중", "product-information": "제품 정보", "profile": "프로필", + "profile-details": "프로필 세부 사항", "public-chat": "오픈 채팅", "public-chats": "오픈 채팅", "public-group-status": "오픈 채팅", @@ -813,6 +824,8 @@ "remind-me-later": "나중에 알림", "remove": "제거", "remove-from-chat": "채팅에서 내보내기", + "remove-from-contacts": "연락처에서 제거", + "remove-from-contacts-text": "연락처에서 특정 사용자를 삭제해도, 사용자의 지갑 주소를 숨길 수는 없습니다.", "remove-network": "네트워크 삭제", "remove-token": "토큰 제거", "removed": "퇴장 됨", @@ -995,7 +1008,7 @@ "type-a-message": "메시지 입력...", "ulc-enabled": "ULC 사용", "unable-to-read-this-code": "코드를 읽을 수 없습니다.", - "unblock-contact": "연락처 차단 해제", + "unblock-contact": "사용자 차단 해제", "unknown-status-go-error": "알 수 없는 status-go 오류", "unlock": "잠금 해제", "unpair-card": "카드 페어링 해제", @@ -1013,7 +1026,7 @@ "v1-messages-warning-title": "경고, 테스트 중인 기능임", "validation-amount-invalid-number": "잘못된 수량입니다", "validation-amount-is-too-precise": "액수가 너무 정확합니다. 최대 소수점 자리수는 {{decimals}}입니다.", - "version": "버전", + "version": "앱 버전", "view-cryptokitties": "CryptoKitties에서 보기", "view-cryptostrikers": "CryptoStrikers에서보기", "view-etheremon": "Etheremon에서보기", @@ -1073,9 +1086,10 @@ "you-are-all-set-description": "이제 스마트폰을 분실 한 경우, 시드 구문을 사용하여 계정과 지갑을 복원 할 수 있습니다.", "you-can-change-account": "계정 이름과 색상을 원하는대로 변경할 수 있습니다", "you-dont-have-stickers": "아직 이모티콘이 없습니다", + "you-will-need-this-code": "스테이터스 및 거래 서명을 위해 이 코드가 필요합니다", "your-contact-code": "디앱에 접근 권한을 부여하여 채팅 키를 검색합니다", "your-data-belongs-to-you": "시드 구문을 잃어 버리면 데이터와 자산을 분실하게 됩니다", "your-data-belongs-to-you-description": "스테이터스는 사용자가 시드 시드 구문을 잃어버릴 경우, 계정을 복구하는 것을 도와드릴 수 없습니다. 데이터 보안을 위해 시드 구문을 따로 백업해 두시는 것을 권장합니다.", "your-recovery-phrase": "시드 구문", "your-recovery-phrase-description": "위 12단어가 사용자의 시드 구문입니다. 이 구문은 사용자의 지갑을 증명하기 위해 반드시 필요하며, 이번 한번만 확인할 수 있습니다. 지갑을 분실하거나 재설치하는 경우 반드시 필요하므로 안전한 장소에 보관하세요." -} +} \ No newline at end of file