implement new wallet flow
This commit is contained in:
parent
788cbc34f3
commit
083ade766e
|
@ -117,7 +117,7 @@ function sendTransaction(params, context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status.command({
|
var send = {
|
||||||
name: "send",
|
name: "send",
|
||||||
icon: "money_white",
|
icon: "money_white",
|
||||||
color: "#5fc48d",
|
color: "#5fc48d",
|
||||||
|
@ -134,4 +134,36 @@ status.command({
|
||||||
},
|
},
|
||||||
handler: sendTransaction,
|
handler: sendTransaction,
|
||||||
validator: validateBalance
|
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"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
var _status_catalog = {
|
var _status_catalog = {
|
||||||
commands: {},
|
commands: {},
|
||||||
responses: {}
|
responses: {},
|
||||||
|
functions: {}
|
||||||
},
|
},
|
||||||
status = {};
|
status = {};
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ Command.prototype.create = function (com) {
|
||||||
this.preview = com.preview;
|
this.preview = com.preview;
|
||||||
this["suggestions-trigger"] = com.suggestionsTrigger || "on-change";
|
this["suggestions-trigger"] = com.suggestionsTrigger || "on-change";
|
||||||
this.fullscreen = com.fullscreen;
|
this.fullscreen = com.fullscreen;
|
||||||
|
this.request = com.request;
|
||||||
this.addToCatalog();
|
this.addToCatalog();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -153,6 +155,9 @@ var status = {
|
||||||
var response = new Response();
|
var response = new Response();
|
||||||
return response.create(h);
|
return response.create(h);
|
||||||
},
|
},
|
||||||
|
registerFunction: function (name, fn){
|
||||||
|
_status_catalog.functions[name] = fn;
|
||||||
|
},
|
||||||
autorun: function (commandName) {
|
autorun: function (commandName) {
|
||||||
_status_catalog.autorun = commandName;
|
_status_catalog.autorun = commandName;
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,3 +25,13 @@ status.command({
|
||||||
});
|
});
|
||||||
|
|
||||||
status.autorun("browse");
|
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);
|
||||||
|
})
|
||||||
|
|
|
@ -113,3 +113,56 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:color color-white
|
:color color-white
|
||||||
:fontSize 16})
|
: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})
|
|
@ -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)]]]]])
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
[status-im.subs]
|
[status-im.subs]
|
||||||
[status-im.components.react :refer [app-registry
|
[status-im.components.react :refer [app-registry
|
||||||
keyboard
|
keyboard
|
||||||
orientation]]
|
orientation
|
||||||
|
view
|
||||||
|
modal]]
|
||||||
[status-im.components.main-tabs :refer [main-tabs]]
|
[status-im.components.main-tabs :refer [main-tabs]]
|
||||||
[status-im.contacts.views.contact-list :refer [contact-list]]
|
[status-im.contacts.views.contact-list :refer [contact-list]]
|
||||||
[status-im.contacts.views.new-contact :refer [new-contact]]
|
[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.profile.photo-capture.screen :refer [profile-photo-capture]]
|
||||||
status-im.data-store.core
|
status-im.data-store.core
|
||||||
[taoensso.timbre :as log]
|
[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! []
|
(defn init-back-button-handler! []
|
||||||
(let [new-listener (fn []
|
(let [new-listener (fn []
|
||||||
|
@ -55,7 +59,8 @@
|
||||||
(let [signed-up? (subscribe [:signed-up?])
|
(let [signed-up? (subscribe [:signed-up?])
|
||||||
view-id (subscribe [:get :view-id])
|
view-id (subscribe [:get :view-id])
|
||||||
account-id (subscribe [:get :current-account-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)
|
(log/debug "Current account: " @account-id)
|
||||||
(r/create-class
|
(r/create-class
|
||||||
{:component-will-mount
|
{:component-will-mount
|
||||||
|
@ -75,7 +80,7 @@
|
||||||
(.addListener keyboard
|
(.addListener keyboard
|
||||||
"keyboardDidHide"
|
"keyboardDidHide"
|
||||||
#(when-not (= 0 @keyboard-height)
|
#(when-not (= 0 @keyboard-height)
|
||||||
(dispatch [:set :keyboard-height 0]))))
|
(dispatch [:set :keyboard-height 0]))))
|
||||||
:render
|
:render
|
||||||
(fn []
|
(fn []
|
||||||
(when @view-id
|
(when @view-id
|
||||||
|
@ -101,14 +106,28 @@
|
||||||
:recover recover
|
:recover recover
|
||||||
:confirm confirm
|
:confirm confirm
|
||||||
:my-profile my-profile)]
|
: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]]
|
(defn init [& [env]]
|
||||||
(dispatch-sync [:reset-app])
|
(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 [:listen-to-network-status!])
|
||||||
(dispatch [:initialize-crypt])
|
(dispatch [:initialize-crypt])
|
||||||
(dispatch [:initialize-geth])
|
(dispatch [:initialize-geth])
|
||||||
(status/set-soft-input-mode status/adjust-resize)
|
(status/set-soft-input-mode status/adjust-resize)
|
||||||
(dispatch [:load-user-phone-number])
|
(dispatch [:load-user-phone-number])
|
||||||
(init-back-button-handler!)))
|
(init-back-button-handler!))
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
valid-mobile-number?]]
|
valid-mobile-number?]]
|
||||||
[status-im.components.status :as status]
|
[status-im.components.status :as status]
|
||||||
[status-im.utils.types :refer [json->clj]]
|
[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.chat.utils :refer [console? not-console?]]
|
||||||
[status-im.constants :refer [console-chat-id]]
|
[status-im.constants :refer [console-chat-id]]
|
||||||
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
||||||
|
@ -324,8 +325,9 @@
|
||||||
(when (= current-chat-id wallet-chat-id)
|
(when (= current-chat-id wallet-chat-id)
|
||||||
(dispatch [:cancel-command]))
|
(dispatch [:cancel-command]))
|
||||||
(dispatch [:load-requests! chat-id])
|
(dispatch [:load-requests! chat-id])
|
||||||
(when-not commands-loaded?
|
(if-not commands-loaded?
|
||||||
(dispatch [:load-commands! chat-id]))
|
(dispatch [:load-commands! chat-id])
|
||||||
|
(dispatch [:invoke-chat-loaded-callbacks chat-id]))
|
||||||
(if (and (seq messages)
|
(if (and (seq messages)
|
||||||
(not= (count messages) 1))
|
(not= (count messages) 1))
|
||||||
db'
|
db'
|
||||||
|
@ -333,6 +335,26 @@
|
||||||
load-messages!
|
load-messages!
|
||||||
init-chat))))
|
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]
|
(defn prepare-chat [{:keys [contacts]} chat-id chat]
|
||||||
(let [name (get-in contacts [chat-id :name])]
|
(let [name (get-in contacts [chat-id :name])]
|
||||||
(merge {:chat-id chat-id
|
(merge {:chat-id chat-id
|
||||||
|
|
|
@ -9,14 +9,13 @@
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.utils.datetime :as time]
|
[status-im.utils.datetime :as time]
|
||||||
[status-im.utils.random :as random]
|
[status-im.utils.random :as random]
|
||||||
[status-im.utils.platform :as platform]))
|
[status-im.utils.platform :as platform]
|
||||||
|
[taoensso.timbre :as log]))
|
||||||
(def command-prefix "c ")
|
|
||||||
|
|
||||||
(defn content-by-command
|
(defn content-by-command
|
||||||
[{:keys [type]} content]
|
[{:keys [type]} content]
|
||||||
(if (and (= :command type) content)
|
(if (and (= :command type) content)
|
||||||
(subs content (count command-prefix))
|
(subs content (count cu/command-prefix))
|
||||||
content))
|
content))
|
||||||
|
|
||||||
(defn invoke-suggestions-handler!
|
(defn invoke-suggestions-handler!
|
||||||
|
@ -54,7 +53,7 @@
|
||||||
(after cancel-command!)
|
(after cancel-command!)
|
||||||
(after #(dispatch [:clear-validation-errors]))]
|
(after #(dispatch [:clear-validation-errors]))]
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ content]]
|
(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))
|
command? (= :command (current-command db :type))
|
||||||
{:keys [parameter-idx command]} (commands/get-command-input db)
|
{:keys [parameter-idx command]} (commands/get-command-input db)
|
||||||
parameter-name (-> command :params (get parameter-idx) :name)]
|
parameter-name (-> command :params (get parameter-idx) :name)]
|
||||||
|
@ -65,10 +64,10 @@
|
||||||
(assoc db :canceled-command (and command? (not starts-as-command?)))))))
|
(assoc db :canceled-command (and command? (not starts-as-command?)))))))
|
||||||
|
|
||||||
(defn invoke-command-preview!
|
(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
|
(let [{:keys [command id]} staged-command
|
||||||
{:keys [name type]} 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)
|
path [(if (= :command type) :commands :responses)
|
||||||
name
|
name
|
||||||
:preview]
|
:preview]
|
||||||
|
@ -88,7 +87,7 @@
|
||||||
|
|
||||||
(register-handler ::validate!
|
(register-handler ::validate!
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [_ {:keys [chat-id handler]} {:keys [error result]}]]
|
(fn [_ [_ command-input {:keys [chat-id handler]} {:keys [error result]}]]
|
||||||
;; todo handle error
|
;; todo handle error
|
||||||
(when-not error
|
(when-not error
|
||||||
(let [{:keys [errors validationHandler parameters]} (:returned result)]
|
(let [{:keys [errors validationHandler parameters]} (:returned result)]
|
||||||
|
@ -97,18 +96,20 @@
|
||||||
|
|
||||||
validationHandler
|
validationHandler
|
||||||
(dispatch [::validation-handler!
|
(dispatch [::validation-handler!
|
||||||
|
command-input
|
||||||
chat-id
|
chat-id
|
||||||
validationHandler
|
validationHandler
|
||||||
parameters])
|
parameters])
|
||||||
|
|
||||||
:else (if handler
|
:else (if handler
|
||||||
(handler)
|
(handler)
|
||||||
(dispatch [::finish-command-staging chat-id]))))))))
|
(dispatch [::finish-command-staging command-input chat-id]))))))))
|
||||||
|
|
||||||
(register-handler :stage-command
|
(register-handler :stage-command
|
||||||
(fn [{:keys [current-chat-id current-account-id] :as db}]
|
(fn [{:keys [current-chat-id current-account-id] :as db} [_ command-input command]]
|
||||||
(let [command-input (commands/get-command-input db)
|
(let [command-input (or command-input (commands/get-command-input db))
|
||||||
command (commands/get-chat-command 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
|
(dispatch [::start-command-validation! {:command-input command-input
|
||||||
:command command
|
:command command
|
||||||
:chat-id current-chat-id
|
:chat-id current-chat-id
|
||||||
|
@ -118,9 +119,9 @@
|
||||||
(register-handler ::finish-command-staging
|
(register-handler ::finish-command-staging
|
||||||
[(after #(dispatch [:start-cancel-command]))
|
[(after #(dispatch [:start-cancel-command]))
|
||||||
(after invoke-command-preview!)]
|
(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)
|
(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
|
command-info {:command command
|
||||||
:params params
|
:params params
|
||||||
:to-message to-message-id
|
:to-message to-message-id
|
||||||
|
@ -140,7 +141,7 @@
|
||||||
[{:keys [current-chat-id] :as db} [_ command-key type]]
|
[{:keys [current-chat-id] :as db} [_ command-key type]]
|
||||||
(-> db
|
(-> db
|
||||||
(commands/set-command-input (or type :commands) command-key)
|
(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)))
|
(assoc :disable-input true)))
|
||||||
|
|
||||||
(register-handler :set-chat-command
|
(register-handler :set-chat-command
|
||||||
|
@ -150,6 +151,7 @@
|
||||||
set-chat-command)
|
set-chat-command)
|
||||||
|
|
||||||
(defn set-response-command [db [_ to-message-id command-key]]
|
(defn set-response-command [db [_ to-message-id command-key]]
|
||||||
|
(log/debug "set-response-command: " to-message-id command-key)
|
||||||
(-> db
|
(-> db
|
||||||
(commands/set-command-input :responses to-message-id command-key)
|
(commands/set-command-input :responses to-message-id command-key)
|
||||||
(assoc :canceled-command false)))
|
(assoc :canceled-command false)))
|
||||||
|
@ -186,9 +188,9 @@
|
||||||
:description (apply i18n/label (wrap-params description))}])))
|
:description (apply i18n/label (wrap-params description))}])))
|
||||||
|
|
||||||
(def validation-handlers
|
(def validation-handlers
|
||||||
{:phone (fn [chat-id [number]]
|
{:phone (fn [command-input chat-id [number]]
|
||||||
(if (pn/valid-mobile-number? 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)))})
|
(dispatch-error! chat-id :t/phone-number :t/invalid-phone)))})
|
||||||
|
|
||||||
(defn validator [name]
|
(defn validator [name]
|
||||||
|
@ -196,9 +198,9 @@
|
||||||
|
|
||||||
(register-handler ::validation-handler!
|
(register-handler ::validation-handler!
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [_ chat-id name params]]
|
(fn [_ [_ command-input chat-id name params]]
|
||||||
(when-let [handler (validator name)]
|
(when-let [handler (validator name)]
|
||||||
(handler chat-id params)))))
|
(handler command-input chat-id params)))))
|
||||||
|
|
||||||
(register-handler ::set-validation-error
|
(register-handler ::set-validation-error
|
||||||
(after #(dispatch [:fix-response-height]))
|
(after #(dispatch [:fix-response-height]))
|
||||||
|
@ -251,7 +253,7 @@
|
||||||
(status/call-jail chat-id
|
(status/call-jail chat-id
|
||||||
path
|
path
|
||||||
parameters
|
parameters
|
||||||
#(dispatch [::validate! data %]))))))
|
#(dispatch [::validate! command-input data %]))))))
|
||||||
|
|
||||||
(register-handler :set-command-parameter
|
(register-handler :set-command-parameter
|
||||||
(fn [db [_ {:keys [value parameter]}]]
|
(fn [db [_ {:keys [value parameter]}]]
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
(:require [re-frame.core :refer [after dispatch enrich]]
|
(:require [re-frame.core :refer [after dispatch enrich]]
|
||||||
[status-im.utils.handlers :refer [register-handler]]
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.data-store.requests :as requests]
|
[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!
|
(defn store-request!
|
||||||
[{:keys [new-request] :as db}]
|
[{:keys [new-request] :as db}]
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
:type (:command content)
|
:type (:command content)
|
||||||
:added (js/Date.)}
|
:added (js/Date.)}
|
||||||
request' (update request :type keyword)]
|
request' (update request :type keyword)]
|
||||||
|
(log/debug "Adding request: " request')
|
||||||
(-> db
|
(-> db
|
||||||
(update-in [:chats chat-id :requests] conj request')
|
(update-in [:chats chat-id :requests] conj request')
|
||||||
(assoc :new-request request))))
|
(assoc :new-request request))))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
[status-im.utils.datetime :as time]
|
[status-im.utils.datetime :as time]
|
||||||
[re-frame.core :refer [enrich after dispatch path]]
|
[re-frame.core :refer [enrich after dispatch path]]
|
||||||
[status-im.chat.utils :as cu]
|
[status-im.chat.utils :as cu]
|
||||||
|
[status-im.commands.utils :as commands-utils]
|
||||||
[status-im.constants :refer [text-content-type
|
[status-im.constants :refer [text-content-type
|
||||||
content-type-command
|
content-type-command
|
||||||
content-type-command-request
|
content-type-command-request
|
||||||
|
@ -18,17 +19,20 @@
|
||||||
[status-im.chat.handlers.console :as console]))
|
[status-im.chat.handlers.console :as console]))
|
||||||
|
|
||||||
(defn prepare-command
|
(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]}]
|
{:keys [id preview preview-string params command to-message handler-data]}]
|
||||||
(let [content {:command (command :name)
|
(let [content (or request {:command (command :name)
|
||||||
:params params}]
|
:params params})]
|
||||||
{:message-id id
|
{:message-id id
|
||||||
:from identity
|
:from identity
|
||||||
:to chat-id
|
:to chat-id
|
||||||
:timestamp (time/now-ms)
|
:timestamp (time/now-ms)
|
||||||
:content (assoc content :preview preview-string
|
:content (assoc content :preview preview-string
|
||||||
:handler-data handler-data)
|
:handler-data handler-data
|
||||||
:content-type content-type-command
|
:type (name (:type command)))
|
||||||
|
:content-type (if request
|
||||||
|
content-type-command-request
|
||||||
|
content-type-command)
|
||||||
:outgoing true
|
:outgoing true
|
||||||
:preview preview-string
|
:preview preview-string
|
||||||
:rendered-preview preview
|
:rendered-preview preview
|
||||||
|
@ -62,7 +66,7 @@
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [_ {:keys [commands message chat-id] :as params}]]
|
(fn [_ [_ {:keys [commands message chat-id] :as params}]]
|
||||||
(doseq [{:keys [command] :as message} commands]
|
(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))]
|
command-name (:name (:command message))]
|
||||||
(if (:sent-to-jail? message)
|
(if (:sent-to-jail? message)
|
||||||
;; todo there could be other reasons for "long-running"
|
;; todo there could be other reasons for "long-running"
|
||||||
|
@ -88,11 +92,13 @@
|
||||||
(register-handler :prepare-command!
|
(register-handler :prepare-command!
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [{:keys [current-public-key] :as db}
|
(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])
|
(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)
|
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))]
|
(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 [:clear-command chat-id (:id staged-command)])
|
||||||
(dispatch [::send-command! add-to-chat-id (assoc params :command command')])
|
(dispatch [::send-command! add-to-chat-id (assoc params :command command')])
|
||||||
|
|
||||||
|
@ -216,24 +222,25 @@
|
||||||
|
|
||||||
(register-handler ::send-command-protocol!
|
(register-handler ::send-command-protocol!
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [{:keys [web3 current-public-key chats] :as db} [_ {:keys [chat-id command]}]]
|
(fn [{:keys [web3 current-public-key chats] :as db}
|
||||||
(let [{:keys [content message-id clock-value]} command]
|
[_ {:keys [chat-id command]}]]
|
||||||
(when (cu/not-console? chat-id)
|
(log/debug "sending command: " command)
|
||||||
(let [{:keys [public-key private-key]} (chats chat-id)
|
(when (cu/not-console? chat-id)
|
||||||
{:keys [group-chat]} (get-in db [:chats chat-id])
|
(let [{:keys [public-key private-key]} (chats chat-id)
|
||||||
payload {:content content
|
{:keys [group-chat]} (get-in db [:chats chat-id])
|
||||||
:content-type content-type-command
|
|
||||||
:timestamp (datetime/now-ms)
|
payload (-> command
|
||||||
:clock-value clock-value}
|
(select-keys [:content :content-type :clock-value])
|
||||||
options {:web3 web3
|
(assoc :timestamp (datetime/now-ms)))
|
||||||
:message {:from current-public-key
|
options {:web3 web3
|
||||||
:message-id message-id
|
:message {:from current-public-key
|
||||||
:payload payload}}]
|
:message-id (:message-id command)
|
||||||
(if group-chat
|
:payload payload}}]
|
||||||
(protocol/send-group-message! (assoc options
|
(if group-chat
|
||||||
:group-id chat-id
|
(protocol/send-group-message! (assoc options
|
||||||
:keypair {:public public-key
|
:group-id chat-id
|
||||||
:private private-key}))
|
:keypair {:public public-key
|
||||||
(protocol/send-message! (assoc-in options
|
:private private-key}))
|
||||||
[:message :to] chat-id)))))
|
(protocol/send-message! (assoc-in options
|
||||||
(dispatch [:inc-clock chat-id])))))
|
[:message :to] chat-id)))
|
||||||
|
(dispatch [:inc-clock chat-id]))))))
|
||||||
|
|
|
@ -3,21 +3,70 @@
|
||||||
[status-im.utils.handlers :refer [register-handler]]
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.utils.handlers :as u]
|
[status-im.utils.handlers :as u]
|
||||||
[status-im.utils.types :as t]
|
[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
|
(register-handler :set-webview-bridge
|
||||||
(fn [db [_ bridge]]
|
(fn [db [_ bridge]]
|
||||||
(assoc db :webview-bridge bridge)))
|
(assoc db :webview-bridge bridge)))
|
||||||
|
|
||||||
(defn contacts-click-handler [whisper-identity]
|
(defn contacts-click-handler [whisper-identity action params]
|
||||||
#(dispatch [:chat-with-command whisper-identity :send]))
|
(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
|
(defn chat-with-command
|
||||||
[_ [_ whisper-identity command]]
|
[_ [_ whisper-identity command-key params]]
|
||||||
(dispatch [:start-chat whisper-identity {} :navigate-back])
|
|
||||||
(dispatch [:remove-contacts-click-handler])
|
(dispatch [:remove-contacts-click-handler])
|
||||||
(let [callback #(dispatch [:set-chat-command command])]
|
(dispatch [:add-chat-loaded-callback whisper-identity
|
||||||
(dispatch [:add-commands-loading-callback whisper-identity callback])))
|
#(dispatch [::send-command command-key params])])
|
||||||
|
(dispatch [:start-chat whisper-identity]))
|
||||||
|
|
||||||
(register-handler :chat-with-command
|
(register-handler :chat-with-command
|
||||||
(u/side-effect! chat-with-command))
|
(u/side-effect! chat-with-command))
|
||||||
|
@ -25,15 +74,47 @@
|
||||||
(register-handler :webview-bridge-message
|
(register-handler :webview-bridge-message
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [_ message-string]]
|
(fn [_ [_ message-string]]
|
||||||
(let [message (t/json->clj message-string)
|
(let [{:keys [event options] :as message} (t/json->clj message-string)
|
||||||
event (keyword (:event message))]
|
event' (keyword event)
|
||||||
|
params (:data options)]
|
||||||
(log/debug (str "message from webview: " message))
|
(log/debug (str "message from webview: " message))
|
||||||
(case event
|
(case event'
|
||||||
:webview-send-transaction (dispatch [:navigate-to :contact-list contacts-click-handler])
|
:webview-send-transaction (dispatch [:show-contacts-menu contacts-click-handler :send params])
|
||||||
(log/error (str "Unknown event: " event)))))))
|
: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
|
(register-handler :send-to-webview-bridge
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [{:keys [webview-bridge]} [_ data]]
|
(fn [{:keys [webview-bridge]} [_ data]]
|
||||||
(when webview-bridge
|
(when webview-bridge
|
||||||
(.sendToBridge webview-bridge (t/clj->json data))))))
|
(.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)))))))
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
animated-view
|
animated-view
|
||||||
text
|
text
|
||||||
icon
|
icon
|
||||||
|
modal
|
||||||
touchable-highlight
|
touchable-highlight
|
||||||
list-view
|
list-view
|
||||||
list-item]]
|
list-item]]
|
||||||
|
|
|
@ -191,3 +191,10 @@
|
||||||
|
|
||||||
(def bottom-info-row-text2
|
(def bottom-info-row-text2
|
||||||
{:color "#888888"})
|
{:color "#888888"})
|
||||||
|
|
||||||
|
(def chat-modal
|
||||||
|
{:position :absolute
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:right 0
|
||||||
|
:bottom 0})
|
|
@ -49,16 +49,17 @@
|
||||||
(reaction (get-in @db [:command-suggestions @chat-id])))))
|
(reaction (get-in @db [:command-suggestions @chat-id])))))
|
||||||
|
|
||||||
(register-sub :get-commands
|
(register-sub :get-commands
|
||||||
(fn [db _]
|
(fn [db [_ chat-id]]
|
||||||
(reaction (commands/get-commands @db))))
|
(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
|
(register-sub :get-chat-by-id
|
||||||
(fn [_ [_ chat-id]]
|
(fn [_ [_ chat-id]]
|
||||||
(reaction (chats/get-by-id chat-id))))
|
(reaction (chats/get-by-id chat-id))))
|
||||||
|
|
||||||
(register-sub :get-responses
|
(register-sub :get-responses
|
||||||
(fn [db _]
|
(fn [db [_ chat-id]]
|
||||||
(let [current-chat (@db :current-chat-id)]
|
(let [current-chat (or chat-id (@db :current-chat-id))]
|
||||||
(reaction (or (get-in @db [:chats current-chat :responses]) {})))))
|
(reaction (or (get-in @db [:chats current-chat :responses]) {})))))
|
||||||
|
|
||||||
(register-sub :get-commands-and-responses
|
(register-sub :get-commands-and-responses
|
||||||
|
|
|
@ -104,7 +104,10 @@
|
||||||
(str params))]))
|
(str params))]))
|
||||||
|
|
||||||
(defview message-content-command [{:keys [content rendered-preview chat-id to from outgoing] :as message}]
|
(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]
|
current-chat-id [:get-current-chat-id]
|
||||||
contact-chat [:get-in [:chats (if outgoing to from)]]]
|
contact-chat [:get-in [:chats (if outgoing to from)]]]
|
||||||
(let [{:keys [command params]} (parse-command-message-content commands content)
|
(let [{:keys [command params]} (parse-command-message-content commands content)
|
||||||
|
@ -341,6 +344,7 @@
|
||||||
:message-id message-id}])))
|
:message-id message-id}])))
|
||||||
:reagent-render
|
:reagent-render
|
||||||
(fn [{:keys [outgoing group-chat] :as message}]
|
(fn [{:keys [outgoing group-chat] :as message}]
|
||||||
|
(log/debug "I HAVE A MESSAGE: " message)
|
||||||
[message-container message
|
[message-container message
|
||||||
[view
|
[view
|
||||||
(let [incoming-group (and group-chat (not outgoing))]
|
(let [incoming-group (and group-chat (not outgoing))]
|
||||||
|
|
|
@ -9,11 +9,13 @@
|
||||||
touchable-highlight]]
|
touchable-highlight]]
|
||||||
[status-im.chat.styles.message :as st]
|
[status-im.chat.styles.message :as st]
|
||||||
[status-im.models.commands :refer [parse-command-request]]
|
[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)
|
(def request-message-icon-scale-delay 600)
|
||||||
|
|
||||||
(defn set-chat-command [message-id command]
|
(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))]))
|
(dispatch [:set-response-chat-command message-id (keyword (:name command))]))
|
||||||
|
|
||||||
(defn label [command]
|
(defn label [command]
|
||||||
|
@ -76,7 +78,8 @@
|
||||||
status-initialized? (subscribe [:get :status-module-initialized?])]
|
status-initialized? (subscribe [:get :status-module-initialized?])]
|
||||||
(fn [{:keys [message-id content from incoming-group]}]
|
(fn [{:keys [message-id content from incoming-group]}]
|
||||||
(let [commands @commands-atom
|
(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
|
[view st/comand-request-view
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
{:on-press (when (and (not @answered?) @status-initialized?)
|
{:on-press (when (and (not @answered?) @status-initialized?)
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
[status-im.utils.datetime :as dt]
|
[status-im.utils.datetime :as dt]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[status-im.utils.name :refer [shortened-name]]
|
[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 []
|
(defn drag-icon []
|
||||||
[view st/drag-container
|
[view st/drag-container
|
||||||
|
@ -117,7 +118,7 @@
|
||||||
(when-not (= "about:blank" url)
|
(when-not (= "about:blank" url)
|
||||||
(if loading
|
(if loading
|
||||||
(dispatch [:set-web-view-url url])
|
(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 []
|
(defn web-view-error []
|
||||||
(r/as-element
|
(r/as-element
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
{:keys [result error]}]]
|
{:keys [result error]}]]
|
||||||
(let [{:keys [context returned]} result
|
(let [{:keys [context returned]} result
|
||||||
{handler-error :error} returned]
|
{handler-error :error} returned]
|
||||||
|
(log/debug "command handler: " result error parameters)
|
||||||
(cond
|
(cond
|
||||||
handler-error
|
handler-error
|
||||||
(log/debug :error-from-handler handler-error
|
(log/debug :error-from-handler handler-error
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
(status/parse-jail identity file
|
(status/parse-jail identity file
|
||||||
(fn [result]
|
(fn [result]
|
||||||
(let [{:keys [error result]} (json->clj result)]
|
(let [{:keys [error result]} (json->clj result)]
|
||||||
|
(log/debug "Error parsing commands: " error result)
|
||||||
(if error
|
(if error
|
||||||
(dispatch [::loading-failed! identity ::error-in-jail error])
|
(dispatch [::loading-failed! identity ::error-in-jail error])
|
||||||
(if identity
|
(if identity
|
||||||
|
@ -125,7 +126,8 @@
|
||||||
(after save-commands-js!)
|
(after save-commands-js!)
|
||||||
(after #(dispatch [:check-autorun]))
|
(after #(dispatch [:check-autorun]))
|
||||||
(after (fn [_ [id]]
|
(after (fn [_ [id]]
|
||||||
(dispatch [:invoke-commands-loading-callbacks id])))]
|
(dispatch [:invoke-commands-loading-callbacks id])
|
||||||
|
(dispatch [:invoke-chat-loaded-callbacks id])))]
|
||||||
add-commands)
|
add-commands)
|
||||||
|
|
||||||
(reg-handler ::add-all-commands
|
(reg-handler ::add-all-commands
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
[re-frame.core :refer [dispatch trim-v debug]]
|
[re-frame.core :refer [dispatch trim-v debug]]
|
||||||
[status-im.utils.handlers :refer [register-handler]]))
|
[status-im.utils.handlers :refer [register-handler]]))
|
||||||
|
|
||||||
|
(def command-prefix "c ")
|
||||||
|
|
||||||
(defn json->clj [json]
|
(defn json->clj [json]
|
||||||
(when-not (= json "undefined")
|
(when-not (= json "undefined")
|
||||||
(js->clj (.parse js/JSON json) :keywordize-keys true)))
|
(js->clj (.parse js/JSON json) :keywordize-keys true)))
|
||||||
|
|
|
@ -30,6 +30,13 @@
|
||||||
[db [_ _ click-handler]]
|
[db [_ _ click-handler]]
|
||||||
(assoc db :contacts-click-handler 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
|
(defn save-contact
|
||||||
[_ [_ contact]]
|
[_ [_ contact]]
|
||||||
(contacts/save contact))
|
(contacts/save contact))
|
||||||
|
|
|
@ -211,3 +211,8 @@
|
||||||
|
|
||||||
(def qr-input
|
(def qr-input
|
||||||
{:margin-right 42})
|
{:margin-right 42})
|
||||||
|
|
||||||
|
(def scan-qr-icon
|
||||||
|
{:margin 18
|
||||||
|
:width 25
|
||||||
|
:height 19})
|
|
@ -8,6 +8,18 @@
|
||||||
(defn on-press [whisper-identity]
|
(defn on-press [whisper-identity]
|
||||||
#(dispatch [:start-chat whisper-identity {} :navigation-replace]))
|
#(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}]
|
(defview contact-view [{:keys [whisper-identity] :as contact}]
|
||||||
[chat [:get-chat whisper-identity]]
|
[chat [:get-chat whisper-identity]]
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
|
|
|
@ -6,16 +6,20 @@
|
||||||
touchable-highlight
|
touchable-highlight
|
||||||
list-view
|
list-view
|
||||||
list-item]]
|
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.status-bar :refer [status-bar]]
|
||||||
[status-im.components.toolbar.view :refer [toolbar]]
|
[status-im.components.toolbar.view :refer [toolbar]]
|
||||||
[status-im.components.toolbar.styles :refer [toolbar-background1]]
|
[status-im.components.toolbar.styles :refer [toolbar-background1]]
|
||||||
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
[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.contacts.styles :as st]
|
||||||
[status-im.utils.listview :as lw]
|
[status-im.utils.listview :as lw]
|
||||||
[status-im.i18n :refer [label]]
|
[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 []
|
(defn new-group-chat-view []
|
||||||
[touchable-highlight
|
[touchable-highlight
|
||||||
|
@ -29,21 +33,42 @@
|
||||||
[text {:style st/name-text}
|
[text {:style st/name-text}
|
||||||
(label :t/new-group-chat)]]]]])
|
(label :t/new-group-chat)]]]]])
|
||||||
|
|
||||||
(defn render-row [click-handler]
|
(defn render-row [chat-modal click-handler action params]
|
||||||
(fn [row _ _]
|
(fn [row _ _]
|
||||||
(list-item
|
(list-item
|
||||||
[contact-view row
|
(if chat-modal
|
||||||
(or click-handler
|
[contact-view-with-letter row click-handler action params]
|
||||||
(let [whisper-identity (:whisper-identity row)]
|
[contact-view row
|
||||||
(on-press whisper-identity)))])))
|
(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 []
|
(defview contact-list-toolbar []
|
||||||
[group [:get :contacts-group]]
|
[group [:get :contacts-group]
|
||||||
|
modal [:get :modal]]
|
||||||
[view
|
[view
|
||||||
[status-bar]
|
[status-bar]
|
||||||
[toolbar {:title (label (if (= group :dapps)
|
[toolbar {:title (label (if-not group
|
||||||
:t/contacts-group-dapps
|
:t/contacts
|
||||||
:t/contacts-group-new-chat))
|
(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
|
:background-color toolbar-background1
|
||||||
:style (get-in platform-specific [:component-styles :toolbar])
|
:style (get-in platform-specific [:component-styles :toolbar])
|
||||||
:actions [{:image {:source {:uri :icon_search}
|
:actions [{:image {:source {:uri :icon_search}
|
||||||
|
@ -53,21 +78,26 @@
|
||||||
(defview contact-list []
|
(defview contact-list []
|
||||||
[contacts [:contacts-with-letters]
|
[contacts [:contacts-with-letters]
|
||||||
group [:get :contacts-group]
|
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)
|
(let [show-new-group-chat? (and (= group :people)
|
||||||
(get-in platform-specific [:chats :new-chat-in-toolbar?]))]
|
(get-in platform-specific [:chats :new-chat-in-toolbar?]))]
|
||||||
[drawer-view
|
[drawer-view
|
||||||
[view st/contacts-list-container
|
[view st/contacts-list-container
|
||||||
[contact-list-toolbar]
|
[contact-list-toolbar]
|
||||||
;; todo add stub
|
;; todo add stub
|
||||||
|
(when modal
|
||||||
|
[qr-scan click-handler action])
|
||||||
(when contacts
|
(when contacts
|
||||||
[list-view {:dataSource (lw/to-datasource contacts)
|
[list-view {:dataSource (lw/to-datasource contacts)
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
:renderRow (render-row click-handler)
|
:renderRow (render-row modal click-handler action params)
|
||||||
:renderHeader #(list-item
|
:renderHeader #(list-item
|
||||||
[view
|
[view
|
||||||
(if show-new-group-chat?
|
(if show-new-group-chat?
|
||||||
[new-group-chat-view])
|
[new-group-chat-view])
|
||||||
[view st/spacing-top]])
|
[view st/spacing-top]])
|
||||||
:renderFooter #(list-item [view st/spacing-bottom])
|
:renderFooter #(list-item [view st/spacing-bottom])
|
||||||
:style st/contacts-list}])]]))
|
:style st/contacts-list}])]]))
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||||
[status-im.handlers]
|
[status-im.handlers]
|
||||||
[status-im.subs]
|
[status-im.subs]
|
||||||
[status-im.components.react :refer [app-registry
|
[status-im.components.react :refer [view
|
||||||
|
modal
|
||||||
|
app-registry
|
||||||
keyboard
|
keyboard
|
||||||
orientation]]
|
orientation]]
|
||||||
[status-im.components.main-tabs :refer [main-tabs]]
|
[status-im.components.main-tabs :refer [main-tabs]]
|
||||||
|
@ -24,7 +26,9 @@
|
||||||
[status-im.profile.screen :refer [profile my-profile]]
|
[status-im.profile.screen :refer [profile my-profile]]
|
||||||
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
|
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
|
||||||
status-im.data-store.core
|
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]
|
(defn orientation->keyword [o]
|
||||||
(keyword (.toLowerCase o)))
|
(keyword (.toLowerCase o)))
|
||||||
|
@ -38,7 +42,7 @@
|
||||||
|
|
||||||
(defn app-root []
|
(defn app-root []
|
||||||
(let [signed-up? (subscribe [:signed-up?])
|
(let [signed-up? (subscribe [:signed-up?])
|
||||||
_ (log/debug "signed up: " @signed-up?)
|
modal-view (subscribe [:get :modal])
|
||||||
view-id (subscribe [:get :view-id])
|
view-id (subscribe [:get :view-id])
|
||||||
account-id (subscribe [:get :current-account-id])
|
account-id (subscribe [:get :current-account-id])
|
||||||
keyboard-height (subscribe [:get :keyboard-height])]
|
keyboard-height (subscribe [:get :keyboard-height])]
|
||||||
|
@ -86,7 +90,21 @@
|
||||||
:login login
|
:login login
|
||||||
:confirm confirm
|
:confirm confirm
|
||||||
:my-profile my-profile)]
|
: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 []
|
(defn init []
|
||||||
(dispatch-sync [:reset-app])
|
(dispatch-sync [:reset-app])
|
||||||
|
|
|
@ -70,4 +70,5 @@
|
||||||
(update content :command #((keyword %) commands)))
|
(update content :command #((keyword %) commands)))
|
||||||
|
|
||||||
(defn parse-command-request [commands content]
|
(defn parse-command-request [commands content]
|
||||||
|
(log/debug "parse-command-request: " commands content)
|
||||||
(update content :command #((keyword %) commands)))
|
(update content :command #((keyword %) commands)))
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
|
|
||||||
(defmethod preload-data! :default [db _] db)
|
(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
|
(register-handler :navigate-forget
|
||||||
(enrich preload-data!)
|
(enrich preload-data!)
|
||||||
(fn [db [_ new-view-id]]
|
(fn [db [_ new-view-id]]
|
||||||
|
@ -36,16 +41,25 @@
|
||||||
db
|
db
|
||||||
(push-view db new-view-id))))
|
(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
|
(register-handler :navigation-replace
|
||||||
(enrich preload-data!)
|
(enrich preload-data!)
|
||||||
(fn [db [_ view-id]]
|
(fn [db [_ view-id]]
|
||||||
(replace-view db view-id)))
|
(replace-view db view-id)))
|
||||||
|
|
||||||
(register-handler :navigate-back
|
(register-handler :navigate-back
|
||||||
(enrich preload-data!)
|
(enrich -preload-data!)
|
||||||
(fn [{:keys [navigation-stack view-id] :as db} _]
|
(fn [{:keys [navigation-stack view-id modal] :as db} _]
|
||||||
(if (>= 1 (count navigation-stack))
|
(cond
|
||||||
db
|
modal (assoc db :modal nil
|
||||||
|
:was-modal? true)
|
||||||
|
(>= 1 (count navigation-stack)) db
|
||||||
|
|
||||||
|
:else
|
||||||
(let [[previous-view-id :as navigation-stack'] (pop navigation-stack)
|
(let [[previous-view-id :as navigation-stack'] (pop navigation-stack)
|
||||||
first-in-stack (first navigation-stack)]
|
first-in-stack (first navigation-stack)]
|
||||||
(if (= view-id first-in-stack)
|
(if (= view-id first-in-stack)
|
||||||
|
@ -67,10 +81,6 @@
|
||||||
(fn [db [_]]
|
(fn [db [_]]
|
||||||
(assoc db :prev-tab-view-id nil)))
|
(assoc db :prev-tab-view-id nil)))
|
||||||
|
|
||||||
(register-handler :remove-contacts-click-handler
|
|
||||||
(fn [db]
|
|
||||||
(dissoc db :contacts-click-handler)))
|
|
||||||
|
|
||||||
(defn show-profile
|
(defn show-profile
|
||||||
[db [_ identity]]
|
[db [_ identity]]
|
||||||
(-> db
|
(-> db
|
||||||
|
|
|
@ -4,20 +4,25 @@
|
||||||
[status-im.components.react :refer [view
|
[status-im.components.react :refer [view
|
||||||
image]]
|
image]]
|
||||||
[status-im.components.camera :refer [camera]]
|
[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.status-bar :refer [status-bar]]
|
||||||
[status-im.components.toolbar.view :refer [toolbar]]
|
[status-im.components.toolbar.view :refer [toolbar]]
|
||||||
[status-im.components.toolbar.styles :refer [toolbar-background1]]
|
[status-im.components.toolbar.styles :refer [toolbar-background1]]
|
||||||
[status-im.qr-scanner.styles :as st]
|
[status-im.qr-scanner.styles :as st]
|
||||||
[status-im.utils.types :refer [json->clj]]
|
[status-im.utils.types :refer [json->clj]]
|
||||||
[status-im.components.styles :as cst]
|
|
||||||
[clojure.string :as str]))
|
[clojure.string :as str]))
|
||||||
|
|
||||||
(defn qr-scanner-toolbar [title]
|
(defview qr-scanner-toolbar [title]
|
||||||
|
[modal [:get :modal]]
|
||||||
[view
|
[view
|
||||||
[status-bar]
|
[status-bar]
|
||||||
[toolbar {:title title
|
[toolbar {:title title
|
||||||
:background-color toolbar-background1
|
: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}
|
:actions [{:image {:source {:uri :icon_lock_white}
|
||||||
:style icon-search}
|
:style icon-search}
|
||||||
:handler #()}]}]])
|
:handler #()}]}]])
|
||||||
|
@ -30,6 +35,7 @@
|
||||||
(let [data (-> (.-data code)
|
(let [data (-> (.-data code)
|
||||||
(str/replace #"ethereum:" ""))]
|
(str/replace #"ethereum:" ""))]
|
||||||
(dispatch [:set-qr-code identifier data])))
|
(dispatch [:set-qr-code identifier data])))
|
||||||
|
:barCodeTypes [:qr]
|
||||||
:style st/barcode-scanner}]
|
:style st/barcode-scanner}]
|
||||||
[view st/rectangle-container
|
[view st/rectangle-container
|
||||||
[view st/rectangle
|
[view st/rectangle
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
(assoc-in [:confirm-transactions :password] 0)))
|
(assoc-in [:confirm-transactions :password] 0)))
|
||||||
|
|
||||||
(defn on-unlock
|
(defn on-unlock
|
||||||
[ids password previous-view-id]
|
[ids password]
|
||||||
(dispatch [:set :wrong-password? false])
|
(dispatch [:set :wrong-password? false])
|
||||||
(doseq [id ids]
|
(doseq [id ids]
|
||||||
(status/complete-transaction
|
(status/complete-transaction
|
||||||
|
@ -38,15 +38,13 @@
|
||||||
password
|
password
|
||||||
#(dispatch [:transaction-completed
|
#(dispatch [:transaction-completed
|
||||||
{:id id
|
{:id id
|
||||||
:response %
|
:response %}]))))
|
||||||
:previous-view-id previous-view-id}]))))
|
|
||||||
|
|
||||||
(register-handler :accept-transactions
|
(register-handler :accept-transactions
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [{:keys [transactions navigation-stack]} [_ password]]
|
(fn [{:keys [transactions]} [_ password]]
|
||||||
(let [ids (keys transactions)
|
(let [ids (keys transactions)]
|
||||||
previous-view-id (second navigation-stack)]
|
(on-unlock ids password)))))
|
||||||
(on-unlock ids password previous-view-id)))))
|
|
||||||
|
|
||||||
(register-handler :deny-transactions
|
(register-handler :deny-transactions
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
|
@ -125,7 +123,7 @@
|
||||||
(remove-pending-message db message-id)))
|
(remove-pending-message db message-id)))
|
||||||
|
|
||||||
(register-handler :transaction-queued
|
(register-handler :transaction-queued
|
||||||
(after #(dispatch [:navigate-to :confirm]))
|
(after #(dispatch [:navigate-to-modal :confirm]))
|
||||||
(fn [db [_ {:keys [id message_id args]}]]
|
(fn [db [_ {:keys [id message_id args]}]]
|
||||||
(let [{:keys [from to value]} args
|
(let [{:keys [from to value]} args
|
||||||
transaction {:id id
|
transaction {:id id
|
||||||
|
@ -137,7 +135,7 @@
|
||||||
|
|
||||||
(register-handler :transaction-completed
|
(register-handler :transaction-completed
|
||||||
(u/side-effect!
|
(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)
|
(let [{:keys [hash error] :as parsed-response} (t/json->clj response)
|
||||||
{:keys [message-id]} (transactions id)]
|
{:keys [message-id]} (transactions id)]
|
||||||
(log/debug :parsed-response parsed-response)
|
(log/debug :parsed-response parsed-response)
|
||||||
|
@ -148,7 +146,7 @@
|
||||||
:message-id message-id}])
|
:message-id message-id}])
|
||||||
(dispatch [::check-completed-transaction!
|
(dispatch [::check-completed-transaction!
|
||||||
{:message-id message-id}])
|
{:message-id message-id}])
|
||||||
(dispatch [:navigation-replace previous-view-id]))
|
(dispatch [:navigate-back]))
|
||||||
(dispatch [::remove-transaction id])))))))
|
(dispatch [::remove-transaction id])))))))
|
||||||
|
|
||||||
(register-handler ::add-transactions-hash
|
(register-handler ::add-transactions-hash
|
||||||
|
|
|
@ -201,6 +201,10 @@
|
||||||
:recover-access "Recover access"
|
:recover-access "Recover access"
|
||||||
:add-account "Add account"
|
:add-account "Add account"
|
||||||
|
|
||||||
|
;wallet-qr-code
|
||||||
|
:done "Done"
|
||||||
|
:main-wallet "Main Wallet"
|
||||||
|
|
||||||
;validation
|
;validation
|
||||||
:invalid-phone "Invalid phone number"
|
:invalid-phone "Invalid phone number"
|
||||||
:amount "Amount"
|
:amount "Amount"
|
||||||
|
|
Loading…
Reference in New Issue