Compare commits
91
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac5f77678 | ||
|
|
895a8b9588 | ||
|
|
7076f365ee | ||
|
|
94ab92d931 | ||
|
|
91cb714ea9 | ||
|
|
c7bd607c5a | ||
|
|
f62afdac16 | ||
|
|
41997d9851 | ||
|
|
2ced6095b9 | ||
|
|
ea1b01a352 | ||
|
|
b7d89aa185 | ||
|
|
fb25ccfc6a | ||
|
|
449b2808e6 | ||
|
|
6bf1aecb6d | ||
|
|
4d26934949 | ||
|
|
0f6aa8d401 | ||
|
|
32ddf81a5e | ||
|
|
7b34a4fb1b | ||
|
|
215e516b2b | ||
|
|
eecd1d50e4 | ||
|
|
5a9279b232 | ||
|
|
6a5e718cd3 | ||
|
|
70009f80ab | ||
|
|
8e3031f106 | ||
|
|
3e0c31af9a | ||
|
|
e1bbefa60b | ||
|
|
4575f83f49 | ||
|
|
37232eee7e | ||
|
|
373ca6ec2d | ||
|
|
fe28f44d4b | ||
|
|
cd81519e9a | ||
|
|
d4a58ac2fb | ||
|
|
75280c4db6 | ||
|
|
9a52339ea7 | ||
|
|
d53bef385a | ||
|
|
35e71289dc | ||
|
|
0dfc8384e6 | ||
|
|
605e3a727d | ||
|
|
41e18993be | ||
|
|
ce3f2b5895 | ||
|
|
3b4cbfa4bc | ||
|
|
9a3cdc4f8b | ||
|
|
1d84c30de3 | ||
|
|
8133c4c5f0 | ||
|
|
e21ece0f8d | ||
|
|
89828ba7fd | ||
|
|
8215498ddb | ||
|
|
ae6205c874 | ||
|
|
328f9686c9 | ||
|
|
9f412577d7 | ||
|
|
11bd340a94 | ||
|
|
3c102b9797 | ||
|
|
bd4b7fc49d | ||
|
|
29252336cc | ||
|
|
4b5b4f9099 | ||
|
|
28da6f8bfc | ||
|
|
e0fe0d2dbd | ||
|
|
07cf34ad60 | ||
|
|
7c4c9a9fc0 | ||
|
|
59ceddbaad | ||
|
|
99ccbc3383 | ||
|
|
8c2d5398b0 | ||
|
|
67c83b13e3 | ||
|
|
11a84ba145 | ||
|
|
1f5bb579c9 | ||
|
|
4586f8007c | ||
|
|
55c620e59d | ||
|
|
525609f0af | ||
|
|
90653955a6 | ||
|
|
d27ab756d0 | ||
|
|
c6a1db6334 | ||
|
|
73777e052e | ||
|
|
a6d3fc3742 | ||
|
|
a671c70838 | ||
|
|
a45991b6dc | ||
|
|
2e9fa22e4f | ||
|
|
737d8c4d53 | ||
|
|
3aa7e103f7 | ||
|
|
c1d2d44da4 | ||
|
|
0fed8113d1 | ||
|
|
53c35cb554 | ||
|
|
be82365542 | ||
|
|
eae8a65597 | ||
|
|
fe54a25a3a | ||
|
|
15a4219ef4 | ||
|
|
2ffbdac89e | ||
|
|
402eb8397d | ||
|
|
ff88049a08 | ||
|
|
cee21241d4 | ||
|
|
60ad7c8a29 | ||
|
|
4989c92780 |
@@ -436,10 +436,11 @@ android-clean: ##@prepare Clean Gradle state
|
||||
rm -rf ~/.gradle
|
||||
|
||||
|
||||
android-ports: export FLOWSTORM_PORT := 7722
|
||||
android-ports: export FLOWSTORM_PORT ?= 7722
|
||||
android-ports: export TARGET := android-sdk
|
||||
android-ports: export RCT_METRO_PORT ?= 8081
|
||||
android-ports: ##@other Add proxies to Android Device/Simulator
|
||||
adb reverse tcp:8081 tcp:8081 && \
|
||||
adb reverse tcp:$(RCT_METRO_PORT) tcp:$(RCT_METRO_PORT) && \
|
||||
adb reverse tcp:3449 tcp:3449 && \
|
||||
adb reverse tcp:4567 tcp:4567 && \
|
||||
adb reverse tcp:$(FLOWSTORM_PORT) tcp:$(FLOWSTORM_PORT) && \
|
||||
|
||||
+3
-3
@@ -1115,7 +1115,7 @@ PODS:
|
||||
- React-perflogger (= 0.73.5)
|
||||
- ReactNativeAudioToolkit (2.0.3):
|
||||
- React
|
||||
- ReactNativeCameraKit (14.0.0-beta13):
|
||||
- ReactNativeCameraKit (14.0.0-beta15):
|
||||
- React-Core
|
||||
- ReactNativeNavigation (7.39.0):
|
||||
- HMSegmentedControl
|
||||
@@ -1485,7 +1485,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: 56e0e498dbb513b96c40bac6284729ba4e62672d
|
||||
FBReactNativeSpec: 146c741a3f40361f6bc13a4ba284678cbedb5881
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 530710e7949eb12c82670bd09e946becf50a3c2a
|
||||
glog: 36c15c01d70ed395c6e4f3619f98a23ee415b07a
|
||||
hermes-engine: 1d1835b2cc54c381909d94d1b3c8e0a2f1a94a0e
|
||||
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
|
||||
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
|
||||
@@ -1551,7 +1551,7 @@ SPEC CHECKSUMS:
|
||||
React-utils: 987a4526a2fc0acdfaf87888adfe0bf9d0452066
|
||||
ReactCommon: 2947b0bffd82ea0e58ca7928881152d4c6dae9af
|
||||
ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06
|
||||
ReactNativeCameraKit: d95d3e19c514526a234d9f93c6db7e7f10eef9ea
|
||||
ReactNativeCameraKit: 71343efc1256720184ce980f164c7eedb78d5c16
|
||||
ReactNativeNavigation: 679d8c8bf3b8ae98d285bce810964d10da85dbdb
|
||||
RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb
|
||||
RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d
|
||||
|
||||
-5
@@ -36,11 +36,6 @@ class NetworkManager(private val reactContext: ReactApplicationContext) : ReactC
|
||||
@ReactMethod
|
||||
fun inputConnectionStringForBootstrapping(connectionString: String, configJSON: String, callback: Callback) {
|
||||
val jsonConfig = JSONObject(configJSON)
|
||||
val receiverConfig = jsonConfig.getJSONObject("receiverConfig")
|
||||
val keyStorePath = utils.pathCombine(utils.getNoBackupDirectory(), "/keystore")
|
||||
receiverConfig.put("keystorePath", keyStorePath)
|
||||
receiverConfig.getJSONObject("nodeConfig").put("rootDataDir", utils.getNoBackupDirectory())
|
||||
|
||||
utils.executeRunnableStatusGoMethod(
|
||||
{ Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()) },
|
||||
callback
|
||||
|
||||
@@ -35,21 +35,7 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
|
||||
configJSON:(NSString *)configJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
||||
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error;
|
||||
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
|
||||
NSMutableDictionary *receiverConfig = configDict[@"receiverConfig"];
|
||||
NSMutableDictionary *nodeConfig = receiverConfig[@"nodeConfig"];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSString *keystoreDir = multiaccountKeystoreDir.path;
|
||||
NSString *rootDataDir = rootUrl.path;
|
||||
|
||||
[receiverConfig setValue:keystoreDir forKey:@"keystorePath"];
|
||||
[nodeConfig setValue:rootDataDir forKey:@"rootDataDir"];
|
||||
NSString *modifiedConfigJSON = [Utils jsonStringWithPrettyPrint:NO fromDictionary:configDict];
|
||||
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, modifiedConfigJSON);
|
||||
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, configJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
hermesEnabled ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_hermesEnabled" "true",
|
||||
buildUrl ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_buildUrl" null,
|
||||
statusGoSrcOverride ? lib.getEnvWithDefault "STATUS_GO_SRC_OVERRIDE" null,
|
||||
reactMetroPort ? lib.getEnvWithDefault "RCT_METRO_PORT" 8081,
|
||||
# If APKs should be split based on architectures
|
||||
androidAbiSplit ? lib.getEnvWithDefault "ANDROID_ABI_SPLIT" "true",
|
||||
# Android architectures to build for
|
||||
@@ -158,6 +159,7 @@ in stdenv.mkDerivation rec {
|
||||
--no-build-cache \
|
||||
--parallel \
|
||||
-Dmaven.repo.local='${deps.gradle}' \
|
||||
-PreactNativeDevServerPort=${toString reactMetroPort} \
|
||||
assemble${gradleBuildType}
|
||||
'';
|
||||
in
|
||||
|
||||
+62
-38
@@ -1,54 +1,78 @@
|
||||
Last updated: 2023-09-22
|
||||
Status Software - Privacy Policy
|
||||
|
||||
Last update: 16 August 2024
|
||||
|
||||
This Status Software - Privacy Policy (“Privacy Policy”) is intended to inform users of Status’ approach to privacy in respect of Status Software. In this regard, if you are using Status Software, this Privacy Policy applies to you.
|
||||
Under the relevant data protection legislation, we are under certain obligations if we process any personal data when you use Status Software. Personal data means all information by which a person can be directly or indirectly identified, in line with the definitions of the General Data Protection Regulation (GDPR), the Swiss Federal Act on Data Protection of June 19, 1992 (DPA) (as amended from time to time) and its ordinances, and other relevant legislation on the protection of personal data. When we refer to privacy legislation in this Privacy Policy we mean GDPR and all such relevant legislation.
|
||||
This Privacy Policy describes how we process the (limited) amount of personal data that we collect when you use Status Software. We also inform you about your rights and choices that you have in respect of any personal data we process.
|
||||
If you do not agree with this Privacy Policy or any part of it, please do not use Status Software. If you are already using Status Software, we ask that you immediately stop using Status Software.
|
||||
Who we are
|
||||
|
||||
Status is developing a set of open source projects that use peer-to-peer technologies to help people transact securely, communicate freely, and organise with confidence. Anyone participating in these projects helps to build technology and tools that empowers people to advance their own sovereign communities.
|
||||
|
||||
Whenever “Status” or “we” or “us” or any similar variation, are used in these terms, we’re referring to Status Research & Development GmbH, a Swiss company with its registered office at Baarerstrasse 10, Zug, Switzerland, and includes its “representatives”, which means Status’ affiliates, directors, officers, employees, agents and any other representatives of Status. For the purposes of these terms, “representatives” also includes Status core contributors without prejudice to the other legal categories mentioned.
|
||||
Whenever "Status" or "we" is used in this Privacy Policy, we're referring to Status Research & Development GmbH, a Swiss company with its registered offices at Baarerstrasse 10, 6302 Zug Switzerland. Our contact information can be found on our website and at the end of this Privacy Policy.
|
||||
|
||||
Status does not provide any services, such as financial services, to users of Status Software or any third party. Status is not an intermediary, agent, advisor, or custodian, and does not owe you any fiduciary duty.
|
||||
|
||||
|
||||
Status Software
|
||||
|
||||
At Status, we strive to develop open source software that can serve as a secure communication tool that helps to uphold human rights. Status Software is specifically designed to facilitate the free flow of information, protect the right to private, secure communications and promote the sovereignty of individuals.
|
||||
|
||||
Status Software is composed of a secure messaging tool, a crypto wallet, and a Web 3 browser integrated together. The software developed by Status in this regard is simply called “Status Software” and you can find more details about its development on Status’ GitHub page.
|
||||
Status Software is composed of a secure messaging tool, a crypto wallet, and a Web 3 browser that are integrated together. Any software developed by Status in this regard is simply called “Status Software” and you can find more details about its development here.
|
||||
Status limits its processing of personal data from your use of Status Software
|
||||
Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, user accounts or any other user information.
|
||||
|
||||
Notwithstanding the above, Status processes personal data on a limited basis from users of Status Software and the specific types of information we process will depend on how you use Status Software. As such, we only collect and process personal data in the following instances:
|
||||
|
||||
Usage Data: Where you opt-in to share usage data, we collect and/or briefly process certain data about your interactions with Status Software. Adhering to the principle of privacy by design, this option is off by default. The situations where we collect usage data and the type of personal data we process are as follows:
|
||||
Network Behaviour: Status Software utilises a private, censorship resistant, peer-to-peer messaging protocol called Waku (https://waku.org/). To allow us to understand the performance, usage patterns, and reliability of the Waku protocol, we collect non-personally identifiable information such as the number of messages sent to you, connected and discovered peers, the rate of successfully sent messages, type of connection to peers, and details about your OS, Status Software application version and bandwidth usage. Such usage data is linked to a randomly generated peer ID associated with your instance of Status Software, which is a unique identifier used for the duration of your interaction with Status Software and generated with each restart of the Status Software. Such usage data will be kept for only as long as necessary to fulfil the aforementioned purposes and in any event, no longer than thirty (30) days and it will be deleted thereafter.
|
||||
Analytics: Status also uses privacy-focused analytics to collect trends and insights about Status Software users. The usage data consists of personal data which we briefly process, that includes your IP address, universally unique identifiers of your device (UUID), and logs of actions, including button presses and screen visits, during your interactions with Status Software. Such usage data will be kept for only as long as necessary to fulfil the aforementioned purposes and it will be deleted thereafter.
|
||||
|
||||
We process any personal data collected in the context of usage data based on your consent when you choose to opt-in. If you no longer wish to provide us any further usage data, you can opt-out at any time by disabling these functions.
|
||||
|
||||
Proxy Server: When you use Status Software, it will automatically interact with a server (“Proxy Server”) that we control and has been implemented to improve the performance of Status Software. The Proxy Server is hosted on a number of reputable third party cloud providers (see section 4 of this Privacy Policy). As part of the implementation of the Proxy Server, certain information, including personal data will be shared from the user’s instance of Status Software and incidentally processed by us by way of the Proxy Server, which include the following:
|
||||
the user’s IP address: your IP address will be processed when third party requests are run through the Proxy Server. This however removes your individual device details and IP address through the external requests made from the Proxy Server to the third party service provider (e.g. Infura); and
|
||||
the user’s public address (also known as a wallet address): your public address is temporarily processed in the Proxy Server by us when making API calls to third party service providers in order to facilitate certain user activities on Status Software (for instance, transactions conducted using the Wallet).
|
||||
|
||||
Apart from your IP address, we do not retain any of the above personal data (or any other information) in line with the principle of data minimisation. The IP address will be kept for only as long as necessary to fulfil the aforementioned purposes and in any event, no longer than fourteen (14) days and it will be deleted thereafter.
|
||||
We process the above personal data for the purposes of facilitating the technical operation of Status Software and optimising the functionality and user’s experience of Status Software. We have a legitimate interest in processing this personal data for these purposes.
|
||||
|
||||
Personal data sharing with third party service providers
|
||||
We share personal data with third party service providers in the context of fulfilling the above purposes in which we collect and process personal data. We have contracted such third party service providers to provide their services and act as data processors on our behalf and are only permitted to process personal data in accordance with our instructions.
|
||||
Third party service providers we engage and services we utilise from them include:
|
||||
DigitalOcean: Providing hosting services in relation to the Proxy Server;
|
||||
Alibaba Cloud: Providing hosting services in relation to the Proxy Server;
|
||||
Google Cloud: Providing hosting services in relation to the Proxy Server;and
|
||||
MixPanel: Providing analytics services in relation to the Usage Data.
|
||||
|
||||
|
||||
Our role in your privacy
|
||||
Third party collection and processing of personal data
|
||||
In addition to our limited collection of personal data, third parties might collect or process personal data as a result of Status Software making use of certain features or to provide certain content. To the extent you interact with such third party content or features, their respective privacy policies will apply.
|
||||
|
||||
This Privacy Statement applies to you as a user of Status Software.
|
||||
|
||||
Under the relevant data protection legislation, we would be under certain obligations if we process any personal data when you use Status Software. Personal data means all information by which a person can be directly or indirectly identified, in line with the definitions of the General Data Protection Regulation (GDPR), the Swiss Federal Act on Data Protection of June 19, 1992 (DPA) (as amended from time to time) and its ordinances, and other relevant legislation on the protection of personal data. When we refer to privacy legislation in this Privacy Statement, we mean GDPR and all such relevant legislation.
|
||||
|
||||
However, since we do not collect or process any of your personal data when you use Status Software, we have decided to inform you of the following instead:
|
||||
|
||||
- Ethereum is a public blockchain:
|
||||
Ethereum is the community-run technology powering the cryptocurrency ether (ETH) and thousands of decentralized applications (DApps). Status Software provides a mobile portal and streamlined access to Ethereum’s growing ecosystem of decentralized applications.
|
||||
|
||||
The Ethereum public network is accessible to anyone in the world with an internet connection. Anyone can read or create transactions on a public blockchain and validate the transactions being executed. Therefore, information you share on the Ethereum blockchain is public, whether or not you use Status Software.
|
||||
|
||||
You will also be able to create (and join) open and public communities on Status Software that are accessible to other users. We note that content in these open and public communities will be accessible to all other users of Status Software. Nonetheless, all content will still be protected by the same end-to-end encryption utilised in Status Software.
|
||||
|
||||
Ethereum Name Service (ENS) allows Ethereum addresses to be replaced by custom text-based names. Anyone can register a stateofus.eth username by staking the required Status Network Tokens (SNT) from within the profile tab on Status Software. When you register an ENS name with a given wallet address, that address becomes associated with the username, reducing the privacy of that account.
|
||||
|
||||
- Offering secure, private messaging:
|
||||
When generating an account, a randomisation process is started on your own device that generates a key pair. Status does not have visibility of or access to your private keys. You will then be given display name options to choose from (derived from this key pair). Your display name will only be shared if you choose to share it by chatting with Status or other users.
|
||||
|
||||
Within Status Software, you can create a list of trusted contacts based on the chat keys you choose to trust. Your trusted contacts are also stored locally on your own device. This means that Status has no access to your contact list and does not collect or process any personal data in this respect.
|
||||
|
||||
On Status Software, you can exchange messages with other users, including photos and audio messages. Only the recipient of a message can decrypt the message by opening it on their own device. This means that Status can never access any user messages in private chats.
|
||||
|
||||
Messaging on Status Software uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, to protect your messages from third parties (including Status). End-to-end encryption means that only the sender and recipient of a message can read its contents, and no one else. This protocol, known as Waku, relies on a distributed network of nodes instead of a centralised server which means that there is no central party or server from which messages can be intercepted, modified or blocked. Learn more about Waku and how it works on Waku’s website.
|
||||
|
||||
- A quick bite on cookies:
|
||||
We do not set any cookies for the use of Status Software. However, the Web 3.0 browser embedded within Status Software technically supports the use of cookies set by third party websites. Status is not responsible for nor is it able to influence whether such cookies are set by such third party websites and you should consult the relevant privacy policies of such third party websites.
|
||||
We do note however the following:
|
||||
Cookies: We do not set any cookies for the use of Status Software. However, the Web 3 browser embedded within Status Software technically supports the use of cookies set by third party websites. Status is not responsible for nor is it able to influence whether such cookies are set by such third party websites and you should consult the relevant privacy policies of such third party websites.
|
||||
Phone number and social media handles: When generating an account on Status Software, you will have the option to utilise your phone number and certain social media handles such as your X (formerly known as Twitter) account, to generate an account. While this information is not shared with Status and Status has no access to it, third party service providers might receive it to authenticate your ownership of such accounts. Using your phone number or social media handle, such as on X, will allow you to find your contacts who are also using Status Software and it will help your contacts to find you. This will allow them to discover and potentially interact with you, if you accept their request to connect, so that you can start building up your network of contacts on Status Software. Please note that this is not required for you to access or utilise Status Software, and you can always choose to generate an account anonymously without utilising your phone number or social media handle.
|
||||
Web 3: An inherent feature of Web 3 is its transparency, particularly in the context of blockchain networks. This means that your public key and wallet address will be visible to others when you engage in transactions on such networks and that third parties may be able to (and for the avoidance of doubt, not through the use of Status Software) connect your public key and wallet address to your identity and determine the Digital Assets you own in your Wallet. You should also be aware that entries on blockchain networks are practically immutable, which means that they generally cannot be deleted or modified by anyone, including Status, even if the transaction turns out to have been made in error or otherwise.
|
||||
|
||||
|
||||
Changes to Status Privacy Statement
|
||||
It is unlikely that this Privacy Statement will change because we do not intend to collect or process your personal data, ever. That said, we reserve the right to modify or replace any part of this Privacy Statement at any time and in our sole discretion.
|
||||
Security measures we take in respect of Status Software
|
||||
As a general approach, Status takes data security seriously and the measures we take as an organisation. In respect of Status Software, we implement a variety of security measures that are reasonably designed to maintain the safety of your personal data when it is shared with us.
|
||||
Exporting personal data outside the European Union and Switzerland
|
||||
While it is not intended that Status will export your personal data outside the European Union or Switzerland, Status is obliged to protect the privacy of such personal data if it is exported outside these areas and it will only be processed in countries or by parties that provide an adequate level of protection as deemed by Switzerland or the European Commission. Otherwise, Status will use specific forms of contractual clauses to ensure such personal data is provided the same protection as required in Switzerland or Europe. The transmission of personal data outside the European Union and Switzerland will always occur in conformity with applicable privacy legislation.
|
||||
Your choices and rights
|
||||
As explained in this Privacy Policy, Status limits its collection and processing of your personal data. Nonetheless, you still have certain choices and rights in respect of the personal data which we do collect and process. As laid out in relevant privacy legislation, you have the right to:
|
||||
Ask us to correct or update your personal data (where possible);
|
||||
Ask us to remove your personal data from our systems;
|
||||
Ask us for a copy of your personal data, which may also be transferred to another data controller at your request;
|
||||
Withdraw your consent to process your personal data (only if consent was asked for a processing activity), which only affects processing activities that are based on your consent and doesn't affect the validity of such processing activities before you have withdrawn your consent;
|
||||
Object to the processing of your personal data; and
|
||||
File a complaint with the Federal Data Protection and Information Commissioner (FDPIC), if you believe that your personal data has been processed unlawfully.
|
||||
|
||||
If you have any questions about the Status Software Privacy Statement, please contact us at legal@status.im or through the appropriate means of communication indicated on Status’ website.
|
||||
Once we receive your request, we might require you to verify your identity such that we can appropriately respond to your request and we will do so in line with any applicable mandatory deadlines. Please contact us with the relevant request at: legal@status.im.
|
||||
Third party websites
|
||||
Within Status Software, you might come across links to third party websites. These third party sites will often have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Please refer to these privacy policies of these third party websites when you use such websites.
|
||||
The Privacy Policy might change
|
||||
We may modify or replace any part of this Privacy Policy. Please check on Status Software periodically for any changes. The new Privacy Policy will be effective immediately upon it being placed in Status Software .
|
||||
|
||||
This document is CC-BY-SA.
|
||||
Contact Information
|
||||
To the extent that you have any questions about the Privacy Policy, please email us at legal@status.im.
|
||||
|
||||
|
||||
This document is licensed under CC-BY-SA.
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
Last updated: 2023-09-22
|
||||
Status Terms of Use
|
||||
|
||||
Last updated: 14 August 2024
|
||||
|
||||
1. Introduction
|
||||
|
||||
@@ -81,20 +83,15 @@ Status Software also includes a Web 3 browser through which you may access and i
|
||||
|
||||
3. Privacy
|
||||
|
||||
Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, transactions, user accounts or any other user information.
|
||||
Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, user accounts or any other user information.
|
||||
|
||||
Therefore, Status does not (and cannot) monetise any users’ data or content, such as messages or browser information and Status does have any interest in doing so.
|
||||
Therefore, Status does not (and cannot) monetise any users’ data or content, such as messages or browser information and Status does not have any interest in doing so.
|
||||
|
||||
When generating an account on Status Software, you will have the option to utilise your phone number and certain social media handles such as your Twitter account, to generate an account. While this information is not shared with Status and Status has no access to it, third party service providers may receive it to authenticate your ownership of such accounts. Using your phone number or social media handle, such as on X (formerly known as Twitter), will allow you to find your contacts who are also using Status Software and it will help your contacts to find you. This will allow them to discover and potentially interact with you, if you accept their request to connect, so that you can start building up your network of contacts on Status Software. Please note that this is not required for you to access or utilise Status Software, and you can always choose to generate an account anonymously without utilising your phone number or social media handle.
|
||||
|
||||
In addition, you may opt in to submit certain technical information (also referred to as telemetry) to assist Status with the development of the Status Software, particularly to measure message reliability and bandwidth usage. In any event this technical information is anonymised by Status Software and also protected by end-to-end encryption. We note that even anonymised data can be vulnerable to being potentially compromised, such as by correlation attacks. You should opt in to submit this technical information only if you are willing to accept these risks.
|
||||
|
||||
You acknowledge that an inherent feature of Web 3 is its transparency, particularly in the context of blockchain networks. This means that your public key and wallet address will be visible to others when you engage in transactions on such networks and that third parties may be able to (and for the avoidance of doubt, not through the use of Status Software) connect your public key and wallet address to your identity and determine the Digital Assets you own in your Wallet. You should also be aware that entries on blockchain networks are practically immutable, which means that they generally cannot be deleted or modified by anyone, including Status, even if the transaction turns out to have been made in error or otherwise.
|
||||
Status however processes a limited amount of user data to ensure the technical operation of the Status Software, enhance its functionality, and improve the user experience. For more details on this limited process of user data, please refer to our Privacy Policy.
|
||||
|
||||
If you use third party services offered through Status Software, your privacy may not be protected and you will be subject to the privacy terms and conditions of such third parties. Please refer to our full terms regarding third party services below.
|
||||
|
||||
|
||||
Learn more about how we protect your data on Status Software by reading the Status Privacy Statement and relevant security page on the Status website.
|
||||
|
||||
|
||||
4. Using third party services and interacting with Web 3
|
||||
@@ -103,9 +100,9 @@ When using Status Software, you may access or use third party products, services
|
||||
|
||||
Your interactions with Web 3 could include many aspects, such as interacting with smart contracts that reside on blockchain networks, for example to swap Digital Assets or use (crypto) bridges, or deploying your own smart contracts through Status Community Spaces. You may also interact with and participate in peer-to-peer networks which could vary in their level of decentralisation, ranging from more centralised networks controlled by a single party to networks governed by decentralised communities.
|
||||
|
||||
You acknowledge when you use Third Party Services or interact with Web 3, that Status does not control or operate any Third Party Service or Web 3. These are products, services or tools that are respectively provided by others or may be operating autonomously, and not by Status.
|
||||
When you use Third Party Services or interact with Web 3, you acknowledge that Status does not control or operate any Third Party Service or Web 3. These products, services, or tools are either controlled, operated, owned, or licensed by the third party providing them, or they may operate autonomously, independently of Status or any other party.
|
||||
|
||||
Please note that when you use Third Party Services or interact with Web 3 that there may be applicable terms and conditions, including privacy policies which govern your use of Third Party Services or interaction with Web 3. In case of interactions with Web 3, such as decentralised exchanges, there may be in some cases no applicable terms and conditions, and the relevant software code will govern your interactions.
|
||||
Please note that using Third Party Services or interacting with Web 3 might be subject to specific terms and conditions, including privacy policies, set by the third-party providers. These providers might exclude and disclaim all liability regarding their Third Party Services and are not responsible or liable for your use of Status Software. It is crucial that you read and understand the applicable terms and conditions, including privacy policies, of these Third Party Services to be aware of the implications of their use. In case of interactions with Web 3, such as decentralised exchanges, there might be in some cases no applicable terms and conditions, and the relevant software code will govern your interactions.
|
||||
|
||||
Please also note that bridges are used to move tokens from one blockchain network to another. When you use a bridge, for example in the course of sending or bridging tokens, Status utilises an algorithm that aims to determine the lowest service fee which the user may incur at the time of a bridge’s use. This algorithm relies on information extracted directly from the relevant blockchain networks. Status is not responsible for the accuracy and correctness of any information that these algorithms may extract from such networks
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ if pgrep -f 'shadow-cljs watch mobile' > /dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pgrep -f 'react-native start' > /dev/null; then
|
||||
if pgrep -f "react-native start --port=${RCT_METRO_PORT:-8081}" > /dev/null; then
|
||||
echo "Error: make run-metro is already running in another terminal" >&2
|
||||
echo "Please close that terminal before running this command." >&2
|
||||
exit 1
|
||||
|
||||
@@ -13,7 +13,7 @@ export BUILD_TYPE=debug
|
||||
|
||||
# Install the APK on running emulator or android device.
|
||||
installAndLaunchApp() {
|
||||
adb install -r ./result/app-arm64-v8a-debug.apk > "${ADB_INSTALL_LOG_FILE}" 2>&1
|
||||
adb install -r "./result/app-${ANDROID_ABI_INCLUDE}-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}"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if pgrep -f 'react-native start' > /dev/null; then
|
||||
if pgrep -f "react-native start --port=${RCT_METRO_PORT:-8081}" > /dev/null; then
|
||||
echo "Info: metro is already running in another terminal"
|
||||
else
|
||||
echo "Info: starting a new metro terminal"
|
||||
react-native start --reset-cache
|
||||
react-native start --port=${RCT_METRO_PORT:-8081} --reset-cache
|
||||
fi
|
||||
|
||||
@@ -4,7 +4,7 @@ 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
|
||||
if ! lsof -i:"${RCT_METRO_PORT:-8081}" &> /dev/null; then
|
||||
echo "."
|
||||
sleep 1
|
||||
((TIMEOUT--))
|
||||
|
||||
@@ -99,9 +99,9 @@
|
||||
(rf/defn system-theme-mode-changed
|
||||
{:events [:system-theme-mode-changed]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(let [current-theme-type (get-in cofx [:db :profile/profile :appearance])]
|
||||
(let [appearance-type (get-in cofx [:db :profile/profile :appearance])]
|
||||
(when (and (multiaccounts.model/logged-in? db)
|
||||
(= current-theme-type status-im.constants/theme-type-system))
|
||||
(= appearance-type status-im.constants/appearance-type-system))
|
||||
{:dispatch [:theme/switch]})))
|
||||
|
||||
(defn- on-biometric-auth-fail
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
[{:keys [db] :as cofx} {:keys [auth-method logout?]}]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
(rf/merge cofx
|
||||
{:dispatch [:init-root :progress]
|
||||
{:dispatch [:update-theme-and-init-root :progress]
|
||||
:effects.shell/reset-state nil
|
||||
:hide-popover nil
|
||||
::logout nil
|
||||
|
||||
@@ -107,11 +107,12 @@
|
||||
(assoc network-tag-props
|
||||
:status
|
||||
(if error? :error :default))]])
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style style/fiat-amount
|
||||
:weight :medium}
|
||||
(str currency-symbol fiat-value)]])
|
||||
(when fiat-value
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style style/fiat-amount
|
||||
:weight :medium}
|
||||
(str currency-symbol fiat-value)])])
|
||||
(when loading?
|
||||
[rn/view {:style (style/row-2-loader theme)}])]]
|
||||
(when (and (not= status :loading) (= type :pay) show-approval-label?)
|
||||
|
||||
@@ -6,16 +6,20 @@
|
||||
(h/describe "Wallet: Token Input"
|
||||
(h/test "Token label renders"
|
||||
(h/render-with-theme-provider [token-input/view
|
||||
{:token :snt
|
||||
:currency :eur
|
||||
:currency-symbol "€"
|
||||
:conversion 1}])
|
||||
{:token-symbol :snt
|
||||
:currency-symbol :snt
|
||||
:on-swap nil
|
||||
:error? false
|
||||
:value "1"
|
||||
:converted-value "10"}])
|
||||
(h/is-truthy (h/get-by-text "SNT")))
|
||||
|
||||
(h/test "Amount renders"
|
||||
(h/render-with-theme-provider [token-input/view
|
||||
{:token :snt
|
||||
:currency :eur
|
||||
:currency-symbol "€"
|
||||
:conversion 1}])
|
||||
{:token-symbol :snt
|
||||
:currency-symbol :snt
|
||||
:on-swap nil
|
||||
:error? false
|
||||
:value "1"
|
||||
:converted-value "€0.00"}])
|
||||
(h/is-truthy (h/get-by-text "€0.00"))))
|
||||
|
||||
@@ -4,15 +4,14 @@
|
||||
[:=>
|
||||
[:catn
|
||||
[:props
|
||||
[:map
|
||||
[:token {:optional true} [:maybe [:or :string :keyword]]]
|
||||
[:currency {:optional true} [:maybe [:or :string :keyword]]]
|
||||
[:error? {:optional true} [:maybe :boolean]]
|
||||
[:title {:optional true} [:maybe :string]]
|
||||
[:conversion {:optional true} [:maybe :double]]
|
||||
[:show-keyboard? {:optional true} [:maybe :boolean]]
|
||||
[:networks {:optional true}
|
||||
[:maybe [:sequential [:map [:source [:maybe :schema.common/image-source]]]]]]
|
||||
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||
[:value {:optional true} [:maybe :string]]]]]
|
||||
[:map {:closed true}
|
||||
[:token-symbol [:maybe [:or :string :keyword]]]
|
||||
[:currency-symbol [:maybe [:or :string :keyword]]]
|
||||
[:hint-component {:optional true} [:maybe :schema.common/hiccup]]
|
||||
[:on-token-press {:optional true} [:maybe fn?]]
|
||||
[:on-swap [:maybe fn?]]
|
||||
[:container-style {:optional true} [:maybe :map]]
|
||||
[:error? [:maybe :boolean]]
|
||||
[:value [:maybe :string]]
|
||||
[:converted-value [:maybe :string]]]]]
|
||||
:any])
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
|
||||
(defn input-container
|
||||
[window-width]
|
||||
{:width (- window-width 120)
|
||||
:margin-left 8
|
||||
:margin-right 8})
|
||||
{:width (- window-width 120)
|
||||
:margin-left 8
|
||||
:margin-right 8
|
||||
:flex-direction :row
|
||||
:align-items :flex-end})
|
||||
|
||||
(def text-input-dimensions
|
||||
(-> typography/heading-1
|
||||
@@ -65,6 +67,6 @@
|
||||
:justify-content :space-between
|
||||
:align-items :center})
|
||||
|
||||
(defn fiat-amount
|
||||
(defn converted-amount
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
|
||||
|
||||
@@ -1,57 +1,15 @@
|
||||
(ns quo.components.wallet.token-input.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
[quo.components.buttons.button.view :as button]
|
||||
[quo.components.dividers.divider-line.view :as divider-line]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.components.tags.network-tags.view :as network-tag]
|
||||
[quo.components.utilities.token.view :as token]
|
||||
[quo.components.wallet.token-input.schema :as component-schema]
|
||||
[quo.components.wallet.token-input.style :as style]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]
|
||||
[utils.number :as number]))
|
||||
|
||||
(defn fiat-format
|
||||
[currency-symbol num-value conversion]
|
||||
(str currency-symbol (.toFixed (* num-value conversion) 2)))
|
||||
|
||||
(defn crypto-format
|
||||
[num-value conversion crypto-decimals token]
|
||||
(str (number/remove-trailing-zeroes
|
||||
(.toFixed (/ num-value conversion) (or crypto-decimals 2)))
|
||||
" "
|
||||
(string/upper-case (or (clj->js token) ""))))
|
||||
|
||||
(defn calc-value
|
||||
[{:keys [crypto? currency-symbol token value conversion crypto-decimals]}]
|
||||
(let [num-value (if (string? value)
|
||||
(or (parse-double value) 0)
|
||||
value)]
|
||||
(if crypto?
|
||||
(fiat-format currency-symbol num-value conversion)
|
||||
(crypto-format num-value conversion crypto-decimals token))))
|
||||
|
||||
(defn- data-info
|
||||
[{:keys [theme token crypto-decimals conversion networks title crypto? currency-symbol amount
|
||||
error?]}]
|
||||
[rn/view {:style style/data-container}
|
||||
[network-tag/view
|
||||
{:networks networks
|
||||
:title title
|
||||
:status (when error? :error)}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/fiat-amount theme)}
|
||||
(calc-value {:crypto? crypto?
|
||||
:currency-symbol currency-symbol
|
||||
:token token
|
||||
:value amount
|
||||
:conversion conversion
|
||||
:crypto-decimals crypto-decimals})]])
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- token-name-text
|
||||
[theme text]
|
||||
@@ -62,91 +20,63 @@
|
||||
(string/upper-case (or (clj->js text) ""))])
|
||||
|
||||
(defn input-section
|
||||
[{:keys [on-change-text value value-internal set-value-internal on-selection-change
|
||||
on-token-press]}]
|
||||
(let [input-ref (atom nil)
|
||||
set-ref #(reset! input-ref %)
|
||||
focus-input #(when-let [ref ^js @input-ref]
|
||||
(.focus ref))
|
||||
controlled-input? (some? value)
|
||||
handle-on-change-text (fn [v]
|
||||
(when-not controlled-input?
|
||||
(set-value-internal v))
|
||||
(when on-change-text
|
||||
(on-change-text v)))
|
||||
handle-selection-change (fn [^js e]
|
||||
(when on-selection-change
|
||||
(-> e
|
||||
(oops/oget "nativeEvent.selection")
|
||||
(js->clj :keywordize-keys true)
|
||||
(on-selection-change))))]
|
||||
(fn [{:keys [token customization-color show-keyboard? crypto? currency value error? selection
|
||||
handle-on-swap allow-selection?]
|
||||
:or {show-keyboard? true
|
||||
allow-selection? true}}]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
window-width (:width (rn/get-window))]
|
||||
[rn/pressable
|
||||
{:style {:width "100%"
|
||||
:flex-direction :row}
|
||||
:on-press on-token-press}
|
||||
[token/view
|
||||
{:token token
|
||||
:size :size-32}]
|
||||
[rn/view {:style (style/input-container window-width)}
|
||||
[rn/pressable
|
||||
{:style style/text-input-container
|
||||
:on-press focus-input}
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/text-input theme error?)
|
||||
:placeholder-text-color (style/placeholder-text theme)
|
||||
:auto-focus true
|
||||
:ref set-ref
|
||||
:placeholder "0"
|
||||
:keyboard-type :numeric
|
||||
:on-change-text handle-on-change-text
|
||||
:selection-color customization-color
|
||||
:show-soft-input-on-focus show-keyboard?
|
||||
:on-selection-change handle-selection-change
|
||||
:selection (clj->js selection)
|
||||
:editable allow-selection?}
|
||||
controlled-input? (assoc :value value)
|
||||
(not controlled-input?) (assoc :default-value value-internal))]
|
||||
[token-name-text theme (if crypto? token currency)]]]
|
||||
[button/button
|
||||
{:icon true
|
||||
:icon-only? true
|
||||
:size 32
|
||||
:on-press handle-on-swap
|
||||
:type :outline
|
||||
:accessibility-label :reorder}
|
||||
:i/reorder]]))))
|
||||
[{:keys [token-symbol on-token-press value error? on-swap currency-symbol]}]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
window-width (:width (rn/get-window))]
|
||||
[rn/pressable
|
||||
{:style {:width "100%"
|
||||
:flex-direction :row}
|
||||
:on-press on-token-press}
|
||||
[token/view
|
||||
{:token token-symbol
|
||||
:size :size-32}]
|
||||
[rn/view {:style (style/input-container window-width)}
|
||||
[rn/text-input
|
||||
{:style (style/text-input theme error?)
|
||||
:placeholder-text-color (style/placeholder-text theme)
|
||||
:placeholder "0"
|
||||
:keyboard-type :numeric
|
||||
:editable false
|
||||
:value value}]
|
||||
[token-name-text theme currency-symbol]]
|
||||
[button/button
|
||||
{:icon true
|
||||
:icon-only? true
|
||||
:size 32
|
||||
:on-press #(when on-swap (on-swap))
|
||||
:type :outline
|
||||
:accessibility-label :reorder}
|
||||
:i/reorder]]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [container-style value on-swap] :as props}]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
width (:width (rn/get-window))
|
||||
[value-internal set-value-internal] (rn/use-state nil)
|
||||
[crypto? set-crypto] (rn/use-state true)
|
||||
handle-on-swap (rn/use-callback
|
||||
(fn []
|
||||
(set-crypto (not crypto?))
|
||||
(when on-swap (on-swap (not crypto?))))
|
||||
[crypto? on-swap])]
|
||||
[{:keys [token-symbol
|
||||
value
|
||||
on-token-press
|
||||
error?
|
||||
container-style
|
||||
on-swap
|
||||
converted-value
|
||||
hint-component
|
||||
currency-symbol]}]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
width (:width (rn/get-window))]
|
||||
[rn/view {:style (merge (style/main-container width) container-style)}
|
||||
[rn/view {:style style/amount-container}
|
||||
[input-section
|
||||
(assoc props
|
||||
:value-internal value-internal
|
||||
:theme theme
|
||||
:set-value-internal set-value-internal
|
||||
:handle-on-swap handle-on-swap
|
||||
:crypto? crypto?)]]
|
||||
{:theme theme
|
||||
:token-symbol token-symbol
|
||||
:on-token-press on-token-press
|
||||
:value value
|
||||
:error? error?
|
||||
:on-swap on-swap
|
||||
:currency-symbol currency-symbol}]]
|
||||
[divider-line/view {:container-style (style/divider theme)}]
|
||||
[data-info
|
||||
(assoc props
|
||||
:theme theme
|
||||
:crypto? crypto?
|
||||
:amount (or value value-internal))]]))
|
||||
[rn/view {:style style/data-container}
|
||||
hint-component
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/converted-amount theme)}
|
||||
converted-value]]]))
|
||||
|
||||
(def view (schema/instrument #'view-internal component-schema/?schema))
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
:text-color (colors/resolve-color :danger theme)}})
|
||||
|
||||
(defn- banner
|
||||
[{:keys [text type second-banner? colors-map]}]
|
||||
[rn/view (when second-banner? {:style style/second-banner-wrapper})
|
||||
[{:keys [text type second-banner? colors-map on-press]}]
|
||||
[rn/pressable
|
||||
{:on-press #(when on-press (on-press))
|
||||
:style (when second-banner? style/second-banner-wrapper)}
|
||||
[hole-view/hole-view
|
||||
{:style (style/hole-view (get-in colors-map [type :background-color]))
|
||||
:holes (if second-banner?
|
||||
|
||||
@@ -126,7 +126,8 @@
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
{:flex 1 :background-color colors/neutral-100-opa-70})}]]
|
||||
{:flex 1
|
||||
:background-color (if shell? colors/neutral-100-opa-60 colors/neutral-100-opa-70)})}]]
|
||||
;; sheet
|
||||
[gesture/gesture-detector {:gesture sheet-gesture}
|
||||
[reanimated/view
|
||||
@@ -138,7 +139,7 @@
|
||||
[quo/blur
|
||||
{:style style/shell-bg
|
||||
:blur-radius (or blur-radius 20)
|
||||
:blur-amount 32
|
||||
:blur-amount 15
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}]])
|
||||
(when selected-item
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
(ns status-im.common.controlled-input.utils
|
||||
(:require
|
||||
[clojure.string :as string]))
|
||||
[clojure.string :as string]
|
||||
[status-im.contexts.wallet.common.utils :as wallet-utils]
|
||||
[utils.money :as money]))
|
||||
|
||||
(def init-state
|
||||
{:value ""
|
||||
@@ -14,7 +16,7 @@
|
||||
|
||||
(defn numeric-value
|
||||
[state]
|
||||
(or (parse-double (input-value state)) 0))
|
||||
(money/bignumber (input-value state)))
|
||||
|
||||
(defn input-error
|
||||
[state]
|
||||
@@ -26,23 +28,29 @@
|
||||
|
||||
(defn upper-limit
|
||||
[state]
|
||||
(:upper-limit state))
|
||||
(money/bignumber (:upper-limit state)))
|
||||
|
||||
(defn lower-limit
|
||||
[state]
|
||||
(:lower-limit state))
|
||||
(money/bignumber (:lower-limit state)))
|
||||
|
||||
(defn upper-limit-exceeded?
|
||||
[state]
|
||||
(and
|
||||
(upper-limit state)
|
||||
(> (numeric-value state) (upper-limit state))))
|
||||
(let [num-value (numeric-value state)]
|
||||
(and
|
||||
(upper-limit state)
|
||||
(if (money/bignumber? num-value)
|
||||
(money/greater-than (numeric-value state) (upper-limit state))
|
||||
false))))
|
||||
|
||||
(defn lower-limit-exceeded?
|
||||
(defn- lower-limit-exceeded?
|
||||
[state]
|
||||
(and
|
||||
(lower-limit state)
|
||||
(< (numeric-value state) (lower-limit state))))
|
||||
(let [num-value (numeric-value state)]
|
||||
(and
|
||||
(lower-limit state)
|
||||
(if (money/bignumber? num-value)
|
||||
(money/less-than (numeric-value state) (lower-limit state))
|
||||
false))))
|
||||
|
||||
(defn- recheck-errorness
|
||||
[state]
|
||||
@@ -75,13 +83,11 @@
|
||||
|
||||
(defn increase
|
||||
[state]
|
||||
(let [new-val (inc (numeric-value state))]
|
||||
(set-input-value state (str new-val))))
|
||||
(set-input-value state (str (money/add (numeric-value state) 1))))
|
||||
|
||||
(defn decrease
|
||||
[state]
|
||||
(let [new-val (dec (numeric-value state))]
|
||||
(set-input-value state (str new-val))))
|
||||
(set-input-value state (str (money/add (numeric-value state) -1))))
|
||||
|
||||
(def ^:private not-digits-or-dot-pattern
|
||||
#"[^0-9+\.]")
|
||||
@@ -130,3 +136,29 @@
|
||||
(defn empty-value?
|
||||
[state]
|
||||
(string/blank? (:value state)))
|
||||
|
||||
(defn- fiat->crypto
|
||||
[value conversion-rate]
|
||||
(-> value
|
||||
(money/fiat->crypto conversion-rate)
|
||||
(wallet-utils/cut-crypto-decimals-to-fit-usd-cents conversion-rate)))
|
||||
|
||||
(defn- crypto->fiat
|
||||
[value conversion-rate]
|
||||
(-> value
|
||||
(money/crypto->fiat conversion-rate)
|
||||
(wallet-utils/cut-fiat-balance-to-two-decimals)))
|
||||
|
||||
(defn ->crypto
|
||||
[state conversion-rate]
|
||||
(-> state
|
||||
(set-input-value (fiat->crypto (input-value state) conversion-rate))
|
||||
(set-upper-limit (fiat->crypto (upper-limit state) conversion-rate))
|
||||
(set-lower-limit (fiat->crypto (lower-limit state) conversion-rate))))
|
||||
|
||||
(defn ->fiat
|
||||
[state conversion-rate]
|
||||
(-> state
|
||||
(set-input-value (crypto->fiat (input-value state) conversion-rate))
|
||||
(set-upper-limit (crypto->fiat (upper-limit state) conversion-rate))
|
||||
(set-lower-limit (crypto->fiat (lower-limit state) conversion-rate))))
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.data-confirmation-sheet.style :as style]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.navigation :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn on-choice-callback
|
||||
[syncing-on-mobile-network?]
|
||||
(rf/dispatch [:network/set-syncing-on-mobile-network syncing-on-mobile-network?])
|
||||
(navigation/hide-bottom-sheet))
|
||||
(events-helper/hide-bottom-sheet))
|
||||
|
||||
(def on-wifi-only (partial on-choice-callback false))
|
||||
(def on-mobile-and-wifi (partial on-choice-callback true))
|
||||
@@ -19,7 +19,7 @@
|
||||
[]
|
||||
(let [settings-drawer? (= (rf/sub [:view-id]) :screen/settings.syncing)
|
||||
syncing-on-mobile-network? (rf/sub [:profile/syncing-on-mobile-network?])]
|
||||
(rn/use-mount navigation/dismiss-keyboard)
|
||||
(rn/use-mount events-helper/dismiss-keyboard)
|
||||
[:<>
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns utils.navigation
|
||||
"Prefer to declare in this namespace only reusable navigation functions that
|
||||
aren't better suited in a particular bounded context."
|
||||
(ns status-im.common.events-helper
|
||||
"Provides highly reusable dispatch functions to ensure consistency and avoid duplication in managing various events,
|
||||
including navigation and other actions that affect application behavior."
|
||||
(:require [utils.re-frame :as rf]))
|
||||
|
||||
(defn navigate-back
|
||||
@@ -21,7 +21,7 @@
|
||||
:padding-bottom 2}}
|
||||
[quo/tag
|
||||
{:accessibility-label :communities-chat-beta-tag
|
||||
:size 32
|
||||
:size 24
|
||||
:type :label
|
||||
:label (i18n/label :t/beta)
|
||||
:labelled? true
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
(ns status-im.common.metrics-confirmation-modal.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.metrics-confirmation-modal.style :as style]
|
||||
[status-im.common.privacy.view :as privacy]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -50,6 +52,15 @@
|
||||
(toggle-metrics false)
|
||||
(hide-bottom-sheet))
|
||||
|
||||
(defn- on-privacy-policy-press
|
||||
[]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[quo.theme/provider :dark
|
||||
[privacy/privacy-statement]])
|
||||
:shell? true}]))
|
||||
|
||||
(defn view
|
||||
[{:keys [settings?]}]
|
||||
(rn/use-mount #(dismiss-keyboard))
|
||||
@@ -64,7 +75,17 @@
|
||||
[bullet-points
|
||||
{:title (i18n/label :t/what-we-wont-receive)
|
||||
:points not-receive-points}]
|
||||
(when-not settings?
|
||||
(if settings?
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style style/info-text}
|
||||
(i18n/label :t/usage-data-shared-from-all-profiles)
|
||||
(i18n/label :t/more-details-in-privacy-policy-1)
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :bold
|
||||
:on-press on-privacy-policy-press}
|
||||
(i18n/label :t/more-details-in-privacy-policy-2)]]
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style style/info-text}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.onboarding.privacy.view
|
||||
(ns status-im.common.privacy.view
|
||||
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
|
||||
(:require [quo.core :as quo]
|
||||
[react-native.gesture :as gesture]))
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.onboarding.terms.view
|
||||
(ns status-im.common.terms.view
|
||||
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
|
||||
(:require [quo.core :as quo]
|
||||
[react-native.gesture :as gesture]))
|
||||
@@ -2,23 +2,42 @@
|
||||
(:require
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.theme.core :as theme]
|
||||
[status-im.common.theme.utils :as utils]))
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:theme/init-theme
|
||||
(fn []
|
||||
(theme/add-device-theme-change-listener)))
|
||||
|
||||
(defn- appearance-type->theme
|
||||
"Converts appearance type identifier to a theme keyword.
|
||||
Returns `:light` or `:dark` based on `appearance-type`:
|
||||
- `appearance-type-light` (1): Light theme.
|
||||
- `appearance-type-dark` (2): Dark theme.
|
||||
- `appearance-type-system` (0): Uses system preference."
|
||||
[appearance-type]
|
||||
(condp = appearance-type
|
||||
constants/appearance-type-dark :dark
|
||||
constants/appearance-type-light :light
|
||||
constants/appearance-type-system (if (theme/device-theme-dark?) :dark :light)
|
||||
:dark))
|
||||
|
||||
;; Switches the theme and triggers related effects.
|
||||
;;
|
||||
;; Parameters:
|
||||
;; - `theme`: Optional theme keyword to apply. If not provided, defaults to the theme determined by
|
||||
;; `appearance-type`.
|
||||
;; - `appearance-type`: Optional appearance type to determine the theme. If not provided, defaults to
|
||||
;; the appearance type from the user's profile.
|
||||
;; - `view-id`: Optional view ID for updating status and navigation color. If not provided, uses the
|
||||
;; current view ID from the database. `view-id` is required because status and navigation bar colors
|
||||
;; depends on the screen. For example, on the home screen, the navigation color should always be dark,
|
||||
;; regardless of the theme, due to the bottom tabs.
|
||||
(re-frame/reg-event-fx
|
||||
:theme/switch
|
||||
(fn [{db :db} [{:keys [theme view-id theme-type]}]]
|
||||
"Switches the theme and triggers related effects.
|
||||
Parameters:
|
||||
- theme: Optional theme keyword to apply. If not provided, defaults to the theme determined by theme-type.
|
||||
- theme-type: Optional theme type to determine the theme. If not provided, defaults to the theme type from the user's profile.
|
||||
- view-id: Optional view ID for updating status color. If not provided, uses the current view ID from the database."
|
||||
(let [theme-type (or theme-type (get-in db [:profile/profile :appearance]))
|
||||
theme (or theme (utils/theme-type->theme-value theme-type))]
|
||||
(fn [{db :db} [{:keys [theme view-id appearance-type]}]]
|
||||
(let [appearance-type (or appearance-type (get-in db [:profile/profile :appearance]))
|
||||
theme (or theme (appearance-type->theme appearance-type))]
|
||||
{:db (assoc db :theme theme)
|
||||
:fx [[:theme/legacy-theme-fx theme]
|
||||
[:dispatch
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
(ns status-im.common.theme.utils
|
||||
(:require
|
||||
[status-im.common.theme.core :as theme]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(defn theme-type->theme-value
|
||||
"Converts theme type identifier to a theme keyword.
|
||||
Returns `:light` or `:dark` based on `theme-type`:
|
||||
- `theme-type-light` (1): Light theme.
|
||||
- `theme-type-dark` (2): Dark theme.
|
||||
- `theme-type-system` (0): Uses system preference."
|
||||
[theme-type]
|
||||
(condp = theme-type
|
||||
constants/theme-type-dark :dark
|
||||
constants/theme-type-light :light
|
||||
constants/theme-type-system (if (theme/device-theme-dark?) :dark :light)
|
||||
:dark))
|
||||
@@ -123,8 +123,6 @@
|
||||
:waku.test
|
||||
["enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"]})
|
||||
|
||||
(def default-kdf-iterations 3200)
|
||||
|
||||
(def community-accounts-selection-enabled? true)
|
||||
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
|
||||
(def test-networks-enabled? (enabled? (get-config :TEST_NETWORKS_ENABLED "0")))
|
||||
|
||||
@@ -425,9 +425,9 @@
|
||||
"We prefix our keys with 0xe701 prior to serialisation them"
|
||||
"0xe701")
|
||||
|
||||
(def ^:const theme-type-system 0)
|
||||
(def ^:const theme-type-light 1)
|
||||
(def ^:const theme-type-dark 2)
|
||||
(def ^:const appearance-type-system 0)
|
||||
(def ^:const appearance-type-light 1)
|
||||
(def ^:const appearance-type-dark 2)
|
||||
(def ^:const bottom-sheet-animation-delay 450)
|
||||
|
||||
(def ^:const local-pair-event-process-success "process-success")
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns status-im.contexts.network.data-store)
|
||||
|
||||
(defn online?
|
||||
[{:network/keys [status]}]
|
||||
(= :online status))
|
||||
@@ -91,7 +91,7 @@
|
||||
(merge
|
||||
{:db (assoc db :profile/profiles-overview multiaccounts)}
|
||||
(when-not (seq multiaccounts)
|
||||
{:dispatch [:init-root :screen/onboarding.intro]}))))
|
||||
{:dispatch [:update-theme-and-init-root :screen/onboarding.intro]}))))
|
||||
|
||||
(rf/defn password-set
|
||||
{:events [:onboarding/password-set]}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.privacy.view :as privacy]
|
||||
[status-im.common.terms.view :as terms]
|
||||
[status-im.contexts.onboarding.common.background.view :as background]
|
||||
[status-im.contexts.onboarding.common.overlay.view :as overlay]
|
||||
[status-im.contexts.onboarding.intro.style :as style]
|
||||
[status-im.contexts.onboarding.privacy.view :as privacy]
|
||||
[status-im.contexts.onboarding.terms.view :as terms]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
(str (i18n/label :t/accept-status-tos-prefix) " ")]
|
||||
[quo/text
|
||||
{:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [terms/terms-of-use])
|
||||
{:content terms/terms-of-use
|
||||
:shell? true}])
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
@@ -50,7 +50,7 @@
|
||||
" & "]
|
||||
[quo/text
|
||||
{:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [privacy/privacy-statement])
|
||||
{:content privacy/privacy-statement
|
||||
:shell? true}])
|
||||
:style style/highlighted-text
|
||||
:size :paragraph-2
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.core :as rn]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.onboarding.welcome.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
@@ -24,20 +22,12 @@
|
||||
:description (i18n/label :t/welcome-to-web3-sub-title)
|
||||
:description-accessibility-label :welcome-sub-title}]))
|
||||
|
||||
(defn dispatch-visibility-status-update
|
||||
[status-type]
|
||||
(re-frame/dispatch
|
||||
[:visibility-status-updates/delayed-visibility-status-update status-type]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [profile-color (rf/sub [:onboarding/customization-color])
|
||||
{:keys [status-type]} (rf/sub [:multiaccount/current-user-visibility-status])
|
||||
window (rf/sub [:dimensions/window])
|
||||
insets (safe-area/get-insets)]
|
||||
(let [profile-color (rf/sub [:onboarding/customization-color])
|
||||
window (rf/sub [:dimensions/window])
|
||||
insets (safe-area/get-insets)]
|
||||
[rn/view {:style (style/page-container insets)}
|
||||
(when (nil? status-type)
|
||||
(dispatch-visibility-status-update constants/visibility-status-automatic))
|
||||
[quo/page-nav
|
||||
{:type :no-title
|
||||
:background :blur
|
||||
@@ -55,7 +45,7 @@
|
||||
:colors [colors/neutral-100-opa-0 colors/neutral-100-opa-80]}])
|
||||
[quo/button
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:init-root :shell-stack])
|
||||
(rf/dispatch [:update-theme-and-init-root :shell-stack])
|
||||
(rf/dispatch [:profile/show-testnet-mode-banner-if-enabled])
|
||||
(rf/dispatch [:universal-links/process-stored-event]))
|
||||
:type :primary
|
||||
|
||||
@@ -25,4 +25,4 @@
|
||||
(rf/dispatch [:navigate-back])
|
||||
(do
|
||||
(rf/dispatch [:theme/switch {:theme :dark}])
|
||||
(rf/dispatch [:init-root root])))}]))
|
||||
(rf/dispatch [:update-theme-and-init-root root])))}]))
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.resources :as resources]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.preview.quo.preview :as preview]))
|
||||
[status-im.common.controlled-input.utils :as controlled-input]
|
||||
[status-im.contexts.preview.quo.preview :as preview]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[utils.money :as money]))
|
||||
|
||||
(def networks
|
||||
[{:source (resources/get-network :arbitrum)}
|
||||
@@ -12,47 +16,75 @@
|
||||
{:source (resources/get-network :ethereum)}])
|
||||
|
||||
(def title "Max: 200 SNT")
|
||||
(def conversion-rate 3450.28)
|
||||
|
||||
(def descriptor
|
||||
[{:key :token
|
||||
[{:key :token-symbol
|
||||
:type :select
|
||||
:options [{:key :eth}
|
||||
{:key :snt}]}
|
||||
{:key :currency
|
||||
:type :select
|
||||
:options [{:key :usd}
|
||||
{:key :eur}]}
|
||||
:options [{:key "$"}
|
||||
{:key "€"}]}
|
||||
{:key :error?
|
||||
:type :boolean}
|
||||
{:key :allow-selection?
|
||||
:type :boolean}])
|
||||
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [state (reagent/atom {:token :eth
|
||||
:currency :usd
|
||||
:conversion 0.02
|
||||
:networks networks
|
||||
:title title
|
||||
:customization-color :blue
|
||||
:show-keyboard? false
|
||||
:allow-selection? true})
|
||||
value (reagent/atom "")
|
||||
set-value (fn [v]
|
||||
(swap! value str v))
|
||||
delete (fn [_]
|
||||
(swap! value #(subs % 0 (dec (count %)))))]
|
||||
(let [state (reagent/atom {:token-symbol :eth
|
||||
:currency "$"
|
||||
:crypto? true
|
||||
:error? false})]
|
||||
(fn []
|
||||
[preview/preview-container
|
||||
{:state state
|
||||
:descriptor descriptor
|
||||
:full-screen? true
|
||||
:component-container-style {:flex 1
|
||||
:justify-content :space-between}}
|
||||
[quo/token-input (assoc @state :value @value)]
|
||||
[quo/numbered-keyboard
|
||||
{:container-style {:padding-bottom (safe-area/get-top)}
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press set-value
|
||||
:on-delete delete}]])))
|
||||
(let [{:keys [currency token-symbol crypto? error?]} @state
|
||||
[input-state set-input-state] (rn/use-state controlled-input/init-state)
|
||||
input-amount (controlled-input/input-value input-state)
|
||||
swap-between-fiat-and-crypto (fn []
|
||||
(if crypto?
|
||||
(set-input-state #(controlled-input/->fiat
|
||||
%
|
||||
conversion-rate))
|
||||
(set-input-state
|
||||
#(controlled-input/->crypto
|
||||
%
|
||||
conversion-rate))))
|
||||
converted-value (if crypto?
|
||||
(utils/prettify-balance currency
|
||||
(money/crypto->fiat
|
||||
input-amount
|
||||
conversion-rate))
|
||||
(utils/prettify-crypto-balance
|
||||
(or (clj->js token-symbol) "")
|
||||
(money/fiat->crypto input-amount
|
||||
conversion-rate)
|
||||
conversion-rate))]
|
||||
[preview/preview-container
|
||||
{:state state
|
||||
:descriptor descriptor
|
||||
:full-screen? true
|
||||
:component-container-style {:flex 1
|
||||
:justify-content :space-between}}
|
||||
[quo/token-input
|
||||
{:token-symbol token-symbol
|
||||
:currency-symbol (if crypto? token-symbol currency)
|
||||
:error? error?
|
||||
:value input-amount
|
||||
:converted-value converted-value
|
||||
:on-swap (fn []
|
||||
(swap! state assoc :crypto? (not crypto?))
|
||||
(swap-between-fiat-and-crypto))
|
||||
:hint-component [quo/network-tags
|
||||
{:networks networks
|
||||
:title title
|
||||
:status (when (:error? @state) :error)}]}]
|
||||
[quo/numbered-keyboard
|
||||
{:container-style {:padding-bottom (safe-area/get-top)}
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press (fn [c]
|
||||
(set-input-state #(controlled-input/add-character % c)))
|
||||
|
||||
:on-delete (fn []
|
||||
(set-input-state controlled-input/delete-last))}]]))))
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
:wakuV2Nameserver "8.8.8.8"
|
||||
:statusProxyEnabled config/status-proxy-enabled?
|
||||
:statusProxyStageName config/status-proxy-stage-name
|
||||
:statusProxyMarketUser "test"
|
||||
:statusProxyMarketPassword "test"
|
||||
:statusProxyBlockchainUser "test"
|
||||
:statusProxyBlockchainPassword "test"
|
||||
:statusProxyMarketUser config/STATUS_BUILD_PROXY_USER
|
||||
:statusProxyMarketPassword config/STATUS_BUILD_PROXY_PASSWORD
|
||||
:statusProxyBlockchainUser config/STATUS_BUILD_PROXY_USER
|
||||
:statusProxyBlockchainPassword config/STATUS_BUILD_PROXY_PASSWORD
|
||||
:openseaAPIKey config/opensea-api-key
|
||||
:poktToken config/POKT_TOKEN
|
||||
:infuraToken config/INFURA_TOKEN
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
(assoc :profile/profiles-overview profiles)
|
||||
(update :profile/login #(select-profile % key-uid)))
|
||||
db-with-settings)
|
||||
:fx [[:dispatch [:init-root :screen/profile.profiles]]
|
||||
:fx [[:dispatch [:update-theme-and-init-root :screen/profile.profiles]]
|
||||
(when (and key-uid userConfirmed)
|
||||
[:effects.biometric/check-if-available
|
||||
{:key-uid key-uid
|
||||
@@ -61,7 +61,7 @@
|
||||
(rf/dispatch [:profile.login/check-biometric-success key-uid
|
||||
auth-method]))}])]})
|
||||
{:db db-with-settings
|
||||
:fx [[:dispatch [:init-root :screen/onboarding.intro]]]}))))
|
||||
:fx [[:dispatch [:update-theme-and-init-root :screen/onboarding.intro]]]}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:profile/update-setting-from-backup
|
||||
|
||||
@@ -73,13 +73,13 @@
|
||||
|
||||
(cond
|
||||
pairing-completed?
|
||||
[[:dispatch [:init-root :screen/onboarding.syncing-results]]]
|
||||
[[:dispatch [:update-theme-and-init-root :screen/onboarding.syncing-results]]]
|
||||
|
||||
(get db :onboarding/new-account?)
|
||||
[[:dispatch [:onboarding/finalize-setup]]]
|
||||
|
||||
:else
|
||||
[[:dispatch [:init-root :shell-stack]]
|
||||
[[:dispatch [:update-theme-and-init-root :shell-stack]]
|
||||
[:dispatch [:profile/show-testnet-mode-banner-if-enabled]]]))})))
|
||||
|
||||
;; login phase 2: we want to load and show chats faster, so we split login into 2 phases
|
||||
@@ -185,7 +185,7 @@
|
||||
{:db (-> db
|
||||
(assoc-in [:profile/login :password] password)
|
||||
(assoc-in [:profile/login :processing] true))
|
||||
:fx [[:dispatch [:init-root :progress]]
|
||||
:fx [[:dispatch [:update-theme-and-init-root :progress]]
|
||||
[:effects.profile/login
|
||||
[(get-in db [:profile/login :key-uid])
|
||||
(security/safe-unmask-data password)]]]})))
|
||||
@@ -251,5 +251,6 @@
|
||||
config/enable-alert-banner?)
|
||||
{:fx [[:dispatch
|
||||
[:alert-banners/add
|
||||
{:type :alert
|
||||
:text (i18n/label :t/testnet-mode-enabled)}]]]})))
|
||||
{:type :alert
|
||||
:text (i18n/label :t/testnet-mode-enabled)
|
||||
:on-press #(rf/dispatch [:wallet/show-disable-testnet-mode-confirmation])}]]]})))
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
(rf/reg-event-fx :profile.settings/change-appearance
|
||||
(fn [_ [theme]]
|
||||
{:fx [[:dispatch [:profile.settings/profile-update :appearance theme]]
|
||||
[:dispatch [:theme/switch {:theme-type theme}]]]}))
|
||||
[:dispatch [:theme/switch {:appearance-type theme}]]]}))
|
||||
|
||||
(rf/reg-fx :profile.settings/get-profile-picture
|
||||
(fn [key-uid]
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.navigation :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- open-blocked-users
|
||||
@@ -21,7 +21,7 @@
|
||||
[quo/page-nav
|
||||
{:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press navigation/navigate-back}]
|
||||
:on-press events-helper/navigate-back}]
|
||||
[quo/page-top {:title (i18n/label :t/messages)}]
|
||||
[quo/category
|
||||
{:label (i18n/label :t/contacts)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[status-im.common.data-confirmation-sheet.view :as data-confirmation-sheet]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.navigation :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- open-paired-devices-list
|
||||
@@ -26,7 +26,7 @@
|
||||
[quo/page-nav
|
||||
{:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press navigation/navigate-back}]
|
||||
:on-press events-helper/navigate-back}]
|
||||
[quo/page-top {:title (i18n/label :t/syncing)}]
|
||||
[quo/category
|
||||
{:data [{:title (i18n/label :t/sync-and-backup)
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
|
||||
[status-im.contexts.settings.privacy-and-security.profile-picture.view :as profile-picture.view]
|
||||
[status-im.contexts.settings.privacy-and-security.style :as style]
|
||||
[status-im.feature-flags :as ff]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.navigation :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- setting-preview-privacy
|
||||
@@ -24,6 +25,15 @@
|
||||
:id :preview-privacy
|
||||
:customization-color customization-color}})
|
||||
|
||||
(defn- on-share-usage-data-press
|
||||
[]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[quo.theme/provider :dark
|
||||
[metrics-modal/view {:settings? true}]])
|
||||
:shell? true}]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [insets (safe-area/get-insets)
|
||||
@@ -68,7 +78,7 @@
|
||||
{:key :header
|
||||
:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press navigation/navigate-back}]
|
||||
:on-press events-helper/navigate-back}]
|
||||
[quo/standard-title
|
||||
{:title (i18n/label :t/privacy-and-security)
|
||||
:container-style style/title-container
|
||||
@@ -85,13 +95,13 @@
|
||||
show-profile-pictures-to
|
||||
open-show-profile-pictures-to-options))
|
||||
(setting-preview-privacy preview-privacy? customization-color toggle-preview-privacy)
|
||||
{:title (i18n/label :t/share-usage-data)
|
||||
:blur? true
|
||||
:action :selector
|
||||
:action-props {:on-change #(rf/dispatch
|
||||
[:centralized-metrics/toggle-centralized-metrics
|
||||
(not centralized-metrics-enabled?)])
|
||||
:customization-color customization-color
|
||||
:checked? centralized-metrics-enabled?}}]
|
||||
{:title (i18n/label :t/share-usage-data)
|
||||
:description :text
|
||||
:description-props {:text (i18n/label :t/from-all-profiles-on-device)}
|
||||
:blur? true
|
||||
:action :selector
|
||||
:action-props {:on-change on-share-usage-data-press
|
||||
:customization-color customization-color
|
||||
:checked? centralized-metrics-enabled?}}]
|
||||
:blur? true
|
||||
:list-type :settings}]]))
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.contexts.settings.wallet.data-store :as data-store]
|
||||
[status-im.contexts.settings.wallet.network-settings.testnet-mode.view :as testnet]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
@@ -292,3 +294,15 @@
|
||||
|
||||
(rf/reg-event-fx :wallet/make-partially-operable-accounts-fully-operable
|
||||
make-partially-operable-accounts-fully-operable)
|
||||
|
||||
(rf/reg-event-fx :wallet/show-disable-testnet-mode-confirmation
|
||||
(fn [{:keys [db]} _]
|
||||
(let [theme (:theme db)
|
||||
blur? (and (not platform/android?) (= theme :dark))]
|
||||
{:fx [[:dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [testnet/view
|
||||
{:enable? false
|
||||
:blur? blur?}])
|
||||
:shell? blur?
|
||||
:theme (when-not platform/android? theme)}]]]})))
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.config :as config]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.profile.config :as profile.config]
|
||||
[status-im.contexts.syncing.utils :as sync-utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -21,26 +21,6 @@
|
||||
{:events [:syncing/clear-states]}
|
||||
[{:keys [db]} role]
|
||||
{:db (dissoc db :syncing)})
|
||||
|
||||
(defn- get-default-node-config
|
||||
[]
|
||||
(let [log-level config/log-level
|
||||
log-config (if (empty? log-level)
|
||||
{:LogLevel "ERROR" :LogEnabled false}
|
||||
{:LogLevel log-level :LogEnabled true})]
|
||||
(merge {:WalletConfig (cond-> {:Enabled true}
|
||||
(not= config/opensea-api-key "")
|
||||
(assoc :OpenseaAPIKey config/opensea-api-key))
|
||||
:WakuV2Config {;; Temporary fix until https://github.com/status-im/status-go/issues/3024
|
||||
;; is resolved
|
||||
:Nameserver "8.8.8.8"
|
||||
:LightClient true}
|
||||
:ShhextConfig {:VerifyTransactionURL config/verify-transaction-url
|
||||
:VerifyENSURL config/verify-ens-url
|
||||
:VerifyENSContractAddress config/verify-ens-contract-address
|
||||
:VerifyTransactionChainID config/verify-transaction-chain-id}}
|
||||
log-config)))
|
||||
|
||||
(defn- input-connection-string-callback
|
||||
[res]
|
||||
(log/info "[local-pairing] input-connection-string-for-bootstrapping callback"
|
||||
@@ -69,24 +49,15 @@
|
||||
(rf/defn initiate-local-pairing-with-connection-string
|
||||
{:events [:syncing/input-connection-string-for-bootstrapping]}
|
||||
[{:keys [db]} connection-string]
|
||||
(let [default-node-config-string (.stringify js/JSON (clj->js (get-default-node-config)))
|
||||
callback
|
||||
(fn [final-node-config]
|
||||
(let [config-map (.stringify js/JSON
|
||||
(clj->js
|
||||
{:receiverConfig
|
||||
{:kdfIterations config/default-kdf-iterations
|
||||
:nodeConfig final-node-config
|
||||
:settingCurrentNetwork config/default-network
|
||||
:deviceType platform/os
|
||||
:deviceName
|
||||
(native-module/get-installation-name)}}))]
|
||||
(rf/dispatch [:syncing/update-role constants/local-pairing-role-receiver])
|
||||
(native-module/input-connection-string-for-bootstrapping
|
||||
connection-string
|
||||
config-map
|
||||
input-connection-string-callback)))]
|
||||
(native-module/prepare-dir-and-update-config "" default-node-config-string callback)))
|
||||
(let [config-map (.stringify js/JSON
|
||||
(clj->js
|
||||
{:receiverConfig
|
||||
{:createAccount (profile.config/create)}}))]
|
||||
(rf/dispatch [:syncing/update-role constants/local-pairing-role-receiver])
|
||||
(native-module/input-connection-string-for-bootstrapping
|
||||
connection-string
|
||||
config-map
|
||||
input-connection-string-callback)))
|
||||
|
||||
(rf/defn preparations-for-connection-string
|
||||
{:events [:syncing/get-connection-string]}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require [camel-snake-kebab.extras :as cske]
|
||||
[clojure.string :as string]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.contexts.network.data-store :as network.data-store]
|
||||
[status-im.contexts.wallet.collectible.utils :as collectible-utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.ethereum.chain :as chain]
|
||||
@@ -122,14 +123,15 @@
|
||||
(rf/reg-event-fx
|
||||
:wallet/request-collectibles-for-current-viewing-account
|
||||
(fn [{:keys [db]} _]
|
||||
(let [current-viewing-account (-> db :wallet :current-viewing-account-address)
|
||||
[request-id] (get-unique-collectible-request-id 1)]
|
||||
{:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1)
|
||||
:fx [[:dispatch
|
||||
[:wallet/request-new-collectibles-for-account
|
||||
{:request-id request-id
|
||||
:account current-viewing-account
|
||||
:amount collectibles-request-batch-size}]]]})))
|
||||
(when (network.data-store/online? db)
|
||||
(let [current-viewing-account (-> db :wallet :current-viewing-account-address)
|
||||
[request-id] (get-unique-collectible-request-id 1)]
|
||||
{:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1)
|
||||
:fx [[:dispatch
|
||||
[:wallet/request-new-collectibles-for-account
|
||||
{:request-id request-id
|
||||
:account current-viewing-account
|
||||
:amount collectibles-request-batch-size}]]]}))))
|
||||
|
||||
(defn- update-fetched-collectibles-progress
|
||||
[db owner-address collectibles offset has-more?]
|
||||
|
||||
@@ -12,14 +12,17 @@
|
||||
[full-name]
|
||||
(first (string/split full-name #" ")))
|
||||
|
||||
(defn prettify-balance
|
||||
[currency-symbol balance]
|
||||
(defn cut-fiat-balance-to-two-decimals
|
||||
[balance]
|
||||
(let [valid-balance? (and balance
|
||||
(or (number? balance) (.-toFixed balance)))]
|
||||
(as-> balance $
|
||||
(if valid-balance? $ 0)
|
||||
(.toFixed $ 2)
|
||||
(str currency-symbol $))))
|
||||
(.toFixed $ 2))))
|
||||
|
||||
(defn prettify-balance
|
||||
[currency-symbol balance]
|
||||
(str currency-symbol (cut-fiat-balance-to-two-decimals balance)))
|
||||
|
||||
(defn get-derivation-path
|
||||
[number-of-accounts]
|
||||
@@ -47,8 +50,8 @@
|
||||
nil))
|
||||
|
||||
(defn calc-max-crypto-decimals
|
||||
[value]
|
||||
(let [str-representation (str value)
|
||||
[one-cent-value]
|
||||
(let [str-representation (str one-cent-value)
|
||||
decimal-part (second (clojure.string/split str-representation #"\."))
|
||||
exponent (extract-exponent str-representation)
|
||||
zeroes-count (count (take-while #(= \0 %) decimal-part))
|
||||
@@ -58,29 +61,60 @@
|
||||
(inc max-decimals)
|
||||
max-decimals)))
|
||||
|
||||
(defn get-crypto-decimals-count
|
||||
[{:keys [market-values-per-currency]}]
|
||||
(let [price (get-in market-values-per-currency [:usd :price])
|
||||
one-cent-value (if (pos? price) (/ 0.01 price) 0)]
|
||||
(calc-max-crypto-decimals one-cent-value)))
|
||||
(defn token-usd-price
|
||||
[token]
|
||||
(get-in token [:market-values-per-currency :usd :price]))
|
||||
|
||||
(defn one-cent-value
|
||||
[token-price-in-usd]
|
||||
(if (pos? token-price-in-usd)
|
||||
(/ 0.01 token-price-in-usd)
|
||||
0))
|
||||
|
||||
(defn analyze-token-amount-for-price
|
||||
"For full details: https://github.com/status-im/status-mobile/issues/18225"
|
||||
[token-price-in-usd token-units]
|
||||
(if (or (nil? token-units)
|
||||
(not (money/bignumber? token-units))
|
||||
(money/equal-to token-units 0))
|
||||
{:zero-value? true}
|
||||
(let [cent-value (one-cent-value token-price-in-usd)]
|
||||
{:usd-cent-value cent-value
|
||||
:standardized-decimals-count (if (nil? token-price-in-usd)
|
||||
missing-price-decimals
|
||||
(calc-max-crypto-decimals cent-value))})))
|
||||
|
||||
(defn cut-crypto-decimals-to-fit-usd-cents
|
||||
[token-units token-price-in-usd]
|
||||
(let [{:keys [zero-value? usd-cent-value standardized-decimals-count]}
|
||||
(analyze-token-amount-for-price token-price-in-usd token-units)]
|
||||
(cond
|
||||
zero-value? "0"
|
||||
(< token-units usd-cent-value) "0"
|
||||
:else (number/remove-trailing-zeroes
|
||||
(.toFixed token-units standardized-decimals-count)))))
|
||||
|
||||
(defn prettify-crypto-balance
|
||||
[token-symbol crypto-balance conversion-rate]
|
||||
(str (cut-crypto-decimals-to-fit-usd-cents crypto-balance conversion-rate)
|
||||
" "
|
||||
(string/upper-case token-symbol)))
|
||||
|
||||
(defn get-standard-crypto-format
|
||||
"For full details: https://github.com/status-im/status-mobile/issues/18225"
|
||||
[{:keys [market-values-per-currency]} token-units]
|
||||
(cond (or (nil? token-units)
|
||||
(money/equal-to token-units 0))
|
||||
"0"
|
||||
[token token-units]
|
||||
(let [token-price-in-usd (token-usd-price token)
|
||||
{:keys [zero-value? usd-cent-value standardized-decimals-count]}
|
||||
(analyze-token-amount-for-price token-price-in-usd token-units)]
|
||||
(cond
|
||||
zero-value?
|
||||
"0"
|
||||
|
||||
(nil? (-> market-values-per-currency :usd :price))
|
||||
(number/remove-trailing-zeroes (.toFixed token-units missing-price-decimals))
|
||||
(< token-units usd-cent-value)
|
||||
(str "<" (number/remove-trailing-zeroes (.toFixed usd-cent-value standardized-decimals-count)))
|
||||
|
||||
:else
|
||||
(let [price (-> market-values-per-currency :usd :price)
|
||||
one-cent-value (if (pos? price) (/ 0.01 price) 0)
|
||||
decimals-count (calc-max-crypto-decimals one-cent-value)]
|
||||
(if (< token-units one-cent-value)
|
||||
(str "<" (number/remove-trailing-zeroes (.toFixed one-cent-value decimals-count)))
|
||||
(number/remove-trailing-zeroes (.toFixed token-units decimals-count))))))
|
||||
:else
|
||||
(number/remove-trailing-zeroes (.toFixed token-units standardized-decimals-count)))))
|
||||
|
||||
(defn get-market-value
|
||||
[currency {:keys [market-values-per-currency]}]
|
||||
@@ -286,18 +320,6 @@
|
||||
(let [counter (count address)]
|
||||
(str (subs address 0 6) "\u2026" (subs address (- counter 3) counter)))))
|
||||
|
||||
(defn make-limit-label-crypto
|
||||
[amount currency]
|
||||
(str amount
|
||||
" "
|
||||
(some-> currency
|
||||
name
|
||||
string/upper-case)))
|
||||
|
||||
(defn make-limit-label-fiat
|
||||
[amount currency-symbol]
|
||||
(str currency-symbol amount))
|
||||
|
||||
(defn get-account-name-error
|
||||
[s existing-account-names]
|
||||
(cond
|
||||
|
||||
@@ -73,7 +73,12 @@
|
||||
token-units (money/bignumber 0.01)]
|
||||
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
|
||||
token-units)
|
||||
"<2")))))
|
||||
"<2")))
|
||||
(let [market-values-per-currency {:usd {:price 0.005}}
|
||||
token-units "0.01"]
|
||||
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
|
||||
token-units)
|
||||
"0")))))
|
||||
|
||||
(deftest calculate-total-token-balance-test
|
||||
(testing "calculate-total-token-balance function"
|
||||
@@ -163,5 +168,3 @@
|
||||
expected-order ["DAI" "ETH" "SNT"]]
|
||||
(is (= expected-order sorted-tokens)))))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -262,3 +262,7 @@
|
||||
;; :cost () ;; tbd not used on desktop
|
||||
:token-fees token-fees
|
||||
:gas-amount (:tx-gas-amount new-path)}))
|
||||
|
||||
(defn tokens-never-loaded?
|
||||
[db]
|
||||
(nil? (get-in db [:wallet :ui :tokens-loading])))
|
||||
|
||||
@@ -7,4 +7,7 @@
|
||||
|
||||
(def defaults
|
||||
{:ui {:network-filter network-filter-defaults
|
||||
:tokens-loading {}}})
|
||||
;; Note: we set it to nil by default to differentiate when the user logs
|
||||
;; in and the device is offline, versus re-fetching when offline and
|
||||
;; tokens already exist in the app-db.
|
||||
:tokens-loading nil}})
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[clojure.string :as string]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.network.data-store :as network.data-store]
|
||||
[status-im.contexts.settings.wallet.effects]
|
||||
[status-im.contexts.settings.wallet.events]
|
||||
[status-im.contexts.wallet.common.activity-tab.events]
|
||||
@@ -107,23 +108,44 @@
|
||||
[:dispatch [:wallet/request-new-collectibles-for-account-from-signal address]]
|
||||
[:dispatch [:wallet/check-recent-history-for-account address]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/get-accounts-success
|
||||
(defn- reconcile-accounts
|
||||
[db-accounts-by-address new-accounts]
|
||||
(reduce
|
||||
(fn [res {:keys [address] :as account}]
|
||||
;; Because we add extra fields (tokens and collectibles) into the RPC
|
||||
;; response from accounts_getAccounts, if we are offline we want to keep
|
||||
;; the old balances in the accounts, thus we merge the up-to-date account
|
||||
;; from status-go into the cached accounts. We also merge when online
|
||||
;; because we will re-fetch balances anyway.
|
||||
;;
|
||||
;; Refactor improvement: don't augment entities from status-go, store
|
||||
;; tokens and collectibles in separate keys in the app-db indexed by
|
||||
;; account address.
|
||||
(assoc res
|
||||
address
|
||||
(-> (get db-accounts-by-address address)
|
||||
(merge account)
|
||||
;; These should not be cached, otherwise when going
|
||||
;; offline->online collectibles won't be fetched.
|
||||
(dissoc :current-collectible-idx :has-more-collectibles?))))
|
||||
{}
|
||||
new-accounts))
|
||||
|
||||
(rf/reg-event-fx :wallet/get-accounts-success
|
||||
(fn [{:keys [db]} [accounts]]
|
||||
(let [wallet-accounts (data-store/rpc->accounts accounts)
|
||||
wallet-db (get db :wallet)
|
||||
new-account? (:new-account? wallet-db)
|
||||
navigate-to-account (:navigate-to-account wallet-db)]
|
||||
{:db (assoc-in db
|
||||
[:wallet :accounts]
|
||||
(utils.collection/index-by :address wallet-accounts))
|
||||
:fx (concat refresh-accounts-fx-dispatches
|
||||
{:db (update-in db [:wallet :accounts] reconcile-accounts wallet-accounts)
|
||||
:fx (concat (when (or (data-store/tokens-never-loaded? db)
|
||||
(network.data-store/online? db))
|
||||
refresh-accounts-fx-dispatches)
|
||||
[(when new-account?
|
||||
[:dispatch [:wallet/navigate-to-new-account navigate-to-account]])])})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/get-accounts
|
||||
(fn [_]
|
||||
(rf/reg-event-fx :wallet/get-accounts
|
||||
(fn []
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "accounts_getAccounts"
|
||||
:on-success [:wallet/get-accounts-success]
|
||||
@@ -486,7 +508,7 @@
|
||||
{:test-networks-enabled? test-networks-enabled?
|
||||
:is-goerli-enabled? is-goerli-enabled?})
|
||||
chains-filtered-by-mode (remove #(not (contains? chain-ids-by-mode %)) down-chain-ids)
|
||||
chains-down? (seq chains-filtered-by-mode)
|
||||
chains-down? (and (network.data-store/online? db) (seq chains-filtered-by-mode))
|
||||
chain-names (when chains-down?
|
||||
(->> (map #(-> (network-utils/id->network %)
|
||||
name
|
||||
|
||||
@@ -518,8 +518,8 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/handle-suggested-routes
|
||||
(fn [_ data]
|
||||
(if-let [{:keys [code details]} (-> data :ErrorResponse first)]
|
||||
(fn [_ [data]]
|
||||
(if-let [{:keys [code details]} (-> data :ErrorResponse)]
|
||||
(let [error-message (if (= code "0") "An error occurred" details)]
|
||||
(log/error "failed to get suggested routes (async)"
|
||||
{:event :wallet/handle-suggested-routes
|
||||
@@ -531,7 +531,6 @@
|
||||
candidates-fix (comp ->old-route-paths
|
||||
remove-invalid-bonder-fees-routes)
|
||||
routes (-> data
|
||||
(first)
|
||||
(data-store/rpc->suggested-routes)
|
||||
(update :best best-routes-fix)
|
||||
(update :candidates candidates-fix))]
|
||||
|
||||
@@ -110,46 +110,23 @@
|
||||
(h/setup-subs sub-mocks)
|
||||
(h/render-with-theme-provider [input-amount/view
|
||||
{:crypto-decimals 2
|
||||
:limit-crypto 250
|
||||
:limit-crypto (money/bignumber 250)
|
||||
:initial-crypto-currency? false}])
|
||||
(h/is-truthy (h/get-by-text "0"))
|
||||
(h/is-truthy (h/get-by-text "ETH"))
|
||||
(h/is-truthy (h/get-by-text "$0.00"))
|
||||
(h/is-truthy (h/get-by-text "USD"))
|
||||
(h/is-truthy (h/get-by-text "0 ETH"))
|
||||
(h/is-truthy (h/get-by-label-text :container))
|
||||
(h/is-disabled (h/get-by-label-text :button-one)))
|
||||
|
||||
(h/test "Fill token input and confirm"
|
||||
(h/setup-subs sub-mocks)
|
||||
(let [on-confirm (h/mock-fn)]
|
||||
(h/render-with-theme-provider [input-amount/view
|
||||
{:on-confirm on-confirm
|
||||
:crypto-decimals 10
|
||||
:limit-crypto 1000
|
||||
:initial-crypto-currency? false}])
|
||||
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
|
||||
|
||||
(-> (h/wait-for #(h/get-by-text "$1234.50"))
|
||||
(.then (fn []
|
||||
(h/is-truthy (h/get-by-label-text :button-one))
|
||||
(h/is-truthy (h/get-by-label-text :container))
|
||||
(h/fire-event :press (h/get-by-label-text :button-one))
|
||||
(h/was-called on-confirm))))))
|
||||
|
||||
(h/test "Fill token input and confirm"
|
||||
(h/setup-subs sub-mocks)
|
||||
|
||||
(let [on-confirm (h/mock-fn)]
|
||||
(h/render-with-theme-provider [input-amount/view
|
||||
{:crypto-decimals 10
|
||||
:limit-crypto 1000
|
||||
:limit-crypto (money/bignumber 1000)
|
||||
:on-confirm on-confirm
|
||||
:initial-crypto-currency? false}])
|
||||
:initial-crypto-currency? true}])
|
||||
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
|
||||
@@ -158,18 +135,19 @@
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
|
||||
|
||||
|
||||
(-> (h/wait-for #(h/get-by-text "$1234.50"))
|
||||
(.then (fn []
|
||||
(h/is-truthy (h/get-by-label-text :button-one))
|
||||
(h/is-truthy (h/get-by-label-text :container))
|
||||
(h/fire-event :press (h/get-by-label-text :button-one))
|
||||
(h/was-called on-confirm))))))
|
||||
(let [btn (h/get-by-label-text :button-one)]
|
||||
(h/is-truthy btn)
|
||||
(h/fire-event :press btn)
|
||||
(h/was-called on-confirm)))))))
|
||||
|
||||
(h/test "Try to fill more than limit"
|
||||
(h/setup-subs sub-mocks)
|
||||
(h/render-with-theme-provider [input-amount/view
|
||||
{:crypto-decimals 1
|
||||
:limit-crypto 1}])
|
||||
:limit-crypto (money/bignumber 1)}])
|
||||
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
|
||||
@@ -181,13 +159,12 @@
|
||||
(h/setup-subs sub-mocks)
|
||||
(h/render-with-theme-provider [input-amount/view
|
||||
{:crypto-decimals 1
|
||||
:limit-crypto 1
|
||||
:limit-crypto (money/bignumber 10)
|
||||
:on-confirm #()}])
|
||||
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
|
||||
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
|
||||
(h/is-truthy (h/get-by-label-text :container-error))
|
||||
(h/fire-event :press (h/query-by-label-text :reorder))
|
||||
(h/is-truthy #(h/get-by-text "Max: $100.00"))))
|
||||
|
||||
(-> (h/wait-for #(h/get-by-text "Max: $1000.00"))
|
||||
(.then (fn []
|
||||
(h/wait-for #(h/is-truthy (h/get-by-label-text :container))))))))
|
||||
|
||||
@@ -153,9 +153,8 @@
|
||||
bottom (safe-area/get-bottom)
|
||||
[crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
|
||||
on-navigate-back on-navigate-back
|
||||
[input-state set-input-state] (rn/use-state controlled-input/init-state)
|
||||
|
||||
[just-toggled-mode? set-just-toggled-mode?] (rn/use-state false)
|
||||
clear-input! #(set-input-state controlled-input/delete-all)
|
||||
handle-on-confirm (fn [amount]
|
||||
(rf/dispatch [:wallet/set-token-amount-to-send
|
||||
{:amount amount
|
||||
@@ -167,59 +166,39 @@
|
||||
:as
|
||||
token} (rf/sub [:wallet/wallet-send-token])
|
||||
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
|
||||
{token-balance :total-balance
|
||||
available-balance :available-balance
|
||||
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
|
||||
{token-balance :total-balance
|
||||
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
|
||||
(str token-symbol)
|
||||
enabled-from-chain-ids])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
currency (rf/sub [:profile/currency])
|
||||
conversion-rate (-> token
|
||||
:market-values-per-currency
|
||||
currency
|
||||
:price)
|
||||
[input-state set-input-state] (rn/use-state
|
||||
(-> controlled-input/init-state
|
||||
(controlled-input/set-upper-limit
|
||||
(utils/cut-crypto-decimals-to-fit-usd-cents
|
||||
(or default-limit-crypto token-balance)
|
||||
conversion-rate))))
|
||||
clear-input! #(set-input-state controlled-input/delete-all)
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
|
||||
loading-routes? (rf/sub
|
||||
[:wallet/wallet-send-loading-suggested-routes?])
|
||||
route (rf/sub [:wallet/wallet-send-route])
|
||||
on-confirm (or default-on-confirm handle-on-confirm)
|
||||
crypto-decimals (or token-decimals default-crypto-decimals)
|
||||
current-crypto-limit (or default-limit-crypto
|
||||
(utils/get-standard-crypto-format
|
||||
token
|
||||
token-balance))
|
||||
available-crypto-limit (or default-limit-crypto
|
||||
(utils/get-standard-crypto-format
|
||||
token
|
||||
available-balance))
|
||||
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
|
||||
available-fiat-limit (.toFixed (* available-balance conversion-rate) 2)
|
||||
current-limit (if crypto-currency?
|
||||
current-crypto-limit
|
||||
current-fiat-limit)
|
||||
available-limit (if crypto-currency?
|
||||
available-crypto-limit
|
||||
available-fiat-limit)
|
||||
valid-input? (not (or (string/blank?
|
||||
(controlled-input/input-value
|
||||
input-state))
|
||||
(<= (controlled-input/numeric-value
|
||||
input-state)
|
||||
0)
|
||||
(> (controlled-input/numeric-value
|
||||
input-state)
|
||||
available-limit)))
|
||||
input-num-value (controlled-input/numeric-value input-state)
|
||||
input-amount (controlled-input/input-value input-state)
|
||||
input-value (controlled-input/input-value input-state)
|
||||
valid-input? (not (or (controlled-input/empty-value? input-state)
|
||||
(controlled-input/input-error input-state)))
|
||||
confirm-disabled? (or (nil? route)
|
||||
(empty? route)
|
||||
(string/blank? (controlled-input/input-value
|
||||
input-state))
|
||||
(<= input-num-value 0)
|
||||
(> input-num-value current-limit))
|
||||
(not valid-input?))
|
||||
amount-in-crypto (if crypto-currency?
|
||||
input-amount
|
||||
input-value
|
||||
(number/remove-trailing-zeroes
|
||||
(.toFixed (/ input-amount conversion-rate)
|
||||
(.toFixed (/ input-value conversion-rate)
|
||||
crypto-decimals)))
|
||||
total-amount-receiver (rf/sub [:wallet/total-amount true])
|
||||
amount-text (str (number/remove-trailing-zeroes
|
||||
@@ -231,11 +210,9 @@
|
||||
native-currency-symbol (when-not confirm-disabled?
|
||||
(get-in first-route
|
||||
[:from :native-currency-symbol]))
|
||||
native-token (when native-currency-symbol
|
||||
(rf/sub [:wallet/token-by-symbol
|
||||
fee-formatted (when native-currency-symbol
|
||||
(rf/sub [:wallet/wallet-send-fee-fiat-formatted
|
||||
native-currency-symbol]))
|
||||
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
|
||||
native-token])
|
||||
show-select-asset-sheet #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
@@ -269,8 +246,7 @@
|
||||
receiver-selected-network))
|
||||
receiver-networks))
|
||||
input-error (controlled-input/input-error input-state)
|
||||
limit-insufficient? (> (controlled-input/numeric-value input-state)
|
||||
current-limit)
|
||||
limit-insufficient? (controlled-input/upper-limit-exceeded? input-state)
|
||||
should-try-again? (and (not limit-insufficient?) no-routes-found?)
|
||||
current-address (rf/sub [:wallet/current-viewing-account-address])
|
||||
owned-eth-token (rf/sub [:wallet/token-by-symbol
|
||||
@@ -283,7 +259,9 @@
|
||||
(if (= token-symbol
|
||||
(string/upper-case
|
||||
constants/mainnet-short-name))
|
||||
(= current-limit input-amount)
|
||||
(money/equal-to
|
||||
(controlled-input/numeric-value input-state)
|
||||
(controlled-input/upper-limit input-state))
|
||||
(money/equal-to (:total-balance
|
||||
owned-eth-token)
|
||||
0)))
|
||||
@@ -297,34 +275,20 @@
|
||||
:valid-input? valid-input?
|
||||
:bounce-duration-ms bounce-duration-ms
|
||||
:token token}))
|
||||
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
|
||||
swap-between-fiat-and-crypto (fn []
|
||||
(set-just-toggled-mode? true)
|
||||
(set-crypto-currency swap-to-crypto-currency?)
|
||||
(set-input-state
|
||||
(fn [input-state]
|
||||
(controlled-input/set-input-value
|
||||
input-state
|
||||
(let [value (controlled-input/input-value
|
||||
input-state)
|
||||
new-value (if swap-to-crypto-currency?
|
||||
(.toFixed (/ value
|
||||
conversion-rate)
|
||||
crypto-decimals)
|
||||
(.toFixed (* value
|
||||
conversion-rate)
|
||||
12))]
|
||||
(number/remove-trailing-zeroes
|
||||
new-value))))))]
|
||||
(if crypto-currency?
|
||||
(set-input-state
|
||||
#(controlled-input/->fiat % conversion-rate))
|
||||
(set-input-state
|
||||
#(controlled-input/->crypto % conversion-rate)))
|
||||
(set-crypto-currency (not crypto-currency?)))]
|
||||
(rn/use-mount
|
||||
(fn []
|
||||
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
|
||||
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
|
||||
#(.remove app-keyboard-listener))))
|
||||
(hot-reload/use-safe-unmount on-navigate-back)
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
|
||||
[current-limit])
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when input-error (debounce/clear-all))
|
||||
@@ -348,24 +312,36 @@
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:switcher-type :select-account}]
|
||||
[quo/token-input
|
||||
{:container-style style/input-container
|
||||
:token token-symbol
|
||||
:currency fiat-currency
|
||||
:currency-symbol currency-symbol
|
||||
:crypto-decimals (min token-decimals 6)
|
||||
:error? (controlled-input/input-error input-state)
|
||||
:networks (seq from-enabled-networks)
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
(utils/make-limit-label-crypto current-limit token-symbol)
|
||||
(utils/make-limit-label-fiat current-limit currency-symbol))})
|
||||
:conversion conversion-rate
|
||||
:show-keyboard? false
|
||||
:value input-amount
|
||||
:on-swap swap-between-fiat-and-crypto
|
||||
:on-token-press show-select-asset-sheet
|
||||
:allow-selection? false}]
|
||||
{:container-style style/input-container
|
||||
:token-symbol token-symbol
|
||||
:value input-value
|
||||
:on-swap swap-between-fiat-and-crypto
|
||||
:on-token-press show-select-asset-sheet
|
||||
:error? (controlled-input/input-error input-state)
|
||||
:currency-symbol (if crypto-currency? token-symbol fiat-currency)
|
||||
:converted-value (if crypto-currency?
|
||||
(utils/prettify-balance
|
||||
currency-symbol
|
||||
(money/crypto->fiat input-value
|
||||
conversion-rate))
|
||||
(utils/prettify-crypto-balance
|
||||
(or (clj->js token-symbol) "")
|
||||
(money/fiat->crypto input-value
|
||||
conversion-rate)
|
||||
conversion-rate))
|
||||
:hint-component [quo/network-tags
|
||||
{:networks (seq from-enabled-networks)
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
(utils/prettify-crypto-balance
|
||||
(or (clj->js token-symbol) "")
|
||||
(controlled-input/upper-limit input-state)
|
||||
conversion-rate)
|
||||
(utils/prettify-balance currency-symbol
|
||||
(controlled-input/upper-limit
|
||||
input-state)))})
|
||||
:status (when (controlled-input/input-error input-state) :error)}]}]
|
||||
[routes/view
|
||||
{:token token-by-symbol
|
||||
:send-amount-in-crypto amount-in-crypto
|
||||
@@ -410,7 +386,7 @@
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press (fn [c]
|
||||
(let [new-text (str input-amount c)
|
||||
(let [new-text (str input-value c)
|
||||
max-decimals (if crypto-currency? crypto-decimals 2)
|
||||
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
|
||||
regex (re-pattern regex-pattern)]
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
(ns status-im.contexts.wallet.send.routes.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.controlled-input.utils :as controlled-input]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.common.utils.networks :as network-utils]
|
||||
[status-im.contexts.wallet.send.routes.style :as style]
|
||||
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
|
||||
[status-im.feature-flags :as ff]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]
|
||||
[utils.number :as number]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
||||
@@ -87,160 +80,164 @@
|
||||
(rf/dispatch [:wallet/update-receiver-networks
|
||||
chain-ids]))}]))}]))
|
||||
|
||||
(defn- edit-amount
|
||||
[{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
|
||||
chain-id])
|
||||
{fiat-currency :currency} (rf/sub [:profile/profile])
|
||||
{token-decimals :decimals
|
||||
:as
|
||||
token} (rf/sub [:wallet/wallet-send-token])
|
||||
currency (rf/sub [:profile/currency])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
send-from-locked-amounts (rf/sub
|
||||
[:wallet/wallet-send-from-locked-amounts])
|
||||
{account-color :color} (rf/sub [:wallet/current-viewing-account])
|
||||
locked-amount (get send-from-locked-amounts chain-id)
|
||||
network-name-str (string/capitalize (name network-name))
|
||||
[input-state set-input-state] (rn/use-state (cond-> controlled-input/init-state
|
||||
init-amount
|
||||
(controlled-input/set-input-value
|
||||
(money/to-string init-amount))
|
||||
locked-amount
|
||||
(controlled-input/set-input-value
|
||||
locked-amount)))
|
||||
[crypto-currency? set-crypto-currency] (rn/use-state true)
|
||||
conversion-rate (-> token
|
||||
:market-values-per-currency
|
||||
currency
|
||||
:price)
|
||||
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
|
||||
(str token-symbol)
|
||||
[chain-id]])
|
||||
current-crypto-limit (utils/get-standard-crypto-format
|
||||
token
|
||||
token-balance)
|
||||
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
|
||||
current-limit (if crypto-currency?
|
||||
current-crypto-limit
|
||||
current-fiat-limit)
|
||||
crypto-decimals token-decimals
|
||||
input-amount (controlled-input/input-value input-state)
|
||||
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
|
||||
bottom (safe-area/get-bottom)
|
||||
amount-in-crypto (if crypto-currency?
|
||||
input-amount
|
||||
(number/remove-trailing-zeroes
|
||||
(.toFixed (/ input-amount
|
||||
conversion-rate)
|
||||
crypto-decimals)))
|
||||
locked-greater-then-send-amount? (let [amount (money/bignumber
|
||||
amount-in-crypto)
|
||||
send-amount (money/bignumber
|
||||
send-amount-in-crypto)]
|
||||
(and (money/bignumber? amount)
|
||||
(money/bignumber? send-amount)
|
||||
(money/greater-than amount send-amount)))
|
||||
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
|
||||
(set-crypto-currency swap-to-crypto-currency?)
|
||||
(set-input-state
|
||||
(fn [input-state]
|
||||
#_(defn- edit-amount
|
||||
[{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
|
||||
chain-id])
|
||||
{fiat-currency :currency} (rf/sub [:profile/profile])
|
||||
{token-decimals :decimals
|
||||
:as
|
||||
token} (rf/sub [:wallet/wallet-send-token])
|
||||
currency (rf/sub [:profile/currency])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
send-from-locked-amounts (rf/sub
|
||||
[:wallet/wallet-send-from-locked-amounts])
|
||||
{account-color :color} (rf/sub [:wallet/current-viewing-account])
|
||||
locked-amount (get send-from-locked-amounts chain-id)
|
||||
network-name-str (string/capitalize (name network-name))
|
||||
[input-state set-input-state] (rn/use-state
|
||||
(cond-> controlled-input/init-state
|
||||
init-amount
|
||||
(controlled-input/set-input-value
|
||||
input-state
|
||||
(let [value (controlled-input/input-value
|
||||
input-state)
|
||||
new-value (if
|
||||
swap-to-crypto-currency?
|
||||
(.toFixed
|
||||
(/ value
|
||||
conversion-rate)
|
||||
crypto-decimals)
|
||||
(.toFixed
|
||||
(* value
|
||||
conversion-rate)
|
||||
12))]
|
||||
(number/remove-trailing-zeroes
|
||||
new-value))))))
|
||||
lock-or-unlock-amount (fn []
|
||||
(if is-amount-locked?
|
||||
(rf/dispatch [:wallet/lock-from-amount
|
||||
chain-id
|
||||
amount-in-crypto])
|
||||
(rf/dispatch [:wallet/unlock-from-amount
|
||||
chain-id]))
|
||||
(rf/dispatch [:hide-bottom-sheet]))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
|
||||
[current-limit])
|
||||
[:<>
|
||||
[quo/drawer-top
|
||||
{:title (i18n/label :t/send-from-network {:network network-name-str})
|
||||
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
|
||||
[quo/token-input
|
||||
{:container-style style/input-container
|
||||
:token token-symbol
|
||||
:currency fiat-currency
|
||||
:currency-symbol currency-symbol
|
||||
:crypto-decimals (min token-decimals 6)
|
||||
:error? (controlled-input/input-error input-state)
|
||||
:networks [network-details]
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
(utils/make-limit-label-crypto current-limit token-symbol)
|
||||
(utils/make-limit-label-fiat current-limit currency-symbol))})
|
||||
:conversion conversion-rate
|
||||
:show-keyboard? false
|
||||
:value (controlled-input/input-value input-state)
|
||||
:on-swap swap-between-fiat-and-crypto
|
||||
:allow-selection? false}]
|
||||
(when locked-greater-then-send-amount?
|
||||
[quo/information-box
|
||||
{:type :error
|
||||
:icon :i/info
|
||||
:style style/error-box}
|
||||
(i18n/label :t/value-higher-than-send-amount)])
|
||||
[quo/disclaimer
|
||||
{:on-change (fn [checked?]
|
||||
(set-is-amount-locked checked?))
|
||||
:checked? is-amount-locked?
|
||||
:container-style style/disclaimer
|
||||
:icon (if is-amount-locked?
|
||||
:i/locked
|
||||
:i/unlocked)
|
||||
:customization-color account-color}
|
||||
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/update)
|
||||
:button-one-props {:on-press lock-or-unlock-amount
|
||||
:customization-color account-color
|
||||
:disabled? (or (controlled-input/empty-value? input-state)
|
||||
(controlled-input/input-error input-state)
|
||||
locked-greater-then-send-amount?)}}]
|
||||
[quo/numbered-keyboard
|
||||
{:container-style (style/keyboard-container bottom)
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press (fn [c]
|
||||
(let [new-text (str input-amount c)
|
||||
max-decimals (if crypto-currency? crypto-decimals 2)
|
||||
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
|
||||
regex (re-pattern regex-pattern)]
|
||||
(when (re-matches regex new-text)
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state #(controlled-input/add-character % c)))))
|
||||
:on-delete (fn []
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state controlled-input/delete-last))
|
||||
:on-long-press-delete (fn []
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state controlled-input/delete-all))}]]))}]))
|
||||
(money/to-string init-amount))
|
||||
locked-amount
|
||||
(controlled-input/set-input-value
|
||||
locked-amount)))
|
||||
[crypto-currency? set-crypto-currency] (rn/use-state true)
|
||||
conversion-rate (-> token
|
||||
:market-values-per-currency
|
||||
currency
|
||||
:price)
|
||||
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
|
||||
(str token-symbol)
|
||||
[chain-id]])
|
||||
current-crypto-limit (utils/get-standard-crypto-format
|
||||
token
|
||||
token-balance)
|
||||
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
|
||||
current-limit (if crypto-currency?
|
||||
current-crypto-limit
|
||||
current-fiat-limit)
|
||||
crypto-decimals token-decimals
|
||||
input-amount (controlled-input/input-value input-state)
|
||||
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
|
||||
bottom (safe-area/get-bottom)
|
||||
amount-in-crypto (if crypto-currency?
|
||||
input-amount
|
||||
(number/remove-trailing-zeroes
|
||||
(.toFixed (/ input-amount
|
||||
conversion-rate)
|
||||
crypto-decimals)))
|
||||
locked-greater-then-send-amount? (let [amount (money/bignumber
|
||||
amount-in-crypto)
|
||||
send-amount (money/bignumber
|
||||
send-amount-in-crypto)]
|
||||
(and (money/bignumber? amount)
|
||||
(money/bignumber? send-amount)
|
||||
(money/greater-than amount
|
||||
send-amount)))
|
||||
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
|
||||
(set-crypto-currency
|
||||
swap-to-crypto-currency?)
|
||||
(set-input-state
|
||||
(fn [input-state]
|
||||
(controlled-input/set-input-value
|
||||
input-state
|
||||
(let [value (controlled-input/input-value
|
||||
input-state)
|
||||
new-value
|
||||
(if
|
||||
swap-to-crypto-currency?
|
||||
(.toFixed
|
||||
(/ value
|
||||
conversion-rate)
|
||||
crypto-decimals)
|
||||
(.toFixed
|
||||
(* value
|
||||
conversion-rate)
|
||||
12))]
|
||||
(number/remove-trailing-zeroes
|
||||
new-value))))))
|
||||
lock-or-unlock-amount (fn []
|
||||
(if is-amount-locked?
|
||||
(rf/dispatch [:wallet/lock-from-amount
|
||||
chain-id
|
||||
amount-in-crypto])
|
||||
(rf/dispatch [:wallet/unlock-from-amount
|
||||
chain-id]))
|
||||
(rf/dispatch [:hide-bottom-sheet]))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
|
||||
[current-limit])
|
||||
[:<>
|
||||
[quo/drawer-top
|
||||
{:title (i18n/label :t/send-from-network {:network network-name-str})
|
||||
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
|
||||
[quo/token-input
|
||||
{:container-style style/input-container
|
||||
:token token-symbol
|
||||
:currency fiat-currency
|
||||
:currency-symbol currency-symbol
|
||||
:crypto-decimals (min token-decimals 6)
|
||||
:error? (controlled-input/input-error input-state)
|
||||
:networks [network-details]
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
(utils/make-limit-label-crypto current-limit token-symbol)
|
||||
(utils/make-limit-label-fiat current-limit currency-symbol))})
|
||||
:conversion conversion-rate
|
||||
:show-keyboard? false
|
||||
:value (controlled-input/input-value input-state)
|
||||
:on-swap swap-between-fiat-and-crypto
|
||||
:allow-selection? false}]
|
||||
(when locked-greater-then-send-amount?
|
||||
[quo/information-box
|
||||
{:type :error
|
||||
:icon :i/info
|
||||
:style style/error-box}
|
||||
(i18n/label :t/value-higher-than-send-amount)])
|
||||
[quo/disclaimer
|
||||
{:on-change (fn [checked?]
|
||||
(set-is-amount-locked checked?))
|
||||
:checked? is-amount-locked?
|
||||
:container-style style/disclaimer
|
||||
:icon (if is-amount-locked?
|
||||
:i/locked
|
||||
:i/unlocked)
|
||||
:customization-color account-color}
|
||||
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/update)
|
||||
:button-one-props {:on-press lock-or-unlock-amount
|
||||
:customization-color account-color
|
||||
:disabled? (or (controlled-input/empty-value? input-state)
|
||||
(controlled-input/input-error input-state)
|
||||
locked-greater-then-send-amount?)}}]
|
||||
[quo/numbered-keyboard
|
||||
{:container-style (style/keyboard-container bottom)
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press (fn [c]
|
||||
(let [new-text (str input-amount c)
|
||||
max-decimals (if crypto-currency? crypto-decimals 2)
|
||||
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
|
||||
regex (re-pattern regex-pattern)]
|
||||
(when (re-matches regex new-text)
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state #(controlled-input/add-character % c)))))
|
||||
:on-delete (fn []
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state controlled-input/delete-last))
|
||||
:on-long-press-delete (fn []
|
||||
(set-is-amount-locked true)
|
||||
(set-input-state controlled-input/delete-all))}]]))}]))
|
||||
|
||||
(defn render-network-values
|
||||
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
|
||||
@@ -388,13 +385,6 @@
|
||||
chain-id-to-disable
|
||||
disabled-from-chain-ids
|
||||
token-available-networks-for-suggested-routes))
|
||||
(when (ff/enabled? ::ff/wallet.custom-network-amounts)
|
||||
:on-long-press) (fn [chain-id amount-calculated-for-chain]
|
||||
(edit-amount
|
||||
{:chain-id chain-id
|
||||
:token-symbol token-symbol
|
||||
:send-amount-in-crypto send-amount-in-crypto
|
||||
:init-amount amount-calculated-for-chain}))
|
||||
:receiver? false
|
||||
:theme theme
|
||||
:loading-routes? loading-routes?
|
||||
|
||||
@@ -213,11 +213,8 @@
|
||||
first-route (first route)
|
||||
native-currency-symbol (get-in first-route
|
||||
[:from :native-currency-symbol])
|
||||
native-token (when native-currency-symbol
|
||||
(rf/sub [:wallet/token-by-symbol
|
||||
native-currency-symbol]))
|
||||
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
|
||||
native-token])
|
||||
native-currency-symbol])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
account-color (:color account)
|
||||
bridge-to-network (when bridge-to-chain-id
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.contexts.wallet.sheets.account-options.view
|
||||
(:require [oops.core :as oops]
|
||||
(:require [clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
quo.theme
|
||||
@@ -33,6 +34,7 @@
|
||||
[{:keys [theme show-account-selector? options-height]}]
|
||||
(let [{:keys [name color emoji address watch-only?
|
||||
default-account?]} (rf/sub [:wallet/current-viewing-account])
|
||||
{:keys [derived-from]} (rf/sub [:wallet/current-viewing-account-keypair])
|
||||
network-preference-details (rf/sub [:wallet/network-preference-details])
|
||||
multichain-address (utils/get-multichain-address
|
||||
network-preference-details
|
||||
@@ -89,7 +91,7 @@
|
||||
#(rf/dispatch [:wallet/share-account
|
||||
{:title share-title :content multichain-address}])
|
||||
600))}
|
||||
(when-not default-account?
|
||||
(when-not (or default-account? (string/blank? derived-from))
|
||||
{:add-divider? (not show-account-selector?)
|
||||
:icon :i/delete
|
||||
:accessibility-label :remove-account
|
||||
|
||||
@@ -43,9 +43,15 @@
|
||||
{:fx [[:dispatch [:wallet/reload]]]}
|
||||
|
||||
"wallet-blockchain-status-changed"
|
||||
{:fx [[:dispatch
|
||||
[:wallet/blockchain-status-changed
|
||||
(transforms/js->clj event-js)]]]}
|
||||
{:fx [[:dispatch-later
|
||||
;; Don't dispatch immediately because the signal may arrive as
|
||||
;; soon as the device goes offline. We need to give some time for
|
||||
;; RN to dispatch the network status update, otherwise when going
|
||||
;; offline the user will immediately see a toast saying "provider
|
||||
;; X is down".
|
||||
[{:ms 500
|
||||
:dispatch [:wallet/blockchain-status-changed
|
||||
(transforms/js->clj event-js)]}]]]}
|
||||
|
||||
"wallet-activity-filtering-done"
|
||||
{:fx
|
||||
|
||||
@@ -28,20 +28,23 @@
|
||||
:max-priority-fee-per-gas "0.011000001"
|
||||
:eip1559-enabled true}
|
||||
:estimated-time 3
|
||||
:receive-amount 99.98
|
||||
:receive-token {:symbol "SNT"
|
||||
:receive-amount "99.98"
|
||||
:pay-token {:symbol "SNT"
|
||||
:address "0x432492384728934239789"}
|
||||
:receive-token {:symbol "USDT"
|
||||
:address "0x432492384728934239789"}})
|
||||
|
||||
(defn- assets-view
|
||||
[search-text on-change-text]
|
||||
(let [on-token-press (fn [token]
|
||||
(let [token-networks (:networks token)]
|
||||
(let [token-networks (:networks token)
|
||||
asset-to-receive (rf/sub [:wallet/token-by-symbol "SNT"])]
|
||||
(rf/dispatch [:wallet.swap/select-asset-to-pay
|
||||
{:token token
|
||||
:network (when (= (count token-networks) 1)
|
||||
(first token-networks))
|
||||
:stack-id :screen/wallet.swap-select-asset-to-pay}])
|
||||
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token token}])
|
||||
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token asset-to-receive}])
|
||||
(rf/dispatch [:wallet.swap/set-pay-amount 100])
|
||||
(rf/dispatch [:wallet.swap/set-swap-proposal dummy-swap-proposal])
|
||||
(rf/dispatch [:wallet.swap/set-provider])))]
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
[quo.foundations.resources :as resources]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.common.standard-authentication.core :as standard-auth]
|
||||
[status-im.contexts.wallet.common.utils.external-links :as external-links]
|
||||
[status-im.contexts.wallet.swap.set-spending-cap.style :as style]
|
||||
[utils.address :as address-utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.navigation :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- swap-title
|
||||
@@ -172,11 +172,7 @@
|
||||
|
||||
(defn footer
|
||||
[{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}]
|
||||
(let [native-token (when native-currency-symbol
|
||||
(rf/sub [:wallet/token-by-symbol
|
||||
native-currency-symbol]))
|
||||
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
|
||||
native-token])
|
||||
(let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])
|
||||
on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))]
|
||||
[rn/view {:style {:margin-bottom -10}}
|
||||
[transaction-details
|
||||
@@ -213,7 +209,7 @@
|
||||
{:footer-container-padding 0
|
||||
:header [quo/page-nav
|
||||
{:icon-name :i/close
|
||||
:on-press navigation/navigate-back
|
||||
:on-press events-helper/navigate-back
|
||||
:margin-top 8
|
||||
:background :blur
|
||||
:accessibility-label :top-bar}]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
(ns status-im.contexts.wallet.swap.setup-swap.style)
|
||||
|
||||
(def container
|
||||
{:flex 1})
|
||||
|
||||
(def keyboard-container
|
||||
{:align-self :flex-end
|
||||
:width "100%"})
|
||||
|
||||
(def inputs-container
|
||||
{:padding-top 12
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def details-container
|
||||
{:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:padding-top 7
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def detail-item
|
||||
{:flex 1
|
||||
:height 36
|
||||
:background-color :transparent})
|
||||
|
||||
(def swap-order-button
|
||||
{:margin-top -9
|
||||
:z-index 2
|
||||
:align-self :center})
|
||||
|
||||
(def receive-token-swap-input-container
|
||||
{:margin-top -9})
|
||||
|
||||
(def footer-container
|
||||
{:flex 1
|
||||
:justify-content :flex-end})
|
||||
@@ -0,0 +1,132 @@
|
||||
(ns status-im.contexts.wallet.swap.setup-swap.view
|
||||
(:require [quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.events-helper :as events-helper]
|
||||
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.swap.setup-swap.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- data-item
|
||||
[{:keys [title subtitle size subtitle-icon loading?]}]
|
||||
[quo/data-item
|
||||
{:container-style style/detail-item
|
||||
:blur? false
|
||||
:card? false
|
||||
:subtitle-type (if subtitle-icon :editable :default)
|
||||
:status (if loading? :loading :default)
|
||||
:title title
|
||||
:subtitle subtitle
|
||||
:size size
|
||||
:icon subtitle-icon}])
|
||||
|
||||
(defn- transaction-details
|
||||
[{:keys [max-slippage native-currency-symbol loading-fees?]}]
|
||||
(let [max-fees (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])]
|
||||
[rn/view {:style style/details-container}
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading-fees?
|
||||
:size :small}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-slippage)
|
||||
:subtitle max-slippage
|
||||
:subtitle-icon :i/edit
|
||||
:loading? loading-fees?}]]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [[pay-value set-pay-value] (rn/use-state "")
|
||||
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
|
||||
{:keys [max-slippage swap-proposal loading-fees?
|
||||
receive-amount network]} (rf/sub [:wallet/swap])
|
||||
currency (rf/sub [:profile/currency])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
|
||||
|
||||
pay-token-fiat-value (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance (or pay-value 0)
|
||||
:token asset-to-pay})
|
||||
receive-token-fiat-value (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance (or receive-amount 0)
|
||||
:token asset-to-receive})
|
||||
native-currency-symbol (get-in swap-proposal
|
||||
[:from :native-currency-symbol])
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
receive-token-symbol (:symbol asset-to-receive)
|
||||
on-press (fn [v] (set-pay-value (str pay-value v)))
|
||||
delete (fn []
|
||||
(set-pay-value #(subs % 0 (dec (count %)))))]
|
||||
[rn/view {:style style/container}
|
||||
[account-switcher/view
|
||||
{:on-press events-helper/navigate-back
|
||||
:icon-name :i/arrow-left
|
||||
:margin-top (safe-area/get-top)
|
||||
:switcher-type :select-account}]
|
||||
[rn/view {:style style/inputs-container}
|
||||
[quo/swap-input
|
||||
{:type :pay
|
||||
:error? false
|
||||
:token pay-token-symbol
|
||||
:customization-color :blue
|
||||
:show-approval-label? false
|
||||
:status :default
|
||||
:currency-symbol currency-symbol
|
||||
:on-swap-press #(js/alert "Swap Pressed")
|
||||
:on-token-press #(js/alert "Token Pressed")
|
||||
:on-max-press #(js/alert "Max Pressed")
|
||||
:value pay-value
|
||||
:fiat-value pay-token-fiat-value
|
||||
:network-tag-props {:title (i18n/label :t/max-token
|
||||
{:number 200
|
||||
:token-symbol pay-token-symbol})
|
||||
:networks [{:source (:source network)}]}
|
||||
:approval-label-props {:status :approve
|
||||
:token-value pay-value
|
||||
:button-props {:on-press
|
||||
#(js/alert "Approve Pressed")}
|
||||
:customization-color color
|
||||
:token-symbol pay-token-symbol}}]
|
||||
[quo/swap-order-button
|
||||
{:container-style style/swap-order-button
|
||||
:on-press #(js/alert "Pressed")}]
|
||||
[quo/swap-input
|
||||
{:type :receive
|
||||
:error? false
|
||||
:token receive-token-symbol
|
||||
:customization-color color
|
||||
:show-approval-label? false
|
||||
:enable-swap? true
|
||||
:status :default
|
||||
:currency-symbol currency-symbol
|
||||
:on-swap-press #(js/alert "Swap Pressed")
|
||||
:on-token-press #(js/alert "Token Pressed")
|
||||
:on-max-press #(js/alert "Max Pressed")
|
||||
:value receive-amount
|
||||
:fiat-value receive-token-fiat-value
|
||||
:container-style style/receive-token-swap-input-container}]]
|
||||
[rn/view {:style style/footer-container}
|
||||
(when swap-proposal
|
||||
[transaction-details
|
||||
{:native-currency-symbol native-currency-symbol
|
||||
:max-slippage max-slippage
|
||||
:loading-fees? loading-fees?}])
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/review-swap)
|
||||
:button-one-props {:disabled? (or (not swap-proposal)
|
||||
loading-fees?)
|
||||
:customization-color color
|
||||
:on-press #(js/alert "Review swap pressed")}}]]
|
||||
[quo/numbered-keyboard
|
||||
{:container-style style/keyboard-container
|
||||
:left-action :dot
|
||||
:delete-key? true
|
||||
:on-press on-press
|
||||
:on-delete delete}]]))
|
||||
@@ -103,11 +103,7 @@
|
||||
(defn footer
|
||||
[{:keys [estimated-time-min native-currency-symbol max-slippage theme account-color provider
|
||||
loading-fees?]}]
|
||||
(let [native-token (when native-currency-symbol
|
||||
(rf/sub [:wallet/token-by-symbol
|
||||
native-currency-symbol]))
|
||||
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
|
||||
native-token])]
|
||||
(let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])]
|
||||
[:<>
|
||||
[transaction-details
|
||||
{:estimated-time-min estimated-time-min
|
||||
|
||||
@@ -19,4 +19,8 @@
|
||||
"Swap confirmation"]
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])}
|
||||
"Set spending cap"]]))
|
||||
"Set spending cap"]
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:navigate-to-within-stack
|
||||
[:screen/wallet.setup-swap :screen/wallet.swap-propasal]])}
|
||||
"Setup swap"]]))
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
(re-frame/dispatch [:bottom-sheet-hidden])
|
||||
(when @state/root-id
|
||||
(reset! theme/device-theme (rn/get-color-scheme))
|
||||
(re-frame/dispatch [:init-root @state/root-id])
|
||||
(re-frame/dispatch [:update-theme-and-init-root @state/root-id])
|
||||
(re-frame/dispatch [:chat/check-last-chat])
|
||||
(re-frame/dispatch [:alert-banners/restore-alert-banners]))
|
||||
(rn/hide-splash-screen)))
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
(let [[status-bar-theme] (get-status-nav-color root-id theme)
|
||||
root (get (roots/roots status-bar-theme) root-id)]
|
||||
(dismiss-all-modals)
|
||||
(rf/dispatch [:theme/switch {:view-id root-id}])
|
||||
(reset! state/root-id (or (get-in root [:root :stack :id]) root-id))
|
||||
(navigation/set-root root)
|
||||
(state/navigation-state-reset [{:id root-id
|
||||
|
||||
@@ -70,10 +70,17 @@
|
||||
(assoc :dispatch [:chat/close (:current-chat-id db)])))
|
||||
|
||||
(rf/defn init-root
|
||||
"WARNING: Use `:update-theme-and-init-root` instead. `:init-root` should not be used directly."
|
||||
{:events [:init-root]}
|
||||
[{:keys [db]} root-id]
|
||||
{:set-root [root-id (:theme db)]})
|
||||
|
||||
(rf/defn update-theme-and-init-root
|
||||
{:events [:update-theme-and-init-root]}
|
||||
[_ root-id]
|
||||
{:fx [[:dispatch [:theme/switch {:view-id root-id}]]
|
||||
[:dispatch [:init-root root-id]]]})
|
||||
|
||||
(rf/defn change-tab
|
||||
{:events [:navigate-change-tab]}
|
||||
[{:keys [db]} stack-id]
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
[status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress]
|
||||
[status-im.contexts.wallet.swap.select-asset-to-pay.view :as wallet-swap-select-asset-to-pay]
|
||||
[status-im.contexts.wallet.swap.set-spending-cap.view :as wallet-swap-set-spending-cap]
|
||||
[status-im.contexts.wallet.swap.setup-swap.view :as wallet-swap-setup-swap]
|
||||
[status-im.contexts.wallet.swap.swap-confirmation.view :as wallet-swap-confirmation]
|
||||
[status-im.contexts.wallet.swap.swap-proposal.view :as wallet-swap-propasal]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.send-transaction.view :as
|
||||
@@ -522,6 +523,10 @@
|
||||
:insets {:top? true}}
|
||||
:component wallet-swap-select-asset-to-pay/view}
|
||||
|
||||
{:name :screen/wallet.setup-swap
|
||||
:options {:insets {:bottom? true}}
|
||||
:component wallet-swap-setup-swap/view}
|
||||
|
||||
{:name :screen/wallet.swap-propasal
|
||||
:options {:insets {:top? true}}
|
||||
:component wallet-swap-propasal/view}
|
||||
|
||||
@@ -152,8 +152,12 @@
|
||||
(fn [toasts [_ toast-id & cursor]]
|
||||
(get-in toasts (into [:toasts toast-id] cursor))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:network/offline?
|
||||
(re-frame/reg-sub :network/offline?
|
||||
:<- [:network/status]
|
||||
(fn [status]
|
||||
(= status :offline)))
|
||||
|
||||
(re-frame/reg-sub :network/online?
|
||||
:<- [:network/status]
|
||||
(fn [status]
|
||||
(= status :online)))
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
:<- [:wallet/swap]
|
||||
:-> :asset-to-pay)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-asset-to-receive
|
||||
:<- [:wallet/swap]
|
||||
:-> :asset-to-receive)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-asset-to-pay-token-symbol
|
||||
:<- [:wallet/swap-asset-to-pay]
|
||||
|
||||
@@ -64,6 +64,43 @@
|
||||
:token-list-id ""
|
||||
:built-on "ETH"
|
||||
:verified true}
|
||||
:asset-to-receive
|
||||
{:description "Dai Stablecoin"
|
||||
:decimals 18
|
||||
:symbol "DAI"
|
||||
:name "Dai Stablecoin"
|
||||
:total-balance 1
|
||||
:balances-per-chain
|
||||
{1
|
||||
{:raw-balance 1000000000000000000
|
||||
:balance "1"
|
||||
:chain-id 1}
|
||||
10
|
||||
{:raw-balance 0
|
||||
:balance "0"
|
||||
:chain-id 10}
|
||||
42161
|
||||
{:raw-balance 0
|
||||
:balance "0"
|
||||
:chain-id 42161}}
|
||||
:networks (concat [(networks :mainnet-network)] (networks :layer-2-networks))
|
||||
:chain-id 0
|
||||
:market-values-per-currency
|
||||
{:usd
|
||||
{:change-24hour -0.00109422754667007
|
||||
:change-pct-day -5.57352274163899
|
||||
:change-pct-24hour -4.177805426737527
|
||||
:high-day 0.0271858672171352
|
||||
:market-cap 170783296.1155821
|
||||
:has-error false
|
||||
:change-pct-hour -0.0160462113709363
|
||||
:low-day 0.02473516779550377
|
||||
:price 0.0251}}
|
||||
:asset-website-url "https://status.im/"
|
||||
:available-balance 1
|
||||
:token-list-id ""
|
||||
:built-on "ETH"
|
||||
:verified true}
|
||||
:network nil})
|
||||
|
||||
(h/deftest-sub :wallet/swap
|
||||
@@ -82,6 +119,14 @@
|
||||
swap-data)
|
||||
(is (match? (swap-data :asset-to-pay) (rf/sub [sub-name])))))
|
||||
|
||||
(h/deftest-sub :wallet/swap-asset-to-receive
|
||||
[sub-name]
|
||||
(testing "Return swap asset-to-receive"
|
||||
(swap! rf-db/app-db assoc-in
|
||||
[:wallet :ui :swap]
|
||||
swap-data)
|
||||
(is (match? (swap-data :asset-to-receive) (rf/sub [sub-name])))))
|
||||
|
||||
(h/deftest-sub :wallet/swap-asset-to-pay-token-symbol
|
||||
[sub-name]
|
||||
(testing "Return asset-to-pay token symbol"
|
||||
|
||||
@@ -48,11 +48,10 @@
|
||||
:wallet/home-tokens-loading?
|
||||
:<- [:wallet/tokens-loading]
|
||||
(fn [tokens-loading]
|
||||
(or (empty? tokens-loading)
|
||||
(->> tokens-loading
|
||||
vals
|
||||
(some true?)
|
||||
boolean))))
|
||||
(->> tokens-loading
|
||||
vals
|
||||
(some true?)
|
||||
boolean)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/current-viewing-account-tokens-loading?
|
||||
@@ -211,7 +210,7 @@
|
||||
:wallet/fully-operable-keypairs-list
|
||||
:<- [:wallet/keypairs-list]
|
||||
(fn [keypairs]
|
||||
(filter #(= :fully (:lowest-operability %)) keypairs)))
|
||||
(filter #(and (= :fully (:lowest-operability %)) (not-empty (:derived-from %))) keypairs)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/keypair-names
|
||||
@@ -351,9 +350,10 @@
|
||||
:<- [:wallet/accounts]
|
||||
:<- [:wallet/balances-in-selected-networks]
|
||||
:<- [:wallet/tokens-loading]
|
||||
:<- [:network/online?]
|
||||
:<- [:profile/currency-symbol]
|
||||
:<- [:wallet/keypairs]
|
||||
(fn [[accounts balances tokens-loading currency-symbol keypairs]]
|
||||
(fn [[accounts balances tokens-loading online? currency-symbol keypairs]]
|
||||
(mapv (fn [{:keys [color address watch-only? key-uid operable] :as account}]
|
||||
(let [account-type (cond
|
||||
(= operable :no) :missing-keypair
|
||||
@@ -373,8 +373,9 @@
|
||||
account
|
||||
keypair)}]))
|
||||
#(rf/dispatch [:wallet/navigate-to-account address]))
|
||||
:loading? (or (get tokens-loading address)
|
||||
(not (contains? tokens-loading address)))
|
||||
:loading? (and online?
|
||||
(or (get tokens-loading address)
|
||||
(not (contains? tokens-loading address))))
|
||||
:balance (utils/prettify-balance currency-symbol
|
||||
(get balances address)))))
|
||||
accounts)))
|
||||
@@ -690,23 +691,29 @@
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/wallet-send-fee-fiat-formatted
|
||||
:<- [:wallet/current-viewing-account]
|
||||
:<- [:wallet/wallet-send-route]
|
||||
:<- [:profile/currency]
|
||||
:<- [:profile/currency-symbol]
|
||||
(fn [[route currency currency-symbol] [_ token-for-fees]]
|
||||
(let [fee-in-native-token (send-utils/calculate-full-route-gas-fee route)
|
||||
fee-in-crypto-formatted (utils/get-standard-crypto-format
|
||||
token-for-fees
|
||||
fee-in-native-token)
|
||||
fee-in-fiat (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance fee-in-native-token
|
||||
:token token-for-fees})
|
||||
fee-formatted (utils/get-standard-fiat-format
|
||||
fee-in-crypto-formatted
|
||||
currency-symbol
|
||||
fee-in-fiat)]
|
||||
fee-formatted)))
|
||||
(fn [[account route currency currency-symbol] [_ token-symbol-for-fees]]
|
||||
(when token-symbol-for-fees
|
||||
(let [tokens (:tokens account)
|
||||
token-for-fees (first (filter #(= (string/lower-case (:symbol %))
|
||||
(string/lower-case token-symbol-for-fees))
|
||||
tokens))
|
||||
fee-in-native-token (send-utils/calculate-full-route-gas-fee route)
|
||||
fee-in-crypto-formatted (utils/get-standard-crypto-format
|
||||
token-for-fees
|
||||
fee-in-native-token)
|
||||
fee-in-fiat (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance fee-in-native-token
|
||||
:token token-for-fees})
|
||||
fee-formatted (utils/get-standard-fiat-format
|
||||
fee-in-crypto-formatted
|
||||
currency-symbol
|
||||
fee-in-fiat)]
|
||||
fee-formatted))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/has-partially-operable-accounts?
|
||||
|
||||
@@ -133,8 +133,7 @@
|
||||
max-fees-fiat (wallet-utils/calculate-token-fiat-value {:currency currency
|
||||
:balance max-fees-ether
|
||||
:token eth-token})
|
||||
max-fees-fiat-formatted (-> max-fees-ether
|
||||
(wallet-utils/get-standard-crypto-format eth-token)
|
||||
max-fees-fiat-formatted (-> (wallet-utils/get-standard-crypto-format eth-token max-fees-ether)
|
||||
(wallet-utils/get-standard-fiat-format currency-symbol
|
||||
max-fees-fiat))
|
||||
balance (-> eth-token
|
||||
|
||||
@@ -13,17 +13,20 @@
|
||||
{:before #(reset! rf-db/app-db {})})
|
||||
|
||||
(def ^:private accounts-with-tokens
|
||||
{:0x1 {:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}]
|
||||
{:0x1 {:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}]
|
||||
:network-preferences-names #{}
|
||||
:customization-color nil
|
||||
:operable? true
|
||||
:operable :fully
|
||||
:address "0x1"}
|
||||
:0x2 {:tokens [{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "200"}}}]
|
||||
:0x2 {:tokens [{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "200"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}]
|
||||
:network-preferences-names #{}
|
||||
:customization-color nil
|
||||
:operable? true
|
||||
@@ -501,10 +504,12 @@
|
||||
(assoc-in [:wallet :ui :send :token-symbol] "ETH")))
|
||||
(let [result (rf/sub [sub-name])]
|
||||
(is (match? result
|
||||
[{:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}]
|
||||
[{:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}]
|
||||
:network-preferences-names #{}
|
||||
:customization-color nil
|
||||
:operable? true
|
||||
@@ -518,10 +523,12 @@
|
||||
(assoc-in [:wallet :ui :send :token] {:symbol "ETH"})))
|
||||
(let [result (rf/sub [sub-name])]
|
||||
(is (match? result
|
||||
[{:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}}]
|
||||
[{:tokens [{:symbol "ETH"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}
|
||||
{:symbol "SNT"
|
||||
:balances-per-chain {1 {:raw-balance "100"}}
|
||||
:market-values-per-currency {:usd {:price 10000}}}]
|
||||
:network-preferences-names #{}
|
||||
:customization-color nil
|
||||
:operable? true
|
||||
@@ -959,15 +966,14 @@
|
||||
(testing "wallet send fee calculated and formatted in fiat"
|
||||
(swap! rf-db/app-db
|
||||
#(-> %
|
||||
(assoc-in [:wallet :accounts] accounts-with-tokens)
|
||||
(assoc-in [:wallet :current-viewing-account-address] "0x1")
|
||||
(assoc-in [:wallet :ui :send :route] route-data)
|
||||
(assoc-in [:profile/profile :currency] :usd)
|
||||
(assoc-in [:profile/profile :currency-symbol] "$")))
|
||||
|
||||
(let [token-for-fees {:decimals 18
|
||||
:symbol "ETH"
|
||||
:name "Ether"
|
||||
:market-values-per-currency {:usd {:price 10000}}}
|
||||
result (rf/sub [sub-name token-for-fees])]
|
||||
(let [token-symbol-for-fees "ETH"
|
||||
result (rf/sub [sub-name token-symbol-for-fees])]
|
||||
(is (match? result "$1.00")))))
|
||||
|
||||
(h/deftest-sub :wallet/has-partially-operable-accounts?
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
prefer to use it for more general purpose concepts, such as the re-frame event
|
||||
layer."
|
||||
(:require-macros test-helpers.unit)
|
||||
;; We must require test-helpers.matchers namespace to register the custom cljs.test directive
|
||||
;; `match-strict?`
|
||||
(:require
|
||||
[re-frame.core :as rf]
|
||||
[re-frame.db :as rf-db]
|
||||
|
||||
@@ -55,8 +55,9 @@
|
||||
(.lessThan ^js bn1 bn2))
|
||||
|
||||
(defn equal-to
|
||||
[bn1 bn2]
|
||||
(.eq ^js bn1 bn2))
|
||||
[n1 n2]
|
||||
(when-let [^js bn1 (bignumber n1)]
|
||||
(.eq ^js bn1 n2)))
|
||||
|
||||
(extend-type BigNumber
|
||||
IEquiv
|
||||
@@ -262,6 +263,12 @@
|
||||
[bn1 bn2]
|
||||
(.round (.dividedBy ^js bn1 bn2) 0))
|
||||
|
||||
(defn fiat->crypto
|
||||
[crypto fiat-price]
|
||||
(when-let [crypto-bn (bignumber crypto)]
|
||||
(div crypto-bn
|
||||
(bignumber fiat-price))))
|
||||
|
||||
(defn absolute-value
|
||||
[bn]
|
||||
(when bn
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "test/set-api-proxy-test-debug",
|
||||
"commit-sha1": "022b6fd45b99819ec14baabd6d482ae85ed468bf",
|
||||
"src-sha256": "03p7x3s9005ghi02x334077nxwdhwlkxmvhcnc07lsygfsnzkkzk"
|
||||
"version": "v0.184.47",
|
||||
"commit-sha1": "83aa01c7daf69d518e624fc584ea22ca27b34b73",
|
||||
"src-sha256": "07gqijx652ap0lf0kvcpd0lxx5x58air19pxgjawwymq2b8kly16"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from json import JSONDecodeError
|
||||
from os import environ
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
import requests
|
||||
from selenium.common import TimeoutException
|
||||
|
||||
@@ -38,7 +40,7 @@ class NetworkApi:
|
||||
return self.send_etherscan_request(params)
|
||||
|
||||
def get_transactions(self, address: str) -> List[dict]:
|
||||
params = {'module': 'account', 'action': 'txlist', 'address': address, 'sort': 'desc'}
|
||||
params = {'module': 'account', 'action': 'txlist', 'address': address, 'page': 1, 'offset': 10, 'sort': 'desc'}
|
||||
return self.send_etherscan_request(params)
|
||||
|
||||
def is_transaction_successful(self, transaction_hash: str) -> int:
|
||||
@@ -106,22 +108,28 @@ class NetworkApi:
|
||||
self.log("Failed iterate transactions(Etherscan unexpected error): " + str(e))
|
||||
continue
|
||||
|
||||
def wait_for_confirmation_of_transaction(self, address, amount, confirmations=6, token=False):
|
||||
def wait_for_confirmation_of_transaction(self, address, tx_time, confirmations=7, token=False):
|
||||
expected_tx_timestamp = datetime.strptime(tx_time, "%Y-%m-%dT%H:%M:%S%z").replace(tzinfo=pytz.UTC)
|
||||
start_time = time.time()
|
||||
if token:
|
||||
token_info = "token transaction"
|
||||
else:
|
||||
token_info = "ETH transaction"
|
||||
self.log('Waiting %s %s for %s to have %s confirmations' % (amount, token_info, address, confirmations))
|
||||
self.log('Waiting for %s of %s to have %s confirmations' % (token_info, address, confirmations))
|
||||
while round(time.time() - start_time, ndigits=2) < 600: # should be < idleTimeout capability
|
||||
transaction = self.find_transaction_by_unique_amount(address, amount, token)
|
||||
if token:
|
||||
transaction = self.get_token_transactions(address)[0]
|
||||
else:
|
||||
transaction = self.get_transactions(address)[0]
|
||||
tx_timestamp = datetime.fromtimestamp(int(transaction['timeStamp'])).replace(tzinfo=pytz.UTC)
|
||||
if tx_timestamp > expected_tx_timestamp:
|
||||
if int(transaction['confirmations']) >= confirmations:
|
||||
return
|
||||
time.sleep(20)
|
||||
self.log(
|
||||
'Expected amount of confirmations is %s, in fact %s' % (confirmations, transaction['confirmations']))
|
||||
if int(transaction['confirmations']) >= confirmations:
|
||||
return
|
||||
time.sleep(20)
|
||||
pytest.fail('Transaction with amount %s was not confirmed, address is %s, still has %s confirmations' % (
|
||||
amount, address, int(transaction['confirmations'])))
|
||||
pytest.fail('The last transaction was not confirmed, address is %s, still has %s confirmations' % (
|
||||
address, int(transaction['confirmations'])))
|
||||
|
||||
def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=360):
|
||||
counter = 0
|
||||
@@ -142,7 +150,7 @@ class NetworkApi:
|
||||
balance = round(self.get_balance(address), 4)
|
||||
if balance == expected_balance:
|
||||
return
|
||||
time.sleep(10)
|
||||
time.sleep(20)
|
||||
raise TimeoutException(
|
||||
'balance is not updated on Etherscan, it is %s but expected to be %s' % (balance, expected_balance))
|
||||
|
||||
|
||||
@@ -264,15 +264,15 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
expected_communities = {
|
||||
# ' 0xUX': ['Design', 'Ethereum', 'Collaboration'],
|
||||
'Status': ['Web3', 'Blockchain', 'Ethereum'],
|
||||
'Status Inu': ['News', 'Social', 'Web3'],
|
||||
# 'Status Inu': ['News', 'Social', 'Web3'],
|
||||
}
|
||||
for community_name, tags in expected_communities.items():
|
||||
self.home.just_fyi("Check %s community tags in the Discover communities screen" % community_name)
|
||||
card = self.home.get_discover_community_card_by_name(community_name=community_name)
|
||||
try:
|
||||
card.wait_for_visibility_of_element(30)
|
||||
if community_name == 'Status':
|
||||
card.swipe_to_web_element()
|
||||
# if community_name == 'Status':
|
||||
# card.swipe_to_web_element()
|
||||
missing_tags = list()
|
||||
for text in tags:
|
||||
try:
|
||||
@@ -281,18 +281,17 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
missing_tags.append(text)
|
||||
if missing_tags:
|
||||
self.errors.append("Community '%s' is missing tag(s) %s." % (community_name, ','.join(tags)))
|
||||
# if community_name == 'Status':
|
||||
self.home.just_fyi("Check Status community screen")
|
||||
card.click()
|
||||
if self.community_view.join_button.is_element_differs_from_template(
|
||||
'status_community_join_button.png'):
|
||||
self.errors.append("Status community Join button is different from expected template.")
|
||||
if self.community_view.community_logo.is_element_differs_from_template('status_community_logo.png'):
|
||||
self.errors.append("Status community logo is different from expected template.")
|
||||
|
||||
if community_name == 'Status':
|
||||
self.home.just_fyi("Check Status community screen")
|
||||
card.click()
|
||||
if self.community_view.join_button.is_element_differs_from_template(
|
||||
'status_community_join_button.png'):
|
||||
self.errors.append("Status community Join button is different from expected template.")
|
||||
if self.community_view.community_logo.is_element_differs_from_template('status_community_logo.png'):
|
||||
self.errors.append("Status community logo is different from expected template.")
|
||||
|
||||
self.community_view.close_community_view_button.click()
|
||||
self.home.swipe_up()
|
||||
# self.community_view.close_community_view_button.click()
|
||||
# self.home.swipe_up()
|
||||
|
||||
except TimeoutException:
|
||||
self.errors.append("Community '%s' is not in the Discover Communities list." % community_name)
|
||||
|
||||
@@ -17,13 +17,14 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
|
||||
|
||||
self.home = self.sign_in.create_user(username=self.username)
|
||||
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
|
||||
self.community_name = "open community"
|
||||
self.open_community_name = "open community"
|
||||
self.channel_name = "general"
|
||||
self.community = self.home.create_community(community_type="open")
|
||||
self.profile_view = self.home.get_profile_view()
|
||||
self.browser_view = self.home.get_dapp_view()
|
||||
self.home.get_chat(self.community_name, community=True).click()
|
||||
self.home.get_chat(self.open_community_name, community=True).click()
|
||||
self.community_view = self.home.get_community_view()
|
||||
self.open_community_url = self.community_view.copy_community_link()
|
||||
self.channel = self.community_view.get_channel(self.channel_name).click()
|
||||
|
||||
@marks.testrail_id(704613)
|
||||
@@ -39,7 +40,6 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
|
||||
self.channel.send_message(url)
|
||||
self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
|
||||
if self.channel.profile_send_contact_request_button.is_element_displayed(10):
|
||||
# username_text = self.profile_view.default_username_text.text
|
||||
username_text = self.profile_view.contact_name_text.text
|
||||
if not (username_text.endswith(url[-6:]) or username_text == text):
|
||||
self.errors.append("Incorrect username is shown for profile url %s" % url)
|
||||
@@ -47,34 +47,10 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
|
||||
self.errors.append("Profile was not opened by the profile url %s" % url)
|
||||
self.profile_view.close_button.click()
|
||||
|
||||
community_urls = {
|
||||
"https://status.app/c/ixyACjgKDVNOVCBjb21tdW5pdHkSHHJlcXVpcmUgMTAgU05UIEdvZXJsaSB0byB1c2UYASIHI2VhYjcwMAM=#zQ3shUeGnhM33QW4g9JfYfeLFAH9ZwbDboNYn5exCR7S3ii1y":
|
||||
"SNT community",
|
||||
"https://status.app/c/G0UAAMTyNsn2QZDEG0EXftOl8pOEfwEBOOSA_YTfIk85xmADDgINGmxpUHAXzK36bN0fK42Xf4YD2yjPk1z2pbFwFw==#zQ3sheoNX5kiuM393TJ6xDnL57aQoiwFWEuJnazJ6W2eNuh9u":
|
||||
"open community",
|
||||
"https://status.app/c/G00AAGS9TbI9mSR-ZNmFrhRjNuEeXAAbcAIUaLLJyjMOG3ACJQ12oIHD78QhzO9s_T5bUeU7rnATWJg3mGgTUemrAg==#zQ3shp9f5M3uyMpwTi3rFpFrP6WCWmNsW9pgK9cjXVTaf2vgj":
|
||||
"closed community"
|
||||
}
|
||||
for url, text in community_urls.items():
|
||||
self.channel.just_fyi("Opening community '%s' by the url %s" % (text, url))
|
||||
self.channel.chat_message_input.clear()
|
||||
self.channel.send_message(url)
|
||||
self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
|
||||
if text == 'SNT community':
|
||||
if self.community_view.community_title.text != text:
|
||||
self.errors.append("Community '%s' was not requested to join by the url %s" % (text, url))
|
||||
else:
|
||||
if not self.community_view.join_button.is_element_displayed(
|
||||
10) or self.community_view.community_title.text != text:
|
||||
self.errors.append("Community '%s' was not requested to join by the url %s" % (text, url))
|
||||
if text != "closed community": # the last one
|
||||
self.home.navigate_back_to_home_view()
|
||||
self.home.get_to_community_channel_from_home(self.community_name)
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(702775)
|
||||
def test_links_deep_links(self):
|
||||
def test_links_deep_links_profile(self):
|
||||
self.home.navigate_back_to_home_view()
|
||||
self.home.browser_tab.click()
|
||||
|
||||
@@ -87,7 +63,6 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
|
||||
for link, text in profile_links.items():
|
||||
self.channel.just_fyi("Opening profile link %s" % link)
|
||||
self.browser_view.open_url(link)
|
||||
# shown_name_text = self.profile_view.default_username_text.text
|
||||
shown_name_text = self.profile_view.contact_name_text.text
|
||||
if text:
|
||||
name_is_shown = shown_name_text == text or shown_name_text.endswith(link[-6:])
|
||||
@@ -97,25 +72,43 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
|
||||
self.errors.append("Profile was not opened by the profile deep link %s" % link)
|
||||
self.browser_view.click_system_back_button()
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(739307)
|
||||
def test_deep_links_communities(self):
|
||||
closed_community_name, snt_community_name = "closed community", "SNT community"
|
||||
self.home.navigate_back_to_home_view()
|
||||
self.home.communities_tab.click()
|
||||
self.home.create_community(community_type="closed")
|
||||
if not self.community_view.community_options_button.is_element_displayed():
|
||||
self.home.get_chat(closed_community_name, community=True).click()
|
||||
closed_community_url = self.community_view.copy_community_link()
|
||||
self.home.navigate_back_to_home_view()
|
||||
self.home.create_community(community_type="token-gated")
|
||||
if not self.community_view.community_options_button.is_element_displayed():
|
||||
self.home.get_chat(snt_community_name, community=True).click()
|
||||
snt_community_url = self.community_view.copy_community_link()
|
||||
self.home.reopen_app(sign_in=False)
|
||||
self.sign_in.create_user(username="second user", first_user=False)
|
||||
self.home.browser_tab.click()
|
||||
|
||||
old, new = "https://status.app/", "status-app://"
|
||||
community_links = {
|
||||
"status.app://c/ixyACjgKDVNOVCBjb21tdW5pdHkSHHJlcXVpcmUgMTAgU05UIEdvZXJsaSB0byB1c2UYASIHI2VhYjcwMAM=#zQ3shUeGnhM33QW4g9JfYfeLFAH9ZwbDboNYn5exCR7S3ii1y":
|
||||
"SNT community",
|
||||
"status.app://c/G0UAAMTyNsn2QZDEG0EXftOl8pOEfwEBOOSA_YTfIk85xmADDgINGmxpUHAXzK36bN0fK42Xf4YD2yjPk1z2pbFwFw==#zQ3sheoNX5kiuM393TJ6xDnL57aQoiwFWEuJnazJ6W2eNuh9u":
|
||||
"open community",
|
||||
"status.app://c/G00AAGS9TbI9mSR-ZNmFrhRjNuEeXAAbcAIUaLLJyjMOG3ACJQ12oIHD78QhzO9s_T5bUeU7rnATWJg3mGgTUemrAg==#zQ3shp9f5M3uyMpwTi3rFpFrP6WCWmNsW9pgK9cjXVTaf2vgj":
|
||||
"closed community"
|
||||
snt_community_name: snt_community_url.replace(old, new),
|
||||
self.open_community_name: self.open_community_url.replace(old, new),
|
||||
closed_community_name: closed_community_url.replace(old, new)
|
||||
}
|
||||
for link, text in community_links.items():
|
||||
for text, link in community_links.items():
|
||||
self.channel.just_fyi("Opening community '%s' by the link %s" % (text, link))
|
||||
self.browser_view.open_url(link)
|
||||
if text == 'SNT community':
|
||||
if text == snt_community_name:
|
||||
if self.community_view.community_title.text != text:
|
||||
self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link))
|
||||
else:
|
||||
if not self.community_view.join_button.is_element_displayed(
|
||||
10) or self.community_view.community_title.text != text:
|
||||
self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link))
|
||||
if text != "closed community": # the last one
|
||||
if text != closed_community_name: # the last one
|
||||
self.home.navigate_back_to_home_view()
|
||||
self.home.browser_tab.click()
|
||||
|
||||
|
||||
@@ -135,11 +135,14 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
|
||||
self.wallet_1.just_fyi("Sending funds from wallet")
|
||||
amount_to_send = 0.0001
|
||||
device_time_before_sending = self.wallet_1.driver.device_time
|
||||
self.wallet_1.send_asset(address='arb1:' + self.receiver['wallet_address'],
|
||||
asset_name='Ether',
|
||||
amount=amount_to_send)
|
||||
self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
|
||||
tx_time=device_time_before_sending)
|
||||
|
||||
device_time = self.wallet_1.driver.device_time
|
||||
device_time_after_sending = self.wallet_1.driver.device_time
|
||||
|
||||
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
|
||||
eth_amount_receiver)
|
||||
@@ -164,10 +167,13 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
|
||||
self.wallet_1.just_fyi("Sending asset from drawer")
|
||||
amount_to_send = 0.0001
|
||||
device_time_before_sending = self.wallet_1.driver.device_time
|
||||
self.wallet_1.send_asset_from_drawer(address='arb1:' + self.receiver['wallet_address'],
|
||||
asset_name='Ether',
|
||||
amount=amount_to_send)
|
||||
device_time = self.wallet_1.driver.device_time
|
||||
self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
|
||||
tx_time=device_time_before_sending)
|
||||
device_time_after_sending = self.wallet_1.driver.device_time
|
||||
|
||||
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
|
||||
eth_amount_receiver)
|
||||
|
||||
@@ -372,7 +372,7 @@ class CommunityView(HomeView):
|
||||
self.community_create_a_channel_button = Button(self.driver, accessibility_id="community-create-channel")
|
||||
self.channel_name_edit_box = EditBox(self.driver, translation_id="name-your-channel-placeholder")
|
||||
self.channel_descripton = ChatView(self.driver).community_description_edit_box
|
||||
self.community_options_button = Button(self.driver, accessibility_id="community-menu-button")
|
||||
self.community_options_button = Button(self.driver, accessibility_id="community-options-for-community")
|
||||
self.view_members_button = Button(self.driver, accessibility_id="view-members")
|
||||
self.community_info_button = Button(self.driver, translation_id="community-info")
|
||||
self.invite_button = Button(self.driver, accessibility_id="community-invite-people")
|
||||
@@ -455,11 +455,10 @@ class CommunityView(HomeView):
|
||||
def copy_community_link(self):
|
||||
self.driver.info("Copy community link")
|
||||
self.community_options_button.click()
|
||||
self.community_info_button.click()
|
||||
self.element_starts_with_text('join.status.im/c/').click()
|
||||
community_link_text = self.element_starts_with_text('join.status.im/c/').text
|
||||
self.home_button.double_click()
|
||||
return 'https://%s' % community_link_text
|
||||
self.share_community_button.click()
|
||||
text = self.sharing_text_native.text
|
||||
self.click_system_back_button(times=2)
|
||||
return text
|
||||
|
||||
def handle_membership_request(self, username: str, approve=True):
|
||||
self.driver.info("Handling membership request of user '%s', approve='%s'" % (username, str(approve)))
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
@@ -290,8 +290,11 @@ class ProfileView(BaseView):
|
||||
self.advertise_device_button = Button(self.driver, accessibility_id="advertise-device")
|
||||
self.sync_all_button = Button(self.driver, translation_id="sync-all-devices")
|
||||
self.syncing_button = Button(self.driver, accessibility_id="icon, Syncing, label-component, icon")
|
||||
self.sync_plus_button = Button(self.driver,
|
||||
xpath="//*[@text='Syncing']/following-sibling::android.view.ViewGroup[1]")
|
||||
self.paired_devices_button = Button(self.driver,
|
||||
accessibility_id="icon, Paired devices, 0 devices, label-component, icon")
|
||||
self.sync_plus_button = Button(
|
||||
self.driver,
|
||||
xpath="//*[@text='Paired devices']/following-sibling::android.view.ViewGroup[@content-desc='icon']")
|
||||
|
||||
# Keycard
|
||||
self.keycard_button = Button(self.driver, accessibility_id="keycard-button")
|
||||
@@ -539,6 +542,7 @@ class ProfileView(BaseView):
|
||||
|
||||
def get_sync_code(self):
|
||||
self.syncing_button.scroll_and_click()
|
||||
self.paired_devices_button.click()
|
||||
self.sync_plus_button.click()
|
||||
for checkbox in Button(
|
||||
self.driver,
|
||||
|
||||
@@ -263,11 +263,15 @@ class SignInView(BaseView):
|
||||
# self.next_button.click()
|
||||
# self.identifiers_button.wait_and_click(30)
|
||||
if enable_notifications:
|
||||
self.enable_notifications_button.click_until_presence_of_element(self.start_button)
|
||||
if self.allow_button.is_element_displayed():
|
||||
self.allow_button.click()
|
||||
self.enable_notifications_button.wait_and_click()
|
||||
for _ in range(3):
|
||||
self.allow_button.click_if_shown(sec=10)
|
||||
self.enable_notifications_button.click_if_shown()
|
||||
if self.start_button.is_element_displayed():
|
||||
break
|
||||
else:
|
||||
self.maybe_later_button.click_until_presence_of_element(self.start_button)
|
||||
self.cancel_button.click_if_shown() # TODO: remove when issue 20806 is fixed
|
||||
self.start_button.click()
|
||||
self.chats_tab.wait_for_visibility_of_element(30)
|
||||
self.driver.info("## New multiaccount is created successfully!", device=False)
|
||||
@@ -297,6 +301,7 @@ class SignInView(BaseView):
|
||||
self.enable_notifications_button.click_until_presence_of_element(self.start_button)
|
||||
else:
|
||||
self.maybe_later_button.click_until_presence_of_element(self.start_button)
|
||||
self.cancel_button.click_if_shown() # TODO: remove when issue 20806 is fixed
|
||||
self.start_button.click()
|
||||
self.chats_tab.wait_for_visibility_of_element(30)
|
||||
self.driver.info("## Multiaccount is recovered successfully!", device=False)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from tests import common_password
|
||||
@@ -120,7 +122,12 @@ class WalletView(BaseView):
|
||||
self.login_button.click()
|
||||
|
||||
def confirm_transaction(self):
|
||||
self.confirm_button.click_until_presence_of_element(self.slide_button_track)
|
||||
self.confirm_button.click()
|
||||
for _ in range(3):
|
||||
if self.slide_button_track.is_element_displayed():
|
||||
break
|
||||
time.sleep(1)
|
||||
self.confirm_button.click()
|
||||
self.slide_and_confirm_with_password()
|
||||
|
||||
def set_amount(self, amount: float):
|
||||
@@ -160,7 +167,6 @@ class WalletView(BaseView):
|
||||
self.add_account_button.click()
|
||||
self.add_account_to_watch.click()
|
||||
self.address_to_watch_input.send_keys(address)
|
||||
self.account_has_activity_label.wait_for_visibility_of_element()
|
||||
self.add_account_continue_button.click()
|
||||
SignInView(self.driver).profile_title_input.send_keys(account_name)
|
||||
self.add_watched_address_button.click()
|
||||
|
||||
@@ -1038,6 +1038,7 @@
|
||||
"free": "↓ Free",
|
||||
"fri": "Fri",
|
||||
"from": "from",
|
||||
"from-all-profiles-on-device": "From all profiles on device",
|
||||
"from-capitalized": "From",
|
||||
"from-label": "From",
|
||||
"gas-amount-limit": "Gas amount limit",
|
||||
@@ -1478,6 +1479,7 @@
|
||||
"max-fees": "Max fees",
|
||||
"max-priority-fee": "Max priority fee",
|
||||
"max-slippage": "Max slippage",
|
||||
"max-token": "Max: {{number}} {{token-symbol}}",
|
||||
"maximum-fee": "Maximum fee",
|
||||
"maximum-fee-desc": "Maximum overall price for the transaction. If the current block base fee exceeds this, your transaction will be included in a following block with a lower base fee.",
|
||||
"may": "May",
|
||||
@@ -1569,6 +1571,8 @@
|
||||
"mobile-syncing-sheet-title": "Sync using mobile data?",
|
||||
"mon": "Mon",
|
||||
"more": "more",
|
||||
"more-details-in-privacy-policy-1": "For more details refer to our ",
|
||||
"more-details-in-privacy-policy-2": "Privacy Policy",
|
||||
"move-and-reset": "Move and Reset",
|
||||
"move-keystore-file": "Move keystore file",
|
||||
"move-keystore-file-to-keycard": "Move keystore file to keycard?",
|
||||
@@ -2070,6 +2074,7 @@
|
||||
"reveal-sync-code": "Reveal sync code",
|
||||
"review-bridge": "Review bridge",
|
||||
"review-send": "Review send",
|
||||
"review-swap": "Review swap",
|
||||
"revoke-access": "Revoke access",
|
||||
"ropsten-testnet": "Ropsten Testnet",
|
||||
"rpc-usage-copy": "Copy",
|
||||
@@ -2565,6 +2570,7 @@
|
||||
"updates-to-tos": "Updates to Terms of Use",
|
||||
"updates-to-tos-desc": "Before you continue, please review the Terms of Use and confirm you take full responsibility for how you use the app.",
|
||||
"url": "URL",
|
||||
"usage-data-shared-from-all-profiles": "Usage data will be shared from all profiles added to device. ",
|
||||
"usd-currency": "USD",
|
||||
"use-as-profile-picture": "Use as profile picture",
|
||||
"use-biometrics": "Use biometrics to fill in your password",
|
||||
|
||||
Reference in New Issue
Block a user