From 083ade766e955f0ba26a0742064a6c7ce1b403c2 Mon Sep 17 00:00:00 2001 From: Adrian Tiberius Date: Thu, 20 Oct 2016 16:51:37 +0300 Subject: [PATCH] implement new wallet flow --- resources/commands.js | 34 +++++- resources/status.js | 7 +- resources/wallet.js | 10 ++ src/status_im/accounts/styles.cljs | 53 +++++++++ .../accounts/views/wallet_qr_code.cljs | 44 ++++++++ src/status_im/android/core.cljs | 33 ++++-- src/status_im/chat/handlers.cljs | 28 ++++- src/status_im/chat/handlers/commands.cljs | 42 +++---- src/status_im/chat/handlers/requests.cljs | 4 +- src/status_im/chat/handlers/send_message.cljs | 65 ++++++----- .../chat/handlers/webview_bridge.cljs | 105 ++++++++++++++++-- src/status_im/chat/screen.cljs | 1 + src/status_im/chat/styles/screen.cljs | 7 ++ src/status_im/chat/subs.cljs | 9 +- src/status_im/chat/views/message.cljs | 6 +- src/status_im/chat/views/request_message.cljs | 7 +- src/status_im/chat/views/response.cljs | 5 +- src/status_im/commands/handlers/jail.cljs | 1 + src/status_im/commands/handlers/loading.cljs | 4 +- src/status_im/commands/utils.cljs | 2 + src/status_im/contacts/handlers.cljs | 7 ++ src/status_im/contacts/styles.cljs | 5 + src/status_im/contacts/views/contact.cljs | 12 ++ .../contacts/views/contact_list.cljs | 66 ++++++++--- src/status_im/ios/core.cljs | 26 ++++- src/status_im/models/commands.cljs | 1 + src/status_im/navigation/handlers.cljs | 26 +++-- src/status_im/qr_scanner/screen.cljs | 12 +- src/status_im/transactions/handlers.cljs | 18 ++- src/status_im/translations/en.cljs | 4 + 30 files changed, 517 insertions(+), 127 deletions(-) create mode 100644 src/status_im/accounts/views/wallet_qr_code.cljs diff --git a/resources/commands.js b/resources/commands.js index e9139a0098..d75adcb21c 100644 --- a/resources/commands.js +++ b/resources/commands.js @@ -117,7 +117,7 @@ function sendTransaction(params, context) { } } -status.command({ +var send = { name: "send", icon: "money_white", color: "#5fc48d", @@ -134,4 +134,36 @@ status.command({ }, handler: sendTransaction, validator: validateBalance +}; + +status.command(send); +status.response(send); + +status.command({ + name: "request", + color: "#7099e6", + description: "Transaction request", + params: [{ + name: "amount", + type: status.types.NUMBER + }], + preview: function (params) { + return status.components.text( + {}, + params.amount + " ETH" + ); + }, + handler: function (params) { + return { + event: "request", + params: [params.amount] + request: { + command: "send", + params: { + amount: params.amount + }, + content: "Requesting " + params.amount + "ETH" + } + }; + }, }); diff --git a/resources/status.js b/resources/status.js index 411ff21329..93f6bc0acf 100644 --- a/resources/status.js +++ b/resources/status.js @@ -1,6 +1,7 @@ var _status_catalog = { commands: {}, - responses: {} + responses: {}, + functions: {} }, status = {}; @@ -31,6 +32,7 @@ Command.prototype.create = function (com) { this.preview = com.preview; this["suggestions-trigger"] = com.suggestionsTrigger || "on-change"; this.fullscreen = com.fullscreen; + this.request = com.request; this.addToCatalog(); return this; @@ -153,6 +155,9 @@ var status = { var response = new Response(); return response.create(h); }, + registerFunction: function (name, fn){ + _status_catalog.functions[name] = fn; + }, autorun: function (commandName) { _status_catalog.autorun = commandName; }, diff --git a/resources/wallet.js b/resources/wallet.js index eeab1786a4..feae6993c8 100644 --- a/resources/wallet.js +++ b/resources/wallet.js @@ -25,3 +25,13 @@ status.command({ }); status.autorun("browse"); + +status.registerFunction("send", function (params, context) { + var data = { + from: context.from, + to: params.address, + value: web3.toWei(params.amount, "ether") + }; + + web3.eth.sendTransaction(data); +}) diff --git a/src/status_im/accounts/styles.cljs b/src/status_im/accounts/styles.cljs index 98f22f224c..50725db89a 100644 --- a/src/status_im/accounts/styles.cljs +++ b/src/status_im/accounts/styles.cljs @@ -113,3 +113,56 @@ {:flex 1 :color color-white :fontSize 16}) + +;wallet-qr-code.cljs + +(def wallet-qr-code + {:flex 1 + :flex-direction :column}) + +(def account-toolbar + {:background-color "#2f3031" + :padding-bottom 45}) + +(def wallet-account-container + {:flex 1 + :flexDirection :row + :height 69 + :alignItems :center + :justifyContent :center}) + +(def qr-code + {:align-items :center + :padding-vertical 40}) + +(def footer + {:position :absolute + :left 0 + :right 0 + :bottom 0 + :height 166 + :background-color "#2f3031"}) + +(def wallet-info + {:align-items :center + :padding-top 42 + :padding-bottom 20}) + +(def wallet-name-text + {:color color-white + :padding-bottom 5}) + +(def wallet-address-text + {:color "#999999" + }) + +(def done-button + {:flex 1 + :flex-direction :column + :align-items :center + :height 60 + :padding-top 20 + :background-color "#7597e4"}) + +(def done-button-text + {:color color-white}) \ No newline at end of file diff --git a/src/status_im/accounts/views/wallet_qr_code.cljs b/src/status_im/accounts/views/wallet_qr_code.cljs new file mode 100644 index 0000000000..8fad10fc0c --- /dev/null +++ b/src/status_im/accounts/views/wallet_qr_code.cljs @@ -0,0 +1,44 @@ +(ns status-im.accounts.views.wallet-qr-code + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [status-im.components.react :refer [view + text + image + touchable-highlight]] + [status-im.components.styles :refer [icon-close]] + [status-im.components.qr-code :refer [qr-code]] + [re-frame.core :refer [dispatch subscribe]] + [status-im.accounts.styles :as st] + [status-im.i18n :refer [label]] + [clojure.string :as s])) + + +(defview wallet-qr-code [] + [{:keys [address photo-path name] :as account} [:get-current-account] + {:keys [amount]} [:get :contacts-click-params]] + [view st/wallet-qr-code + [view st/account-toolbar + [view st/wallet-account-container + [view st/photo-container + [view st/account-photo-container + [image {:source {:uri (if (s/blank? photo-path) :avatar photo-path)} + :style st/photo-image}]]] + [view st/name-container + [text {:style st/name-text + :number-of-lines 1} name]] + [view st/online-container + [touchable-highlight {:onPress #(dispatch [:navigate-back])} + [image {:source {:uri :icon-close-white} + :style icon-close}]]]]] + [view st/qr-code + [qr-code {:value (prn-str {:address address + :amount amount}) + :size 200}]] + [view st/footer + [view st/wallet-info + [text {:style st/wallet-name-text} (label :t/main-wallet)] + [text {:style st/wallet-address-text} address]] + + [touchable-highlight {:onPress #(dispatch [:navigate-back])} + [view st/done-button + [text {:style st/done-button-text} (label :t/done)]]]]]) + diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 243fa5e97c..125540b281 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -7,7 +7,9 @@ [status-im.subs] [status-im.components.react :refer [app-registry keyboard - orientation]] + orientation + view + modal]] [status-im.components.main-tabs :refer [main-tabs]] [status-im.contacts.views.contact-list :refer [contact-list]] [status-im.contacts.views.new-contact :refer [new-contact]] @@ -28,7 +30,9 @@ [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] status-im.data-store.core [taoensso.timbre :as log] - [status-im.components.status :as status])) + [status-im.components.status :as status] + [status-im.chat.styles.screen :as st] + [status-im.accounts.views.wallet-qr-code :refer [wallet-qr-code]])) (defn init-back-button-handler! [] (let [new-listener (fn [] @@ -55,7 +59,8 @@ (let [signed-up? (subscribe [:signed-up?]) view-id (subscribe [:get :view-id]) account-id (subscribe [:get :current-account-id]) - keyboard-height (subscribe [:get :keyboard-height])] + keyboard-height (subscribe [:get :keyboard-height]) + modal-view (subscribe [:get :modal])] (log/debug "Current account: " @account-id) (r/create-class {:component-will-mount @@ -75,7 +80,7 @@ (.addListener keyboard "keyboardDidHide" #(when-not (= 0 @keyboard-height) - (dispatch [:set :keyboard-height 0])))) + (dispatch [:set :keyboard-height 0])))) :render (fn [] (when @view-id @@ -101,14 +106,28 @@ :recover recover :confirm confirm :my-profile my-profile)] - [component]))))}))) + [view + {:flex 1} + [component] + (when @modal-view + [view + st/chat-modal + [modal {:animation-type :slide + :transparent false + :on-request-close #(dispatch [:navigate-back])} + (let [component (case @modal-view + :qr-scanner qr-scanner + :wallet-qr-code wallet-qr-code + :confirm confirm + :contact-list-modal contact-list)] + [component])]])]))))}))) (defn init [& [env]] (dispatch-sync [:reset-app]) - (.registerComponent app-registry "StatusIm" #(r/reactify-component app-root) + (.registerComponent app-registry "StatusIm" #(r/reactify-component app-root)) (dispatch [:listen-to-network-status!]) (dispatch [:initialize-crypt]) (dispatch [:initialize-geth]) (status/set-soft-input-mode status/adjust-resize) (dispatch [:load-user-phone-number]) - (init-back-button-handler!))) + (init-back-button-handler!)) diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 127309dd7d..da150c9539 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -24,7 +24,8 @@ valid-mobile-number?]] [status-im.components.status :as status] [status-im.utils.types :refer [json->clj]] - [status-im.chat.handlers.commands :refer [command-prefix]] + status-im.chat.handlers.commands + [status-im.commands.utils :refer [command-prefix]] [status-im.chat.utils :refer [console? not-console?]] [status-im.constants :refer [console-chat-id]] [status-im.utils.gfycat.core :refer [generate-gfy]] @@ -324,8 +325,9 @@ (when (= current-chat-id wallet-chat-id) (dispatch [:cancel-command])) (dispatch [:load-requests! chat-id]) - (when-not commands-loaded? - (dispatch [:load-commands! chat-id])) + (if-not commands-loaded? + (dispatch [:load-commands! chat-id]) + (dispatch [:invoke-chat-loaded-callbacks chat-id])) (if (and (seq messages) (not= (count messages) 1)) db' @@ -333,6 +335,26 @@ load-messages! init-chat)))) +(register-handler :add-chat-loaded-callback + (fn [db [_ chat-id callback]] + (log/debug "Add chat loaded callback: " chat-id callback) + (update-in db [::chat-loaded-callbacks chat-id] conj callback))) + +(register-handler ::clear-chat-loaded-callbacks + (fn [db [_ chat-id]] + (log/debug "Clear chat loaded callback: " chat-id) + (assoc-in db [::chat-loaded-callbacks chat-id] nil))) + +(register-handler :invoke-chat-loaded-callbacks + (u/side-effect! + (fn [db [_ chat-id]] + (log/debug "Invoking chat loaded callbacks: " chat-id) + (let [callbacks (get-in db [::chat-loaded-callbacks chat-id])] + (log/debug "Invoking chat loaded callbacks: " callbacks) + (doseq [callback callbacks] + (callback)) + (dispatch [::clear-chat-loaded-callbacks chat-id]))))) + (defn prepare-chat [{:keys [contacts]} chat-id chat] (let [name (get-in contacts [chat-id :name])] (merge {:chat-id chat-id diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs index 9b77676de9..331363d0cb 100644 --- a/src/status_im/chat/handlers/commands.cljs +++ b/src/status_im/chat/handlers/commands.cljs @@ -9,14 +9,13 @@ [status-im.i18n :as i18n] [status-im.utils.datetime :as time] [status-im.utils.random :as random] - [status-im.utils.platform :as platform])) - -(def command-prefix "c ") + [status-im.utils.platform :as platform] + [taoensso.timbre :as log])) (defn content-by-command [{:keys [type]} content] (if (and (= :command type) content) - (subs content (count command-prefix)) + (subs content (count cu/command-prefix)) content)) (defn invoke-suggestions-handler! @@ -54,7 +53,7 @@ (after cancel-command!) (after #(dispatch [:clear-validation-errors]))] (fn [{:keys [current-chat-id] :as db} [_ content]] - (let [starts-as-command? (str/starts-with? content command-prefix) + (let [starts-as-command? (str/starts-with? content cu/command-prefix) command? (= :command (current-command db :type)) {:keys [parameter-idx command]} (commands/get-command-input db) parameter-name (-> command :params (get parameter-idx) :name)] @@ -65,10 +64,10 @@ (assoc db :canceled-command (and command? (not starts-as-command?))))))) (defn invoke-command-preview! - [{:keys [staged-command] :as db} [_ chat-id]] + [{:keys [staged-command] :as db} [_ command-input chat-id]] (let [{:keys [command id]} staged-command {:keys [name type]} command - parameters (:params (commands/get-command-input db)) + parameters (:params (or command-input (commands/get-command-input db))) path [(if (= :command type) :commands :responses) name :preview] @@ -88,7 +87,7 @@ (register-handler ::validate! (u/side-effect! - (fn [_ [_ {:keys [chat-id handler]} {:keys [error result]}]] + (fn [_ [_ command-input {:keys [chat-id handler]} {:keys [error result]}]] ;; todo handle error (when-not error (let [{:keys [errors validationHandler parameters]} (:returned result)] @@ -97,18 +96,20 @@ validationHandler (dispatch [::validation-handler! + command-input chat-id validationHandler parameters]) :else (if handler (handler) - (dispatch [::finish-command-staging chat-id])))))))) + (dispatch [::finish-command-staging command-input chat-id])))))))) (register-handler :stage-command - (fn [{:keys [current-chat-id current-account-id] :as db}] - (let [command-input (commands/get-command-input db) - command (commands/get-chat-command db)] + (fn [{:keys [current-chat-id current-account-id] :as db} [_ command-input command]] + (let [command-input (or command-input (commands/get-command-input db)) + command (or command (commands/get-chat-command db))] + (log/debug "Staging command 1: " command-input command) (dispatch [::start-command-validation! {:command-input command-input :command command :chat-id current-chat-id @@ -118,9 +119,9 @@ (register-handler ::finish-command-staging [(after #(dispatch [:start-cancel-command])) (after invoke-command-preview!)] - (fn [db [_ chat-id]] + (fn [db [_ command-input chat-id]] (let [db (assoc-in db [:chats chat-id :input-text] nil) - {:keys [command content to-message-id params]} (command-input db) + {:keys [command content to-message-id params]} (or command-input (command-input db)) command-info {:command command :params params :to-message to-message-id @@ -140,7 +141,7 @@ [{:keys [current-chat-id] :as db} [_ command-key type]] (-> db (commands/set-command-input (or type :commands) command-key) - (assoc-in [:chats current-chat-id :command-input :content] command-prefix) + (assoc-in [:chats current-chat-id :command-input :content] cu/command-prefix) (assoc :disable-input true))) (register-handler :set-chat-command @@ -150,6 +151,7 @@ set-chat-command) (defn set-response-command [db [_ to-message-id command-key]] + (log/debug "set-response-command: " to-message-id command-key) (-> db (commands/set-command-input :responses to-message-id command-key) (assoc :canceled-command false))) @@ -186,9 +188,9 @@ :description (apply i18n/label (wrap-params description))}]))) (def validation-handlers - {:phone (fn [chat-id [number]] + {:phone (fn [command-input chat-id [number]] (if (pn/valid-mobile-number? number) - (dispatch [::finish-command-staging chat-id]) + (dispatch [::finish-command-staging command-input chat-id]) (dispatch-error! chat-id :t/phone-number :t/invalid-phone)))}) (defn validator [name] @@ -196,9 +198,9 @@ (register-handler ::validation-handler! (u/side-effect! - (fn [_ [_ chat-id name params]] + (fn [_ [_ command-input chat-id name params]] (when-let [handler (validator name)] - (handler chat-id params))))) + (handler command-input chat-id params))))) (register-handler ::set-validation-error (after #(dispatch [:fix-response-height])) @@ -251,7 +253,7 @@ (status/call-jail chat-id path parameters - #(dispatch [::validate! data %])))))) + #(dispatch [::validate! command-input data %])))))) (register-handler :set-command-parameter (fn [db [_ {:keys [value parameter]}]] diff --git a/src/status_im/chat/handlers/requests.cljs b/src/status_im/chat/handlers/requests.cljs index 60488c7914..6aa15d962b 100644 --- a/src/status_im/chat/handlers/requests.cljs +++ b/src/status_im/chat/handlers/requests.cljs @@ -2,7 +2,8 @@ (:require [re-frame.core :refer [after dispatch enrich]] [status-im.utils.handlers :refer [register-handler]] [status-im.data-store.requests :as requests] - [status-im.utils.handlers :refer [register-handler] :as u])) + [status-im.utils.handlers :refer [register-handler] :as u] + [taoensso.timbre :as log])) (defn store-request! [{:keys [new-request] :as db}] @@ -15,6 +16,7 @@ :type (:command content) :added (js/Date.)} request' (update request :type keyword)] + (log/debug "Adding request: " request') (-> db (update-in [:chats chat-id :requests] conj request') (assoc :new-request request)))) diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index 37d61a20e2..6142299973 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -7,6 +7,7 @@ [status-im.utils.datetime :as time] [re-frame.core :refer [enrich after dispatch path]] [status-im.chat.utils :as cu] + [status-im.commands.utils :as commands-utils] [status-im.constants :refer [text-content-type content-type-command content-type-command-request @@ -18,17 +19,20 @@ [status-im.chat.handlers.console :as console])) (defn prepare-command - [identity chat-id clock-value + [identity chat-id clock-value request {:keys [id preview preview-string params command to-message handler-data]}] - (let [content {:command (command :name) - :params params}] + (let [content (or request {:command (command :name) + :params params})] {:message-id id :from identity :to chat-id :timestamp (time/now-ms) :content (assoc content :preview preview-string - :handler-data handler-data) - :content-type content-type-command + :handler-data handler-data + :type (name (:type command))) + :content-type (if request + content-type-command-request + content-type-command) :outgoing true :preview preview-string :rendered-preview preview @@ -62,7 +66,7 @@ (u/side-effect! (fn [_ [_ {:keys [commands message chat-id] :as params}]] (doseq [{:keys [command] :as message} commands] - (let [params' (assoc params :staged-command message) + (let [params' (assoc params :staged-command message) command-name (:name (:command message))] (if (:sent-to-jail? message) ;; todo there could be other reasons for "long-running" @@ -88,11 +92,13 @@ (register-handler :prepare-command! (u/side-effect! (fn [{:keys [current-public-key] :as db} - [_ add-to-chat-id {:keys [chat-id staged-command handler-data] :as params}]] + [_ add-to-chat-id {:keys [chat-id staged-command command handler-data] :as params}]] (let [{:keys [clock-value]} (get-in db [:chats add-to-chat-id]) + request (:request (:handler-data command)) command' (->> (assoc staged-command :handler-data handler-data) - (prepare-command current-public-key chat-id clock-value) + (prepare-command current-public-key chat-id clock-value request) (cu/check-author-direction db chat-id))] + (log/debug "Handler data: " request handler-data (dissoc params :commands :staged-command)) (dispatch [:clear-command chat-id (:id staged-command)]) (dispatch [::send-command! add-to-chat-id (assoc params :command command')]) @@ -216,24 +222,25 @@ (register-handler ::send-command-protocol! (u/side-effect! - (fn [{:keys [web3 current-public-key chats] :as db} [_ {:keys [chat-id command]}]] - (let [{:keys [content message-id clock-value]} command] - (when (cu/not-console? chat-id) - (let [{:keys [public-key private-key]} (chats chat-id) - {:keys [group-chat]} (get-in db [:chats chat-id]) - payload {:content content - :content-type content-type-command - :timestamp (datetime/now-ms) - :clock-value clock-value} - options {:web3 web3 - :message {:from current-public-key - :message-id message-id - :payload payload}}] - (if group-chat - (protocol/send-group-message! (assoc options - :group-id chat-id - :keypair {:public public-key - :private private-key})) - (protocol/send-message! (assoc-in options - [:message :to] chat-id))))) - (dispatch [:inc-clock chat-id]))))) + (fn [{:keys [web3 current-public-key chats] :as db} + [_ {:keys [chat-id command]}]] + (log/debug "sending command: " command) + (when (cu/not-console? chat-id) + (let [{:keys [public-key private-key]} (chats chat-id) + {:keys [group-chat]} (get-in db [:chats chat-id]) + + payload (-> command + (select-keys [:content :content-type :clock-value]) + (assoc :timestamp (datetime/now-ms))) + options {:web3 web3 + :message {:from current-public-key + :message-id (:message-id command) + :payload payload}}] + (if group-chat + (protocol/send-group-message! (assoc options + :group-id chat-id + :keypair {:public public-key + :private private-key})) + (protocol/send-message! (assoc-in options + [:message :to] chat-id))) + (dispatch [:inc-clock chat-id])))))) diff --git a/src/status_im/chat/handlers/webview_bridge.cljs b/src/status_im/chat/handlers/webview_bridge.cljs index e13667e36a..a3ec25dcd9 100644 --- a/src/status_im/chat/handlers/webview_bridge.cljs +++ b/src/status_im/chat/handlers/webview_bridge.cljs @@ -3,21 +3,70 @@ [status-im.utils.handlers :refer [register-handler]] [status-im.utils.handlers :as u] [status-im.utils.types :as t] - [taoensso.timbre :as log])) + [status-im.i18n :refer [label]] + [taoensso.timbre :as log] + [status-im.models.commands :as commands] + [status-im.commands.utils :as cu] + [status-im.components.status :as s] + [status-im.constants :as c] + [cljs.reader :refer [read-string]])) + +(def web3 (js/require "web3")) + +(defn by-public-key [public-key contacts] + (when-let [{:keys [address]} (contacts public-key)] + (when address {:address address}))) + +(defn scan-qr-handler + [{:keys [contacts]} [_ _ data]] + (log/debug "scaned qr" data) + (let [data' (read-string data) + data'' (cond + (map? data') data' + (.isAddress web3.prototype data') {:address data'} + (string? data') (by-public-key data' contacts) + :else nil)] + (when data'' + (dispatch [:send-to-webview-bridge + {:params data'' + :event (name :webview-send-transaction)}])))) + +(register-handler :webview-address-from-qr + (u/side-effect! scan-qr-handler)) (register-handler :set-webview-bridge (fn [db [_ bridge]] (assoc db :webview-bridge bridge))) -(defn contacts-click-handler [whisper-identity] - #(dispatch [:chat-with-command whisper-identity :send])) +(defn contacts-click-handler [whisper-identity action params] + (log/debug "Contact clicked: " whisper-identity action params) + (dispatch [:navigate-back]) + (when action + (if (= whisper-identity :qr-scan) + (if (= action :send) + (dispatch [:show-scan-qr :webview-address-from-qr]) + (dispatch [:navigate-to-modal :wallet-qr-code])) + (dispatch [:chat-with-command whisper-identity action params])))) + +(register-handler ::send-command + (u/side-effect! + (fn [db [_ command-key params]] + (let [command (commands/get-response-or-command :commands db command-key) + command-input {:content (str cu/command-prefix "0") + :command command + :parameter-idx 0 + :params {"amount" (:amount params)} + :to-message-id nil}] + (log/debug "Staging command: " command-key command command-input) + (dispatch [:stage-command command-input command]))))) + (defn chat-with-command - [_ [_ whisper-identity command]] - (dispatch [:start-chat whisper-identity {} :navigate-back]) + [_ [_ whisper-identity command-key params]] (dispatch [:remove-contacts-click-handler]) - (let [callback #(dispatch [:set-chat-command command])] - (dispatch [:add-commands-loading-callback whisper-identity callback]))) + (dispatch [:add-chat-loaded-callback whisper-identity + #(dispatch [::send-command command-key params])]) + (dispatch [:start-chat whisper-identity])) (register-handler :chat-with-command (u/side-effect! chat-with-command)) @@ -25,15 +74,47 @@ (register-handler :webview-bridge-message (u/side-effect! (fn [_ [_ message-string]] - (let [message (t/json->clj message-string) - event (keyword (:event message))] + (let [{:keys [event options] :as message} (t/json->clj message-string) + event' (keyword event) + params (:data options)] (log/debug (str "message from webview: " message)) - (case event - :webview-send-transaction (dispatch [:navigate-to :contact-list contacts-click-handler]) - (log/error (str "Unknown event: " event))))))) + (case event' + :webview-send-transaction (dispatch [:show-contacts-menu contacts-click-handler :send params]) + :webview-receive-transaction (dispatch [:show-contacts-menu contacts-click-handler :request params]) + :webview-scan-qr (dispatch [:show-scan-qr :webview-address-from-qr]) + :webview-send-eth (dispatch [:webview-send-eth! params]) + (log/error (str "Unknown event: " event'))))))) + +(register-handler :show-contacts-menu + (after #(dispatch [:navigate-to-modal :contact-list-modal])) + (fn [db [_ click-handler action params]] + (assoc db :contacts-click-handler click-handler + :contacts-click-action action + :contacts-click-params params))) + +(def qr-context {:toolbar-title (label :t/address)}) + +(register-handler :show-scan-qr + (after #(dispatch [:navigate-to-modal :qr-scanner qr-context])) + (fn [db [_ click-handler]] + (assoc-in db [:qr-codes qr-context] click-handler))) (register-handler :send-to-webview-bridge (u/side-effect! (fn [{:keys [webview-bridge]} [_ data]] (when webview-bridge (.sendToBridge webview-bridge (t/clj->json data)))))) + +(register-handler :webview-send-eth! + (u/side-effect! + (fn [{:keys [current-account-id]} [_ {:keys [amount address]}]] + (let [context {:from current-account-id} + path [:functions :send] + parameters {:context context + :parameters {:amount amount + :address address}}] + (s/call-jail c/wallet-chat-id + path + parameters + (fn [data] + (log/debug :webview-send-eth-callback data))))))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 2a5ecb6e19..a1bcd771f9 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -5,6 +5,7 @@ animated-view text icon + modal touchable-highlight list-view list-item]] diff --git a/src/status_im/chat/styles/screen.cljs b/src/status_im/chat/styles/screen.cljs index 41863131e2..3a97e188a5 100644 --- a/src/status_im/chat/styles/screen.cljs +++ b/src/status_im/chat/styles/screen.cljs @@ -191,3 +191,10 @@ (def bottom-info-row-text2 {:color "#888888"}) + +(def chat-modal + {:position :absolute + :left 0 + :top 0 + :right 0 + :bottom 0}) \ No newline at end of file diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 51bfd8f0d6..0f1da993a7 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -49,16 +49,17 @@ (reaction (get-in @db [:command-suggestions @chat-id]))))) (register-sub :get-commands - (fn [db _] - (reaction (commands/get-commands @db)))) + (fn [db [_ chat-id]] + (let [current-chat (or chat-id (@db :current-chat-id))] + (reaction (or (get-in @db [:chats current-chat :commands]) {}))))) (register-sub :get-chat-by-id (fn [_ [_ chat-id]] (reaction (chats/get-by-id chat-id)))) (register-sub :get-responses - (fn [db _] - (let [current-chat (@db :current-chat-id)] + (fn [db [_ chat-id]] + (let [current-chat (or chat-id (@db :current-chat-id))] (reaction (or (get-in @db [:chats current-chat :responses]) {}))))) (register-sub :get-commands-and-responses diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs index a3365e82b0..0dd95842bb 100644 --- a/src/status_im/chat/views/message.cljs +++ b/src/status_im/chat/views/message.cljs @@ -104,7 +104,10 @@ (str params))])) (defview message-content-command [{:keys [content rendered-preview chat-id to from outgoing] :as message}] - [commands [:get-commands-and-responses (if outgoing to from)] + [commands [(if (= (:type content) "response") + :get-responses + :get-commands) + (if outgoing to from)] current-chat-id [:get-current-chat-id] contact-chat [:get-in [:chats (if outgoing to from)]]] (let [{:keys [command params]} (parse-command-message-content commands content) @@ -341,6 +344,7 @@ :message-id message-id}]))) :reagent-render (fn [{:keys [outgoing group-chat] :as message}] + (log/debug "I HAVE A MESSAGE: " message) [message-container message [view (let [incoming-group (and group-chat (not outgoing))] diff --git a/src/status_im/chat/views/request_message.cljs b/src/status_im/chat/views/request_message.cljs index e77cca3035..3af3e9fb9b 100644 --- a/src/status_im/chat/views/request_message.cljs +++ b/src/status_im/chat/views/request_message.cljs @@ -9,11 +9,13 @@ touchable-highlight]] [status-im.chat.styles.message :as st] [status-im.models.commands :refer [parse-command-request]] - [status-im.components.animation :as anim])) + [status-im.components.animation :as anim] + [taoensso.timbre :as log])) (def request-message-icon-scale-delay 600) (defn set-chat-command [message-id command] + (log/debug "set-chat-command: " message-id command) (dispatch [:set-response-chat-command message-id (keyword (:name command))])) (defn label [command] @@ -76,7 +78,8 @@ status-initialized? (subscribe [:get :status-module-initialized?])] (fn [{:keys [message-id content from incoming-group]}] (let [commands @commands-atom - {:keys [command content]} (parse-command-request commands content)] + {:keys [command content]} (parse-command-request commands content) + _ (log/debug "message-content: " command content)] [view st/comand-request-view [touchable-highlight {:on-press (when (and (not @answered?) @status-initialized?) diff --git a/src/status_im/chat/views/response.cljs b/src/status_im/chat/views/response.cljs index 462c868689..f57c7da81c 100644 --- a/src/status_im/chat/views/response.cljs +++ b/src/status_im/chat/views/response.cljs @@ -25,7 +25,8 @@ [status-im.utils.datetime :as dt] [taoensso.timbre :as log] [status-im.utils.name :refer [shortened-name]] - [status-im.utils.js-resources :as js-res])) + [status-im.utils.js-resources :as js-res] + [status-im.commands.utils :as cu])) (defn drag-icon [] [view st/drag-container @@ -117,7 +118,7 @@ (when-not (= "about:blank" url) (if loading (dispatch [:set-web-view-url url]) - (dispatch [:set-chat-command-content (str "c " url)]))))) + (dispatch [:set-chat-command-content (str cu/command-prefix url)]))))) (defn web-view-error [] (r/as-element diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 1d81de59b3..e29a4f76f3 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -21,6 +21,7 @@ {:keys [result error]}]] (let [{:keys [context returned]} result {handler-error :error} returned] + (log/debug "command handler: " result error parameters) (cond handler-error (log/debug :error-from-handler handler-error diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index 9f15554901..271e33d2be 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -59,6 +59,7 @@ (status/parse-jail identity file (fn [result] (let [{:keys [error result]} (json->clj result)] + (log/debug "Error parsing commands: " error result) (if error (dispatch [::loading-failed! identity ::error-in-jail error]) (if identity @@ -125,7 +126,8 @@ (after save-commands-js!) (after #(dispatch [:check-autorun])) (after (fn [_ [id]] - (dispatch [:invoke-commands-loading-callbacks id])))] + (dispatch [:invoke-commands-loading-callbacks id]) + (dispatch [:invoke-chat-loaded-callbacks id])))] add-commands) (reg-handler ::add-all-commands diff --git a/src/status_im/commands/utils.cljs b/src/status_im/commands/utils.cljs index e994f340aa..6ed45cd2bf 100644 --- a/src/status_im/commands/utils.cljs +++ b/src/status_im/commands/utils.cljs @@ -6,6 +6,8 @@ [re-frame.core :refer [dispatch trim-v debug]] [status-im.utils.handlers :refer [register-handler]])) +(def command-prefix "c ") + (defn json->clj [json] (when-not (= json "undefined") (js->clj (.parse js/JSON json) :keywordize-keys true))) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 0e78baed5e..485eca828e 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -30,6 +30,13 @@ [db [_ _ click-handler]] (assoc db :contacts-click-handler click-handler)) + +(register-handler :remove-contacts-click-handler + (fn [db] + (dissoc db + :contacts-click-handler + :contacts-click-action))) + (defn save-contact [_ [_ contact]] (contacts/save contact)) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 796a401e79..ccb73898ac 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -211,3 +211,8 @@ (def qr-input {:margin-right 42}) + +(def scan-qr-icon + {:margin 18 + :width 25 + :height 19}) \ No newline at end of file diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs index 2d11be5334..0f1ae659d0 100644 --- a/src/status_im/contacts/views/contact.cljs +++ b/src/status_im/contacts/views/contact.cljs @@ -8,6 +8,18 @@ (defn on-press [whisper-identity] #(dispatch [:start-chat whisper-identity {} :navigation-replace])) +(defn letter-view [letter] + [view st/letter-container + (when letter + [text {:style st/letter-text} letter])]) + +(defview contact-view-with-letter [{:keys [whisper-identity letter] :as contact} click-handler action params] + [touchable-highlight + {:onPress #(click-handler whisper-identity action params)} + [view st/contact-container + [letter-view letter] + [contact-inner-view contact]]]) + (defview contact-view [{:keys [whisper-identity] :as contact}] [chat [:get-chat whisper-identity]] [touchable-highlight diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs index 069cb6f531..775bea5b7c 100644 --- a/src/status_im/contacts/views/contact_list.cljs +++ b/src/status_im/contacts/views/contact_list.cljs @@ -6,16 +6,20 @@ touchable-highlight list-view list-item]] - [status-im.contacts.views.contact :refer [contact-view on-press]] + [status-im.contacts.views.contact :refer [contact-view + on-press + contact-view-with-letter]] [status-im.components.status-bar :refer [status-bar]] [status-im.components.toolbar.view :refer [toolbar]] [status-im.components.toolbar.styles :refer [toolbar-background1]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] - [status-im.components.styles :refer [icon-search]] + [status-im.components.styles :refer [icon-search + icon-back]] [status-im.contacts.styles :as st] [status-im.utils.listview :as lw] [status-im.i18n :refer [label]] - [status-im.utils.platform :refer [platform-specific]])) + [status-im.utils.platform :refer [platform-specific]] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]])) (defn new-group-chat-view [] [touchable-highlight @@ -29,21 +33,42 @@ [text {:style st/name-text} (label :t/new-group-chat)]]]]]) -(defn render-row [click-handler] +(defn render-row [chat-modal click-handler action params] (fn [row _ _] (list-item - [contact-view row - (or click-handler - (let [whisper-identity (:whisper-identity row)] - (on-press whisper-identity)))]))) + (if chat-modal + [contact-view-with-letter row click-handler action params] + [contact-view row + (or click-handler + (let [whisper-identity (:whisper-identity row)] + (on-press whisper-identity)))])))) + +(defn qr-scan [click-handler action] + [touchable-highlight + {:onPress #(click-handler :qr-scan action)} + [view st/contact-container + [view st/contact-inner-container + [image {:source {:uri :icon_menu_group} + :style st/scan-qr-icon}] + [view st/info-container + [text {:style st/name-text + :number-of-lines 1} + (label :t/scan-qr)]]]]]) (defview contact-list-toolbar [] - [group [:get :contacts-group]] + [group [:get :contacts-group] + modal [:get :modal]] [view [status-bar] - [toolbar {:title (label (if (= group :dapps) - :t/contacts-group-dapps - :t/contacts-group-new-chat)) + [toolbar {:title (label (if-not group + :t/contacts + (if (= group :dapps) + :t/contacts-group-dapps + :t/contacts-group-new-chat))) + :nav-action (when modal + {:handler #(dispatch [:navigate-back]) + :image {:source {:uri :icon_back} + :style icon-back}}) :background-color toolbar-background1 :style (get-in platform-specific [:component-styles :toolbar]) :actions [{:image {:source {:uri :icon_search} @@ -53,21 +78,26 @@ (defview contact-list [] [contacts [:contacts-with-letters] group [:get :contacts-group] - click-handler [:get :contacts-click-handler]] + modal [:get :modal] + click-handler [:get :contacts-click-handler] + action [:get :contacts-click-action] + params [:get :contacts-click-params]] (let [show-new-group-chat? (and (= group :people) (get-in platform-specific [:chats :new-chat-in-toolbar?]))] [drawer-view [view st/contacts-list-container [contact-list-toolbar] ;; todo add stub + (when modal + [qr-scan click-handler action]) (when contacts [list-view {:dataSource (lw/to-datasource contacts) :enableEmptySections true - :renderRow (render-row click-handler) + :renderRow (render-row modal click-handler action params) :renderHeader #(list-item - [view - (if show-new-group-chat? - [new-group-chat-view]) - [view st/spacing-top]]) + [view + (if show-new-group-chat? + [new-group-chat-view]) + [view st/spacing-top]]) :renderFooter #(list-item [view st/spacing-bottom]) :style st/contacts-list}])]])) diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs index f02d1d8b11..6f0a1ce0c0 100644 --- a/src/status_im/ios/core.cljs +++ b/src/status_im/ios/core.cljs @@ -3,7 +3,9 @@ [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.handlers] [status-im.subs] - [status-im.components.react :refer [app-registry + [status-im.components.react :refer [view + modal + app-registry keyboard orientation]] [status-im.components.main-tabs :refer [main-tabs]] @@ -24,7 +26,9 @@ [status-im.profile.screen :refer [profile my-profile]] [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] status-im.data-store.core - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.chat.styles.screen :as st] + [status-im.accounts.views.wallet-qr-code :refer [wallet-qr-code]])) (defn orientation->keyword [o] (keyword (.toLowerCase o))) @@ -38,7 +42,7 @@ (defn app-root [] (let [signed-up? (subscribe [:signed-up?]) - _ (log/debug "signed up: " @signed-up?) + modal-view (subscribe [:get :modal]) view-id (subscribe [:get :view-id]) account-id (subscribe [:get :current-account-id]) keyboard-height (subscribe [:get :keyboard-height])] @@ -86,7 +90,21 @@ :login login :confirm confirm :my-profile my-profile)] - [component]))))}))) + [view + {:flex 1} + [component] + (when @modal-view + [view + st/chat-modal + [modal {:animation-type :slide + :transparent false + :on-request-close #(dispatch [:navigate-back])} + (let [component (case @modal-view + :qr-scanner qr-scanner + :wallet-qr-code wallet-qr-code + :confirm confirm + :contact-list-modal contact-list)] + [component])]])]))))}))) (defn init [] (dispatch-sync [:reset-app]) diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs index 9bc8b60632..264af071bc 100644 --- a/src/status_im/models/commands.cljs +++ b/src/status_im/models/commands.cljs @@ -70,4 +70,5 @@ (update content :command #((keyword %) commands))) (defn parse-command-request [commands content] + (log/debug "parse-command-request: " commands content) (update content :command #((keyword %) commands))) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index a5966b2515..ba2a990634 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -24,6 +24,11 @@ (defmethod preload-data! :default [db _] db) +(defn -preload-data! [{:keys [was-modal?] :as db} & args] + (if was-modal? + (dissoc db :was-modal) + (apply preload-data! db args))) + (register-handler :navigate-forget (enrich preload-data!) (fn [db [_ new-view-id]] @@ -36,16 +41,25 @@ db (push-view db new-view-id)))) +(register-handler :navigate-to-modal + (enrich preload-data!) + (fn [db [_ modal-view]] + (assoc db :modal modal-view))) + (register-handler :navigation-replace (enrich preload-data!) (fn [db [_ view-id]] (replace-view db view-id))) (register-handler :navigate-back - (enrich preload-data!) - (fn [{:keys [navigation-stack view-id] :as db} _] - (if (>= 1 (count navigation-stack)) - db + (enrich -preload-data!) + (fn [{:keys [navigation-stack view-id modal] :as db} _] + (cond + modal (assoc db :modal nil + :was-modal? true) + (>= 1 (count navigation-stack)) db + + :else (let [[previous-view-id :as navigation-stack'] (pop navigation-stack) first-in-stack (first navigation-stack)] (if (= view-id first-in-stack) @@ -67,10 +81,6 @@ (fn [db [_]] (assoc db :prev-tab-view-id nil))) -(register-handler :remove-contacts-click-handler - (fn [db] - (dissoc db :contacts-click-handler))) - (defn show-profile [db [_ identity]] (-> db diff --git a/src/status_im/qr_scanner/screen.cljs b/src/status_im/qr_scanner/screen.cljs index 9aa2d7630e..d252699a11 100644 --- a/src/status_im/qr_scanner/screen.cljs +++ b/src/status_im/qr_scanner/screen.cljs @@ -4,20 +4,25 @@ [status-im.components.react :refer [view image]] [status-im.components.camera :refer [camera]] - [status-im.components.styles :refer [icon-search]] + [status-im.components.styles :refer [icon-search + icon-back]] [status-im.components.status-bar :refer [status-bar]] [status-im.components.toolbar.view :refer [toolbar]] [status-im.components.toolbar.styles :refer [toolbar-background1]] [status-im.qr-scanner.styles :as st] [status-im.utils.types :refer [json->clj]] - [status-im.components.styles :as cst] [clojure.string :as str])) -(defn qr-scanner-toolbar [title] +(defview qr-scanner-toolbar [title] + [modal [:get :modal]] [view [status-bar] [toolbar {:title title :background-color toolbar-background1 + :nav-action (when modal + {:handler #(dispatch [:navigate-back]) + :image {:source {:uri :icon_back} + :style icon-back}}) :actions [{:image {:source {:uri :icon_lock_white} :style icon-search} :handler #()}]}]]) @@ -30,6 +35,7 @@ (let [data (-> (.-data code) (str/replace #"ethereum:" ""))] (dispatch [:set-qr-code identifier data]))) + :barCodeTypes [:qr] :style st/barcode-scanner}] [view st/rectangle-container [view st/rectangle diff --git a/src/status_im/transactions/handlers.cljs b/src/status_im/transactions/handlers.cljs index 50bd2113ca..f3c60b7d94 100644 --- a/src/status_im/transactions/handlers.cljs +++ b/src/status_im/transactions/handlers.cljs @@ -30,7 +30,7 @@ (assoc-in [:confirm-transactions :password] 0))) (defn on-unlock - [ids password previous-view-id] + [ids password] (dispatch [:set :wrong-password? false]) (doseq [id ids] (status/complete-transaction @@ -38,15 +38,13 @@ password #(dispatch [:transaction-completed {:id id - :response % - :previous-view-id previous-view-id}])))) + :response %}])))) (register-handler :accept-transactions (u/side-effect! - (fn [{:keys [transactions navigation-stack]} [_ password]] - (let [ids (keys transactions) - previous-view-id (second navigation-stack)] - (on-unlock ids password previous-view-id))))) + (fn [{:keys [transactions]} [_ password]] + (let [ids (keys transactions)] + (on-unlock ids password))))) (register-handler :deny-transactions (u/side-effect! @@ -125,7 +123,7 @@ (remove-pending-message db message-id))) (register-handler :transaction-queued - (after #(dispatch [:navigate-to :confirm])) + (after #(dispatch [:navigate-to-modal :confirm])) (fn [db [_ {:keys [id message_id args]}]] (let [{:keys [from to value]} args transaction {:id id @@ -137,7 +135,7 @@ (register-handler :transaction-completed (u/side-effect! - (fn [{:keys [transactions]} [_ {:keys [id response previous-view-id]}]] + (fn [{:keys [transactions]} [_ {:keys [id response]}]] (let [{:keys [hash error] :as parsed-response} (t/json->clj response) {:keys [message-id]} (transactions id)] (log/debug :parsed-response parsed-response) @@ -148,7 +146,7 @@ :message-id message-id}]) (dispatch [::check-completed-transaction! {:message-id message-id}]) - (dispatch [:navigation-replace previous-view-id])) + (dispatch [:navigate-back])) (dispatch [::remove-transaction id]))))))) (register-handler ::add-transactions-hash diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 59a8ca8979..293faebb88 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -201,6 +201,10 @@ :recover-access "Recover access" :add-account "Add account" + ;wallet-qr-code + :done "Done" + :main-wallet "Main Wallet" + ;validation :invalid-phone "Invalid phone number" :amount "Amount"