Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f93a27f18b | ||
|
|
425ef64901 | ||
|
|
0e4c1b3c17 | ||
|
|
1eb9e87383 | ||
|
|
61b0a2ecac | ||
|
|
78576f4aeb | ||
|
|
9ff70ed990 | ||
|
|
6d5ef17a31 | ||
|
|
504d92fd57 | ||
|
|
3ff6013127 | ||
|
|
fb13c3016d | ||
|
|
4c4ba97308 | ||
|
|
f992f4addd | ||
|
|
b4d27d287a | ||
|
|
fb58d7205e | ||
|
|
0208224741 | ||
|
|
b3a2f397f9 | ||
|
|
5915dd42bd | ||
|
|
a00e44d056 | ||
|
|
b4640dabb9 | ||
|
|
9c62e63035 | ||
|
|
fc72ba6a1e | ||
|
|
1919094b46 | ||
|
|
e7b92dcf25 | ||
|
|
4b3bc73239 | ||
|
|
947a1ef29c | ||
|
|
4edb14bb5f | ||
|
|
c6a7808049 | ||
|
|
be2d0feda3 | ||
|
|
c91b4339dc | ||
|
|
5a832871fe |
@@ -194,8 +194,3 @@ test/appium/tests/users.py
|
||||
|
||||
## git hooks
|
||||
lefthook.yml
|
||||
|
||||
## clj-kondo
|
||||
/.clj-kondo/taoensso/*
|
||||
/.clj-kondo/babashka/*
|
||||
/.clj-kondo/rewrite-clj/rewrite-clj/config.edn
|
||||
|
||||
@@ -305,7 +305,7 @@ endif
|
||||
|
||||
# Get all clojure files, including untracked, excluding removed
|
||||
define find_all_clojure_files
|
||||
$$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn)
|
||||
$$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -E '((\.clj-kondo\/status-im)|(src).*\.clj[sc]?$$)|((\.clj-kondo\/(status-im|config))|(src).*\.edn$$)|shadow-cljs\.edn')
|
||||
endef
|
||||
|
||||
lint: export TARGET := clojure
|
||||
@@ -335,26 +335,44 @@ shadow-server: export TARGET := clojure
|
||||
shadow-server:##@ Start shadow-cljs in server mode for watching
|
||||
yarn shadow-cljs server
|
||||
|
||||
test-watch: export TARGET := clojure
|
||||
test-watch: ##@ Watch tests and re-run no changes to cljs files
|
||||
yarn install
|
||||
nodemon --exec 'yarn shadow-cljs compile mocks && yarn shadow-cljs compile test && node --require ./test-resources/override.js target/test/test.js' -e cljs
|
||||
|
||||
test-watch-for-repl: export TARGET := clojure
|
||||
test-watch-for-repl: ##@ Watch tests and support REPL connections
|
||||
yarn install
|
||||
rm -f target/test/test.js
|
||||
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
|
||||
'yarn shadow-cljs compile mocks && yarn shadow-cljs watch test --verbose' \
|
||||
'until [ -f ./target/test/test.js ] ; do sleep 1 ; done ; node --require ./test-resources/override.js ./target/test/test.js --repl'
|
||||
|
||||
test: export TARGET := clojure
|
||||
test: ##@test Run tests once in NodeJS
|
||||
# Here we create the gyp bindings for nodejs
|
||||
yarn install
|
||||
_test-clojure: export TARGET := clojure
|
||||
_test-clojure: export WATCH ?= false
|
||||
_test-clojure:
|
||||
ifeq ($(WATCH), true)
|
||||
yarn install && \
|
||||
yarn shadow-cljs compile mocks && \
|
||||
nodemon --exec "yarn shadow-cljs compile test && node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO" -e cljs
|
||||
else
|
||||
yarn install && \
|
||||
yarn shadow-cljs compile mocks && \
|
||||
yarn shadow-cljs compile test && \
|
||||
node --require ./test-resources/override.js target/test/test.js
|
||||
node --require ./test-resources/override.js "$$SHADOW_OUTPUT_TO"
|
||||
endif
|
||||
|
||||
test: export SHADOW_OUTPUT_TO := target/test/test.js
|
||||
test: export SHADOW_NS_REGEXP := .*-test$$
|
||||
test: ##@test Run all Clojure tests
|
||||
test: _test-clojure
|
||||
|
||||
test-watch-for-repl: export SHADOW_OUTPUT_TO := target/test/test.js
|
||||
test-watch-for-repl: export SHADOW_NS_REGEXP := .*-test$$
|
||||
test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connections
|
||||
yarn install
|
||||
rm -f target/test/test.js
|
||||
yarn shadow-cljs compile mocks && \
|
||||
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
|
||||
'yarn shadow-cljs watch test --verbose' \
|
||||
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
|
||||
|
||||
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
|
||||
test-unit: export SHADOW_NS_REGEXP := ^(?!status-im\.integration-test).*-test$$
|
||||
test-unit: ##@test Run unit tests
|
||||
test-unit: _test-clojure
|
||||
|
||||
test-integration: export SHADOW_OUTPUT_TO := target/integration_test/test.js
|
||||
test-integration: export SHADOW_NS_REGEXP := ^status-im\.integration-test.*$$
|
||||
test-integration: ##@test Run integration tests
|
||||
test-integration: _test-clojure
|
||||
|
||||
android-test: jsbundle
|
||||
android-test: export TARGET := android
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ library 'status-jenkins-lib@v1.8.4'
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos && arm64 && nix-2.14 && xcode-14.3' }
|
||||
agent { label 'macos && arm64 && nix-2.14 && xcode-15.1' }
|
||||
|
||||
parameters {
|
||||
string(
|
||||
|
||||
+10
-2
@@ -52,16 +52,24 @@ pipeline {
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Tests') {
|
||||
stage('Unit Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
set -eo pipefail
|
||||
make test 2>&1 | tee -a ${LOG_FILE}
|
||||
make test-unit 2>&1 | tee -a ${LOG_FILE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Integration Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
set -eo pipefail
|
||||
make test-integration 2>&1 | tee -a ${LOG_FILE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Component Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
To run tests:
|
||||
|
||||
```
|
||||
make test
|
||||
make test
|
||||
```
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ To run tests:
|
||||
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
|
||||
|
||||
```
|
||||
make test-watch
|
||||
make test WATCH=true
|
||||
```
|
||||
|
||||
Developers can also manually change the shadow-cljs option `:ns-regex` to control which namespaces the test runner should pick.
|
||||
Developers can also manually change the shadow-cljs option `:ns-regex` to control which namespaces the test runner should pick.
|
||||
|
||||
## Testing with REPL
|
||||
|
||||
|
||||
+1
-1
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
|
||||
:submodules: true
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
|
||||
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
|
||||
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/** Uses an unbounded queue, but allows timeout of core threads
|
||||
* (modified case 2 in
|
||||
* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html ) */
|
||||
public class StatusThreadPoolExecutor {
|
||||
private static final int NUMBER_OF_CORES =
|
||||
Runtime.getRuntime().availableProcessors();
|
||||
private static final int THREADS_TO_CORES_RATIO = 100;
|
||||
private static final int KEEP_ALIVE_TIME = 1;
|
||||
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
|
||||
|
||||
private final BlockingQueue<Runnable> mQueue;
|
||||
private final ThreadPoolExecutor mThreadPool;
|
||||
|
||||
private StatusThreadPoolExecutor() {
|
||||
mQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
mThreadPool = new ThreadPoolExecutor(
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
KEEP_ALIVE_TIME,
|
||||
KEEP_ALIVE_TIME_UNIT,
|
||||
mQueue);
|
||||
|
||||
// Allow pool to drain
|
||||
mThreadPool.allowCoreThreadTimeOut(true);
|
||||
}
|
||||
|
||||
/** Pugh singleton */
|
||||
private static class Holder {
|
||||
private static StatusThreadPoolExecutor instance = new StatusThreadPoolExecutor();
|
||||
}
|
||||
|
||||
public static StatusThreadPoolExecutor getInstance() {
|
||||
return Holder.instance;
|
||||
}
|
||||
|
||||
public void execute(final Runnable r) {
|
||||
mThreadPool.execute(r);
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package im.status.ethereum.module
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Uses an unbounded queue but allows timeout of core threads
|
||||
* (modified case 2 in
|
||||
* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html )
|
||||
*/
|
||||
class StatusThreadPoolExecutor private constructor() {
|
||||
private val NUMBER_OF_CORES: Int = Runtime.getRuntime().availableProcessors()
|
||||
private val THREADS_TO_CORES_RATIO: Int = 100
|
||||
private val KEEP_ALIVE_TIME: Int = 1
|
||||
private val KEEP_ALIVE_TIME_UNIT: TimeUnit = TimeUnit.SECONDS
|
||||
|
||||
private val mQueue: BlockingQueue<Runnable> = LinkedBlockingQueue()
|
||||
private val mThreadPool: ThreadPoolExecutor
|
||||
|
||||
init {
|
||||
mThreadPool = ThreadPoolExecutor(
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
KEEP_ALIVE_TIME.toLong(),
|
||||
KEEP_ALIVE_TIME_UNIT,
|
||||
mQueue
|
||||
)
|
||||
|
||||
// Allow pool to drain
|
||||
mThreadPool.allowCoreThreadTimeOut(true)
|
||||
}
|
||||
|
||||
/** Singleton holder */
|
||||
private object Holder {
|
||||
val instance = StatusThreadPoolExecutor()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): StatusThreadPoolExecutor {
|
||||
return Holder.instance
|
||||
}
|
||||
}
|
||||
|
||||
fun execute(r: Runnable) {
|
||||
mThreadPool.execute(r)
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package android.util;
|
||||
|
||||
public class Log {
|
||||
public static int d(String tag, String msg) {
|
||||
System.out.println("DEBUG: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int i(String tag, String msg) {
|
||||
System.out.println("INFO: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int w(String tag, String msg) {
|
||||
System.out.println("WARN: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int e(String tag, String msg) {
|
||||
System.out.println("ERROR: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package android.util
|
||||
|
||||
object Log {
|
||||
@JvmStatic
|
||||
fun d(tag: String, msg: String): Int {
|
||||
println("DEBUG: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun i(tag: String, msg: String): Int {
|
||||
println("INFO: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun w(tag: String, msg: String): Int {
|
||||
println("WARN: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun e(tag: String, msg: String): Int {
|
||||
println("ERROR: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface AccountManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,234 +0,0 @@
|
||||
#import "AccountManager.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation AccountManager
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(createAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"createAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoCreateAccountAndLogin(request);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"restoreAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoRestoreAccountAndLogin(request);
|
||||
}
|
||||
|
||||
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config
|
||||
withKeyUID:(NSString *)keyUID {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
NSURL *absTestnetFolderName = [rootUrl URLByAppendingPathComponent:@"ethereum/testnet"];
|
||||
|
||||
if (![fileManager fileExistsAtPath:absTestnetFolderName.path])
|
||||
[fileManager createDirectoryAtPath:absTestnetFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
NSURL *flagFolderUrl = [rootUrl URLByAppendingPathComponent:@"ropsten_flag"];
|
||||
|
||||
if(![fileManager fileExistsAtPath:flagFolderUrl.path]){
|
||||
NSLog(@"remove lightchaindata");
|
||||
NSURL *absLightChainDataUrl = [absTestnetFolderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"];
|
||||
if([fileManager fileExistsAtPath:absLightChainDataUrl.path]) {
|
||||
[fileManager removeItemAtPath:absLightChainDataUrl.path
|
||||
error:nil];
|
||||
}
|
||||
[fileManager createDirectoryAtPath:flagFolderUrl.path
|
||||
withIntermediateDirectories:NO
|
||||
attributes:nil
|
||||
error:&error];
|
||||
}
|
||||
|
||||
NSLog(@"after remove lightchaindata");
|
||||
|
||||
NSString *keystore = @"keystore";
|
||||
NSURL *absTestnetKeystoreUrl = [absTestnetFolderName URLByAppendingPathComponent:keystore];
|
||||
NSURL *absKeystoreUrl = [rootUrl URLByAppendingPathComponent:keystore];
|
||||
if([fileManager fileExistsAtPath:absTestnetKeystoreUrl.path]){
|
||||
NSLog(@"copy keystore");
|
||||
[fileManager copyItemAtPath:absTestnetKeystoreUrl.path toPath:absKeystoreUrl.path error:nil];
|
||||
[fileManager removeItemAtPath:absTestnetKeystoreUrl.path error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"after lightChainData");
|
||||
|
||||
NSLog(@"preconfig: %@", config);
|
||||
NSData *configData = [config dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *configJSON = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
|
||||
NSString *relativeDataDir = [configJSON objectForKey:@"DataDir"];
|
||||
NSString *absDataDir = [rootUrl.path stringByAppendingString:relativeDataDir];
|
||||
NSURL *absDataDirUrl = [NSURL fileURLWithPath:absDataDir];
|
||||
NSString *keystoreDir = [@"/keystore/" stringByAppendingString:keyUID];
|
||||
[configJSON setValue:keystoreDir forKey:@"KeyStoreDir"];
|
||||
[configJSON setValue:@"" forKey:@"LogDir"];
|
||||
[configJSON setValue:@"geth.log" forKey:@"LogFile"];
|
||||
NSString *resultingConfig = [Utils jsonStringWithPrettyPrint:NO fromDictionary:configJSON];
|
||||
|
||||
NSLog(@"node config %@", resultingConfig);
|
||||
|
||||
if(![fileManager fileExistsAtPath:absDataDir]) {
|
||||
[fileManager createDirectoryAtPath:absDataDir
|
||||
withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"logUrlPath %@ rootDir %@", @"geth.log", rootUrl.path);
|
||||
NSURL *absLogUrl = [absDataDirUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
if(![fileManager fileExistsAtPath:absLogUrl.path]) {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
[dict setObject:[NSNumber numberWithInt:511] forKey:NSFilePosixPermissions];
|
||||
[fileManager createFileAtPath:absLogUrl.path contents:nil attributes:dict];
|
||||
}
|
||||
|
||||
return resultingConfig;
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
|
||||
config:(NSString *)config
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"PrepareDirAndUpdateConfig() method called");
|
||||
#endif
|
||||
NSString *updatedConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
callback(@[updatedConfig]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
config:(NSString *)config
|
||||
accountsData:(NSString *)accountsData) {
|
||||
#if DEBUG
|
||||
NSLog(@"SaveAccountAndLogin() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
NSString *keyUID = [Utils getKeyUID:multiaccountData];
|
||||
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
NSString *result = StatusgoSaveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
config:(NSString *)config
|
||||
accountsData:(NSString *)accountsData
|
||||
chatKey:(NSString *)chatKey) {
|
||||
#if DEBUG
|
||||
NSLog(@"SaveAccountAndLoginWithKeycard() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
NSString *keyUID = [Utils getKeyUID:multiaccountData];
|
||||
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
NSString *result = StatusgoSaveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(login:(NSString *)accountData
|
||||
password:(NSString *)password) {
|
||||
#if DEBUG
|
||||
NSLog(@"Login() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
[Utils migrateKeystore:accountData password:password];
|
||||
NSString *result = StatusgoLogin(accountData, password);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
chatKey:(NSString *)chatKey
|
||||
nodeConfigJSON:(NSString *)nodeConfigJSON) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginWithKeycard() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
[Utils migrateKeystore:accountData password:password];
|
||||
|
||||
NSString *result = StatusgoLoginWithKeycard(accountData, password, chatKey, nodeConfigJSON);
|
||||
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
configJSON:(NSString *)configJSON) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginWithConfig() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
[Utils migrateKeystore:accountData password:password];
|
||||
NSString *result = StatusgoLoginWithConfig(accountData, password, configJSON);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginAccount:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginAccount() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLoginAccount(request);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(verify:(NSString *)address
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"VerifyAccountPassword() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
NSURL *absKeystoreUrl = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
|
||||
NSString *result = StatusgoVerifyAccountPassword(absKeystoreUrl.path, address, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"VerifyDatabasePassword() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoVerifyDatabasePassword(keyUID, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"OpenAccounts() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSString *result = StatusgoOpenAccounts(rootUrl.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(logout) {
|
||||
#if DEBUG
|
||||
NSLog(@"Logout() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLogout();
|
||||
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface DatabaseManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,33 +0,0 @@
|
||||
#import "DatabaseManager.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation DatabaseManager
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(exportUnencryptedDatabase:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"exportUnencryptedDatabase() method called");
|
||||
#endif
|
||||
|
||||
NSString *filePath = [Utils getExportDbFilePath];
|
||||
StatusgoExportUnencryptedDatabase(accountData, password, filePath);
|
||||
|
||||
callback(@[filePath]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(importUnencryptedDatabase:(NSString *)accountData
|
||||
password:(NSString *)password) {
|
||||
#if DEBUG
|
||||
NSLog(@"importUnencryptedDatabase() method called");
|
||||
#endif
|
||||
"";
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface EncryptionUtils : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,241 +0,0 @@
|
||||
#import "EncryptionUtils.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation EncryptionUtils
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
#pragma mark - InitKeystore method
|
||||
|
||||
RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"initKeystore() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *keystoreDir = [commonKeystoreDir URLByAppendingPathComponent:keyUID];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^(void)
|
||||
{
|
||||
NSString *res = StatusgoInitKeystore(keystoreDir.path);
|
||||
NSLog(@"InitKeyStore result %@", res);
|
||||
callback(@[]);
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
|
||||
currentPassword:(NSString *)currentPassword
|
||||
newPassword:(NSString *)newPassword
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"reEncryptDbAndKeystore() method called");
|
||||
#endif
|
||||
// changes password and re-encrypts keystore
|
||||
NSString *result = StatusgoChangeDatabasePassword(keyUID, currentPassword, newPassword);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
|
||||
accountData:(NSString *)accountData
|
||||
settings:(NSString *)settings
|
||||
keycardUID:(NSString *)keycardUID
|
||||
currentPassword:(NSString *)currentPassword
|
||||
newPassword:(NSString *)newPassword
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"convertToKeycardAccount() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
StatusgoInitKeystore(multiaccountKeystoreDir.path);
|
||||
NSString *result = StatusgoConvertToKeycardAccount(accountData, settings, keycardUID, currentPassword, newPassword);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeTransfer:(NSString *)to
|
||||
value:(NSString *)value) {
|
||||
return StatusgoEncodeTransfer(to,value);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeFunctionCall:(NSString *)method
|
||||
paramsJSON:(NSString *)paramsJSON) {
|
||||
return StatusgoEncodeFunctionCall(method,paramsJSON);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(decodeParameters:(NSString *)decodeParamJSON) {
|
||||
return StatusgoDecodeParameters(decodeParamJSON);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToNumber:(NSString *)hex) {
|
||||
return StatusgoHexToNumber(hex);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(numberToHex:(NSString *)numString) {
|
||||
return StatusgoNumberToHex(numString);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(sha3:(NSString *)str) {
|
||||
return StatusgoSha3(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(utf8ToHex:(NSString *)str) {
|
||||
return StatusgoUtf8ToHex(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToUtf8:(NSString *)str) {
|
||||
return StatusgoHexToUtf8(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setBlankPreviewFlag:(BOOL *)newValue)
|
||||
{
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
[userDefaults setBool:newValue forKey:@"BLANK_PREVIEW"];
|
||||
|
||||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"HashTransaction() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTransaction(txArgsJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashMessage:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashMessage() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashMessage(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey
|
||||
base58btc:(NSString *)base58btc
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(localPairingPreflightOutboundCheck:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"LocalPairingPreflightOutboundCheck() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLocalPairingPreflightOutboundCheck();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey
|
||||
base58btc:(NSString *)base58btc
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoMultiformatDeserializePublicKey(multiCodecKey,base58btc);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoCompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDecompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDeserializeAndCompressKey(desktopKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashTypedData() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTypedData(data);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashTypedDataV4() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTypedDataV4(data);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SignMessage
|
||||
|
||||
RCT_EXPORT_METHOD(signMessage:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignMessage() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignMessage(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SignTypedData
|
||||
|
||||
RCT_EXPORT_METHOD(signTypedData:(NSString *)data
|
||||
account:(NSString *)account
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignTypedData() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignTypedData(data, account, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SignTypedDataV4
|
||||
|
||||
RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
|
||||
account:(NSString *)account
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignTypedDataV4() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignTypedDataV4(data, account, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - ExtractGroupMembershipSignatures
|
||||
|
||||
RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"ExtractGroupMembershipSignatures() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoExtractGroupMembershipSignatures(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SignGroupMembership
|
||||
|
||||
RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignGroupMembership() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignGroupMembership(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface LogManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,115 +0,0 @@
|
||||
#import "LogManager.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
#import "SSZipArchive.h"
|
||||
|
||||
@implementation LogManager
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
#pragma mark - SendLogs method
|
||||
|
||||
RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
|
||||
jsLogs:(NSString *)jsLogs
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
// TODO: Implement SendLogs for iOS
|
||||
#if DEBUG
|
||||
NSLog(@"SendLogs() method called, not implemented");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *zipFile = [rootUrl URLByAppendingPathComponent:@"logs.zip"];
|
||||
[fileManager removeItemAtPath:zipFile.path error:nil];
|
||||
|
||||
NSURL *logsFolderName = [rootUrl URLByAppendingPathComponent:@"logs"];
|
||||
|
||||
if (![fileManager fileExistsAtPath:logsFolderName.path])
|
||||
[fileManager createDirectoryAtPath:logsFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
NSURL *dbFile = [logsFolderName URLByAppendingPathComponent:@"db.json"];
|
||||
NSURL *jsLogsFile = [logsFolderName URLByAppendingPathComponent:@"Status.log"];
|
||||
#if DEBUG
|
||||
NSString *networkDirPath = @"ethereum/mainnet_rpc_dev";
|
||||
#else
|
||||
NSString *networkDirPath = @"ethereum/mainnet_rpc";
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc_dev";
|
||||
#else
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc";
|
||||
#endif
|
||||
|
||||
NSURL *networkDir = [rootUrl URLByAppendingPathComponent:networkDirPath];
|
||||
NSURL *originalGethLogsFile = [networkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *gethLogsFile = [logsFolderName URLByAppendingPathComponent:@"mainnet_geth.log"];
|
||||
|
||||
NSURL *goerliNetworkDir = [rootUrl URLByAppendingPathComponent:goerliNetworkDirPath];
|
||||
NSURL *goerliGethLogsFile = [goerliNetworkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *goerliLogsFile = [logsFolderName URLByAppendingPathComponent:@"goerli_geth.log"];
|
||||
|
||||
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
|
||||
|
||||
[dbJson writeToFile:dbFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[jsLogs writeToFile:jsLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
//NSString* gethLogs = StatusgoExportNodeLogs();
|
||||
//[gethLogs writeToFile:gethLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[fileManager copyItemAtPath:originalGethLogsFile.path toPath:gethLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:goerliGethLogsFile.path toPath:goerliLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:mainGethLogsFile.path toPath:mainLogsFile.path error:nil];
|
||||
|
||||
[SSZipArchive createZipFileAtPath:zipFile.path withContentsOfDirectory:logsFolderName.path];
|
||||
[fileManager removeItemAtPath:logsFolderName.path error:nil];
|
||||
|
||||
callback(@[zipFile.absoluteString]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(initLogging:(BOOL)enabled
|
||||
mobileSystem:(BOOL)mobileSystem
|
||||
logLevel:(NSString *)logLevel
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
NSString *logDirectory = [self logFileDirectory];
|
||||
NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"geth.log"];
|
||||
|
||||
NSMutableDictionary *jsonConfig = [NSMutableDictionary dictionary];
|
||||
jsonConfig[@"Enabled"] = @(enabled);
|
||||
jsonConfig[@"MobileSystem"] = @(mobileSystem);
|
||||
jsonConfig[@"Level"] = logLevel;
|
||||
jsonConfig[@"File"] = logFilePath;
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonConfig options:0 error:&error];
|
||||
|
||||
if (error) {
|
||||
// Handle JSON serialization error
|
||||
callback(@[error.localizedDescription]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *config = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
||||
// Call your native logging initialization method here
|
||||
NSString *initResult = StatusgoInitLogging(config);
|
||||
|
||||
callback(@[initResult]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(logFileDirectory) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface NetworkManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,118 +0,0 @@
|
||||
#import "NetworkManager.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation NetworkManager
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(addPeer:(NSString *)enode
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoAddPeer(enode);
|
||||
callback(@[result]);
|
||||
#if DEBUG
|
||||
NSLog(@"AddPeer() method called");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startSearchForLocalPairingPeers:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoStartSearchForLocalPairingPeers();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
||||
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error;
|
||||
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
|
||||
NSMutableDictionary *senderConfig = configDict[@"senderConfig"];
|
||||
NSString *keyUID = senderConfig[@"keyUID"];
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
NSString *keystoreDir = multiaccountKeystoreDir.path;
|
||||
|
||||
[senderConfig setValue:keystoreDir forKey:@"keystorePath"];
|
||||
NSString *modifiedConfigJSON = [Utils jsonStringWithPrettyPrint:NO fromDictionary:configDict];
|
||||
|
||||
NSString *result = StatusgoGetConnectionStringForBootstrappingAnotherDevice(modifiedConfigJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
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);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
|
||||
signature:(NSString *)signature
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"sendTransactionWithSignature() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSendTransactionWithSignature(txArgsJSON, signature);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SendTransaction
|
||||
|
||||
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SendTransaction() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSendTransaction(txArgsJSON, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(callRPC:(NSString *)payload
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSString *result = StatusgoCallRPC(payload);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(@[result]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSString *result = StatusgoCallPrivateRPC(payload);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(@[result]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Recover
|
||||
|
||||
RCT_EXPORT_METHOD(recover:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"Recover() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoRecover(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -2,8 +2,48 @@
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "SSZipArchive.h"
|
||||
|
||||
#import "Utils.h"
|
||||
@interface NSDictionary (BVJSONString)
|
||||
-(NSString*) bv_jsonStringWithPrettyPrint:(BOOL) prettyPrint;
|
||||
@end
|
||||
|
||||
@implementation NSDictionary (BVJSONString)
|
||||
|
||||
-(NSString*) bv_jsonStringWithPrettyPrint:(BOOL) prettyPrint {
|
||||
NSError *error;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self
|
||||
options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0)
|
||||
error:&error];
|
||||
|
||||
if (! jsonData) {
|
||||
NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
|
||||
return @"{}";
|
||||
} else {
|
||||
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface NSArray (BVJSONString)
|
||||
- (NSString *)bv_jsonStringWithPrettyPrint:(BOOL)prettyPrint;
|
||||
@end
|
||||
|
||||
@implementation NSArray (BVJSONString)
|
||||
-(NSString*) bv_jsonStringWithPrettyPrint:(BOOL) prettyPrint {
|
||||
NSError *error;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self
|
||||
options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0)
|
||||
error:&error];
|
||||
|
||||
if (! jsonData) {
|
||||
NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
|
||||
return @"[]";
|
||||
} else {
|
||||
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
static RCTBridge *bridge;
|
||||
|
||||
@@ -56,6 +96,7 @@ RCT_EXPORT_METHOD(shouldMoveToInternalStorage:(RCTResponseSenderBlock)onResultCa
|
||||
onResultCallback(@[[NSNull null]]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - moveToInternalStorage
|
||||
|
||||
RCT_EXPORT_METHOD(moveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
|
||||
@@ -63,6 +104,96 @@ RCT_EXPORT_METHOD(moveToInternalStorage:(RCTResponseSenderBlock)onResultCallback
|
||||
onResultCallback(@[[NSNull null]]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - InitKeystore method
|
||||
|
||||
RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"initKeystore() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *keystoreDir = [commonKeystoreDir URLByAppendingPathComponent:keyUID];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^(void)
|
||||
{
|
||||
NSString *res = StatusgoInitKeystore(keystoreDir.path);
|
||||
NSLog(@"InitKeyStore result %@", res);
|
||||
callback(@[]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SendLogs method
|
||||
|
||||
RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
|
||||
jsLogs:(NSString *)jsLogs
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
// TODO: Implement SendLogs for iOS
|
||||
#if DEBUG
|
||||
NSLog(@"SendLogs() method called, not implemented");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *zipFile = [rootUrl URLByAppendingPathComponent:@"logs.zip"];
|
||||
[fileManager removeItemAtPath:zipFile.path error:nil];
|
||||
|
||||
NSURL *logsFolderName = [rootUrl URLByAppendingPathComponent:@"logs"];
|
||||
|
||||
if (![fileManager fileExistsAtPath:logsFolderName.path])
|
||||
[fileManager createDirectoryAtPath:logsFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
NSURL *dbFile = [logsFolderName URLByAppendingPathComponent:@"db.json"];
|
||||
NSURL *jsLogsFile = [logsFolderName URLByAppendingPathComponent:@"Status.log"];
|
||||
#if DEBUG
|
||||
NSString *networkDirPath = @"ethereum/mainnet_rpc_dev";
|
||||
#else
|
||||
NSString *networkDirPath = @"ethereum/mainnet_rpc";
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc_dev";
|
||||
#else
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc";
|
||||
#endif
|
||||
|
||||
NSURL *networkDir = [rootUrl URLByAppendingPathComponent:networkDirPath];
|
||||
NSURL *originalGethLogsFile = [networkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *gethLogsFile = [logsFolderName URLByAppendingPathComponent:@"mainnet_geth.log"];
|
||||
|
||||
NSURL *goerliNetworkDir = [rootUrl URLByAppendingPathComponent:goerliNetworkDirPath];
|
||||
NSURL *goerliGethLogsFile = [goerliNetworkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *goerliLogsFile = [logsFolderName URLByAppendingPathComponent:@"goerli_geth.log"];
|
||||
|
||||
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
|
||||
|
||||
[dbJson writeToFile:dbFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[jsLogs writeToFile:jsLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
//NSString* gethLogs = StatusgoExportNodeLogs();
|
||||
//[gethLogs writeToFile:gethLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[fileManager copyItemAtPath:originalGethLogsFile.path toPath:gethLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:goerliGethLogsFile.path toPath:goerliLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:mainGethLogsFile.path toPath:mainLogsFile.path error:nil];
|
||||
|
||||
[SSZipArchive createZipFileAtPath:zipFile.path withContentsOfDirectory:logsFolderName.path];
|
||||
[fileManager removeItemAtPath:logsFolderName.path error:nil];
|
||||
|
||||
callback(@[zipFile.absoluteString]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"exportLogs() method called");
|
||||
@@ -71,12 +202,21 @@ RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addPeer:(NSString *)enode
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoAddPeer(enode);
|
||||
callback(@[result]);
|
||||
#if DEBUG
|
||||
NSLog(@"AddPeer() method called");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"DeleteMultiaccount() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
NSString *result = StatusgoDeleteMultiaccount(keyUID, multiaccountKeystoreDir.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
@@ -88,7 +228,7 @@ RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
|
||||
#if DEBUG
|
||||
NSLog(@"DeleteImportedKey() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
NSString *result = StatusgoDeleteImportedKey(address, password, multiaccountKeystoreDir.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
@@ -146,6 +286,137 @@ RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"HashTransaction() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTransaction(txArgsJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashMessage:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashMessage() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashMessage(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(localPairingPreflightOutboundCheck:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"LocalPairingPreflightOutboundCheck() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLocalPairingPreflightOutboundCheck();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startSearchForLocalPairingPeers:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoStartSearchForLocalPairingPeers();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
||||
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error;
|
||||
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
|
||||
NSMutableDictionary *senderConfig = configDict[@"senderConfig"];
|
||||
NSString *keyUID = senderConfig[@"keyUID"];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
NSString *keystoreDir = multiaccountKeystoreDir.path;
|
||||
|
||||
[senderConfig setValue:keystoreDir forKey:@"keystorePath"];
|
||||
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
|
||||
|
||||
NSString *result = StatusgoGetConnectionStringForBootstrappingAnotherDevice(modifiedConfigJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
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 = [configDict bv_jsonStringWithPrettyPrint:NO];
|
||||
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, modifiedConfigJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey
|
||||
base58btc:(NSString *)base58btc
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey
|
||||
base58btc:(NSString *)base58btc
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoMultiformatDeserializePublicKey(multiCodecKey,base58btc);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDecompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoCompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDeserializeAndCompressKey(desktopKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashTypedData() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTypedData(data);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"hashTypedDataV4() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoHashTypedDataV4(data);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
|
||||
signature:(NSString *)signature
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"sendTransactionWithSignature() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSendTransactionWithSignature(txArgsJSON, signature);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
@@ -164,6 +435,388 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
-(NSString *) getKeyUID:(NSString *)jsonString {
|
||||
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *json = [NSJSONSerialization
|
||||
JSONObjectWithData:data
|
||||
options:NSJSONReadingMutableContainers
|
||||
error:nil];
|
||||
|
||||
return [json valueForKey:@"key-uid"];
|
||||
}
|
||||
|
||||
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config
|
||||
withKeyUID:(NSString *)keyUID {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
NSURL *absTestnetFolderName = [rootUrl URLByAppendingPathComponent:@"ethereum/testnet"];
|
||||
|
||||
if (![fileManager fileExistsAtPath:absTestnetFolderName.path])
|
||||
[fileManager createDirectoryAtPath:absTestnetFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
NSURL *flagFolderUrl = [rootUrl URLByAppendingPathComponent:@"ropsten_flag"];
|
||||
|
||||
if(![fileManager fileExistsAtPath:flagFolderUrl.path]){
|
||||
NSLog(@"remove lightchaindata");
|
||||
NSURL *absLightChainDataUrl = [absTestnetFolderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"];
|
||||
if([fileManager fileExistsAtPath:absLightChainDataUrl.path]) {
|
||||
[fileManager removeItemAtPath:absLightChainDataUrl.path
|
||||
error:nil];
|
||||
}
|
||||
[fileManager createDirectoryAtPath:flagFolderUrl.path
|
||||
withIntermediateDirectories:NO
|
||||
attributes:nil
|
||||
error:&error];
|
||||
}
|
||||
|
||||
NSLog(@"after remove lightchaindata");
|
||||
|
||||
NSString *keystore = @"keystore";
|
||||
NSURL *absTestnetKeystoreUrl = [absTestnetFolderName URLByAppendingPathComponent:keystore];
|
||||
NSURL *absKeystoreUrl = [rootUrl URLByAppendingPathComponent:keystore];
|
||||
if([fileManager fileExistsAtPath:absTestnetKeystoreUrl.path]){
|
||||
NSLog(@"copy keystore");
|
||||
[fileManager copyItemAtPath:absTestnetKeystoreUrl.path toPath:absKeystoreUrl.path error:nil];
|
||||
[fileManager removeItemAtPath:absTestnetKeystoreUrl.path error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"after lightChainData");
|
||||
|
||||
NSLog(@"preconfig: %@", config);
|
||||
NSData *configData = [config dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *configJSON = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
|
||||
NSString *relativeDataDir = [configJSON objectForKey:@"DataDir"];
|
||||
NSString *absDataDir = [rootUrl.path stringByAppendingString:relativeDataDir];
|
||||
NSURL *absDataDirUrl = [NSURL fileURLWithPath:absDataDir];
|
||||
NSString *keystoreDir = [@"/keystore/" stringByAppendingString:keyUID];
|
||||
[configJSON setValue:keystoreDir forKey:@"KeyStoreDir"];
|
||||
[configJSON setValue:@"" forKey:@"LogDir"];
|
||||
[configJSON setValue:@"geth.log" forKey:@"LogFile"];
|
||||
|
||||
NSString *resultingConfig = [configJSON bv_jsonStringWithPrettyPrint:NO];
|
||||
NSLog(@"node config %@", resultingConfig);
|
||||
|
||||
if(![fileManager fileExistsAtPath:absDataDir]) {
|
||||
[fileManager createDirectoryAtPath:absDataDir
|
||||
withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"logUrlPath %@ rootDir %@", @"geth.log", rootUrl.path);
|
||||
NSURL *absLogUrl = [absDataDirUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
if(![fileManager fileExistsAtPath:absLogUrl.path]) {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
[dict setObject:[NSNumber numberWithInt:511] forKey:NSFilePosixPermissions];
|
||||
[fileManager createFileAtPath:absLogUrl.path contents:nil attributes:dict];
|
||||
}
|
||||
|
||||
return resultingConfig;
|
||||
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
|
||||
config:(NSString *)config
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"PrepareDirAndUpdateConfig() method called");
|
||||
#endif
|
||||
NSString *updatedConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
callback(@[updatedConfig]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
config:(NSString *)config
|
||||
accountsData:(NSString *)accountsData) {
|
||||
#if DEBUG
|
||||
NSLog(@"SaveAccountAndLogin() method called");
|
||||
#endif
|
||||
[self getExportDbFilePath];
|
||||
NSString *keyUID = [self getKeyUID:multiaccountData];
|
||||
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
NSString *result = StatusgoSaveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
config:(NSString *)config
|
||||
accountsData:(NSString *)accountsData
|
||||
chatKey:(NSString *)chatKey) {
|
||||
#if DEBUG
|
||||
NSLog(@"SaveAccountAndLoginWithKeycard() method called");
|
||||
#endif
|
||||
[self getExportDbFilePath];
|
||||
NSString *keyUID = [self getKeyUID:multiaccountData];
|
||||
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
NSString *result = StatusgoSaveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
- (NSString *) getExportDbFilePath {
|
||||
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"export.db"];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if ([fileManager fileExistsAtPath:filePath]) {
|
||||
[fileManager removeItemAtPath:filePath error:nil];
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (NSURL *) getKeyStoreDir:(NSString *)keyUID {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *multiaccountKeystoreDir = [oldKeystoreDir URLByAppendingPathComponent:keyUID];
|
||||
|
||||
return multiaccountKeystoreDir;
|
||||
}
|
||||
|
||||
- (void) migrateKeystore:(NSString *)accountData
|
||||
password:(NSString *)password {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSString *keyUID = [self getKeyUID:accountData];
|
||||
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
|
||||
NSArray *keys = [fileManager contentsOfDirectoryAtPath:multiaccountKeystoreDir.path error:nil];
|
||||
if (keys.count == 0) {
|
||||
NSString *migrationResult = StatusgoMigrateKeyStoreDir(accountData, password, oldKeystoreDir.path, multiaccountKeystoreDir.path);
|
||||
NSLog(@"keystore migration result %@", migrationResult);
|
||||
|
||||
NSString *initKeystoreResult = StatusgoInitKeystore(multiaccountKeystoreDir.path);
|
||||
NSLog(@"InitKeyStore result %@", initKeystoreResult);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(login:(NSString *)accountData
|
||||
password:(NSString *)password) {
|
||||
#if DEBUG
|
||||
NSLog(@"Login() method called");
|
||||
#endif
|
||||
[self getExportDbFilePath];
|
||||
[self migrateKeystore:accountData password:password];
|
||||
NSString *result = StatusgoLogin(accountData, password);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
configJSON:(NSString *)configJSON) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginWithConfig() method called");
|
||||
#endif
|
||||
[self getExportDbFilePath];
|
||||
[self migrateKeystore:accountData password:password];
|
||||
NSString *result = StatusgoLoginWithConfig(accountData, password, configJSON);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginAccount:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginAccount() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLoginAccount(request);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
chatKey:(NSString *)chatKey
|
||||
nodeConfigJSON:(NSString *)nodeConfigJSON) {
|
||||
#if DEBUG
|
||||
NSLog(@"LoginWithKeycard() method called");
|
||||
#endif
|
||||
[self getExportDbFilePath];
|
||||
[self migrateKeystore:accountData password:password];
|
||||
|
||||
NSString *result = StatusgoLoginWithKeycard(accountData, password, chatKey, nodeConfigJSON);
|
||||
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(logout) {
|
||||
#if DEBUG
|
||||
NSLog(@"Logout() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoLogout();
|
||||
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"OpenAccounts() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSString *result = StatusgoOpenAccounts(rootUrl.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(verify:(NSString *)address
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"VerifyAccountPassword() method called");
|
||||
#endif
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
NSURL *absKeystoreUrl = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
|
||||
NSString *result = StatusgoVerifyAccountPassword(absKeystoreUrl.path, address, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"VerifyDatabasePassword() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoVerifyDatabasePassword(keyUID, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
|
||||
currentPassword:(NSString *)currentPassword
|
||||
newPassword:(NSString *)newPassword
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"reEncryptDbAndKeystore() method called");
|
||||
#endif
|
||||
// changes password and re-encrypts keystore
|
||||
NSString *result = StatusgoChangeDatabasePassword(keyUID, currentPassword, newPassword);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
|
||||
accountData:(NSString *)accountData
|
||||
settings:(NSString *)settings
|
||||
keycardUID:(NSString *)keycardUID
|
||||
currentPassword:(NSString *)currentPassword
|
||||
newPassword:(NSString *)newPassword
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"convertToKeycardAccount() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
StatusgoInitKeystore(multiaccountKeystoreDir.path);
|
||||
NSString *result = StatusgoConvertToKeycardAccount(accountData, settings, keycardUID, currentPassword, newPassword);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SendTransaction
|
||||
|
||||
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SendTransaction() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSendTransaction(txArgsJSON, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SignMessage
|
||||
|
||||
RCT_EXPORT_METHOD(signMessage:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignMessage() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignMessage(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Recover
|
||||
|
||||
RCT_EXPORT_METHOD(recover:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"Recover() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoRecover(message);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SignTypedData
|
||||
|
||||
RCT_EXPORT_METHOD(signTypedData:(NSString *)data
|
||||
account:(NSString *)account
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignTypedData() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignTypedData(data, account, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SignTypedDataV4
|
||||
|
||||
RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
|
||||
account:(NSString *)account
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignTypedDataV4() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignTypedDataV4(data, account, password);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - SignGroupMembership
|
||||
|
||||
RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignGroupMembership() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignGroupMembership(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - ExtractGroupMembershipSignatures
|
||||
|
||||
RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"ExtractGroupMembershipSignatures() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoExtractGroupMembershipSignatures(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - GetNodeConfig
|
||||
|
||||
RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
|
||||
@@ -174,14 +827,206 @@ RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - only android methods
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustResize) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustResize() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustPan) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustPan() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) {
|
||||
#if DEBUG
|
||||
NSLog(@"setSoftInputMode() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clearCookies) {
|
||||
NSHTTPCookie *cookie;
|
||||
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
||||
|
||||
for (cookie in [storage cookies]) {
|
||||
[storage deleteCookie:cookie];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clearStorageAPIs) {
|
||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||
|
||||
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
|
||||
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
||||
for (NSString *string in array) {
|
||||
NSLog(@"Removing %@", [path stringByAppendingPathComponent:string]);
|
||||
if ([[string pathExtension] isEqualToString:@"localstorage"])
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[path stringByAppendingPathComponent:string] error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(callRPC:(NSString *)payload
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSString *result = StatusgoCallRPC(payload);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(@[result]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(keystoreDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
|
||||
return commonKeystoreDir.path;
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(backupDisabledDataDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(logFileDirectory) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(initLogging:(BOOL)enabled
|
||||
mobileSystem:(BOOL)mobileSystem
|
||||
logLevel:(NSString *)logLevel
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
NSString *logDirectory = [self logFileDirectory];
|
||||
NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"geth.log"];
|
||||
|
||||
NSMutableDictionary *jsonConfig = [NSMutableDictionary dictionary];
|
||||
jsonConfig[@"Enabled"] = @(enabled);
|
||||
jsonConfig[@"MobileSystem"] = @(mobileSystem);
|
||||
jsonConfig[@"Level"] = logLevel;
|
||||
jsonConfig[@"File"] = logFilePath;
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonConfig options:0 error:&error];
|
||||
|
||||
if (error) {
|
||||
// Handle JSON serialization error
|
||||
callback(@[error.localizedDescription]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *config = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
||||
// Call your native logging initialization method here
|
||||
NSString *initResult = StatusgoInitLogging(config);
|
||||
|
||||
callback(@[initResult]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeTransfer:(NSString *)to
|
||||
value:(NSString *)value) {
|
||||
return StatusgoEncodeTransfer(to,value);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeFunctionCall:(NSString *)method
|
||||
paramsJSON:(NSString *)paramsJSON) {
|
||||
return StatusgoEncodeFunctionCall(method,paramsJSON);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(decodeParameters:(NSString *)decodeParamJSON) {
|
||||
return StatusgoDecodeParameters(decodeParamJSON);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToNumber:(NSString *)hex) {
|
||||
return StatusgoHexToNumber(hex);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(numberToHex:(NSString *)numString) {
|
||||
return StatusgoNumberToHex(numString);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(sha3:(NSString *)str) {
|
||||
return StatusgoSha3(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(utf8ToHex:(NSString *)str) {
|
||||
return StatusgoUtf8ToHex(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToUtf8:(NSString *)str) {
|
||||
return StatusgoHexToUtf8(str);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(checkAddressChecksum:(NSString *)address) {
|
||||
return StatusgoCheckAddressChecksum(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isAddress:(NSString *)address) {
|
||||
return StatusgoIsAddress(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(fleets) {
|
||||
return StatusgoFleets();
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toChecksumAddress:(NSString *)address) {
|
||||
return StatusgoToChecksumAddress(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(validateMnemonic:(NSString *)seed
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"validateMnemonic() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoValidateMnemonic(seed);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"createAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoCreateAccountAndLogin(request);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"restoreAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoRestoreAccountAndLogin(request);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSString *result = StatusgoCallPrivateRPC(payload);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(@[result]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(closeApplication) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(connectionChange:(NSString *)type
|
||||
isExpensive:(BOOL)isExpensive) {
|
||||
#if DEBUG
|
||||
@@ -211,6 +1056,36 @@ RCT_EXPORT_METHOD(startLocalNotifications) {
|
||||
StatusgoStartLocalNotifications();
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(exportUnencryptedDatabase:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"exportUnencryptedDatabase() method called");
|
||||
#endif
|
||||
|
||||
NSString *filePath = [self getExportDbFilePath];
|
||||
StatusgoExportUnencryptedDatabase(accountData, password, filePath);
|
||||
|
||||
callback(@[filePath]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(importUnencryptedDatabase:(NSString *)accountData
|
||||
password:(NSString *)password) {
|
||||
#if DEBUG
|
||||
NSLog(@"importUnencryptedDatabase() method called");
|
||||
#endif
|
||||
"";
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setBlankPreviewFlag:(BOOL *)newValue)
|
||||
{
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
[userDefaults setBool:newValue forKey:@"BLANK_PREVIEW"];
|
||||
|
||||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(activateKeepAwake)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -255,7 +1130,7 @@ RCT_EXPORT_METHOD(deactivateKeepAwake)
|
||||
|
||||
- (NSString*) deviceName
|
||||
{
|
||||
return [[UIDevice currentDevice] name];
|
||||
return [[UIDevice currentDevice] name];;
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
|
||||
@@ -3,20 +3,13 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
206C9F3E1D474E910063E3E6 /* RCTStatus.h in Copy Files */ = {isa = PBXBuildFile; fileRef = 206C9F3D1D474E910063E3E6 /* RCTStatus.h */; };
|
||||
206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 206C9F3F1D474E910063E3E6 /* RCTStatus.m */; };
|
||||
CE4E31B11D86951A0033ED64 /* Statusgo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4E31B01D86951A0033ED64 /* Statusgo.xcframework */; };
|
||||
E92244EB2B485F2400915F4C /* UIHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E92244E92B485F2400915F4C /* UIHelper.m */; };
|
||||
E967A3AC2B47BD5A00FB19B2 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E967A3AB2B47BD5A00FB19B2 /* Utils.m */; };
|
||||
E9BEF3602B470BF1001F6755 /* NetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9BEF35E2B470BF1001F6755 /* NetworkManager.m */; };
|
||||
E9C33AA62B4828A60074B1C5 /* DatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9C33AA52B4828A60074B1C5 /* DatabaseManager.m */; };
|
||||
E9DB08932B4858B400F51053 /* LogManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9DB08912B4858B400F51053 /* LogManager.m */; };
|
||||
E9F5C3322B483B6C001A7F40 /* EncryptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F5C3312B483B6C001A7F40 /* EncryptionUtils.m */; };
|
||||
E9FC4ED12B47EEFF00E834DB /* AccountManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9FC4ECF2B47EEFF00E834DB /* AccountManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -38,20 +31,6 @@
|
||||
206C9F3D1D474E910063E3E6 /* RCTStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTStatus.h; sourceTree = "<group>"; };
|
||||
206C9F3F1D474E910063E3E6 /* RCTStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTStatus.m; sourceTree = "<group>"; };
|
||||
CE4E31B01D86951A0033ED64 /* Statusgo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Statusgo.xcframework; sourceTree = "<group>"; };
|
||||
E92244E92B485F2400915F4C /* UIHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIHelper.m; sourceTree = "<group>"; };
|
||||
E92244EA2B485F2400915F4C /* UIHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIHelper.h; sourceTree = "<group>"; };
|
||||
E967A3AA2B47BD5A00FB19B2 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
|
||||
E967A3AB2B47BD5A00FB19B2 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = "<group>"; };
|
||||
E9BEF35E2B470BF1001F6755 /* NetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkManager.m; sourceTree = "<group>"; };
|
||||
E9BEF35F2B470BF1001F6755 /* NetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkManager.h; sourceTree = "<group>"; };
|
||||
E9C33AA42B4828A60074B1C5 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = "<group>"; };
|
||||
E9C33AA52B4828A60074B1C5 /* DatabaseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DatabaseManager.m; sourceTree = "<group>"; };
|
||||
E9DB08912B4858B400F51053 /* LogManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LogManager.m; sourceTree = "<group>"; };
|
||||
E9DB08922B4858B400F51053 /* LogManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogManager.h; sourceTree = "<group>"; };
|
||||
E9F5C3302B483B6C001A7F40 /* EncryptionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EncryptionUtils.h; sourceTree = "<group>"; };
|
||||
E9F5C3312B483B6C001A7F40 /* EncryptionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EncryptionUtils.m; sourceTree = "<group>"; };
|
||||
E9FC4ECF2B47EEFF00E834DB /* AccountManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AccountManager.m; sourceTree = "<group>"; };
|
||||
E9FC4ED02B47EEFF00E834DB /* AccountManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccountManager.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -86,20 +65,6 @@
|
||||
206C9F3C1D474E910063E3E6 /* Status */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E92244EA2B485F2400915F4C /* UIHelper.h */,
|
||||
E92244E92B485F2400915F4C /* UIHelper.m */,
|
||||
E9DB08922B4858B400F51053 /* LogManager.h */,
|
||||
E9DB08912B4858B400F51053 /* LogManager.m */,
|
||||
E9F5C3302B483B6C001A7F40 /* EncryptionUtils.h */,
|
||||
E9F5C3312B483B6C001A7F40 /* EncryptionUtils.m */,
|
||||
E9C33AA42B4828A60074B1C5 /* DatabaseManager.h */,
|
||||
E9C33AA52B4828A60074B1C5 /* DatabaseManager.m */,
|
||||
E9FC4ED02B47EEFF00E834DB /* AccountManager.h */,
|
||||
E9FC4ECF2B47EEFF00E834DB /* AccountManager.m */,
|
||||
E967A3AA2B47BD5A00FB19B2 /* Utils.h */,
|
||||
E967A3AB2B47BD5A00FB19B2 /* Utils.m */,
|
||||
E9BEF35F2B470BF1001F6755 /* NetworkManager.h */,
|
||||
E9BEF35E2B470BF1001F6755 /* NetworkManager.m */,
|
||||
206C9F3D1D474E910063E3E6 /* RCTStatus.h */,
|
||||
206C9F3F1D474E910063E3E6 /* RCTStatus.m */,
|
||||
);
|
||||
@@ -162,14 +127,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E9DB08932B4858B400F51053 /* LogManager.m in Sources */,
|
||||
E9F5C3322B483B6C001A7F40 /* EncryptionUtils.m in Sources */,
|
||||
E967A3AC2B47BD5A00FB19B2 /* Utils.m in Sources */,
|
||||
E92244EB2B485F2400915F4C /* UIHelper.m in Sources */,
|
||||
E9BEF3602B470BF1001F6755 /* NetworkManager.m in Sources */,
|
||||
206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */,
|
||||
E9C33AA62B4828A60074B1C5 /* DatabaseManager.m in Sources */,
|
||||
E9FC4ED12B47EEFF00E834DB /* AccountManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface UIHelper : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
@@ -1,51 +0,0 @@
|
||||
#import "UIHelper.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
|
||||
@implementation UIHelper
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
#pragma mark - only android methods
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustResize) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustResize() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustPan) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustPan() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) {
|
||||
#if DEBUG
|
||||
NSLog(@"setSoftInputMode() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clearCookies) {
|
||||
NSHTTPCookie *cookie;
|
||||
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
||||
|
||||
for (cookie in [storage cookies]) {
|
||||
[storage deleteCookie:cookie];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clearStorageAPIs) {
|
||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||
|
||||
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
|
||||
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
||||
for (NSString *string in array) {
|
||||
NSLog(@"Removing %@", [path stringByAppendingPathComponent:string]);
|
||||
if ([[string pathExtension] isEqualToString:@"localstorage"])
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[path stringByAppendingPathComponent:string] error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -1,16 +0,0 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface Utils : NSObject <RCTBridgeModule>
|
||||
|
||||
+ (NSString *)jsonStringWithPrettyPrint:(BOOL)prettyPrint fromDictionary:(NSDictionary *)dictionary;
|
||||
+ (NSString *)jsonStringWithPrettyPrint:(BOOL)prettyPrint fromArray:(NSArray *)array;
|
||||
+ (NSURL *)getKeyStoreDirForKeyUID:(NSString *)keyUID;
|
||||
+ (NSString *)getExportDbFilePath;
|
||||
+ (NSString *)getKeyUID:(NSString *)jsonString;
|
||||
+ (void)migrateKeystore:(NSString *)accountData password:(NSString *)password;
|
||||
|
||||
@end
|
||||
@@ -1,128 +0,0 @@
|
||||
#import "Utils.h"
|
||||
#import "React/RCTBridge.h"
|
||||
#import "React/RCTEventDispatcher.h"
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation Utils
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
+ (NSString *)jsonStringWithPrettyPrint:(BOOL)prettyPrint fromDictionary:(NSDictionary *)dictionary {
|
||||
NSError *error;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
|
||||
options:(NSJSONWritingOptions)(prettyPrint ? NSJSONWritingPrettyPrinted : 0)
|
||||
error:&error];
|
||||
|
||||
if (!jsonData) {
|
||||
NSLog(@"jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
|
||||
return @"{}";
|
||||
} else {
|
||||
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)jsonStringWithPrettyPrint:(BOOL)prettyPrint fromArray:(NSArray *)array {
|
||||
NSError *error;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:array
|
||||
options:(NSJSONWritingOptions)(prettyPrint ? NSJSONWritingPrettyPrinted : 0)
|
||||
error:&error];
|
||||
|
||||
if (!jsonData) {
|
||||
NSLog(@"jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
|
||||
return @"[]";
|
||||
} else {
|
||||
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSURL *)getKeyStoreDirForKeyUID:(NSString *)keyUID {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl = [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
|
||||
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *multiaccountKeystoreDir = [oldKeystoreDir URLByAppendingPathComponent:keyUID];
|
||||
|
||||
return multiaccountKeystoreDir;
|
||||
}
|
||||
|
||||
+ (NSString *) getKeyUID:(NSString *)jsonString {
|
||||
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *json = [NSJSONSerialization
|
||||
JSONObjectWithData:data
|
||||
options:NSJSONReadingMutableContainers
|
||||
error:nil];
|
||||
|
||||
return [json valueForKey:@"key-uid"];
|
||||
}
|
||||
|
||||
+ (NSString *) getExportDbFilePath {
|
||||
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"export.db"];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if ([fileManager fileExistsAtPath:filePath]) {
|
||||
[fileManager removeItemAtPath:filePath error:nil];
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
+ (void) migrateKeystore:(NSString *)accountData
|
||||
password:(NSString *)password {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *keyUID = [self getKeyStoreDirForKeyUID:accountData];
|
||||
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDirForKeyUID:keyUID.path];
|
||||
|
||||
NSArray *keys = [fileManager contentsOfDirectoryAtPath:multiaccountKeystoreDir.path error:nil];
|
||||
if (keys.count == 0) {
|
||||
NSString *migrationResult = StatusgoMigrateKeyStoreDir(accountData, password, oldKeystoreDir.path, multiaccountKeystoreDir.path);
|
||||
NSLog(@"keystore migration result %@", migrationResult);
|
||||
|
||||
NSString *initKeystoreResult = StatusgoInitKeystore(multiaccountKeystoreDir.path);
|
||||
NSLog(@"InitKeyStore result %@", initKeystoreResult);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(backupDisabledDataDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(keystoreDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
|
||||
return commonKeystoreDir.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(validateMnemonic:(NSString *)seed
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"validateMnemonic() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoValidateMnemonic(seed);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(checkAddressChecksum:(NSString *)address) {
|
||||
return StatusgoCheckAddressChecksum(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toChecksumAddress:(NSString *)address) {
|
||||
return StatusgoToChecksumAddress(address);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -12,6 +12,7 @@ stdenv.mkDerivation {
|
||||
"patchBuildIdPhase"
|
||||
"patchKeyChainPhase"
|
||||
"patchGlogPhase"
|
||||
"patchBoostPodSpec"
|
||||
"installPhase"
|
||||
];
|
||||
|
||||
@@ -77,6 +78,14 @@ stdenv.mkDerivation {
|
||||
--replace 'export CXX="' '#export CXX="'
|
||||
'';
|
||||
|
||||
# to fix pod checksum issue : https://github.com/facebook/react-native/issues/42180
|
||||
# TODO remove this patch after upgrading to react-native 0.73.2
|
||||
patchBoostPodSpec = ''
|
||||
substituteInPlace ./node_modules/react-native/third-party-podspecs/boost.podspec \
|
||||
--replace 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2' \
|
||||
'https://sourceforge.net/projects/boost/files/boost/1.76.0/boost_1_76_0.tar.bz2' \
|
||||
'';
|
||||
|
||||
# The ELF types are incompatible with the host platform, so let's not even try
|
||||
# TODO: Use Android NDK to strip binaries manually
|
||||
dontPatchELF = true;
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ in {
|
||||
yarn = super.yarn.override { nodejs = super.nodejs-18_x; };
|
||||
openjdk = super.openjdk11_headless;
|
||||
xcodeWrapper = super.xcodeenv.composeXcodeWrapper {
|
||||
version = "14.0";
|
||||
version = "15.0";
|
||||
allowHigher = true;
|
||||
};
|
||||
go = super.go_1_19;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
ios = {targets ? [ "ios/arm64" "iossimulator/amd64"]}: callPackage ./build.nix {
|
||||
platform = "ios";
|
||||
platformVersion = "8.0";
|
||||
platformVersion = "11.0";
|
||||
outputFileName = "Statusgo.xcframework";
|
||||
inherit meta source goBuildLdFlags targets;
|
||||
};
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
+2
-2
@@ -108,13 +108,13 @@
|
||||
;; produced by the target :mocks below and redefines node require
|
||||
;; function to use the mocks instead of the rn libraries
|
||||
:test
|
||||
{:output-to "target/test/test.js"
|
||||
{:output-to #shadow/env "SHADOW_OUTPUT_TO"
|
||||
:output-dir "target/test"
|
||||
:optimizations :simple
|
||||
:target :node-test
|
||||
:dev {:devtools {:preloads [status-im.setup.schema-preload]}}
|
||||
;; Uncomment line below to `make test-watch` a specific file
|
||||
;; :ns-regexp "status-im.subs.messages-test$"
|
||||
:ns-regexp #shadow/env "SHADOW_NS_REGEXP"
|
||||
:main legacy.status-im.test-runner/main
|
||||
;; set :ui-driven to true to let shadow-cljs inject node-repl
|
||||
:ui-driven true
|
||||
|
||||
@@ -39,14 +39,16 @@ export function interpolateNavigationViewOpacity(props) {
|
||||
});
|
||||
}
|
||||
|
||||
export function messagesListOnScroll(distanceFromListTop, callback) {
|
||||
export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callback) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
const currentY = event.contentOffset.y;
|
||||
const layoutHeight = event.layoutMeasurement.height;
|
||||
const contentSizeY = event.contentSize.height - layoutHeight;
|
||||
distanceFromListTop.value = contentSizeY - currentY;
|
||||
runOnJS(callback)(currentY, layoutHeight);
|
||||
const newDistance = contentSizeY - currentY;
|
||||
distanceFromListTop.value = newDistance;
|
||||
chatListScrollY.value = currentY;
|
||||
runOnJS(callback)(layoutHeight, newDistance);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,3 +65,28 @@ export function placeholderZIndex(isCalculationsComplete) {
|
||||
return isCalculationsComplete.value ? 0 : 2;
|
||||
});
|
||||
}
|
||||
|
||||
export function scrollDownButtonOpacity(chatListScrollY, isComposerFocused, windowHeight) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
if (isComposerFocused.value) {
|
||||
return 0;
|
||||
} else {
|
||||
return chatListScrollY.value > windowHeight * 0.75 ? 1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonOpacity(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 0 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonPosition(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 35 : 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[legacy.status-im.data-store.messages :as data-store.messages]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
[legacy.status-im.utils.deprecated-types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.contexts.chat.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messages.list.state :as view.state]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as view.state]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- message-loaded?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[cljs.test :refer-macros [deftest is testing]]
|
||||
[legacy.status-im.chat.models.loading :as loading]
|
||||
[legacy.status-im.chat.models.message :as message]
|
||||
[status-im.contexts.chat.messages.list.state :as list.state]))
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as list.state]))
|
||||
|
||||
(deftest add-received-message-test
|
||||
(with-redefs [message/add-message #(identity %1)]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[legacy.status-im.utils.deprecated-types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.chat.contacts.events :as contacts-store]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[status-im.contexts.shell.activity-center.events :as activity-center]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[quo.foundations.typography :as typography]
|
||||
[react-native.core :as rn]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.messages.delete-message-for-me.events]
|
||||
[status-im.contexts.chat.messages.delete-message.events]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf])
|
||||
(:require-macros [legacy.status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[legacy.status-im.ui.components.react :as react]
|
||||
[legacy.status-im.ui.screens.link-previews-settings.styles :as styles]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.chat.messages.link-preview.events]
|
||||
[status-im.contexts.chat.messenger.messages.link-preview.events]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn prepare-urls-items-data
|
||||
|
||||
+63
-98
@@ -11,41 +11,6 @@
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-Status ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn account-manager
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-AccountManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn encryption
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-EncryptionUtils ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn database
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-DatabaseManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn ui-helper
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-UIHelper ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn log-manager
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-LogManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn utils
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-Utils ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn network
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-NetworkManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn init
|
||||
[handler]
|
||||
(.addListener ^js (.-DeviceEventEmitter ^js react-native) "gethEvent" #(handler (.-jsonEvent ^js %))))
|
||||
@@ -54,23 +19,23 @@
|
||||
[]
|
||||
(log/debug "[native-module] clear-web-data")
|
||||
(when (status)
|
||||
(.clearCookies ^js (ui-helper))
|
||||
(.clearStorageAPIs ^js (ui-helper))))
|
||||
(.clearCookies ^js (status))
|
||||
(.clearStorageAPIs ^js (status))))
|
||||
|
||||
(defn init-keystore
|
||||
[key-uid callback]
|
||||
(log/debug "[native-module] init-keystore" key-uid)
|
||||
(.initKeystore ^js (encryption) key-uid callback))
|
||||
(.initKeystore ^js (status) key-uid callback))
|
||||
|
||||
(defn open-accounts
|
||||
[callback]
|
||||
(log/debug "[native-module] open-accounts")
|
||||
(.openAccounts ^js (account-manager) #(callback (types/json->clj %))))
|
||||
(.openAccounts ^js (status) #(callback (types/json->clj %))))
|
||||
|
||||
(defn prepare-dir-and-update-config
|
||||
[key-uid config callback]
|
||||
(log/debug "[native-module] prepare-dir-and-update-config")
|
||||
(.prepareDirAndUpdateConfig ^js (account-manager)
|
||||
(.prepareDirAndUpdateConfig ^js (status)
|
||||
key-uid
|
||||
config
|
||||
#(callback (types/json->clj %))))
|
||||
@@ -82,7 +47,7 @@
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.saveAccountAndLoginWithKeycard
|
||||
^js (account-manager)
|
||||
^js (status)
|
||||
multiaccount-data
|
||||
password
|
||||
settings
|
||||
@@ -98,7 +63,7 @@
|
||||
(let [config (if config (types/clj->json config) "")]
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.loginWithConfig ^js (account-manager) account-data hashed-password config))))
|
||||
#(.loginWithConfig ^js (status) account-data hashed-password config))))
|
||||
|
||||
(defn login-account
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
@@ -107,15 +72,15 @@
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
keyUid
|
||||
#(.loginAccount ^js (account-manager) (types/clj->json request))))
|
||||
#(.loginAccount ^js (status) (types/clj->json request))))
|
||||
|
||||
(defn create-account-and-login
|
||||
[request]
|
||||
(.createAccountAndLogin ^js (account-manager) (types/clj->json request)))
|
||||
(.createAccountAndLogin ^js (status) (types/clj->json request)))
|
||||
|
||||
(defn restore-account-and-login
|
||||
[request]
|
||||
(.restoreAccountAndLogin ^js (account-manager) (types/clj->json request)))
|
||||
(.restoreAccountAndLogin ^js (status) (types/clj->json request)))
|
||||
|
||||
(defn export-db
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
@@ -124,7 +89,7 @@
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.exportUnencryptedDatabase ^js (database) account-data hashed-password callback)))
|
||||
#(.exportUnencryptedDatabase ^js (status) account-data hashed-password callback)))
|
||||
|
||||
(defn import-db
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
@@ -133,13 +98,13 @@
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.importUnencryptedDatabase ^js (database) account-data hashed-password)))
|
||||
#(.importUnencryptedDatabase ^js (status) account-data hashed-password)))
|
||||
|
||||
(defn logout
|
||||
[]
|
||||
(log/debug "[native-module] logout")
|
||||
(clear-web-data)
|
||||
(.logout ^js (account-manager)))
|
||||
(.logout ^js (status)))
|
||||
|
||||
(defn multiaccount-load-account
|
||||
"NOTE: beware, the password has to be sha3 hashed
|
||||
@@ -149,7 +114,7 @@
|
||||
from memory"
|
||||
[address hashed-password callback]
|
||||
(log/debug "[native-module] multiaccount-load-account")
|
||||
(.multiAccountLoadAccount ^js (account-manager)
|
||||
(.multiAccountLoadAccount ^js (status)
|
||||
(types/clj->json {:address address
|
||||
:password hashed-password})
|
||||
callback))
|
||||
@@ -162,7 +127,7 @@
|
||||
[account-id paths callback]
|
||||
(log/debug "[native-module] multiaccount-derive-addresses")
|
||||
(when (status)
|
||||
(.multiAccountDeriveAddresses ^js (account-manager)
|
||||
(.multiAccountDeriveAddresses ^js (status)
|
||||
(types/clj->json {:accountID account-id
|
||||
:paths paths})
|
||||
callback)))
|
||||
@@ -180,7 +145,7 @@
|
||||
(when (status)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.multiAccountStoreAccount ^js (account-manager)
|
||||
#(.multiAccountStoreAccount ^js (status)
|
||||
(types/clj->json {:accountID account-id
|
||||
:password hashed-password})
|
||||
callback))))
|
||||
@@ -193,7 +158,7 @@
|
||||
account-id)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.multiAccountStoreDerived ^js (account-manager)
|
||||
#(.multiAccountStoreDerived ^js (status)
|
||||
(types/clj->json {:accountID account-id
|
||||
:paths paths
|
||||
:password hashed-password})
|
||||
@@ -206,7 +171,7 @@
|
||||
to store the key"
|
||||
[n mnemonic-length paths callback]
|
||||
(log/debug "[native-module] multiaccount-generate-and-derive-addresses")
|
||||
(.multiAccountGenerateAndDeriveAddresses ^js (account-manager)
|
||||
(.multiAccountGenerateAndDeriveAddresses ^js (status)
|
||||
(types/clj->json {:n n
|
||||
:mnemonicPhraseLength mnemonic-length
|
||||
:bip39Passphrase ""
|
||||
@@ -216,7 +181,7 @@
|
||||
(defn multiaccount-import-mnemonic
|
||||
[mnemonic password callback]
|
||||
(log/debug "[native-module] multiaccount-import-mnemonic")
|
||||
(.multiAccountImportMnemonic ^js (account-manager)
|
||||
(.multiAccountImportMnemonic ^js (status)
|
||||
(types/clj->json {:mnemonicPhrase mnemonic
|
||||
;;NOTE this is not the multiaccount password
|
||||
:Bip39Passphrase password})
|
||||
@@ -225,7 +190,7 @@
|
||||
(defn multiaccount-import-private-key
|
||||
[private-key callback]
|
||||
(log/debug "[native-module] multiaccount-import-private-key")
|
||||
(.multiAccountImportPrivateKey ^js (account-manager)
|
||||
(.multiAccountImportPrivateKey ^js (status)
|
||||
(types/clj->json {:privateKey private-key})
|
||||
callback))
|
||||
|
||||
@@ -233,13 +198,13 @@
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[address hashed-password callback]
|
||||
(log/debug "[native-module] verify")
|
||||
(.verify ^js (account-manager) address hashed-password callback))
|
||||
(.verify ^js (status) address hashed-password callback))
|
||||
|
||||
(defn verify-database-password
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[key-uid hashed-password callback]
|
||||
(log/debug "[native-module] verify-database-password")
|
||||
(.verifyDatabasePassword ^js (account-manager) key-uid hashed-password callback))
|
||||
(.verifyDatabasePassword ^js (status) key-uid hashed-password callback))
|
||||
|
||||
(defn login-with-keycard
|
||||
[{:keys [key-uid multiaccount-data password chat-key node-config]}]
|
||||
@@ -247,40 +212,40 @@
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.loginWithKeycard ^js (account-manager) multiaccount-data password chat-key (types/clj->json node-config))))
|
||||
#(.loginWithKeycard ^js (status) multiaccount-data password chat-key (types/clj->json node-config))))
|
||||
|
||||
(defn set-soft-input-mode
|
||||
[mode]
|
||||
(log/debug "[native-module] set-soft-input-mode")
|
||||
(.setSoftInputMode ^js (ui-helper) mode))
|
||||
(.setSoftInputMode ^js (status) mode))
|
||||
|
||||
(defn call-rpc
|
||||
[payload callback]
|
||||
(log/debug "[native-module] call-rpc")
|
||||
(.callRPC ^js (network) payload callback))
|
||||
(.callRPC ^js (status) payload callback))
|
||||
|
||||
(defn call-private-rpc
|
||||
[payload callback]
|
||||
(.callPrivateRPC ^js (network) payload callback))
|
||||
(.callPrivateRPC ^js (status) payload callback))
|
||||
|
||||
(defn hash-transaction
|
||||
"used for keycard"
|
||||
[rpcParams callback]
|
||||
(log/debug "[native-module] hash-transaction")
|
||||
(.hashTransaction ^js (encryption) rpcParams callback))
|
||||
(.hashTransaction ^js (status) rpcParams callback))
|
||||
|
||||
(defn hash-message
|
||||
"used for keycard"
|
||||
[message callback]
|
||||
(log/debug "[native-module] hash-message")
|
||||
(.hashMessage ^js (encryption) message callback))
|
||||
(.hashMessage ^js (status) message callback))
|
||||
|
||||
(defn start-searching-for-local-pairing-peers
|
||||
"starts a UDP multicast beacon that both listens for and broadcasts to LAN peers"
|
||||
[callback]
|
||||
(log/info "[native-module] Start Searching for Local Pairing Peers"
|
||||
{:fn :start-searching-for-local-pairing-peers})
|
||||
(.startSearchForLocalPairingPeers ^js (network) callback))
|
||||
(.startSearchForLocalPairingPeers ^js (status) callback))
|
||||
|
||||
(defn local-pairing-preflight-outbound-check
|
||||
"Checks whether the device has allows connecting to the local server"
|
||||
@@ -295,7 +260,7 @@
|
||||
(log/info "[native-module] Fetching Connection String"
|
||||
{:fn :get-connection-string-for-bootstrapping-another-device
|
||||
:config-json config-json})
|
||||
(.getConnectionStringForBootstrappingAnotherDevice ^js (network) config-json callback))
|
||||
(.getConnectionStringForBootstrappingAnotherDevice ^js (status) config-json callback))
|
||||
|
||||
(defn input-connection-string-for-bootstrapping
|
||||
"Provides connection string to status-go for the purpose of local pairing on the receiver end"
|
||||
@@ -304,7 +269,7 @@
|
||||
{:fn :input-connection-string-for-bootstrapping
|
||||
:config-json config-json
|
||||
:connection-string connection-string})
|
||||
(.inputConnectionStringForBootstrapping ^js (network) connection-string config-json callback))
|
||||
(.inputConnectionStringForBootstrapping ^js (status) connection-string config-json callback))
|
||||
|
||||
(defn deserialize-and-compress-key
|
||||
"Provides a community id (public key) to status-go which is first deserialized
|
||||
@@ -315,7 +280,7 @@
|
||||
(log/info "[native-module] Deserializing and then compressing public key"
|
||||
{:fn :deserialize-and-compress-key
|
||||
:key input-key})
|
||||
(.deserializeAndCompressKey ^js (encryption) input-key callback))
|
||||
(.deserializeAndCompressKey ^js (status) input-key callback))
|
||||
|
||||
(defn compressed-key->public-key
|
||||
"Provides compressed key to status-go and gets back the uncompressed public key via deserialization"
|
||||
@@ -323,59 +288,59 @@
|
||||
(log/info "[native-module] Deserializing compressed key"
|
||||
{:fn :compressed-key->public-key
|
||||
:public-key public-key})
|
||||
(.multiformatDeserializePublicKey ^js (encryption) public-key deserialization-key callback))
|
||||
(.multiformatDeserializePublicKey ^js (status) public-key deserialization-key callback))
|
||||
|
||||
(defn hash-typed-data
|
||||
"used for keycard"
|
||||
[data callback]
|
||||
(log/debug "[native-module] hash-typed-data")
|
||||
(.hashTypedData ^js (encryption) data callback))
|
||||
(.hashTypedData ^js (status) data callback))
|
||||
|
||||
(defn hash-typed-data-v4
|
||||
"used for keycard"
|
||||
[data callback]
|
||||
(log/debug "[native-module] hash-typed-data-v4")
|
||||
(.hashTypedDataV4 ^js (encryption) data callback))
|
||||
(.hashTypedDataV4 ^js (status) data callback))
|
||||
|
||||
(defn send-transaction-with-signature
|
||||
"used for keycard"
|
||||
[rpcParams sig callback]
|
||||
(log/debug "[native-module] send-transaction-with-signature")
|
||||
(.sendTransactionWithSignature ^js (network) rpcParams sig callback))
|
||||
(.sendTransactionWithSignature ^js (status) rpcParams sig callback))
|
||||
|
||||
(defn sign-message
|
||||
"NOTE: beware, the password in rpcParams has to be sha3 hashed"
|
||||
[rpcParams callback]
|
||||
(log/debug "[native-module] sign-message")
|
||||
(.signMessage ^js (encryption) rpcParams callback))
|
||||
(.signMessage ^js (status) rpcParams callback))
|
||||
|
||||
(defn recover-message
|
||||
[rpcParams callback]
|
||||
(log/debug "[native-module] recover")
|
||||
(.recover ^js (network) rpcParams callback))
|
||||
(.recover ^js (status) rpcParams callback))
|
||||
|
||||
(defn send-transaction
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[rpcParams hashed-password callback]
|
||||
(log/debug "[native-module] send-transaction")
|
||||
(.sendTransaction ^js (network) rpcParams hashed-password callback))
|
||||
(.sendTransaction ^js (status) rpcParams hashed-password callback))
|
||||
|
||||
(defn sign-typed-data
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[data account hashed-password callback]
|
||||
(log/debug "[native-module] sign-typed-data")
|
||||
(.signTypedData ^js (encryption) data account hashed-password callback))
|
||||
(.signTypedData ^js (status) data account hashed-password callback))
|
||||
|
||||
(defn sign-typed-data-v4
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[data account hashed-password callback]
|
||||
(log/debug "[native-module] sign-typed-data-v4")
|
||||
(.signTypedDataV4 ^js (encryption) data account hashed-password callback))
|
||||
(.signTypedDataV4 ^js (status) data account hashed-password callback))
|
||||
|
||||
(defn send-logs
|
||||
[dbJson js-logs callback]
|
||||
(log/debug "[native-module] send-logs")
|
||||
(.sendLogs ^js (log-manager) dbJson js-logs callback))
|
||||
(.sendLogs ^js (status) dbJson js-logs callback))
|
||||
|
||||
(defn close-application
|
||||
[]
|
||||
@@ -400,7 +365,7 @@
|
||||
(defn set-blank-preview-flag
|
||||
[flag]
|
||||
(log/debug "[native-module] set-blank-preview-flag")
|
||||
(.setBlankPreviewFlag ^js (encryption) flag))
|
||||
(.setBlankPreviewFlag ^js (status) flag))
|
||||
|
||||
(defn get-device-model-info
|
||||
[]
|
||||
@@ -428,7 +393,7 @@
|
||||
(defn toggle-webview-debug
|
||||
[on]
|
||||
(log/debug "[native-module] toggle-webview-debug" on)
|
||||
(.toggleWebviewDebug ^js (ui-helper) on))
|
||||
(.toggleWebviewDebug ^js (status) on))
|
||||
|
||||
(defn rooted-device?
|
||||
[callback]
|
||||
@@ -451,72 +416,72 @@
|
||||
(defn encode-transfer
|
||||
[to-norm amount-hex]
|
||||
(log/debug "[native-module] encode-transfer")
|
||||
(.encodeTransfer ^js (encryption) to-norm amount-hex))
|
||||
(.encodeTransfer ^js (status) to-norm amount-hex))
|
||||
|
||||
(defn decode-parameters
|
||||
[bytes-string types]
|
||||
(log/debug "[native-module] decode-parameters")
|
||||
(let [json-str (.decodeParameters ^js (encryption)
|
||||
(let [json-str (.decodeParameters ^js (status)
|
||||
(types/clj->json {:bytesString bytes-string :types types}))]
|
||||
(types/json->clj json-str)))
|
||||
|
||||
(defn hex-to-number
|
||||
[hex]
|
||||
(log/debug "[native-module] hex-to-number")
|
||||
(let [json-str (.hexToNumber ^js (encryption) hex)]
|
||||
(let [json-str (.hexToNumber ^js (status) hex)]
|
||||
(types/json->clj json-str)))
|
||||
|
||||
(defn number-to-hex
|
||||
[num]
|
||||
(log/debug "[native-module] number-to-hex")
|
||||
(.numberToHex ^js (encryption) (str num)))
|
||||
(.numberToHex ^js (status) (str num)))
|
||||
|
||||
(defn sha3
|
||||
[s]
|
||||
(log/debug "[native-module] sha3")
|
||||
(when s
|
||||
(.sha3 ^js (encryption) (str s))))
|
||||
(.sha3 ^js (status) (str s))))
|
||||
|
||||
(defn utf8-to-hex
|
||||
[s]
|
||||
(log/debug "[native-module] utf8-to-hex")
|
||||
(.utf8ToHex ^js (encryption) s))
|
||||
(.utf8ToHex ^js (status) s))
|
||||
|
||||
(defn hex-to-utf8
|
||||
[s]
|
||||
(log/debug "[native-module] hex-to-utf8")
|
||||
(.hexToUtf8 ^js (encryption) s))
|
||||
(.hexToUtf8 ^js (status) s))
|
||||
|
||||
(defn check-address-checksum
|
||||
[address]
|
||||
(log/debug "[native-module] check-address-checksum")
|
||||
(let [result (.checkAddressChecksum ^js (utils) address)]
|
||||
(let [result (.checkAddressChecksum ^js (status) address)]
|
||||
(types/json->clj result)))
|
||||
|
||||
(defn address?
|
||||
[address]
|
||||
(log/debug "[native-module] address?")
|
||||
(when address
|
||||
(let [result (.isAddress ^js (utils) address)]
|
||||
(let [result (.isAddress ^js (status) address)]
|
||||
(types/json->clj result))))
|
||||
|
||||
(defn to-checksum-address
|
||||
[address]
|
||||
(log/debug "[native-module] to-checksum-address")
|
||||
(.toChecksumAddress ^js (utils) address))
|
||||
(.toChecksumAddress ^js (status) address))
|
||||
|
||||
(defn validate-mnemonic
|
||||
"Validate that a mnemonic conforms to BIP39 dictionary/checksum standards"
|
||||
[mnemonic callback]
|
||||
(log/debug "[native-module] validate-mnemonic")
|
||||
(.validateMnemonic ^js (utils) mnemonic callback))
|
||||
(.validateMnemonic ^js (status) mnemonic callback))
|
||||
|
||||
(defn delete-multiaccount
|
||||
"Delete multiaccount from database, deletes multiaccount's database and
|
||||
key files."
|
||||
[key-uid callback]
|
||||
(log/debug "[native-module] delete-multiaccount")
|
||||
(.deleteMultiaccount ^js (account-manager) key-uid callback))
|
||||
(.deleteMultiaccount ^js (status) key-uid callback))
|
||||
|
||||
(defn delete-imported-key
|
||||
"Delete imported key file."
|
||||
@@ -528,7 +493,7 @@
|
||||
[input selection]
|
||||
(log/debug "[native-module] resetKeyboardInput")
|
||||
(when platform/android?
|
||||
(.resetKeyboardInputCursor ^js (ui-helper) input selection)))
|
||||
(.resetKeyboardInputCursor ^js (status) input selection)))
|
||||
|
||||
;; passwords are hashed
|
||||
(defn reset-password
|
||||
@@ -536,12 +501,12 @@
|
||||
(log/debug "[native-module] change-database-password")
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.reEncryptDbAndKeystore ^js (encryption) key-uid current-password# new-password# callback)))
|
||||
#(.reEncryptDbAndKeystore ^js (status) key-uid current-password# new-password# callback)))
|
||||
|
||||
(defn convert-to-keycard-account
|
||||
[{:keys [key-uid] :as multiaccount-data} settings current-password# new-password callback]
|
||||
(log/debug "[native-module] convert-to-keycard-account")
|
||||
(.convertToKeycardAccount ^js (encryption)
|
||||
(.convertToKeycardAccount ^js (status)
|
||||
key-uid
|
||||
(types/clj->json multiaccount-data)
|
||||
(types/clj->json settings)
|
||||
@@ -552,7 +517,7 @@
|
||||
|
||||
(defn backup-disabled-data-dir
|
||||
[]
|
||||
(.backupDisabledDataDir ^js (utils)))
|
||||
(.backupDisabledDataDir ^js (status)))
|
||||
|
||||
(defn fleets
|
||||
[]
|
||||
@@ -560,12 +525,12 @@
|
||||
|
||||
(defn keystore-dir
|
||||
[]
|
||||
(.keystoreDir ^js (utils)))
|
||||
(.keystoreDir ^js (status)))
|
||||
|
||||
(defn log-file-directory
|
||||
[]
|
||||
(.logFileDirectory ^js (log-manager)))
|
||||
(.logFileDirectory ^js (status)))
|
||||
|
||||
(defn init-status-go-logging
|
||||
[{:keys [enable? mobile-system? log-level callback]}]
|
||||
(.initLogging ^js (log-manager) enable? mobile-system? log-level callback))
|
||||
(.initLogging ^js (status) enable? mobile-system? log-level callback))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
utils.money))
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [value icon theme style accessibility-label]}]
|
||||
[{:keys [value icon theme style accessibility-label text-size]}]
|
||||
[rn/view
|
||||
{:style (merge style/container style)
|
||||
:accessibility-label accessibility-label}
|
||||
@@ -19,7 +19,7 @@
|
||||
:resize-mode :center
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]
|
||||
[quo.text/text
|
||||
{:size :paragraph-1
|
||||
{:size (or text-size :paragraph-1)
|
||||
:weight :regular
|
||||
:style (style/text theme)} (utils.money/format-amount value)]])
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
:container-style])
|
||||
|
||||
(defn- base-input
|
||||
[{:keys [on-change-text on-char-limit-reach weight]}]
|
||||
[{:keys [on-change-text on-char-limit-reach weight default-value]}]
|
||||
(let [status (reagent/atom :default)
|
||||
internal-on-focus #(reset! status :focus)
|
||||
internal-on-blur #(reset! status :default)
|
||||
@@ -72,7 +72,7 @@
|
||||
(if (> height min-height)
|
||||
(reset! multiple-lines? true)
|
||||
(reset! multiple-lines? false)))
|
||||
char-count (reagent/atom 0)
|
||||
char-count (reagent/atom (count default-value))
|
||||
update-char-limit! (fn [new-text char-limit]
|
||||
(when on-change-text (on-change-text new-text))
|
||||
(let [amount-chars (count new-text)]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
(ns quo.components.links.internal-link-card.channel.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme margin-right?]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 72
|
||||
:margin-right (when margin-right? 4)
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn loading-thumbnail-box
|
||||
[theme size]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:height (if (= :message size) 139 160)
|
||||
:border-radius 12})
|
||||
|
||||
(defn thumbnail
|
||||
[size]
|
||||
{:width "100%"
|
||||
:height (if (= :message size) 139 160)
|
||||
:margin-top 8
|
||||
:border-radius 12})
|
||||
|
||||
(defn container
|
||||
[size theme]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if (= :message size) 215 236)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:margin-right 6
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 8})
|
||||
|
||||
(defn channel-chevron-props
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
|
||||
|
||||
(def row-spacing
|
||||
{:flex-direction :row
|
||||
:margin-bottom 13})
|
||||
@@ -0,0 +1,92 @@
|
||||
(ns quo.components.links.internal-link-card.channel.view
|
||||
(:require
|
||||
[quo.components.icon :as icon]
|
||||
[quo.components.links.internal-link-card.channel.style :as style]
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- description-comp
|
||||
[description]
|
||||
[rn/view {:style {:margin-bottom 4}}
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 3
|
||||
:accessibility-label :description}
|
||||
description]])
|
||||
|
||||
(defn- title-comp
|
||||
[title channel-name theme]
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title]
|
||||
[icon/icon :i/chevron-right (style/channel-chevron-props theme)]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
channel-name]])
|
||||
|
||||
(defn- banner-comp
|
||||
[thumbnail size]
|
||||
[rn/image
|
||||
{:style (style/thumbnail size)
|
||||
:source thumbnail
|
||||
:accessibility-label :banner}])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style (assoc style/logo :margin-bottom 2)}])
|
||||
|
||||
(defn- loading-view
|
||||
[theme size]
|
||||
[rn/view
|
||||
{:accessibility-label :loading-channel-link-view
|
||||
:style {:height 215}}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme true)}]]
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme false)}]]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style (style/loading-thumbnail-box theme size)}]])
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [title description loading? icon banner
|
||||
theme on-press channel-name size]
|
||||
:or {channel-name "empty name"}}]
|
||||
[rn/pressable
|
||||
{:style (style/container size theme)
|
||||
:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
(if loading?
|
||||
[loading-view theme size]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title channel-name theme]]
|
||||
(when description
|
||||
[description-comp description])
|
||||
(when banner
|
||||
[banner-comp banner size])])])
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -0,0 +1,87 @@
|
||||
(ns quo.components.links.internal-link-card.community.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-stat
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 32
|
||||
:height 8
|
||||
:border-radius 16})
|
||||
|
||||
(def loading-stat-container
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-right 12
|
||||
:margin-bottom -6})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 145
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn loading-thumbnail-box
|
||||
[theme size]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:height (if (= :message size) 139 160)
|
||||
:border-radius 12})
|
||||
|
||||
(defn thumbnail
|
||||
[size]
|
||||
{:width "100%"
|
||||
:height (if (= :message size) 139 160)
|
||||
:margin-top 6
|
||||
:border-radius 12})
|
||||
|
||||
(defn container
|
||||
[size theme]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if (= :message size) 245 266)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:width 16
|
||||
:height 16
|
||||
:border-radius 8
|
||||
:margin-right 4
|
||||
:margin-bottom 2})
|
||||
|
||||
(def row-spacing
|
||||
{:flex-direction :row
|
||||
:margin-bottom 12
|
||||
:margin-top 4})
|
||||
|
||||
(def stat-container
|
||||
{:flex-direction :row
|
||||
:margin-top 12})
|
||||
@@ -0,0 +1,96 @@
|
||||
(ns quo.components.links.internal-link-card.community.view
|
||||
(:require
|
||||
[quo.components.community.community-stat.view :as community-stat]
|
||||
[quo.components.links.internal-link-card.community.style :as style]
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- description-comp
|
||||
[description members-count active-members-count]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 3
|
||||
:accessibility-label :description}
|
||||
description]
|
||||
[rn/view {:style style/stat-container}
|
||||
[community-stat/view
|
||||
{:value members-count
|
||||
:icon :i/members
|
||||
:accessibility-label :members-count
|
||||
:style {:margin-right 12}
|
||||
:text-size :paragraph-2}]
|
||||
(when active-members-count
|
||||
[community-stat/view
|
||||
{:value active-members-count
|
||||
:icon :i/active-members
|
||||
:accessibility-label :active-members-count
|
||||
:text-size :paragraph-2}])]])
|
||||
|
||||
(defn- title-comp
|
||||
[title]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title])
|
||||
|
||||
(defn- thumbnail-comp
|
||||
[thumbnail size]
|
||||
[rn/image
|
||||
{:style (style/thumbnail size)
|
||||
:source thumbnail
|
||||
:accessibility-label :thumbnail}])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style style/logo}])
|
||||
|
||||
(defn- stat-loading
|
||||
[theme]
|
||||
[rn/view {:style style/loading-stat-container}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-stat theme)}]])
|
||||
|
||||
(defn- loading-view
|
||||
[theme size]
|
||||
[rn/view {:accessibility-label :loading-community-link-view}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme)}]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style style/row-spacing}
|
||||
[stat-loading theme]
|
||||
[stat-loading theme]]
|
||||
[rn/view {:style (style/loading-thumbnail-box theme size)}]])
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [title description loading? icon banner members-count active-members-count
|
||||
theme on-press size]}]
|
||||
[rn/pressable
|
||||
{:style (style/container size theme)
|
||||
:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
(if loading?
|
||||
[loading-view theme size]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title]]
|
||||
(when description
|
||||
[description-comp description members-count active-members-count])
|
||||
(when banner
|
||||
[thumbnail-comp banner size])])])
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -0,0 +1,81 @@
|
||||
(ns quo.components.links.internal-link-card.component-spec
|
||||
(:require
|
||||
[quo.components.links.internal-link-card.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(defn- render
|
||||
[component]
|
||||
(h/render-with-theme-provider component :light))
|
||||
|
||||
(def user-props
|
||||
{:title "Some title"
|
||||
:subtitle "Some description"
|
||||
:loading? false
|
||||
:icon "data:image/png,logo-x"
|
||||
:type :user
|
||||
:customization-color "#ff0000"
|
||||
:emoji-hash "🌟🚀🐠🌈🏰🔮🦉🐼🍉🎨🚲🌙🍔🌵"})
|
||||
|
||||
(h/describe "Internal link card - User"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view user-props])
|
||||
(h/is-truthy (h/query-by-text (:title user-props)))
|
||||
(h/is-truthy (h/query-by-text (:subtitle user-props))))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc user-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
|
||||
(def community-props
|
||||
{:title "Some title"
|
||||
:description "Some description"
|
||||
:icon "data:image/png,logo-x"
|
||||
:banner "data:image/png,whatever"
|
||||
:members-count "20"
|
||||
:loading? false
|
||||
:active-members-count "15"
|
||||
:type :community})
|
||||
|
||||
(h/describe "Internal link card - Community"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view community-props])
|
||||
(h/is-truthy (h/query-by-text (:title community-props)))
|
||||
(h/is-truthy (h/query-by-text (:description community-props)))
|
||||
(h/is-truthy (h/query-by-text (:members-count community-props)))
|
||||
(h/is-truthy (h/query-by-text (:active-members-count community-props)))
|
||||
(h/is-truthy (h/query-by-label-text :logo))
|
||||
(h/is-truthy (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "does not render thumbnail if prop is not present"
|
||||
(render [view/view (dissoc community-props :banner)])
|
||||
(h/is-null (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc community-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
|
||||
(def channel-props
|
||||
{:title "Doodles"
|
||||
:description "Coloring the world with joy • ᴗ •"
|
||||
:icon "data:image/png,logo-x"
|
||||
:banner "data:image/png,whatever"
|
||||
:loading? false
|
||||
:channel-name "#general"
|
||||
:type :channel})
|
||||
|
||||
(h/describe "Internal link card - Channel"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view channel-props])
|
||||
(h/is-truthy (h/query-by-text (:title channel-props)))
|
||||
(h/is-truthy (h/query-by-text (:description channel-props)))
|
||||
(h/is-truthy (h/query-by-text (:channel-name channel-props)))
|
||||
(h/is-truthy (h/query-by-label-text :logo))
|
||||
(h/is-truthy (h/query-by-label-text :banner)))
|
||||
|
||||
(h/test "does not render banner if prop is not present"
|
||||
(render [view/view (dissoc channel-props :banner)])
|
||||
(h/is-null (h/query-by-label-text :banner)))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc channel-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
@@ -0,0 +1,23 @@
|
||||
(ns quo.components.links.internal-link-card.schema)
|
||||
|
||||
(def ?schema
|
||||
[:=>
|
||||
[:catn
|
||||
[:props
|
||||
[:map {:closed true}
|
||||
[:title {:optional true} [:maybe :string]]
|
||||
[:description {:optional true} [:maybe :string]]
|
||||
[:channel-name {:optional true} [:maybe :string]]
|
||||
[:loading? {:optional true} [:maybe :boolean]]
|
||||
[:subtitle {:optional true} [:maybe :string]]
|
||||
[:icon {:optional true} [:maybe [:or :string :int]]]
|
||||
[:banner {:optional true} [:maybe [:or :string :int]]]
|
||||
[:type {:optional true} [:maybe :keyword]]
|
||||
[:on-press {:optional true} [:maybe fn?]]
|
||||
[:members-count {:optional true} [:maybe [:or :int :string]]]
|
||||
[:active-members-count {:optional true} [:maybe [:or :int :string]]]
|
||||
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||
[:emoji-hash {:optional true} [:maybe :string]]
|
||||
[:size {:optional true} [:maybe :keyword]]
|
||||
[:theme :schema.common/theme]]]]
|
||||
:any])
|
||||
@@ -0,0 +1,66 @@
|
||||
(ns quo.components.links.internal-link-card.user.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 145
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn last-bar-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 271
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn gradient-start-color
|
||||
[customization-color theme]
|
||||
(colors/theme-colors (colors/resolve-color customization-color theme 10)
|
||||
(colors/resolve-color customization-color theme 20)
|
||||
theme))
|
||||
|
||||
(defn container
|
||||
[loading? theme size]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if loading? 92 110)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:margin-right 6
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 8
|
||||
:margin-bottom 2})
|
||||
|
||||
(def row-spacing {:flex-direction :row :margin-bottom 12})
|
||||
@@ -0,0 +1,83 @@
|
||||
(ns quo.components.links.internal-link-card.user.view
|
||||
(:require
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.links.internal-link-card.user.style :as style]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- subtitle-comp
|
||||
[subtitle emoji-hash]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 2
|
||||
:accessibility-label :subtitle
|
||||
:style {:margin-bottom 12}}
|
||||
subtitle]
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:weight :regular
|
||||
:accessibility-label :emoji-hash}
|
||||
emoji-hash]]])
|
||||
|
||||
(defn- title-comp
|
||||
[title]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style style/logo}])
|
||||
|
||||
(defn- loading-view
|
||||
[theme]
|
||||
[rn/view {:accessibility-label :loading-user-link-view}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme)}]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style (style/last-bar-line-bar theme)}]])
|
||||
|
||||
(defn- linear-gradient-props
|
||||
[theme customization-color]
|
||||
[(style/gradient-start-color customization-color theme) :transparent])
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [title loading? icon
|
||||
theme on-press subtitle emoji-hash customization-color size]}]
|
||||
(if loading?
|
||||
[rn/pressable
|
||||
{:accessibility-label :internal-link-card
|
||||
:on-press on-press
|
||||
:style (style/container loading? theme size)}
|
||||
[loading-view theme]]
|
||||
[linear-gradient/linear-gradient
|
||||
(assoc {:style (style/container loading? theme size)}
|
||||
:colors
|
||||
(linear-gradient-props theme customization-color))
|
||||
[rn/pressable
|
||||
{:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title]]
|
||||
(when subtitle
|
||||
[subtitle-comp subtitle emoji-hash])]]))
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -0,0 +1,11 @@
|
||||
(ns quo.components.links.internal-link-card.view
|
||||
(:require [quo.components.links.internal-link-card.channel.view :as channel.view]
|
||||
[quo.components.links.internal-link-card.community.view :as community.view]
|
||||
[quo.components.links.internal-link-card.user.view :as user.view]))
|
||||
|
||||
(defn view
|
||||
[{card-type :type :as props}]
|
||||
(case card-type
|
||||
:community [community.view/view props]
|
||||
:channel [channel.view/view props]
|
||||
:user [user.view/view props]))
|
||||
@@ -26,6 +26,7 @@
|
||||
(on-press label)))
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:hit-slop {:top 8 :bottom 8 :left 25 :right 25}
|
||||
:style (style/container background-color)}
|
||||
(case type
|
||||
:key [icons/icon
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn view
|
||||
[{:keys [type]} & children]
|
||||
[{:keys [type container-style]} & children]
|
||||
[rn/view {:style (style/overlay-background type)}
|
||||
(if (= type :shell)
|
||||
[blur/view
|
||||
@@ -14,7 +14,7 @@
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent
|
||||
:style style/container}
|
||||
[rn/view {:style style/blur-container}
|
||||
[rn/view {:style (merge style/blur-container container-style)}
|
||||
children]]
|
||||
[rn/view {:style style/container}
|
||||
[rn/view {:style (merge style/container container-style)}
|
||||
children])])
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- category-internal
|
||||
[{:keys [label data] :as props}]
|
||||
[rn/view {:style (style/container label)}
|
||||
[{:keys [label data container-style] :as props}]
|
||||
[rn/view {:style (merge (style/container label) container-style)}
|
||||
(when label
|
||||
[text/text
|
||||
{:weight :medium
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
(defn sub-container
|
||||
[align-action]
|
||||
{:flex-direction :row
|
||||
:padding-right 0.5
|
||||
:align-items (or align-action :center)})
|
||||
|
||||
(def left-container
|
||||
{:margin-horizontal 12
|
||||
(defn left-container
|
||||
[image?]
|
||||
{:margin-horizontal (if image? 12 0)
|
||||
:flex 1
|
||||
:height "100%"
|
||||
:justify-content :flex-start})
|
||||
@@ -57,6 +59,7 @@
|
||||
{:width 15
|
||||
:height 15
|
||||
:border-radius 12
|
||||
:margin-right 4
|
||||
:background-color background-color})
|
||||
|
||||
(def status-tag-container
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
:accessibility-label accessibility-label}
|
||||
[rn/view {:style (style/left-sub-container props)}
|
||||
[image-component props]
|
||||
[rn/view {:style style/left-container}
|
||||
[rn/view {:style (style/left-container (:image props))}
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:style {:color (when blur? colors/white)}} title]
|
||||
|
||||
@@ -37,13 +37,14 @@
|
||||
(def ^:private b64-png-image-prefix "data:image/png;base64,")
|
||||
|
||||
(defn temp-empty-symbol
|
||||
[token size]
|
||||
[token size style]
|
||||
[rn/view
|
||||
{:style (token-style {:justify-content :center
|
||||
:align-items :center
|
||||
:border-radius 20
|
||||
:border-width 1
|
||||
:border-color :grey}
|
||||
{:style (token-style (merge {:justify-content :center
|
||||
:align-items :center
|
||||
:border-radius 20
|
||||
:border-width 1
|
||||
:border-color :grey}
|
||||
style)
|
||||
size)}
|
||||
[quo/text {:style {:color :grey}}
|
||||
(some-> token
|
||||
@@ -73,6 +74,6 @@
|
||||
[rn/image
|
||||
{:style (token-style style size)
|
||||
:source source}]
|
||||
[temp-empty-symbol token size])))
|
||||
[temp-empty-symbol token size style])))
|
||||
|
||||
(def view (schema/instrument #'view-internal ?schema))
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
colors/white))
|
||||
|
||||
(defn card
|
||||
[{:keys [customization-color type theme pressed?]}]
|
||||
{:width 162
|
||||
[{:keys [customization-color type theme pressed? metrics?]}]
|
||||
{:width 161
|
||||
:height (if metrics? 88 68)
|
||||
:background-color (when (not= :watch-only type)
|
||||
(colors/theme-colors
|
||||
(colors/resolve-color customization-color
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
:style (style/card {:customization-color customization-color
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? false})}
|
||||
:pressed? false
|
||||
:metrics? metrics?})}
|
||||
[rn/view {:style style/loader-container}
|
||||
[rn/view
|
||||
{:style (assoc (style/loader-view {:width 16
|
||||
@@ -113,7 +114,8 @@
|
||||
:style (style/card {:customization-color customization-color
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? @pressed?})
|
||||
:pressed? @pressed?
|
||||
:metrics? metrics?})
|
||||
:on-press on-press}
|
||||
(when (and customization-color (and (not watch-only?) (not missing-keypair?)))
|
||||
[customization-colors/overlay
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
[:checked? {:optional true} [:maybe :boolean]]
|
||||
[:disabled? {:optional true} [:maybe :boolean]]
|
||||
[:on-change {:optional true} [:maybe fn?]]
|
||||
[:container-style {:optional true} [:maybe :map]]
|
||||
[:theme :schema.common/theme]]]]
|
||||
:any])
|
||||
|
||||
@@ -58,11 +59,11 @@
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys
|
||||
[checked? disabled? on-change token-details keycard? theme]
|
||||
[checked? disabled? on-change token-details keycard? theme container-style]
|
||||
{:keys
|
||||
[name address emoji customization-color]} :account}]
|
||||
[rn/view
|
||||
{:style (style/container theme)
|
||||
{:style (merge (style/container theme) container-style)
|
||||
:accessibility-label :wallet-account-permissions}
|
||||
[rn/view {:style style/row1}
|
||||
[account-avatar/view
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require
|
||||
[quo.components.avatars.account-avatar.view :as account-avatar]
|
||||
[quo.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.components.wallet.summary-info.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
@@ -26,24 +27,29 @@
|
||||
|
||||
(defn networks
|
||||
[values theme]
|
||||
(let [{:keys [ethereum optimism arbitrum]} values]
|
||||
(let [{:keys [ethereum optimism arbitrum]} values
|
||||
show-optimism? (pos? optimism)
|
||||
show-arbitrum? (pos? arbitrum)]
|
||||
[rn/view
|
||||
{:style style/networks-container
|
||||
:accessibility-label :networks}
|
||||
[network-amount
|
||||
{:network :ethereum
|
||||
:amount (str ethereum " ETH")
|
||||
:divider? true
|
||||
:theme theme}]
|
||||
[network-amount
|
||||
{:network :optimism
|
||||
:amount (str optimism " ETH")
|
||||
:divider? true
|
||||
:theme theme}]
|
||||
[network-amount
|
||||
{:network :arbitrum
|
||||
:amount (str arbitrum " ETH")
|
||||
:theme theme}]]))
|
||||
(when (pos? ethereum)
|
||||
[network-amount
|
||||
{:network :ethereum
|
||||
:amount (str ethereum " ETH")
|
||||
:divider? (or show-arbitrum? show-optimism?)
|
||||
:theme theme}])
|
||||
(when show-optimism?
|
||||
[network-amount
|
||||
{:network :optimism
|
||||
:amount (str optimism " OPT")
|
||||
:divider? show-arbitrum?
|
||||
:theme theme}])
|
||||
(when show-arbitrum?
|
||||
[network-amount
|
||||
{:network :arbitrum
|
||||
:amount (str arbitrum " ARB")
|
||||
:theme theme}])]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [theme type account-props networks? values]}]
|
||||
@@ -51,8 +57,13 @@
|
||||
{:style (style/container networks? theme)}
|
||||
[rn/view
|
||||
{:style style/info-container}
|
||||
(if (= type :status-account)
|
||||
[account-avatar/view account-props]
|
||||
(case type
|
||||
:status-account [account-avatar/view account-props]
|
||||
:saved-account [wallet-user-avatar/wallet-user-avatar (assoc account-props :size :size-32)]
|
||||
:account [wallet-user-avatar/wallet-user-avatar
|
||||
(assoc account-props
|
||||
:size :size-32
|
||||
:neutral? true)]
|
||||
[user-avatar/user-avatar account-props])
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
(when (not= type :account) [text/text {:weight :semi-bold} (:name account-props)])
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
quo.components.inputs.title-input.view
|
||||
quo.components.ios.drawer-bar.view
|
||||
quo.components.keycard.view
|
||||
quo.components.links.internal-link-card.view
|
||||
quo.components.links.link-preview.view
|
||||
quo.components.links.url-preview-list.view
|
||||
quo.components.links.url-preview.view
|
||||
@@ -288,6 +289,7 @@
|
||||
(def numbered-keyboard quo.components.numbered-keyboard.numbered-keyboard.view/view)
|
||||
|
||||
;;;; Links
|
||||
(def internal-link-card quo.components.links.internal-link-card.view/view)
|
||||
(def link-preview quo.components.links.link-preview.view/view)
|
||||
(def url-preview quo.components.links.url-preview.view/view)
|
||||
(def url-preview-list quo.components.links.url-preview-list.view/view)
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
quo.components.inputs.recovery-phrase.component-spec
|
||||
quo.components.inputs.title-input.component-spec
|
||||
quo.components.keycard.component-spec
|
||||
quo.components.links.internal-link-card.component-spec
|
||||
quo.components.links.link-preview.component-spec
|
||||
quo.components.links.url-preview-list.component-spec
|
||||
quo.components.links.url-preview.component-spec
|
||||
|
||||
+8
-2
@@ -5,11 +5,17 @@
|
||||
in non-debug environments.
|
||||
|
||||
`value` is first transformed via `malli.core/schema` to make sure we fail fast
|
||||
and only register valid schemas."
|
||||
and only register valid schemas.
|
||||
|
||||
Pass raw schema `value` instead of schema instance when it's `:function` schema
|
||||
https://github.com/status-im/status-mobile/pull/18364#issuecomment-1875543794"
|
||||
[sym value]
|
||||
`(if ^boolean js/goog.DEBUG
|
||||
(try
|
||||
(malli.core/=> ~sym (malli.core/schema ~value))
|
||||
(let [schema-instance# (malli.core/schema ~value)]
|
||||
(if (and (seq ~value) (= :function (first ~value)))
|
||||
(malli.core/=> ~sym ~value)
|
||||
(malli.core/=> ~sym schema-instance#)))
|
||||
(catch js/Error e#
|
||||
(taoensso.timbre/error "Failed to instrument function"
|
||||
{:symbol ~sym :error e#})
|
||||
|
||||
@@ -24,11 +24,17 @@
|
||||
|
||||
(defn get-label-by-type
|
||||
[biometric-type]
|
||||
(case biometric-type
|
||||
(condp = biometric-type
|
||||
:fingerprint (i18n/label :t/biometric-fingerprint)
|
||||
:FaceID (i18n/label :t/biometric-faceid)
|
||||
(i18n/label :t/biometric-touchid)))
|
||||
|
||||
(defn get-icon-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
:FaceID :i/face-id
|
||||
:i/touch-id))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:biometric/get-supported-biometric-type
|
||||
(fn []
|
||||
@@ -126,3 +132,26 @@
|
||||
{:events [:biometric/authenticate]}
|
||||
[_ opts]
|
||||
{:biometric/authenticate opts})
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/on-enable-success
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-biometric)
|
||||
:dispatch [:keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password password}]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/enable
|
||||
(fn [_ [password]]
|
||||
{:dispatch [:biometric/authenticate
|
||||
{:on-success #(rf/dispatch [:biometric/on-enable-success password])
|
||||
:on-fail #(rf/dispatch [:biometric/show-message %])}]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/disable
|
||||
(fn [{:keys [db]}]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-none)
|
||||
:keychain/clear-user-password key-uid})))
|
||||
|
||||
@@ -95,7 +95,8 @@
|
||||
:button-text (i18n/label :t/confirm)
|
||||
:close-button-text (i18n/label :t/cancel)
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/close-chat chat-id])
|
||||
(hide-sheet-and-dispatch [:chat.ui/close-and-remove-chat
|
||||
chat-id])
|
||||
(when inside-chat?
|
||||
(rf/dispatch [:navigate-back])))}])}]))
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
:keychain/clear-user-password
|
||||
(fn [key-uid]
|
||||
(keychain/reset-credentials (password-migration-key-name key-uid))
|
||||
(keychain/reset-credentials (str key-uid "-auth"))
|
||||
(keychain/reset-credentials key-uid)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@@ -142,6 +143,11 @@
|
||||
(.then #(when on-success (on-success)))
|
||||
(.catch #(when on-error (on-error %))))))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
:keychain/save-password-and-auth-method
|
||||
(fn [_ [opts]]
|
||||
{:keychain/save-password-and-auth-method opts}))
|
||||
|
||||
;; NOTE: migrating the plaintext password in the keychain
|
||||
;; with the hashed one. Added due to the sync onboarding
|
||||
;; flow, where the password arrives already hashed.
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
[legacy.status-im.mailserver.core :as mailserver]
|
||||
[legacy.status-im.visibility-status-updates.core :as visibility-status-updates]
|
||||
[status-im.common.pairing.events :as pairing]
|
||||
[status-im.contexts.chat.messages.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messages.transport.events :as messages.transport]
|
||||
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messenger.messages.transport.events :as messages.transport]
|
||||
[status-im.contexts.communities.discover.events]
|
||||
[status-im.contexts.profile.login.events :as profile.login]
|
||||
[status-im.contexts.profile.push-notifications.local.events :as local-notifications]
|
||||
|
||||
@@ -6,3 +6,8 @@
|
||||
(fn [{:keys [db]} [callback]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:keychain/get-user-password [key-uid callback]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:standard-auth/reset-login-password
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update db :profile/login dissoc :password :error)}))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im.common.standard-authentication.standard-auth.authorize
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[react-native.touch-id :as biometric]
|
||||
[status-im.common.standard-authentication.enter-password.view :as enter-password]
|
||||
[taoensso.timbre :as log]
|
||||
@@ -18,11 +17,11 @@
|
||||
auth-button-label theme blur? auth-button-icon-left]}]
|
||||
(let [handle-auth-success (fn [biometric?]
|
||||
(fn [entered-password]
|
||||
(let [sha3-pwd (if biometric?
|
||||
(str (security/safe-unmask-data entered-password))
|
||||
(native-module/sha3 (str (security/safe-unmask-data
|
||||
entered-password))))]
|
||||
(on-auth-success sha3-pwd))))
|
||||
(let [sha3-masked-password (if biometric?
|
||||
entered-password
|
||||
(security/hash-masked-password
|
||||
entered-password))]
|
||||
(on-auth-success sha3-masked-password))))
|
||||
password-login (fn [{:keys [on-press-biometrics]}]
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
@@ -53,10 +52,12 @@
|
||||
(password-login {:on-press-biometrics
|
||||
#(on-press-biometrics
|
||||
on-press-biometrics)}))}))]
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if (and biometric-auth? biometric-type)
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))))
|
||||
(if biometric-auth?
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if biometric-type
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))
|
||||
(password-login {}))))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- view-internal
|
||||
@@ -15,7 +16,7 @@
|
||||
#(reset! reset-slider? true)
|
||||
200))
|
||||
auth-method (rf/sub [:auth-method])
|
||||
biometric-auth? (= auth-method "biometric")]
|
||||
biometric-auth? (= auth-method constants/auth-method-biometric)]
|
||||
(fn [{:keys [track-text
|
||||
customization-color
|
||||
auth-button-label
|
||||
@@ -25,11 +26,11 @@
|
||||
size
|
||||
theme
|
||||
blur?
|
||||
container-style]}]
|
||||
[rn/view {:style {:flex 1}}
|
||||
container-style]
|
||||
:or {container-style {:flex 1}}}]
|
||||
[rn/view {:style container-style}
|
||||
[quo/slide-button
|
||||
{:size size
|
||||
:container-style container-style
|
||||
:customization-color customization-color
|
||||
:on-reset (when @reset-slider? #(reset! reset-slider? false))
|
||||
:on-complete #(authorize/authorize {:on-close on-close
|
||||
|
||||
@@ -200,24 +200,23 @@
|
||||
(handle-url url))))
|
||||
|
||||
(defn generate-profile-url
|
||||
([cofx] (generate-profile-url cofx nil))
|
||||
([{:keys [db]} [{:keys [public-key cb]}]]
|
||||
(let [profile-public-key (get-in db [:profile/profile :public-key])
|
||||
profile? (or (not public-key) (= public-key profile-public-key))
|
||||
ens-name? (if profile?
|
||||
(get-in db [:profile/profile :ens-name?])
|
||||
(get-in db [:contacts/contacts public-key :ens-name]))
|
||||
public-key (if profile? profile-public-key public-key)]
|
||||
(when public-key
|
||||
{:json-rpc/call
|
||||
[{:method (if ens-name? "wakuext_shareUserURLWithENS" "wakuext_shareUserURLWithData")
|
||||
:params [public-key]
|
||||
:on-success (fn [url]
|
||||
(rf/dispatch [:universal-links/save-profile-url public-key url])
|
||||
(when (fn? cb) (cb)))
|
||||
:on-error #(log/error "failed to wakuext_shareUserURLWithData"
|
||||
{:error %
|
||||
:public-key public-key})}]}))))
|
||||
[{:keys [db]} [{:keys [public-key cb]}]]
|
||||
(let [profile-public-key (get-in db [:profile/profile :public-key])
|
||||
profile? (or (not public-key) (= public-key profile-public-key))
|
||||
ens-name? (if profile?
|
||||
(get-in db [:profile/profile :ens-name?])
|
||||
(get-in db [:contacts/contacts public-key :ens-name]))
|
||||
public-key (if profile? profile-public-key public-key)]
|
||||
(when public-key
|
||||
{:json-rpc/call
|
||||
[{:method (if ens-name? "wakuext_shareUserURLWithENS" "wakuext_shareUserURLWithData")
|
||||
:params [public-key]
|
||||
:on-success (fn [url]
|
||||
(rf/dispatch [:universal-links/save-profile-url public-key url])
|
||||
(when (fn? cb) (cb)))
|
||||
:on-error #(log/error "failed to wakuext_shareUserURLWithData"
|
||||
{:error %
|
||||
:public-key public-key})}]})))
|
||||
|
||||
(schema/=> generate-profile-url
|
||||
[:=>
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.universal-links :as links]))
|
||||
|
||||
(def pubkey
|
||||
"0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")
|
||||
|
||||
(deftest handle-url-test
|
||||
(testing "the user is not logged in"
|
||||
(testing "it stores the url for later processing"
|
||||
(is (= {:db {:universal-links/url "some-url"}}
|
||||
(links/handle-url {:db {}} "some-url")))))
|
||||
(is (match? {:db {:universal-links/url "some-url"}}
|
||||
(links/handle-url {:db {}} "some-url")))))
|
||||
(testing "the user is logged in"
|
||||
(let [db {:profile/profile {:public-key "pk"}
|
||||
:app-state "active"
|
||||
@@ -18,9 +21,9 @@
|
||||
(is (nil? (get-in (links/handle-url {:db db} "some-url")
|
||||
[:db :universal-links/url]))))
|
||||
(testing "Handle a custom string"
|
||||
(is (= (get-in (links/handle-url {:db db} "https://status.app/u#statuse2e")
|
||||
[:router/handle-uri :uri])
|
||||
"https://status.app/u#statuse2e"))))))
|
||||
(is (match? (get-in (links/handle-url {:db db} "https://status.app/u#statuse2e")
|
||||
[:router/handle-uri :uri])
|
||||
"https://status.app/u#statuse2e"))))))
|
||||
|
||||
(deftest url-event-listener
|
||||
(testing "the url is not nil"
|
||||
@@ -28,77 +31,58 @@
|
||||
(let [actual (atom nil)]
|
||||
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||
(links/url-event-listener #js {:url "some-url"})
|
||||
(is (= [:universal-links/handle-url "some-url"] @actual))))))
|
||||
(is (match? [:universal-links/handle-url "some-url"] @actual))))))
|
||||
(testing "the url is nil"
|
||||
(testing "it does not dispatches the url"
|
||||
(let [actual (atom nil)]
|
||||
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||
(links/url-event-listener #js {})
|
||||
(is (= nil @actual)))))))
|
||||
(is (match? nil @actual)))))))
|
||||
|
||||
(deftest generate-profile-url
|
||||
(testing "user has ens name"
|
||||
(testing "it calls the ens rpc method with ens name as param"
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:ens-name? true :public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db})]
|
||||
(are [result expected] (= result expected)
|
||||
(let [db {:profile/profile {:ens-name? true :public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db} [])]
|
||||
(are [result expected] (match? result expected)
|
||||
"wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "user has no ens name"
|
||||
(testing "it calls the ens rpc method with public keyas param"
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db})]
|
||||
(are [result expected] (= result expected)
|
||||
(let [db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db} [])]
|
||||
(are [result expected] (match? result expected)
|
||||
"wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "contact has ens name"
|
||||
(testing "it calls the ens rpc method with ens name as param"
|
||||
(let [pubkey "pubkey"
|
||||
ens "ensname.eth"
|
||||
db {:contacts/contacts {pubkey {:ens-name ens}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (= result expected)
|
||||
(let [ens "ensname.eth"
|
||||
db {:contacts/contacts {pubkey {:ens-name ens}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (match? result expected)
|
||||
"wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "contact has no ens name"
|
||||
(testing "it calls the ens rpc method with public keyas param"
|
||||
(let [pubkey "pubkey"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (= result expected)
|
||||
(let [db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (match? result expected)
|
||||
"wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first))))))
|
||||
|
||||
(deftest save-profile-url
|
||||
(testing "given a contact public key and profile url"
|
||||
(testing "it updates the contact in db"
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (= (get-in rst [:db :contacts/contacts pubkey :universal-profile-url]) url)))))
|
||||
(let [url "url"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (match? (get-in rst [:db :contacts/contacts pubkey :universal-profile-url]) url)))))
|
||||
(testing "given a user public key and profile url"
|
||||
(testing "it updates the user profile in db"
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (= (get-in rst [:db :profile/profile :universal-profile-url]) url)))))
|
||||
(testing "given a invalid url"
|
||||
(testing "it returns the db untouched"
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} ["invalid pubkey" url])]
|
||||
(is (= (:db rst) db)))))
|
||||
(testing "given a nil as url"
|
||||
(testing "it returns nil"
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} ["invalid pubkey"])]
|
||||
(is (nil? rst))))))
|
||||
(let [url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (match? (get-in rst [:db :profile/profile :universal-profile-url]) url))))))
|
||||
|
||||
(deftest universal-link-test
|
||||
(testing "universal-link?"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
(ns status-im.common.validation.profile
|
||||
(:require [clojure.string :as string]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
;; NOTE - validation should match with Desktop
|
||||
;; https://github.com/status-im/status-desktop/blob/2ba96803168461088346bf5030df750cb226df4c/ui/imports/utils/Constants.qml#L468
|
||||
(def min-length 5)
|
||||
(def max-length 24)
|
||||
|
||||
(def emoji-regex
|
||||
#"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])")
|
||||
|
||||
(def status-regex #"^[a-zA-Z0-9\-_ ]+$")
|
||||
|
||||
(def common-names ["Ethereum" "Bitcoin"])
|
||||
|
||||
(defn has-emojis? [s] (boolean (re-find emoji-regex s)))
|
||||
|
||||
(defn has-common-names? [s] (pos? (count (filter #(string/includes? s %) common-names))))
|
||||
|
||||
(defn has-special-characters? [s] (not (re-find status-regex s)))
|
||||
|
||||
(defn name-too-short? [s] (< (count (string/trim (str s))) min-length))
|
||||
|
||||
(defn name-too-long? [s] (> (count (string/trim (str s))) max-length))
|
||||
|
||||
(defn validation-name
|
||||
[s]
|
||||
(cond
|
||||
(or (= s nil) (= s "")) nil
|
||||
(string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"})
|
||||
(string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"})
|
||||
(string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"})
|
||||
(string/starts-with? s " ") (i18n/label :t/start-with-space)
|
||||
(string/ends-with? s " ") (i18n/label :t/ends-with-space)
|
||||
(has-common-names? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)})
|
||||
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
(has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-short? s) (i18n/label :t/minimum-characters {:min-chars min-length})
|
||||
(name-too-long? s) (i18n/label :t/profile-name-is-too-long)))
|
||||
@@ -0,0 +1,52 @@
|
||||
(ns status-im.common.validation.profile-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest are]]
|
||||
[status-im.common.validation.profile :as profile-validator]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(deftest has-emojis-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/has-emojis? arg))
|
||||
"Hello 😊" true?
|
||||
"Hello" false?))
|
||||
|
||||
(deftest has-common-names-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/has-common-names? arg))
|
||||
"Ethereum" true?
|
||||
"Hello" false?))
|
||||
|
||||
(deftest has-special-characters-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/has-special-characters? arg))
|
||||
"@name" true?
|
||||
"name" false?))
|
||||
|
||||
(deftest name-too-short-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/name-too-short? arg))
|
||||
"abc" true?
|
||||
"abcdef" false?))
|
||||
|
||||
(deftest name-too-long-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/name-too-long? arg))
|
||||
(apply str (repeat 25 "a")) true?
|
||||
"abcdef" false?))
|
||||
|
||||
(deftest validation-name-test
|
||||
(are [arg expected]
|
||||
(= (profile-validator/validation-name arg) expected)
|
||||
nil nil
|
||||
"" nil
|
||||
"@name" (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
"name-eth" (i18n/label :t/ending-not-allowed {:ending "-eth"})
|
||||
"name_eth" (i18n/label :t/ending-not-allowed {:ending "_eth"})
|
||||
"name.eth" (i18n/label :t/ending-not-allowed {:ending ".eth"})
|
||||
" name" (i18n/label :t/start-with-space)
|
||||
"name " (i18n/label :t/ends-with-space)
|
||||
"Ethereum" (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)})
|
||||
"Hello 😊" (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
"abc" (i18n/label :t/minimum-characters {:min-chars 5})
|
||||
(apply str (repeat 25 "a")) (i18n/label :t/profile-name-is-too-long)))
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
(def mainnet-rpc-url (str "https://eth-archival.gateway.pokt.network/v1/lb/" POKT_TOKEN))
|
||||
(def goerli-rpc-url (str "https://goerli-archival.gateway.pokt.network/v1/lb/" POKT_TOKEN))
|
||||
(def mainnet-chain-explorer-link "https://etherscan.io/address/")
|
||||
(def optimism-mainnet-chain-explorer-link "https://optimistic.etherscan.io/address/")
|
||||
(def arbitrum-mainnet-chain-explorer-link "https://arbiscan.io/address/")
|
||||
(def opensea-api-key OPENSEA_API_KEY)
|
||||
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
|
||||
(def mailserver-confirmations-enabled? (enabled? (get-config :MAILSERVER_CONFIRMATIONS_ENABLED)))
|
||||
@@ -91,7 +94,7 @@
|
||||
|
||||
(def mainnet-networks
|
||||
[{:id "mainnet_rpc"
|
||||
:chain-explorer-link "https://etherscan.io/address/"
|
||||
:chain-explorer-link mainnet-chain-explorer-link
|
||||
:name "Mainnet with upstream RPC"
|
||||
:config {:NetworkId (chain/chain-keyword->chain-id :mainnet)
|
||||
:DataDir "/ethereum/mainnet_rpc"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.contexts.chat.actions.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -22,10 +23,14 @@
|
||||
:label (i18n/label :t/new-chat)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:group-chat/clear-contacts])
|
||||
(rf/dispatch [:open-modal :start-a-new-chat]))}
|
||||
(debounce/dispatch-and-chill
|
||||
[:open-modal :start-a-new-chat]
|
||||
1000))}
|
||||
{:icon :i/add-user
|
||||
:accessibility-label :add-a-contact
|
||||
:label (i18n/label :t/add-a-contact)
|
||||
:sub-label (i18n/label :t/enter-a-chat-key)
|
||||
:add-divider? true
|
||||
:on-press #(rf/dispatch [:open-modal :new-contact])}]]])
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[:open-modal :new-contact]
|
||||
1000)}]]])
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
(ns status-im.contexts.chat.composer.sub-view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn bar
|
||||
[theme]
|
||||
[rn/view {:style style/bar-container}
|
||||
[rn/view {:style (style/bar theme)}]])
|
||||
|
||||
(defn f-blur-view
|
||||
[{:keys [layout-height focused? theme]}]
|
||||
[reanimated/view {:style (style/blur-container layout-height focused?)}
|
||||
[blur/view (style/blur-view theme)]])
|
||||
|
||||
(defn blur-view
|
||||
[props]
|
||||
[:f> f-blur-view props])
|
||||
|
||||
(defn- f-shell-button
|
||||
[{:keys [focused?]} scroll-to-bottom-fn show-floating-scroll-down-button?]
|
||||
(let [customization-color (rf/sub [:profile/customization-color])
|
||||
hide-shell? (or @focused? @show-floating-scroll-down-button?)
|
||||
y-shell (reanimated/use-shared-value (if hide-shell? 35 0))
|
||||
opacity (reanimated/use-shared-value (if hide-shell? 0 1))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/animate opacity (if hide-shell? 0 1))
|
||||
(reanimated/animate y-shell (if hide-shell? 35 0)))
|
||||
[@focused? @show-floating-scroll-down-button?])
|
||||
[:<>
|
||||
[reanimated/view
|
||||
{:style (style/shell-button y-shell opacity)}
|
||||
[quo/floating-shell-button
|
||||
{:jump-to
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:shell/navigate-to-jump-to]))
|
||||
:customization-color customization-color
|
||||
:label (i18n/label :t/jump-to)
|
||||
:style {:align-self :center}}}
|
||||
{}]]
|
||||
(when (and (not @focused?)
|
||||
@show-floating-scroll-down-button?)
|
||||
[quo/floating-shell-button
|
||||
{:scroll-to-bottom {:on-press scroll-to-bottom-fn}}
|
||||
style/scroll-to-bottom-button])]))
|
||||
|
||||
(defn shell-button
|
||||
[state scroll-to-bottom-fn show-floating-scroll-down-button?]
|
||||
[:f> f-shell-button state scroll-to-bottom-fn show-floating-scroll-down-button?])
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.contexts.chat.effects
|
||||
(:require
|
||||
[react-native.async-storage :as async-storage]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as chat.state]
|
||||
[status-im.contexts.shell.jump-to.constants :as shell.constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -16,3 +17,9 @@
|
||||
(when (= stored-key-uid key-uid)
|
||||
(rf/dispatch [:chat/pop-to-root-and-navigate-to-chat chat-id
|
||||
shell.constants/open-screen-without-animation])))))))))
|
||||
|
||||
(rf/reg-fx :effects.chat/scroll-to-bottom
|
||||
(fn []
|
||||
(some-> ^js @chat.state/messages-list-ref
|
||||
(.scrollToOffset #js
|
||||
{:animated true}))))
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.muting.helpers :refer [format-mute-till]]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.composer.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.contacts.events :as contacts-store]
|
||||
status-im.contexts.chat.effects
|
||||
status-im.contexts.chat.lightbox.events
|
||||
status-im.contexts.chat.messages.content.reactions.events
|
||||
[status-im.contexts.chat.messages.delete-message-for-me.events :as delete-for-me]
|
||||
[status-im.contexts.chat.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messages.list.state :as chat.state]
|
||||
[status-im.contexts.chat.messenger.composer.link-preview.events :as link-preview]
|
||||
status-im.contexts.chat.messenger.lightbox.events
|
||||
status-im.contexts.chat.messenger.messages.content.reactions.events
|
||||
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events :as delete-for-me]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as chat.state]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.datetime :as datetime]
|
||||
@@ -281,7 +281,7 @@
|
||||
|
||||
(rf/defn close-and-remove-chat
|
||||
"Closes the chat and removes it from chat list while retaining history, producing all necessary effects for that"
|
||||
{:events [:chat.ui/close-chat]}
|
||||
{:events [:chat.ui/close-and-remove-chat]}
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(rf/merge cofx
|
||||
{:effects/push-notifications-clear-message-notifications [chat-id]
|
||||
@@ -386,7 +386,7 @@
|
||||
:confirm-button-text (i18n/label :t/delete)
|
||||
:on-accept #(do
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch [:chat.ui/close-chat chat-id]))}})
|
||||
(rf/dispatch [:chat.ui/close-and-remove-chat chat-id]))}})
|
||||
|
||||
(rf/defn navigate-to-user-pinned-messages
|
||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||
@@ -447,3 +447,8 @@
|
||||
:params [{:id chat-id}]
|
||||
:on-success #()
|
||||
:on-error #(log/error "failed to fetch messages for chat" chat-id %)}]})
|
||||
|
||||
(rf/defn scroll-to-bottom
|
||||
{:events [:chat.ui/scroll-to-bottom]}
|
||||
[_]
|
||||
{:effects.chat/scroll-to-bottom nil})
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.new-chat.styles
|
||||
(ns status-im.contexts.chat.home.new-chat.styles
|
||||
(:require
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.new-chat.view
|
||||
(ns status-im.contexts.chat.home.new-chat.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
@@ -10,7 +10,7 @@
|
||||
[status-im.common.contact-list.view :as contact-list]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.new-chat.styles :as style]
|
||||
[status-im.contexts.chat.home.new-chat.styles :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.camera.style
|
||||
(ns status-im.contexts.chat.messenger.camera.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.platform :as platform]
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.camera.view
|
||||
(ns status-im.contexts.chat.messenger.camera.view
|
||||
(:require
|
||||
[oops.core :refer [oget]]
|
||||
[quo.core :as quo]
|
||||
@@ -11,7 +11,7 @@
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.chat.camera.style :as style]
|
||||
[status-im.contexts.chat.messenger.camera.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
(ns status-im.contexts.chat.composer.actions.style
|
||||
(ns status-im.contexts.chat.messenger.composer.actions.style
|
||||
(:require
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.constants :as constants]))
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]))
|
||||
|
||||
(def actions-container
|
||||
{:height constants/actions-container-height
|
||||
+14
-18
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.actions.view
|
||||
(ns status-im.contexts.chat.messenger.composer.actions.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
@@ -9,8 +9,8 @@
|
||||
[status-im.common.alert.effects :as alert.effects]
|
||||
[status-im.common.device-permissions :as device-permissions]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.composer.actions.style :as style]
|
||||
[status-im.contexts.chat.composer.constants :as comp-constants]
|
||||
[status-im.contexts.chat.messenger.composer.actions.style :as style]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as comp-constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
{:keys [text-value focused? maximized?]}
|
||||
{:keys [height saved-height last-height opacity background-y container-opacity]}
|
||||
window-height
|
||||
edit
|
||||
scroll-to-bottom-fn]
|
||||
edit]
|
||||
(reanimated/animate height comp-constants/input-height)
|
||||
(reanimated/set-shared-value saved-height comp-constants/input-height)
|
||||
(reanimated/set-shared-value last-height comp-constants/input-height)
|
||||
@@ -39,11 +38,11 @@
|
||||
(reset! text-value "")
|
||||
(reset! sending-links? false)
|
||||
(reset! sending-images? false)
|
||||
(when (and (not (some? edit)) scroll-to-bottom-fn)
|
||||
(scroll-to-bottom-fn)))
|
||||
(when-not (some? edit)
|
||||
(rf/dispatch [:chat.ui/scroll-to-bottom])))
|
||||
|
||||
(defn f-send-button
|
||||
[props state animations window-height images? btn-opacity scroll-to-bottom-fn z-index edit]
|
||||
[props state animations window-height images? btn-opacity z-index edit]
|
||||
(let [{:keys [text-value]} state
|
||||
customization-color (rf/sub [:profile/customization-color])]
|
||||
(rn/use-effect (fn []
|
||||
@@ -61,19 +60,17 @@
|
||||
[reanimated/view
|
||||
{:style (style/send-button btn-opacity @z-index)}
|
||||
[quo/button
|
||||
{:icon-only? true
|
||||
:size 32
|
||||
{:icon-only? true
|
||||
:size 32
|
||||
:customization-color customization-color
|
||||
:accessibility-label :send-message-button
|
||||
:on-press #(send-message props state animations window-height edit scroll-to-bottom-fn)}
|
||||
:on-press #(send-message props state animations window-height edit)}
|
||||
:i/arrow-up]]))
|
||||
|
||||
(defn send-button
|
||||
[props {:keys [text-value] :as state} animations window-height images? edit btn-opacity
|
||||
scroll-to-bottom-fn]
|
||||
[props {:keys [text-value] :as state} animations window-height images? edit btn-opacity]
|
||||
(let [z-index (reagent/atom (if (and (empty? @text-value) (not images?)) 0 1))]
|
||||
[:f> f-send-button props state animations window-height images? btn-opacity scroll-to-bottom-fn
|
||||
z-index edit]))
|
||||
[:f> f-send-button props state animations window-height images? btn-opacity z-index edit]))
|
||||
|
||||
(defn disabled-audio-button
|
||||
[opacity]
|
||||
@@ -231,7 +228,7 @@
|
||||
:icon :i/format}])
|
||||
|
||||
(defn view
|
||||
[props state animations window-height insets scroll-to-bottom-fn {:keys [edit images]}]
|
||||
[props state animations window-height insets {:keys [edit images]}]
|
||||
(let [send-btn-opacity (reanimated/use-shared-value 0)
|
||||
audio-btn-opacity (reanimated/interpolate send-btn-opacity [0 1] [1 0])]
|
||||
[rn/view {:style style/actions-container}
|
||||
@@ -242,8 +239,7 @@
|
||||
[image-button props animations insets edit]
|
||||
[reaction-button]
|
||||
[format-button]]
|
||||
[:f> send-button props state animations window-height images edit send-btn-opacity
|
||||
scroll-to-bottom-fn]
|
||||
[:f> send-button props state animations window-height images edit send-btn-opacity]
|
||||
(when (and (not edit) (not images))
|
||||
;; TODO(alwx): needs to be replaced with an `audio-button` later. See
|
||||
;; https://github.com/status-im/status-mobile/issues/16084 for more details.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.constants
|
||||
(ns status-im.contexts.chat.messenger.composer.constants
|
||||
(:require
|
||||
[quo.foundations.typography :as typography]
|
||||
[react-native.platform :as platform]))
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.edit.style)
|
||||
(ns status-im.contexts.chat.messenger.composer.edit.style)
|
||||
|
||||
(def container
|
||||
{:flex-direction :row
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
(ns status-im.contexts.chat.composer.edit.view
|
||||
(ns status-im.contexts.chat.messenger.composer.edit.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.constants :as constants]
|
||||
[status-im.contexts.chat.composer.edit.style :as style]
|
||||
[status-im.contexts.chat.composer.utils :as utils]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.composer.edit.style :as style]
|
||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.effects
|
||||
(ns status-im.contexts.chat.messenger.composer.effects
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
@@ -7,9 +7,9 @@
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.chat.composer.constants :as constants]
|
||||
[status-im.contexts.chat.composer.keyboard :as kb]
|
||||
[status-im.contexts.chat.composer.utils :as utils]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.composer.keyboard :as kb]
|
||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
(ns status-im.contexts.chat.composer.events
|
||||
(ns status-im.contexts.chat.messenger.composer.events
|
||||
(:require [clojure.string :as string]
|
||||
[legacy.status-im.chat.models.mentions :as mentions]
|
||||
[legacy.status-im.data-store.messages :as data-store-messages]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.composer.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messages.transport.events :as messages.transport]
|
||||
[status-im.contexts.chat.messenger.composer.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messenger.messages.transport.events :as messages.transport]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.emojilib :as emoji]
|
||||
[utils.i18n :as i18n]
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
(ns status-im.contexts.chat.composer.gesture
|
||||
(ns status-im.contexts.chat.messenger.composer.gesture
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.constants :as constants]
|
||||
[status-im.contexts.chat.composer.utils :as utils]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
(ns status-im.contexts.chat.composer.gradients.style
|
||||
(ns status-im.contexts.chat.messenger.composer.gradients.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.constants :as constants]))
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]))
|
||||
|
||||
(defn- top-gradient-style
|
||||
[opacity z-index showing-extra-space?]
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
(ns status-im.contexts.chat.composer.gradients.view
|
||||
(ns status-im.contexts.chat.messenger.composer.gradients.view
|
||||
(:require
|
||||
[react-native.core :as rn]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.gradients.style :as style]
|
||||
[status-im.contexts.chat.messenger.composer.gradients.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn f-view
|
||||
+11
-11
@@ -1,14 +1,14 @@
|
||||
(ns status-im.contexts.chat.composer.handlers
|
||||
(ns status-im.contexts.chat.messenger.composer.handlers
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.chat.composer.constants :as constants]
|
||||
[status-im.contexts.chat.composer.keyboard :as kb]
|
||||
[status-im.contexts.chat.composer.selection :as selection]
|
||||
[status-im.contexts.chat.composer.utils :as utils]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.composer.keyboard :as kb]
|
||||
[status-im.contexts.chat.messenger.composer.selection :as selection]
|
||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]))
|
||||
@@ -16,11 +16,11 @@
|
||||
(defn focus
|
||||
"Animate to the `saved-height`, display background-overlay if needed, and set cursor position"
|
||||
[{:keys [input-ref] :as props}
|
||||
{:keys [text-value focused? lock-selection? saved-cursor-position]}
|
||||
{:keys [text-value focused? lock-selection? saved-cursor-position composer-focused?]}
|
||||
{:keys [height saved-height last-height opacity background-y container-opacity]
|
||||
:as animations}
|
||||
{:keys [max-height] :as dimensions}
|
||||
show-floating-scroll-down-button?]
|
||||
{:keys [max-height] :as dimensions}]
|
||||
(reanimated/set-shared-value composer-focused? true)
|
||||
(reset! focused? true)
|
||||
(rf/dispatch [:chat.ui/set-input-focused true])
|
||||
(let [last-height-value (reanimated/get-shared-value last-height)]
|
||||
@@ -35,13 +35,12 @@
|
||||
(when (and (not-empty @text-value) @input-ref)
|
||||
(.setNativeProps ^js @input-ref
|
||||
(clj->js {:selection {:start @saved-cursor-position :end @saved-cursor-position}})))
|
||||
(kb/handle-refocus-emoji-kb-ios props animations dimensions)
|
||||
(reset! show-floating-scroll-down-button? false))
|
||||
(kb/handle-refocus-emoji-kb-ios props animations dimensions))
|
||||
|
||||
(defn blur
|
||||
"Save the current height, minimize the composer, animate-out the background, and save cursor position"
|
||||
[{:keys [text-value focused? lock-selection? cursor-position saved-cursor-position gradient-z-index
|
||||
maximized? recording?]}
|
||||
maximized? recording? composer-focused?]}
|
||||
{:keys [height saved-height last-height gradient-opacity container-opacity opacity background-y]}
|
||||
{:keys [content-height max-height window-height]}
|
||||
{:keys [images link-previews? reply]}]
|
||||
@@ -53,6 +52,7 @@
|
||||
max-height
|
||||
content-height
|
||||
saved-height)]
|
||||
(reanimated/set-shared-value composer-focused? false)
|
||||
(reset! focused? false)
|
||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
||||
(reanimated/set-shared-value last-height reopen-height)
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
(ns status-im.contexts.chat.composer.images.style
|
||||
(ns status-im.contexts.chat.messenger.composer.images.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[status-im.contexts.chat.composer.constants :as constants]))
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]))
|
||||
|
||||
(def image-container
|
||||
{:padding-top constants/images-padding-top
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.images.view
|
||||
(ns status-im.contexts.chat.messenger.composer.images.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
@@ -6,8 +6,8 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.constants :as constants]
|
||||
[status-im.contexts.chat.composer.images.style :as style]
|
||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.composer.images.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn image
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
(ns status-im.contexts.chat.composer.keyboard
|
||||
(ns status-im.contexts.chat.messenger.composer.keyboard
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[react-native.async-storage :as async-storage]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.chat.composer.utils :as utils]))
|
||||
[status-im.contexts.chat.messenger.composer.utils :as utils]))
|
||||
|
||||
(defn get-kb-height
|
||||
[curr-height default-height]
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im.contexts.chat.composer.link-preview.events
|
||||
(ns status-im.contexts.chat.messenger.composer.link-preview.events
|
||||
(:require
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
(ns status-im.contexts.chat.composer.link-preview.events-test
|
||||
(ns status-im.contexts.chat.messenger.composer.link-preview.events-test
|
||||
(:require
|
||||
[cljs.test :refer [deftest is testing]]
|
||||
matcher-combinators.test
|
||||
[status-im.contexts.chat.composer.link-preview.events :as events]))
|
||||
[status-im.contexts.chat.messenger.composer.link-preview.events :as events]))
|
||||
|
||||
(def url-github "https://github.com")
|
||||
(def url-gitlab "https://gitlab.com")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user