From 608243bd3d5bce1f2729a3fe65b10f233641c5eb Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Tue, 11 Oct 2016 17:24:52 +0300 Subject: [PATCH] rework !send command --- .../react-native-status/android/build.gradle | 4 +- .../com/statusim/module/StatusService.java | 58 +++++- .../ios/RCTStatus/RCTStatus.h | 2 +- .../ios/RCTStatus/RCTStatus.m | 41 ++++- .../react-native-status/ios/RCTStatus/pom.xml | 2 +- project.clj | 2 +- resources/commands.js | 27 +-- resources/status.js | 32 +++- src/status_im/chat/handlers.cljs | 6 +- src/status_im/chat/handlers/commands.cljs | 8 +- src/status_im/chat/handlers/send_message.cljs | 48 +++-- src/status_im/chat/screen.cljs | 18 +- src/status_im/commands/handlers/jail.cljs | 71 ++++---- src/status_im/components/status.cljs | 1 + src/status_im/handlers.cljs | 1 + src/status_im/ios/core.cljs | 2 + src/status_im/transactions/handlers.cljs | 167 ++++++++++++------ src/status_im/transactions/screen.cljs | 3 - .../transactions/views/transaction_page.cljs | 10 +- src/status_im/utils/datetime.cljs | 4 +- 20 files changed, 348 insertions(+), 159 deletions(-) diff --git a/modules/react-native-status/android/build.gradle b/modules/react-native-status/android/build.gradle index 75fe4ed72b..378f50fb47 100644 --- a/modules/react-native-status/android/build.gradle +++ b/modules/react-native-status/android/build.gradle @@ -14,5 +14,5 @@ android { dependencies { compile 'com.facebook.react:react-native:+' - compile(group: 'status-im', name: 'status-go', version: '0.1.1', ext: 'aar') -} \ No newline at end of file + compile(group: 'status-im', name: 'status-go', version: 'test-jcp6', ext: 'aar') +} diff --git a/modules/react-native-status/android/src/main/java/com/statusim/module/StatusService.java b/modules/react-native-status/android/src/main/java/com/statusim/module/StatusService.java index f6b2bf71b9..2f8daa3b33 100644 --- a/modules/react-native-status/android/src/main/java/com/statusim/module/StatusService.java +++ b/modules/react-native-status/android/src/main/java/com/statusim/module/StatusService.java @@ -5,12 +5,15 @@ import android.content.Intent; import android.os.*; import android.support.annotation.Nullable; import android.util.Log; +import java.util.concurrent.Callable; import java.lang.ref.WeakReference; import com.github.status_im.status_go.cmd.Statusgo; import java.io.File; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class StatusService extends Service { @@ -19,6 +22,8 @@ public class StatusService extends Service { private static boolean isNodeInitialized = false; private final Handler handler = new Handler(); + private ExecutorService executor = null; + private static String dataFolder; private static Messenger applicationMessenger = null; @@ -74,6 +79,9 @@ public class StatusService extends Service { public void onDestroy() { super.onDestroy(); + if (executor != null) { + executor.shutdownNow(); + } //TODO: stop geth stopNode(null); //isNodeInitialized = false; @@ -83,6 +91,9 @@ public class StatusService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { + if (executor == null) { + executor = Executors.newCachedThreadPool(); + } return Service.START_STICKY; } @@ -155,7 +166,11 @@ public class StatusService extends Service { Statusgo.StartNode(dataFolder); Log.d(TAG, "Geth node started"); Log.w(TAG, "adding peer"); - Statusgo.AddPeer("enode://e15869ba08a25e49be7568b951e15af5d77a472c8e4104a14a4951f99936d65f91240d5b5f23674aee44f1ac09d8adfc6a9bff75cd8c2df73a26442f313f2da4@162.243.63.248:30303"); + + Statusgo.AddPeer("enode://efe4e6899e05237180c0970aedb81cb5aecf5b200779c7c9e1f955783e8299b364c0b981c03f4c36ad5328ef972b417afde260bbf2c5a8db37ba7f5738033952@198.199.105.122:30303"); + Statusgo.AddPeer("enode://5a5839435f48d1e3f2f907e4582f0a134e0b7857afe507073978ca32cf09ea54989dac433605047d0bc4cd19a8c80affac6876069014283aa7c7bb4954d0e623@95.85.40.211:30303"); + Statusgo.AddPeer("enode://2f05d430b4cb1c0e2a0772d48da3a034f1b596ea7163ab80d3404802d10b7d55bde323897c2be0d36026181e1a68510ea1f42a646ef9494c27e61f61e4088b7d@188.166.229.119:30303"); + Statusgo.AddPeer("enode://ad61a21f83f12b0ca494611650f5e4b6427784e7c62514dcb729a3d65106de6f12836813acf39bdc35c12ecfd0e230723678109fd4e7091ce389697bd7da39b4@139.59.212.114:30303"); isNodeInitialized = true; } createAndSendReply(message, StatusMessages.MSG_START_NODE, null); @@ -256,12 +271,45 @@ public class StatusService extends Service { String chatId = data.getString("chatId"); String path = data.getString("path"); String params = data.getString("params"); + String callbackIdentifier = data.getString(StatusConnector.CALLBACK_IDENTIFIER); - String result = Statusgo.Call(chatId, path, params); + Log.d(TAG, "Before StatusGo.Call"); + Callable callable = new JailRequest(message.replyTo, chatId, path, params, callbackIdentifier); + executor.submit(callable); + } + + public class JailRequest implements Callable { + + String chatId; + String path; + String params; + String callbackIdentifier; + Messenger messenger; + + JailRequest(Messenger messenger, String chatId, String path, String params, String callbackIdentifier) { + + this.messenger = messenger; + this.chatId = chatId; + this.path = path; + this.params = params; + this.callbackIdentifier = callbackIdentifier; + } + + public String call() throws Exception { + Log.d(TAG, "StatusGo.Call"); + String result = Statusgo.Call(chatId, path, params); + + Bundle replyData = new Bundle(); + replyData.putString("data", result); + Message replyMessage = Message.obtain(null, StatusMessages.MSG_JAIL_CALL, 0, 0, null); + Log.d(TAG, "Callback identifier: " + callbackIdentifier); + replyData.putString(StatusConnector.CALLBACK_IDENTIFIER, callbackIdentifier); + replyMessage.setData(replyData); + sendReply(messenger, replyMessage); + + return result; + } - Bundle replyData = new Bundle(); - replyData.putString("data", result); - createAndSendReply(message, StatusMessages.MSG_JAIL_CALL, replyData); } public static boolean isNodeInitialized() { diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.h b/modules/react-native-status/ios/RCTStatus/RCTStatus.h index c3793a8b96..7e5193717b 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.h +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.h @@ -3,4 +3,4 @@ #import "RCTLog.h" @interface Status : NSObject -@end \ No newline at end of file +@end diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 68f1778102..c2f67a723b 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -1,12 +1,22 @@ #import "RCTStatus.h" - +#import "RCTBridge.h" +#import "RCTEventDispatcher.h" #import static bool isStatusInitialized; - +static RCTBridge *bridge; @implementation Status{ } +-(RCTBridge *)bridge +{ + return bridge; +} + +-(void)setBridge:(RCTBridge *)newBridge +{ + bridge = newBridge; +} RCT_EXPORT_MODULE(); @@ -41,8 +51,12 @@ RCT_EXPORT_METHOD(callJail:(NSString *)chatId #if DEBUG NSLog(@"CallJail() method called"); #endif - char * result = Call((char *) [chatId UTF8String], (char *) [path UTF8String], (char *) [params UTF8String]); - callback(@[[NSString stringWithUTF8String: result]]); + dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + char * result = Call((char *) [chatId UTF8String], (char *) [path UTF8String], (char *) [params UTF8String]); + dispatch_async( dispatch_get_main_queue(), ^{ + callback(@[[NSString stringWithUTF8String: result]]); + }); + }); } //////////////////////////////////////////////////////////////////// @@ -68,8 +82,9 @@ RCT_EXPORT_METHOD(startNode:(RCTResponseSenderBlock)onResultCallback) { NSLog(@"error %@", error); }else NSLog(@"folderName: %@", folderName); - - NSString *peer = @"enode://e15869ba08a25e49be7568b951e15af5d77a472c8e4104a14a4951f99936d65f91240d5b5f23674aee44f1ac09d8adfc6a9bff75cd8c2df73a26442f313f2da4@162.243.63.248:30303"; + + NSString *peer1 = @"enode://e15869ba08a25e49be7568b951e15af5d77a472c8e4104a14a4951f99936d65f91240d5b5f23674aee44f1ac09d8adfc6a9bff75cd8c2df73a26442f313f2da4@162.243.63.248:30303"; + NSString *peer2 = @"enode://ad61a21f83f12b0ca494611650f5e4b6427784e7c62514dcb729a3d65106de6f12836813acf39bdc35c12ecfd0e230723678109fd4e7091ce389697bd7da39b4@139.59.212.114:30303"; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { StartNode((char *) [folderName.path UTF8String]); @@ -77,7 +92,8 @@ RCT_EXPORT_METHOD(startNode:(RCTResponseSenderBlock)onResultCallback) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { - AddPeer((char *) [peer UTF8String]); + AddPeer((char *) [peer1 UTF8String]); + AddPeer((char *) [peer2 UTF8String]); }); onResultCallback(@[[NSNull null]]); return; @@ -161,4 +177,15 @@ RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) { #endif } ++ (void)signalEvent:(char *) signal +{ + NSString *sig = [NSString stringWithUTF8String:signal]; +#if DEBUG + NSLog(@"SignalEvent"); + NSLog(sig); +#endif + [bridge.eventDispatcher sendAppEventWithName:@"gethEvent" + body:@{@"jsonEvent": sig}]; +} + @end diff --git a/modules/react-native-status/ios/RCTStatus/pom.xml b/modules/react-native-status/ios/RCTStatus/pom.xml index 306a915461..021e4b3220 100644 --- a/modules/react-native-status/ios/RCTStatus/pom.xml +++ b/modules/react-native-status/ios/RCTStatus/pom.xml @@ -25,7 +25,7 @@ status-im status-go-ios - 0.1.2 + ios-signals zip true ./ diff --git a/project.clj b/project.clj index 478bbf4d91..4592c83a54 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} - :dependencies [[org.clojure/clojure "1.9.0-alpha12"] + :dependencies [[org.clojure/clojure "1.9.0-alpha13"] [org.clojure/clojurescript "1.9.229"] [reagent "0.5.1" :exclusions [cljsjs/react]] [re-frame "0.7.0"] diff --git a/resources/commands.js b/resources/commands.js index 0b8e0284f1..497ad774ed 100644 --- a/resources/commands.js +++ b/resources/commands.js @@ -62,8 +62,8 @@ status.command({ }] }); -function validateBalance(params) { - if(!params.value){ +function validateBalance(params, context) { + if (!params.amount) { return { errors: [ status.components.validationMessage( @@ -75,19 +75,19 @@ function validateBalance(params) { } try { - var val = web3.toWei(params.value, "ether"); + var val = web3.toWei(params.amount, "ether"); } catch (err) { return { errors: [ status.components.validationMessage( "Amount", - "Amount is not valid number"//err.message + "Amount is not valid number" ) ] }; } - var balance = web3.eth.getBalance(params.command.address); + var balance = web3.eth.getBalance(context.from); if (bn(val).greaterThan(bn(balance))) { return { errors: [ @@ -102,15 +102,18 @@ function validateBalance(params) { } } -function sendTransaction(params) { +function sendTransaction(params, context) { var data = { - from: params.command.from, - to: params.command.to, - value: web3.toWei(params.value, "ether") + from: context.from, + to: context.to, + value: web3.toWei(params.amount, "ether") }; - var hash = web3.eth.sendTransaction(data); - return {"transaction-hash": hash}; + try { + return web3.eth.sendTransaction(data); + } catch (err) { + return {error: err}; + } } status.command({ @@ -124,7 +127,7 @@ status.command({ preview: function (params) { return status.components.text( {}, - params.value + " ETH" + params.amount + " ETH" ); }, handler: sendTransaction, diff --git a/resources/status.js b/resources/status.js index 335c417580..411ff21329 100644 --- a/resources/status.js +++ b/resources/status.js @@ -1,7 +1,8 @@ var _status_catalog = { - commands: {}, - responses: {} -}; + commands: {}, + responses: {} + }, + status = {}; function Command() { } @@ -44,10 +45,25 @@ Response.prototype.onReceiveResponse = function (handler) { this.onReceive = handler; }; +var context = {} + +function addContext(ns, key, value) { + context[ns][key] = value; +} + function call(pathStr, paramsStr) { var params = JSON.parse(paramsStr), path = JSON.parse(pathStr), - fn, res; + fn, callResult, message_id; + + if (typeof params.context !== "undefined" && + typeof params.context["message-id"] !== "undefined") { + message_id = params.context["message-id"]; + } else { + message_id = null; + } + context[message_id] = {}; + status.message_id = message_id; fn = path.reduce(function (catalog, name) { if (catalog && catalog[name]) { @@ -61,9 +77,13 @@ function call(pathStr, paramsStr) { return null; } - res = fn(params.parameters, params.context); + callResult = fn(params.parameters, params.context); + result = { + returned: callResult, + context: context[message_id] + }; - return JSON.stringify(res); + return JSON.stringify(result); } function text(options, s) { diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 3321fec436..0121132d05 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -275,11 +275,13 @@ [{:keys [current-chat-id] :as db} [_ _ id]] (let [chat-id (or id current-chat-id) messages (get-in db [:chats chat-id :messages]) - db' (assoc db :current-chat-id chat-id)] + db' (assoc db :current-chat-id chat-id) + commands-loaded? (get-in db [:chats chat-id :commands-loaded])] (when (= current-chat-id wallet-chat-id) (dispatch [:cancel-command])) (dispatch [:load-requests! chat-id]) - (dispatch [:load-commands! chat-id]) + (when-not commands-loaded? + (dispatch [:load-commands! chat-id])) (if (and (seq messages) (not= (count messages) 1)) db' diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs index a37d7c778a..cfde8c3879 100644 --- a/src/status_im/chat/handlers/commands.cljs +++ b/src/status_im/chat/handlers/commands.cljs @@ -91,7 +91,7 @@ (fn [_ [_ {:keys [chat-id handler]} {:keys [error result]}]] ;; todo handle error (when-not error - (let [{:keys [errors validationHandler parameters]} result] + (let [{:keys [errors validationHandler parameters]} (:returned result)] (cond errors (dispatch [::add-validation-errors chat-id errors]) @@ -128,6 +128,7 @@ :id (random/id)}] (-> db (commands/stage-command command-info) + (assoc-in [:command->chat (:id command-info)] chat-id) (assoc :staged-command command-info) (assoc-in [:disable-staging chat-id] true))))) @@ -225,7 +226,7 @@ (register-handler ::start-command-validation! (u/side-effect! - (fn [db [_ {:keys [command-input chat-id] :as data}]] + (fn [db [_ {:keys [command-input chat-id address] :as data}]] (let [command-input' (or command-input (commands/get-command-input db)) {:keys [parameter-idx params command]} command-input' {:keys [name type]} command @@ -233,7 +234,8 @@ :params (get parameter-idx) :name) - context {:current-parameter current-parameter} + context {:current-parameter current-parameter + :from address} path [(if (= :command type) :commands :responses) name :validator] diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index a1bf1b4d22..d3c8fc0c61 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -16,14 +16,16 @@ [taoensso.timbre :refer-macros [debug]])) (defn prepare-command - [identity chat-id {:keys [preview preview-string params command to-message]}] + [identity chat-id + {:keys [id preview preview-string params command to-message handler-data]}] (let [content {:command (command :name) :params params}] - {:message-id (random/id) + {:message-id id :from identity :to chat-id :timestamp (time/now-ms) - :content (assoc content :preview preview-string) + :content (assoc content :preview preview-string + :handler-data handler-data) :content-type content-type-command :outgoing true :preview preview-string @@ -55,7 +57,9 @@ (fn [_ [_ {:keys [commands message] :as params}]] (doseq [{:keys [command] :as message} commands] (let [params' (assoc params :staged-command message)] - (if (:sending message) + (if (:sent-to-jail? message) + ;; todo there could be other reasons for "long-running" + ;; hanling of the command besides sendTransaction (dispatch [:navigate-to :confirm]) (if (:has-handler command) (dispatch [::invoke-command-handlers! params']) @@ -71,16 +75,18 @@ (register-handler :prepare-command! (u/side-effect! (fn [{:keys [current-public-key] :as db} - [_ {:keys [chat-id staged-command] :as params}]] - (let [command' (->> staged-command + [_ {:keys [chat-id staged-command handler-data] :as params}]] + (let [command' (->> (assoc staged-command :handler-data handler-data) (prepare-command current-public-key chat-id) (cu/check-author-direction db chat-id))] - (dispatch [::clear-command chat-id (:id staged-command)]) + (dispatch [:clear-command chat-id (:id staged-command)]) (dispatch [::send-command! (assoc params :command command')]))))) -(register-handler ::clear-command +(register-handler :clear-command (fn [db [_ chat-id id]] - (update-in db [:chats chat-id :staged-commands] dissoc id))) + (if chat-id + (update-in db [:chats chat-id :staged-commands] dissoc id) + db))) (register-handler ::send-command! (u/side-effect! @@ -112,20 +118,28 @@ (register-handler ::invoke-command-handlers! (u/side-effect! - (fn [db [_ {:keys [chat-id address staged-command] :as parameters}]] - (let [{:keys [command params]} staged-command + (fn [db [_ {:keys [chat-id address staged-command] + :as parameters}]] + (let [{:keys [id command params]} staged-command {:keys [type name]} command path [(if (= :command type) :commands :responses) name :handler] to (get-in db [:contacts chat-id :address]) params {:parameters params - :context {:from address - :to to}}] - (status/call-jail chat-id - path - params - #(dispatch [:command-handler! chat-id parameters %])))))) + :context {:from address + :to to + :message-id id}}] + (dispatch [::command-in-processing chat-id id]) + (status/call-jail + chat-id + path + params + #(dispatch [:command-handler! chat-id parameters %])))))) + +(register-handler ::command-in-processing + (fn [db [_ chat-id id]] + (assoc-in db [:chats chat-id :staged-commands id :sent-to-jail?] true))) (register-handler ::prepare-message (u/side-effect! diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 0fe9acb988..2274a9eef6 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -134,7 +134,7 @@ (defview messages-view [group-chat] [messages [:chat :messages] contacts [:chat :contacts] - loaded? [:all-messages-loaded?]] + loaded? [:all-messages-loaded?]] (let [contacts' (contacts-by-identity contacts) messages (messages-with-timemarks messages)] [list-view {:renderRow (fn [row _ index] @@ -155,11 +155,11 @@ (anim/start (anim/spring val {:toValue @offset})))) (defn messages-container [messages] - (let [offset (subscribe [:messages-offset]) + (let [offset (subscribe [:messages-offset]) messages-offset (anim/create-value 0) - context {:offset offset - :val messages-offset} - on-update (messages-container-animation-logic context)] + context {:offset offset + :val messages-offset} + on-update (messages-container-animation-logic context)] (r/create-class {:component-did-mount on-update @@ -172,11 +172,11 @@ messages])}))) (defn chat [] - (let [group-chat (subscribe [:chat :group-chat]) - show-actions? (subscribe [:chat-ui-props :show-actions?]) + (let [group-chat (subscribe [:chat :group-chat]) + show-actions? (subscribe [:chat-ui-props :show-actions?]) show-bottom-info? (subscribe [:chat-ui-props :show-bottom-info?]) - command? (subscribe [:command?]) - layout-height (subscribe [:get :layout-height])] + command? (subscribe [:command?]) + layout-height (subscribe [:get :layout-height])] (r/create-class {:component-did-mount #(dispatch [:check-autorun]) :reagent-render diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 401488e2b1..af4c4ec055 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -10,11 +10,6 @@ [status-im.constants :refer [console-chat-id]] [taoensso.timbre :as log])) -(defn init-render-command! - [_ [chat-id command message-id data]] - (status/call-jail chat-id [command :render] data - #(dispatch [::render-command chat-id message-id %]))) - (defn render-command [db [chat-id message-id markup]] (let [hiccup (generate-hiccup markup)] @@ -31,28 +26,40 @@ (def regular-events {}) (defn command-hadler! - [_ [chat-id {:keys [command] :as parameters} {:keys [result error]}]] - (cond - result - (let [{:keys [event params transaction-hash]} result - command' (assoc command :handler-data result) - parameters' (assoc parameters :command command')] - (if transaction-hash - (dispatch [:wait-for-transaction transaction-hash parameters']) - (let [events (if (= console-chat-id chat-id) - (merge regular-events console-events) - regular-events) - parameters'' (if-let [handler (events (keyword event))] - (assoc parameters' :handler #(handler params command')) - parameters')] - (dispatch [:prepare-command! parameters''])))) - (not error) - (dispatch [:prepare-command! parameters]) - :else nil)) + [_ [chat-id + {:keys [staged-command] :as parameters} + {:keys [result error]}]] + (let [{:keys [context returned]} result + {:keys [event params] + handler-error :error} returned] + (cond + handler-error + (log/debug :error-from-handler handler-error + :chat-id chat-id + :command staged-command) + + result + (let [{:keys [event params]} returned + command' (assoc staged-command :handler-data returned) + parameters' (assoc parameters :command command')] + (if (:eth_sendTransaction context) + (dispatch [:wait-for-transaction (:id staged-command) parameters']) + (let [events (if (= console-chat-id chat-id) + (merge regular-events console-events) + regular-events) + parameters'' (if-let [handler (events (keyword event))] + (assoc parameters' :handler #(handler params command')) + parameters')] + (dispatch [:prepare-command! parameters''])))) + + (not (or error handler-error)) + (dispatch [:prepare-command! parameters]) + + :else nil))) (defn suggestions-handler! [db [{:keys [chat-id]} {:keys [result]}]] - (let [{:keys [markup webViewUrl]} result + (let [{:keys [markup webViewUrl]} (:returned result) hiccup (generate-hiccup markup)] (-> db (assoc-in [:suggestions chat-id] (generate-hiccup markup)) @@ -68,12 +75,13 @@ (defn command-preview [db [chat-id command-id {:keys [result]}]] - (if result - (let [path [:chats chat-id :staged-commands command-id]] - (update-in db path assoc - :preview (generate-hiccup result) - :preview-string (str result))) - db)) + (let [result' (:returned result)] + (if result' + (let [path [:chats chat-id :staged-commands command-id]] + (update-in db path assoc + :preview (generate-hiccup result') + :preview-string (str result'))) + db))) (defn print-error-message! [message] (fn [_ params] @@ -81,7 +89,6 @@ (show-popup "Error" (s/join "\n" [message params])) (log/debug message params)))) -(reg-handler :init-render-command! init-render-command!) (reg-handler ::render-command render-command) (reg-handler :command-handler! @@ -93,6 +100,8 @@ (after (print-error-message! "Error on param suggestions")) (after (fn [_ [{:keys [command]}]] (when (= :on-send (keyword (:suggestions-trigger command))) + #_(when (:webViewUrl (:returned result)) + (dispatch [:set-soft-input-mode :pan])) (r/dismiss-keyboard!))))] suggestions-handler!) (reg-handler :suggestions-event! (u/side-effect! suggestions-events-handler!)) diff --git a/src/status_im/components/status.cljs b/src/status_im/components/status.cljs index 672af4d944..210566f407 100644 --- a/src/status_im/components/status.cljs +++ b/src/status_im/components/status.cljs @@ -35,6 +35,7 @@ (defn complete-transaction [hash password callback] + (log/debug :complete-transaction (boolean status) hash password) (when status (.completeTransaction status hash password callback))) diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 5c77134318..9f886dbb61 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -102,6 +102,7 @@ (register-handler :signal-event (u/side-effect! (fn [_ [_ event-str]] + (log/debug :event-str event-str) (let [{:keys [type event]} (t/json->clj event-str)] (case type "transaction.queued" (dispatch [:transaction-queued event]) diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs index ea0ca04345..f02d1d8b11 100644 --- a/src/status_im/ios/core.cljs +++ b/src/status_im/ios/core.cljs @@ -15,6 +15,7 @@ [status-im.chat.screen :refer [chat]] [status-im.accounts.login.screen :refer [login]] [status-im.accounts.screen :refer [accounts]] + [status-im.transactions.screen :refer [confirm]] [status-im.chats-list.screen :refer [chats-list]] [status-im.new-group.screen :refer [new-group]] [status-im.participants.views.add :refer [new-participants]] @@ -83,6 +84,7 @@ :profile-photo-capture profile-photo-capture :accounts accounts :login login + :confirm confirm :my-profile my-profile)] [component]))))}))) diff --git a/src/status_im/transactions/handlers.cljs b/src/status_im/transactions/handlers.cljs index a8bb07f8c8..50765be641 100644 --- a/src/status_im/transactions/handlers.cljs +++ b/src/status_im/transactions/handlers.cljs @@ -6,45 +6,62 @@ [status-im.utils.types :as t] [status-im.components.status :as status] cljsjs.web3 - [clojure.string :as s])) + [clojure.string :as s] + [taoensso.timbre :as log])) + +;; flow: +;; :accept-transactions +;; ↓ +;; :transaction-completed +;; ↓ +;; ::remove-transaction && [:set :wrong-password? false] <- on error +;; ::remove-transaction <- when transaction is +;; not from the jail +;; ::add-transactions-hash +;; && ::check-completed-transaction! +;; && :navigation-replace <- on success + (defmethod nav/preload-data! :confirm [{:keys [transactions-queue] :as db} _] (assoc db :transactions transactions-queue)) -(defn on-unlock - [hashes password] - ;; todo: add message about wrong password - (do - ;(dispatch [:set :wrong-password? false]) - (doseq [hash hashes] - (status/complete-transaction - hash - password - #(dispatch [:transaction-completed hash %]))) - (dispatch [:navigate-back]))) - ;(dispatch [:set :wrong-password? true]) - +(defn on-unlock + [ids password previous-view-id] + (dispatch [:set :wrong-password? false]) + (doseq [id ids] + (status/complete-transaction + id + password + #(dispatch [:transaction-completed + {:id id + :response % + :previous-view-id previous-view-id}])))) (register-handler :accept-transactions (u/side-effect! - (fn [{:keys [transactions current-account-id]} [_ password]] - (let [hashes (keys transactions)] - (on-unlock hashes password))))) + (fn [{:keys [transactions navigation-stack]} [_ password]] + (let [ids (keys transactions) + previous-view-id (second navigation-stack)] + (on-unlock ids password previous-view-id))))) (register-handler :deny-transactions (u/side-effect! (fn [{:keys [transactions]}] - (let [hashes (keys transactions)] - (dispatch [::remove-pending-messages hashes]) - (dispatch [::remove-trqqansactions hashes]) + (let [transactions' (vals transactions) + messages-ids (map :message-id transactions') + ids (map :id transactions')] + (dispatch [::remove-pending-messages messages-ids]) + (dispatch [::remove-transactions ids]) (dispatch [:navigate-back]))))) (register-handler :deny-transaction (u/side-effect! - (fn [_ [_ hash]] - (dispatch [::remove-pending-message hash]) - (dispatch [::remove-transaction hash])))) + (fn [{:keys [transactions]} [_ id]] + (let [{:keys [message-id] :as transaction} (get transactions id)] + (when transaction + (dispatch [::remove-pending-message message-id]) + (dispatch [::remove-transaction id])))))) (register-handler ::remove-transactions (fn [db [_ hashes]] @@ -58,55 +75,101 @@ (update :transactions dissoc hash) (update :transactions-queue dissoc hash)))) +(defn mark-command-as-pending [db chat-id id] + (let [path [:chats chat-id :staged-commands id]] + (if (get-in db path) + (update-in db path assoc :pending true) + db))) + (register-handler :wait-for-transaction - (fn [db [_ hash {:keys [chat-id command] :as params}]] + (after (fn [_ [_ message-id]] + (dispatch [::check-completed-transaction! + {:message-id message-id}]))) + (fn [db [_ message-id {:keys [chat-id command] :as params}]] (let [id (:id command)] (-> db - (update-in [:chats chat-id :staged-commands id] assoc :pending true) - (assoc-in [:transaction-subscribers hash] params))))) + (mark-command-as-pending chat-id id) + (assoc-in [:transaction-subscribers message-id] params))))) -(defn remove-pending-message [db hash] - (let [{:keys [chat-id command]} (get-in db [:transaction-subscribers hash]) - path [:chats chat-id :staged-commands]] - (-> db - (update :transaction-subscribers dissoc hash) - (update-in path dissoc (:id command))))) +(defn remove-pending-message + [{:keys [command->chat] :as db} message-id] + (let [chat-id (get command->chat message-id) + path [:chats chat-id :staged-commands]] + (if chat-id + (-> db + (update :transaction-subscribers dissoc message-id) + (update-in path dissoc message-id)) + db))) (register-handler ::remove-pending-messages - (fn [db [_ hashes]] - (reduce remove-pending-message db hashes))) + (fn [db [_ ids]] + (log/debug :message-ids ids) + (reduce remove-pending-message db ids))) (register-handler ::remove-pending-message - (fn [db [_ hash]] - (remove-pending-message db hash))) + (fn [db [_ message-id]] + (remove-pending-message db message-id))) (register-handler :transaction-queued (after #(dispatch [:navigate-to :confirm])) - (fn [db [_ {:keys [hash args]}]] + (fn [db [_ {:keys [id message_id args]}]] (let [{:keys [from to value]} args - transaction {:hash hash - :from from - :to to - :value (.toDecimal js/Web3.prototype value)}] - (assoc-in db [:transactions-queue hash] transaction)))) + transaction {:id id + :from from + :to to + :value (.toDecimal js/Web3.prototype value) + :message-id message_id}] + (assoc-in db [:transactions-queue id] transaction)))) (register-handler :transaction-completed (u/side-effect! - (fn [db [_ old-hash result-str]] - (let [{:keys [hash error]} (t/json->clj result-str)] - ;; todo: handle error - (when hash - (dispatch [::send-pending-message old-hash hash]) - (dispatch [::remove-transaction old-hash])))))) + (fn [{:keys [transactions command->chat]} [_ {:keys [id response previous-view-id]}]] + (let [{:keys [hash error] :as parsed-response} (t/json->clj response) + {:keys [message-id]} (transactions id)] + (log/debug :parsed-response parsed-response) + (if (and error (string? error) (not (s/blank? error))) + ;; todo: revisit this + ;; currently transaction is removed after attempt + ;; to complete it with wrong password + (do + (dispatch [::remove-transaction id]) + (dispatch [:set :wrong-password? true]) + (when-let [chat-id (get command->chat message-id)] + (dispatch [:clear-command chat-id message-id]))) + (if message-id + (do (dispatch [::add-transactions-hash {:id id + :hash hash + :message-id message-id}]) + (dispatch [::check-completed-transaction! + {:message-id message-id}]) + (dispatch [:navigation-replace previous-view-id])) + (dispatch [::remove-transaction id]))))))) + +(register-handler ::add-transactions-hash + (fn [db [_ {:keys [id hash message-id]}]] + (-> db + (assoc-in [:transactions id :hash] hash) + (assoc-in [:message-id->transaction-id message-id] id)))) (register-handler ::send-pending-message (u/side-effect! - (fn [{:keys [transaction-subscribers] :as db} [_ old-hash new-hash]] - (when-let [params (transaction-subscribers old-hash)] - (let [params' (assoc-in params [:handler-data :transaction-hash] new-hash)] + (fn [{:keys [transaction-subscribers]} [_ message-id hash]] + (when-let [params (transaction-subscribers message-id)] + (let [params' (assoc-in params [:handler-data :transaction-hash] hash)] (dispatch [:prepare-command! params'])) - (dispatch [::remove-transaction-subscriber old-hash]))))) + (dispatch [::remove-transaction-subscriber message-id]))))) (register-handler ::remove-transaction-subscriber (fn [db [_ old-hash]] (update db :transaction-subscribers dissoc old-hash))) + +(register-handler ::check-completed-transaction! + (u/side-effect! + (fn [{:keys [message-id->transaction-id transactions transaction-subscribers]} + [_ {:keys [message-id]}]] + (let [id (get message-id->transaction-id message-id) + {:keys [hash]} (get transactions id) + pending-message (get transaction-subscribers message-id)] + (when (and pending-message id hash) + (dispatch [::send-pending-message message-id hash]) + (dispatch [::remove-transaction id])))))) diff --git a/src/status_im/transactions/screen.cljs b/src/status_im/transactions/screen.cljs index d984d3cb13..de51e87816 100644 --- a/src/status_im/transactions/screen.cljs +++ b/src/status_im/transactions/screen.cljs @@ -57,6 +57,3 @@ :value password :label (label :t/password) :on-change-text #(dispatch [:set-in [:confirm-transactions :password] %])}]]]) - - -;(re-frame.core/dispatch [:set :view-id :confirm]) diff --git a/src/status_im/transactions/views/transaction_page.cljs b/src/status_im/transactions/views/transaction_page.cljs index 016de5b7b1..64fafd5b6e 100644 --- a/src/status_im/transactions/views/transaction_page.cljs +++ b/src/status_im/transactions/views/transaction_page.cljs @@ -14,11 +14,11 @@ [status-im.i18n :refer [label label-pluralize]] cljsjs.web3)) -(defn title-bar [title hash] +(defn title-bar [title id] [view st/title-bar [text {:style st/title-bar-text} title] [touchable-highlight {:style st/icon-close-container - :on-press #(dispatch [:deny-transaction hash])} + :on-press #(dispatch [:deny-transaction id])} [view [image {:source {:uri :icon_close_gray} :style st/icon-close}]]]]) @@ -31,7 +31,7 @@ [view st/transaction-info-column-value [text {:style st/transaction-info-value} value]]]]) -(defview transaction-page [{:keys [hash from to value] :as transaction}] +(defview transaction-page [{:keys [id from to value] :as transaction}] [{:keys [name] :as contact} [:contact-by-address to]] (let [eth-value (.fromWei js/Web3.prototype value "ether") title (str eth-value " ETH to " name) @@ -39,8 +39,8 @@ [(label :t/recipient) name] [(label :t/value) (str eth-value " ETH")]]] [view {:style st/transaction-page - :key hash} - [title-bar title hash] + :key id} + [title-bar title id] [view st/scroll-view-container [scroll-view {:style st/scroll-view :contentContainerStyle st/scroll-view-content diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs index 3f15a4a0f4..24d82741c9 100644 --- a/src/status_im/utils/datetime.cljs +++ b/src/status_im/utils/datetime.cljs @@ -46,8 +46,8 @@ (let [diff (t/in-seconds (t/interval time (t/now)))] (if (< diff 60) (label :t/active-online) - (let [unit (first (drop-while #(or (>= diff (:limit %)) - (not (:limit %))) + (let [unit (first (drop-while #(and (>= diff (:limit %)) + (:limit %)) units))] (-> (/ diff (:in-second unit)) Math/floor