Compare commits

..
Author SHA1 Message Date
Ulises M 747d6bb128 WIP: debugging 2024-02-13 13:30:46 -06:00
Ulises M a29264a1a1 Reimplement avatar animations using new worklet approach 2024-02-12 15:06:35 -06:00
Ulises M 1386bd4777 Add worklet translation to shadow-cljs 2024-02-12 15:06:03 -06:00
391 changed files with 11110 additions and 5054 deletions
-1
View File
@@ -34,4 +34,3 @@ STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
TEST_STATEOFUS=1
FAST_CREATE_COMMUNITY_ENABLED=1
FLAG_NEW_CONTACT_UI_ENABLED=0
-3
View File
@@ -194,6 +194,3 @@ test/appium/tests/users.py
## git hooks
lefthook.yml
## build time logs
/logs/*.log
+25 -11
View File
@@ -36,8 +36,6 @@ endif
export TMPDIR = /tmp/tmp-status-mobile-$(BUILD_TAG)
# This has to be specified for both the Node.JS server process and the Qt process.
export REACT_SERVER_PORT ?= 5001
# Default metro port used by scripts/run-android.sh.
export RCT_METRO_PORT ?= 8081
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
export NODE_OPTIONS += --openssl-legacy-provider
# The path can be anything, but home is usually safest.
@@ -46,7 +44,7 @@ export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
# Our custom config is located in nix/nix.conf
export NIX_USER_CONF_FILES = $(PWD)/nix/nix.conf
# Location of symlinks to derivations that should not be garbage collected
export _NIX_GCROOTS = ./.nix-gcroots
export _NIX_GCROOTS = /nix/var/nix/gcroots/per-user/$(USER)/status-mobile
# Defines which variables will be kept for Nix pure shell, use semicolon as divider
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,\
BUILD_TYPE,BUILD_NUMBER,COMMIT_HASH,\
@@ -56,6 +54,12 @@ export _NIX_KEEP ?= TMPDIR,BUILD_ENV,\
# Useful for Android release builds
TMP_BUILD_NUMBER := $(shell ./scripts/version/gen_build_no.sh | cut -c1-10)
# MacOS root is read-only, read nix/README.md for details
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
export NIX_IGNORE_SYMLINK_STORE=1
endif
#----------------
# Nix targets
#----------------
@@ -210,7 +214,7 @@ build-fdroid: ##@build Build release for F-Droid
build-android: export BUILD_ENV ?= prod
build-android: export BUILD_TYPE ?= nightly
build-android: export ORG_GRADLE_PROJECT_versionCode ?= $(TMP_BUILD_NUMBER)
build-android: export BUILD_NUMBER ?= $(TMP_BUILD_NUMBER)
build-android: export ANDROID_ABI_SPLIT ?= false
build-android: export ANDROID_ABI_INCLUDE ?= armeabi-v7a;arm64-v8a;x86
build-android: ##@build Build unsigned Android APK
@@ -263,7 +267,7 @@ run-clojure: ##@run Watch for and build Clojure changes for mobile
run-metro: export TARGET := clojure
run-metro: ##@run Start Metro to build React Native changes
@scripts/run-metro.sh
@scripts/start-react-native.sh
run-re-frisk: export TARGET := clojure
run-re-frisk: ##@run Start re-frisk server
@@ -274,15 +278,22 @@ run-android: export TARGET := android
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
run-android: export ORG_GRADLE_PROJECT_hermesEnabled := false
# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set.
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh)
run-android: ##@run Build Android APK and start it on the device
@scripts/run-android.sh
npx react-native run-android --appIdSuffix debug
SIMULATOR=iPhone 13
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
run-ios: export TARGET := ios
run-ios: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
run-ios: ##@run Build iOS app and start it in on the simulator
@scripts/run-ios.sh "${SIMULATOR}"
run-ios: export XCBeautify=$(shell which xcbeautify) # for react-native-cli to pick this up and to auto format output
run-ios: ##@run Build iOS app and start it in a simulator/device
ifneq ("$(SIMULATOR)", "")
npx react-native run-ios --simulator="$(SIMULATOR)"
else
npx react-native run-ios
endif
show-ios-devices: ##@other shows connected ios device and its name
xcrun xctrace list devices
@@ -290,8 +301,11 @@ show-ios-devices: ##@other shows connected ios device and its name
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
run-ios-device: export TARGET := ios
run-ios-device: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
run-ios-device: ##@run iOS app and start it on the first connected iPhone
@scripts/run-ios-device.sh
run-ios-device: ##@run iOS app and start it on a connected device by its name
ifndef DEVICE_NAME
$(error Usage: make run-ios-device DEVICE_NAME=your-device-name)
endif
react-native run-ios --device "$(DEVICE_NAME)"
#--------------
# Tests
@@ -359,7 +373,7 @@ test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connection
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests\.contract-test).*-test$$
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests-im\.contract-test).*-test$$
test-unit: ##@test Run unit tests
test-unit: _test-clojure
+3 -3
View File
@@ -37,7 +37,7 @@ project dependencies, coding guidelines and testing procedures.
Check out our [coding guidelines](doc/new-guidelines.md).
- **Community Management**
Metcalfe's law states that the value of a network is proportional to the square of the number of connected users of the system - without community Status is meaningless. We're looking to create a positive, fun environment to explore new ideas, experiment and grow the Status community. Building a community takes a lot of work but the people you'll meet and the long-lasting relationships you form will be well worth it, check out our [Mission and Community Principles](https://status.app/manifesto)
Metcalfe's law states that the value of a network is proportional to the square of the number of connected users of the system - without community Status is meaningless. We're looking to create a positive, fun environment to explore new ideas, experiment and grow the Status community. Building a community takes a lot of work but the people you'll meet and the long-lasting relationships you form will be well worth it, check out our [Mission and Community Principles](https://status.im/about)
- **Specification / Documentation**
John Dewey once said, "Education is not preparation for life; education is life *itself* ". Developers and Designers need guidance and it all starts from documentation and specifications. Our software is only as good as its documentation, check out our [docs](doc/) and see how you can improve what we have.
@@ -53,10 +53,10 @@ Status is a visual interface to make permanent changes on the Blockchain, it han
- **Evangelism**
Help us spread the word! Tell a friend *right now*, in fact, tell **everyone** - yell from a mountain if you have to, every person counts! If you've got a great story to tell or have some interesting way you've spread the word about Status let us know about it in our [chat](https://join.status.im/chat/public/status)
## Give me Binaries!
You can get our Beta builds for both Android and iOS on our [website](https://status.app), through our [nightly builds](https://status.im/nightly/), or by [building it yourself](doc/starting-guide.md).
You can get our Beta builds for both Android and iOS on our [website](https://status.im), through our [nightly builds](https://status.im/nightly/), or by [building it yourself](https://status.im/technical/build_status/).
## Core Contributors
+3 -3
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.19' }
agent { label 'linux && x86_64 && nix-2.14' }
options {
timestamps()
@@ -15,7 +15,7 @@ pipeline {
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '20',
artifactNumToKeepStr: '1',
artifactNumToKeepStr: '10',
))
/* Allows combined build to copy */
copyArtifactPermission('/status-mobile/*')
+7 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
@@ -13,7 +13,7 @@ pipeline {
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
artifactNumToKeepStr: '10',
))
}
@@ -35,6 +35,8 @@ pipeline {
stage('Prep') {
steps { script {
println("Current JOB: ${env.JOB_NAME}")
/* just for a shorter access */
btype = utils.getBuildType()
} }
}
stage('Build') {
@@ -82,8 +84,10 @@ pipeline {
when { expression { params.PUBLISH } }
steps { script {
switch (btype) {
case 'nightly': /* Create JSON file with newest build URLs */
case 'nightly':
/* Create JSON file with newest build URLs */
s3.updateBucketJSON(urls, 'latest.json');
build(job: 'website/status.im', wait: false);
break;
case 'release':
github.publishReleaseFiles(repo: 'status-mobile');
+1 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
@@ -23,7 +23,6 @@ pipeline {
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
))
}
+6 -4
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'macos && arm64 && nix-2.19 && xcode-15.1' }
agent { label 'macos && arm64 && nix-2.14 && xcode-15.1' }
parameters {
string(
@@ -23,7 +23,7 @@ pipeline {
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '20',
artifactNumToKeepStr: '1',
artifactNumToKeepStr: '10',
))
/* Allows combined build to copy */
copyArtifactPermission('/status-mobile/*')
@@ -39,6 +39,8 @@ pipeline {
LANGUAGE = "en_US.UTF-8"
TARGET = 'ios'
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
FASTLANE_DISABLE_COLORS = 1
BUNDLE_PATH = "${HOME}/.bundle"
}
@@ -63,7 +65,7 @@ pipeline {
}
post {
failure {
archiveArtifacts 'logs/*'
archiveArtifacts 'ios/logs/*'
}
}
}
+4 -12
View File
@@ -1,21 +1,21 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.19' }
agent { label 'linux && x86_64 && nix-2.14' }
options {
timestamps()
/* Prevent Jenkins jobs from running forever */
timeout(time: 20, unit: 'MINUTES')
timeout(time: 15, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '20',
artifactNumToKeepStr: '1',
artifactNumToKeepStr: '10',
))
/* Abort old PR builds. */
disableConcurrentBuilds(
@@ -62,14 +62,6 @@ pipeline {
}
}
}
stage('Contract Tests') {
steps {
sh """#!/bin/bash
set -eo pipefail
make test-contract 2>&1 | tee -a ${LOG_FILE}
"""
}
}
stage('Integration Tests') {
steps {
sh """#!/bin/bash
+1 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
@@ -29,11 +29,6 @@ pipeline {
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
))
}
stages {
+1 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
@@ -47,11 +47,6 @@ pipeline {
options {
disableConcurrentBuilds()
timeout(time: 90, unit: 'MINUTES')
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
))
}
stages {
+1 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
@@ -28,11 +28,6 @@ pipeline {
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(
numToKeepStr: '10',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
))
}
+3 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'macos' }
@@ -10,6 +10,8 @@ pipeline {
LC_ALL = 'en_US.UTF-8'
TARGET = 'ios'
FASTLANE_DISABLE_COLORS = 1
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
/* avoid writing to r/o /nix */
GEM_HOME = '~/.rubygems'
}
+4 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label params.AGENT_LABEL }
@@ -18,6 +18,8 @@ pipeline {
}
environment {
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
/* we source .bash_profile to be able to use nix-store */
NIX_SSHOPTS = "-oStrictHostKeyChecking=no"
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
@@ -34,7 +36,6 @@ pipeline {
buildDiscarder(logRotator(
numToKeepStr: '20',
daysToKeepStr: '30',
artifactNumToKeepStr: '1',
))
}
@@ -81,7 +82,7 @@ pipeline {
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, 'android')
/* Build/fetch deps required to build android release. */
nix.build(
attr: 'targets.mobile.android.build.buildInputs',
attr: 'targets.mobile.android.release.buildInputs',
sandbox: false,
pure: false,
link: false
+3 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
@@ -10,6 +10,8 @@ pipeline {
LC_ALL = 'en_US.UTF-8'
TARGET = 'ios'
FASTLANE_DISABLE_COLORS = 1
/* See nix/README.md */
NIX_IGNORE_SYMLINK_STORE = 1
/* avoid writing to r/o /nix */
GEM_HOME = '~/.rubygems'
}
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.7'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent {
-4
View File
@@ -10,8 +10,6 @@
[Coding guidelines](new-guidelines.md)
[UI components coding guidelines](ui-guidelines.md)
[Release Checklist](release-checklist.md)
[Release Guide](release-guide.md)
@@ -30,8 +28,6 @@
[Contributing to status-go](status-go-changes.md)
[Malli schemas (recorded demo)](https://www.youtube.com/watch?v=SlRio70aYVI) ([slides](files/forging-code-with-schemas-sep-2023-slides.pdf))
## Testing
[How to run local tests](testing.md)
+2 -1
View File
@@ -122,9 +122,10 @@ See below:
Do the following:
Ensure you have 2 terminals running the following
Ensure you have 3 terminals running the following
- `make run-clojure`
- `make run-metro`
- `make run-ios` / `make run-android`
[See the STARTING GUIDE for details](STARTING_GUIDE.md#development)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

+23 -37
View File
@@ -4,41 +4,22 @@ This document provides information on how to start developing Status App.
# Getting Started
To start developing clone the status-mobile repo in the directory of your choice.
To start developing start a shell for the platform you are interested in.
```
git clone https://github.com/status-im/status-mobile.git
make shell TARGET=android
```
This step will take a while the first time as it will download all dependencies.
Then open a terminal and cd into this directory.
# Development
```
cd status-mobile
```
There are three steps necessary to start development, in this case for Android:
Then build the clojure terminal
1. `make run-clojure` - Compiles Clojure into JavaScript, watches for changes on cljs files, and hot-reloads code in the app.
2. `make run-metro` - Starts metro bundler and watches JavaScript code.
3. `make run-android` or `make run-ios` - Builds the Android/iOS app and starts it on the device.
```
make run-clojure
```
note: ⚠️ This might take a while if this is your first time.
This command installs `nix` and pulls in all the dependencies.
Do answer with "Y" to all the prompts and press "Enter" when `nix` setup asks you to Acknowledge.
This command builds the `jsbundle` and then compiles `Clojure` into `JavaScript`, watches for changes on `cljs` files, and hot-reloads code in the app.
wait till you see the following message :
```
[:mobile] Build completed. (1801 files, 52 compiled, 0 warnings, 9.52s)
```
Once the clojure terminal is running you need to run the appropriate command next for your platform in a separate terminal :
`make run-android` or `make run-ios`
These commands will build the app, start a metro bundler and deploy the app on your simulator OR connected device (android only). For building and deploying to connected iPhones use `make run-ios-device` instead of `make run-ios`
Also check [developing on a physical iOS Device](#Additional-requirements-for-developing-on-physical-ios-device).
The first two will continue watching for changes and keep re-building the app. They need to be ready first.
The last one will exit once the app is up and ready.
## Simulators and Devices
### Android
@@ -65,9 +46,13 @@ Running `make run-ios` will target `iPhone 13` by default.
If you need to run on any other simulator, you can specify the simulator type by adding the `SIMULATOR` flag:
```sh
make run-ios SIMULATOR="iPhone 15"
make run-ios SIMULATOR="iPhone 13"
```
#### Running on a physical device
Some manual steps are necessary for [developing on a physical iOS Device](#physical-ios-device).
# Build release
To build the app, you can simply run on of the following:
@@ -117,15 +102,16 @@ Steps:
3. [Setup Git to sign commits](https://help.github.com/en/github/authenticating-to-github/signing-commits)
4. [Setup GitHub to validate commits](https://help.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account)
## Additional requirements for developing on Physical iOS Device
## Physical iOS Device
To use a physical iPhone your device UDID must be added to provisioning profiles and your Apple account invited as Developer to Status team.
1. [Get your UDID of your iPhone.](https://www.extentia.com/post/finding-the-udid-of-an-ios-device)
2. Request from someone with access like @cammellos or @jakubgs to
- Add the UDID to development devices on Apple Developer Portal.
- Invite your Apple account to be Developer in Status team.
3. Open XCode using the project from `status-mobile/ios` directory.
- You might see error: `Select a development team in the Signing & Capabilities editor`
- Select `Status Research & Development GmbH` as the development team.
4. In a new terminal execute `make clean` and then `make xcode-clean`
- Add the UDID to development devices on Apple Developer Portal.
- Invite your Apple account to be Developer in Status team.
3. Run a build in XCode using the project from `status-mobile/ios` directory.
- You might see error: `Select a development team in the Signing & Capabilities editor`
- Select `Status Research & Development GmbH` as the development team and rebuild again.
Once build finishes Status should start on your iPhone with its logs in terminal running `make run-metro`.
+6 -32
View File
@@ -2,12 +2,14 @@
## Unit & integration tests
To run all tests:
To run tests:
```
make test
```
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
```
@@ -49,46 +51,18 @@ Here I'm showing a terminal-only experience using Tmux (left pane Emacs, right p
[2022-12-19 13-17.webm](https://user-images.githubusercontent.com/46027/208471199-1909c446-c82d-42a0-9350-0c15ca562713.webm)
## Component tests only
## Component tests
To run tests:
```
make component-test
make component-test
```
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
```
make component-test-watch
make component-test-watch
```
Check [component tests doc](./component-tests-overview.md) for more.
## Unit tests only
To run unit tests:
```
make test-unit
```
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
```
make test-unit WATCH=true
```
## Integration tests only
To run integration tests:
```
make test-integration
```
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
```
make test-integration WATCH=true
```
+20 -1
View File
@@ -88,7 +88,6 @@ info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this
Update yarn.lock file. In order to do this, perform the following steps on a clean `status-mobile` repo:
```
cd status-mobile
make shell
yarn install
```
and don't forget to commit updated `yarn.lock` together with `package.json`.
@@ -128,3 +127,23 @@ Status-mobile uses `shadow-cljs` for hot reloading changes and uses its own [rel
### Solution
Open react native's [In-App Developer Menu](https://reactnative.dev/docs/debugging#accessing-the-in-app-developer-menu) and press "Disable Fast Refresh" or "Disable Hot Reloading"
## App Crashes after few reloads
### Cause
For x86 CPU architecture Android Devices, Hermes is creating the issue and the app crashes after a few reloads.
([Original Issue](https://github.com/status-im/status-mobile/issues/14031))
<details>
<summary>How to Find CPU architecture</summary>
CPU architecture of android device can be found using
- `adb shell uname -m` or
- `adb shell getprop ro.product.cpu.abilist`
</details>
### Solution
Disable Hermes while building the app
`make run-android DISABLE_HERMES=true`
-254
View File
@@ -1,254 +0,0 @@
# UI components coding guidelines
> [!IMPORTANT]
> React apps are made out of components. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire screen.
> React components are JavaScript functions that return markup
> This document will provide best practices on how to write efficient React components in ClojureScript
At the time of creating the Status app, the Reagent library was a solid choice. Back then, hooks didn't exist, and there weren't any libraries providing effective global state management. After Reagent's emergence, another library called Re-frame built upon Reagent. Together, they offered powerful tools for developing React applications with ClojureScript. However, as React evolved, significant changes occurred. Class components, utilized in Reagent, became deprecated. Instead, functional components and hooks emerged for state management. In Status 2.0, we began incorporating more functional components and hooks, resulting in a blend of both approaches. To simplify matters and reduce confusion, we opted to transition to functional components and hooks for local state management.
BEFORE:
```clojure
(defn- view-internal
[_ _]
(let [pressed? (reagent/atom false)]
(fn
[{:keys [theme on-press on-long-press icon]}]
[rn/pressable
{:style (style/main @pressed? theme)
:on-press on-press
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? nil)
:on-long-press on-long-press}
[quo.icons/icon icon]])))
(def view (theme/with-theme view-internal))
```
NOW:
```clojure
(defn view
[{:keys [on-press on-long-press icon]}]
(let [[pressed? set-pressed] (rn/use-state false)
theme (theme/use-theme-value)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed nil))]
[rn/pressable
{:style (style/main pressed? theme)
:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out
:on-long-press on-long-press}
[quo.icons/icon icon]]))
```
- We no longer need to create an anonymous function for rendering. This removes unnecessary confusion and the need for specific knowledge on how it works and why it was needed.
- `rn/use-state` is used instead of `reagent/atom`
- State values no longer need to be dereferenced; they are accessible as regular symbols. This eliminates a common bug where the "@" symbol was inadvertently omitted.
- `theme/with-theme` wrapper is not needed anymore, `(theme/use-theme-value)` hook can be used directly in the components
- `:f>` not needed anymore, all components are functional by default
- `rn/use-callback` hook should be used for anon callback functions
> [!IMPORTANT]
> DO NOT USE anon functions directly in the props
BAD
```clojure
(defn view
[]
(let [[pressed? set-pressed] (rn/use-state false)]
[rn/pressable
{:style (style/main pressed?)
:on-press-in #(set-pressed true)
:on-press-out #(set-pressed nil)}]))
```
GOOD:
```clojure
(defn view
[]
(let [[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed nil))]
[rn/pressable
{:style (style/main pressed?)
:on-press-in on-press-in
:on-press-out on-press-out}]))
```
## Global state and subscriptions
For global state management, we utilize Re-frame subscriptions. They can be likened to React state. To obtain the state, `(rf/sub [])` is employed, and to modify it, `(rf/dispatch [])` is utilized. However, they update components in a similar manner to React states.
```clojure
(defn view
[{:keys [selected-tab]}]
(let [collectible-list (rf/sub [:wallet/all-collectibles])
on-collectible-press (rn/use-callback
(fn [{:keys [id]}]
(rf/dispatch [:wallet/get-collectible-details id])))]
[rn/view {:style style/container}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles/view {:collectibles collectible-list
:on-collectible-press on-collectible-press}])
[activity/view]]))
```
## Regular atoms
In certain instances, components utilized regular atoms; however, they should now be used with `rn/use-ref-atom`
BEFORE:
```clojure
(defn view
[]
(let [focused? (atom false)]
(fn []
(let [on-clear #(reset! status (if @focused? :active :default))
on-focus #(reset! focused? true)
on-blur #(reset! focused? false)]))))
```
NOW:
```clojure
(defn view
[]
(let [focused? (rn/use-ref-atom false)
on-clear (rn/use-callback #(set-status (if @focused? :active :default)))
on-focus (rn/use-callback #(reset! focused? true))
on-blur (rn/use-callback #(reset! focused? false))]))
```
## Effects
LIFECYCLE:
```clojure
(defn view
[{:keys []}]
(let [opacity (reanimated/use-shared-value 0)]
(rn/use-mount #(reanimated/animate opacity 1))
[rn/view
{:style (style/opacity opacity)}]))
```
```clojure
(defn view
[{:keys []}]
(let []
(rn/use-unmount #(rn/dispatch [:unmounted]))
[rn/view]))
```
> [!IMPORTANT]
> Effects should NOT be utilized as a response to state changes for modifying logic. If you're unsure how to achieve this without using effects, please consult the team in the general chat. There may be instances where using effects is appropriate, so we can explore a solution together and enhance our guidelines.
BAD:
```clojure
(defn f-zoom-button
[{:keys [selected? current-zoom]}]
(let [size (reanimated/use-shared-value (if selected? 37 25))]
(rn/use-effect #(reanimated/animate size (if selected? 37 25)) [current-zoom])
[rn/touchable-opacity
{:style (style/zoom-button-container size)}]))
```
BAD:
```clojure
(defn view
[collectible-list (rf/sub [:wallet/all-collectibles])]
(let []
(rn/use-effect #(rn/dispatch [:all-collectibles-changed]) [collectible-list])
[rn/view]))
```
Instead `:all-collectibles-changed` should be used in the handler which changes `collectible-list` state
## Performance tips
To begin with, we need to understand that there are two distinct stages for a component: creation and update. React creates a render tree, a UI tree, composed of the rendered components.
![react_tree.png](react_tree.png)
### Component creation
For component creation, the most critical factor is the number of elements involved, so we should strive to minimize them. For instance, it's advisable to avoid using unnecessary wrappers or containers.
BAD:
```clojure
(defn view
[]
(let []
[rn/view {:style {:padding-top 20}}
[quo/button]]))
```
GOOD:
```clojure
(defn view
[]
(let []
[quo/button {:container-style {:padding-top 20}}]))
```
### Component updates
For component updates, it's crucial to recognize that React will invoke the function where state is utilized. Therefore, if you utilize state in the root component, React will execute the root function and re-render the entire root component along with all its children (unless optimizations like memoization are implemented).
BAD:
```clojure
(defn component
[{:keys [label]}]
(let []
[rn/text label]))
(defn component2
[{:keys [label2]}]
(let []
[rn/text label2]))
(defn screen
[]
(let [screen-params (rf/sub [:screen-params])]
[component screen-params]
[component1]
[component2 screen-params]
[component3]
[rn/view {:padding-top 20}
[quo/button]]))
```
Here, we have lost control over the `screen-params` map. It can contain any data, and if any field within this map changes, the entire screen function will be executed, resulting in the re-rendering of both `component` and `component2`.
GOOD:
```clojure
(defn component
[]
(let [label (rf/sub [:screen-params-label])]
[rn/text label]))
(defn component2
[]
(let [label2 (rf/sub [:screen-params-label2])]
[rn/text label2]))
(defn screen
[]
(let []
[component]
[component1]
[component2]
[component3]
[rn/view {:padding-top 20}
[quo/button]]))
```
So, now the screen component function will never be invoked, and `component` and `component2` will be re-rendered only when `label` or `label2` have changed.
+2 -3
View File
@@ -1,5 +1,4 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
@@ -97,7 +96,7 @@ end
# if false - read list of devices from AppStoreConnect, and upgrade the provisioning profiles from it
# `pr_build`:
# if true - uses StatusImPR scheme and postfixed app id with `.pr` to build an app, which can be used in parallel with release
# if false - uses StatusIm scheme to build the release app
# if false - uses StatusIm scheme to build the release app
def build_ios_adhoc(readonly: false, pr_build: false)
# PR builds should appear as a separate App on iOS
@@ -124,7 +123,7 @@ def build_ios_adhoc(readonly: false, pr_build: false)
export_method: 'ad-hoc',
output_name: 'StatusIm',
output_directory: 'status-ios',
buildlog_path: 'logs',
buildlog_path: 'ios/logs',
export_options: {
signingStyle: 'manual',
provisioningProfiles: {
+1 -1
View File
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
:submodules: true
SPEC CHECKSUMS:
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
boost: 57d2868c099736d80fcd648bf211b4431e51a558
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
+5 -5
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@@ -542,7 +542,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/usr/bin/env sh";
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ../logs/react-native-xcode.log 2>&1";
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
};
28606042DA81A750949525E4 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
@@ -578,7 +578,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/usr/bin/env sh";
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ../logs/react-native-xcode.log 2>&1";
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
};
3AAD2AD724A3A60E0075D594 /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
@@ -592,7 +592,7 @@
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = "/usr/bin/env sh";
shellScript = "\"${PROJECT_DIR}/scripts/set_xcode_version.sh\" > ../logs/set_xcode_version.log 2>&1";
shellScript = "\"${PROJECT_DIR}/scripts/set_xcode_version.sh\" > ./logs/set_xcode_version.log 2>&1";
};
523CD6BCB20FD78FBF3564AD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@@ -716,7 +716,7 @@
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = "/usr/bin/env sh";
shellScript = "\"${PROJECT_DIR}/scripts/set_xcode_version.sh\" > ../logs/set_xcode_version.log 2>&1";
shellScript = "\"${PROJECT_DIR}/scripts/set_xcode_version.sh\" > ./logs/set_xcode_version.log 2>&1";
};
/* End PBXShellScriptBuildPhase section */
+1 -3
View File
@@ -74,8 +74,6 @@
</dict>
<key>NSCameraUsageDescription</key>
<string>Status uses camera to take pictures and scan QR codes.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Status needs access to the local network to give you the ability to connect with peers.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Location access is required for some DApps to function properly.</string>
<key>NSLocationWhenInUseUsageDescription</key>
@@ -135,7 +133,7 @@
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<true/>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A00000080400010101</string>
+14
View File
@@ -0,0 +1,14 @@
# Description
This directory is the destination of logs created by Fastlane and React Native Xcode script.
# Logs
* `react-native-xcode.log` - Output from `node_modules/react-native/scripts/react-native-xcode.sh`.
- Created by redirecting output of `shellScript` in `ios/StatusIm.xcodeproj/project.pbxproj`.
* `Status PR-StatusImPR.log - Created by [Fastlane Gym](https://docs.fastlane.tools/actions/gym/).
- Configred via [`Fastfile`](../Fastfile) using `buildlog_path` argument for `build_ios_app`.
# CI
These log files are uploaded to Jenkins as CI job artifacts on failure using `archiveArtifacts`.
-27
View File
@@ -1,27 +0,0 @@
# Description
This directory is the destination of logs created during build time of debug builds and logs created by Fastlane on CI.
# Logs
* `xcrun_device_install.log` - Output from `status-mobile/scripts/run-ios-device.sh`.
- Created by redirecting output of `xcrun simctl install "$UDID" "$APP_PATH"`.
* `xcrun_device_process_launch.log` - Output from `status-mobile/scripts/run-ios-device.sh`.
- Created by specifying `--json-output` flag for `xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}"`.
* `xcrun_device_process_resume.log` - Output from `status-mobile/scripts/run-ios-device.sh`.
- Created by redirecting output of `xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}"`.
* `adb_install.log` - Output from `scripts/run-android.sh`.
- Created by redirecting output of `adb install -r ./result/app-debug.apk`.
* `adb_shell_monkey.log` - Output from `status-mobile/scripts/run-android.sh`.
- Created by redirecting output of `adb shell monkey -p im.status.ethereum.debug 1 >`.
* `ios_simulators_list.log` - Output from `status-mobile/scripts/run-ios.sh`.
- Created by redirecting output of `xcrun simctl list devices -j`.
# CI
These log files are uploaded to Jenkins as CI job artifacts on failure using `archiveArtifacts`.
* `react-native-xcode.log` - Output from `node_modules/react-native/scripts/react-native-xcode.sh`.
- Created by redirecting output of `shellScript` in `ios/StatusIm.xcodeproj/project.pbxproj`.
* `Status PR-StatusImPR.log - Created by [Fastlane Gym](https://docs.fastlane.tools/actions/gym/).
- Configred via [`Fastfile`](../Fastfile) using `buildlog_path` argument for `build_ios_app`.
@@ -342,14 +342,4 @@ public class AccountManager extends ReactContextBaseJavaModule {
final String keyStoreDir = this.utils.getKeyStorePath(keyUID);
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.deleteMultiaccount(keyUID, keyStoreDir), callback);
}
@ReactMethod
public void getRandomMnemonic(final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.getRandomMnemonic(), callback);
}
@ReactMethod
public void createAccountFromMnemonicAndDeriveAccountsForPaths(final String mnemonic, final Callback callback) throws JSONException {
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.createAccountFromMnemonicAndDeriveAccountsForPaths(mnemonic), callback);
}
}
@@ -214,20 +214,4 @@ RCT_EXPORT_METHOD(logout) {
NSLog(@"%@", result);
}
RCT_EXPORT_METHOD(getRandomMnemonic:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"GetRandomMnemonic() method called");
#endif
NSString *result = StatusgoGetRandomMnemonic();
callback(@[result]);
}
RCT_EXPORT_METHOD(createAccountFromMnemonicAndDeriveAccountsForPaths:(NSString *)mnemonic callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"createAccountFromMnemonicAndDeriveAccountsForPaths() method called");
#endif
NSString *result = StatusgoCreateAccountFromMnemonicAndDeriveAccountsForPaths(mnemonic);
callback(@[result]);
}
@end
@@ -4,25 +4,10 @@
#import "Statusgo.h"
#import "Utils.h"
@interface NetworkManager() <NSNetServiceBrowserDelegate>
@property (nonatomic, strong) NSNetServiceBrowser *serviceBrowser;
@property (nonatomic, copy) RCTResponseSenderBlock callback;
@end
@implementation NetworkManager
RCT_EXPORT_MODULE();
- (instancetype)init {
if ((self = [super init])) {
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
self.serviceBrowser.delegate = self;
}
return self;
}
RCT_EXPORT_METHOD(startSearchForLocalPairingPeers:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoStartSearchForLocalPairingPeers();
callback(@[result]);
@@ -110,39 +95,6 @@ RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
});
}
RCT_EXPORT_METHOD(requestLocalNetworkAccess:(RCTResponseSenderBlock)callback) {
self.callback = callback;
// Start browsing for services using TCP on the local network.
dispatch_async(dispatch_get_main_queue(), ^{
[self.serviceBrowser searchForServicesOfType:@"_http._tcp." inDomain:@""];
});
}
// NSNetServiceBrowser delegate method
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary<NSString *,NSNumber *> *)errorDict {
RCTLogInfo(@"Failed to search for services: %@", errorDict);
if (self.callback) {
self.callback(@[@"Failed to search for services"]);
}
}
// NSNetServiceBrowser delegate method
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing {
RCTLogInfo(@"Found service: %@", service);
// we stop the search after finding the first service or wait until the search is finished.
[browser stop];
if (self.callback) {
self.callback(@[[NSNull null], @"Requested local network access and found service"]);
}
}
// NSNetServiceBrowser delegate method
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser {
RCTLogInfo(@"Stopped searching for services");
}
#pragma mark - Recover
RCT_EXPORT_METHOD(recover:(NSString *)message
@@ -120,12 +120,9 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(checkAddressChecksum:(NSString *)address)
return StatusgoCheckAddressChecksum(address);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isAddress:(NSString *)address) {
return StatusgoIsAddress(address);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toChecksumAddress:(NSString *)address) {
return StatusgoToChecksumAddress(address);
}
@end
+5 -7
View File
@@ -61,7 +61,7 @@ We will use the `make jsbundle` target as an example of a derivation you can bui
3. [`build.sh`](/nix/scripts/build.sh) calls `nix-build --attr targets.mobile.jsbundle` with extra arguments
4. `nix-build` builds the derivation from [`nix/mobile/jsbundle/default.nix`](/nix/mobile/jsbundle/default.nix)
The same can be done for other targets like `targets.mobile.android.build`.
The same can be done for other targets like `targets.mobile.android.release`.
Except in that case extra arguments are required which is why the [`scripts/release-android.sh`](/scripts/release-android.sh) is used in the `make release-android` target.
If you run `make release-android` you'll see the `nix-build` command used:
@@ -71,23 +71,21 @@ nix-build \
--fallback \
--no-out-link \
--show-trace \
--attr targets.mobile.android.build \
--attr targets.mobile.android.release \
--argstr secrets-file '/tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
--option extra-sandbox-paths '/home/joe/.gradle/status-im.keystore /tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
default.nix
```
Some of those are required which is why just calling:
```
nix-build --attr targets.mobile.android.build
nix-build --attr targets.mobile.android.release
```
Would fail.
# Garbage Collection
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `.nix-gcroots` folder. Specifically:
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `/nix/var/nix/gcroots` subfolder. Specifically:
```sh
_NIX_GCROOTS="${_NIX_GCROOTS:-${GIT_ROOT}/.nix-gcroots}
_NIX_GCROOTS="${_NIX_GCROOTS:-/nix/var/nix/gcroots/per-user/${USER}/status-mobile}"
```
These symlinks in turn will be symlinked from `/nix/var/nix/gcroots/auto` through use of `nix-store --add-gcroots`.
Whenever `nix/scripts/build.sh` or `nix/scripts/shell.sh` are called they update symlinks named after given targets in that folder. This in combination with `keep-outputs = true` set in `nix/nix.conf` prevents garbage collection from removing too much.
+1 -1
View File
@@ -28,7 +28,7 @@ export STATUS_GO_IPFS_GATEWAY_URL="https://ipfs.status.im/"
```
You can see the defaults in code:
- [`nix/status-go/default.nix`](./status-go/default.nix)
- [`nix/mobile/android/release.nix`](./mobile/android/build.nix)
- [`nix/mobile/android/release.nix`](./mobile/android/release.nix)
## Shell
+1 -1
View File
@@ -6,7 +6,7 @@ This directory contains the tools and the data that allows Nix to manage Gradle
Simply calling `generate.sh` should result in a `deps.json` file which is used in the derivation that provides Gradle dependencies when building the Android app.
You can see in [`nix/mobile/android/release.nix`](../../mobile/android/build.nix) that it's used via the `-Dmaven.repo.local='${deps.gradle}'` Gradle flag.
You can see in [`nix/mobile/android/release.nix`](../../mobile/android/release.nix) that it's used via the `-Dmaven.repo.local='${deps.gradle}'` Gradle flag.
# Files
+2 -2
View File
@@ -2,7 +2,7 @@
, jsbundle, status-go, androidPkgs, androidShell }:
rec {
build = callPackage ./build.nix {
release = callPackage ./release.nix {
inherit jsbundle status-go;
};
@@ -15,7 +15,7 @@ rec {
];
inputsFrom = [
(build {})
(release {})
androidShell
];
@@ -7,15 +7,15 @@
# Path to the file containing secret environment variables
secretsFile ? "",
# Build type (influences which .env file gets used for feature flags)
# TODO: pr or relase for default?
buildType ? lib.getEnvWithDefault "BUILD_TYPE" "release",
# Used for versionCode
versionCode ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_versionCode" 9999,
buildNumber ? lib.getEnvWithDefault "BUILD_NUMBER" 9999,
# Included in APK Manifest for easier identification.
commitHash ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_commitHash" "unknown",
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
hermesEnabled ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_hermesEnabled" "true",
buildUrl ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_buildUrl" null,
# TODO: or GIT_COMMIT from Jenkins?
commitHash ? lib.getEnvWithDefault "COMMIT_HASH" "unknown",
# Gradle options passed for Android builds
androidGradleOpts ? lib.getEnvWithDefault "ANDROID_GRADLE_OPTS" null,
statusGoSrcOverride ? lib.getEnvWithDefault "STATUS_GO_SRC_OVERRIDE" null,
# If APKs should be split based on architectures
androidAbiSplit ? lib.getEnvWithDefault "ANDROID_ABI_SPLIT" "false",
@@ -40,10 +40,8 @@ let
else if (elem buildType ["pr" "manual"]) then ".env.jenkins"
else ".env";
gradleBuildType =
if buildType == "pr" then "Pr"
else if buildType == "debug" then "Debug"
else "Release";
# There are only two types of Gradle build targets: pr and release
gradleBuildType = if buildType == "pr" then "Pr" else "Release";
apksPath = "./android/app/build/outputs/apk/${toLower gradleBuildType}";
@@ -83,10 +81,7 @@ in stdenv.mkDerivation rec {
ANDROID_ABI_INCLUDE = androidAbiInclude;
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
ORG_GRADLE_PROJECT_versionCode = versionCode;
ORG_GRADLE_PROJECT_commitHash = commitHash;
ORG_GRADLE_PROJECT_buildUrl = buildUrl;
ORG_GRADLE_PROJECT_hermesEnabled = hermesEnabled;
ORG_GRADLE_PROJECT_hermesEnabled = true;
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
NODE_OPTIONS = "--openssl-legacy-provider";
@@ -121,6 +116,8 @@ in stdenv.mkDerivation rec {
# Symlink React Native entrypoint.
cp -Lr ${builtJsBundle} ./result
pwd
find -L result
# Copy android/ directory
mkdir -p ./android/build
@@ -147,6 +144,7 @@ in stdenv.mkDerivation rec {
"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${makeLibraryPath [ pkgs.zlib ]}";
gradleCommand = ''
${pkgs.gradle}/bin/gradle \
${toString androidGradleOpts} \
--console=plain \
--offline \
--no-daemon \
@@ -154,6 +152,8 @@ in stdenv.mkDerivation rec {
--no-watch-fs \
--no-build-cache \
-Dmaven.repo.local='${deps.gradle}' \
-PversionCode=${toString buildNumber} \
-PcommitHash=${commitHash} \
assemble${gradleBuildType}
'';
in
@@ -171,14 +171,12 @@ in stdenv.mkDerivation rec {
${adhocEnvVars} ${gradleCommand}
popd > /dev/null
'';
doCheck = buildType != "debug";
checkPhase = ''
doCheck = true;
checkPhase = ''
ls ${apksPath}/*.apk \
| xargs -n1 ${pkgs.unzip}/bin/unzip -qql \
| grep 'index.android.bundle'
'';
installPhase = ''
mkdir -p $out
cp ${apksPath}/*.apk $out/
+1 -1
View File
@@ -20,8 +20,8 @@ in {
buildInputs = with pkgs; [
xcodeWrapper watchman procps
flock # used in nix/scripts/node_modules.sh
ios-deploy # used in 'make run-ios-device'
xcbeautify # used in 'make run-ios'
libimobiledevice # used in `make run-ios-device`
];
# WARNING: Executes shellHook in reverse order.
+12
View File
@@ -26,6 +26,18 @@ in {
react-native = callPackage ./deps/react-native { };
};
# Fix for missing libarclite_macosx.a in Xcode 14.3.
# https://github.com/ios-control/ios-deploy/issues/580
ios-deploy = super.darwin.ios-deploy.overrideAttrs (old: rec {
version = "1.12.2";
src = super.fetchFromGitHub {
owner = "ios-control";
repo = "ios-deploy";
rev = version;
sha256 = "sha256-TVGC+f+1ow3b93CK3PhIL70le5SZxxb2ug5OkIg8XCA";
};
});
# Clojure's linter receives frequent upgrades, and we want to take advantage
# of the latest available rules.
clj-kondo = super.clj-kondo.override rec {
+1 -1
View File
@@ -45,7 +45,7 @@ if [[ -z "${TARGET}" ]]; then
fi
# Hack fix for missing Android SDK for aarch64 on Darwin. See systemOverride in `nix/pkgs.nix`.
if [[ "${TARGET}" =~ ^(targets.status-go.mobile.android|targets.mobile.android.build)$ ]]; then
if [[ "${TARGET}" =~ ^(targets.status-go.mobile.android|targets.mobile.android.release)$ ]]; then
os=$(uname -s | tr '[:upper:]' '[:lower:]')
export NIXPKGS_SYSTEM_OVERRIDE="x86_64-${os}"
fi
+2 -2
View File
@@ -2,12 +2,12 @@
set -Ee
_NIX_GCROOTS="${_NIX_GCROOTS:-/nix/var/nix/gcroots/per-user/${USER}/status-mobile}"
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/nix/scripts/source.sh"
source "${GIT_ROOT}/scripts/colors.sh"
_NIX_GCROOTS="${_NIX_GCROOTS:-${GIT_ROOT}/.nix-gcroots}"
TARGET="${1}"
shift
if [[ -z "${TARGET}" ]]; then
+5 -3
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
export NIX_VERSION="2.19.3"
export NIX_PACKAGE="nixVersions.nix_2_19"
# WARNING: Versions 2.14, 2.15, 2.16 have an issue creating gcroots:
# https://github.com/NixOS/nix/issues/8564
export NIX_VERSION="2.13.4"
export NIX_PACKAGE="nixVersions.nix_2_13"
export NIX_INSTALL_URL="https://nixos.org/releases/nix/nix-${NIX_VERSION}/install"
export NIX_INSTALL_SHA256="73d47b0ab783fddca1b2d44a03d52a74c97c28a1fc8ff5a29419079302ef9c3d"
export NIX_INSTALL_SHA256="a9908cc48f5886b4f22172bdd2f9657873276fd295e78c6ed97fb308c6d284d0"
export NIX_INSTALL_PATH="/tmp/nix-install-${NIX_VERSION}"
+3 -1
View File
@@ -21,6 +21,8 @@ function append_env_export() {
VAR_VALUE=$(must_get_env "${VAR_NAME}")
echo "export ${VAR_NAME}=\"${VAR_VALUE}\";" >> "${SECRETS_FILE_PATH}"
}
export COMMIT_HASH="$(git rev-parse --verify HEAD)"
nixOpts=()
# We create if now so the trap knows its location
@@ -71,4 +73,4 @@ else
fi
"${GIT_ROOT}/nix/scripts/build.sh" targets.mobile.android.build "${nixOpts[@]}"
"${GIT_ROOT}/nix/scripts/build.sh" targets.mobile.android.release "${nixOpts[@]}"
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
ADB_INSTALL_LOG_FILE="${GIT_ROOT}/logs/adb_install.log"
ADB_SHELL_MONKEY_LOG_FILE="${GIT_ROOT}/logs/adb_shell_monkey.log"
# Generate android debug build.
export ANDROID_ABI_INCLUDE=$("${GIT_ROOT}/scripts/adb_devices_abis.sh")
export BUILD_ENV=debug
export BUILD_TYPE=debug
"${GIT_ROOT}/scripts/build-android.sh"
# Install the APK on running emulator or android device.
installAndLaunchApp() {
adb install -r ./result/app-debug.apk > "${ADB_INSTALL_LOG_FILE}" 2>&1
"${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1
# connected android devices need this port to be exposed for metro
adb reverse "tcp:${RCT_METRO_PORT}" "tcp:${RCT_METRO_PORT}"
adb shell monkey -p im.status.ethereum.debug 1 > "${ADB_SHELL_MONKEY_LOG_FILE}" 2>&1
}
showAdbLogs() {
cat "${ADB_INSTALL_LOG_FILE}" >&2;
cat "${ADB_SHELL_MONKEY_LOG_FILE}" >&2;
}
trap showAdbLogs EXIT ERR INT QUIT
installAndLaunchApp &
exec "${GIT_ROOT}/scripts/run-metro.sh" 2>&1
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
set -m # needed to access jobs
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
XCRUN_DEVICE_INSTALL_LOG_DIR="${GIT_ROOT}/logs/xcrun_device_install.log"
XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR="${GIT_ROOT}/logs/xcrun_device_process_launch.log"
XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR="${GIT_ROOT}/logs/xcrun_device_process_resume.log"
# Install on the connected device
installAndLaunchApp() {
xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output "${XCRUN_DEVICE_INSTALL_LOG_DIR}" 2>&1
# Extract installationURL
INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' "${XCRUN_DEVICE_INSTALL_LOG_DIR}")
# launch the app and put it in background
xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}"
# Extract background PID of status app
STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}")
"${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1
# now that metro is ready, resume the app from background
xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1
}
showXcrunLogs() {
cat "${XCRUN_DEVICE_INSTALL_LOG_DIR}" >&2;
cat "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" >&2;
cat "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" >&2;
}
# find the first connected iPhone's UUID
DEVICE_UUID=$(idevice_id -l)
# Check if any device is connected
if [ -z "${DEVICE_UUID}" ]; then
echo "No connected iPhone device detected."
exit 1
else
echo "Connected iPhone UDID: ${DEVICE_UUID}"
fi
BUILD_DIR="${GIT_ROOT}/build"
#iOS build of debug scheme
xcodebuild -workspace "ios/StatusIm.xcworkspace" -configuration Debug -scheme StatusIm -destination id="${DEVICE_UUID}" -derivedDataPath "${BUILD_DIR}" -verbose | xcbeautify
APP_PATH="${BUILD_DIR}/Build/Products/Debug-iphoneos/StatusIm.app"
trap showXcrunLogs EXIT ERR INT QUIT
installAndLaunchApp &
exec "${GIT_ROOT}/scripts/run-metro.sh" 2>&1
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
set -m # needed to access jobs
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
XCRUN_INSTALL_LOG_FILE="${GIT_ROOT}/logs/xcrun_install.log"
XCRUN_LAUNCH_LOG_FILE="${GIT_ROOT}/logs/xcrun_launch.log"
XCRUN_SIMULATOR_JSON_FILE="${GIT_ROOT}/logs/ios_simulators_list.log"
# Install on the simulator
installAndLaunchApp() {
xcrun simctl install "$UDID" "$APP_PATH" > "${XCRUN_INSTALL_LOG_FILE}" 2>&1
"${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1
xcrun simctl launch "$UDID" im.status.ethereum.debug > "${XCRUN_LAUNCH_LOG_FILE}" 2>&1
}
showXcrunLogs() {
cat "${XCRUN_INSTALL_LOG_FILE}" >&2;
cat "${XCRUN_LAUNCH_LOG_FILE}" >&2;
}
# Check if the first argument is provided
if [ -z "${1-}" ]; then
echo "Error: No simulator name provided." >&2
exit 1
fi
# fetch available iOS Simulators
xcrun simctl list devices -j > "${XCRUN_SIMULATOR_JSON_FILE}"
SIMULATOR=${1}
# get the first available UDID for Simulators that match the name
read -r UDID SIMULATOR_STATE IS_AVAILABLE < <(jq --raw-output --arg simulator "${SIMULATOR}" '
[ .devices[] | .[] | select(.name == $simulator) ] |
map(select(.isAvailable)) + map(select(.isAvailable | not)) |
first |
"\(.udid) \(.state) \(.isAvailable)"
' "${XCRUN_SIMULATOR_JSON_FILE}")
if [ "${IS_AVAILABLE}" == false ] || [ "${UDID}" == null ]; then
echo "Error: Simulator ${SIMULATOR} is not available, Please find and install them."
echo "For help please refer"
echo "https://developer.apple.com/documentation/safari-developer-tools/adding-additional-simulators#Add-and-remove-Simulators " >&2
exit 1
fi
# sometimes a simulator is already running, shut it down to avoid errors
if [ "${SIMULATOR_STATE}" != "Shutdown" ]; then
xcrun simctl shutdown "${UDID}"
fi
# boot up iOS for simulator
xcrun simctl boot "${UDID}"
# start the simulator
open -a Simulator --args -CurrentDeviceUDID "${UDID}"
BUILD_DIR="${GIT_ROOT}/build"
#iOS build of debug scheme
xcodebuild -workspace "ios/StatusIm.xcworkspace" -configuration Debug -scheme StatusIm -destination id="${UDID}" -derivedDataPath "${BUILD_DIR}" -verbose | xcbeautify
APP_PATH="${BUILD_DIR}/Build/Products/Debug-iphonesimulator/StatusIm.app"
trap showXcrunLogs EXIT ERR INT QUIT
installAndLaunchApp &
exec "${GIT_ROOT}/scripts/run-metro.sh" 2>&1
-5
View File
@@ -1,5 +0,0 @@
#!/usr/bin/env bash
pkill -f 'react-native start'
react-native start --reset-cache
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/scripts/colors.sh"
METRO_PORT=8081
METRO_PID="$(lsof -i :${METRO_PORT} | awk 'NR!=1 {print $2}' | sort -u | tr '\r\n' ' ')"
if [ ! -z "$METRO_PID" ]; then
echo -e "${YLW}TCP port ${METRO_PORT} is required by the Metro packager.\nThe following processes currently have the port open, preventing Metro from starting:${RST}"
ps -fp $METRO_PID
echo -e "${YLW}Do you want to terminate them (y/n)?${RST}"
read -n 1 term
[[ $term == 'y' ]] && kill $METRO_PID
fi
react-native start --reset-cache
-14
View File
@@ -1,14 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
TIMEOUT=10 # Metro should not take this long to start.
while [ "${TIMEOUT}" -gt 0 ]; do
if ! lsof -i:8081 &> /dev/null; then
echo "."
sleep 1
((TIMEOUT--))
else
break
fi
done
+3
View File
@@ -45,6 +45,9 @@
;; To match the folder created by Nix build of JSBundle.
:output-dir "result"
:init-fn status-im.core/init
:build-hooks [(status-im.setup.build-hooks.worklets/transform-output)
(status-im.setup.build-hooks.worklets/optimize-start)
(status-im.setup.build-hooks.worklets/optimize-output)]
;; When false, the Shadow-CLJS watcher won't automatically refresh
;; the target files (a.k.a hot reload). When false, you can manually
;; reload by calling `shadow.cljs.devtools.api/watch-compile-all!`.
@@ -28,10 +28,10 @@
[:f>
(fn []
(rn/use-mount (fn []
(rn/hw-back-add-listener dismiss-bottom-sheet-callback)
(fn []
(rn/hw-back-remove-listener dismiss-bottom-sheet-callback))))
(rn/use-effect (fn []
(rn/hw-back-add-listener dismiss-bottom-sheet-callback)
(fn []
(rn/hw-back-remove-listener dismiss-bottom-sheet-callback))))
[theme/provider {:theme (or page-theme (theme/get-theme))}
[bottom-sheet/bottom-sheet opts
(when content
@@ -16,14 +16,12 @@
:align-self :center})
(def backdrop
{:position :absolute
:left 0
:right 0
:bottom 0
:top 0})
(def backdrop-color
{:background-color colors/neutral-100})
{:position :absolute
:left 0
:right 0
:bottom 0
:top 0
:background-color colors/neutral-100})
(def container
{:position :absolute
+2 -4
View File
@@ -207,13 +207,11 @@
[@show-bottom-sheet? @expanded? @gesture-running?])
[:<>
[rn/pressable
{:on-press (when backdrop-dismiss? close-bottom-sheet)
:style styles/backdrop}
[rn/touchable-without-feedback {:on-press (when backdrop-dismiss? close-bottom-sheet)}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity bg-opacity}
styles/backdrop-color)}]]
styles/backdrop)}]]
(cond->> [reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
+3 -1
View File
@@ -1,6 +1,7 @@
(ns legacy.status-im.currency.core
(:require
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
[legacy.status-im.wallet.prices :as prices]
[utils.re-frame :as rf]))
(defn get-currency
@@ -15,4 +16,5 @@
:currency
currency
;; on changing currency, we should fetch tokens prices again
{:on-success #(rf/dispatch [:wallet/get-wallet-token])})))
{:on-success #(rf/dispatch [:wallet/get-wallet-token])})
(prices/update-prices)))
+2 -1
View File
@@ -7,6 +7,7 @@
[legacy.status-im.ethereum.ens :as ens]
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
[legacy.status-im.utils.random :as random]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.navigation.events :as navigation]
@@ -125,7 +126,7 @@
{:events [::set-pub-key]}
[{:keys [db]}]
(let [{:keys [username address custom-domain?]} (:ens/registration db)
address address
address (or address (wallet.utils/default-address db))
{:keys [public-key]} (:profile/profile db)
chain-id (chain/chain-id db)
username (fullname custom-domain? username)]
@@ -4,6 +4,7 @@
[legacy.status-im.ethereum.decode :as decode]
[legacy.status-im.ethereum.encode :as encode]
[legacy.status-im.utils.mobile-sync :as utils.mobile-sync]
[legacy.status-im.wallet.core :as wallet]
[re-frame.core :as re-frame]
[status-im.common.json-rpc.events :as json-rpc]
[taoensso.timbre :as log]
@@ -255,13 +256,24 @@
(let [checksum (eip55/address->checksum address)
max-known-block (get-max-block-with-transfers db address)
effects (cond-> [(when (seq transfers)
(set-lowest-fetched-block checksum transfers))]
(set-lowest-fetched-block checksum transfers))
(wallet/set-max-block-with-transfers checksum transfers)]
(seq transfers)
(concat
[]
(mapv add-transfer transfers))
(and max-known-block
(some #(> (:block %) max-known-block) transfers))
(conj (wallet/update-balances
[address]
(zero? max-known-block)))
(and (zero? max-known-block)
(empty? transfers))
(conj (wallet/set-zero-balances {:address address}))
(< (count transfers) limit)
(conj (tx-history-end-reached checksum)))]
(apply rf/merge cofx (tx-fetching-ended [checksum]) effects)))
@@ -270,7 +282,8 @@
{:events [::new-transfers]}
[cofx transfers params]
(rf/merge cofx
(handle-new-transfer transfers params)))
(handle-new-transfer transfers params)
(wallet/stop-fetching-on-empty-tx-history transfers)))
(rf/defn tx-fetching-failed
{:events [::tx-fetching-failed]}
+7
View File
@@ -32,6 +32,10 @@
legacy.status-im.visibility-status-popover.core
legacy.status-im.visibility-status-updates.core
legacy.status-im.waku.core
legacy.status-im.wallet.accounts.core
legacy.status-im.wallet.choose-recipient.core
[legacy.status-im.wallet.core :as wallet]
legacy.status-im.wallet.custom-tokens.core
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[react-native.core :as rn]
@@ -137,6 +141,7 @@
(rf/merge cofx
{:db (dissoc db :app-in-background-since)}
(mailserver/process-next-messages-request)
(wallet/restart-wallet-service-after-background app-in-background-since)
(when-not new-account?
(universal-links/process-stored-event))
#(when-let [chat-id (:current-chat-id db)]
@@ -193,8 +198,10 @@
:reset-card (keycard/reset-card-screen-did-load %)
:enter-pin-settings (keycard/enter-pin-screen-did-load %)
:keycard-login-pin (keycard/login-pin-screen-did-load %)
:add-new-account-pin (keycard/enter-pin-screen-did-load %)
:keycard-authentication-method (keycard/authentication-method-screen-did-load %)
:multiaccounts (keycard/multiaccounts-screen-did-load %)
:wallet-legacy (wallet/wallet-will-focus %)
nil)))
;;TODO :replace by named events
+7 -4
View File
@@ -401,12 +401,15 @@
(merge
args
{:on-success
(fn [_response]
(fn [response]
(log/debug "[keycard response succ] export-key")
(js/alert "feature no longer supported"))
(re-frame/dispatch [:keycard.callback/on-export-key-success
response]))
:on-failure
(fn [_response]
(log/debug "[keycard response fail] export-key"))})))
(fn [response]
(log/debug "[keycard response fail] export-key")
(re-frame/dispatch [:keycard.callback/on-export-key-error
(error-object->map response)]))})))
(defn unpair-and-delete
[args]
+5
View File
@@ -12,6 +12,7 @@
[legacy.status-im.keycard.recovery :as recovery]
[legacy.status-im.keycard.sign :as sign]
legacy.status-im.keycard.unpair
[legacy.status-im.keycard.wallet :as wallet]
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
[re-frame.db]
[status-im.navigation.events :as navigation]
@@ -400,6 +401,10 @@
(= pin-code-length numbers-entered))
(common/verify-pin {:pin-step :current})
(and (= enter-step :export-key)
(= pin-code-length numbers-entered))
(wallet/verify-pin-with-delay)
(and (= enter-step :sign)
(= pin-code-length numbers-entered))
(sign/prepare-to-sign)
@@ -1,13 +1,51 @@
(ns legacy.status-im.keycard.export-key
(:require
[legacy.status-im.keycard.common :as common]
[legacy.status-im.keycard.wallet :as wallet]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(rf/defn on-export-key-error
{:events [:keycard.callback/on-export-key-error]}
[{:keys [db] :as cofx} error]
(log/debug "[keycard] export key error" error)
(let [tag-was-lost? (common/tag-lost? (:error error))
pin-retries (common/pin-retries (:error error))]
(cond
tag-was-lost?
(rf/merge cofx
{:db (assoc-in db [:keycard :pin :status] nil)}
(common/set-on-card-connected :wallet-legacy.accounts/generate-new-keycard-account))
(not (nil? pin-retries))
(rf/merge cofx
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
(update-in [:keycard :pin]
assoc
:status :error
:enter-step :export-key
:puk []
:current []
:original []
:confirmation []
:sign []
:export-key []
:error-label :t/pin-mismatch))}
(common/hide-connection-sheet)
(when (zero? pin-retries) (common/frozen-keycard-popup)))
:else
(rf/merge cofx
(common/show-wrong-keycard-alert)
(common/clear-pin)
(common/hide-connection-sheet)))))
(rf/defn on-export-key-success
{:events [:keycard.callback/on-export-key-success]}
[{:keys [db] :as cofx} pubkey]
(let [callback-fn (get-in db [:keycard :on-export-success])]
(rf/merge cofx
{:dispatch (callback-fn pubkey)}
(wallet/hide-pin-sheet)
(common/clear-pin)
(common/hide-connection-sheet))))
+3 -1
View File
@@ -3,6 +3,7 @@
[clojure.string :as string]
[legacy.status-im.keycard.common :as common]
[legacy.status-im.utils.deprecated-types :as types]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[utils.address :as address]
@@ -23,7 +24,8 @@
typed? (get-in db [:keycard :typed?])
pin (common/vector->string (get-in db [:keycard :pin :sign]))
from (or (get-in db [:signing/tx :from :address])
(get-in db [:signing/tx :message :from]))
(get-in db [:signing/tx :message :from])
(wallet.utils/default-address db))
path (reduce
(fn [_ {:keys [address path]}]
(when (address/address= from address)
+76
View File
@@ -0,0 +1,76 @@
(ns legacy.status-im.keycard.wallet
(:require
[legacy.status-im.bottom-sheet.events :as bottom-sheet]
[legacy.status-im.keycard.common :as common]
[legacy.status-im.ui.screens.wallet.add-new.views :as add-new.views]
[legacy.status-im.utils.hex :as utils.hex]
[native-module.core :as native-module]
[status-im.constants :as constants]
[utils.ethereum.eip.eip55 :as eip55]
[utils.re-frame :as rf]))
(rf/defn show-pin-sheet
{:events [:keycard/new-account-pin-sheet]}
[{:keys [db] :as cofx}]
(rf/merge
cofx
{:db (-> db
(assoc-in [:keycard :pin :enter-step] :export-key)
(update-in [:keycard :pin] dissoc :export-key))
:dismiss-keyboard nil}
(bottom-sheet/show-bottom-sheet-old {:view {:content add-new.views/pin}})))
(rf/defn verify-pin-with-delay
[cofx]
{:utils/dispatch-later
;; We need to give previous sheet some time to be fully hidden
[{:ms 200
:dispatch [:wallet-legacy.accounts/verify-pin]}]})
(rf/defn hide-pin-sheet
{:events [:keycard/new-account-pin-sheet-hide]}
[cofx]
(bottom-sheet/hide-bottom-sheet-old cofx))
(defn public-key->address
[public-key]
(let [length (count public-key)
normalized-key (case length
132 (str "0x" (subs public-key 4))
130 public-key
128 (str "0x" public-key)
nil)]
(when normalized-key
(subs (native-module/sha3 normalized-key) 26))))
(rf/defn generate-new-keycard-account
{:events [:wallet-legacy.accounts/generate-new-keycard-account]}
[{:keys [db]}]
(let [path-num (inc (get-in db [:profile/profile :latest-derived-path]))
path (str constants/path-wallet-root "/" path-num)
pin (common/vector->string (get-in db [:keycard :pin :export-key]))]
{:db
(assoc-in
db
[:keycard :on-export-success]
#(vector :wallet-legacy.accounts/account-stored
(let [public-key (utils.hex/normalize-hex %)]
{;; Strip leading 04 prefix denoting uncompressed key format
:address (eip55/address->checksum
(str "0x"
(public-key->address
(subs public-key 2))))
:public-key (str "0x" public-key)
:path path})))
:keycard/export-key {:pin pin :path path}}))
(rf/defn verify-pin
{:events [:wallet-legacy.accounts/verify-pin]}
[cofx]
(common/verify-pin
cofx
{:pin-step :export-key
:on-card-connected :wallet-legacy.accounts/verify-pin
:on-success :wallet-legacy.accounts/generate-new-keycard-account
:on-failure :keycard/new-account-pin-sheet}))
@@ -5,6 +5,7 @@
[legacy.status-im.multiaccounts.model :as multiaccounts.model]
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
[legacy.status-im.utils.mobile-sync :as utils]
[legacy.status-im.wallet.core :as wallet]
[status-im.contexts.chat.home.add-new-contact.events :as add-new-contact]
[status-im.navigation.events :as navigation]
[taoensso.timbre :as log]
@@ -34,7 +35,8 @@
(and logged-in? initialized?)
[(mailserver/process-next-messages-request)
(bottom-sheet/hide-bottom-sheet-old)
#(add-new-contact/set-new-identity-reconnected %)]
(wallet/restart-wallet-service nil)
(add-new-contact/set-new-identity-reconnected)]
logged-in?
[(mailserver/process-next-messages-request)
@@ -65,7 +67,8 @@
(boolean remember-choice?)
{})
(when (and cellular? sync?)
(mailserver/process-next-messages-request)))))))
(mailserver/process-next-messages-request))
(wallet/restart-wallet-service nil))))))
(rf/defn mobile-network-continue-syncing
{:events [:mobile-network/continue-syncing]}
@@ -1,5 +1,6 @@
(ns legacy.status-im.multiaccounts.logout.core
(:require
[legacy.status-im.wallet.core :as wallet]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.common.keychain.events :as keychain]
@@ -30,6 +31,7 @@
(let [key-uid (get-in db [:profile/profile :key-uid])]
(rf/merge cofx
{:set-root :progress
:chat.ui/clear-inputs nil
:effects.shell/reset-state nil
:hide-popover nil
::logout nil
@@ -38,6 +40,7 @@
:profile/get-profiles-overview #(rf/dispatch
[:profile/get-profiles-overview-success %])}
(keychain/save-auth-method key-uid auth-method)
(wallet/clear-timeouts)
(initialize-app-db))))
(rf/defn logout
+7 -1
View File
@@ -2,6 +2,7 @@
(:require
["@react-native-community/netinfo" :default net-info]
[legacy.status-im.mobile-sync-settings.core :as mobile-network]
[legacy.status-im.wallet.core :as wallet]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
@@ -10,7 +11,12 @@
(rf/defn change-network-status
[{:keys [db] :as cofx} is-connected?]
(rf/merge cofx
{:db (assoc db :network-status (if is-connected? :online :offline))}))
{:db (assoc db :network-status (if is-connected? :online :offline))}
(when (and is-connected?
(or (not= (count (get-in db [:wallet-legacy :accounts]))
(count (get db :profile/wallet-accounts)))
(wallet/has-empty-balances? db)))
(wallet/update-balances nil nil))))
(rf/defn change-network-type
[{:keys [db] :as cofx} old-network-type network-type expensive?]
+1 -1
View File
@@ -72,7 +72,7 @@
:PeerExchange true
:Port 0
:UDPPort 0
:LightClient false})
:LightClient true})
(def login-node-config
{:WalletConfig (cond-> {:Enabled true}
+11
View File
@@ -72,3 +72,14 @@
{:events [:show-tooltip]}
[_ tooltip-id]
{:show-tooltip tooltip-id})
(rf/defn show-profile
{:events [:chat.ui/show-profile]}
[{:keys [db]} identity ens-name]
(let [my-public-key (get-in db [:profile/profile :public-key])]
(if (not= my-public-key identity)
{:db (-> db
(assoc :contacts/identity identity)
(assoc :contacts/ens-name ens-name))
:dispatch [:contacts/build-contact identity ens-name true]}
{:dispatch [:navigate-to :my-profile]})))
@@ -0,0 +1,59 @@
(ns legacy.status-im.search.core-test
(:require
[cljs.test :refer-macros [deftest testing is]]
[legacy.status-im.subs.wallet.search :as search.subs]))
(defn extract-chat-attributes
[chat]
(let [{:keys [name alias tags]} (val chat)]
(into [name alias] tags)))
(deftest filter-chats
(let [chats {:chat-1 {:name "name1"
:alias "alias1"
:tags #{"tag1"}}
:chat-2 {:name "name2"
:alias "alias2"
:tags #{"tag2" "tag3"}}
:chat-3 {:name "name3"
:alias "alias3"
:tags #{}}
:chat-4 {:name "name4"
:alias "alias4"
:tags #{"tag4"}}}]
(testing "no search filter"
(is (= (count chats)
(count (search.subs/apply-filter ""
chats
extract-chat-attributes
false)))))
(testing "searching for a specific tag"
(is (= 1
(count (search.subs/apply-filter "tag2"
chats
extract-chat-attributes
false)))))
(testing "searching for a partial tag"
(is (= 3
(count (search.subs/apply-filter "tag"
chats
extract-chat-attributes
false)))))
(testing "searching for a specific alias"
(is (= 1
(count (search.subs/apply-filter "alias4"
chats
extract-chat-attributes
false)))))
(testing "searching for a partial alias"
(is (= 4
(count (search.subs/apply-filter "alias"
chats
extract-chat-attributes
false)))))
(testing "searching for a specific chat name"
(is (= 1
(count (search.subs/apply-filter "name4"
chats
extract-chat-attributes
false)))))))
+9 -4
View File
@@ -10,6 +10,9 @@
[legacy.status-im.utils.deprecated-types :as types]
[legacy.status-im.utils.hex :as utils.hex]
[legacy.status-im.utils.utils :as utils]
[legacy.status-im.wallet.core :as wallet]
[legacy.status-im.wallet.prices :as prices]
[legacy.status-im.wallet.utils :as wallet.utils]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.common.json-rpc.events :as json-rpc]
@@ -80,7 +83,7 @@
[{{:signing/keys [sign tx] :as db} :db}]
(let [{{:keys [data typed? from v4]} :message} tx
{:keys [in-progress? password]} sign
from from
from (or from (wallet.utils/default-address db))
hashed-password (native-module/sha3 (security/safe-unmask-data
password))]
(when-not in-progress?
@@ -332,7 +335,7 @@
:dismiss-keyboard nil}
#(when-not wallet-set-up-passed?
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
(prices/update-prices)
#(when-not gas
{:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true)
:signing/update-estimated-gas {:obj (-> tx-obj
@@ -392,6 +395,7 @@
:signing/show-transaction-result nil}
(prepare-unconfirmed-transaction result tx-obj symbol amount)
(check-queue)
(wallet/watch-tx (get from :address) result)
#(when on-result
{:dispatch (conj on-result result)}))))
@@ -404,6 +408,7 @@
(rf/merge
cofx
{:db (dissoc db :signing/tx :signing/sign)}
(wallet/watch-tx (get from :address) transaction-hash)
(if (keycard.common/keycard-multiaccount? db)
(signing.keycard/hash-message
{:data data
@@ -506,8 +511,8 @@
{:dispatch (conj on-error "transaction was cancelled by user")}))))
(defn normalize-tx-obj
[_db tx]
(update-in tx [:tx-obj :from] #(eip55/address->checksum %)))
[db tx]
(update-in tx [:tx-obj :from] #(eip55/address->checksum (or % (wallet.utils/default-address db)))))
(rf/defn sign
"Signing transaction or message, shows signing sheet
+10
View File
@@ -1,6 +1,7 @@
(ns legacy.status-im.stickers.core
(:require
[legacy.status-im.utils.utils :as utils]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[utils.ethereum.chain :as chain]
@@ -38,6 +39,15 @@
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]})
(rf/defn buy-pack
{:events [:stickers/buy-pack]}
[{db :db} pack-id]
{:json-rpc/call [{:method "stickers_buyPrepareTx"
:params [(chain/chain-id db) (wallet.utils/default-address db) (int pack-id)]
:on-success #(re-frame/dispatch [:signing.ui/sign
{:tx-obj %
:on-result [:stickers/pending-pack pack-id]}])}]})
(rf/defn pending-pack
{:events [:stickers/pending-pack]}
[{db :db} id]
+4
View File
@@ -7,6 +7,10 @@
legacy.status-im.subs.mailservers
legacy.status-im.subs.networks
legacy.status-im.subs.stickers
legacy.status-im.subs.wallet.search
legacy.status-im.subs.wallet.signing
legacy.status-im.subs.wallet.transactions
legacy.status-im.subs.wallet.wallet
[re-frame.core :as re-frame]))
(defn reg-root-key-sub
@@ -0,0 +1,73 @@
(ns legacy.status-im.subs.wallet.search
(:require
[clojure.string :as string]
[legacy.status-im.utils.currency :as currency]
[re-frame.core :as re-frame]))
(defn sort-by-timestamp
[coll]
(when (not-empty coll)
(sort-by #(-> % second :timestamp)
>
(into {} coll))))
(defn apply-filter
"extract-attributes-fn is a function that take an element from the collection
and returns a vector of attributes which are strings
apply-filter returns the elements for which at least one attribute includes
the search-filter
apply-filter returns nil if there is no element that match the filter
apply-filter returns full collection if the search-filter is empty"
[search-filter coll extract-attributes-fn sort?]
(let [results (if (not-empty search-filter)
(let [search-filter (string/lower-case search-filter)]
(filter (fn [element]
(some (fn [v]
(let [s (cond (string? v) v
(keyword? v) (name v))]
(when (string? s)
(string/includes? (string/lower-case s)
search-filter))))
(extract-attributes-fn element)))
coll))
coll)]
(if sort?
(sort-by-timestamp results)
results)))
(defn extract-currency-attributes
[currency]
(let [{:keys [code display-name]} (val currency)]
[code display-name]))
(re-frame/reg-sub
:wallet-legacy/search-filtered-currencies
:<- [:search/currency-filter]
(fn [search-currency-filter]
{:search-filter search-currency-filter
:currencies (apply-filter search-currency-filter
currency/currencies
extract-currency-attributes
false)}))
(defn extract-token-attributes
[token]
[(:symbol token) (:name token)])
(re-frame/reg-sub
:wallet-legacy/search-token-filter
:<- [:ui/search]
(fn [search]
(get search :token-filter)))
(re-frame/reg-sub
:wallet-legacy/filtered-grouped-chain-tokens
:<- [:wallet-legacy/grouped-chain-tokens]
:<- [:wallet-legacy/search-token-filter]
(fn [[{custom-tokens true default-tokens nil} search-token-filter]]
{:search-filter search-token-filter
:tokens {true (apply-filter search-token-filter custom-tokens extract-token-attributes false)
nil (apply-filter search-token-filter
default-tokens
extract-token-attributes
false)}}))
@@ -0,0 +1,235 @@
(ns legacy.status-im.subs.wallet.signing
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.tokens :as tokens]
[legacy.status-im.signing.gas :as signing.gas]
[legacy.status-im.wallet.db :as wallet.db]
[re-frame.core :as re-frame]
[utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.money :as money]))
(re-frame/reg-sub
::send-transaction
:<- [:wallet-legacy]
(fn [wallet]
(:send-transaction wallet)))
(re-frame/reg-sub
:wallet-legacy.send/symbol
:<- [::send-transaction]
(fn [send-transaction]
(:symbol send-transaction)))
(re-frame/reg-sub
:wallet-legacy.send/camera-flashlight
:<- [::send-transaction]
(fn [send-transaction]
(:camera-flashlight send-transaction)))
(re-frame/reg-sub
:wallet-legacy/settings
:<- [:wallet-legacy]
(fn [{:keys [settings]}]
(reduce-kv #(conj %1 %3) [] settings)))
(re-frame/reg-sub
:wallet-legacy.request/transaction
:<- [:wallet-legacy]
:request-transaction)
(re-frame/reg-sub
:wallet-legacy/binance-chain?
:<- [:current-network]
(fn [network]
(chain/binance-chain-id? (get-in network [:config :NetworkId]))))
(re-frame/reg-sub
:signing/fee
:<- [:signing/tx]
(fn [{:keys [gas gasPrice maxFeePerGas]}]
(signing.gas/calculate-max-fee gas (or maxFeePerGas gasPrice))))
(re-frame/reg-sub
:signing/currencies
:<- [:prices]
:<- [:wallet-legacy/currency]
:<- [:ethereum/native-currency]
(fn [[prices {:keys [code]} {sym :symbol}]]
[(name sym)
code
(get-in prices [sym (keyword code)])]))
(re-frame/reg-sub
:signing/priority-fee-suggestions-range
:<- [:wallet-legacy/current-priority-fee]
:<- [:wallet-legacy/slow-base-fee]
:<- [:wallet-legacy/normal-base-fee]
:<- [:wallet-legacy/fast-base-fee]
(fn [[latest-tip slow normal fast]]
(reduce
(fn [acc [k fees]]
(assoc acc
k
(reduce
(fn [acc [k fee]]
(assoc acc
k
(-> fee
money/wei->gwei
(money/to-fixed 2))))
{}
fees)))
{}
(signing.gas/get-fee-options latest-tip slow normal fast))))
(re-frame/reg-sub
:signing/phrase
:<- [:profile/profile]
(fn [{:keys [signing-phrase]}]
signing-phrase))
(re-frame/reg-sub
:signing/sign-message
:<- [:signing/sign]
:<- [:profile/wallet-accounts]
:<- [:prices]
(fn [[sign wallet-accounts prices]]
(if (= :pinless (:type sign))
(let [message (get-in sign [:formatted-data :message])
wallet-acc (some #(when (= (:address %) (:receiver message)) %) wallet-accounts)]
(cond-> sign
(and (:amount message) (:currency message))
(assoc :fiat-amount
(money/fiat-amount-value (:amount message)
(:currency message)
:USD
prices)
:fiat-currency "USD")
(and (:receiver message) wallet-acc)
(assoc :account wallet-acc)))
sign)))
(defn- too-precise-amount?
"Checks if number has any extra digit beyond the allowed number of decimals.
It does so by checking the number against its rounded value."
[amount decimals]
(let [^js bn (money/bignumber amount)]
(not (.eq bn (.round bn decimals)))))
(defn get-amount-error
[amount decimals]
(when (and (seq amount) decimals)
(let [normalized-amount (money/normalize amount)
value (money/bignumber normalized-amount)]
(cond
(not (money/valid? value))
{:amount-error (i18n/label :t/validation-amount-invalid-number)}
(too-precise-amount? normalized-amount decimals)
{:amount-error (i18n/label :t/validation-amount-is-too-precise {:decimals decimals})}
:else nil))))
(defn get-sufficient-funds-error
[balance sym amount]
(when-not (money/sufficient-funds? amount (get balance sym))
{:amount-error (i18n/label :t/wallet-insufficient-funds)}))
(defn gas-required-exceeds-allowance?
[gas-error-message]
(and gas-error-message
(string/starts-with?
gas-error-message
"gas required exceeds allowance")))
(defn get-sufficient-gas-error
[gas-error-message balance sym amount ^js gas ^js gasPrice]
(if (and gas gasPrice)
(let [^js fee (.times gas gasPrice)
^js available-ether (money/bignumber (get balance :ETH 0))
^js available-for-gas (if (= :ETH sym)
(.minus available-ether (money/bignumber amount))
available-ether)]
(merge {:gas-error-state (when gas-error-message :gas-is-set)}
(when-not (money/sufficient-funds? fee (money/bignumber available-for-gas))
{:gas-error (i18n/label :t/wallet-insufficient-gas)})))
(let [insufficient-balance? (gas-required-exceeds-allowance? gas-error-message)]
{:gas-error-state (when gas-error-message :gas-isnt-set)
:insufficient-balalce? insufficient-balance?
:gas-error (if insufficient-balance?
(i18n/label :t/insufficient-balance-to-cover-fee)
(or gas-error-message
(i18n/label :t/invalid-number)))})))
(re-frame/reg-sub
:signing/amount-errors
(fn [[_ address] _]
[(re-frame/subscribe [:signing/tx])
(re-frame/subscribe [:balance address])])
(fn [[{:keys [amount token gas gasPrice maxFeePerGas approve? gas-error-message]} balance]]
(let [gas-price (or maxFeePerGas gasPrice)]
(if (and amount token (not approve?))
(let [amount-bn (money/formatted->internal (money/bignumber amount)
(:symbol token)
(:decimals token))
amount-error (or (get-amount-error amount (:decimals token))
(get-sufficient-funds-error balance (:symbol token) amount-bn))]
(merge
amount-error
(get-sufficient-gas-error gas-error-message balance (:symbol token) amount-bn gas gas-price)))
(get-sufficient-gas-error gas-error-message balance nil nil gas gas-price)))))
(re-frame/reg-sub
:wallet-legacy.send/prepare-transaction-with-balance
:<- [:wallet-legacy/prepare-transaction]
:<- [:wallet-legacy]
:<- [:offline?]
:<- [:wallet-legacy/all-tokens]
:<- [:current-network]
(fn [[{:keys [from to amount-text] :as transaction}
wallet offline? all-tokens current-network]]
(let [sym (:symbol transaction)
balance (get-in wallet [:accounts (:address from) :balance])
{:keys [decimals] :as token} (tokens/asset-for all-tokens current-network sym)
{:keys [value error]} (wallet.db/parse-amount amount-text decimals)
amount (money/formatted->internal value sym decimals)
{:keys [amount-error] :as transaction-new}
(merge transaction
{:amount-error error}
(when amount
(get-sufficient-funds-error balance sym amount)))]
(assoc transaction-new
:amount amount
:balance balance
:token (assoc token :amount (get balance (:symbol token)))
:sign-enabled? (and to
(nil? amount-error)
(not (nil? amount))
(not offline?))))))
(re-frame/reg-sub
:wallet-legacy.request/prepare-transaction-with-balance
:<- [:wallet-legacy/prepare-transaction]
:<- [:wallet-legacy]
:<- [:offline?]
:<- [:wallet-legacy/all-tokens]
:<- [:current-network]
(fn [[{:keys [from to amount-text] :as transaction}
wallet offline? all-tokens current-network]]
(let [sym (:symbol transaction)
balance (get-in wallet [:accounts (:address from) :balance])
{:keys [decimals] :as token} (tokens/asset-for all-tokens current-network sym)
{:keys [value error]} (wallet.db/parse-amount amount-text decimals)
amount (money/formatted->internal value sym decimals)
{:keys [amount-error] :as transaction-new}
(assoc transaction :amount-error error)]
(assoc transaction-new
:amount amount
:balance balance
:token (assoc token :amount (get balance (:symbol token)))
:sign-enabled? (and to
from
(nil? amount-error)
(not (nil? amount))
(not offline?))))))
@@ -0,0 +1,247 @@
(ns legacy.status-im.subs.wallet.transactions
(:require
[legacy.status-im.ethereum.transactions.core :as transactions]
[legacy.status-im.notifications.wallet :as notifications.wallet]
[legacy.status-im.wallet.db :as wallet.db]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[utils.money :as money]))
(re-frame/reg-sub
:wallet-legacy/accounts
:<- [:wallet-legacy]
(fn [wallet]
(get wallet :accounts)))
(re-frame/reg-sub
:wallet-legacy/account-by-transaction-hash
:<- [:wallet-legacy/accounts]
(fn [accounts [_ tx-hash]]
(some (fn [[address account]]
(when-let [transaction (get-in account [:transactions tx-hash])]
(assoc transaction :address address)))
accounts)))
(re-frame/reg-sub
:wallet-legacy/transactions
:<- [:wallet-legacy]
(fn [wallet [_ address]]
(get-in wallet [:accounts address :transactions])))
(re-frame/reg-sub
:wallet-legacy/filters
:<- [:wallet-legacy]
(fn [wallet]
(get wallet :filters)))
(defn enrich-transaction
[{:keys [type to from value token] :as transaction}
contacts native-currency]
(let [[contact-address key-contact key-wallet]
(if (= type :inbound)
[from :from-contact :to-wallet]
[to :to-contact :from-wallet])
wallet (i18n/label :t/main-wallet)
contact (get contacts contact-address)
{:keys [symbol-display decimals] :as asset}
(or token native-currency)
amount-text (if value
(wallet.utils/format-amount value decimals)
"...")
currency-text (when asset
(clojure.core/name (or symbol-display
(:symbol asset))))]
(cond-> transaction
contact (assoc key-contact (:name contact))
:always (assoc key-wallet
wallet
:amount-text amount-text
:currency-text currency-text))))
(re-frame/reg-sub
:wallet-legacy.transactions/transactions
(fn [[_ address] _]
[(re-frame/subscribe [:wallet-legacy/transactions address])
(re-frame/subscribe [:contacts/contacts-by-address])
(re-frame/subscribe [:ethereum/native-currency])])
(fn [[transactions contacts native-currency]]
(reduce (fn [acc [tx-hash transaction]]
(assoc acc
tx-hash
(enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't
;;look good for performance, we need to calculate this only once for each transaction
{}
transactions)))
(re-frame/reg-sub
:wallet-legacy.transactions/all-filters?
:<- [:wallet-legacy/filters]
(fn [filters]
(= wallet.db/default-wallet-filters
filters)))
(def filters-labels
{:inbound (i18n/label :t/incoming)
:outbound (i18n/label :t/outgoing)
:pending (i18n/label :t/pending)
:failed (i18n/label :t/failed)})
(re-frame/reg-sub
:wallet-legacy.transactions/filters
:<- [:wallet-legacy/filters]
(fn [filters]
(map (fn [id]
(let [checked? (filters id)]
{:id id
:label (filters-labels id)
:checked? checked?
:on-touch #(if checked?
(re-frame/dispatch [:wallet-legacy.transactions/remove-filter id])
(re-frame/dispatch [:wallet-legacy.transactions/add-filter id]))}))
wallet.db/default-wallet-filters)))
(re-frame/reg-sub
:wallet-legacy.transactions.filters/screen
:<- [:wallet-legacy.transactions/filters]
:<- [:wallet-legacy.transactions/all-filters?]
(fn [[filters all-filters?]]
{:all-filters? all-filters?
:filters filters
:on-touch-select-all (when-not all-filters?
#(re-frame/dispatch
[:wallet-legacy.transactions/add-all-filters]))}))
(defn- enrich-transaction-for-list
[filters
{:keys [type from-contact from to-contact to timestamp] :as transaction}
address]
(when (filters type)
(assoc
(case type
:inbound
(assoc transaction
:label (i18n/label :t/from-capitalized)
:contact-accessibility-label :sender-text
:address-accessibility-label :sender-address-text
:contact from-contact
:address from)
(assoc transaction
:label (i18n/label :t/to-capitalized)
:contact-accessibility-label :recipient-name-text
:address-accessibility-label :recipient-address-text
:contact to-contact
:address to))
:time-formatted (datetime/timestamp->time timestamp)
:on-touch-fn #(re-frame/dispatch [:wallet-legacy.ui/show-transaction-details
(:hash transaction)
address]))))
(defn group-transactions-by-date
[transactions]
(->> transactions
(group-by #(datetime/timestamp->date-key (:timestamp %)))
(sort-by key >)
(map (fn [[date-key transactions]]
{:title (datetime/timestamp->mini-date (:timestamp (first transactions)))
:key date-key
:data (sort-by :timestamp > transactions)}))))
(re-frame/reg-sub
:wallet-legacy.transactions.history/screen
(fn [[_ address] _]
[(re-frame/subscribe [:wallet-legacy.transactions/transactions address])
(re-frame/subscribe [:wallet-legacy/filters])
(re-frame/subscribe [:wallet-legacy.transactions/all-filters?])])
(fn [[transactions filters all-filters?] [_ address]]
{:all-filters? all-filters?
:total (count transactions)
:transaction-history-sections
(->> transactions
vals
(keep #(enrich-transaction-for-list filters % address))
(group-transactions-by-date))}))
(re-frame/reg-sub
:wallet-legacy/recipient-recent-txs
(fn [[_ address] _]
[(re-frame/subscribe [:wallet-legacy.transactions/transactions address])])
(fn [[transactions] _]
(->> transactions
vals
(sort-by :timestamp >)
(remove #(= (:type %) :pending))
(take 3))))
(re-frame/reg-sub
:wallet-legacy.transactions.details/current-transaction
(fn [[_ _ address] _]
[(re-frame/subscribe [:wallet-legacy.transactions/transactions address])
(re-frame/subscribe [:ethereum/native-currency])
(re-frame/subscribe [:chain-id])])
(fn [[transactions native-currency chain-id] [_ tx-hash _]]
(let [{:keys [gas-used gas-price fee-cap tip-cap timestamp type]
:as transaction}
(get transactions tx-hash)
native-currency-text (name (or (:symbol-display native-currency)
(:symbol native-currency)))]
(when transaction
(merge transaction
{:gas-price-eth (if gas-price
(money/wei->str :eth
gas-price
native-currency-text)
"-")
:gas-price-gwei (if gas-price
(money/wei->str :gwei
gas-price)
"-")
:fee-cap-gwei (if fee-cap
(money/wei->str :gwei
fee-cap)
"-")
:tip-cap-gwei (if tip-cap
(money/wei->str :gwei
tip-cap)
"-")
:date (datetime/timestamp->long-date timestamp)}
(if (= type :unsigned)
{:block (i18n/label :t/not-applicable)
:cost (i18n/label :t/not-applicable)
:gas-limit (i18n/label :t/not-applicable)
:gas-used (i18n/label :t/not-applicable)
:nonce (i18n/label :t/not-applicable)
:hash (i18n/label :t/not-applicable)}
{:cost (when gas-used
(money/wei->str :eth
(money/fee-value gas-used gas-price)
native-currency-text))
:url (transactions/get-transaction-details-url
chain-id
(:hash transaction))}))))))
(re-frame/reg-sub
:wallet-legacy.transactions.details/screen
(fn [[_ tx-hash address] _]
[(re-frame/subscribe [:wallet-legacy.transactions.details/current-transaction tx-hash address])
(re-frame/subscribe [:ethereum/current-block])])
(fn [[transaction current-block]]
(let [confirmations (wallet.db/get-confirmations transaction
current-block)]
(assoc transaction
:confirmations confirmations
:confirmations-progress
(if (>= confirmations transactions/confirmations-count-threshold)
100
(* 100 (/ confirmations transactions/confirmations-count-threshold)))))))
(re-frame/reg-sub
:push-notifications/wallet-transactions
:<- [:push-notifications/preferences]
(fn [pref]
(first (filter #(notifications.wallet/preference= %
{:service "wallet"
:event "transaction"
:identifier "all"})
pref))))
@@ -0,0 +1,295 @@
(ns legacy.status-im.subs.wallet.wallet
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.tokens :as tokens]
[legacy.status-im.utils.currency :as currency]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[status-im.config :as config]
[utils.i18n :as i18n]
[utils.money :as money]))
(re-frame/reg-sub
:balance
:<- [:wallet-legacy]
(fn [wallet [_ address]]
(get-in wallet [:accounts address :balance])))
(re-frame/reg-sub
:balance-default
:<- [:wallet-legacy]
:<- [:profile/wallet-accounts]
(fn [[wallet accounts]]
(get-in wallet [:accounts (:address (wallet.utils/get-default-account accounts)) :balance])))
(re-frame/reg-sub
:balances
:<- [:wallet-legacy]
:<- [:multiaccount/visible-accounts]
(fn [[wallet accounts]]
(let [accounts (map :address accounts)]
(map :balance (vals (select-keys (:accounts wallet) accounts))))))
(re-frame/reg-sub
:empty-balances?
:<- [:balances]
(fn [balances]
(every?
(fn [balance]
(every?
(fn [^js asset]
(or (nil? asset) (.isZero asset)))
(vals balance)))
balances)))
(re-frame/reg-sub
:price
:<- [:prices]
(fn [prices [_ fsym tsym]]
(get-in prices [fsym tsym])))
(re-frame/reg-sub
:last-day
:<- [:prices]
(fn [prices [_ fsym tsym]]
(get-in prices [fsym tsym :last-day])))
(re-frame/reg-sub
:wallet-legacy/settings-currency
:<- [:profile/profile]
(fn [settings]
(or (get settings :currency) :usd)))
(defn get-balance-total-value
[balance prices currency token->decimals]
(reduce-kv (fn [acc sym value]
(if-let [price (get-in prices [sym currency])]
(+ acc
(or (some-> (money/internal->formatted value sym (token->decimals sym))
^js (money/crypto->fiat price)
.toNumber)
0))
acc))
0
balance))
(re-frame/reg-sub
:wallet-legacy/token->decimals
:<- [:wallet-legacy/all-tokens]
(fn [all-tokens]
(into {} (map #(vector (:symbol %) (:decimals %)) (vals all-tokens)))))
(re-frame/reg-sub
:portfolio-value
:<- [:balances]
:<- [:prices]
:<- [:wallet-legacy/currency]
:<- [:wallet-legacy/token->decimals]
(fn [[balances prices currency token->decimals]]
(if (and balances prices)
(let [currency-key (-> currency :code keyword)
balance-total-value (apply
+
(map #(get-balance-total-value % prices currency-key token->decimals)
balances))]
(if (pos? balance-total-value)
(-> balance-total-value
(money/with-precision 2)
str
(i18n/format-currency (:code currency)))
"0"))
"...")))
(re-frame/reg-sub
:account-portfolio-value
(fn [[_ address] _]
[(re-frame/subscribe [:balance address])
(re-frame/subscribe [:prices])
(re-frame/subscribe [:wallet-legacy/currency])
(re-frame/subscribe [:wallet-legacy/token->decimals])])
(fn [[balance prices currency token->decimals]]
(if (and balance prices)
(let [currency-key (-> currency :code keyword)
balance-total-value (get-balance-total-value balance prices currency-key token->decimals)]
(if (pos? balance-total-value)
(-> balance-total-value
(money/with-precision 2)
str
(i18n/format-currency (:code currency)))
"0"))
"...")))
(re-frame/reg-sub
:wallet-legacy/sorted-tokens
:<- [:wallet-legacy/all-tokens]
(fn [all-tokens]
(tokens/sorted-tokens-for all-tokens)))
(re-frame/reg-sub
:wallet-legacy/grouped-chain-tokens
:<- [:wallet-legacy/sorted-tokens]
:<- [:wallet-legacy/visible-tokens-symbols]
(fn [[all-tokens visible-tokens]]
(let [vt-set (set visible-tokens)]
(group-by :custom?
(map #(assoc % :checked? (boolean (get vt-set (keyword (:symbol %))))) all-tokens)))))
(re-frame/reg-sub
:wallet-legacy/fetching-tx-history?
:<- [:wallet-legacy]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :history?])))
(re-frame/reg-sub
:wallet-legacy/fetching-recent-tx-history?
:<- [:wallet-legacy]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :recent?])))
(re-frame/reg-sub
:wallet-legacy/tx-history-fetched?
:<- [:wallet-legacy]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :all-fetched?])))
(re-frame/reg-sub
:wallet-legacy/chain-explorer-link
(fn [db [_ address]]
(let [network (:networks/current-network db)
link (get-in config/default-networks-by-id
[network :chain-explorer-link])]
(when link
(str link address)))))
(re-frame/reg-sub
:wallet-legacy/error-message
:<- [:wallet-legacy]
(fn [wallet]
(or (get-in wallet [:errors :balance-update])
(get-in wallet [:errors :prices-update]))))
(re-frame/reg-sub
:wallet-legacy/visible-tokens-symbols
:<- [:ethereum/chain-keyword]
:<- [:profile/profile]
(fn [[chain current-multiaccount]]
(get-in current-multiaccount [:wallet-legacy/visible-tokens chain])))
(re-frame/reg-sub
:wallet-legacy/visible-assets
:<- [:current-network]
:<- [:wallet-legacy/visible-tokens-symbols]
:<- [:wallet-legacy/sorted-tokens]
(fn [[network visible-tokens-symbols all-tokens-sorted]]
(conj (filter #(contains? visible-tokens-symbols (:symbol %)) all-tokens-sorted)
(tokens/native-currency network))))
(re-frame/reg-sub
:wallet-legacy/visible-assets-with-amount
(fn [[_ address] _]
[(re-frame/subscribe [:balance address])
(re-frame/subscribe [:wallet-legacy/visible-assets])])
(fn [[balance visible-assets]]
(map #(assoc % :amount (get balance (:symbol %))) visible-assets)))
(defn update-value
[prices currency]
(fn [{:keys [decimals amount] :as token}]
(let [sym (:symbol token)
currency-kw (-> currency :code keyword)
price (get-in prices [sym currency-kw])]
(assoc token
:price price
:value (when (and amount price)
(-> (money/internal->formatted amount sym decimals)
(money/crypto->fiat price)
(money/with-precision 2)
str
(i18n/format-currency (:code currency))))))))
(re-frame/reg-sub
:wallet-legacy/visible-assets-with-values
(fn [[_ address] _]
[(re-frame/subscribe [:wallet-legacy/visible-assets-with-amount address])
(re-frame/subscribe [:prices])
(re-frame/subscribe [:wallet-legacy/currency])])
(fn [[assets prices currency]]
(let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets)
tokens-with-values (map (update-value prices currency) tokens)]
{:tokens tokens-with-values
:nfts nfts})))
(defn get-asset-amount
[balances sym]
(reduce #(if-let [^js bl (get %2 sym)]
(.plus ^js (or ^js %1 ^js (money/bignumber 0)) bl)
%1)
nil
balances))
(re-frame/reg-sub
:wallet-legacy/all-visible-assets-with-amount
:<- [:balances]
:<- [:wallet-legacy/visible-assets]
(fn [[balances visible-assets]]
(map #(assoc % :amount (get-asset-amount balances (:symbol %))) visible-assets)))
(re-frame/reg-sub
:wallet-legacy/all-visible-assets-with-values
:<- [:wallet-legacy/all-visible-assets-with-amount]
:<- [:prices]
:<- [:wallet-legacy/currency]
(fn [[assets prices currency]]
(let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets)
tokens-with-values (map (update-value prices currency) tokens)]
{:tokens tokens-with-values
:nfts nfts})))
(re-frame/reg-sub
:wallet-legacy/transferrable-assets-with-amount
(fn [[_ address]]
(re-frame/subscribe [:wallet-legacy/visible-assets-with-amount address]))
(fn [all-assets]
(filter #(not (:nft? %)) all-assets)))
(re-frame/reg-sub
:wallet-legacy/currency
:<- [:wallet-legacy/settings-currency]
(fn [currency-id]
(get currency/currencies currency-id (get currency/currencies :usd))))
(defn filter-recipient-favs
[search-filter {:keys [name]}]
(string/includes? (string/lower-case (str name)) search-filter))
(re-frame/reg-sub
:wallet-legacy/favourites-filtered
:<- [:wallet-legacy/favourites]
:<- [:wallet-legacy/search-recipient-filter]
(fn [[favs search-filter]]
(let [favs (vals favs)]
(if (string/blank? search-filter)
favs
(filter (partial filter-recipient-favs
(string/lower-case search-filter))
favs)))))
(re-frame/reg-sub
:wallet-legacy/collectible-collection
:<- [:wallet-legacy/collectible-collections]
(fn [all-collections [_ address]]
(when address
(let [all-collections (get all-collections (string/lower-case address) [])]
(sort-by :name all-collections)))))
(re-frame/reg-sub
:wallet-legacy/collectible-assets-by-collection-and-address
:<- [:wallet-legacy/collectible-assets]
(fn [all-assets [_ address collectible-slug]]
(get-in all-assets [address collectible-slug] [])))
(re-frame/reg-sub
:wallet-legacy/fetching-assets-by-collectible-slug
:<- [:wallet-legacy/fetching-collection-assets]
(fn [fetching-collection-assets [_ collectible-slug]]
(get fetching-collection-assets collectible-slug false)))
@@ -0,0 +1,152 @@
(ns legacy.status-im.subs.wallet.wallet-test
(:require
[cljs.test :refer [deftest is testing]]
[legacy.status-im.subs.wallet.transactions :as wallet.transactions]
[legacy.status-im.subs.wallet.wallet :as wallet]
[re-frame.db :as rf-db]
[test-helpers.unit :as h]
[utils.money :as money]
[utils.re-frame :as rf]))
(def money-zero (money/bignumber 0))
(def money-eth (money/bignumber 8000000000000000000))
(def money-snt (money/bignumber 756000000000000000000))
(def main-account-id "0x0Fbd")
(def accounts
[{:address "0x0Fbd"
:name "Main account"
:hidden false
:removed false}
{:address "0x5B03"
:name "Secondary account"
:hidden false
:removed false}])
(def wallet
{:accounts {main-account-id
{:balance {:ETH money-eth :SNT money-snt}
:transactions {}
:max-block 0}
"0x5B03"
{:balance {:ETH money-eth :SNT money-snt}
:transactions {}
:max-block 10}}})
(def prices
{:ETH {:USD 1282.23}
:SNT {:USD 0.0232}})
(def tokens
{"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
{:address "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
:name "Ether"
:symbol :ETH
:decimals 18
:chainId 1}
"0x744d70fdbe2ba4cf95131626614a1763df805b9e"
{:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
:name "Status Network Token"
:symbol :SNT
:decimals 18
:chainId 1}})
(h/deftest-sub :balances
[sub-name]
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:wallet-legacy wallet)
(is (= [{:ETH money-eth
:SNT money-snt}
{:ETH money-eth
:SNT money-snt}]
(rf/sub [sub-name]))))
(h/deftest-sub :wallet-legacy/token->decimals
[sub-name]
(swap! rf-db/app-db assoc :wallet-legacy/all-tokens tokens)
(is (= {:SNT 18 :ETH 18}
(rf/sub [sub-name]))))
(deftest get-balance-total-value-test
(is (= 697.53
(wallet/get-balance-total-value
{:ETH (money/bignumber 1000000000000000000)
:SNT (money/bignumber 100000000000000000000)
:AST (money/bignumber 10000)}
{:ETH {:USD 677.91}
:SNT {:USD 0.1562}
:AST {:USD 4}}
:USD
{:ETH 18
:SNT 18
:AST 4}))))
(h/deftest-sub :portfolio-value
[sub-name]
(testing "returns fallback value when balances and prices are not available"
(is (= "..." (rf/sub [sub-name]))))
(testing "returns zero when balance is not positive"
(let [empty-wallet {:accounts {main-account-id
{:balance {:ETH money-zero
:SNT money-zero}}}}]
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:prices prices
:wallet-legacy empty-wallet
:wallet-legacy/all-tokens tokens)
(is (= "0" (rf/sub [sub-name])))))
(testing "returns formatted value in the default USD currency"
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:prices prices
:wallet-legacy wallet
:wallet-legacy/all-tokens tokens)
(is (= "20,550.76" (rf/sub [sub-name])))))
(h/deftest-sub :account-portfolio-value
[sub-name]
(testing "returns fallback value when balances and prices are not available"
(is (= "..." (rf/sub [sub-name]))))
(testing "returns zero when balance is not positive"
(let [empty-wallet {:accounts {main-account-id
{:balance {:ETH money-zero
:SNT money-zero}}}}]
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:prices prices
:wallet-legacy empty-wallet
:wallet-legacy/all-tokens tokens)
(is (= "0" (rf/sub [sub-name main-account-id])))))
(testing "returns formatted value in the default USD currency"
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:prices prices
:wallet-legacy wallet
:wallet-legacy/all-tokens tokens)
(is (= "10,275.38" (rf/sub [sub-name main-account-id])))))
(def transactions
[{:timestamp "1505912551000"}
{:timestamp "1505764322000"}
{:timestamp "1505750000000"}])
(def grouped-transactions
'({:title "20 Sep"
:key :20170920
:data
({:timestamp "1505912551000"})}
{:title "18 Sep"
:key :20170918
:data
({:timestamp "1505764322000"}
{:timestamp "1505750000000"})}))
(deftest group-transactions-by-date
(testing "Check if transactions are sorted by date"
(is (= (wallet.transactions/group-transactions-by-date transactions)
grouped-transactions))))
@@ -1,12 +1,13 @@
(ns legacy.status-im.ui.components.keyboard-avoid-presentation
(:require
[legacy.status-im.ui.components.react :as react]
[react-native.utils :as rn.utils]
[reagent.core :as reagent]))
(defn keyboard-avoiding-view
[& argv]
(let [[props children] (rn.utils/get-props-and-children argv)]
[]
(let [this (reagent/current-component)
props (reagent/props this)
children (reagent/children this)]
(reagent/as-element
(into [react/keyboard-avoiding-view
(update props
@@ -3,16 +3,16 @@
[legacy.status-im.ui.components.spacing :as spacing]
[legacy.status-im.ui.components.text :as text]
[react-native.core :as rn]
[react-native.utils :as rn.utils]))
[reagent.core :as reagent]))
(defn footer
[& argv]
(let [[props children] (rn.utils/get-props-and-children argv)
[]
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}}
props]
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:small spacing/padding-vertical))}
(into [text/text {:color color}]
children)]))
(reagent/children this))]))
@@ -3,18 +3,18 @@
[legacy.status-im.ui.components.spacing :as spacing]
[legacy.status-im.ui.components.text :as text]
[react-native.core :as rn]
[react-native.utils :as rn.utils]))
[reagent.core :as reagent]))
(defn header
[& argv]
(let [[props children] (rn.utils/get-props-and-children argv)
[]
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}}
props]
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:x-tiny spacing/padding-vertical))}
(into [text/text
{:color color
:style {:margin-top 10}}]
children)]))
(reagent/children this))]))
+7 -5
View File
@@ -3,7 +3,7 @@
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.typography :as typography]
[react-native.core :as rn]
[react-native.utils :as rn.utils]))
[reagent.core :as reagent]))
(defn text-style
[{:keys [size align weight monospace color style]}]
@@ -40,9 +40,11 @@
style))
(defn text
[& argv]
(let [[props children] (rn.utils/get-props-and-children argv)]
(into [rn/text
[]
(let [this (reagent/current-component)
props (reagent/props this)
component rn/text]
(into [component
(merge {:style (text-style props)}
(dissoc props
:style
@@ -51,4 +53,4 @@
:color
:align
:animated?))]
children)))
(reagent/children this))))
@@ -20,7 +20,7 @@
current-fleet
webview-debug
test-networks-enabled?
is-goerli-enabled?]}]
is-sepolia-enabled?]}]
(keep
identity
[{:size :small
@@ -112,13 +112,13 @@
:accessory :switch
:active test-networks-enabled?}
{:size :small
:title "Enable Goerli as test network"
:title "Enable Sepolia as test network"
:accessibility-label :enable-sepolia-as-test-network
:container-margin-bottom 8
:on-press
#(re-frame/dispatch [:profile.settings/toggle-goerli-test-network])
#(re-frame/dispatch [:profile.settings/toggle-sepolia-test-network])
:accessory :switch
:active is-goerli-enabled?}
:active is-sepolia-enabled?}
{:size :small
:title (i18n/label :t/set-currency)
:accessibility-label :wallet-change-currency
@@ -139,7 +139,7 @@
(views/defview advanced-settings
[]
(views/letsubs [test-networks-enabled? [:profile/test-networks-enabled?]
is-goerli-enabled? [:profile/is-goerli-enabled?]
is-sepolia-enabled? [:profile/is-sepolia-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
webview-debug [:profile/webview-debug]
network-name [:network-name]
@@ -156,6 +156,6 @@
:dev-mode? false
:webview-debug webview-debug
:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?})
:is-sepolia-enabled? is-sepolia-enabled?})
:key-fn (fn [_ i] (str i))
:render-fn render-item}]))
@@ -11,6 +11,7 @@
[legacy.status-im.ui.screens.browser.accounts :as accounts]
[legacy.status-im.ui.screens.browser.empty-tab.styles :as styles]
[legacy.status-im.ui.screens.browser.views :as browser]
[legacy.status-im.ui.screens.wallet.components.views :as components]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[utils.i18n :as i18n]
@@ -143,7 +144,7 @@
:container-style styles/input-container-style
:accessibility-label :dapp-url-input
:return-key-type :go}]
[react/view {:style {:height 1 :background-color (colors/alpha colors/black 0.1)}}]
[components/separator-dark]
[list/flat-list
{:header [list-header (empty? bookmarks)]
:data bookmarks
@@ -8,6 +8,7 @@
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.screens.wallet.components.views :as components]
[legacy.status-im.utils.utils :as utils]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
@@ -31,7 +32,7 @@
:subtitle (utils/get-shortened-checksum-address (:address account))
:accessory [icons/icon :main-icons/check {:color colors/gray}]}]
[react/view {:padding-vertical 8}
[react/view {:style {:height 1 :background-color (colors/alpha colors/black 0.1)}}]]
[components/separator]]
[list.item/list-item
{:theme :negative
:title (i18n/label :t/revoke-access)
@@ -85,7 +86,7 @@
(js/setTimeout
#(browser/share-link url)
200))}]
[react/view {:style {:height 1 :background-color (colors/alpha colors/black 0.1)}}]])
[components/separator]])
(if connected?
[list.item/list-item
{:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
@@ -8,6 +8,7 @@
[legacy.status-im.ui.components.plus-button :as components.plus-button]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.components.topbar :as topbar]
[legacy.status-im.ui.screens.wallet.components.views :as components]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[utils.i18n :as i18n]
@@ -66,7 +67,7 @@
:on-press #(do (re-frame/dispatch [:browser.ui/clear-all-browsers-pressed])
(re-frame/dispatch [:browser.ui/open-empty-tab]))}]
:title (i18n/label :t/tabs)}]
[react/view {:style {:height 1 :background-color (colors/alpha colors/black 0.1)}}]
[components/separator-dark]
[list/flat-list
{:data (conj browsers
{:empty-tab true
@@ -16,6 +16,7 @@
[legacy.status-im.ui.screens.browser.permissions.views :as permissions.views]
[legacy.status-im.ui.screens.browser.site-blocked.views :as site-blocked.views]
[legacy.status-im.ui.screens.browser.styles :as styles]
[legacy.status-im.ui.screens.wallet.components.views :as components]
[legacy.status-im.utils.js-resources :as js-res]
[re-frame.core :as re-frame]
[react-native.permissions :as components.permissions]
@@ -257,7 +258,7 @@
url-original (browser/get-current-url current-browser)]
[react/view {:style styles/browser}
[toolbar-content url url-original secure? url-editing? unsafe?]
[react/view {:style {:height 1 :background-color (colors/alpha colors/black 0.1)}}]
[components/separator-dark]
[react/view
(when loading?
[connectivity/loading-indicator-anim window-width])]
@@ -16,6 +16,7 @@
[legacy.status-im.ui.components.topbar :as topbar]
[legacy.status-im.ui.screens.chat.utils :as chat.utils]
[legacy.status-im.ui.screens.profile.components.views :as profile.components]
[legacy.status-im.ui.screens.wallet.send.sheets :as sheets]
[legacy.status-im.utils.utils :as utils]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
@@ -291,7 +292,9 @@
:title (:name account)
:subtitle (utils/get-shortened-checksum-address (:address account))
:chevron true
:on-press #(js/alert "funcitonality no longer supported")}]))
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content (fn [] [sheets/accounts-list :from
::ens/change-address])}])}]))
(defn- registration
[checked contract address public-key]
@@ -8,6 +8,24 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn local-notifications
[]
(let [{:keys [enabled?]} (rf/sub [:push-notifications/wallet-transactions])
{:keys [notifications-enabled?]} (rf/sub [:profile/profile])]
[:<>
[quo/separator
{:color (:ui-02 @colors/theme)
:style {:margin-vertical 8}}]
[quo/list-header (i18n/label :t/local-notifications)]
[list.item/list-item
{:size :small
:title (i18n/label :t/notifications-transactions)
:accessibility-label :notifications-button
:active (and notifications-enabled? enabled?)
:on-press #(rf/dispatch [:push-notifications.wallet/switch-transactions
(not enabled?)])
:accessory :switch}]]))
(defn notifications-settings-ios
[]
(let [{:keys [notifications-enabled?
@@ -45,7 +63,8 @@
:on-press #(rf/dispatch
[:push-notifications/switch-block-mentions
(not push-notifications-block-mentions?)])
:accessory :switch}]]))
:accessory :switch}]
[local-notifications]]))
(defn notifications-settings-android
[]
@@ -57,7 +76,8 @@
:subtitle (i18n/label :t/local-notifications-subtitle)
:active notifications-enabled?
:on-press #(rf/dispatch [:push-notifications/switch (not notifications-enabled?)])
:accessory :switch}]]))
:accessory :switch}]
[local-notifications]]))
(defn notifications-settings
[]
@@ -9,6 +9,8 @@
[legacy.status-im.ui.screens.reset-password.views :as reset-password.views]
[legacy.status-im.ui.screens.signing.sheets :as signing-sheets]
[legacy.status-im.ui.screens.signing.views :as signing]
[legacy.status-im.ui.screens.wallet.request.views :as request]
[legacy.status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase]
[re-frame.core :as re-frame]
[react-native.platform :as platform]
[reagent.core :as reagent]))
@@ -129,6 +131,12 @@
(vector? view)
view
(= :signing-phrase view)
[signing-phrase/signing-phrase]
(= :share-account view)
[request/share-address]
(= :share-chat-key view)
[profile.user/share-chat-key]
@@ -49,6 +49,19 @@
[legacy.status-im.ui.screens.sync-settings.views :as sync-settings]
[legacy.status-im.ui.screens.wakuv2-settings.edit-node.views :as edit-wakuv2-node]
[legacy.status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
[legacy.status-im.ui.screens.wallet.account-settings.views :as account-settings]
[legacy.status-im.ui.screens.wallet.account.views :as wallet.account]
[legacy.status-im.ui.screens.wallet.accounts-manage.views :as accounts-manage]
[legacy.status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[legacy.status-im.ui.screens.wallet.add-new.views :as add-account]
[legacy.status-im.ui.screens.wallet.buy-crypto.views :as wallet.buy-crypto]
[legacy.status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles]
[legacy.status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
[legacy.status-im.ui.screens.wallet.recipient.views :as recipient]
[legacy.status-im.ui.screens.wallet.send.views :as wallet.send]
[legacy.status-im.ui.screens.wallet.settings.views :as wallet-settings]
[legacy.status-im.ui.screens.wallet.swap.views :as wallet.swap]
[legacy.status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.contexts.chat.group-details.view :as group-details]
[utils.i18n :as i18n]))
@@ -93,11 +106,86 @@
;;TODO custom subtitle
:component group-chat/new-group}
;;WALLET
{:name :wallet-legacy
:on-focus [:wallet-legacy/tab-opened]
;;TODO wallet redesign
;;:options {:statusBar {:backgroundColor quo.colors/neutral-5}}
:component wallet.accounts/accounts-overview-old}
{:name :wallet-account
;;TODO dynamic titleaccounts-overview
:options {:insets {:top? true}}
:component wallet.account/account}
{:name :add-new-account
;;TODO dynamic title
:options {:insets {:top? true :bottom? true}}
:component add-account/add-account-view}
{:name :add-new-account-pin
;;TODO dynamic title
:options {:insets {:top? true}}
:component add-account/pin}
{:name :account-settings
;;TODO dynamic title
:options {:insets {:top? true}}
:component account-settings/account-settings}
{:name :wallet-transaction-details
;;TODO dynamic title
:options {:insets {:top? true}}
:component wallet-transactions/transaction-details}
{:name :wallet-settings-assets
;;TODO dynamic title
:options {:insets {:top? true}}
:component wallet-settings/manage-assets}
{:name :wallet-add-custom-token
:on-focus [:wallet-legacy/wallet-add-custom-token]
:options {:topBar {:title {:text (i18n/label :t/add-custom-token)}}
:insets {:top? true}}
:component custom-tokens/add-custom-token}
{:name :wallet-custom-token-details
;;TODO dynamic title
:options {:insets {:top? true}}
:component custom-tokens/custom-token-details}
{:name :currency-settings
:options {:topBar {:title {:text (i18n/label :t/main-currency)}}
:insets {:top? true}}
:component currency-settings/currency-settings}
{:name :manage-accounts
:options {:topBar {:title {:text (i18n/label :t/wallet-manage-accounts)}}
:insets {:top? true}}
:component accounts-manage/manage}
{:name :token-swap
;;TODO dynamic title
:options {:insets {:top? true}}
:component wallet.swap/swap}
{:name :token-swap-advanced-nonce
:options {:topBar {:title {:text (i18n/label :t/nonce)}}
:insets {:top? true}}
:component wallet.swap/nonce-modal}
{:name :token-swap-advanced-approve-token
:options {:topBar {:title {:text (i18n/label :t/approve-token)}}
:insets {:top? true}}
:component wallet.swap/approve-token-modal}
{:name :token-swap-advanced-transaction-fee
:options {:topBar {:title {:text (i18n/label :t/transaction-fee)}}
:insets {:top? true}}
:component wallet.swap/transaction-fee-modal}
{:name :token-swap-advanced-swap-details
:options {:topBar {:title {:text (i18n/label :t/swap-details)}}
:insets {:top? true}}
:component wallet.swap/swap-details-modal}
{:name :swap-asset-selector
;;TODO dynamic title
:options {:insets {:top? true}}
:component wallet.swap/asset-selector}
;;PROFILE
{:name :my-profile
@@ -316,6 +404,20 @@
:top? true}}
:component communities.invite/legacy-invite}
;[Wallet] Recipient
{:name :recipient
;;TODO accessories
:options {:insets {:bottom? true
:top? true}}
:component recipient/recipient}
;[Wallet] New favourite
{:name :new-favourite
:options {:topBar {:title {:text (i18n/label :t/new-favourite)}}
:insets {:bottom? true
:top? true}}
:component recipient/new-favourite}
;QR Scanner
{:name :qr-scanner
;;TODO custom topbar
@@ -336,6 +438,44 @@
:top? true}}
:component notifications-settings/notifications-settings}
;[Wallet] Prepare Transaction
{:name :prepare-send-transaction
:on-dissmiss [:wallet-legacy/cancel-transaction-command]
:options {:topBar {:title {:text (i18n/label :t/send-transaction)}}
:swipeToDismiss false
:hardwareBackButton {:dismissModalOnPress false}
:insets {:bottom? true
:top? true}}
:component wallet.send/prepare-send-transaction}
;[Wallet] Request Transaction
{:name :request-transaction
:on-dissmiss [:wallet-legacy/cancel-transaction-command]
:options {:topBar {:title {:text (i18n/label :t/request-transaction)}}
:swipeToDismiss false
:hardwareBackButton {:dismissModalOnPress false}
:insets {:bottom? true
:top? true}}
:component wallet.send/request-transaction}
;[Wallet] Buy crypto
{:name :buy-crypto
:insets {:bottom? true}
:component wallet.buy-crypto/container}
;[Wallet] Buy crypto website
{:name :buy-crypto-website
;;TODO subtitle
:options {:insets {:bottom? true
:top? true}}
:component wallet.buy-crypto/website}
{:name :nft-details
;;TODO dynamic title
:options {:insets {:bottom? true
:top? true}}
:component wallet.collectibles/nft-details-modal}
;[Browser] New bookmark
{:name :new-bookmark
;;TODO dynamic title
@@ -2,6 +2,7 @@
(:require-macros [legacy.status-im.utils.views :as views])
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.tokens :as tokens]
[legacy.status-im.react-native.resources :as resources]
[legacy.status-im.signing.eip1559 :as eip1559]
[legacy.status-im.ui.components.bottom-panel.views :as bottom-panel]
@@ -14,8 +15,10 @@
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.screens.signing.sheets :as sheets]
[legacy.status-im.ui.screens.signing.styles :as styles]
[legacy.status-im.ui.screens.wallet.components.views :as wallet.components]
[legacy.status-im.utils.deprecated-types :as types]
[legacy.status-im.utils.utils :as utils]
[legacy.status-im.wallet.utils :as wallet.utils]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.contexts.profile.utils :as profile.utils]
@@ -54,7 +57,11 @@
:style {:margin-right 8}}
display-symbol]
(if icon
[react/view]
[wallet.components/token-icon
(assoc icon
:style {:background-color colors/gray-lighter
:border-radius 16}
:image-style {:width 24 :height 24})]
[chat-icon/custom-icon-view-list (:name token) color 32])]}]
[separator]]))
@@ -321,6 +328,7 @@
[{:keys [from contact amount token cancel?] :as tx}]
(views/letsubs [fee [:signing/fee]
sign [:signing/sign]
chain [:current-network]
{:keys [amount-error gas-error gas-error-state insufficient-balalce?]}
[:signing/amount-errors (:address from)]
keycard-multiaccount? [:keycard-multiaccount?]
@@ -329,8 +337,8 @@
mainnet? [:mainnet?]
prices-loading? [:prices-loading?]
management-enabled? [:wallet-legacy/transactions-management-enabled?]]
(let [display-symbol nil
fee-display-symbol nil]
(let [display-symbol (wallet.utils/display-symbol token)
fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))]
[react/view (styles/sheet)
[header sign tx display-symbol fee fee-display-symbol]
[separator]
@@ -38,7 +38,7 @@
(or owned free)
(re-frame/dispatch [:stickers/install-pack id])
not-enough-snt? nil
:else (fn [] (js/alert "feature no longer supported")))}
:else (re-frame/dispatch [:stickers/buy-pack id]))}
[react/view (styles/price-badge (and (not (or owned free)) not-enough-snt?))
(when (and (not free) (not owned))
[icons/tiny-icon :tiny-icons/tiny-snt
@@ -0,0 +1,68 @@
(ns legacy.status-im.ui.screens.wallet.account.styles
(:require
[clojure.string :as string]
[legacy.status-im.ui.components.animation :as animation]
[legacy.status-im.ui.components.colors :as colors]))
(defn card
[window-width color]
{:width (- window-width 30)
:height 161
:background-color (if (string/blank? color)
colors/blue
color)
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
:shadow-opacity 1
:shadow-color (if (colors/dark?)
"rgba(0, 0, 0, 0.75)"
"rgba(0, 9, 26, 0.12)")
:elevation 2
:border-radius 8
:justify-content :space-between})
(defn divider
[]
{:height 52
:width 1
:background-color colors/black-transparent-20
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
:shadow-opacity 1
:shadow-color (if (colors/dark?)
"rgba(0, 0, 0, 0.75)"
"rgba(0, 9, 26, 0.12)")})
(defn bottom-send-recv-buttons-raise
[anim-y]
(animation/timing
anim-y
{:toValue 0
:duration 200
:easing (.-ease ^js animation/easing)
:useNativeDriver true}))
(defn bottom-send-recv-buttons-lower
[anim-y y]
(animation/timing
anim-y
{:toValue y
:duration 200
:easing (.-ease ^js animation/easing)
:useNativeDriver true}))
(def round-action-button
{:background-color colors/blue
:height 44
:flex 1
:justify-content :center
:align-items :center
:width 44
:border-radius 44})
(def top-actions
{:flex 1
:flex-direction :row
:justify-content :space-between
:width "60%"
:align-self :center})
@@ -0,0 +1,363 @@
(ns legacy.status-im.ui.screens.wallet.account.views
(:require
[legacy.status-im.ui.components.animation :as animation]
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.core :as components.core]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.components.spacing :as spacing]
[legacy.status-im.ui.components.tabs :as tabs]
[legacy.status-im.ui.components.topbar :as topbar]
[legacy.status-im.ui.screens.wallet.account.styles :as styles]
[legacy.status-im.ui.screens.wallet.accounts.common :as common]
[legacy.status-im.ui.screens.wallet.accounts.sheets :as sheets]
[legacy.status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto]
[legacy.status-im.ui.screens.wallet.collectibles.views :as collectibles.views]
[legacy.status-im.ui.screens.wallet.transactions.views :as history]
[quo.core :as quo]
[quo.foundations.colors :as quo.colors]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.config :as config]
[utils.address :as address]
[utils.i18n :as i18n]
[utils.re-frame :as rf])
(:require-macros [legacy.status-im.utils.views :as views]))
(def state (reagent/atom {:tab :assets}))
(def selected-tab (reagent/atom :tokens))
(defn button
[label icon color handler]
[react/touchable-highlight {:on-press handler :style {:flex 1}}
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/view {:flex-direction :row :align-items :center}
[icons/icon icon {:color color}]
[react/text {:style {:margin-left 8 :color color}} label]]]])
(def button-group-height 52)
(views/defview account-card
[{:keys [address color type] :as account}]
(views/letsubs [currency [:wallet-legacy/currency]
portfolio-value [:account-portfolio-value address]
window-width [:dimensions/window-width]
prices-loading? [:prices-loading?]]
[react/view {:style (styles/card window-width color)}
[react/view {:padding 16 :padding-bottom 12 :flex 1 :justify-content :space-between}
[react/view {:style {:flex-direction :row}}
(if prices-loading?
[react/small-loading-indicator :colors/white-persist]
[react/text {:style {:font-size 32 :color colors/white-persist :font-weight "600"}}
portfolio-value])
[react/text {:style {:font-size 32 :color colors/white-transparent-persist :font-weight "600"}}
(str " " (:code currency))]]
[components.core/text
{:number-of-lines 1
:ellipsize-mode :middle
:monospace true
:size :small
:style {:width (/ window-width 3)
:line-height 22
:color colors/white-transparent-70-persist}}
(address/normalized-hex address)]]
[react/view {:position :absolute :top 12 :right 12}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet-legacy/share-popover address])}
[icons/icon :main-icons/share
{:color colors/white-persist
:accessibility-label :share-wallet-address-icon}]]]
[react/view
{:height button-group-height
:background-color colors/black-transparent-20
:border-bottom-right-radius 8
:border-bottom-left-radius 8
:flex-direction :row}
(if (= type :watch)
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/text {:style {:margin-left 8 :color colors/white-persist}}
(i18n/label :t/watch-only)]]
[button
(i18n/label :t/wallet-send)
:main-icons/send
colors/white-persist
#(re-frame/dispatch [:wallet-legacy/prepare-transaction-from-wallet account])])
[react/view {:style (styles/divider)}]
[button
(i18n/label :t/receive)
:main-icons/receive
colors/white-persist
#(re-frame/dispatch [:wallet-legacy/share-popover address])]]]))
(views/defview transactions
[address]
(views/letsubs [data [:wallet-legacy.transactions.history/screen address]]
[history/history-list data address]))
(defn opensea-link
[address]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:browser.ui/open-url (str "https://opensea.io/" address)])}
[react/view
{:style {:flex 1
:padding-horizontal 14
:flex-direction :row
:align-items :center
:background-color colors/blue-light
:height 52}}
[icons/tiny-icon
:tiny-icons/tiny-external
{:color colors/blue
:container-style {:margin-right 5}}]
[react/text
{:style {:color colors/blue}}
(i18n/label :t/check-on-opensea)]]])
(views/defview assets-and-collections-new
[address]
(views/letsubs [{:keys [tokens]} [:wallet-legacy/visible-assets-with-values address]
currency [:wallet-legacy/currency]
opensea-enabled? [:opensea-enabled?]
collectible-collection [:wallet-legacy/collectible-collection address]]
;ethereum-network? [:ethereum-network?]]
(let [tab @selected-tab]
[react/view {:flex 1}
[react/view {:padding-horizontal 20 :padding-bottom 20}
[quo/tabs
{:size 24
:on-change #(reset! selected-tab %)
:default-active :tokens
:data [{:id :tokens :label "Tokens"}
{:id :nft :label "NFTs"}
{:id :activity :label "Activity"}]}]]
(cond
(= tab :tokens)
[react/scroll-view
(for [item tokens]
^{:key (:name item)}
[common/render-asset-new item nil nil (:code currency)])]
(= tab :nft)
[:<>
[opensea-link address]
;; Hide collectibles behind a feature flag
(when config/collectibles-enabled?
(cond
(not opensea-enabled?)
[collectibles.views/enable-opensea-view]
(and opensea-enabled? (seq collectible-collection))
[collectibles.views/nft-collections address]
:else
[react/view {:align-items :center :margin-top 32}
[react/text {:style {:color colors/gray}}
(i18n/label :t/no-collectibles)]]))]
(= tab :activity)
[transactions address])])))
(views/defview bottom-send-recv-buttons
[{:keys [address type] :as account} anim-y]
[react/animated-view
{:style {:background-color colors/white
:bottom 0
:flex-direction :row
:height button-group-height
:position :absolute
:shadow-offset {:width 0 :height 1}
:shadow-opacity 0.75
:shadow-radius 1
:transform [{:translateY anim-y}]
:width "100%"}}
(if (= type :watch)
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/text {:style {:margin-left 8 :color colors/blue-persist}}
(i18n/label :t/watch-only)]]
[button
(i18n/label :t/wallet-send)
:main-icons/send
colors/blue-persist
#(re-frame/dispatch [:wallet-legacy/prepare-transaction-from-wallet account])])
[button
(i18n/label :t/receive)
:main-icons/receive
colors/blue-persist
#(re-frame/dispatch [:wallet-legacy/share-popover address])]])
(defn anim-listener
[anim-y scroll-y]
(let [to-show (atom false)]
(animation/add-listener
scroll-y
(fn [anim]
(let [y-trigger 149]
(cond
(and (>= (.-value anim) y-trigger) (not @to-show))
(animation/start
(styles/bottom-send-recv-buttons-raise anim-y)
#(reset! to-show true))
(and (< (.-value anim) y-trigger) @to-show)
(animation/start
(styles/bottom-send-recv-buttons-lower anim-y button-group-height)
#(reset! to-show false))))))))
(defn round-action-button
[{:keys [icon title on-press]}]
[react/view
{:style {:flex 1
:align-items :center
:margin-vertical (:large spacing/spacing)}}
[react/touchable-opacity
{:style styles/round-action-button
:on-press on-press}
(icons/icon icon {:color colors/white})]
[components.core/text
{:color :secondary
:size :small
:style {:margin-top (:tiny spacing/spacing)}}
title]])
(defn top-actions
[]
[react/view {:style styles/top-actions}
[round-action-button
{:icon :main-icons/add
:title (i18n/label :t/buy-crypto)
:on-press #(re-frame/dispatch [:buy-crypto.ui/open-screen])}]
[round-action-button
{:icon :main-icons/change
:title (i18n/label :t/swap)
:on-press #(re-frame/dispatch [:open-modal :token-swap])}]])
(views/defview assets-and-collections
[address]
(views/letsubs [{:keys [tokens]} [:wallet-legacy/visible-assets-with-values address]
currency [:wallet-legacy/currency]
opensea-enabled? [:opensea-enabled?]
collectible-collection [:wallet-legacy/collectible-collection address]
ethereum-network? [:ethereum-network?]]
(let [{:keys [tab]} @state]
[react/view {:flex 1}
[react/view {:flex-direction :row :margin-bottom 8 :padding-horizontal 4}
[tabs/tab-title state :assets (i18n/label :t/wallet-assets) (= tab :assets)]
(when ethereum-network?
[tabs/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)])
[tabs/tab-title state :history (i18n/label :t/history) (= tab :history)]]
[quo/separator {:style {:margin-top -8}}]
(cond
(= tab :assets)
[:<>
(for [item tokens]
^{:key (:name item)}
[common/render-asset item nil nil (:code currency)])]
(= tab :nft)
[:<>
[opensea-link address]
;; Hide collectibles behind a feature flag
(when config/collectibles-enabled?
(cond
(not opensea-enabled?)
[collectibles.views/enable-opensea-view]
(and opensea-enabled? (seq collectible-collection))
[collectibles.views/nft-collections address]
:else
[react/view {:align-items :center :margin-top 32}
[react/text {:style {:color colors/gray}}
(i18n/label :t/no-collectibles)]]))]
(= tab :history)
[transactions address])])))
(defn account-new
[selected-account]
(let [;{:keys [name address] :as account} (rf/sub [:account-by-address selected-account])
currency (rf/sub [:wallet-legacy/currency])
portfolio-value (rf/sub [:account-portfolio-value selected-account])
width (rf/sub [:dimensions/window-width])
button-width (/ (- width 40 (* 2 12)) 3)]
;fetching-error (rf/sub [:wallet-legacy/fetching-error])]
[react/view
{:flex 1
:background-color (quo.colors/theme-colors quo.colors/white quo.colors/neutral-90)
:border-top-left-radius 20
:border-top-right-radius 20
:elevation 4
:shadow-opacity 1
:shadow-radius 20
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}}
[react/view {:padding 20}
[quo/text {:size :heading-2 :weight :semi-bold} (str portfolio-value " " (:code currency))]]
[react/view
[react/scroll-view {:horizontal true :margin-bottom 32 :showsHorizontalScrollIndicator false}
[react/view {:width 20}]
[quo/button {:size 56 :width button-width :above :i/placeholder} "Buy"]
[react/view {:width 12}]
[quo/button {:size 56 :width button-width :type :secondary :above :i/placeholder} "Send"]
[react/view {:width 12}]
[quo/button {:size 56 :width button-width :type :secondary :above :i/placeholder}
"Receive"]
[react/view {:width 12}]
[quo/button {:size 56 :width button-width :type :secondary :above :i/placeholder} "Swap"]
[react/view {:width 20}]]]
[assets-and-collections-new selected-account]]))
(views/defview account
[]
(views/letsubs [{:keys [name address] :as current-account} [:multiaccount/current-account]
fetching-error [:wallet-legacy/fetching-error]]
(let [anim-y (animation/create-value button-group-height)
scroll-y (animation/create-value 0)]
(anim-listener anim-y scroll-y)
[:<>
[topbar/topbar
{:title name
:right-accessories
[{:icon :main-icons/more
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content sheets/account-settings
:content-height 60}])}]}]
[react/animated-scroll-view
{:contentContainerStyle {:padding-bottom button-group-height}
:on-scroll (animation/event
[{:nativeEvent {:contentOffset {:y scroll-y}}}]
{:useNativeDriver true})
:scrollEventThrottle 1
:refreshControl (common/refresh-control
(and
@common/updates-counter
@(re-frame/subscribe [:wallet-legacy/refreshing-history?])))}
(when fetching-error
[react/view
{:style {:flex 1
:align-items :center
:margin 8}}
[icons/icon
:main-icons/warning
{:color :red
:container-style {:background-color (colors/get-color :negative-02)
:height 40
:width 40
:border-radius 20
:align-items :center
:justify-content :center}}]
[react/view
{:style {:justify-content :center
:align-items :center
:margin-top 8
:margin-horizontal 67.5
:text-align :center}}
[components.core/text
{:color :secondary
:size :small
:style {:text-align :center}}
(i18n/label :t/transfers-fetching-failure)]]])
[react/view {:padding-left 16}
[react/scroll-view {:horizontal true}
[react/view {:flex-direction :row :padding-top 8 :padding-bottom 12}
[account-card current-account]]]]
(if config/swap-enabled?
[top-actions]
[buy-crypto/banner])
[assets-and-collections address]]
[bottom-send-recv-buttons current-account anim-y]])))
@@ -0,0 +1,177 @@
(ns legacy.status-im.ui.screens.wallet.account-settings.views
(:require-macros [legacy.status-im.utils.views :refer [defview letsubs]])
(:require
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.copyable-text :as copyable-text]
[legacy.status-im.ui.components.core :as quo]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.components.toolbar :as toolbar]
[legacy.status-im.ui.components.topbar :as topbar]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[utils.security.core :as security]))
(defn not-valid-password?
[password]
(< (count (security/safe-unmask-data password)) 6))
(defn delete-account
[_]
(let [password (reagent/atom nil)
text-input-ref (atom nil)
error (reagent/atom nil)]
(fn [account]
(when (and @text-input-ref error (not @password))
(.clear ^js @text-input-ref))
[react/view {:padding 20 :width 300}
[quo/text-input
{:style {:margin-bottom 40}
:label (i18n/label :t/password)
:show-cancel false
:secure-text-entry true
:return-key-type :next
:on-submit-editing nil
:auto-focus true
:on-change-text #(reset! password (security/mask-data %))
:get-ref #(reset! text-input-ref %)
:error (when (and @error (not @password))
(if (= :wrong-password @error)
(i18n/label :t/wrong-password)
(str @error)))}]
[quo/button
{:on-press (fn []
(re-frame/dispatch [:wallet-legacy.accounts/delete-key
account
@password
#(reset! error :wrong-password)])
(reset! password nil))
:theme :negative
:accessibility-label :delete-account-confirm
:disabled (not-valid-password? @password)}
(i18n/label :t/delete)]])))
(defview colors-popover
[selected-color on-press]
(letsubs [width [:dimensions/window-width]]
[react/view {:flex 1 :padding-bottom 16}
[react/scroll-view {:style {:margin 16}}
(doall
(for [color colors/account-colors]
^{:key color}
[react/touchable-highlight {:on-press #(on-press color)}
[react/view
{:height 52
:background-color color
:border-radius 8
:width (* 0.7 width)
:justify-content :center
:padding-left 12
:margin-bottom 16}
[react/view
{:height 32
:width 32
:border-radius 20
:align-items :center
:justify-content :center
:background-color colors/black-transparent}
(when (= selected-color color)
[icons/icon :main-icons/check {:color colors/white}])]]]))]
[toolbar/toolbar
{:center
[quo/button
{:on-press #(re-frame/dispatch [:hide-popover])
:type :secondary}
(i18n/label :t/cancel)]}]]))
(defn property
[label value]
[react/view {:margin-top 28}
[react/text {:style {:color colors/gray}} label]
(if (string? value)
[react/text {:style {:margin-top 6}} value]
value)])
(defview account-settings
[]
(letsubs [{:keys [address color path type] :as account} [:multiaccount/current-account]
new-account (reagent/atom nil)
keycard? [:keycard-multiaccount?]]
[react/keyboard-avoiding-view
{:style {:flex 1}
:ignore-offset true}
[topbar/topbar
(cond-> {:title (i18n/label :t/account-settings)}
(and @new-account (not= "" (:name @new-account)))
(assoc :right-accessories
[{:label (i18n/label :t/apply)
:on-press
#(do
(re-frame/dispatch [:wallet-legacy.accounts/save-account
account
@new-account])
(reset! new-account nil))}]))]
[react/scroll-view
{:keyboard-should-persist-taps :handled
:style {:flex 1}}
[react/view {:padding-bottom 28 :padding-top 10}
[react/view {:margin-horizontal 16}
[quo/text-input
{:label (i18n/label :t/account-name)
:auto-focus false
:default-value (:name account)
:accessibility-label :enter-account-name
:on-change-text #(swap! new-account assoc :name %)}]
[react/text {:style {:margin-top 16 :color colors/gray}} (i18n/label :t/account-color)]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:show-popover
{:view [colors-popover color
(fn [new-color]
(swap! new-account assoc :color new-color)
(re-frame/dispatch [:hide-popover]))]
:style {:max-height "60%"}}])}
[react/view
{:height 52
:margin-top 12
:background-color (or (:color @new-account) color)
:border-radius 8
:align-items :flex-end
:justify-content :center
:padding-right 12}
[icons/icon :main-icons/dropdown {:color colors/white}]]]
[property (i18n/label :t/type)
(case type
:watch (i18n/label :t/watch-only)
(:key :seed) (i18n/label :t/off-status-tree)
(i18n/label :t/on-status-tree))]
[property (i18n/label :t/wallet-address)
[copyable-text/copyable-text-view
{:copied-text address}
[quo/text
{:style {:margin-top 6}
:monospace true}
address]]]
(when-not (= type :watch)
[property (i18n/label :t/derivation-path)
[copyable-text/copyable-text-view
{:copied-text path}
[quo/text
{:style {:margin-top 6}
:monospace true} path]]])
(when-not (= type :watch)
[property (i18n/label :t/storage)
(i18n/label (if keycard?
:t/keycard
:t/this-device))])]
(when (#{:key :seed :watch} type)
[react/view
[react/view {:margin-bottom 8 :margin-top 28 :height 1 :background-color colors/gray-lighter}]
[list.item/list-item
{:theme :negative
:title (i18n/label :t/delete-account)
:on-press #(if (= :watch type)
(re-frame/dispatch [:wallet-legacy.settings/show-delete-account-confirmation
account])
(re-frame/dispatch [:show-popover {:view [delete-account account]}]))}]])]]]))
@@ -0,0 +1,81 @@
(ns legacy.status-im.ui.screens.wallet.accounts.common
(:require
[legacy.status-im.ui.components.chat-icon.screen :as chat-icon]
[legacy.status-im.ui.components.core :as components]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.screens.wallet.components.views :as wallet.components]
[legacy.status-im.utils.utils :as utils.utils]
[legacy.status-im.wallet.utils :as wallet.utils]
[quo.core :as quo]
[quo.foundations.colors :as quo.colors]
[re-frame.core :as re-frame]
[react-native.core :as rn]
[reagent.core :as reagent]))
;; Note(rasom): sometimes `refreshing` might get stuck on iOS if action happened
;; too fast. By updating this atom in 1s we ensure that `refreshing?` property
;; is updated properly in this case.
(def updates-counter (reagent/atom 0))
(defn schedule-counter-reset
[]
(utils.utils/set-timeout
(fn []
(swap! updates-counter inc)
(when @(re-frame/subscribe [:wallet-legacy/refreshing-history?])
(schedule-counter-reset)))
1000))
(defn refresh-action
[]
(schedule-counter-reset)
(re-frame/dispatch [:wallet-legacy.ui/pull-to-refresh-history]))
(defn refresh-control
[refreshing?]
(reagent/as-element
[rn/refresh-control
{:refreshing (boolean refreshing?)
:onRefresh refresh-action}]))
(defn render-asset
[{:keys [icon decimals amount color value] :as token} _ _ currency]
[list.item/list-item
{:title [components/text {:weight :medium}
[components/text {:weight :inherit}
(str (if amount
(wallet.utils/format-amount amount decimals)
"...")
" ")]
[components/text
{:color :secondary
:weight :inherit}
(wallet.utils/display-symbol token)]]
:subtitle (str (if value value "...") " " currency)
:accessibility-label (str (:symbol token) "-asset-value")
:icon (if icon
[wallet.components/token-icon icon]
[chat-icon/custom-icon-view-list (:name token) color])}])
(defn render-asset-new
[{:keys [icon decimals amount color value name] :as token} _ _ currency]
[rn/view {:height 56 :margin-horizontal 8 :margin-top 4}
[rn/view {:position :absolute :left 12 :top 12}
(if icon
[wallet.components/token-icon (merge icon {:width 32 :height 32})]
[chat-icon/custom-icon-view-list (:name token) color])]
[rn/view {:position :absolute :left 52 :top 8 :right 12}
[rn/view {:flex-direction :row :justify-content :space-between :align-items :center}
[quo/text {:weight :semi-bold :style {:height 22}}
name]
[quo/text {:size :paragraph-2 :weight :medium}
(str (if value value "...") " " currency)]]
[quo/text
{:size :paragraph-2
:weight :medium
:style {:color (quo.colors/theme-colors quo.colors/neutral-50 quo.colors/neutral-40)}}
(str (if amount
(wallet.utils/format-amount amount decimals)
"...")
" "
(wallet.utils/display-symbol token))]]])
@@ -0,0 +1,135 @@
(ns legacy.status-im.ui.screens.wallet.accounts.sheets
(:require
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.react :as react]
[re-frame.core :as re-frame]
[utils.i18n :as i18n]))
(defn hide-sheet-and-dispatch
[event]
(re-frame/dispatch [:bottom-sheet/hide-old])
(re-frame/dispatch event))
(defn accounts-options
[mnemonic]
(fn []
[:<>
[list.item/list-item
{:theme :accent
:title (i18n/label :t/wallet-manage-accounts)
:icon :main-icons/account
:accessibility-label :wallet-manage-accounts
:on-press #(hide-sheet-and-dispatch
[:navigate-to :manage-accounts])}]
[list.item/list-item
{:theme :accent
:title (i18n/label :t/wallet-manage-assets)
:icon :main-icons/token
:accessibility-label :wallet-manage-assets
:on-press #(hide-sheet-and-dispatch
[:navigate-to :wallet-settings-assets])}]
[list.item/list-item
{:theme :accent
:title (i18n/label :t/scan-tokens)
:icon :main-icons/refresh
:accessibility-label :wallet-scan-token
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy/update-balances nil true])}]
[list.item/list-item
{:theme :accent
:title (i18n/label :t/set-currency)
:icon :main-icons/language
:accessibility-label :wallet-set-currency
:on-press #(hide-sheet-and-dispatch
[:navigate-to :currency-settings])}]
[list.item/list-item
{:theme :accent
:title (i18n/label :t/view-signing)
:icon :main-icons/info
:on-press #(hide-sheet-and-dispatch
[:show-popover {:view :signing-phrase}])}]
(when mnemonic
[list.item/list-item
{:theme :negative
:title (i18n/label :t/wallet-backup-recovery-title)
:icon :main-icons/security
:accessibility-label :wallet-backup-recovery-title
:on-press #(hide-sheet-and-dispatch
[:navigate-to :backup-seed])}])]))
(defn account-card-actions
[account type wallet]
[react/view
(when-not (= type :watch)
[list.item/list-item
{:theme :accent
:title (i18n/label :t/wallet-send)
:icon :main-icons/send
:accessibility-label :send-transaction-button
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy/prepare-transaction-from-wallet account])}])
[list.item/list-item
{:theme :accent
:title (i18n/label :t/share)
:icon :main-icons/share
:accessibility-label :share-account-button
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy/share-popover (:address account)])}]
(when-not wallet
[list.item/list-item
{:theme :accent
:title (i18n/label :t/hide)
:icon :main-icons/hide
:accessibility-label :hide-account-button
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy.accounts/save-account account {:hidden true}])}])])
(defn add-account
[]
(let [keycard? @(re-frame/subscribe [:keycard-multiaccount?])]
[react/view
[list.item/list-item
{:title (i18n/label :t/generate-a-new-account)
:theme :accent
:icon :main-icons/add
:accessibility-label :add-account-sheet-generate
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy.accounts/start-adding-new-account
{:type :generate}])}]
[list.item/list-item
{:theme :accent
:title (i18n/label :t/add-a-watch-account)
:icon :main-icons/show
:accessibility-label :add-account-sheet-watch
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy.accounts/start-adding-new-account
{:type :watch}])}]
(when-not keycard?
[list.item/list-item
{:title (i18n/label :t/enter-a-seed-phrase)
:theme :accent
:icon :main-icons/text
:accessibility-label :add-account-sheet-seed
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy.accounts/start-adding-new-account
{:type :seed}])}])
(when-not keycard?
[list.item/list-item
{:title (i18n/label :t/enter-a-private-key)
:theme :accent
:icon :main-icons/address
:accessibility-label :add-account-sheet-private-key
:on-press #(hide-sheet-and-dispatch
[:wallet-legacy.accounts/start-adding-new-account
{:type :key}])}])]))
(defn account-settings
[]
[react/view
[list.item/list-item
{:theme :accent
:title (i18n/label :t/account-settings)
:accessibility-label :account-settings-bottom-sheet
:icon :main-icons/info
:on-press #(hide-sheet-and-dispatch
[:navigate-to :account-settings])}]])
@@ -0,0 +1,165 @@
(ns legacy.status-im.ui.screens.wallet.accounts.styles
(:require
[clojure.string :as string]
[legacy.status-im.ui.components.colors :as colors]))
(def dot-size 6)
(def dot-container
{:height 200
:flex-direction :column
:flex-wrap :wrap
:padding-horizontal 8})
(defn dot-selector
[]
{:flex-direction :row
:justify-content :space-between
:align-items :center})
(defn dot-style
[selected]
{:background-color (if selected colors/blue colors/blue-light)
:overflow :hidden
:opacity 1
:margin-horizontal 3
:width dot-size
:height dot-size
:border-radius 3})
(defn container
[{:keys [minimized]}]
(when-not minimized
{:padding-bottom 8
:padding-horizontal 16}))
(defn value-container
[{:keys [minimized animation]}]
(when minimized
{:opacity animation}))
(defn value-text
[{:keys [minimized]}]
{:font-size (if minimized 20 32)
:line-height 40
:color colors/black})
(defn accounts-mnemonic
[{:keys [animation]}]
{:opacity (if animation 1 0)
:flex 1
:justify-content :center
:position :absolute
:top 0
:bottom 0
:left 0})
(def card-margin 8)
(defn page-width
[card-width]
(+ card-width (* card-margin 2)))
(defn card-common
[card-width]
{:margin card-margin
:width card-width
:height 82
:border-radius 16})
(defn card
[color card-width]
(merge (card-common card-width)
{:background-color (if (string/blank? color)
colors/blue
color)
:justify-content :space-between
:padding-horizontal 12
:padding-top 12
:padding-bottom 10}))
(defn add-card
[card-width]
(merge (card-common card-width)
{:background-color colors/white
:flex-direction :row
:border-width 1
:border-color colors/gray-lighter
:justify-content :center
:align-items :center}))
(def add-text
{:color colors/blue
:margin-left 8
:font-weight "500"
:font-size 15
:line-height 22})
(def card-name
{:color colors/white-persist
:font-weight "500"
:font-size 15
:line-height 22})
(def card-address
{:number-of-lines 1
:ellipsize-mode :middle
:size :small
:monospace true
:style {:width 110
:font-size 15
:line-height 22
:color colors/white-transparent-70-persist}})
(def card-value
{:color colors/white-persist
:font-size 22
:font-weight "500"})
(def card-value-currency
{:color colors/white-persist
:font-size 22
:font-weight "500"})
(def card-icon-more
{:border-radius 32
:width 36
:height 36
:justify-content :center
:align-items :center
:background-color colors/black-transparent})
(def card-icon-type
{:border-radius 32
:width 36
:height 36
:justify-content :center
:align-items :center
:margin-left :auto
:margin-right 12
:background-color colors/white-persist})
(def send-button-container
{:position :absolute
:z-index 2
:align-items :center
:justify-content :center
:left 0
:right 0
:bottom 56
:height 40})
(defn send-button
[]
{:width 40
:height 40
:background-color colors/blue
:border-radius 20
:align-items :center
:justify-content :center
:shadow-offset {:width 0 :height 1}
:shadow-radius 6
:shadow-opacity 1
:shadow-color (if (colors/dark?)
"rgba(0, 0, 0, 0.75)"
"rgba(0, 12, 63, 0.2)")
:elevation 2})
@@ -0,0 +1,302 @@
(ns legacy.status-im.ui.screens.wallet.accounts.views
(:require
[legacy.status-im.keycard.login :as keycard.login]
[legacy.status-im.qr-scanner.core :as qr-scanner]
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.core :as components.core]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.screens.wallet.account.views :as account.views]
[legacy.status-im.ui.screens.wallet.accounts.common :as common]
[legacy.status-im.ui.screens.wallet.accounts.sheets :as sheets]
[legacy.status-im.ui.screens.wallet.accounts.styles :as styles]
[legacy.status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto]
[quo.core :as quo]
[quo.foundations.colors :as quo.colors]
[re-frame.core :as re-frame]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[utils.i18n :as i18n])
(:require-macros [legacy.status-im.utils.views :as views]))
(views/defview account-card
[{:keys [name color address type wallet] :as account} keycard? card-width]
(views/letsubs [currency [:wallet-legacy/currency]
portfolio-value [:account-portfolio-value address]
prices-loading? [:prices-loading?]]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
:accessibility-label (str "accountcard" name)}
[react/view {:style (styles/card color card-width)}
[react/view {:flex-direction :row :align-items :center :justify-content :space-between}
[react/text {:style styles/card-name} name]
[components.core/text styles/card-address
address]]
[react/view {:flex-direction :row :align-items :center :justify-content :space-between}
[react/view {:style {:flex-direction :row}}
(if prices-loading?
[react/small-loading-indicator :colors/white-persist]
[react/text
{:style styles/card-value
:accessibility-label "account-total-value"} portfolio-value])
[react/text {:style styles/card-value-currency} (str " " (:code currency))]]
(let [icon (cond
(= type :watch)
:main-icons/show
(and (not= type :watch) keycard?)
:main-icons/keycard-account)]
(when icon
[icons/icon icon
{:container-style styles/card-icon-type
:color color}]))
[react/touchable-highlight
{:style styles/card-icon-more
:on-press #(re-frame/dispatch
[:bottom-sheet/show-sheet-old
{:content (fn [] [sheets/account-card-actions account type wallet])
:content-height 130}])}
[icons/icon :main-icons/more {:color colors/white-persist}]]]]]))
(defn add-card
[card-width]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content sheets/add-account
:content-height 260}])
:accessibility-label "add-new-account"}
[react/view {:style (styles/add-card card-width)}
[icons/icon :main-icons/add-circle {:color colors/blue}]
[react/text {:style styles/add-text} (i18n/label :t/add-account)]]])
(views/defview assets
[]
(views/letsubs [{:keys [tokens]} [:wallet-legacy/all-visible-assets-with-values]
currency [:wallet-legacy/currency]]
[:<>
(for [item tokens]
^{:key (:name item)}
[common/render-asset item nil nil (:code currency)])]))
(views/defview send-button
[]
(views/letsubs [account [:multiaccount/default-account]]
[react/view styles/send-button-container
[components.core/button
{:accessibility-label :send-transaction-button
:type :scale
:on-press #(re-frame/dispatch [:wallet-legacy/prepare-transaction-from-wallet
account])}
[react/view (styles/send-button)
[icons/icon :main-icons/send {:color colors/white-persist}]]]]))
(defn dot
[]
(fn [{:keys [selected]}]
[react/view {:style (styles/dot-style selected)}]))
(defn dots-selector
[{:keys [n selected]}]
[react/view {:style (styles/dot-selector)}
(for [i (range n)]
^{:key i}
[dot {:selected (= selected i)}])])
;;ACCOUNTS OLD
(views/defview accounts-old
[]
(views/letsubs [accounts [:multiaccount/visible-accounts]
keycard? [:keycard-multiaccount?]
window-width [:dimensions/window-width]
index (reagent/atom 0)]
(let [card-width (quot window-width 1.1)
page-width (styles/page-width card-width)]
[react/view
{:style {:align-items :center
:flex 1
:justify-content :flex-end}}
[react/scroll-view
{:horizontal true
:deceleration-rate "fast"
:snap-to-interval page-width
:shows-horizontal-scroll-indicator false
:scroll-event-throttle 64
:on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)]
(reset! index (Math/max (Math/round (/ x page-width))
0)))}
[react/view styles/dot-container
(doall
(for [account accounts]
^{:key account}
[account-card account keycard? card-width]))
[add-card card-width]]]
(let [columns (Math/ceil (/ (inc (count accounts)) 2))
totalwidth (* (styles/page-width card-width) columns)
n (Math/ceil (/ totalwidth window-width))]
(when (> n 1)
[dots-selector
{:selected @index
:n n}]))])))
;;TOTAL VALUE OLD
(views/defview total-value-old
[{:keys [animation minimized]}]
(views/letsubs [currency [:wallet-legacy/currency]
portfolio-value [:portfolio-value]
empty-balances? [:empty-balances?]
frozen-card? [:keycard/frozen-card?]
{:keys [mnemonic]} [:profile/profile]]
[reanimated/view {:style (styles/container {:minimized minimized})}
(when (or
(and frozen-card? minimized)
(and mnemonic minimized (not empty-balances?)))
[reanimated/view {:style (styles/accounts-mnemonic {:animation animation})}
[react/touchable-highlight
{:on-press #(re-frame/dispatch
(if frozen-card?
[::keycard.login/reset-pin]
[:navigate-to :backup-seed]))}
[react/view
{:flex-direction :row
:align-items :center}
[react/view
{:width 14
:height 14
:background-color colors/gray
:border-radius 7
:align-items :center
:justify-content :center
:margin-right 9}
[react/text
{:style {:color colors/white
:font-size 13
:font-weight "700"}}
"!"]]
[react/text
{:style {:color colors/gray}
:accessibility-label :back-up-your-seed-phrase-warning}
(if frozen-card?
(i18n/label :t/your-card-is-frozen)
(i18n/label :t/back-up-your-seed-phrase))]]]])
[reanimated/view
{:style (styles/value-container {:minimized minimized
:animation animation})
:pointer-events :none}
[reanimated/view {:style {:justify-content :center}}
[components.core/text
{:animated? true
:weight :semi-bold
:style (styles/value-text {:minimized minimized})}
portfolio-value
[components.core/text
{:animated? true
:size :inherit
:weight :inherit
:color :secondary}
(str " " (:code currency))]]]]
(when-not minimized
[reanimated/view
[components.core/text {:color :secondary}
(i18n/label :t/wallet-total-value)]])]))
(views/defview total-value
[]
(views/letsubs [currency [:wallet-legacy/currency]
portfolio-value [:portfolio-value]]
[react/view {:padding-vertical 12}
[quo/text (i18n/label :t/wallet-total-value)]
[quo/text {:size :heading-1 :weight :semi-bold}
(str portfolio-value " " (:code currency))]
[react/scroll-view {:horizontal true}]]))
(views/defview accounts
[selected-account-atom]
(views/letsubs [visible-accounts [:multiaccount/visible-accounts]]
(do
(reset! selected-account-atom (:address (first visible-accounts)))
(let [accounts-data (for [account visible-accounts]
{:label (:name account)
:id (:address account)})]
[react/scroll-view
{:horizontal true
:shows-horizontal-scroll-indicator false
:scroll-event-throttle 64
:margin-top 12
:margin-bottom 20}
[react/view {:flex-direction :row}
[quo/tabs
{:default-active (:address (first visible-accounts))
:on-change #(reset! selected-account-atom %)
:data accounts-data}]
[quo/button
{:type :grey
:size 32
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content sheets/add-account
:content-height 260}])}
"Add account"]]]))))
(defn accounts-overview
[]
(let [mnemonic @(re-frame/subscribe [:mnemonic])
selected-account-atom (reagent/atom nil)]
(fn []
[react/view
{:style {:flex 1
:padding-top (safe-area/get-top)
:background-color (quo.colors/theme-colors quo.colors/neutral-5
quo.colors/neutral-95)}}
[react/view {:padding-horizontal 20}
[react/view {:flex-direction :row :height 56 :align-items :center :justify-content :flex-end}
[quo/button
{:icon true
:size 32
:type :grey
:accessibility-label :accounts-qr-code
:on-press #(re-frame/dispatch
[::qr-scanner/scan-code
{:handler :wallet-legacy.send/qr-scanner-result}])}
:i/placeholder]
[react/view {:width 12}]
[quo/button
{:icon true
:size 32
:type :grey
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content (sheets/accounts-options mnemonic)}])
:accessibility-label :accounts-more-options}
:i/placeholder]]
[total-value]
[accounts selected-account-atom]]
[account.views/account-new @selected-account-atom]])))
(defn accounts-overview-old
[]
(let [mnemonic @(re-frame/subscribe [:mnemonic])
mainnet? @(re-frame/subscribe [:mainnet?])]
[react/view
{:style {:flex 1}}
[components.core/animated-header
{:extended-header total-value-old
:refresh-control common/refresh-control
:refreshing-sub (re-frame/subscribe [:wallet-legacy/refreshing-history?])
:refreshing-counter common/updates-counter
:use-insets true
:right-accessories [{:on-press #(re-frame/dispatch
[::qr-scanner/scan-code
{:handler :wallet-legacy.send/qr-scanner-result}])
:icon :main-icons/qr
:accessibility-label :accounts-qr-code}
{:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
{:content (sheets/accounts-options
mnemonic)}])
:icon :main-icons/more
:accessibility-label :accounts-more-options}]}
[accounts-old]
(when mainnet?
[buy-crypto/banner])
[assets]
[react/view {:height 68}]]
[send-button]]))

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