diff --git a/.env b/.env index f10c4f1143..5fa5dfed27 100644 --- a/.env +++ b/.env @@ -3,3 +3,4 @@ WALLET_WIP_ENABLED=1 NOTIFICATIONS_WIP_ENABLED=1 DEBUG_LOGS_ENABLED=1 STUB_STATUS_GO=0 +NETWORK_SWITCHING=0 diff --git a/.env.jenkins b/.env.jenkins index 414965518d..d75688fc36 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -3,4 +3,4 @@ WALLET_WIP_ENABLED=1 NOTIFICATIONS_WIP_ENABLED=1 DEBUG_LOGS_ENABLED=1 STUB_STATUS_GO=0 - +NETWORK_SWITCHING=1 diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index bdef203176..9226f72bf8 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -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"); diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 23d9ede21c..7bbe913741 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -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]); + }); } //////////////////////////////////////////////////////////////////// diff --git a/resources/icons/network.svg b/resources/icons/network.svg new file mode 100644 index 0000000000..78a2d00832 --- /dev/null +++ b/resources/icons/network.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/run-osx.sh b/run-osx.sh index c966b527ca..d37771ef57 100755 --- a/run-osx.sh +++ b/run-osx.sh @@ -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##*| }" diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs index f65dd56cd9..a1b4faf157 100644 --- a/src/status_im/components/drawer/view.cljs +++ b/src/status_im/components/drawer/view.cljs @@ -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} diff --git a/src/status_im/components/icons/vector_icons.cljs b/src/status_im/components/icons/vector_icons.cljs index c287c78845..6ad6f2f5b5 100644 --- a/src/status_im/components/icons/vector_icons.cljs +++ b/src/status_im/components/icons/vector_icons.cljs @@ -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))))])) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index b7494cd825..90bfc03b12 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -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"})}}) diff --git a/src/status_im/data_store/accounts.cljs b/src/status_im/data_store/accounts.cljs index fd018e6757..d8bf32b57b 100644 --- a/src/status_im/data_store/accounts.cljs +++ b/src/status_im/data_store/accounts.cljs @@ -9,6 +9,3 @@ (defn save [account update?] (data-store/save account update?)) - -(defn save-all [accounts update?] - (data-store/save-all accounts update?)) \ No newline at end of file diff --git a/src/status_im/data_store/networks.cljs b/src/status_im/data_store/networks.cljs new file mode 100644 index 0000000000..de272c1658 --- /dev/null +++ b/src/status_im/data_store/networks.cljs @@ -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)) diff --git a/src/status_im/data_store/realm/accounts.cljs b/src/status_im/data_store/realm/accounts.cljs index 4551da71a7..a88b8ac019 100644 --- a/src/status_im/data_store/realm/accounts.cljs +++ b/src/status_im/data_store/realm/accounts.cljs @@ -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?)))) diff --git a/src/status_im/data_store/realm/networks.cljs b/src/status_im/data_store/realm/networks.cljs new file mode 100644 index 0000000000..55d4ecb650 --- /dev/null +++ b/src/status_im/data_store/realm/networks.cljs @@ -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))) diff --git a/src/status_im/data_store/realm/schemas/base/core.cljs b/src/status_im/data_store/realm/schemas/base/core.cljs index af8f8d82f0..911b5702b9 100644 --- a/src/status_im/data_store/realm/schemas/base/core.cljs +++ b/src/status_im/data_store/realm/schemas/base/core.cljs @@ -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}]) diff --git a/src/status_im/data_store/realm/schemas/base/v3/account.cljs b/src/status_im/data_store/realm/schemas/base/v3/account.cljs index fcf06af223..327e64cd9a 100644 --- a/src/status_im/data_store/realm/schemas/base/v3/account.cljs +++ b/src/status_im/data_store/realm/schemas/base/v3/account.cljs @@ -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) diff --git a/src/status_im/data_store/realm/schemas/base/v4/account.cljs b/src/status_im/data_store/realm/schemas/base/v4/account.cljs new file mode 100644 index 0000000000..94d925c2f4 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v4/account.cljs @@ -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))))) diff --git a/src/status_im/data_store/realm/schemas/base/v4/core.cljs b/src/status_im/data_store/realm/schemas/base/v4/core.cljs new file mode 100644 index 0000000000..850129dd06 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v4/core.cljs @@ -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)) + + diff --git a/src/status_im/data_store/realm/schemas/base/v4/network.cljs b/src/status_im/data_store/realm/schemas/base/v4/network.cljs new file mode 100644 index 0000000000..26629559cc --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v4/network.cljs @@ -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")) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index a51b4dedb3..997f4cb279 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -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 [] diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index 179f3857a5..ad1aa2f503 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -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] diff --git a/src/status_im/native_module/impl/non_status_go_module.cljs b/src/status_im/native_module/impl/non_status_go_module.cljs index d5670d833c..84d759f787 100644 --- a/src/status_im/native_module/impl/non_status_go_module.cljs +++ b/src/status_im/native_module/impl/non_status_go_module.cljs @@ -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]) diff --git a/src/status_im/native_module/module.cljs b/src/status_im/native_module/module.cljs index df7141a633..d2691bf307 100644 --- a/src/status_im/native_module/module.cljs +++ b/src/status_im/native_module/module.cljs @@ -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]) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 63bb5e0c94..c9f9400ff4 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -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"}) diff --git a/src/status_im/ui/screens/accounts/db.cljs b/src/status_im/ui/screens/accounts/db.cljs index 5d06a5d4cd..e61afb3dc9 100644 --- a/src/status_im/ui/screens/accounts/db.cljs +++ b/src/status_im/ui/screens/accounts/db.cljs @@ -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))) diff --git a/src/status_im/ui/screens/accounts/events.cljs b/src/status_im/ui/screens/accounts/events.cljs index e2e6b5122b..8d6ee4f32d 100644 --- a/src/status_im/ui/screens/accounts/events.cljs +++ b/src/status_im/ui/screens/accounts/events.cljs @@ -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? diff --git a/src/status_im/ui/screens/accounts/login/events.cljs b/src/status_im/ui/screens/accounts/login/events.cljs index 6387c1b2c9..92da9b7acc 100644 --- a/src/status_im/ui/screens/accounts/login/events.cljs +++ b/src/status_im/ui/screens/accounts/login/events.cljs @@ -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?]})))))) diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 4053b4c041..ce51ede946 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -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 diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index f06bf391a1..32318c432a 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -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 diff --git a/src/status_im/ui/screens/network_settings/add_rpc/views.cljs b/src/status_im/ui/screens/network_settings/add_rpc/views.cljs new file mode 100644 index 0000000000..2d49745050 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/add_rpc/views.cljs @@ -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) #()])])) diff --git a/src/status_im/ui/screens/network_settings/db.cljs b/src/status_im/ui/screens/network_settings/db.cljs new file mode 100644 index 0000000000..db31d7ee49 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/db.cljs @@ -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))) diff --git a/src/status_im/ui/screens/network_settings/events.cljs b/src/status_im/ui/screens/network_settings/events.cljs new file mode 100644 index 0000000000..a3da34c1b9 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/events.cljs @@ -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]]})) \ No newline at end of file diff --git a/src/status_im/ui/screens/network_settings/navigation.cljs b/src/status_im/ui/screens/network_settings/navigation.cljs new file mode 100644 index 0000000000..9b21eb3882 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/navigation.cljs @@ -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)) \ No newline at end of file diff --git a/src/status_im/ui/screens/network_settings/network_details/views.cljs b/src/status_im/ui/screens/network_settings/network_details/views.cljs new file mode 100644 index 0000000000..a545ac9295 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/network_details/views.cljs @@ -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)]]]]))) diff --git a/src/status_im/ui/screens/network_settings/parse_json/views.cljs b/src/status_im/ui/screens/network_settings/parse_json/views.cljs new file mode 100644 index 0000000000..e08e243deb --- /dev/null +++ b/src/status_im/ui/screens/network_settings/parse_json/views.cljs @@ -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) #()])])) diff --git a/src/status_im/ui/screens/network_settings/styles.cljs b/src/status_im/ui/screens/network_settings/styles.cljs new file mode 100644 index 0000000000..79f60f49d8 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/styles.cljs @@ -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}}) diff --git a/src/status_im/ui/screens/network_settings/subs.cljs b/src/status_im/ui/screens/network_settings/subs.cljs new file mode 100644 index 0000000000..53028e4fa7 --- /dev/null +++ b/src/status_im/ui/screens/network_settings/subs.cljs @@ -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)))) \ No newline at end of file diff --git a/src/status_im/ui/screens/network_settings/views.cljs b/src/status_im/ui/screens/network_settings/views.cljs new file mode 100644 index 0000000000..017218a8ef --- /dev/null +++ b/src/status_im/ui/screens/network_settings/views.cljs @@ -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}]]])) diff --git a/src/status_im/ui/screens/profile/styles.cljs b/src/status_im/ui/screens/profile/styles.cljs index e90f9f13f5..3766c8192e 100644 --- a/src/status_im/ui/screens/profile/styles.cljs +++ b/src/status_im/ui/screens/profile/styles.cljs @@ -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 diff --git a/src/status_im/ui/screens/profile/views.cljs b/src/status_im/ui/screens/profile/views.cljs index aab3c7fedf..f44f93714f 100644 --- a/src/status_im/ui/screens/profile/views.cljs +++ b/src/status_im/ui/screens/profile/views.cljs @@ -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 diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index ef14fce565..7367d1b581 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -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)) diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index a7164145d3..5f3f9e7aba 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -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] diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index 3c4a0b5804..83056efb41 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -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))) diff --git a/src/status_im/utils/transactions.cljs b/src/status_im/utils/transactions.cljs index b8db2f177d..36c22e5890 100644 --- a/src/status_im/utils/transactions.cljs +++ b/src/status_im/utils/transactions.cljs @@ -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] diff --git a/test/cljs/status_im/test/accounts/events.cljs b/test/cljs/status_im/test/accounts/events.cljs index efc77d4187..931fe5a581 100644 --- a/test/cljs/status_im/test/accounts/events.cljs +++ b/test/cljs/status_im/test/accounts/events.cljs @@ -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))))))))))) \ No newline at end of file + (:address new-account) (assoc new-account'' + :updates-private-key "new private" + :updates-public-key "new public")} + (update @accounts (:address new-account) dissoc :last-updated)))))))))))