parsing of commands.js, command's handler, suggestions handler in jail (commit contains .aar oops)

This commit is contained in:
Roman Volosovskyi 2016-06-14 14:19:56 +03:00
parent 3aef0f2d9c
commit ab93b21a4c
12 changed files with 504 additions and 108 deletions

View File

@ -131,8 +131,9 @@ dependencies {
compile project(':react-native-linear-gradient') compile project(':react-native-linear-gradient')
compile project(':ReactNativeAndroidSmsListener') compile project(':ReactNativeAndroidSmsListener')
compile project(':react-native-status') compile project(':react-native-status')
// compile(name:'geth', ext:'aar') // todo replace this when jail will be integrated into geth
compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar') compile (name: "geth-android-16", ext:"aar")
//compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar')
compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"]) compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"])
} }
@ -142,3 +143,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile from configurations.compile
into 'libs' into 'libs'
} }

Binary file not shown.

176
resources/commands.js Normal file
View File

@ -0,0 +1,176 @@
status.command({
name: "location",
description: "Send location",
color: "#9a5dcf"
}).param({
name: "address",
type: status.types.STRING
});
function text(options, s) {
return ["text", options, s];
}
function view(options, elements) {
return ["view", options].concat(elements);
}
function image(options) {
return ["image", options];
}
function touchable(options, element) {
return ["touchable", options, element];
}
function scrollView(options, elements) {
return ["scroll-view", options].concat(elements);
}
var phones = [
{
number: "89171111111",
description: "Number format 1"
},
{
number: "89371111111",
description: "Number format 1"
},
{
number: "+79171111111",
description: "Number format 2"
},
{
number: "9171111111",
description: "Number format 3"
}
];
function suggestionsContainerStyle(suggestionsCount) {
return {
marginVertical: 1,
marginHorizontal: 0,
height: Math.min(150, (56 * suggestionsCount)),
backgroundColor: "white",
borderRadius: 5
};
}
var suggestionContainerStyle = {
paddingLeft: 16,
backgroundColor: "white"
};
var suggestionSubContainerStyle = {
height: 56,
borderBottomWidth: 1,
borderBottomColor: "#0000001f"
};
var valueStyle = {
marginTop: 9,
fontSize: 14,
fontFamily: "font",
color: "#000000de"
};
var descriptionStyle = {
marginTop: 1.5,
fontSize: 14,
fontFamily: "font",
color: "#838c93de"
};
function startsWith(str1, str2) {
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
}
function phoneSuggestions(params) {
var ph, suggestions;
if (!params.value || params.value == "") {
ph = phones;
} else {
ph = phones.filter(function (phone) {
return startsWith(phone.number, params.value);
});
}
if (ph.length == 0) {
return;
}
suggestions = ph.map(function (phone) {
return touchable(
{onPress: [status.events.SET_VALUE, phone.number]},
view(suggestionContainerStyle,
[view(suggestionSubContainerStyle,
[
text({style: valueStyle}, phone.number),
text({style: descriptionStyle}, phone.description)
])])
);
});
return scrollView(suggestionsContainerStyle(ph.length), suggestions);
}
status.response({
name: "phone",
description: "Send phone number",
color: "#5fc48d",
params: [{
name: "phone",
type: status.types.PHONE_NUMBER,
suggestions: phoneSuggestions
}],
handler: function (params) {
return {
event: "sign-up",
params: [params.value]
};
}
});
status.command({
name: "help",
description: "Help",
color: "#9a5dcf",
params: [{
name: "query",
type: status.types.STRING
}]
});
status.response({
name: "confirmation-code",
color: "#7099e6",
description: "Confirmation code",
parameters: [{
name: "code",
type: status.types.NUMBER
}],
handler: function (params) {
return {
event: "confirm-sign-up",
params: [params.value]
};
}
});
status.response({
name: "keypair",
color: "#7099e6",
description: "Keypair password",
icon: "icon_lock_white",
parameters: [{
name: "password",
type: status.types.PASSWORD
}],
handler: function (params) {
return {
event: "save-password",
params: [params.value]
};
}
});

77
resources/status.js Normal file
View File

@ -0,0 +1,77 @@
var _status_catalog = {
commands: {},
responses: {}
};
function Command() {
}
function Response() {
}
Command.prototype.addToCatalog = function () {
_status_catalog.commands[this.name] = this;
};
Command.prototype.param = function (parameter) {
this.params.push(parameter);
return this;
};
Command.prototype.create = function (com) {
this.name = com.name;
this.description = com.description;
this.handler = com.handler;
this.color = com.color;
this.icon = com.icon;
this.params = com.params || [];
this.addToCatalog();
return this;
};
Response.prototype = Object.create(Command.prototype);
Response.prototype.addToCatalog = function () {
_status_catalog.responses[this.name] = this;
};
Response.prototype.onReceiveResponse = function (handler) {
this.onReceive = handler;
};
function call(pathStr, paramsStr) {
var params = JSON.parse(paramsStr),
path = JSON.parse(pathStr),
fn, res;
fn = path.reduce(function (catalog, name) {
if (catalog && catalog[name]) {
return catalog[name];
}
},
_status_catalog
);
res = fn(params);
return JSON.stringify(res);
}
var status = {
command: function (n, d, h) {
var command = new Command();
return command.create(n, d, h);
},
response: function (n, d, h) {
var response = new Response();
return response.create(n, d, h);
},
types: {
STRING: 'string',
PHONE_NUMBER: 'phone-number',
PASSWORD: 'password'
},
events: {
SET_VALUE: 'set-value'
}
};

View File

@ -16,7 +16,8 @@
[status-im.persistence.realm :as r] [status-im.persistence.realm :as r]
[status-im.handlers.server :as server] [status-im.handlers.server :as server]
[status-im.utils.phone-number :refer [format-phone-number]] [status-im.utils.phone-number :refer [format-phone-number]]
[status-im.utils.datetime :as time])) [status-im.utils.datetime :as time]
[status-im.components.jail :as j]))
(register-handler :set-show-actions (register-handler :set-show-actions
(fn [db [_ show-actions]] (fn [db [_ show-actions]]
@ -41,7 +42,24 @@
(assoc-in [:chats current-chat-id :command-input] {}) (assoc-in [:chats current-chat-id :command-input] {})
(update-in [:chats current-chat-id :input-text] safe-trim)))) (update-in [:chats current-chat-id :input-text] safe-trim))))
(defn invoke-suggestions-handler!
[{:keys [current-chat-id] :as db} _]
(let [commands (get-in db [:chats current-chat-id :commands])
{:keys [command content]} (get-in db [:chats current-chat-id :command-input])]
(let [path [(if (commands command) :commands :responses)
(:name command)
:params
0
:suggestions]
params {:value content}]
(j/call current-chat-id
path
params
#(dispatch [:suggestions-handler {:command command
:content content} %])))))
(register-handler :set-chat-command-content (register-handler :set-chat-command-content
(after invoke-suggestions-handler!)
(fn [db [_ content]] (fn [db [_ content]]
(commands/set-chat-command-content db content))) (commands/set-chat-command-content db content)))
@ -55,11 +73,13 @@
{:keys [command content]} {:keys [command content]}
(get-in db [:chats current-chat-id :command-input]) (get-in db [:chats current-chat-id :command-input])
command-info {:command command command-info {:command command
:content content :content content}]
:handler (:handler command)}] (-> db
(commands/stage-command db command-info)))) (commands/stage-command command-info)
(assoc :staged-command command-info)))))
(register-handler :set-response-chat-command (register-handler :set-response-chat-command
(after invoke-suggestions-handler!)
(fn [db [_ to-msg-id command-key]] (fn [db [_ to-msg-id command-key]]
(commands/set-response-chat-command db to-msg-id command-key))) (commands/set-response-chat-command db to-msg-id command-key)))
@ -179,6 +199,20 @@
(doseq [new-command new-commands] (doseq [new-command new-commands]
(messages/save-message current-chat-id (dissoc new-command :handler)))) (messages/save-message current-chat-id (dissoc new-command :handler))))
(defn invoke-commands-handlers!
[{:keys [new-commands current-chat-id] :as db}]
(let [commands (get-in db [:chats current-chat-id :commands])]
(doseq [{:keys [content] :as com} new-commands]
(let [{:keys [command content]} content
path [(if (commands command) :commands :responses)
command
:handler]
params {:value content}]
(j/call current-chat-id
path
params
#(dispatch [:command-handler! com %]))))))
(defn handle-commands (defn handle-commands
[{:keys [new-commands]}] [{:keys [new-commands]}]
(doseq [{{content :content} :content (doseq [{{content :content} :content
@ -196,6 +230,8 @@
((after send-message!)) ((after send-message!))
((after save-message-to-realm!)) ((after save-message-to-realm!))
((after save-commands-to-realm!)) ((after save-commands-to-realm!))
;; todo maybe it is better to track if it was handled or not
((after invoke-commands-handlers!))
((after handle-commands)))) ((after handle-commands))))
(register-handler :unstage-command (register-handler :unstage-command

View File

@ -173,7 +173,7 @@
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id msg-id {:msg-id msg-id
:content (command-content :content (command-content
:keypair-password :keypair
(label :t/keypair-generated)) (label :t/keypair-generated))
:content-type content-type-command-request :content-type content-type-command-request
:outgoing false :outgoing false
@ -185,8 +185,8 @@
{:chat-id "console" {:chat-id "console"
:name "console" :name "console"
; todo remove/change dapp config fot console ; todo remove/change dapp config fot console
:dapp-url "http://localhost:8185" :dapp-url "http://localhost:8185/resources"
:dapp-hash 834331894 :dapp-hash 858845357
:color default-chat-color :color default-chat-color
:group-chat false :group-chat false
:is-active true :is-active true

View File

@ -15,8 +15,7 @@
(def suggestion-height 56) (def suggestion-height 56)
(def suggestion-container (def suggestion-container
{:flexDirection :column {:paddingLeft 16
:paddingLeft 16
:backgroundColor color-white}) :backgroundColor color-white})
(def suggestion-sub-container (def suggestion-sub-container
@ -37,7 +36,7 @@
:color text2-color}) :color text2-color})
(defn suggestions-container [suggestions-count] (defn suggestions-container [suggestions-count]
{:flexDirection :row {:flex 1
:marginVertical 1 :marginVertical 1
:marginHorizontal 0 :marginHorizontal 0
:height (min 150 (* suggestion-height suggestions-count)) :height (min 150 (* suggestion-height suggestions-count))

View File

@ -1,8 +1,7 @@
(ns status-im.chat.suggestions (ns status-im.chat.suggestions
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.db :as db] [status-im.db :as db]
[status-im.models.commands :refer [commands [status-im.models.commands :refer [get-commands
get-commands
get-chat-command-request get-chat-command-request
get-chat-command-to-msg-id get-chat-command-to-msg-id
clear-staged-commands]] clear-staged-commands]]

View File

@ -24,13 +24,11 @@
(list-item [suggestion-list-item row])) (list-item [suggestion-list-item row]))
(defview content-suggestions-view [] (defview content-suggestions-view []
[suggestions [:get-content-suggestions]] [suggestions [:get :current-suggestion]]
(when (seq suggestions) (when (seq suggestions)
[view [view
[touchable-highlight {:style st/drag-down-touchable [touchable-highlight {:style st/drag-down-touchable
;; TODO hide suggestions? ;; TODO hide suggestions?
:onPress (fn [])} :onPress (fn [])}
[view [icon :drag_down st/drag-down-icon]]] [view [icon :drag_down st/drag-down-icon]]]
[view (st/suggestions-container (count suggestions)) suggestions]))
[list-view {:dataSource (to-datasource suggestions)
:renderRow render-row}]]]))

View File

@ -6,7 +6,11 @@
[status-im.utils.utils :refer [http-get toast]] [status-im.utils.utils :refer [http-get toast]]
[clojure.string :as s] [clojure.string :as s]
[status-im.persistence.realm :as realm] [status-im.persistence.realm :as realm]
[status-im.components.jail :as j])) [status-im.components.jail :as j]
[clojure.walk :as w]
[status-im.components.react :refer [text scroll-view view
image touchable-highlight]]
[clojure.set :as set]))
(defn reg-handler (defn reg-handler
([name handler] (reg-handler name nil handler)) ([name handler] (reg-handler name nil handler))
@ -17,7 +21,10 @@
(defn load-commands! (defn load-commands!
[_ [identity]] [_ [identity]]
(if-let [{:keys [file]} (realm/get-one-by-field :commands :chat-id identity)] (dispatch [::fetch-commands! identity])
;; todo uncomment
#_(if-let [{:keys [file]} (realm/get-one-by-field :commands :chat-id
identity)]
(dispatch [::parse-commands! identity file]) (dispatch [::parse-commands! identity file])
(dispatch [::fetch-commands! identity]))) (dispatch [::fetch-commands! identity])))
@ -43,38 +50,28 @@
;; todo tbd hashing algorithm ;; todo tbd hashing algorithm
(hash file)) (hash file))
(defn json->clj [json] (defn json->cljs [json]
(js->clj (.parse js/JSON json) :keywordize-keys true)) (if (= json "undefined")
nil
;; todo remove this (js->clj (.parse js/JSON json) :keywordize-keys true)))
(def res {:commands {:location {:description "Send location"
:color "#9a5dcf"
:name "location"}
:phone {:description "Send phone number"
:color "#5fc48d"
:name "phone"}
:help {:description "Help" :color "#9a5dcf" :name "help"}}
:responses {:money {:description "Send money" :color "#5fc48d" :name "money"}
:confirmation-code {:description "Confirmation code"
:color "#7099e6"
:name "confirmationCode"}
:keypair-password {:description "Keypair password"
:color "#7099e6"
:name "keypair-password"
:icon "icon_lock_white"}}})
(defn parse-commands! [_ [identity file]] (defn parse-commands! [_ [identity file]]
(j/parse identity file (j/parse identity file
(fn [result] (fn [result]
(let [commands (json->clj result)] (let [commands (json->cljs result)]
;; todo use commands from jail ;; todo use commands from jail
(dispatch [::add-commands identity file res]))) (dispatch [::add-commands identity file commands])))
#(dispatch [::loading-failed! identity ::error-in-jail %]))) #_(dispatch [::loading-failed! identity ::error-in-jail %])))
(defn validate-hash (defn validate-hash
[db [identity file]] [db [identity file]]
(let [valid? (= (get-hash-by-identity db identity) (let [valid? true
;; todo check
#_(= (get-hash-by-identity db identity)
(get-hash-by-file file))] (get-hash-by-file file))]
(println :hash
(get-hash-by-identity db identity)
(get-hash-by-file file))
(assoc db ::valid-hash valid?))) (assoc db ::valid-hash valid?)))
(defn add-commands (defn add-commands
@ -96,6 +93,76 @@
(name reason) (name reason)
details])))) details]))))
(defn init-render-command!
[_ [chat-id command message-id data]]
(j/call chat-id [command :render] data
(fn [res]
(dispatch [::render-command chat-id message-id (json->cljs res)]))))
(def elements
{:text text
:view view
:scroll-view scroll-view
:image image
:touchable touchable-highlight})
(defn get-element [n]
(elements (keyword (.toLowerCase n))))
(def events #{:onPress})
(defn wrap-event [event]
#(dispatch [:suggestions-event! event]))
(defn check-events [m]
(let [ks (set (keys m))
evs (set/intersection ks events)]
(reduce #(update %1 %2 wrap-event) m evs)))
(defn generate-hiccup [markup]
;; todo implement validation
(w/prewalk
(fn [el]
(if (and (vector? el) (string? (first el)))
(-> el
(update 0 get-element)
(update 1 check-events))
el))
markup))
(defn render-command
[db [chat-id message-id markup]]
(let [hiccup (generate-hiccup markup)]
(assoc-in db [:rendered-commands chat-id message-id] hiccup)))
(def console-events
{:save-password #(dispatch [:save-password %])
:sign-up #(dispatch [:sign-up %])
:confirm-sign-up #(dispatch [:sign-up-confirm %])})
(def regular-events {})
(defn command-nadler!
[_ [{:keys [to]} response]]
(let [{:keys [event params]} (json->cljs response)
events (if (= "console" to)
(merge regular-events console-events)
regular-events)]
(when-let [handler (events (keyword event))]
(apply handler params))))
(defn suggestions-handler
[db [_ response-json]]
(let [response (json->cljs response-json)]
(println response)
(assoc db :current-suggestion (generate-hiccup response))))
(defn suggestions-events-handler!
[db [[n data]]]
(case (keyword n)
:set-value (dispatch [:set-chat-command-content data])
db))
(reg-handler :load-commands! (u/side-effect! load-commands!)) (reg-handler :load-commands! (u/side-effect! load-commands!))
(reg-handler ::fetch-commands! (u/side-effect! fetch-commands!)) (reg-handler ::fetch-commands! (u/side-effect! fetch-commands!))
@ -111,3 +178,9 @@
(reg-handler ::loading-failed! (u/side-effect! loading-failed!)) (reg-handler ::loading-failed! (u/side-effect! loading-failed!))
(reg-handler :init-render-command! init-render-command!)
(reg-handler ::render-command render-command)
(reg-handler :command-handler! (u/side-effect! command-nadler!))
(reg-handler :suggestions-handler suggestions-handler)
(reg-handler :suggestions-event! (u/side-effect! suggestions-events-handler!))

View File

@ -1,15 +1,94 @@
(ns status-im.components.jail (ns status-im.components.jail
(:require [status-im.components.react :as r])) (:require [status-im.components.react :as r]))
(def status-js
"var _status_catalog = {
commands: {},
responses: {}
};
function Command() {
}
function Response() {
}
Command.prototype.addToCatalog = function () {
_status_catalog.commands[this.name] = this;
};
Command.prototype.param = function (parameter) {
this.params.push(parameter);
return this;
};
Command.prototype.create = function (com) {
this.name = com.name;
this.description = com.description;
this.handler = com.handler;
this.color = com.color;
this.icon = com.icon;
this.params = com.params || [];
this.addToCatalog();
return this;
};
Response.prototype = Object.create(Command.prototype);
Response.prototype.addToCatalog = function () {
_status_catalog.responses[this.name] = this;
};
Response.prototype.onReceiveResponse = function (handler) {
this.onReceive = handler;
};
function call(pathStr, paramsStr) {
var params = JSON.parse(paramsStr),
path = JSON.parse(pathStr),
fn, res;
fn = path.reduce(function (catalog, name) {
if (catalog && catalog[name]) {
return catalog[name];
}
},
_status_catalog
);
res = fn(params);
return JSON.stringify(res);
}
var status = {
command: function (n, d, h) {
var command = new Command();
return command.create(n, d, h);
},
response: function (n, d, h) {
var response = new Response();
return response.create(n, d, h);
},
types: {
STRING: 'string',
PHONE_NUMBER: 'phone-number',
PASSWORD: 'password'
},
events: {
SET_VALUE: 'set-value'
}
};")
(def jail (.-Jail (.-NativeModules r/react))) (def jail (.-Jail (.-NativeModules r/react)))
(.init jail status-js)
(defn parse [chat-id file success-callback fail-callback] (defn parse [chat-id file callback]
(.parse jail chat-id file success-callback fail-callback)) (.parse jail chat-id file callback))
(defn call (defn cljs->json [data]
[chat-id path params callback] (.stringify js/JSON (clj->js data)))
(.call jail chat-id (clj->js path) (clj->js params) callback))
(defn add-listener (defn call [chat-id path params callback]
[chat-id callback] (println :call chat-id (cljs->json path) (cljs->json params))
(.addListener jail chat-id callback)) (.call jail chat-id (cljs->json path) (cljs->json params) callback))

View File

@ -6,49 +6,6 @@
[status-im.components.styles :refer [color-blue color-dark-mint]] [status-im.components.styles :refer [color-blue color-dark-mint]]
[status-im.i18n :refer [label]])) [status-im.i18n :refer [label]]))
;; todo delete
(def commands [{:command :money
:text "!money"
:description (label :t/money-command-description)
:color color-dark-mint
:request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"}
:suggestion true}
{:command :location
:text "!location"
:description (label :t/location-command-description)
:color "#9a5dcf"
:suggestion true}
{:command :phone
:text "!phone"
:description (label :t/phone-command-description)
:color color-dark-mint
:request-text (label :t/phone-request-text)
:suggestion true
:handler #(dispatch [:sign-up %])}
{:command :confirmation-code
:text "!confirmationCode"
:description (label :t/confirmation-code-command-description)
:request-text (label :t/confirmation-code-request-text)
:color color-blue
:request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"}
:suggestion true
:handler #(dispatch [:sign-up-confirm %])}
{:command :keypair-password
:text "!keypair-password"
:description (label :t/keypair-password-command-description)
:color color-blue
:request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"}
:suggestion false
:handler #(dispatch [:save-password %])}
{:command :help
:text "!help"
:description (label :t/help-command-description)
:color "#9a5dcf"
:suggestion true}])
(defn get-commands [{:keys [current-chat-id] :as db}] (defn get-commands [{:keys [current-chat-id] :as db}]
(or (get-in db [:chats current-chat-id :commands]) {})) (or (get-in db [:chats current-chat-id :commands]) {}))