data_store and network_settings from #1115

missed default networks
fix :load-default-networks
network settings screen
fix network details screen
networks in account
fix network property saving
update startNode and stopNode on iOS
test RPC proxy on iOS
StopNode and StartNode on androd
pass UpstreamConfig to status-go
upgrade status-go
added default network on login
fixed UI, added vector icons
added network name in drawer
migrated accounts
workaround for account v4 migration
This commit is contained in:
Roman Volosovskyi 2017-08-21 17:49:31 +03:00
parent dbb1487fbd
commit cf7a9845c6
44 changed files with 1045 additions and 331 deletions

1
.env
View File

@ -3,3 +3,4 @@ WALLET_WIP_ENABLED=1
NOTIFICATIONS_WIP_ENABLED=1
DEBUG_LOGS_ENABLED=1
STUB_STATUS_GO=0
NETWORK_SWITCHING=0

View File

@ -3,4 +3,4 @@ WALLET_WIP_ENABLED=1
NOTIFICATIONS_WIP_ENABLED=1
DEBUG_LOGS_ENABLED=1
STUB_STATUS_GO=0
NETWORK_SWITCHING=1

View File

@ -104,7 +104,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("gethEvent", params);
}
private void doStartNode() {
private void doStartNode(final String defaultConfig) {
Activity currentActivity = getCurrentActivity();
@ -139,15 +139,52 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
}
String config;
int devCluster = this.debug ? 1 : 0;
String defaultConfig = Statusgo.GenerateConfig(dataFolder, 3, devCluster);
String testnetDataDir = root + "/ethereum/testnet";
String oldKeystoreDir = testnetDataDir + "/keystore";
String newKeystoreDir = root + "/keystore";
final File oldKeystore = new File(oldKeystoreDir);
if (oldKeystore.exists()) {
try {
final File newKeystore = new File(newKeystoreDir);
copyDirectory(oldKeystore, newKeystore);
if (oldKeystore.isDirectory()) {
String[] children = oldKeystore.list();
for (int i = 0; i < children.length; i++) {
new File(oldKeystoreDir, children[i]).delete();
}
}
oldKeystore.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
int dev = 0;
if (this.debug) {
dev = 1;
}
String config = Statusgo.GenerateConfig(testnetDataDir, 3, dev);
try {
JSONObject jsonConfig = new JSONObject(defaultConfig);
JSONObject customConfig = new JSONObject(defaultConfig);
JSONObject jsonConfig = new JSONObject(config);
String gethLogFileName = "geth.log";
jsonConfig.put("LogEnabled", true);
jsonConfig.put("LogEnabled", false);
jsonConfig.put("LogFile", gethLogFileName);
jsonConfig.put("LogLevel", "DEBUG");
jsonConfig.put("DataDir", root + customConfig.get("DataDir"));
jsonConfig.put("NetworkId", customConfig.get("NetworkId"));
try {
Object upstreamConfig = customConfig.get("UpstreamConfig");
if (upstreamConfig != null) {
Log.d(TAG, "UpstreamConfig is not null");
jsonConfig.put("UpstreamConfig", upstreamConfig);
}
} catch (Exception e) {
}
jsonConfig.put("KeyStoreDir", newKeystoreDir);
String gethLogPath = dataFolder + "/" + gethLogFileName;
File logFile = new File(gethLogPath);
if (logFile.exists()) {
@ -176,11 +213,12 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} catch (JSONException e) {
Log.d(TAG, "Something went wrong " + e.getMessage());
Log.d(TAG, "Default configuration will be used");
config = defaultConfig;
}
Statusgo.StartNode(config);
Log.d(TAG, "Node config " + config);
String res = Statusgo.StartNode(config);
Log.d(TAG, "StartNode result: " + res);
Log.d(TAG, "Geth node started");
}
@ -265,24 +303,21 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void startNode(Callback callback) {
public void startNode(final String config) {
Log.d(TAG, "startNode");
status.sendMessage();
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Thread thread = new Thread() {
@Override
public void run() {
doStartNode();
doStartNode(config);
}
};
thread.start();
callback.invoke(false);
}
@ReactMethod
@ -321,6 +356,20 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
thread.start();
}
@ReactMethod
public void stopNode() {
Thread thread = new Thread() {
@Override
public void run() {
Log.d(TAG, "stopNode");
String res = Statusgo.StopNode();
}
};
thread.start();
}
@ReactMethod
public void login(final String address, final String password, final Callback callback) {
Log.d(TAG, "login");

View File

@ -101,157 +101,101 @@ RCT_EXPORT_METHOD(callJail:(NSString *)chatId
});
}
const int STATE_ACTIVE = 0;
const int STATE_LOCKED_WITH_ACTIVE_APP = 1;
const int STATE_BACKGROUND = 2;
const int STATE_LOCKED_WITH_INACTIVE_APP = 3;
int wozniakConstant = STATE_ACTIVE;
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
// the "com.apple.springboard.lockcomplete" notification will always come after the "com.apple.springboard.lockstate" notification
CFStringRef nameCFString = (CFStringRef)name;
NSString *lockState = (__bridge NSString*)nameCFString;
NSLog(@"Darwin notification NAME = %@",name);
NSString* sm = [NSString stringWithFormat:@"%i", wozniakConstant];
NSString *s1 = [NSString stringWithFormat:@"%@ %@", @"LOCK MAGIC", sm];
NSLog(s1);
if([lockState isEqualToString:@"com.apple.springboard.lockcomplete"])
{
NSLog(@"DEVICE LOCKED");
// User locks phone when application is active
if(wozniakConstant == STATE_ACTIVE){
wozniakConstant = STATE_LOCKED_WITH_ACTIVE_APP;
StopNodeRPCServer();
}
// Here lockcomplete event comes when app is unlocked
// because it couldn't come when locking happened
// as application was not active (it could not handle callback)
if (wozniakConstant == STATE_LOCKED_WITH_INACTIVE_APP) {
wozniakConstant = STATE_ACTIVE;
StopNodeRPCServer();
StartNodeRPCServer();
}
}
else
{
NSLog(@"LOCK STATUS CHANGED");
NSString *s = [NSString stringWithFormat:@"%@ %@", @"LOCK", lockState];
NSLog(s);
// if lockstate happens before lockcomplete it means
// that phone was locked when application was not active
if(wozniakConstant == STATE_ACTIVE){
wozniakConstant = STATE_LOCKED_WITH_INACTIVE_APP;
}
if(wozniakConstant == STATE_BACKGROUND){
wozniakConstant = STATE_ACTIVE;
StartNodeRPCServer();
}
// one more lockstate event comes along with lockcomplete
// when phone is locked with active application
if(wozniakConstant == STATE_LOCKED_WITH_ACTIVE_APP){
wozniakConstant = STATE_BACKGROUND;
}
}
}
////////////////////////////////////////////////////////////////////
#pragma mark - startNode
//////////////////////////////////////////////////////////////////// startNode
RCT_EXPORT_METHOD(startNode:(RCTResponseSenderBlock)onResultCallback) {
RCT_EXPORT_METHOD(startNode:(NSString *)configString) {
#if DEBUG
NSLog(@"StartNode() method called");
#endif
if (!isStatusInitialized){
isStatusInitialized = true;
NSError *error = nil;
NSURL *folderName =[[[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
lastObject]
URLByAppendingPathComponent:@"ethereum/testnet"];
if (![[NSFileManager defaultManager] fileExistsAtPath:folderName.path])
[[NSFileManager defaultManager] createDirectoryAtPath:folderName.path withIntermediateDirectories:NO attributes:nil error:&error];
NSURL *flagFolderUrl = [[[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
lastObject]
URLByAppendingPathComponent:@"ropsten_flag"];
if(![[NSFileManager defaultManager] fileExistsAtPath:flagFolderUrl.path]){
NSURL *lightChainData = [folderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"];
[[NSFileManager defaultManager] removeItemAtPath:lightChainData.path
error:nil];
NSString *content = @"";
NSData *fileContents = [content dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createDirectoryAtPath:flagFolderUrl.path
withIntermediateDirectories:NO
attributes:nil
error:&error];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSURL *rootUrl =[[fileManager
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
lastObject];
NSURL *testnetFolderName = [rootUrl URLByAppendingPathComponent:@"ethereum/testnet"];
if (![fileManager fileExistsAtPath:testnetFolderName.path])
[fileManager createDirectoryAtPath:testnetFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
NSURL *flagFolderUrl = [rootUrl URLByAppendingPathComponent:@"ropsten_flag"];
if(![fileManager fileExistsAtPath:flagFolderUrl.path]){
NSLog(@"remove lightchaindata");
NSURL *lightChainData = [testnetFolderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"];
if([fileManager fileExistsAtPath:lightChainData.path]) {
[fileManager removeItemAtPath:lightChainData.path
error:nil];
}
if (error){
NSLog(@"error %@", error);
}else
NSLog(@"folderName: %@", folderName);
#if DEBUG
int devCluster = 1;
[fileManager createDirectoryAtPath:flagFolderUrl.path
withIntermediateDirectories:NO
attributes:nil
error:&error];
}
NSLog(@"after remove lightchaindata");
NSURL *oldKeystoreUrl = [testnetFolderName URLByAppendingPathComponent:@"keystore"];
NSURL *newKeystoreUrl = [rootUrl URLByAppendingPathComponent:@"keystore"];
if([fileManager fileExistsAtPath:oldKeystoreUrl.path]){
NSLog(@"copy keystore");
[fileManager copyItemAtPath:oldKeystoreUrl.path toPath:newKeystoreUrl.path error:nil];
[fileManager removeItemAtPath:oldKeystoreUrl.path error:nil];
}
NSLog(@"after lightChainData");
NSLog(@"preconfig: %@", configString);
NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configJSON = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
int networkId = [configJSON[@"NetworkId"] integerValue];
NSString *dataDir = [configJSON objectForKey:@"DataDir"];
NSString *upstreamURL = [configJSON valueForKeyPath:@"UpstreamConfig.URL"];
NSString *networkDir = [rootUrl.path stringByAppendingString:dataDir];
#ifdef DEBUG
int dev = 1;
#else
int devCluster = 0;
int dev = 0;
#endif
char *configChars = GenerateConfig([folderName.path UTF8String], 3, devCluster);
NSString *config = [NSString stringWithUTF8String: configChars];
NSData *configData = [config dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *resultingConfigJson = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKey:@"LogEnabled"];
[resultingConfigJson setValue:@"geth.log" forKey:@"LogFile"];
[resultingConfigJson setValue:@"DEBUG" forKey:@"LogLevel"];
NSString *resultingConfig = [resultingConfigJson bv_jsonStringWithPrettyPrint:NO];
NSURL *logUrl = [folderName URLByAppendingPathComponent:@"geth.log"];
NSFileManager *manager = [NSFileManager defaultManager];
if([[NSFileManager defaultManager] fileExistsAtPath:logUrl.path]) {
[manager removeItemAtPath:logUrl.path error:nil];
}
if(![manager fileExistsAtPath:folderName.path]) {
[manager createDirectoryAtPath:folderName.path withIntermediateDirectories:YES attributes:nil error:nil];
}
char *configChars = GenerateConfig((char *)[networkDir UTF8String], networkId, dev);
NSString *config = [NSString stringWithUTF8String: configChars];
configData = [config dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *resultingConfigJson = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
[resultingConfigJson setValue:newKeystoreUrl.path forKey:@"KeyStoreDir"];
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKey:@"LogEnabled"];
[resultingConfigJson setValue:@"geth.log" forKey:@"LogFile"];
[resultingConfigJson setValue:@"DEBUG" forKey:@"LogLevel"];
if(upstreamURL != nil) {
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"UpstreamConfig.Enabled"];
[resultingConfigJson setValue:upstreamURL forKeyPath:@"UpstreamConfig.URL"];
}
NSString *resultingConfig = [resultingConfigJson bv_jsonStringWithPrettyPrint:NO];
NSLog(@"node config %@", resultingConfig);
NSURL *networkDirUrl = [NSURL fileURLWithPath:networkDir];
NSURL *logUrl = [networkDirUrl URLByAppendingPathComponent:@"geth.log"];
if([fileManager fileExistsAtPath:logUrl.path]) {
[fileManager removeItemAtPath:logUrl.path error:nil];
}
if(![fileManager fileExistsAtPath:networkDirUrl.path]) {
[fileManager createDirectoryAtPath:networkDirUrl.path withIntermediateDirectories:YES attributes:nil error:nil];
}
NSLog(@"logUrlPath %@", logUrl.path);
if(![fileManager fileExistsAtPath:logUrl.path]) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[NSNumber numberWithInt:511] forKey:NSFilePosixPermissions];
[manager createFileAtPath:logUrl.path contents:nil attributes:dict];
#ifndef DEBUG
[Instabug addFileAttachmentWithURL:[folderName URLByAppendingPathComponent:@"geth.log"]];
#endif
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void) {
StartNode((char *) [resultingConfig UTF8String]);
});
onResultCallback(@[[NSNull null]]);
//Screen lock notifications
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.springboard.lockcomplete"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.springboard.lockstate"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
return;
[fileManager createFileAtPath:logUrl.path contents:nil attributes:dict];
}
#ifndef DEBUG
[Instabug addFileAttachmentWithURL:logUrl];
#endif
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
char *res = StartNode((char *) [resultingConfig UTF8String]);
NSLog(@"StartNode result %@", [NSString stringWithUTF8String: res]); });
}
////////////////////////////////////////////////////////////////////
@ -290,13 +234,19 @@ RCT_EXPORT_METHOD(stopNodeRPCServer) {
StopNodeRPCServer();
}
RCT_EXPORT_METHOD(stopNode:(RCTResponseSenderBlock)callback) {
////////////////////////////////////////////////////////////////////
#pragma mark - StopNode method
//////////////////////////////////////////////////////////////////// StopNode
RCT_EXPORT_METHOD(stopNode) {
#if DEBUG
NSLog(@"stopNode() method called");
NSLog(@"StopNode() method called");
#endif
// TODO: stop node
callback(@[[NSNull null]]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
char *res = StopNode();
NSLog(@"StopNode result %@", [NSString stringWithUTF8String: res]);
});
}
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="">
<path d="M17,17 C17.8284271,17 18.5,16.3284271 18.5,15.5 C18.5,14.6715729 17.8284271,14 17,14 C16.1715729,14 15.5,14.6715729 15.5,15.5 C15.5,16.3284271 16.1715729,17 17,17 Z M17,18.5 C15.3431458,18.5 14,17.1568542 14,15.5 C14,13.8431458 15.3431458,12.5 17,12.5 C18.6568542,12.5 20,13.8431458 20,15.5 C20,17.1568542 18.6568542,18.5 17,18.5 Z M7,17 C7.82842712,17 8.5,16.3284271 8.5,15.5 C8.5,14.6715729 7.82842712,14 7,14 C6.17157288,14 5.5,14.6715729 5.5,15.5 C5.5,16.3284271 6.17157288,17 7,17 Z M7,18.5 C5.34314575,18.5 4,17.1568542 4,15.5 C4,13.8431458 5.34314575,12.5 7,12.5 C8.65685425,12.5 10,13.8431458 10,15.5 C10,17.1568542 8.65685425,18.5 7,18.5 Z M9,14.75 L15,14.75 L15,16.25 L9,16.25 L9,14.75 Z"/>
<path d="M13.299539,7.65049482 C13.7137526,6.93305589 13.4679399,6.01567028 12.7505009,5.60145672 C12.033062,5.18724315 11.1156764,5.43305589 10.7014628,6.15049482 C10.2872493,6.86793376 10.533062,7.78531937 11.2505009,8.19953293 C11.9679399,8.61374649 12.8853255,8.36793376 13.299539,7.65049482 Z M14.5985771,8.40049482 C13.77015,9.83537269 11.9353788,10.3269982 10.5005009,9.49857103 C9.06562306,8.67014391 8.57399759,6.83537269 9.40242471,5.40049482 C10.2308518,3.96561695 12.0656231,3.47399149 13.5005009,4.30241861 C14.9353788,5.13084574 15.4270043,6.96561695 14.5985771,8.40049482 Z M7.35098187,13.4536981 L10.3509819,8.25754563 L11.65002,9.00754563 L8.65001998,14.2036981 L7.35098187,13.4536981 Z M16.65002,13.4536981 L15.3509819,14.2036981 L12.3509819,9.00754563 L13.65002,8.25754563 L16.65002,13.4536981 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -54,7 +54,7 @@ fi
if [ "$device_type" = "genymotion" ]
then
# Find Device based on Android version 6.0.0
device=$(/Applications/Genymotion\ Shell.app/Contents/MacOS/genyshell -c "devices list" | grep "6.0.0")
device=$(/Applications/Genymotion\ Shell.app/Contents/MacOS/genyshell -c "devices list" | grep "6.0.0\|7.0.0")
#echo ${device##*| }
# Launch device in Genymotion
open -a /Applications/Genymotion.app/Contents/MacOS/player.app --args --vm-name "${device##*| }"

View File

@ -145,10 +145,11 @@
:uppercase? platform/android?}
(i18n/label :t/view-all)]]])))
(defn current-network []
[view {:style st/network-label-container}
[text {:style st/network-label} (i18n/label :t/current-network)]
[text {:style st/network-title} "Ropsten"]])
(defview current-network []
(letsubs [network [:get-current-account-network]]
[view {:style st/network-label-container}
[text {:style st/network-label} (i18n/label :t/current-network)]
[text {:style st/network-title} (:name network)]]))
(defn options-btn []
[view {:style st/options-button}

View File

@ -64,7 +64,8 @@
:icons/dropdown-up (slurp-svg "./resources/icons/dropdown_up.svg")
:icons/dropdown (slurp-svg "./resources/icons/dropdown.svg")
:icons/grab (slurp-svg "./resources/icons/grab.svg")
:icons/share (slurp-svg "./resources/icons/share.svg")})
:icons/share (slurp-svg "./resources/icons/share.svg")
:icons/network (slurp-svg "./resources/icons/network.svg")})
(defn normalize-property-name [n]
(if (= n :icons/options)
@ -78,25 +79,25 @@
([name {:keys [color container-style style accessibility-label]
:or {accessibility-label :icon}}]
^{:key name}
[react/view {:style container-style
[react/view {:style container-style
:accessibility-label accessibility-label}
(if-let [icon-fn (get icons (normalize-property-name name))]
(into []
(concat
[svg (merge default-viewbox style)]
(icon-fn
(cond
(keyword? color)
(case color
:dark styles/icon-dark-color
:gray styles/icon-gray-color
:blue styles/color-light-blue
:active styles/color-blue4
:white styles/color-white
:red styles/icon-red-color
styles/icon-dark-color)
(string? color)
color
:else
styles/icon-dark-color))))
[svg (merge default-viewbox style)]
(icon-fn
(cond
(keyword? color)
(case color
:dark styles/icon-dark-color
:gray styles/icon-gray-color
:blue styles/color-light-blue
:active styles/color-blue4
:white styles/color-white
:red styles/icon-red-color
styles/icon-dark-color)
(string? color)
color
:else
styles/icon-dark-color))))
(throw (js/Error. (str "Unknown icon: " name))))]))

View File

@ -1,4 +1,5 @@
(ns status-im.constants)
(ns status-im.constants
(:require [status-im.utils.types :as types]))
(def ethereum-rpc-url "http://localhost:8545")
@ -26,3 +27,28 @@
(def console-chat-id "console")
(def wallet-chat-id "wallet")
(def default-network "testnet_rpc")
(def default-networks
{"mainnet" {:id "mainnet",
:name "Mainnet",
:config (types/clj->json
{:NetworkId 1
:DataDir "/ethereum/mainnet"})}
"testnet" {:id "testnet",
:name "Ropsten",
:config (types/clj->json
{:NetworkId 3
:DataDir "/ethereum/testnet"})}
"testnet_rpc" {:id "testnet_rpc",
:name "Ropsten with RPC",
:config (types/clj->json
{:NetworkId 3
:DataDir "/ethereum/testnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"}})}
"rinkeby" {:id "rinkeby",
:name "Rinkeby",
:config (types/clj->json
{:NetworkId 4
:DataDir "/ethereum/rinkeby"})}})

View File

@ -9,6 +9,3 @@
(defn save [account update?]
(data-store/save account update?))
(defn save-all [accounts update?]
(data-store/save-all accounts update?))

View File

@ -0,0 +1,22 @@
(ns status-im.data-store.networks
(:require [status-im.data-store.realm.networks :as data-store]))
(defn get-all
[]
(data-store/get-all-as-list))
(defn save
[{:keys [id] :as network}]
(data-store/save network (data-store/exists? id)))
(defn save-all
[networks]
(mapv save networks))
(defn save-property
[id property-name value]
(data-store/save-property id property-name value))
(defn delete
[id]
(data-store/delete id))

View File

@ -1,20 +1,25 @@
(ns status-im.data-store.realm.accounts
(:require [status-im.data-store.realm.core :as realm]))
(defn- reformat-networks [account]
(update account :networks
(fn [networks]
(into {}
(map (fn [{:keys [id] :as network}]
[id network])
(vals (js->clj networks :keywordize-keys true)))))))
(defn get-all []
(realm/get-all realm/base-realm :account))
(defn get-all-as-list []
(realm/realm-collection->list (get-all)))
(map reformat-networks (realm/realm-collection->list (get-all))))
(defn get-by-address [address]
(realm/get-one-by-field-clj realm/base-realm :account :address address))
(reformat-networks
(realm/get-one-by-field-clj realm/base-realm :account :address address)))
(defn save [account update?]
(realm/write realm/base-realm
#(realm/create realm/base-realm :account account update?)))
(defn save-all [accounts update?]
(realm/write realm/base-realm
(fn []
(mapv #(realm/create realm/base-realm :account % update?) accounts))))
(let [account' (update account :networks (comp vec vals))]
#(realm/create realm/base-realm :account account' update?))))

View File

@ -0,0 +1,33 @@
(ns status-im.data-store.realm.networks
(:require [status-im.data-store.realm.core :as realm])
(:refer-clojure :exclude [exists?]))
(defn get-all
[]
(-> @realm/account-realm
(realm/get-all :network)))
(defn get-all-as-list
[]
(realm/realm-collection->list (get-all)))
(defn save
[network update?]
(realm/save @realm/account-realm :network network update?))
(defn save-property
[id property-name value]
(realm/write @realm/account-realm
(fn []
(-> @realm/account-realm
(realm/get-one-by-field :network :id id)
(aset (name property-name) value)))))
(defn exists?
[id]
(realm/exists? @realm/account-realm :network {:id id}))
(defn delete
[id]
(when-let [network (realm/get-one-by-field @realm/account-realm :network :id id)]
(realm/delete @realm/account-realm network)))

View File

@ -1,7 +1,8 @@
(ns status-im.data-store.realm.schemas.base.core
(:require [status-im.data-store.realm.schemas.base.v1.core :as v1]
[status-im.data-store.realm.schemas.base.v2.core :as v2]
[status-im.data-store.realm.schemas.base.v3.core :as v3]))
[status-im.data-store.realm.schemas.base.v3.core :as v3]
[status-im.data-store.realm.schemas.base.v4.core :as v4]))
; put schemas ordered by version
(def schemas [{:schema v1/schema
@ -12,4 +13,7 @@
:migration v2/migration}
{:schema v3/schema
:schemaVersion 3
:migration v3/migration}])
:migration v3/migration}
{:schema v4/schema
:schemaVersion 4
:migration v4/migration}])

View File

@ -24,7 +24,6 @@
(defn migration [_old-realm new-realm]
(log/debug "migrating account schema v3")
;; make sure that console chat has `:unremovable?` set to true
(let [accounts (.objects new-realm "account")]
(dotimes [i (.-length accounts)]
(let [account (aget accounts i)

View File

@ -0,0 +1,32 @@
(ns status-im.data-store.realm.schemas.base.v4.account
(:require [taoensso.timbre :as log]
[status-im.constants :as constants]))
(def schema {:name :account
:primaryKey :address
:properties {:address :string
:public-key :string
:updates-public-key {:type :string
:optional true}
:updates-private-key {:type :string
:optional true}
:name {:type :string :optional true}
:phone {:type :string :optional true}
:email {:type :string :optional true}
:status {:type :string :optional true}
:debug? {:type :bool :default false}
:photo-path :string
:signing-phrase {:type :string}
:last-updated {:type :int :default 0}
:signed-up? {:type :bool
:default false}
:network :string
:networks {:type :list
:objectType :network}}})
(defn migration [_old-realm new-realm]
(log/debug "migrating account schema v4")
(let [accounts (.objects new-realm "account")]
(dotimes [i (.-length accounts)]
(let [account (aget accounts i)]
(aset account "network" constants/default-network)))))

View File

@ -0,0 +1,16 @@
(ns status-im.data-store.realm.schemas.base.v4.core
(:require [status-im.data-store.realm.schemas.base.v4.network :as network]
[status-im.data-store.realm.schemas.base.v4.account :as account]
[status-im.data-store.realm.schemas.base.v1.kv-store :as kv-store]
[taoensso.timbre :as log]))
(def schema [network/schema
account/schema
kv-store/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v4 base database: " old-realm new-realm)
(network/migration old-realm new-realm)
(account/migration old-realm new-realm))

View File

@ -0,0 +1,15 @@
(ns status-im.data-store.realm.schemas.base.v4.network
(:require [taoensso.timbre :as log]))
(def schema {:name :network
:primaryKey :id
:properties {:id :string
:name {:type :string
:optional true}
:config {:type :string
:optional true}
:rpc-url {:type :string
:optional true}}})
(defn migration [old-realm new-realm]
(log/debug "migrating network schema v4"))

View File

@ -28,8 +28,12 @@
(module-interface/-move-to-internal-storage rns-module callback))
(defn start-node [callback]
(module-interface/-start-node rns-module callback))
(defn start-node [config]
(module-interface/-start-node rns-module config))
(defn stop-node []
(module-interface/-stop-node rns-module))
(defn stop-rpc-server []

View File

@ -72,9 +72,14 @@
(when status
(call-module #(.moveToInternalStorage status on-result))))
(defn start-node [on-result]
(defn stop-node []
(when status
(call-module #(.startNode status on-result))))
(call-module #(.stopNode status))))
(defn start-node [config]
(when status
(call-module #(.startNode status config))))
(defn stop-rpc-server []
(when status
@ -190,8 +195,10 @@
;; status-go calls
(-init-jail [this]
(init-jail))
(-start-node [this callback]
(start-node callback))
(-start-node [this config]
(start-node config))
(-stop-node [this]
(stop-node))
(-stop-rpc-server [this]
(stop-rpc-server))
(-start-rpc-server [this]

View File

@ -12,9 +12,10 @@
module/IReactNativeStatus
;; status-go calls
(-init-jail [this])
(-start-node [this callback]
(-start-node [this config]
(re-frame/dispatch [:signal-event "{\"type\":\"node.started\",\"event\":{}}"])
(re-frame/dispatch [:signal-event "{\"type\":\"node.ready\",\"event\":{}}"]))
(-stop-node [this])
(-stop-rpc-server [this])
(-start-rpc-server [this])
(-restart-rpc [this])

View File

@ -3,7 +3,8 @@
(defprotocol IReactNativeStatus
(-init-jail [this])
(-move-to-internal-storage [this callback])
(-start-node [this callback])
(-start-node [this config])
(-stop-node [this])
(-stop-rpc-server [this])
(-start-rpc-server [this])
(-restart-rpc [this])

View File

@ -373,4 +373,24 @@
:transactions-filter-title "Filter History"
:transactions-filter-tokens "Tokens"
:transactions-filter-type "Type"
:transactions-filter-select-all "Select all"})
:transactions-filter-select-all "Select all"
;network settings
:new-network "New network"
:add-network "Add network"
:add-new-network "Add new network"
:existing-networks "Existing networks"
:add-json-file "Add a JSON file"
:paste-json-as-text "Paste JSON as text"
:paste-json "Paste JSON"
:specify-rpc-url "Specify a RPC URL"
:edit-rpc-url "Edit a RPC URL"
:edit-network-config "Edit network config"
:connected "Connected"
:process-json "Process JSON"
:error-processing-json "Error processing JSON"
:rpc-url "RPC URL"
:remove-network "Remove network"
:network-settings "Network settings"
:edit-network-warning "Be careful, editing the network data may disable this network for you"
:connecting-requires-login "Connecting to another network requires login"})

View File

@ -1,7 +1,8 @@
(ns status-im.ui.screens.accounts.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]
status-im.utils.db))
status-im.utils.db
status-im.ui.screens.network-settings.db))
(spec/def :account/address :global/address)
(spec/def :account/name :global/not-empty-string)
@ -16,6 +17,7 @@
(spec/def :account/debug? (spec/nilable boolean?))
(spec/def :account/status (spec/nilable string?))
(spec/def :account/network (spec/nilable string?))
(spec/def :account/networks (spec/nilable :networks/networks))
(spec/def :account/phone (spec/nilable string?))
(spec/def :account/signing-phrase :global/not-empty-string)
@ -25,7 +27,7 @@
:opt-un [:account/debug? :account/status :account/last-updated
:account/updates-private-key :account/updates-public-key
:account/email :account/signed-up? :account/network
:account/phone]))
:account/phone :account/networks]))
(spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account)))

View File

@ -96,11 +96,16 @@
(register-handler-fx
:add-account
(fn [{{:keys [network] :as db} :db} [_ {:keys [address] :as account} password]]
(let [account' (assoc account :network network)]
{:db (assoc-in db [:accounts/accounts address] account')
::save-account account'
:dispatch-later [{:ms 400 :dispatch [:login-account address password true]}]})))
(fn [{{:keys [network]
:networks/keys [networks]
:as db} :db} [_ {:keys [address] :as account} password]]
(let [account' (assoc account :network network
:networks networks)]
(merge
{:db (assoc-in db [:accounts/accounts address] account')
::save-account account'}
(when password
{:dispatch-later [{:ms 400 :dispatch [:login-account address password true]}]})))))
(register-handler-fx
:create-account
@ -123,8 +128,21 @@
(let [accounts (->> all-accounts
(map (fn [{:keys [address] :as account}]
[address account]))
(into {}))]
{:db (assoc db :accounts/accounts accounts)})))
(into {}))
;;workaround for realm bug, migrating account v4
events (mapv #(when (empty? (:networks %)) [:account-update-networks (:address %)]) (vals accounts))]
(merge
{:db (assoc db :accounts/accounts accounts)}
(when-not (empty? events)
{:dispatch-n events})))))
(register-handler-fx
:account-update-networks
(fn [{{:accounts/keys [accounts] :networks/keys [networks] :as db} :db} [_ id]]
(let [current-account (get accounts id)
new-account (assoc current-account :networks networks)]
{:db (assoc-in db [:accounts/accounts id] new-account)
::save-account new-account})))
(register-handler-fx
:check-status-change
@ -142,11 +160,9 @@
(register-handler-fx
:account-update
(fn [{{:keys [network]
:accounts/keys [accounts current-account-id] :as db} :db} [_ new-account-fields]]
(let [current-account (get accounts current-account-id)
new-account-fields' (assoc new-account-fields :network (or (:network current-account) network))
new-account (update-account current-account new-account-fields')]
(fn [{{:accounts/keys [accounts current-account-id] :as db} :db} [_ new-account-fields]]
(let [current-account (get accounts current-account-id)
new-account (update-account current-account new-account-fields)]
{:db (assoc-in db [:accounts/accounts current-account-id] new-account)
::save-account new-account
::broadcast-account-update (merge
@ -161,8 +177,8 @@
(let [{:accounts/keys [accounts current-account-id]} db
{:keys [public private]} keypair
current-account (get accounts current-account-id)
new-account (update-account current-account {:updates-public-key public
:updates-private-key private})]
new-account (update-account current-account {:updates-public-key public
:updates-private-key private})]
{:db (assoc-in db [:accounts/accounts current-account-id] new-account)
::save-account new-account
::send-keys-update (merge
@ -174,7 +190,7 @@
:send-account-update-if-needed
(fn [{{:accounts/keys [accounts current-account-id]} :db} _]
(let [{:keys [last-updated]} (get accounts current-account-id)
now (time/now-ms)
now (time/now-ms)
needs-update? (> (- now last-updated) time/week)]
(log/info "Need to send account-update: " needs-update?)
(when needs-update?

View File

@ -12,6 +12,9 @@
;;;; FX
(reg-fx ::stop-node (fn [] (status/stop-node)))
(reg-fx
::login
(fn [[address password]]
@ -40,27 +43,73 @@
:name name)
:dispatch [:navigate-to :login]}))
(defn wrap-with-login-account-fx [db address password]
{:db db
::login [address password]})
(register-handler-fx
::login-account
(fn [{db :db} [_ address password]]
(wrap-with-login-account-fx
(assoc db :node/after-start nil)
address password)))
(defn get-network-by-address [db address]
(let [accounts (get db :accounts/accounts)
{:keys [network networks]} (get accounts address)
config (get-in networks [network :config])]
{:network network
:config config}))
(defn wrap-with-initialize-geth-fx [db address password]
(let [{:keys [network config]} (get-network-by-address db address)]
{:initialize-geth-fx config
:db (assoc db :network network
:node/after-start [::login-account address password])}))
(register-handler-fx
::start-node
(fn [{db :db} [_ address password]]
(wrap-with-initialize-geth-fx
(assoc db :node/after-stop nil)
address password)))
(defn wrap-with-stop-node-fx [db address password]
{:db (assoc db :node/after-stop [::start-node address password])
::stop-node nil})
(register-handler-fx
:login-account
(fn [{db :db} [_ address password account-creation?]]
{:db (-> db
(assoc :accounts/account-creation? account-creation?)
(assoc-in [:accounts/login :processing] true))
::login [address password]}))
(fn [{{:keys [network status-node-started?] :as db} :db}
[_ address password account-creation?]]
(let [{account-network :network} (get-network-by-address db address)
db' (-> db
(dissoc :db)
(assoc :accounts/account-creation? account-creation?)
(assoc-in [:accounts/login :processing] true))
wrap-fn (cond (not status-node-started?)
wrap-with-initialize-geth-fx
(= account-network network)
wrap-with-login-account-fx
:else
wrap-with-stop-node-fx)]
(wrap-fn db' address password))))
(register-handler-fx
:login-handler
(fn [{db :db} [_ result address]]
(let [data (json->clj result)
error (:error data)
(let [data (json->clj result)
error (:error data)
success (zero? (count error))
db' (assoc-in db [:accounts/login :processing] false)]
(log/debug "Logged in account: ")
db' (assoc-in db [:accounts/login :processing] false)]
(log/debug "Logged in account: " result)
(merge
{:db (if success db' (assoc-in db' [:accounts/login :error] error))}
(when success
(let [is-login-screen? (= (:view-id db) :login)
new-account? (not is-login-screen?)]
new-account? (not is-login-screen?)]
(log/debug "Logged in: " (:view-id db) is-login-screen? new-account?)
{::clear-web-data nil
::change-account [address new-account?]}))))))

View File

@ -1,6 +1,6 @@
(ns status-im.ui.screens.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [status-im.constants :refer [console-chat-id]]
(:require [status-im.constants :as constants]
[status-im.utils.platform :as platform]
[cljs.spec.alpha :as spec]
status-im.ui.screens.accounts.db
@ -11,7 +11,8 @@
status-im.chat.new-public-chat.db
status-im.ui.screens.profile.db
status-im.transactions.specs
status-im.ui.screens.discover.db))
status-im.ui.screens.discover.db
status-im.ui.screens.network-settings.db))
;; initial state of app-db
(def app-db {:current-public-key ""
@ -24,7 +25,7 @@
:group/contact-groups {}
:group/selected-contacts #{}
:chats {}
:current-chat-id console-chat-id
:current-chat-id constants/console-chat-id
:loading-allowed true
:selected-participants #{}
:discoveries {}
@ -34,7 +35,8 @@
:wallet {}
:prices {}
:notifications {}
:network "testnet"})
:network constants/default-network
:networks/networks constants/default-networks})
;;;;GLOBAL
@ -79,6 +81,11 @@
(spec/def ::network (spec/nilable string?))
;;;;NODE
(spec/def :node/after-start (spec/nilable vector?))
(spec/def :node/after-stop (spec/nilable vector?))
(spec/def ::db (allowed-keys
:opt
[:contacts/contacts
@ -104,7 +111,11 @@
:my-profile/drawer
:my-profile/profile
:my-profile/default-name
:wallet/request-transaction]
:wallet/request-transaction
:networks/selected-network
:networks/networks
:node/after-start
:node/after-stop]
:opt-un
[::current-public-key
::modal

View File

@ -13,6 +13,7 @@
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.group.events
status-im.ui.screens.navigation
status-im.ui.screens.network-settings.events
status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events
status-im.ui.screens.wallet.events
@ -47,33 +48,29 @@
::initialize-crypt-fx
(fn []
(crypt/gen-random-bytes
1024
(fn [{:keys [error buffer]}]
(if error
(log/error "Failed to generate random bytes to initialize sjcl crypto")
(->> (.toString buffer "hex")
(.toBits (.. dependencies/eccjs -sjcl -codec -hex))
(.addEntropy (.. dependencies/eccjs -sjcl -random))))))))
1024
(fn [{:keys [error buffer]}]
(if error
(log/error "Failed to generate random bytes to initialize sjcl crypto")
(->> (.toString buffer "hex")
(.toBits (.. dependencies/eccjs -sjcl -codec -hex))
(.addEntropy (.. dependencies/eccjs -sjcl -random))))))))
(defn node-started [result]
(log/debug "Started Node"))
(defn move-to-internal-storage []
(defn move-to-internal-storage [config]
(status/move-to-internal-storage
(fn []
(status/start-node node-started))))
#(status/start-node config)))
(reg-fx
::initialize-geth-fx
(fn []
:initialize-geth-fx
(fn [config]
(status/should-move-to-internal-storage?
(fn [should-move?]
(if should-move?
(dispatch [:request-permissions
[:read-external-storage]
#(move-to-internal-storage)
#(dispatch [:move-to-internal-failure-message])])
(status/start-node node-started))))))
(fn [should-move?]
(if should-move?
(dispatch [:request-permissions
[:read-external-storage]
#(move-to-internal-storage config)
#(dispatch [:move-to-internal-failure-message])])
(status/start-node config))))))
(reg-fx
::status-module-initialized-fx
@ -100,8 +97,8 @@
(fn []
(when config/testfairy-enabled?
(utils/show-popup
(i18n/label :testfairy-title)
(i18n/label :testfairy-message)))))
(i18n/label :testfairy-title)
(i18n/label :testfairy-message)))))
(reg-fx
::get-fcm-token-fx
@ -133,16 +130,18 @@
(register-handler-fx
:initialize-db
(fn [{{:keys [status-module-initialized? status-node-started?
network-status network _]} :db} _]
{::init-store nil
:db (assoc app-db
:accounts/current-account-id nil
:contacts/contacts {}
:network-status network-status
:status-module-initialized? (or platform/ios? js/goog.DEBUG status-module-initialized?)
:status-node-started? status-node-started?
:network (or network "testnet"))}))
(fn [{{:keys [status-module-initialized? status-node-started?
network-status network _]
:networks/keys [networks]} :db} _]
(let [network' (or network (get app-db :network))]
{::init-store nil
:db (assoc app-db
:accounts/current-account-id nil
:contacts/contacts {}
:network-status network-status
:status-module-initialized? (or platform/ios? js/goog.DEBUG status-module-initialized?)
:status-node-started? status-node-started?
:network network')})))
(register-handler-db
:initialize-account-db
@ -181,14 +180,14 @@
:chat
:accounts)]
(merge
{:db (assoc db :view-id view
:navigation-stack (list view))}
(when (or (empty? accounts) open-console?)
{:dispatch-n (concat
[[:init-console-chat]
[:load-commands!]]
(when open-console?
[[:navigate-to :chat console-chat-id]]))})))))
{:db (assoc db :view-id view
:navigation-stack (list view))}
(when (or (empty? accounts) open-console?)
{:dispatch-n (concat
[[:init-console-chat]
[:load-commands!]]
(when open-console?
[[:navigate-to :chat console-chat-id]]))})))))
(register-handler-fx
:initialize-crypt
@ -197,8 +196,14 @@
(register-handler-fx
:initialize-geth
(fn [_ _]
{::initialize-geth-fx nil}))
(fn [{db :db} _]
(let [{:accounts/keys [current-account-id accounts]} db
default-networks (:networks/networks db)
default-network (:network db)
{:keys [network networks]} (get accounts current-account-id)
network-config (or (get-in networks [network :config])
(get-in default-networks [default-network :config]))]
{:initialize-geth-fx network-config})))
(register-handler-fx
:webview-geo-permissions-granted
@ -235,6 +240,7 @@
"transaction.queued" (dispatch [:transaction-queued event])
"transaction.failed" (dispatch [:transaction-failed event])
"node.started" (dispatch [:status-node-started])
"node.stopped" (dispatch [:status-node-stopped])
"module.initialized" (dispatch [:status-module-initialized])
"request_geo_permissions" (dispatch [:request-permissions [:geolocation]
#(dispatch [:webview-geo-permissions-granted])])
@ -244,13 +250,19 @@
(register-handler-fx
:status-module-initialized
(fn [{:keys [db]} _]
{:db (assoc db :status-module-initialized? true)
{:db (assoc db :status-module-initialized? true)
::status-module-initialized-fx nil}))
(register-handler-db
(register-handler-fx
:status-node-started
(fn [db]
(assoc db :status-node-started? true)))
(fn [{{:node/keys [after-start] :as db} :db}]
(merge {:db (assoc db :status-node-started? true)}
(when after-start {:dispatch-n [after-start]}))))
(register-handler-fx
:status-node-stopped
(fn [{{:node/keys [after-stop]} :db}]
(when after-stop {:dispatch-n [after-stop]})))
(register-handler-fx
:app-state-change
@ -272,19 +284,19 @@
(fn []
(let [watch-id (atom nil)]
(.getCurrentPosition
navigator.geolocation
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
(clj->js {:enableHighAccuracy true :timeout 20000 :maximumAge 1000}))
navigator.geolocation
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
#(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])
(clj->js {:enableHighAccuracy true :timeout 20000 :maximumAge 1000}))
(when platform/android?
(reset! watch-id
(.watchPosition
navigator.geolocation
#(do
(.clearWatch
navigator.geolocation
@watch-id)
(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])))))))]}))
navigator.geolocation
#(do
(.clearWatch
navigator.geolocation
@watch-id)
(dispatch [:update-geolocation (js->clj % :keywordize-keys true)])))))))]}))
(register-handler-db
:update-geolocation

View File

@ -0,0 +1,23 @@
(ns status-im.ui.screens.network-settings.add-rpc.views
(:require
[re-frame.core :refer [dispatch]]
[status-im.components.status-bar :as status-bar]
[status-im.components.toolbar-new.view :as toolbar-new]
[status-im.components.text-input-with-label.view :refer [text-input-with-label]]
[status-im.ui.screens.network-settings.views :as network-settings]
[status-im.components.react :as react]
[status-im.components.sticky-button :as sticky-button]
[status-im.i18n :as i18n]
[clojure.string :as str]))
(defn add-rpc-url []
(let [rpc-url "text"]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar-new/toolbar {:title (i18n/label :t/add-network)}]
[network-settings/network-badge]
[react/view {:margin-top 8}
[text-input-with-label {:label (i18n/label :t/rpc-url)}]]
[react/view {:flex 1}]
(when (not (str/blank? rpc-url))
[sticky-button/sticky-button (i18n/label :t/add-network) #()])]))

View File

@ -0,0 +1,14 @@
(ns status-im.ui.screens.network-settings.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]))
(spec/def :networks/id string?)
(spec/def :networks/name string?)
(spec/def :networks/config string?)
(spec/def :networks/network
(spec/keys :req-un [:networks/id :networks/name :networks/config]))
(spec/def :networks/selected-network :networks/network)
(spec/def :networks/networks (spec/nilable (spec/map-of :networks/id :networks/network)))

View File

@ -0,0 +1,33 @@
(ns status-im.ui.screens.network-settings.events
(:require [re-frame.core :refer [dispatch dispatch-sync after] :as re-frame]
[status-im.utils.handlers :refer [register-handler] :as handlers]
[status-im.data-store.networks :as networks]
[status-im.ui.screens.network-settings.navigation]))
;;;; FX
(re-frame/reg-fx
::save-networks
(fn [networks]
(networks/save-all networks)))
;; handlers
(handlers/register-handler-fx
:add-networks
(fn [{{:networks/keys [networks] :as db} :db} [_ new-networks]]
(let [identities (set (keys networks))
new-networks' (->> new-networks
(remove #(identities (:id %)))
(map #(vector (:id %) %))
(into {}))]
{:db (-> db
(update :networks merge new-networks')
(assoc :new-networks (vals new-networks')))
:save-networks new-networks'})))
(handlers/register-handler-fx
:connect-network
(fn [_ [_ network]]
{:dispatch-n [[:account-update {:network network}]
[:navigate-to-clean :accounts]]}))

View File

@ -0,0 +1,6 @@
(ns status-im.ui.screens.network-settings.navigation
(:require [status-im.ui.screens.navigation :as navigation]))
(defmethod navigation/preload-data! :network-details
[db [_ _ network]]
(assoc db :networks/selected-network network))

View File

@ -0,0 +1,54 @@
(ns status-im.ui.screens.network-settings.network-details.views
(:require-macros [status-im.utils.views :as views])
(:require
[re-frame.core :as rf]
[status-im.components.status-bar :as status-bar]
[status-im.components.toolbar-new.view :as new-toolbar]
[status-im.components.context-menu :as context-menu]
[status-im.ui.screens.network-settings.views :as network-settings]
[status-im.components.react :as react]
[status-im.utils.platform :as platform]
[status-im.i18n :as i18n]
[status-im.ui.screens.network-settings.styles :as st]))
(def options
[{:text (i18n/label :t/add-json-file)
:value #(rf/dispatch [:network-add-json-file])}
{:text (i18n/label :t/paste-json-as-text)
:value #(rf/dispatch [:network-paste-json-as-text])}
{:text (i18n/label :t/:edit-rpc-url)
:value #(rf/dispatch [:network-edit-rpc-url])}
{:text (i18n/label :t/:remove-network)
:value #(rf/dispatch [:network-remove])}])
(views/defview network-details []
(views/letsubs [{:keys [id name config]} [:get :networks/selected-network]
{:keys [network]} [:get-current-account]]
(let [connected? (= id network)]
[react/view {:flex 1}
[status-bar/status-bar]
[new-toolbar/toolbar]
(when-not connected?
[react/touchable-highlight {:on-press #(rf/dispatch [:connect-network id])}
[react/view st/connect-button-container
[react/view st/connect-button
[react/text {:style st/connect-button-label
:uppercase? (get-in platform/platform-specific [:uppercase?])}
(i18n/label :t/connect)]]
[react/text {:style st/connect-button-description}
(i18n/label :t/connecting-requires-login)]]])
[react/view st/network-config-container
[react/text {:style st/network-config-text}
config]]
[react/view {:opacity 0.4}
[react/view st/edit-button-container
[react/view st/edit-button
[react/text {:style st/edit-button-label
:uppercase? (get-in platform/platform-specific [:uppercase?])}
(i18n/label :t/edit-network-config)]]
#_[context-menu ; TODO should be implemented later
[view st/edit-button
[text {:style st/edit-button-label} (i18n/label :t/edit-network-config)]]
options]
[react/text {:style st/edit-button-description}
(i18n/label :t/edit-network-warning)]]]])))

View File

@ -0,0 +1,31 @@
(ns status-im.ui.screens.network-settings.parse-json.views
(:require
[re-frame.core :refer [dispatch]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar-new.view :refer [toolbar]]
[status-im.ui.screens.network-settings.views :as network-settings]
[status-im.components.react :refer [view text text-input]]
[status-im.components.sticky-button :as sticky-button]
[status-im.ui.screens.network-settings.styles :as st]
[status-im.i18n :as i18n]
[clojure.string :as str]))
(defn paste-json-text []
(let [network-json "test"
error nil]
[view {:flex 1}
[status-bar]
[toolbar {:title (i18n/label :t/add-network)}]
[network-settings/network-badge]
[view {:margin-top 16
:margin-left 16
:flex 1}
(when error
[text {:style {:color :red}}
(i18n/label :t/error-processing-json)])
[text-input {:style st/paste-json-text-input
:flex 1
:placeholder (i18n/label :t/paste-json)
:multiline true}]]
(when (not (str/blank? network-json))
[sticky-button/sticky-button (i18n/label :t/process-json) #()])]))

View File

@ -0,0 +1,144 @@
(ns status-im.ui.screens.network-settings.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.components.styles :as common]))
(def networks-list
{:background-color common/color-light-gray})
(defstyle badge-name-text
{:color common/color-black
:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 16}})
(defstyle badge-connected-text
{:color common/color-gray4
:ios {:margin-top 5
:font-size 14
:letter-spacing -0.2}
:android {:font-size 13}})
(defstyle paste-json-text-input
{:ios {:font-size 17
:line-height 24
:letter-spacing -0.2}})
(def connect-button-container
{:margin-top 8
:align-items :center
:margin-bottom 16
:margin-horizontal 16})
(defstyle connect-button
{:height 52
:align-items :center
:justify-content :center
:background-color common/color-light-blue
:ios {:width 343
:border-radius 8
:opacity 0.9}
:android {:width 328
:border-radius 4}})
(defstyle connect-button-label
{:color common/color-white
:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 14}})
(defstyle connect-button-description
{:color common/color-gray4
:ios {:margin-top 8
:height 20
:font-size 14
:letter-spacing -0.2}
:android {:margin-top 12
:font-size 12}})
(defstyle network-config-container
{:height 160
:margin-top 8
:padding-top 16
:padding-left 16
:margin-horizontal 16
:background-color "#eef2f5"
:ios {:border-radius 9
:opacity 0.9}
:android {:border-radius 4}})
(defstyle network-config-text
{:color common/color-black
:ios {:opacity 0.8
:font-size 17
:line-height 24
:letter-spacing -0.2}
:android {:opacity 0.4
:font-size 16
:line-height 24}})
(def edit-button-container
{:margin-top 16
:align-items :center
:margin-bottom 16
:margin-horizontal 16})
(defstyle edit-button
{:height 52
:align-items :center
:justify-content :center
:background-color common/color-light-blue-transparent
:ios {:width 343
:border-radius 8}
:android {:width 328
:border-radius 4}})
(defstyle edit-button-label
{:color common/color-light-blue
:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 14}})
(defstyle edit-button-description
{:text-align :center
:color common/color-gray4
:ios {:margin-top 8
:font-size 14
:letter-spacing -0.2}
:android {:margin-top 12
:font-size 12}})
(defn network-icon [connected? size]
{:width size
:height size
:border-radius (/ size 2)
:background-color (if connected? "#729ae6" "#eef2f5")
:align-items :center :justify-content :center})
(def network-badge
{:height 88
:padding-left 16
:flex-direction :row
:align-items :center})
(defstyle network-item
{:flex-direction :row
:background-color :white
:align-items :center
:padding-horizontal 16
:ios {:height 64}
:android {:height 56}})
(defstyle network-item-name-text
{:color common/color-black
:ios {:font-size 17
:letter-spacing -0.2
:line-height 20}
:android {:font-size 16}})
(defstyle network-item-connected-text
{:color common/color-gray4
:ios {:font-size 14
:margin-top 6
:letter-spacing -0.2}
:android {:font-size 12
:margin-top 2}})

View File

@ -0,0 +1,9 @@
(ns status-im.ui.screens.network-settings.subs
(:require [re-frame.core :refer [reg-sub subscribe]]))
(reg-sub
:get-current-account-network
:<- [:get-current-account]
:<- [:get :networks/networks]
(fn [[current-account networks]]
(get networks (:network current-account))))

View File

@ -0,0 +1,80 @@
(ns status-im.ui.screens.network-settings.views
(:require-macros [status-im.utils.views :as views])
(:require
[status-im.utils.listview :as lw]
[re-frame.core :as rf]
[status-im.components.status-bar :as status-bar]
[status-im.components.toolbar-new.view :as toolbar-new]
[status-im.components.action-button.action-button :as action-button]
[status-im.components.action-button.styles :as action-button-styles]
[status-im.components.react :as react]
[status-im.components.icons.vector-icons :as vi]
#_[status-im.components.context-menu :refer [context-menu]]
[status-im.components.common.common :as common]
[status-im.components.renderers.renderers :as renderers]
[status-im.ui.screens.network-settings.styles :as st]
[status-im.i18n :as i18n]))
(defn network-icon [connected? size]
[react/view (st/network-icon connected? size)
[vi/icon :icons/network {:color (if connected? :white :gray)}]])
(defn network-badge [& [{:keys [name connected? options]}]]
[react/view st/network-badge
[network-icon connected? 56]
[react/view {:padding-left 16}
[react/text {:style st/badge-name-text}
(or name (i18n/label :t/new-network))]
(when connected?
[react/text {:style st/badge-connected-text}
(i18n/label :t/connected)])]])
(defn actions-view []
[react/view action-button-styles/actions-list
[react/view {:opacity 0.4}
[action-button/action-button
{:label (i18n/label :t/add-new-network)
:icon :icons/add
:icon-opts {:color :blue}}]]
#_[context-menu ; TODO should be implemented later
[action-button-view (i18n/label :t/add-new-network) :add_blue]
[{:text (i18n/label :t/add-json-file) :value #(dispatch [:navigate-to :paste-json-text])}
{:text (i18n/label :t/paste-json-as-text) :value #(dispatch [:navigate-to :paste-json-text])}
{:text (i18n/label :t/specify-rpc-url) :value #(dispatch [:navigate-to :add-rpc-url])}]]])
(defn render-row [current-network]
(fn [{:keys [id name config] :as row} _ _]
(let [connected? (= id current-network)]
(react/list-item
^{:key row}
[react/touchable-highlight
{:on-press #(rf/dispatch [:navigate-to :network-details row])}
[react/view st/network-item
[network-icon connected? 40]
[react/view {:padding-horizontal 16}
[react/text {:style st/network-item-name-text}
name]
(when connected?
[react/text {:style st/network-item-connected-text}
(i18n/label :t/connected)])]]]))))
(views/defview network-settings []
(views/letsubs [{:keys [network networks]} [:get-current-account]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar-new/toolbar {:title (i18n/label :t/network-settings)}]
[react/view {:flex 1}
[react/list-view {:dataSource (lw/to-datasource (vals networks))
:renderRow (render-row network)
:renderHeader #(react/list-item
[react/view
[actions-view]
[common/bottom-shadow]
[common/form-title (i18n/label :t/existing-networks)
{:count-value (count networks)}]
[common/list-header]])
:renderFooter #(react/list-item [react/view
[common/list-footer]
[common/bottom-shadow]])
:renderSeparator renderers/list-separator-renderer
:style st/networks-list}]]]))

View File

@ -62,7 +62,7 @@
{:optionsContainer {:margin-top 78}})
(def edit-profile-name-container
{:flex 1
{:flex 1
:padding-top 30})
(def edit-profile-icon-container
@ -128,7 +128,19 @@
{:color color-gray4}))
(def info-item-separator
{:margin-left 16})
{:margin-left 16})
(defstyle network-settings
{:padding-horizontal 16
:flex-direction :row
:align-items :center
:background-color color-white
:android {:height 72}
:ios {:height 64}})
(def network-settings-text
(merge {:flex 1}
profile-setting-text))
(def edit-line-color
(if platform/ios?
@ -154,7 +166,7 @@
:padding-bottom 0}})
(defstyle profile-status-input
{:line-height 24;;TODO doesnt' work for multiline because a bug in the RN
{:line-height 24 ;;TODO doesnt' work for multiline because a bug in the RN
:color text1-color
:padding-left 0
:ios {:font-size 17
@ -162,18 +174,18 @@
:padding-top 0
:height 74
:letter-spacing -0.2}
:android {:font-size 16
:padding-top 5
:height 74
:android {:font-size 16
:padding-top 5
:height 74
:text-align-vertical :top
:padding-bottom 0}})
:padding-bottom 0}})
(defstyle profile-status-text
{:color text1-color
:line-height 24
:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 16}})
{:color text1-color
:line-height 24
:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 16}})
(defstyle edit-profile-status
{:background-color color-light-gray

View File

@ -20,12 +20,13 @@
[status-im.i18n :refer [label]]
[status-im.ui.screens.profile.styles :as styles]
[status-im.utils.datetime :as time]
[status-im.utils.utils :refer [hash-tag?]])
(:require-macros [status-im.utils.views :refer [defview]]))
[status-im.utils.utils :refer [hash-tag?]]
[status-im.utils.config :as config])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn my-profile-toolbar []
[toolbar/toolbar {:actions [(actions/opts [{:value #(dispatch [:my-profile/edit-profile])
:text (label :t/edit)}])]}])
:text (label :t/edit)}])]}])
(defn profile-toolbar [contact]
[toolbar/toolbar
@ -47,7 +48,7 @@
[my-profile-icon {:account contact
:edit? false}]
[react/view styles/profile-badge-name-container
[react/text {:style styles/profile-name-text
[react/text {:style styles/profile-name-text
:number-of-lines 1}
name]
(when-not (nil? last-online)
@ -82,9 +83,9 @@
[react/text {:style styles/profile-setting-title}
label]
[react/view styles/profile-setting-spacing]
[react/text {:style (if empty-value?
styles/profile-setting-text-empty
styles/profile-setting-text)
[react/text {:style (if empty-value?
styles/profile-setting-text-empty
styles/profile-setting-text)
:number-of-lines 1
:ellipsizeMode text-mode
:accessibility-label accessibility-label}
@ -103,7 +104,7 @@
(defn profile-options [contact k text]
(into []
(concat [{:value (show-qr contact k)
:text (label :t/show-qr)}]
:text (label :t/show-qr)}]
(when text
(share-options text)))))
@ -128,7 +129,7 @@
(defn tag-view [tag]
[react/text {:style {:color color-blue}
:font :medium}
:font :medium}
(str tag " ")])
(defn colorize-status-hashtags [status]
@ -150,6 +151,14 @@
:empty-value? phone-empty?
:accessibility-label :profile-phone-number}]))
(defn network-settings []
[react/touchable-highlight
{:on-press #(dispatch [:navigate-to :network-settings])}
[react/view styles/network-settings
[react/text {:style styles/network-settings-text}
(label :t/network-settings)]
[vi/icon :icons/forward {:color :gray}]]])
(defn profile-info [{:keys [whisper-identity status phone] :as contact}]
[react/view
[profile-info-address-item contact]
@ -167,7 +176,10 @@
[profile-info-phone-item
phone
[{:value #(dispatch [:my-profile/change-phone-number])
:text (label :t/edit)}]]])
:text (label :t/edit)}]]
[info-item-separator]
(when config/network-switching-enabled?
[network-settings])])
(defn profile-status [status & [edit?]]
[react/view styles/profile-status-container

View File

@ -10,6 +10,7 @@
status-im.ui.screens.profile.subs
status-im.ui.screens.wallet.subs
status-im.ui.screens.wallet.transactions.subs
status-im.ui.screens.network-settings.subs
status-im.transactions.subs
status-im.bots.subs))

View File

@ -46,7 +46,11 @@
[status-im.ui.screens.wallet.request.views :refer [request-transaction]]
[status-im.ui.screens.wallet.wallet-list.views :refer [wallet-list-screen]]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.components.status-bar :as status-bar]))
[status-im.components.status-bar :as status-bar]
[status-im.ui.screens.network-settings.views :refer [network-settings]]
[status-im.ui.screens.network-settings.add-rpc.views :refer [add-rpc-url]]
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
[status-im.ui.screens.network-settings.parse-json.views :refer [paste-json-text]]))
(defn validate-current-view
[current-view signed-up?]
@ -91,8 +95,13 @@
:accounts accounts
:login login
:recover recover
:network-settings network-settings
:paste-json-text paste-json-text
:add-rpc-url add-rpc-url
:network-details network-details
(throw (str "Unknown view: " current-view)))]
[(if android? menu-context view) common-styles/flex
[view common-styles/flex
[component]

View File

@ -13,4 +13,5 @@
(def wallet-wip-enabled? (enabled? (get-config :WALLET_WIP_ENABLED 0)))
(def notifications-wip-enabled? (enabled? (get-config :NOTIFICATIONS_WIP_ENABLED 0)))
(def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0)))
(def network-switching-enabled? (enabled? (get-config :NETWORK_SWITCHING 0)))

View File

@ -6,6 +6,7 @@
(defn get-network-subdomain [network]
(case network
"testnet" "ropsten"
"testnet_rpc" "ropsten"
"mainnet" "api"))
(defn get-transaction-details-url [network hash]

View File

@ -6,7 +6,8 @@
status-im.ui.screens.db
status-im.ui.screens.subs
[status-im.ui.screens.events :as events]
[status-im.ui.screens.accounts.events :as account-events]))
[status-im.ui.screens.accounts.events :as account-events]
[status-im.constants :as constants]))
(def account-from-realm
{:last-updated 1502965625859
@ -17,11 +18,12 @@
:name "Sleepy Serene Leopardseal"
:updates-private-key "3849320857de8efe1e1ec57e08e92ed2bce196cb8763756ae4e6e7e011c1d857de0a115b3dc7eff066afe75a8794ea9905b"
:updates-public-key "384975d68aec6426faacf8b4ba2c55d5a84b70a8a26eb616e06e9c9e63f95dfdf1c1c165773e1cdca2d198a0bc5386d8a6f2079414e073b4730c8f4745292a6cdfb3fa28143ad5937128643c6addf356b66962376dc8b12274d9abfb2e1c6447ac3"
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEXw8PCM2JYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWF3ggAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmBECiQccq18AAAAASUVORK5CYII="
:photo-path "photo"
:debug? false
:signing-phrase "baby atom base"
:status "be the hero of your own journey"
:network "testnet"
:network constants/default-network
:networks constants/default-networks
:public-key "0x049b3a8c04f2c5bccda91c1f5e6434ae72957e93a31c0301b4563eda1d6ce419f63c503ebaee143115f96c1f04f232a7a22ca0454e9ee3d579ad1f870315b151d0"})
(def new-account
@ -30,8 +32,10 @@
:name "Disloyal Trusting Rainbowfish"
:updates-private-key "3849071831f581f5e2a4f095a53e0a697144b32ea6de9e92cc08936f2efa40d2f1702bdb131356df0930a3a0d301221f2b5"
:updates-public-key "38453ecc298b8b35de00c85d3217f00aa7040a7d3053dbbf6831d03c750df40b27977906692b3b5d6fec8134706b2bf65900c61130047488520cb60080a59b118cb281f3aaf65ba704c7efde8f9357d2b22fe8110b38a4dd714c1c9e108a8b067fe"
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEXw8PDYjLoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG2YFqAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKUMBsXBa1BAAAAAASUVORK5CYII="
:photo-path "new-account-photo"
:status "the future starts today, not tomorrow"
:network constants/default-network
:networks constants/default-networks
:signing-phrase "long loan limo"
:public-key "0x04f5722fba79eb36d73263417531007f43d13af76c6233573a8e3e60f667710611feba0785d751b50609bfc0b7cef35448875c5392c0a91948c95798a0ce600847"})
@ -45,7 +49,7 @@
(rf/reg-cofx
:get-new-keypair!
(fn [coeffects _]
(assoc coeffects :keypair {:public "new public"
(assoc coeffects :keypair {:public "new public"
:private "new private"})))
(rf/reg-cofx
@ -77,12 +81,12 @@
(is (= {(:address account-from-realm) account-from-realm} @accounts)))
(testing ":add-account event"
(let [new-account' (assoc new-account :network "testnet")]
(let [new-account' (assoc new-account :network constants/default-network)]
(rf/dispatch [:add-account new-account])
(is (= {(:address account-from-realm) account-from-realm
(:address new-account) new-account'} @accounts))
(:address new-account) new-account'} @accounts))
(testing ":account-update event"
@ -95,7 +99,7 @@
(rf/dispatch [:account-update {:status "new status" :name "new name"}])
(is (= {(:address account-from-realm) account-from-realm
(:address new-account) new-account''}
(:address new-account) new-account''}
(update @accounts (:address new-account) dissoc :last-updated)))
(testing ":account-update-keys event"
@ -103,7 +107,7 @@
(rf/dispatch [:account-update-keys])
(is (= {(:address account-from-realm) account-from-realm
(:address new-account) (assoc new-account''
:updates-private-key "new private"
:updates-public-key "new public")}
(update @accounts (:address new-account) dissoc :last-updated)))))))))))
(:address new-account) (assoc new-account''
:updates-private-key "new private"
:updates-public-key "new public")}
(update @accounts (:address new-account) dissoc :last-updated)))))))))))