Refactoring chat events to shorten event chains

This commit is contained in:
Herich 2017-08-15 22:56:44 +02:00 committed by Roman Volosovskyi
parent 5a49926ad0
commit d9db548e57
36 changed files with 1950 additions and 1584 deletions

View File

@ -0,0 +1,98 @@
(ns status-im.bots.events
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]))
;;;; Helper fns
(defn- chats-with-bot [chats bot]
(reduce (fn [acc [_ {:keys [chat-id contacts]}]]
(let [contacts (map :identity contacts)]
(if (some #{bot} contacts)
(conj acc chat-id)
acc)))
[]
chats))
(defn- subscription-values [subscriptions current-bot-db]
(reduce (fn [sub-values [sub-name sub-path]]
(assoc sub-values sub-name (get-in current-bot-db sub-path)))
{}
subscriptions))
;; TODO(janherich): optimze this, for sure we don't need to re-calculate all bot subscriptions every time something in bot db changes
(defn- check-subscriptions-fx
[db {:keys [bot path value]}]
(let [{:keys [bot-db chats]} db
subscriptions (get-in db [:bot-subscriptions path])]
{:call-jail-function-n
(for [{:keys [bot subscriptions name]} subscriptions
:let [subs-values (subscription-values subscriptions (get bot-db bot))]]
{:chat-id bot
:function :subscription
:parameters {:name name
:subscriptions subs-values}
:callback-events-creator (fn [jail-response]
(into [[::calculated-subscription
{:bot bot
:path [name]
:result jail-response}]]
(map (fn [chat-id]
[::calculated-subscription
{:bot chat-id
:path [name]
:result jail-response}])
(chats-with-bot chats bot))))})}))
(defn set-in-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot path value] :as params}]
(let [bot (or bot current-chat-id)
new-db (assoc-in app-db (concat [:bot-db bot] path) value)]
(merge {:db new-db}
(check-subscriptions-fx new-db params))))
(defn update-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot db]}]
(let [bot (or bot current-chat-id)]
(update-in app-db [:bot-db bot] merge db)))
(defn clear-bot-db
[{:keys [current-chat-id] :as app-db}]
(assoc-in app-db [:bot-db current-chat-id] nil))
;;;; Handlers
(handlers/register-handler-fx
:set-in-bot-db
[re-frame/trim-v]
(fn [{:keys [db]} [params]]
(set-in-bot-db db params)))
(handlers/register-handler-db
:update-bot-db
[re-frame/trim-v]
(fn [db [params]]
(update-bot-db db params)))
(handlers/register-handler-db
:register-bot-subscription
[re-frame/trim-v]
(fn [db [{:keys [bot subscriptions] :as opts}]]
(reduce
(fn [db [sub-name sub-path]]
(let [keywordized-sub-path (mapv keyword
(if (coll? sub-path)
sub-path
[sub-path]))]
(update-in db [:bot-subscriptions keywordized-sub-path] conj
(assoc-in opts [:subscriptions sub-name] keywordized-sub-path))))
db
subscriptions)))
(handlers/register-handler-db
::calculated-subscription
[re-frame/trim-v]
(fn [db [{:keys [bot path]
{:keys [error result]} :result}]]
(if error
db
(assoc-in db (concat [:bot-db bot] path) (:returned result)))))

View File

@ -1,77 +0,0 @@
(ns status-im.bots.handlers
(:require [re-frame.core :as re-frame]
[status-im.native-module.core :as status]
[status-im.utils.handlers :as u]))
(defn chats-with-bot [chats bot]
(reduce (fn [acc [_ {:keys [chat-id contacts]}]]
(let [contacts (map :identity contacts)]
(if (some #{bot} contacts)
(conj acc chat-id)
acc)))
[]
chats))
(defn check-subscriptions
[{:keys [bot-db chats] :as db} [handler {:keys [path key bot]}]]
(let [path' (or path [key])
subscriptions (get-in db [:bot-subscriptions path'])
current-bot-db (get bot-db bot)]
(doseq [{:keys [bot subscriptions name]} subscriptions]
(let [subs-values (reduce (fn [res [sub-name sub-path]]
(assoc res sub-name (get-in current-bot-db sub-path)))
{} subscriptions)]
(status/call-function!
{:chat-id bot
:function :subscription
:parameters {:name name
:subscriptions subs-values}
:callback #(do
(re-frame/dispatch
[::calculated-subscription {:bot bot
:path [name]
:result %}])
(doseq [chat-id (chats-with-bot chats bot)]
(re-frame/dispatch
[::calculated-subscription {:bot chat-id
:path [name]
:result %}])))})))))
(u/register-handler :set-in-bot-db
(re-frame/after check-subscriptions)
(fn [{:keys [current-chat-id] :as db} [_ {:keys [bot path value]}]]
(let [bot (or bot current-chat-id)]
(assoc-in db (concat [:bot-db bot] path) value))))
(u/register-handler :register-bot-subscription
(fn [db [_ {:keys [bot subscriptions] :as opts}]]
(reduce
(fn [db [sub-name sub-path]]
(let [sub-path' (if (coll? sub-path) sub-path [sub-path])
sub-path'' (mapv keyword sub-path')]
(update-in db [:bot-subscriptions sub-path''] conj
(assoc-in opts [:subscriptions sub-name] sub-path''))))
db
subscriptions)))
(u/register-handler ::calculated-subscription
(u/side-effect!
(fn [_ [_ {:keys [bot path]
{:keys [error result]} :result
:as data}]]
(when-not error
(let [returned (:returned result)
opts {:bot bot
:path path
:value returned}]
(re-frame/dispatch [:set-in-bot-db opts]))))))
(u/register-handler :update-bot-db
(fn [{:keys [current-chat-id] :as app-db} [_ {:keys [bot db]}]]
(let [bot (or bot current-chat-id)]
(update-in app-db [:bot-db bot] merge db))))
(u/register-handler :clear-bot-db
(fn [{:keys [current-chat-id] :as app-db} [_ {:keys [bot]}]]
(let [bot (or bot current-chat-id)]
(assoc-in app-db [:bot-db bot] nil))))

View File

@ -0,0 +1,253 @@
(ns status-im.chat.events
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.handlers :as handlers]
[status-im.chat.models :as model]
[status-im.chat.models.unviewed-messages :as unviewed-messages-model]
[status-im.chat.sign-up :as sign-up]
[status-im.chat.constants :as chat-const]
[status-im.data-store.handler-data :as handler-data]
[status-im.data-store.messages :as msg-store]
[status-im.data-store.contacts :as contacts-store]
[status-im.data-store.chats :as chats-store]
[status-im.protocol.core :as protocol]
[status-im.constants :as const]
status-im.chat.events.input
status-im.chat.events.commands
status-im.chat.events.animation
status-im.chat.events.receive-message
status-im.chat.events.sign-up
status-im.chat.events.console))
;;;; Coeffects
(re-frame/reg-cofx
:stored-unviewed-messages
(fn [cofx _]
(assoc cofx :stored-unviewed-messages (msg-store/get-unviewed))))
(re-frame/reg-cofx
:get-stored-message
(fn [cofx _]
(assoc cofx :get-stored-message msg-store/get-by-id)))
(re-frame/reg-cofx
:get-stored-messages
(fn [cofx _]
(assoc cofx :get-stored-messages msg-store/get-by-chat-id)))
(re-frame/reg-cofx
:get-last-stored-message
(fn [cofx _]
(assoc cofx :get-last-stored-message msg-store/get-last-message)))
(re-frame/reg-cofx
:get-message-previews
(fn [cofx _]
(assoc cofx :message-previews (msg-store/get-previews))))
(re-frame/reg-cofx
:all-stored-chats
(fn [cofx _]
(assoc cofx :all-stored-chats (chats-store/get-all))))
;;;; Effects
(re-frame/reg-fx
:update-message
(fn [message]
(msg-store/update message)))
(re-frame/reg-fx
:save-message
(fn [{:keys [chat-id] :as message}]
(msg-store/save chat-id message)))
(re-frame/reg-fx
:save-chat
(fn [chat]
(chats-store/save chat)))
(re-frame/reg-fx
:save-all-contacts
(fn [contacts]
(contacts-store/save-all contacts)))
(re-frame/reg-fx
:protocol-send-seen
(fn [params]
(protocol/send-seen! params)))
;;;; Helper fns
(defn init-console-chat
[{:keys [chats] :accounts/keys [current-account-id] :as db} existing-account?]
(if (chats const/console-chat-id)
{:db db}
(cond-> {:db (-> db
(assoc :new-chat sign-up/console-chat)
(update :chats assoc const/console-chat-id sign-up/console-chat)
(assoc :current-chat-id const/console-chat-id))
:dispatch-n [[:add-contacts [sign-up/console-contact]]]
:save-chat sign-up/console-chat
:save-all-contacts [sign-up/console-contact]}
(not current-account-id)
(update :dispatch-n concat sign-up/intro-events)
existing-account?
(update :dispatch-n concat sign-up/start-signup-events))))
;;;; Handlers
(handlers/register-handler-db
:set-layout-height
[re-frame/trim-v]
(fn [db [height]]
(assoc db :layout-height height)))
(handlers/register-handler-db
:set-chat-ui-props
[re-frame/trim-v]
(fn [db [kvs]]
(model/set-chat-ui-props db kvs)))
(handlers/register-handler-db
:toggle-chat-ui-props
[re-frame/trim-v]
(fn [db [ui-element]]
(model/toggle-chat-ui-prop db ui-element)))
(handlers/register-handler-db
:show-message-details
[re-frame/trim-v]
(fn [db [details]]
(model/set-chat-ui-props db {:show-bottom-info? true
:show-emoji? false
:bottom-info details})))
(handlers/register-handler-fx
:load-more-messages
[(re-frame/inject-cofx :get-stored-messages)]
(fn [{{:keys [current-chat-id loading-allowed] :as db} :db
get-stored-messages :get-stored-messages} _]
(let [all-loaded? (get-in db [:chats current-chat-id :all-loaded?])]
(if (and loading-allowed (not all-loaded?))
(let [messages-path [:chats current-chat-id :messages]
messages (get-in db messages-path)
chat-messages (filter #(= current-chat-id (:chat-id %)) messages)
new-messages (get-stored-messages current-chat-id (count chat-messages))
all-loaded? (> const/default-number-of-messages (count new-messages))]
{:db (-> db
(assoc :loading-allowed false)
(update-in messages-path concat new-messages)
(assoc-in [:chats current-chat-id :all-loaded?] all-loaded?))
;; we permit loading more messages again after 400ms
:dispatch-later [{:ms 400 :dispatch [:set :loading-allowed true]}]})
{:db db}))))
(handlers/register-handler-db
:set-message-shown
[re-frame/trim-v]
(fn [db [{:keys [chat-id message-id]}]]
(update-in db
[:chats chat-id :messages]
(fn [messages]
(map (fn [message]
(if (= message-id (:message-id message))
(assoc message :new? false)
message))
messages)))))
(handlers/register-handler-fx
:init-console-chat
(fn [{:keys [db]} _]
(init-console-chat db false)))
(handlers/register-handler-fx
:initialize-chats
[(re-frame/inject-cofx :all-stored-chats)
(re-frame/inject-cofx :stored-unviewed-messages)
(re-frame/inject-cofx :get-last-stored-message)
(re-frame/inject-cofx :get-message-previews)]
(fn [{:keys [db all-stored-chats stored-unviewed-messages get-last-stored-message message-previews]} _]
(let [{:accounts/keys [account-creation?]} db
new-db (unviewed-messages-model/load-unviewed-messages db stored-unviewed-messages)
event [:load-default-contacts!]]
(if account-creation?
{:db new-db
:dispatch-n [event]}
(let [chats (->> all-stored-chats
(map (fn [{:keys [chat-id] :as chat}]
[chat-id (assoc chat :last-message (get-last-stored-message chat-id))]))
(into {}))]
(-> new-db
(assoc-in [:message-data :preview] message-previews)
(assoc :handler-data (handler-data/get-all))
(assoc :chats chats)
(init-console-chat true)
(update :dispatch-n conj event)))))))
(handlers/register-handler-fx
:reload-chats
[(re-frame/inject-cofx :all-stored-chats) (re-frame/inject-cofx :get-last-stored-message)]
(fn [{:keys [db all-stored-chats get-last-stored-message]} _]
(let [updated-chats (->> all-stored-chats
(map (fn [{:keys [chat-id] :as chat}]
(let [prev-chat (get (:chats db) chat-id)
updated-chat (assoc chat :last-message (get-last-stored-message chat-id))]
[chat-id (merge prev-chat updated-chat)])))
(into {}))]
(-> (assoc db :chats updated-chats)
(init-console-chat true)))))
(handlers/register-handler-fx
:send-seen!
[re-frame/trim-v]
(fn [{:keys [db]} [{:keys [message-id chat-id from]}]]
(let [{:keys [web3 current-public-key chats]
:contacts/keys [contacts]} db
{:keys [group-chat public?]} (get chats chat-id)]
(cond-> {:db (unviewed-messages-model/remove-unviewed-messages db chat-id)
:update-message {:message-id message-id
:message-status :seen}}
(and (not (get-in contacts [chat-id] :dapp?))
(not public?))
(assoc :protocol-send-seen
{:web3 web3
:message (cond-> {:from current-public-key
:to from
:message-id message-id}
group-chat (assoc :group-id chat-id))})))))
(handlers/register-handler-fx
:show-mnemonic
[(re-frame/inject-cofx :get-stored-message) re-frame/trim-v]
(fn [{:keys [get-stored-message]} [mnemonic signing-phrase]]
(let [crazy-math-message? (get-stored-message chat-const/crazy-math-message-id)]
{:dispatch-n (sign-up/passphrase-messages-events mnemonic
signing-phrase
crazy-math-message?)})))
(handlers/register-handler-fx
:account-generation-message
[(re-frame/inject-cofx :get-stored-message)]
(fn [{:keys [get-stored-message]} _]
(when-not (get-stored-message chat-const/passphrase-message-id)
{:dispatch sign-up/account-generation-event})))
(handlers/register-handler-fx
:move-to-internal-failure-message
[(re-frame/inject-cofx :get-stored-message)]
(fn [{:keys [get-stored-message]} _]
(when-not (get-stored-message chat-const/move-to-internal-failure-message-id)
{:dispatch sign-up/move-to-internal-failure-event})))
(comment
(handlers/register-handler-fx
:init-chat
[(re-frame/inject-cofx :get-stored-messages)]
(fn [{:keys [db get-stored-messages]} _]
(let [current-chat-id (:current-chat-id db)]
{:db (assoc-in [:chats current-chat-id :messages] (get-stored-messages current-chat-id))
;; TODO(janherich): make this dispatch into fn call once commands loading is refactored
:dispatch [:load-commands! current-chat-id]}))))

View File

@ -0,0 +1,75 @@
(ns status-im.chat.events.animation
(:require [re-frame.core :as re-frame]
[status-im.chat.views.input.utils :as input-utils]
[status-im.utils.handlers :as handlers]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]))
;;;; Helper fns
(defn set-expandable-height
[{:keys [current-chat-id] :as db} key value]
(-> db
(assoc-in [:chat-animations current-chat-id key :height] value)
(update-in [:chat-animations current-chat-id key :changes-counter] inc)))
(defn choose-predefined-expandable-height
[{:keys [current-chat-id chat-ui-props layout-height] :as db} key preset]
(if (= preset :max)
(set-expandable-height db key :max)
(let [input-height (get-in chat-ui-props [current-chat-id :input-height])
chat-input-margin (if platform/ios?
(get db :keyboard-height)
0)
bottom (+ input-height chat-input-margin)
height (case preset
:min input-utils/min-height
(input-utils/default-container-area-height bottom layout-height))]
(set-expandable-height db key height))))
;;;; Handlers
(handlers/register-handler-db
:set-expandable-height
[re-frame/trim-v]
(fn [db [key value]]
(set-expandable-height db key value)))
(handlers/register-handler-db
:choose-predefined-expandable-height
[re-frame/trim-v]
(fn [db [key preset]]
(choose-predefined-expandable-height db key preset)))
(handlers/register-handler-db
:fix-expandable-height
[re-frame/trim-v]
(fn [{:keys [current-chat-id chats chat-ui-props layout-height] :as db} [vy current key]]
(let [input-height (get-in chat-ui-props [current-chat-id :input-height])
chat-input-margin (if platform/ios?
(get db :keyboard-height)
0)
bottom (+ input-height chat-input-margin)
min-height input-utils/min-height
max-height (input-utils/max-container-area-height bottom layout-height)
default-height (input-utils/default-container-area-height bottom layout-height)
possible-values [min-height default-height max-height]
moving-down? (pos? vy)
closest-index (->> possible-values
(map-indexed vector)
(sort-by (fn [[i v]] (Math/abs (- v current))))
(ffirst))
height (cond (and moving-down? (not= closest-index 0))
(get possible-values (dec closest-index))
(and (not moving-down?) (not= closest-index 2))
(get possible-values (inc closest-index))
moving-down?
min-height
(not moving-down?)
max-height)]
(set-expandable-height db key height))))

View File

@ -1,17 +1,15 @@
(ns status-im.chat.events.commands (ns status-im.chat.events.commands
(:require [cljs.reader :as reader] (:require [cljs.reader :as reader]
[clojure.string :as str] [clojure.string :as str]
[re-frame.core :refer [reg-fx reg-cofx inject-cofx dispatch trim-v]] [re-frame.core :as re-frame]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.data-store.messages :as msg-store] [status-im.utils.handlers :as handlers]
[status-im.utils.handlers :refer [register-handler-fx]]
[status-im.native-module.core :as status]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]))
;;;; Helper fns ;;;; Helper fns
(defn generate-context (defn- generate-context
"Generates context for jail call" "Generates context for jail call"
[{:keys [chats] :accounts/keys [current-account-id]} chat-id to group-id] [{:keys [chats] :accounts/keys [current-account-id]} chat-id to group-id]
(merge {:platform platform/platform (merge {:platform platform/platform
@ -22,101 +20,80 @@
(not (nil? group-id)))}} (not (nil? group-id)))}}
i18n/delimeters)) i18n/delimeters))
;;;; Coeffects (defn request-command-message-data
"Requests command message data from jail"
(reg-cofx [db
::get-persisted-message {{:keys [command content-command params type]} :content
(fn [coeffects _] :keys [chat-id jail-id group-id message-id] :as message}
(assoc coeffects :get-persisted-message msg-store/get-by-id))) data-type]
(let [{:keys [chats]
;;;; Effects :accounts/keys [current-account-id]
:contacts/keys [contacts]} db
(reg-fx jail-id (or jail-id chat-id)
::update-persisted-message jail-id (if (get-in chats [jail-id :group-chat])
(fn [message] (get-in chats [jail-id :command-suggestions (keyword command) :owner-id])
(msg-store/update message))) jail-id)]
(if (get-in contacts [jail-id :commands-loaded?])
(let [path [(if (= :response (keyword type)) :responses :commands)
(reg-fx (or content-command command)
:chat-fx/call-jail data-type]
(fn [{:keys [callback-events-creator] :as opts}] to (get-in contacts [chat-id :address])
(status/call-jail jail-params {:parameters params
(-> opts :context (generate-context db chat-id to group-id)}]
(dissoc :callback-events-creator) {:call-jail {:jail-id jail-id
(assoc :callback :path path
(fn [jail-response] :params jail-params
(doseq [event (callback-events-creator jail-response)] :callback-events-creator (fn [jail-response]
(dispatch event)))))))) [[::jail-command-data-response
jail-response message data-type]])}})
{:dispatch-n [[:add-commands-loading-callback jail-id
#(re-frame/dispatch [:request-command-message-data message data-type])]
[:load-commands! jail-id]]})))
;;;; Handlers ;;;; Handlers
(register-handler-fx (handlers/register-handler-fx
::jail-command-data-response ::jail-command-data-response
[trim-v] [re-frame/trim-v]
(fn [{:keys [db]} [{{:keys [returned]} :result} {:keys [message-id on-requested]} data-type]] (fn [{:keys [db]} [{{:keys [returned]} :result} {:keys [message-id on-requested]} data-type]]
(cond-> {} (cond-> {}
returned returned
(assoc :db (assoc-in db [:message-data data-type message-id] returned)) (assoc :db (assoc-in db [:message-data data-type message-id] returned))
(and returned (and returned
(= :preview data-type)) (= :preview data-type))
(assoc ::update-persisted-message {:message-id message-id (assoc :update-message {:message-id message-id
:preview (prn-str returned)}) :preview (prn-str returned)})
on-requested on-requested
(assoc :dispatch (on-requested returned))))) (assoc :dispatch (on-requested returned)))))
(register-handler-fx (handlers/register-handler-fx
:request-command-data :request-command-message-data
[trim-v] [re-frame/trim-v]
(fn [{:keys [db]} (fn [{:keys [db]} [message data-type]]
[{{:keys [command content-command params type]} :content (request-command-message-data db message data-type)))
:keys [chat-id jail-id group-id message-id handler-data] :as message}
data-type]]
(let [{:keys [chats]
:accounts/keys [current-account-id]
:contacts/keys [contacts]} db
jail-id (or jail-id chat-id)
jail-id (if (get-in chats [jail-id :group-chat])
(get-in chats [jail-id :command-suggestions (keyword command) :owner-id])
jail-id)]
(if (get-in contacts [jail-id :commands-loaded?])
(let [path [(if (= :response (keyword type)) :responses :commands)
(or content-command command)
data-type]
to (get-in contacts [chat-id :address])
jail-params {:parameters params
:context (generate-context db chat-id to group-id)}]
{:chat-fx/call-jail {:jail-id jail-id
:path path
:params jail-params
:callback-events-creator (fn [jail-response]
[[::jail-command-data-response
jail-response message data-type]])}})
{:dispatch-n [[:add-commands-loading-callback jail-id
#(dispatch [:request-command-data message data-type])]
[:load-commands! jail-id]]}))))
(register-handler-fx (handlers/register-handler-fx
:execute-command-immediately :execute-command-immediately
[trim-v] [re-frame/trim-v]
(fn [_ [{command-name :name :as command}]] (fn [_ [{command-name :name :as command}]]
(case (keyword command-name) (case (keyword command-name)
:grant-permissions :grant-permissions
{:dispatch [:request-permissions {:dispatch [:request-permissions
[:read-external-storage] [:read-external-storage]
#(dispatch [:initialize-geth])]} #(re-frame/dispatch [:initialize-geth])]}
(log/debug "ignoring command: " command)))) (log/debug "ignoring command: " command))))
(register-handler-fx (handlers/register-handler-fx
:request-command-preview :request-command-preview
[trim-v (inject-cofx ::get-persisted-message)] [re-frame/trim-v (re-frame/inject-cofx :get-stored-message)]
(fn [{:keys [db get-persisted-message]} [{:keys [message-id] :as message}]] (fn [{:keys [db get-stored-message]} [{:keys [message-id] :as message}]]
(let [previews (get-in db [:message-data :preview])] (let [previews (get-in db [:message-data :preview])]
(when-not (contains? previews message-id) (when-not (contains? previews message-id)
(let [{serialized-preview :preview} (get-persisted-message message-id)] (let [{serialized-preview :preview} (get-stored-message message-id)]
;; if preview is already cached in db, do not request it from jail ;; if preview is already cached in db, do not request it from jail
;; and write it directly to message-data path ;; and write it directly to message-data path
(if serialized-preview (if serialized-preview
{:db (assoc-in db {:db (assoc-in db
[:message-data :preview message-id] [:message-data :preview message-id]
(reader/read-string serialized-preview))} (reader/read-string serialized-preview))}
{:dispatch [:request-command-data message :preview]})))))) (request-command-message-data db message :preview)))))))

View File

@ -0,0 +1,129 @@
(ns status-im.chat.events.console
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.constants :as const]
[status-im.i18n :as i18n]
[status-im.chat.events.sign-up :as sign-up-events]
[status-im.ui.screens.accounts.events :as accounts-events]
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[goog.string :as gstring]
goog.string.format))
;;;; Helper fns
(defn console-respond-command-events
[command random-id-seq]
(let [{:keys [command handler-data]} command]
(when command
(let [{:keys [name]} command]
(case name
"js" (let [{:keys [err data messages]} handler-data
content (or err data)
message-events (mapv (fn [{:keys [message type]} id]
[:received-message
{:message-id id
:content (str type ": " message)
:content-type const/text-content-type
:outgoing false
:chat-id const/console-chat-id
:from const/console-chat-id
:to "me"}])
messages random-id-seq)]
(conj message-events
[:received-message
{:message-id (first random-id-seq)
:content (str content)
:content-type const/text-content-type
:outgoing false
:chat-id const/console-chat-id
:from const/console-chat-id
:to "me"}]))
(log/debug "ignoring command: " command))))))
(def faucet-base-url->url
{"http://faucet.ropsten.be:3001" "http://faucet.ropsten.be:3001/donate/0x%s"
"http://46.101.129.137:3001" "http://46.101.129.137:3001/donate/0x%s"})
(defn- faucet-response-event [message-id content]
[:received-message
{:message-id message-id
:content content
:content-type const/text-content-type
:outgoing false
:chat-id const/console-chat-id
:from const/console-chat-id
:to "me"}])
(def console-commands->fx
{"password"
(fn [{:keys [db]} {:keys [params]}]
(accounts-events/create-account db (:password params)))
"phone"
(fn [{:keys [db]} {:keys [params id]}]
(-> db
(sign-up-events/sign-up (:phone params) id)
(as-> fx
(assoc fx :dispatch-n [(:dispatch fx)]))
(dissoc :dispatch)))
"confirmation-code"
(fn [{:keys [db]} {:keys [params id]}]
(sign-up-events/sign-up-confirm db (:code params) id))
"faucet"
(fn [{:keys [db random-id]} {:keys [params id]}]
(let [{:accounts/keys [accounts current-account-id]} db
current-address (get-in accounts [current-account-id :address])
faucet-url (get faucet-base-url->url (:url params))]
{:http-get {:url (gstring/format faucet-url current-address)
:success-event-creator (fn [_]
(faucet-response-event
random-id
(i18n/label :t/faucet-success)))
:failure-event-creator (fn [_]
(faucet-response-event
random-id
(i18n/label :t/faucet-error)))}}))
"debug"
(fn [{:keys [random-id] :as cofx} {:keys [params id]}]
(let [debug? (= "On" (:mode params))
fx (accounts-events/account-update cofx {:debug? debug?})]
(assoc fx :dispatch-n (if debug?
[[:debug-server-start]
[:received-message
{:message-id random-id
:content (i18n/label :t/debug-enabled)
:content-type const/text-content-type
:outgoing false
:chat-id const/console-chat-id
:from const/console-chat-id
:to "me"}]]
[[:debug-server-stop]]))))})
(def commands-names (set (keys console-commands->fx)))
(def commands-with-delivery-status
(disj commands-names "password" "faucet" "debug"))
;;;; Handlers
;; TODO(janherich) remove this once send-message events are refactored
(handlers/register-handler-fx
:invoke-console-command-handler!
[re-frame/trim-v (re-frame/inject-cofx :random-id) (re-frame/inject-cofx :now)]
(fn [cofx [{:keys [chat-id command] :as command-params}]]
(let [fx-fn (get console-commands->fx (-> command :command :name))]
(-> cofx
(fx-fn command)
(update :dispatch-n (fnil conj []) [:prepare-command! chat-id command-params])))))
;; TODO(janherich) remove this once send-message events are refactored
(handlers/register-handler-fx
:console-respond-command
[(re-frame/inject-cofx :random-id-seq) re-frame/trim-v]
(fn [{:keys [random-id-seq]} [command]]
(when-let [events (console-respond-command-events command random-id-seq)]
{:dispatch-n events})))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
(ns status-im.chat.events.receive-message
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.handlers :as handlers]
[status-im.utils.random :as random]
[status-im.utils.clocks :as clocks]
[status-im.constants :as const]
[status-im.chat.utils :as chat-utils]
[status-im.chat.models.unviewed-messages :as unviewed-messages-model]
[status-im.data-store.chats :as chat-store]
[status-im.data-store.messages :as msg-store]))
(re-frame/reg-cofx
:pop-up-chat?
(fn [cofx]
(assoc cofx :pop-up-chat? (fn [chat-id]
(or (not (chat-store/exists? chat-id))
(chat-store/is-active? chat-id))))))
(re-frame/reg-cofx
:get-last-clock-value
(fn [cofx]
(assoc cofx :get-last-clock-value msg-store/get-last-clock-value)))
(re-frame/reg-cofx
:current-timestamp
(fn [cofx]
;; TODO (janherich) why is actual timestmap generation in random namespace ?
(assoc cofx :current-timestamp (random/timestamp))))
(defn- get-current-identity
[{:accounts/keys [accounts current-account-id]}]
(get-in accounts [current-account-id :public-key]))
(defn- wallet-message
[{:keys [content-type] :as message}]
(log/debug "Wallet msg")
(let [wallet-ct (if (= content-type const/content-type-command)
const/content-type-wallet-command
const/content-type-wallet-request)]
(-> message
(assoc :clock-value 0
:chat-id const/wallet-chat-id
:content-type wallet-ct)
(dissoc :group-id))))
(defn add-message
[{:keys [db get-stored-message get-last-stored-message pop-up-chat?
get-last-clock-value current-timestamp random-id]}
{:keys [from group-id chat-id content-type
message-id timestamp clock-value]
:as message
:or {clock-value 0}}]
(let [chat-identifier (or group-id chat-id from)
current-identity (get-current-identity db)]
;; proceed with adding message if message is not already stored in realm,
;; it's not from current user (outgoing message) and it's for relevant chat
;; (either current active chat or new chat not existing yet)
(if (and (not (get-stored-message chat-identifier))
(not= from current-identity)
(pop-up-chat? chat-identifier))
(let [group-chat? (not (nil? group-id))
enriched-message (assoc (chat-utils/check-author-direction
(get-last-stored-message chat-identifier)
message)
:chat-id chat-identifier
:timestamp (or timestamp current-timestamp)
:clock-value (clocks/receive
clock-value
(get-last-clock-value chat-identifier)))]
(cond-> {:db (-> db
(chat-utils/add-message-to-db chat-identifier chat-identifier enriched-message
(:new? enriched-message))
(unviewed-messages-model/add-unviewed-message chat-identifier message-id)
(assoc-in [:chats chat-identifier :last-message] message))
:dispatch-n [[:upsert-chat! {:chat-id chat-identifier
:group-chat group-chat?}]
[:request-command-message-data enriched-message :short-preview]]
:save-message (dissoc enriched-message :new?)}
(get-in enriched-message [:content :command])
(update :dispatch-n conj [:request-command-preview enriched-message])
(= (:content-type enriched-message) const/content-type-command-request)
(update :dispatch-n conj [:add-request chat-identifier enriched-message])
;; TODO(janherich) refactor this ugly special treatment of wallet send commands for logged in user
(and (= (get-in message [:content :params :bot-db :public :recipient :whisper-identity])
current-identity)
(= content-type const/content-type-command)
(not= chat-identifier const/wallet-chat-id)
(= "send" (get-in message [:content :command])))
(update :dispatch-n conj [:received-message (wallet-message (assoc message :message-id random-id))])))
{:db db})))
(def ^:private receive-interceptors
[(re-frame/inject-cofx :get-stored-message) (re-frame/inject-cofx :get-last-stored-message)
(re-frame/inject-cofx :pop-up-chat?) (re-frame/inject-cofx :get-last-clock-value)
(re-frame/inject-cofx :current-timestamp) (re-frame/inject-cofx :random-id)
re-frame/trim-v])
(handlers/register-handler-fx
:received-protocol-message!
receive-interceptors
(fn [cofx [{:keys [from to payload]}]]
(add-message cofx (merge payload
{:from from
:to to
:chat-id from}))))
(handlers/register-handler-fx
:received-message
receive-interceptors
(fn [cofx [message]]
(add-message cofx message)))
(handlers/register-handler-fx
:received-message-when-commands-loaded
receive-interceptors
(fn [{:keys [db] :as cofx} [chat-id message]]
(if (and (:status-node-started? db)
(get-in db [:contacts/contacts chat-id :commands-loaded?]))
(add-message cofx message)
{:dispatch-later [{:ms 400 :dispatch [:received-message-when-commands-loaded chat-id message]}]})))

View File

@ -0,0 +1,123 @@
(ns status-im.chat.events.sign-up
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.utils.phone-number :as phone-number-util]
[status-im.constants :as const]
[status-im.chat.sign-up :as sign-up]
[status-im.ui.screens.accounts.events :as accounts-events]
[status-im.ui.screens.contacts.events :as contacts-events]))
;;;; Helpers fns
(defn sign-up
"Creates effects for signing up"
[db phone-number message-id]
(let [current-account-id (:accounts/current-account-id db)
{:keys [public-key address]} (get-in db [:accounts/accounts current-account-id])]
{:dispatch sign-up/start-listening-confirmation-code-sms-event
:http-post {:action "sign-up"
:data {:phone-number (phone-number-util/format-phone-number phone-number)
:whisper-identity public-key
:address address}
:success-event-creator (fn [_]
[::sign-up-success message-id])
:failure-event-creator (fn [_]
[::http-request-failure [::sign-up phone-number message-id]])}}))
(defn sign-up-confirm
"Creates effects for sign-up confirmation"
[db confirmation-code message-id]
{:http-post {:action "sign-up-confirm"
:data {:code confirmation-code}
:success-event-creator (fn [body]
[::sign-up-confirm-response body message-id])
:failure-event-creator (fn [_]
[::http-request-failure [::sign-up-confirm confirmation-code message-id]])}})
;;;; Handlers
(handlers/register-handler-fx
::sign-up
[re-frame/trim-v]
(fn [{:keys [db]} [phone-number message-id]]
(sign-up db phone-number message-id)))
(defn- message-seen [{:keys [db] :as fx} message-id]
(merge fx
{:db (assoc-in db [:message-data :statuses message-id] {:status :seen})
:update-message {:message-id message-id
:message-status :seen}}))
(handlers/register-handler-fx
::sign-up-success
[re-frame/trim-v (re-frame/inject-cofx :random-id)]
(fn [{:keys [db random-id]} message-id]
(-> {:db db
:dispatch-n [;; create manual way for entering confirmation code
(sign-up/enter-confirmation-code-event random-id)
;; create automatic way for receiving confirmation code
sign-up/start-listening-confirmation-code-sms-event]}
(message-seen message-id))))
(handlers/register-handler-fx
:start-listening-confirmation-code-sms
[re-frame/trim-v]
(fn [{:keys [db]} [sms-listener]]
{:db (if-not (:confirmation-code-sms-listener db)
(assoc db :confirmation-code-sms-listener sms-listener)
db)}))
(handlers/register-handler-fx
::sign-up-confirm
(fn [{:keys [db]} [confirmation-code message-id]]
(sign-up-confirm db confirmation-code message-id)))
(defn- sign-up-confirmed [{:keys [db] :as fx}]
(cond-> (update fx :dispatch-n conj [:request-permissions
[:read-contacts]
#(re-frame/dispatch [:sync-contacts (fn [contacts]
[::contacts-synced contacts])])])
(:confirmation-code-sms-listener db)
(merge {:db (dissoc db :confirmation-code-sms-listener)
:remove-sms-listener (:confirmation-code-sms-listener db)})))
(handlers/register-handler-fx
::sign-up-confirm-response
[re-frame/trim-v (re-frame/inject-cofx :random-id)]
(fn [{:keys [db random-id]} [{:keys [message status]} message-id]]
(cond-> {:db db
:dispatch-n [[:received-message
{:message-id random-id
:content message
:content-type const/text-content-type
:outgoing false
:chat-id const/console-chat-id
:from const/console-chat-id
:to "me"}]]}
message-id
(message-seen message-id)
(= "confirmed" status)
sign-up-confirmed
(= "failed" status)
(update :dispatch-n conj (sign-up/incorrect-confirmation-code-event random-id)))))
(handlers/register-handler-fx
::contacts-synced
[re-frame/trim-v (re-frame/inject-cofx :random-id) (re-frame/inject-cofx :now)]
(fn [{:keys [db random-id] :as cofx} [contacts]]
(-> db
(contacts-events/add-contacts contacts)
(as-> fx
(merge fx
(accounts-events/account-update (assoc cofx :db (:db fx)) {:signed-up? true})
{:dispatch (sign-up/contacts-synchronised-event random-id)})))))
(handlers/register-handler-fx
::http-request-failure
[re-frame/trim-v]
(fn [_ [original-event-vector]]
;; TODO(janherich): in case of http request failure, we will try to hit http endpoint in loop forever,
;; maybe it's better to cut it after N tries and display error message with explanation to user
{:dispatch-later [{:ms 1000 :dispatch original-event-vector}]}))

View File

@ -9,180 +9,30 @@
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[status-im.data-store.chats :as chats] [status-im.data-store.chats :as chats]
[status-im.data-store.contacts :as contacts] [status-im.data-store.contacts :as contacts]
[status-im.data-store.messages :as messages] [status-im.data-store.messages :as messages]
[status-im.data-store.handler-data :as handler-data]
[status-im.data-store.pending-messages :as pending-messages] [status-im.data-store.pending-messages :as pending-messages]
[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
default-number-of-messages
console-chat-id console-chat-id
wallet-chat-id]] wallet-chat-id]]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.chat.sign-up :as sign-up-service] [status-im.chat.sign-up :as sign-up-service]
[status-im.ui.screens.navigation :as nav] [status-im.ui.screens.navigation :as nav]
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u] [status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
[status-im.handlers.server :as server]
[status-im.utils.phone-number :refer [format-phone-number [status-im.utils.phone-number :refer [format-phone-number
valid-mobile-number?]] valid-mobile-number?]]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.utils.types :refer [json->clj]] [status-im.utils.types :refer [json->clj]]
[status-im.chat.utils :refer [console? not-console? safe-trim]] [status-im.chat.utils :refer [console? not-console? safe-trim]]
[status-im.utils.gfycat.core :refer [generate-gfy]] [status-im.utils.gfycat.core :refer [generate-gfy]]
status-im.chat.events.input status-im.chat.events
status-im.chat.events.commands status-im.chat.handlers.requests
status-im.chat.handlers.animation status-im.chat.handlers.send-message
status-im.chat.handlers.requests
status-im.chat.handlers.unviewed-messages
status-im.chat.handlers.send-message
status-im.chat.handlers.receive-message
status-im.chat.handlers.faucet
[cljs.core.async :as a] [cljs.core.async :as a]
status-im.chat.handlers.webview-bridge status-im.chat.handlers.webview-bridge
status-im.chat.handlers.console
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(register-handler :set-layout-height
(fn [db [_ height]]
(assoc db :layout-height height)))
(register-handler :set-chat-ui-props
(fn [{:keys [current-chat-id] :as db} [_ kvs]]
(update-in db [:chat-ui-props current-chat-id] merge kvs)))
(register-handler :toggle-chat-ui-props
(fn [{:keys [current-chat-id chat-ui-props] :as db} [_ ui-element chat-id]]
(let [chat-id (or chat-id current-chat-id)]
(update-in db [:chat-ui-props chat-id ui-element] not))))
(register-handler :show-message-details
(u/side-effect!
(fn [_ [_ details]]
(dispatch [:set-chat-ui-props {:show-bottom-info? true
:show-emoji? false
:bottom-info details}]))))
(register-handler :load-more-messages
(fn [{:keys [current-chat-id loading-allowed] :as db} _]
(let [all-loaded? (get-in db [:chats current-chat-id :all-loaded?])]
(if loading-allowed
(do (am/go
(<! (a/timeout 400))
(dispatch [:set :loading-allowed true]))
(if all-loaded?
db
(let [messages-path [:chats current-chat-id :messages]
messages (get-in db messages-path)
chat-messages (filter #(= current-chat-id (:chat-id %)) messages)
new-messages (messages/get-by-chat-id current-chat-id (count chat-messages))
all-loaded? (> default-number-of-messages (count new-messages))]
(-> db
(assoc :loading-allowed false)
(update-in messages-path concat new-messages)
(assoc-in [:chats current-chat-id :all-loaded?] all-loaded?)))))
db))))
(defn set-message-shown
[db chat-id message-id]
(update-in db
[:chats chat-id :messages]
(fn [messages]
(map (fn [message]
(if (= message-id (:message-id message))
(assoc message :new? false)
message))
messages))))
(register-handler :set-message-shown
(fn [db [_ {:keys [chat-id message-id]}]]
(set-message-shown db chat-id message-id)))
(register-handler :cancel-command
(fn [{:keys [current-chat-id] :as db} _]
(-> db
(assoc-in [:chats current-chat-id :command-input] {})
(update-in [:chats current-chat-id :input-text] safe-trim))))
(defn init-console-chat
([{:keys [chats] :accounts/keys [current-account-id] :as db} existing-account?]
(let [new-chat sign-up-service/console-chat]
(if (chats console-chat-id)
db
(do
(dispatch [:add-contacts [sign-up-service/console-contact]])
(chats/save new-chat)
(contacts/save-all [sign-up-service/console-contact])
(when-not current-account-id
(sign-up-service/intro))
(when existing-account?
(sign-up-service/start-signup))
(-> db
(assoc :new-chat new-chat)
(update :chats assoc console-chat-id new-chat)
(assoc :current-chat-id console-chat-id)))))))
(register-handler :init-console-chat
(fn [db _]
(init-console-chat db false)))
(register-handler :account-generation-message
(u/side-effect!
(fn [_]
(when-not (messages/get-by-id chat-consts/passphrase-message-id)
(sign-up-service/account-generation-message)))))
(register-handler :move-to-internal-failure-message
(u/side-effect!
(fn [_]
(when-not (messages/get-by-id chat-consts/move-to-internal-failure-message-id)
(sign-up-service/move-to-internal-failure-message)))))
(register-handler :show-mnemonic
(u/side-effect!
(fn [_ [_ mnemonic signing-phrase]]
(let [crazy-math-message? (messages/get-by-id chat-consts/crazy-math-message-id)]
(sign-up-service/passphrase-messages mnemonic signing-phrase crazy-math-message?)))))
(defn- handle-sms [{body :body}]
(when-let [matches (re-matches #"(\d{4})" body)]
(dispatch [:sign-up-confirm (second matches)])))
(register-handler :sign-up
(after (fn [_ [_ phone-number]]
(dispatch [:account-update {:phone phone-number}])))
(fn [db [_ phone-number message-id]]
(sign-up-service/start-listening-confirmation-code-sms)
(let [formatted (format-phone-number phone-number)]
(server/sign-up db
formatted
message-id
sign-up-service/on-sign-up-response))))
(register-handler :start-listening-confirmation-code-sms
(fn [db [_ listener]]
(if-not (:confirmation-code-sms-listener db)
(assoc db :confirmation-code-sms-listener listener)
db)))
(register-handler :stop-listening-confirmation-code-sms
(fn [db]
(if (:confirmation-code-sms-listener db)
(sign-up-service/stop-listening-confirmation-code-sms db)
db)))
(register-handler :sign-up-confirm
(u/side-effect!
(fn [_ [_ confirmation-code message-id]]
(server/sign-up-confirm
confirmation-code
message-id
sign-up-service/on-send-code-response))))
(register-handler :set-signed-up
(u/side-effect!
(fn [_ [_ signed-up]]
(dispatch [:account-update {:signed-up? signed-up}]))))
(defn load-messages! (defn load-messages!
([db] (load-messages! db nil)) ([db] (load-messages! db nil))
([{:keys [current-chat-id] :as db} _] ([{:keys [current-chat-id] :as db} _]
@ -208,47 +58,6 @@
init-chat init-chat
load-commands!)) load-commands!))
(defn initialize-chats
[{:keys [loaded-chats chats] :accounts/keys [account-creation?] :as db} _]
(let [chats' (if account-creation?
chats
(->> loaded-chats
(map (fn [{:keys [chat-id] :as chat}]
(let [last-message (messages/get-last-message chat-id)]
[chat-id (assoc chat :last-message last-message)])))
(into {})))]
(-> db
(assoc :chats chats')
(assoc :handler-data (handler-data/get-all))
(dissoc :loaded-chats)
(init-console-chat true))))
(defn load-chats!
[{:accounts/keys [account-creation?] :as db} _]
(if account-creation?
db
(assoc db :loaded-chats (chats/get-all))))
(register-handler :initialize-chats
[(after #(dispatch [:load-unviewed-messages!]))
(after #(dispatch [:load-default-contacts!]))]
(u/handlers->
load-chats!
initialize-chats))
(register-handler :reload-chats
(fn [{:keys [chats] :as db} _]
(let [chats' (->> (chats/get-all)
(map (fn [{:keys [chat-id] :as chat}]
(let [last-message (messages/get-last-message chat-id)
prev-chat (get chats chat-id)
new-chat (assoc chat :last-message last-message)]
[chat-id (merge prev-chat new-chat)])))
(into {}))]
(-> (assoc db :chats chats')
(init-console-chat true)))))
(defmethod nav/preload-data! :chat (defmethod nav/preload-data! :chat
[{:keys [current-chat-id] :accounts/keys [current-account-id] :as db} [_ _ id]] [{:keys [current-chat-id] :accounts/keys [current-account-id] :as db} [_ _ id]]
(let [chat-id (or id current-chat-id) (let [chat-id (or id current-chat-id)
@ -427,27 +236,6 @@
remove-pending-messages! remove-pending-messages!
delete-chat!)) delete-chat!))
(defn send-seen!
[{:keys [web3 current-public-key chats]
:contacts/keys [contacts]}
[_ {:keys [from chat-id message-id]}]]
(when-not (get-in contacts [chat-id :dapp?])
(let [{:keys [group-chat public?]} (chats chat-id)]
(when-not public?
(protocol/send-seen! {:web3 web3
:message {:from current-public-key
:to from
:group-id (when group-chat chat-id)
:message-id message-id}})))))
(register-handler :send-seen!
[(after (fn [_ [_ {:keys [message-id]}]]
(messages/update {:message-id message-id
:message-status :seen})))
(after (fn [_ [_ {:keys [chat-id]}]]
(dispatch [:remove-unviewed-messages chat-id])))]
(u/side-effect! send-seen!))
(register-handler :check-and-open-dapp! (register-handler :check-and-open-dapp!
(u/side-effect! (u/side-effect!
(fn [{:keys [current-chat-id global-commands] (fn [{:keys [current-chat-id global-commands]

View File

@ -1,65 +0,0 @@
(ns status-im.chat.handlers.animation
(:require [re-frame.core :refer [after dispatch subscribe debug path]]
[status-im.chat.views.input.utils :as input-utils]
[status-im.utils.handlers :as handlers]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]))
(handlers/register-handler :set-expandable-height
(fn [{:keys [current-chat-id] :as db} [_ key value]]
(-> db
(assoc-in [:chat-animations current-chat-id key :height] value)
(update-in [:chat-animations current-chat-id key :changes-counter] inc))))
(handlers/register-handler :hide-expandable
(handlers/side-effect!
(fn [_ [_ key]]
(dispatch [:set-expandable-height key 1]))))
(handlers/register-handler :choose-predefined-expandable-height
(handlers/side-effect!
(fn [{:keys [current-chat-id chat-ui-props layout-height] :as db} [_ key preset]]
(if (= preset :max)
(dispatch [:set-expandable-height key :max])
(let [input-height (get-in chat-ui-props [current-chat-id :input-height])
chat-input-margin (if platform/ios?
(get db :keyboard-height)
0)
bottom (+ input-height chat-input-margin)
height (case preset
:min input-utils/min-height
(input-utils/default-container-area-height bottom layout-height))]
(dispatch [:set-expandable-height key height]))))))
(handlers/register-handler :fix-expandable-height
(handlers/side-effect!
(fn [{:keys [current-chat-id chats chat-ui-props layout-height] :as db} [_ vy current key]]
(let [input-height (get-in chat-ui-props [current-chat-id :input-height])
chat-input-margin (if platform/ios?
(get db :keyboard-height)
0)
bottom (+ input-height chat-input-margin)
min-height input-utils/min-height
max-height (input-utils/max-container-area-height bottom layout-height)
default-height (input-utils/default-container-area-height bottom layout-height)
possible-values [min-height default-height max-height]
moving-down? (pos? vy)
closest-index (->> possible-values
(map-indexed vector)
(sort-by (fn [[i v]] (Math/abs (- v current))))
(ffirst))
height (cond (and moving-down? (not= closest-index 0))
(get possible-values (dec closest-index))
(and (not moving-down?) (not= closest-index 2))
(get possible-values (inc closest-index))
moving-down?
min-height
(not moving-down?)
max-height)]
(dispatch [:set-expandable-height key height])))))

View File

@ -1,100 +0,0 @@
(ns status-im.chat.handlers.console
(:require [re-frame.core :refer [dispatch dispatch-sync after]]
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.constants :refer [console-chat-id
text-content-type]]
[status-im.data-store.messages :as messages]
[status-im.i18n :refer [label]]
[status-im.utils.random :as random]
[taoensso.timbre :as log]))
(def console-commands
{:password
(fn [{:keys [password]} _]
(dispatch [:create-account password]))
:phone
(fn [{:keys [phone]} id]
(dispatch [:sign-up phone id]))
:confirmation-code
(fn [{:keys [code]} id]
(dispatch [:sign-up-confirm code id]))
:faucet
(fn [{:keys [url]} id]
(dispatch [:open-faucet url id]))
:debug
(fn [{:keys [mode]} id]
(let [debug-on? (= mode "On")]
(dispatch [:account-update {:debug? debug-on?}])
(if debug-on?
(do
(dispatch [:debug-server-start])
(dispatch [:received-message
{:message-id (random/id)
:content (label :t/debug-enabled)
:content-type text-content-type
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}]))
(dispatch [:debug-server-stop]))))})
(def commands-names (set (keys console-commands)))
(def commands-with-delivery-status
(disj commands-names :password :faucet :debug))
(register-handler :invoke-console-command-handler!
(u/side-effect!
(fn [_ [_ {{:keys [command
params
id]
:as content} :command
chat-id :chat-id
:as all-params}]]
(let [{:keys [name]} command]
(dispatch [:prepare-command! chat-id all-params])
((console-commands (keyword name)) params id)))))
(register-handler :set-message-status
(after
(fn [_ [_ message-id status]]
(messages/update {:message-id message-id
:message-status status})))
(fn [db [_ message-id status]]
(assoc-in db [:message-data :statuses message-id] {:status status})))
(register-handler :console-respond-command
(u/side-effect!
(fn [_ [_ {:keys [command]}]]
(let [{:keys [command handler-data]} command]
(when command
(let [{:keys [name]} command]
(case name
"js" (let [{:keys [err data messages]} handler-data
content (or err data)]
(doseq [message messages]
(let [{:keys [message type]} message]
(dispatch [:received-message
{:message-id (random/id)
:content (str type ": " message)
:content-type text-content-type
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}])))
(when content
(dispatch [:received-message
{:message-id (random/id)
:content (str content)
:content-type text-content-type
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}])))
(log/debug "ignoring command: " command))))))))

View File

@ -1,49 +0,0 @@
(ns status-im.chat.handlers.faucet
(:require [re-frame.core :refer [dispatch]]
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.utils.utils :refer [http-get]]
[status-im.utils.random :as random]
[status-im.constants :refer [console-chat-id
text-content-type]]
[status-im.i18n :refer [label]]
[goog.string :as gstring]
goog.string.format))
(def faucets
[{:name "http://faucet.ropsten.be:3001"
:type :api
:api-url "http://faucet.ropsten.be:3001/donate/0x%s"}
{:name "http://46.101.129.137:3001"
:type :api
:api-url "http://46.101.129.137:3001/donate/0x%s"}])
(defn faucet-by-name [faucet-name]
(->> faucets
(filter #(= (:name %) faucet-name))
(first)))
(defn received-message [content]
(dispatch [:received-message
{:message-id (random/id)
:content content
:content-type text-content-type
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}]))
(defmulti open-faucet (fn [_ _ {:keys [type]}] type))
(defmethod open-faucet :api
[_ current-address {:keys [api-url]}]
(let [api-url (gstring/format api-url current-address)]
(http-get api-url
#(received-message (label :t/faucet-success))
#(received-message (label :t/faucet-error)))))
(register-handler :open-faucet
(u/side-effect!
(fn [{:accounts/keys [accounts current-account-id]} [_ faucet-name _]]
(if-let [faucet (faucet-by-name faucet-name)]
(let [current-address (get-in accounts [current-account-id :address])]
(open-faucet faucet-name current-address faucet))))))

View File

@ -1,112 +0,0 @@
(ns status-im.chat.handlers.receive-message
(:require [status-im.utils.handlers :refer [register-handler] :as u]
[re-frame.core :refer [enrich after debug dispatch path]]
[status-im.data-store.messages :as messages]
[status-im.chat.utils :as cu]
[status-im.utils.random :as random]
[status-im.constants :refer [wallet-chat-id
content-type-command
content-type-command-request]
:as c]
[cljs.reader :refer [read-string]]
[status-im.data-store.chats :as chats]
[status-im.utils.scheduler :as s]
[taoensso.timbre :as log]
[status-im.utils.clocks :as clocks]))
(defn store-message [{chat-id :chat-id :as message}]
(messages/save chat-id (dissoc message :new?)))
(defn get-current-identity
[{:accounts/keys [accounts current-account-id]}]
(get-in accounts [current-account-id :public-key]))
(declare add-message-to-wallet)
(defn add-message
[db {:keys [from group-id chat-id
message-id timestamp clock-value]
:as message
:or {clock-value 0}}]
(let [same-message (messages/get-by-id message-id)
current-identity (get-current-identity db)
chat-id' (or group-id chat-id from)
exists? (chats/exists? chat-id')
active? (chats/is-active? chat-id')
local-clock (messages/get-last-clock-value chat-id')
clock-new (clocks/receive clock-value local-clock)
recipient-pub-key (get-in message [:content :params :bot-db :public :recipient :whisper-identity])]
(when (and (not same-message)
(not= from current-identity)
(or (not exists?) active?))
(let [group-chat? (not (nil? group-id))
previous-message (messages/get-last-message chat-id')
message' (assoc (cu/check-author-direction previous-message message)
:chat-id chat-id'
:timestamp (or timestamp (random/timestamp))
:clock-value clock-new)]
(store-message message')
(dispatch [:upsert-chat! {:chat-id chat-id'
:group-chat group-chat?}])
(when (get-in message [:content :command])
(dispatch [:request-command-preview message]))
(dispatch [::add-message chat-id' message'])
(dispatch [::set-last-message message'])
(when (= (:content-type message') content-type-command-request)
(dispatch [:add-request chat-id' message']))
(dispatch [:add-unviewed-message chat-id' message-id]))
(if (and
(= recipient-pub-key current-identity)
(= (:content-type message) content-type-command)
(not= chat-id' wallet-chat-id)
(= "send" (get-in message [:content :command])))
(add-message-to-wallet db message)))))
(defn add-message-to-wallet [db {:keys [content-type] :as message}]
(let [ct (if (= content-type c/content-type-command)
c/content-type-wallet-command
c/content-type-wallet-request)
message' (-> (assoc message :clock-value 0
:message-id (random/id)
:chat-id wallet-chat-id
:content-type ct)
(dissoc :group-id))]
(add-message db message')))
(register-handler :received-protocol-message!
(u/side-effect!
(fn [_ [_ {:keys [from to payload]}]]
(dispatch [:received-message (merge payload
{:from from
:to to
:chat-id from})]))))
(register-handler :received-message
(after #(dispatch [:update-suggestions]))
(u/side-effect!
(fn [db [_ message]]
(add-message db message))))
(register-handler ::add-message
(fn [db [_ add-to-chat-id {:keys [chat-id new?] :as message}]]
(cu/add-message-to-db db add-to-chat-id chat-id message new?)))
(register-handler ::set-last-message
(fn [{:keys [chats] :as db} [_ {:keys [chat-id] :as message}]]
(dispatch [:request-command-data message :short-preview])
(assoc-in db [:chats chat-id :last-message] message)))
(defn commands-loaded? [db chat-id]
(get-in db [:contacts/contacts chat-id :commands-loaded?]))
(def timeout 400)
(register-handler :received-message-when-commands-loaded
(u/side-effect!
(fn [{:keys [status-node-started?] :as db} [_ chat-id message]]
(if (and status-node-started? (commands-loaded? db chat-id))
(dispatch [:received-message message])
(s/execute-later
#(dispatch [:received-message-when-commands-loaded chat-id message])
timeout)))))

View File

@ -19,7 +19,7 @@
[status-im.utils.datetime :as datetime] [status-im.utils.datetime :as datetime]
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[taoensso.timbre :refer-macros [debug] :as log] [taoensso.timbre :refer-macros [debug] :as log]
[status-im.chat.handlers.console :as console] [status-im.chat.events.console :as console]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.clocks :as clocks])) [status-im.utils.clocks :as clocks]))
@ -60,7 +60,7 @@
(defn console-command? [chat-id command-name] (defn console-command? [chat-id command-name]
(and (= console-chat-id chat-id) (and (= console-chat-id chat-id)
(console/commands-names (keyword command-name)))) (console/commands-names command-name)))
(register-handler :check-commands-handlers! (register-handler :check-commands-handlers!
(u/side-effect! (u/side-effect!
@ -124,10 +124,12 @@
(register-handler ::save-command! (register-handler ::save-command!
(u/side-effect! (u/side-effect!
(fn [_ [_ chat-id {:keys [command]} hidden-params]] (fn [db [_ chat-id {:keys [command]} hidden-params]]
(let [command (-> command (let [preview (get-in db [:message-data :preview (:message-id command)])
(update-in [:content :params] #(apply dissoc % hidden-params)) command (cond-> (-> command
(dissoc :to-message :has-handler))] (update-in [:content :params] #(apply dissoc % hidden-params))
(dissoc :to-message :has-handler :raw-input))
preview (assoc :preview (pr-str preview)))]
(messages/save chat-id command))))) (messages/save chat-id command)))))
(register-handler ::dispatch-responded-requests! (register-handler ::dispatch-responded-requests!

View File

@ -1,37 +0,0 @@
(ns status-im.chat.handlers.unviewed-messages
(:require [re-frame.core :refer [after enrich path dispatch]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.data-store.messages :as messages]))
(defn set-unviewed-messages [db]
(let [messages (->> (:raw-unviewed-messages db)
(group-by :chat-id)
(map (fn [[id messages]]
[id {:messages-ids (map :message-id messages)
:count (count messages)}]))
(into {}))]
(-> db
(assoc :unviewed-messages messages)
(dissoc :raw-unviewed-messages))))
(defn load-messages! [db]
(let [messages (messages/get-unviewed)]
(assoc db :raw-unviewed-messages messages)))
(register-handler ::set-unviewed-messages set-unviewed-messages)
(register-handler :load-unviewed-messages!
(after #(dispatch [::set-unviewed-messages]))
load-messages!)
(register-handler :add-unviewed-message
(path :unviewed-messages)
(fn [db [_ chat-id message-id]]
(-> db
(update-in [chat-id :messages-ids] conj message-id)
(update-in [chat-id :count] inc))))
(register-handler :remove-unviewed-messages
(path :unviewed-messages)
(fn [db [_ chat-id]]
(dissoc db chat-id)))

View File

@ -68,8 +68,7 @@
(dispatch [:proceed-command (dispatch [:proceed-command
{:command command, {:command command,
:metadata nil, :metadata nil,
:args [(get contact :name) amount]} :args [(get contact :name) amount]}])))))
current-chat-id])))))
(defn chat-with-command (defn chat-with-command
[_ [_ whisper-identity command-key params]] [_ [_ whisper-identity command-key params]]

View File

@ -0,0 +1,11 @@
(ns status-im.chat.models)
(defn set-chat-ui-props
"Updates ui-props in active chat by merging provided kvs into them"
[{:keys [current-chat-id] :as db} kvs]
(update-in db [:chat-ui-props current-chat-id] merge kvs))
(defn toggle-chat-ui-prop
"Toggles chat ui prop in active chat"
[{:keys [current-chat-id] :as db} ui-element]
(update-in db [:chat-ui-props current-chat-id ui-element] not))

View File

@ -47,13 +47,13 @@
{:keys [contacts requests]} (get-in db [:chats chat-id])] {:keys [contacts requests]} (get-in db [:chats chat-id])]
(->> contacts (->> contacts
(map (fn [{:keys [identity]}] (map (fn [{:keys [identity]}]
(let [{:keys [commands responses]} (get-in db [:contacts/contacts identity])] (let [{:keys [commands responses]} (get-in db [:contacts/contacts identity])]
(let [commands' (mapv (fn [[k v]] [k [v :any]]) (merge global-commands commands)) (let [commands' (mapv (fn [[k v]] [k [v :any]]) (merge global-commands commands))
responses' (mapv (fn [{:keys [message-id type]}] responses' (mapv (fn [{:keys [message-id type]}]
(when-let [response (get responses type)] (when-let [response (get responses type)]
[type [response message-id]])) [type [response message-id]]))
requests)] requests)]
(into commands' responses'))))) (into commands' responses')))))
(reduce (fn [m cur] (into (or m {}) cur))) (reduce (fn [m cur] (into (or m {}) cur)))
(into {})))) (into {}))))
@ -78,7 +78,7 @@
(str/replace (str/trim command-text) #" +" " ") (str/replace (str/trim command-text) #" +" " ")
command-text) command-text)
splitted (cond-> (str/split command-text-normalized const/spacing-char) splitted (cond-> (str/split command-text-normalized const/spacing-char)
space? (drop-last))] space? (drop-last))]
(->> splitted (->> splitted
(reduce (fn [[list command-started?] arg] (reduce (fn [[list command-started?] arg]
(let [quotes-count (count (filter #(= % const/arg-wrapping-char) arg)) (let [quotes-count (count (filter #(= % const/arg-wrapping-char) arg))
@ -95,7 +95,7 @@
[[] false]) [[] false])
(first))))) (first)))))
(defn join-command-args [args] (defn join-command-args
"Transforms a list of args to a string. The opposite of `split-command-args`. "Transforms a list of args to a string. The opposite of `split-command-args`.
Examples: Examples:
@ -107,6 +107,7 @@
Input: ['/send' 'Complex name with space in between' '1.0'] Input: ['/send' 'Complex name with space in between' '1.0']
Output: '/send \"Complex name with space in between\" 1.0'" Output: '/send \"Complex name with space in between\" 1.0'"
[args]
(when args (when args
(->> args (->> args
(map (fn [arg] (map (fn [arg]
@ -139,13 +140,13 @@
(subs command-name 1))) (subs command-name 1)))
(vals possible-actions)) (vals possible-actions))
(first))] (first))]
{:command command {:command command
:metadata (if (and (nil? (:to-message-id input-metadata)) (not= :any to-message-id)) :metadata (if (and (nil? (:to-message-id input-metadata)) (not= :any to-message-id))
(assoc input-metadata :to-message-id to-message-id) (assoc input-metadata :to-message-id to-message-id)
input-metadata) input-metadata)
:args (if (empty? seq-arguments) :args (if (empty? seq-arguments)
(rest command-args) (rest command-args)
seq-arguments)})))) seq-arguments)}))))
([{:keys [current-chat-id] :as db} chat-id] ([{:keys [current-chat-id] :as db} chat-id]
(selected-chat-command db chat-id (get-in db [:chats chat-id :input-text])))) (selected-chat-command db chat-id (get-in db [:chats chat-id :input-text]))))
@ -178,12 +179,11 @@
-1 (`*no-argument-error*`) means error. It can happen if there is no command or selection. -1 (`*no-argument-error*`) means error. It can happen if there is no command or selection.
This method is basically just another way of calling `current-chat-argument-position`." This method is basically just another way of calling `current-chat-argument-position`."
[{:keys [current-chat-id] :as db} chat-id] [{:keys [current-chat-id] :as db}]
(let [chat-id (or chat-id current-chat-id) (let [input-text (get-in db [:chats current-chat-id :input-text])
input-text (get-in db [:chats chat-id :input-text]) seq-arguments (get-in db [:chats current-chat-id :seq-arguments])
seq-arguments (get-in db [:chats chat-id :seq-arguments]) selection (get-in db [:chat-ui-props current-chat-id :selection])
selection (get-in db [:chat-ui-props chat-id :selection]) chat-command (selected-chat-command db current-chat-id)]
chat-command (selected-chat-command db chat-id)]
(current-chat-argument-position chat-command input-text selection seq-arguments))) (current-chat-argument-position chat-command input-text selection seq-arguments)))
(defn command-completion (defn command-completion
@ -247,12 +247,12 @@
prev-command (get-in db [:chat-ui-props current-chat-id :prev-command])] prev-command (get-in db [:chat-ui-props current-chat-id :prev-command])]
(if command (if command
(cond-> db (cond-> db
;; clear the bot db ;; clear the bot db
(not= prev-command (-> command :command :name)) (not= prev-command (-> command :command :name))
(assoc-in [:bot-db (or (:bot command) current-chat-id)] nil) (assoc-in [:bot-db (or (:bot command) current-chat-id)] nil)
;; clear the chat's validation messages ;; clear the chat's validation messages
true true
(assoc-in [:chat-ui-props current-chat-id :validation-messages] nil)) (assoc-in [:chat-ui-props current-chat-id :validation-messages] nil))
(-> db (-> db
;; clear input metadata ;; clear input metadata
(assoc-in [:chats current-chat-id :input-metadata] nil) (assoc-in [:chats current-chat-id :input-metadata] nil)
@ -268,7 +268,7 @@
(defmethod validation-handler :phone (defmethod validation-handler :phone
[_] [_]
(fn [[number] error-events-creator] (fn [[number] error-events-creator]
(when-not (phone-number/valid-mobile-number? number) (when-not (phone-number/valid-mobile-number? number)
(error-events-creator [validation-message (error-events-creator [validation-message
{:title (i18n/label :t/phone-number) {:title (i18n/label :t/phone-number)
:description (i18n/label :t/invalid-phone)}])))) :description (i18n/label :t/invalid-phone)}]))))

View File

@ -0,0 +1,18 @@
(ns status-im.chat.models.unviewed-messages)
(defn load-unviewed-messages [db raw-unviewed-messages]
(assoc db :unviewed-messages
(->> raw-unviewed-messages
(group-by :chat-id)
(map (fn [[id messages]]
[id {:messages-ids (map :message-id messages)
:count (count messages)}]))
(into {}))))
(defn add-unviewed-message [db chat-id message-id]
(-> db
(update-in [:unviewed-messages chat-id :messages-ids] conj message-id)
(update-in [:unviewed-messages chat-id :count] inc)))
(defn remove-unviewed-messages [db chat-id]
(update db :unviewed-messages dissoc chat-id))

View File

@ -1,25 +1,20 @@
(ns status-im.chat.sign-up (ns status-im.chat.sign-up
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.components.styles :refer [default-chat-color]] [status-im.components.styles :refer [default-chat-color]]
[status-im.utils.utils :refer [http-post]]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.utils.sms-listener :refer [add-sms-listener [status-im.utils.sms-listener :refer [add-sms-listener
remove-sms-listener]] remove-sms-listener]]
[status-im.constants :refer [console-chat-id [status-im.constants :as const]
text-content-type [status-im.chat.constants :as chat-const]
content-type-command
content-type-command-request
content-type-status]]
[status-im.chat.constants :as const]
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[clojure.string :as s])) [clojure.string :as s]))
(defn send-console-message [text] (defn send-console-message [text]
{:message-id (random/id) {:message-id (random/id)
:from "me" :from "me"
:to console-chat-id :to const/console-chat-id
:content text :content text
:content-type text-content-type :content-type const/text-content-type
:outgoing true}) :outgoing true})
;; todo fn name is not too smart, but... ;; todo fn name is not too smart, but...
@ -29,192 +24,165 @@
:content content}) :content content})
;; -- Send phone number ---------------------------------------- ;; -- Send phone number ----------------------------------------
(defn on-sign-up-response [& [message]] (defn- confirmation-code-event [correct? message-id]
(let [message-id (random/id)] [:received-message
(dispatch [:received-message {:message-id message-id
{:message-id message-id :content (command-content
:content (command-content :confirmation-code
:confirmation-code (if correct?
(or message (label :t/confirmation-code))) (label :t/confirmation-code)
:content-type content-type-command-request (label :t/incorrect-code)))
:outgoing false :content-type const/content-type-command-request
:chat-id console-chat-id :outgoing false
:from console-chat-id :chat-id const/console-chat-id
:to "me"}]))) :from const/console-chat-id
:to "me"}])
(defn start-listening-confirmation-code-sms [] (def enter-confirmation-code-event (partial confirmation-code-event true))
(dispatch [:request-permissions (def incorrect-confirmation-code-event (partial confirmation-code-event false))
[:receive-sms]
(fn []
(let [listener (add-sms-listener
(fn [{body :body}]
(when-let [matches (re-matches #"(\d{4})" body)]
(dispatch [:sign-up-confirm (second matches)]))))]
(dispatch [:start-listening-confirmation-code-sms listener])))]))
(defn stop-listening-confirmation-code-sms [db] (defn- sms-receive-handler [{confirmation-code :body}]
(when-let [listener (:confirmation-code-sms-listener db)] (when-let [matches (re-matches #"(\d{4})" confirmation-code)]
(remove-sms-listener listener) (dispatch [:sign-up-confirm (second matches)])))
(dissoc db :confirmation-code-sms-listener)))
(def start-listening-confirmation-code-sms-event
[:request-permissions
[:receive-sms]
(fn []
(let [listener (add-sms-listener sms-receive-handler)]
(dispatch [:start-listening-confirmation-code-sms listener])))])
;; -- Send confirmation code and synchronize contacts--------------------------- ;; -- Send confirmation code and synchronize contacts---------------------------
(defn on-sync-contacts [] (defn contacts-synchronised-event [message-id]
(dispatch [:received-message [:received-message
{:message-id (random/id) {:message-id message-id
:content (label :t/contacts-syncronized) :content (label :t/contacts-syncronized)
:content-type text-content-type :content-type const/text-content-type
:outgoing false :outgoing false
:chat-id console-chat-id :chat-id const/console-chat-id
:from console-chat-id :from const/console-chat-id
:to "me"}]) :to "me"}])
(dispatch [:set-signed-up true]))
(defn sync-contacts [] (def start-signup-events
;; TODO 'on-sync-contacts' is never called [[:received-message
(dispatch [:request-permissions {:message-id (random/id)
[:read-contacts] :content (command-content
#(dispatch [:sync-contacts on-sync-contacts])])) :phone
(label :t/phone-number-required))
(defn on-send-code-response [body] :content-type const/content-type-command-request
(dispatch [:received-message :outgoing false
{:message-id (random/id) :chat-id const/console-chat-id
:content (:message body) :from const/console-chat-id
:content-type text-content-type :to "me"}]
:outgoing false [:received-message
:chat-id console-chat-id {:message-id (random/id)
:from console-chat-id :content (label :t/shake-your-phone)
:to "me"}]) :content-type const/text-content-type
(let [status (keyword (:status body))] :outgoing false
(when (= :confirmed status) :chat-id const/console-chat-id
(dispatch [:stop-listening-confirmation-code-sms]) :from const/console-chat-id
(sync-contacts) :to "me"}]])
;; TODO should be called after sync-contacts?
(dispatch [:set-signed-up true]))
(when (= :failed status)
(on-sign-up-response (label :t/incorrect-code)))))
(defn start-signup []
(let [message-id (random/id)]
(dispatch [:received-message
{:message-id message-id
:content (command-content
:phone
(label :t/phone-number-required))
:content-type content-type-command-request
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}]))
(dispatch [:received-message
{:message-id (random/id)
:content (label :t/shake-your-phone)
:content-type text-content-type
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}]))
;; -- Saving password ---------------------------------------- ;; -- Saving password ----------------------------------------
(defn account-generation-message [] (def account-generation-event
(dispatch [:received-message [:received-message
{:message-id const/crazy-math-message-id {:message-id chat-const/crazy-math-message-id
:content (label :t/account-generation-message) :content (label :t/account-generation-message)
:content-type text-content-type :content-type const/text-content-type
:outgoing false :outgoing false
:chat-id console-chat-id :chat-id const/console-chat-id
:from console-chat-id :from const/console-chat-id
:to "me"}])) :to "me"}])
(defn move-to-internal-failure-message [] (def move-to-internal-failure-event
(dispatch [:received-message [:received-message
{:message-id const/move-to-internal-failure-message-id {:message-id chat-const/move-to-internal-failure-message-id
:content (command-content :content (command-content
:grant-permissions :grant-permissions
(label :t/move-to-internal-failure-message)) (label :t/move-to-internal-failure-message))
:content-type content-type-command-request :content-type const/content-type-command-request
:outgoing false :outgoing false
:chat-id console-chat-id :chat-id const/console-chat-id
:from console-chat-id :from const/console-chat-id
:to "me"}])) :to "me"}])
(defn passphrase-messages-events [mnemonic signing-phrase crazy-math-message?]
(defn passphrase-messages [mnemonic signing-phrase crazy-math-message?] (into [[:received-message
(dispatch [:received-message {:message-id chat-const/passphrase-message-id
{:message-id const/passphrase-message-id :content (if crazy-math-message?
:content (if crazy-math-message? (label :t/phew-here-is-your-passphrase)
(label :t/phew-here-is-your-passphrase) (label :t/here-is-your-passphrase))
(label :t/here-is-your-passphrase)) :content-type const/text-content-type
:content-type text-content-type :outgoing false
:outgoing false :chat-id const/console-chat-id
:chat-id console-chat-id :from const/console-chat-id
:from console-chat-id :to "me"}]
:to "me"}]) [:received-message
(dispatch [:received-message {:message-id (random/id)
{:message-id (random/id) :content mnemonic
:content mnemonic :content-type const/text-content-type
:content-type text-content-type :outgoing false
:outgoing false :chat-id const/console-chat-id
:chat-id console-chat-id :from const/console-chat-id
:from console-chat-id :to "me"}]
:to "me"}]) [:received-message
(dispatch [:received-message {:message-id chat-const/signing-phrase-message-id
{:message-id const/signing-phrase-message-id :content (label :t/here-is-your-signing-phrase)
:content (label :t/here-is-your-signing-phrase) :content-type const/text-content-type
:content-type text-content-type :outgoing false
:outgoing false :chat-id const/console-chat-id
:chat-id console-chat-id :from const/console-chat-id
:from console-chat-id :to "me"}]
:to "me"}]) [:received-message
(dispatch [:received-message {:message-id (random/id)
{:message-id (random/id) :content signing-phrase
:content signing-phrase :content-type const/text-content-type
:content-type text-content-type :outgoing false
:outgoing false :chat-id const/console-chat-id
:chat-id console-chat-id :from const/console-chat-id
:from console-chat-id :to "me"}]]
:to "me"}]) start-signup-events))
(start-signup))
(def intro-status (def intro-status
{:message-id const/intro-status-message-id {:message-id chat-const/intro-status-message-id
:content (label :t/intro-status) :content (label :t/intro-status)
:from console-chat-id :from const/console-chat-id
:chat-id console-chat-id :chat-id const/console-chat-id
:content-type content-type-status :content-type const/content-type-status
:outgoing false :outgoing false
:to "me"}) :to "me"})
(defn intro [] (def intro-events
(dispatch [:received-message intro-status]) [[:received-message intro-status]
(dispatch [:received-message-when-commands-loaded [:received-message-when-commands-loaded
console-chat-id const/console-chat-id
{:chat-id console-chat-id {:chat-id const/console-chat-id
:message-id const/intro-message1-id :message-id chat-const/intro-message1-id
:content (command-content :content (command-content
:password :password
(label :t/intro-message1)) (label :t/intro-message1))
:content-type content-type-command-request :content-type const/content-type-command-request
:outgoing false :outgoing false
:from console-chat-id :from const/console-chat-id
:to "me"}])) :to "me"}]])
(def console-chat (def console-chat
{:chat-id console-chat-id {:chat-id const/console-chat-id
:name (s/capitalize console-chat-id) :name (s/capitalize const/console-chat-id)
:color default-chat-color :color default-chat-color
:group-chat false :group-chat false
:is-active true :is-active true
:unremovable? true :unremovable? true
:timestamp (.getTime (js/Date.)) :timestamp (.getTime (js/Date.))
:photo-path console-chat-id :photo-path const/console-chat-id
:contacts [{:identity console-chat-id :contacts [{:identity const/console-chat-id
:text-color "#FFFFFF" :text-color "#FFFFFF"
:background-color "#AB7967"}]}) :background-color "#AB7967"}]})
(def console-contact (def console-contact
{:whisper-identity console-chat-id {:whisper-identity const/console-chat-id
:name (s/capitalize console-chat-id) :name (s/capitalize const/console-chat-id)
:photo-path console-chat-id :photo-path const/console-chat-id
:dapp? true :dapp? true
:unremovable? true :unremovable? true
:bot-url "local://console-bot" :bot-url "local://console-bot"

View File

@ -2,8 +2,6 @@
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [reagent.core :as r] (:require [reagent.core :as r]
[re-frame.core :refer [dispatch subscribe]] [re-frame.core :refer [dispatch subscribe]]
[clojure.string :as str]
[status-im.chat.constants :as const]
[status-im.components.react :refer [view [status-im.components.react :refer [view
text text
list-view list-view
@ -12,20 +10,15 @@
[status-im.components.renderers.renderers :as renderers] [status-im.components.renderers.renderers :as renderers]
[status-im.utils.listview :as lw])) [status-im.utils.listview :as lw]))
(defn- select-contact [arg-index bot-db-key {:keys [name] :as contact}]
(let [contact (select-keys contact [:address :whisper-identity :name :photo-path :dapp?])
name (str/replace name (re-pattern const/arg-wrapping-char) "")]
(dispatch [:set-command-argument [arg-index name true]])
(dispatch [:set-in-bot-db {:path [:public (keyword bot-db-key)]
:value contact}])
(dispatch [:select-next-argument])))
(defn render-row [arg-index bot-db-key] (defn render-row [arg-index bot-db-key]
(fn [contact _ _] (fn [contact _ _]
(list-item (list-item
^{:key contact} ^{:key contact}
[contact-view {:contact contact [contact-view {:contact contact
:on-press #(select-contact arg-index bot-db-key contact)}]))) :on-press #(dispatch
[:set-contact-as-command-argument {:arg-index arg-index
:bot-db-key bot-db-key
:contact contact}])}])))
(defview choose-contact-view [{title :title (defview choose-contact-view [{title :title
arg-index :index arg-index :index
@ -44,4 +37,4 @@
:renderRow (render-row arg-index bot-db-key) :renderRow (render-row arg-index bot-db-key)
:bounces false :bounces false
:keyboardShouldPersistTaps :always :keyboardShouldPersistTaps :always
:renderSeparator renderers/list-separator-renderer}]]) :renderSeparator renderers/list-separator-renderer}]])

View File

@ -41,9 +41,7 @@
show-suggestions? [:show-suggestions?]] show-suggestions? [:show-suggestions?]]
[view style/commands-root [view style/commands-root
[view style/command-list-icon-container [view style/command-list-icon-container
[touchable-highlight {:on-press #(do (dispatch [:toggle-chat-ui-props :show-suggestions?]) [touchable-highlight {:on-press #(dispatch [:show-suggestions])}
(dispatch [:set-chat-ui-props {:validation-messages nil}])
(dispatch [:update-suggestions]))}
[view style/commands-list-icon [view style/commands-list-icon
(if show-suggestions? (if show-suggestions?
[vi/icon :icons/close] [vi/icon :icons/close]
@ -103,8 +101,8 @@
(.-height))] (.-height))]
(set-layout-height-fn h))) (set-layout-height-fn h)))
:on-selection-change #(let [s (-> (.-nativeEvent %) :on-selection-change #(let [s (-> (.-nativeEvent %)
(.-selection)) (.-selection))
end (.-end s)] end (.-end s)]
(dispatch [:update-text-selection end])) (dispatch [:update-text-selection end]))
:style (style/input-view height single-line-input?) :style (style/input-view height single-line-input?)
:placeholder-text-color style/color-input-helper-placeholder :placeholder-text-color style/color-input-helper-placeholder

View File

@ -39,7 +39,7 @@
get-contact-translated]] get-contact-translated]]
[status-im.chat.utils :as cu] [status-im.chat.utils :as cu]
[clojure.string :as str] [clojure.string :as str]
[status-im.chat.handlers.console :as console] [status-im.chat.events.console :as console]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(def window-width (:width (get-dimensions "window"))) (def window-width (:width (get-dimensions "window")))
@ -299,9 +299,8 @@
(defview message-delivery-status (defview message-delivery-status
[{:keys [message-id chat-id message-status user-statuses content]}] [{:keys [message-id chat-id message-status user-statuses content]}]
[app-db-message-status-value [:get-in [:message-data :statuses message-id :status]]] [app-db-message-status-value [:get-in [:message-data :statuses message-id :status]]]
(let [delivery-status (get-in user-statuses [chat-id :status]) (let [delivery-status (get-in user-statuses [chat-id :status])
command-name (keyword (:command content)) status (cond (and (not (console/commands-with-delivery-status (:command content)))
status (cond (and (not (console/commands-with-delivery-status command-name))
(cu/console? chat-id)) (cu/console? chat-id))
:seen :seen

View File

@ -122,4 +122,4 @@
{:sections (clj->js (map wrap-per-section-render-fn sections)) {:sections (clj->js (map wrap-per-section-render-fn sections))
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)} :renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)}
(when p/ios? {:SectionSeparatorComponent (fn [] (r/as-element [section-separator]))}) (when p/ios? {:SectionSeparatorComponent (fn [] (r/as-element [section-separator]))})
props)])) props)]))

View File

@ -18,9 +18,9 @@
(defn- command-type? (defn- command-type?
[type] [type]
(contains? (contains?
#{c/content-type-command c/content-type-command-request #{c/content-type-command c/content-type-command-request
c/content-type-wallet-request c/content-type-wallet-command} c/content-type-wallet-request c/content-type-wallet-command}
type)) type))
(def default-values (def default-values
{:outgoing false {:outgoing false
@ -97,6 +97,14 @@
[] []
(data-store/get-unviewed)) (data-store/get-unviewed))
(defn get-previews
[]
(->> (data-store/get-all-as-list)
(filter :preview)
(reduce (fn [acc {:keys [message-id preview]}]
(assoc acc message-id (read-string preview)))
{})))
(defn save (defn save
;; todo remove chat-id parameter ;; todo remove chat-id parameter
[chat-id {:keys [message-id content] [chat-id {:keys [message-id content]
@ -112,7 +120,7 @@
:timestamp (timestamp)})] :timestamp (timestamp)})]
(data-store/save message')))) (data-store/save message'))))
(defn update (defn update
[{:keys [message-id] :as message}] [{:keys [message-id] :as message}]
(when (data-store/exists? message-id) (when (data-store/exists? message-id)
(let [message (update-if-present message :user-statuses vals)] (let [message (update-if-present message :user-statuses vals)]
@ -120,4 +128,3 @@
(defn delete-by-chat-id [chat-id] (defn delete-by-chat-id [chat-id]
(data-store/delete-by-chat-id chat-id)) (data-store/delete-by-chat-id chat-id))

View File

@ -1,38 +1,47 @@
(ns status-im.ui.screens.accounts.events (ns status-im.ui.screens.accounts.events
(:require (:require
status-im.ui.screens.accounts.login.events status-im.ui.screens.accounts.login.events
status-im.ui.screens.accounts.recover.events status-im.ui.screens.accounts.recover.events
[status-im.data-store.accounts :as accounts-store] [status-im.data-store.accounts :as accounts-store]
[re-frame.core :refer [reg-cofx reg-fx dispatch inject-cofx]] [re-frame.core :as re-frame]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.utils.types :refer [json->clj]] [status-im.utils.types :refer [json->clj]]
[status-im.utils.identicon :refer [identicon]] [status-im.utils.identicon :refer [identicon]]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[clojure.string :as str] [clojure.string :as str]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.handlers :refer [register-handler-db register-handler-fx get-hashtags] :as handlers] [status-im.utils.handlers :as handlers]
[status-im.ui.screens.accounts.statuses :as statuses] [status-im.ui.screens.accounts.statuses :as statuses]
[status-im.utils.signing-phrase.core :as signing-phrase] [status-im.utils.signing-phrase.core :as signing-phrase]
[status-im.utils.gfycat.core :refer [generate-gfy]])) [status-im.utils.gfycat.core :refer [generate-gfy]]))
;;;; Helper fns
(defn create-account
"Takes db and password, creates map of effects describing account creation"
[db password]
{:db (assoc db :accounts/creating-account? true)
::create-account password
:dispatch-later [{:ms 400 :dispatch [:account-generation-message]}]})
;;;; COFX ;;;; COFX
(reg-cofx (re-frame/reg-cofx
:get-new-keypair! :get-new-keypair!
(fn [coeffects _] (fn [coeffects _]
(assoc coeffects :keypair (protocol/new-keypair!)))) (assoc coeffects :keypair (protocol/new-keypair!))))
(reg-cofx (re-frame/reg-cofx
::get-all-accounts ::get-all-accounts
(fn [coeffects _] (fn [coeffects _]
(assoc coeffects :all-accounts (accounts-store/get-all)))) (assoc coeffects :all-accounts (accounts-store/get-all))))
;;;; FX ;;;; FX
(reg-fx (re-frame/reg-fx
::save-account ::save-account
(fn [account] (fn [account]
(accounts-store/save account true))) (accounts-store/save account true)))
@ -55,46 +64,46 @@
:signing-phrase phrase}] :signing-phrase phrase}]
(log/debug "account-created") (log/debug "account-created")
(when-not (str/blank? public-key) (when-not (str/blank? public-key)
(dispatch [:show-mnemonic mnemonic phrase]) (re-frame/dispatch [:show-mnemonic mnemonic phrase])
(dispatch [:add-account account password])))) (re-frame/dispatch [:add-account account password]))))
(reg-fx (re-frame/reg-fx
::create-account ::create-account
(fn [password] (fn [password]
(status/create-account (status/create-account
password password
#(account-created % password)))) #(account-created % password))))
(reg-fx (re-frame/reg-fx
::broadcast-account-update ::broadcast-account-update
(fn [{:keys [current-public-key web3 name photo-path status (fn [{:keys [current-public-key web3 name photo-path status
updates-public-key updates-private-key]}] updates-public-key updates-private-key]}]
(when web3 (when web3
(protocol/broadcast-profile! (protocol/broadcast-profile!
{:web3 web3 {:web3 web3
:message {:from current-public-key :message {:from current-public-key
:message-id (random/id) :message-id (random/id)
:keypair {:public updates-public-key :keypair {:public updates-public-key
:private updates-private-key} :private updates-private-key}
:payload {:profile {:name name :payload {:profile {:name name
:status status :status status
:profile-image photo-path}}}})))) :profile-image photo-path}}}}))))
(reg-fx (re-frame/reg-fx
::send-keys-update ::send-keys-update
(fn [{:keys [web3 current-public-key contacts (fn [{:keys [web3 current-public-key contacts
updates-public-key updates-private-key]}] updates-public-key updates-private-key]}]
(doseq [id (handlers/identities contacts)] (doseq [id (handlers/identities contacts)]
(protocol/update-keys! (protocol/update-keys!
{:web3 web3 {:web3 web3
:message {:from current-public-key :message {:from current-public-key
:to id :to id
:message-id (random/id) :message-id (random/id)
:payload {:keypair {:public updates-public-key :payload {:keypair {:public updates-public-key
:private updates-private-key}}}})))) :private updates-private-key}}}}))))
;;;; Handlers ;;;; Handlers
(register-handler-fx (handlers/register-handler-fx
:add-account :add-account
(fn [{{:keys [network] (fn [{{:keys [network]
:networks/keys [networks] :networks/keys [networks]
@ -107,23 +116,16 @@
(when password (when password
{:dispatch-later [{:ms 400 :dispatch [:login-account address password true]}]}))))) {:dispatch-later [{:ms 400 :dispatch [:login-account address password true]}]})))))
(register-handler-fx (handlers/register-handler-fx
:create-account
(fn [{db :db} [_ password]]
{:db (assoc db :accounts/creating-account? true)
::create-account password
:dispatch-later [{:ms 400 :dispatch [:account-generation-message]}]}))
(register-handler-fx
:create-new-account-handler :create-new-account-handler
(fn [_ _] (fn [_ _]
{:dispatch-n [[:initialize-db] {:dispatch-n [[:initialize-db]
[:load-accounts] [:load-accounts]
[:check-console-chat true]]})) [:check-console-chat true]]}))
(register-handler-fx (handlers/register-handler-fx
:load-accounts :load-accounts
[(inject-cofx ::get-all-accounts)] [(re-frame/inject-cofx ::get-all-accounts)]
(fn [{:keys [db all-accounts]} _] (fn [{:keys [db all-accounts]} _]
(let [accounts (->> all-accounts (let [accounts (->> all-accounts
(map (fn [{:keys [address] :as account}] (map (fn [{:keys [address] :as account}]
@ -136,7 +138,7 @@
(when-not (empty? events) (when-not (empty? events)
{:dispatch-n events}))))) {:dispatch-n events})))))
(register-handler-fx (handlers/register-handler-fx
:account-update-networks :account-update-networks
(fn [{{:accounts/keys [accounts] :networks/keys [networks] :as db} :db} [_ id]] (fn [{{:accounts/keys [accounts] :networks/keys [networks] :as db} :db} [_ id]]
(let [current-account (get accounts id) (let [current-account (get accounts id)
@ -144,41 +146,51 @@
{:db (assoc-in db [:accounts/accounts id] new-account) {:db (assoc-in db [:accounts/accounts id] new-account)
::save-account new-account}))) ::save-account new-account})))
(register-handler-fx (handlers/register-handler-fx
:check-status-change :check-status-change
(fn [{{:accounts/keys [accounts current-account-id]} :db} [_ status]] (fn [{{:accounts/keys [accounts current-account-id]} :db} [_ status]]
(let [{old-status :status} (get accounts current-account-id) (let [{old-status :status} (get accounts current-account-id)
status-updated? (and (not= status nil) status-updated? (and (not= status nil)
(not= status old-status))] (not= status old-status))]
(when status-updated? (when status-updated?
(let [hashtags (get-hashtags status)] (let [hashtags (handlers/get-hashtags status)]
(when (seq hashtags) (when (seq hashtags)
{:dispatch [:broadcast-status status hashtags]})))))) {:dispatch [:broadcast-status status hashtags]}))))))
(defn update-account [current-account new-fields] (defn- update-account [current-account new-fields now]
(merge current-account (assoc new-fields :last-updated (time/now-ms)))) (merge current-account (assoc new-fields :last-updated now)))
(register-handler-fx (defn account-update
"Takes map of coeffects containing `:db` and `:now` keys + new account fields,
returns all effects necessary for account update."
[{{:keys [network]
:accounts/keys [accounts current-account-id] :as db} :db now :now} new-account-fields]
(let [current-account (get accounts current-account-id)
new-account (update-account current-account new-account-fields now)]
{:db (assoc-in db [:accounts/accounts current-account-id] new-account)
::save-account new-account
::broadcast-account-update (merge
(select-keys db [:current-public-key :web3])
(select-keys new-account [:name :photo-path :status
:updates-public-key :updates-private-key]))}))
;; TODO(janherich): remove this event once it's not used anymore
(handlers/register-handler-fx
:account-update :account-update
(fn [{{:accounts/keys [accounts current-account-id] :as db} :db} [_ new-account-fields]] [(re-frame/inject-cofx :now) re-frame/trim-v]
(let [current-account (get accounts current-account-id) (fn [cofx [new-account-fields]]
new-account (update-account current-account new-account-fields)] (account-update cofx new-account-fields)))
{:db (assoc-in db [:accounts/accounts current-account-id] new-account)
::save-account new-account
::broadcast-account-update (merge
(select-keys db [:current-public-key :web3])
(select-keys new-account [:name :photo-path :status
:updates-public-key :updates-private-key]))})))
(register-handler-fx (handlers/register-handler-fx
:account-update-keys :account-update-keys
[(inject-cofx :get-new-keypair!)] [(re-frame/inject-cofx :get-new-keypair!) (re-frame/inject-cofx :now)]
(fn [{:keys [db keypair]} _] (fn [{:keys [db keypair now]} _]
(let [{:accounts/keys [accounts current-account-id]} db (let [{:accounts/keys [accounts current-account-id]} db
{:keys [public private]} keypair {:keys [public private]} keypair
current-account (get accounts current-account-id) current-account (get accounts current-account-id)
new-account (update-account current-account {:updates-public-key public new-account (update-account current-account {:updates-public-key public
:updates-private-key private})] :updates-private-key private}
now)]
{:db (assoc-in db [:accounts/accounts current-account-id] new-account) {:db (assoc-in db [:accounts/accounts current-account-id] new-account)
::save-account new-account ::save-account new-account
::send-keys-update (merge ::send-keys-update (merge
@ -186,17 +198,19 @@
(select-keys new-account [:updates-public-key (select-keys new-account [:updates-public-key
:updates-private-key]))}))) :updates-private-key]))})))
(register-handler-fx (handlers/register-handler-fx
:send-account-update-if-needed :send-account-update-if-needed
(fn [{{:accounts/keys [accounts current-account-id]} :db} _] [(re-frame/inject-cofx :now)]
(fn [{{:accounts/keys [accounts current-account-id]} :db now :now :as cofx} _]
(let [{:keys [last-updated]} (get accounts current-account-id) (let [{:keys [last-updated]} (get accounts current-account-id)
now (time/now-ms)
needs-update? (> (- now last-updated) time/week)] needs-update? (> (- now last-updated) time/week)]
(log/info "Need to send account-update: " needs-update?) (log/info "Need to send account-update: " needs-update?)
(when needs-update? (when needs-update?
(dispatch [:account-update]))))) ;; TODO(janherich): this is very strange and misleading, need to figure out why it'd necessary to update
;; account with network update when last update was more then week ago
(account-update cofx nil)))))
(register-handler-db (handlers/register-handler-db
:set-current-account :set-current-account
(fn [{:accounts/keys [accounts] :as db} [_ address]] (fn [{:accounts/keys [accounts] :as db} [_ address]]
(let [key (:public-key (accounts address))] (let [key (:public-key (accounts address))]

View File

@ -27,7 +27,7 @@
(fn [] (fn []
(when (and (get-in @message [:content :command]) (when (and (get-in @message [:content :command])
(not @preview)) (not @preview))
(dispatch [:request-command-data @message :short-preview]))) (dispatch [:request-command-message-data @message :short-preview])))
:reagent-render :reagent-render
(fn [_] (fn [_]

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.contacts.events (ns status-im.ui.screens.contacts.events
(:require [re-frame.core :refer [dispatch reg-fx reg-cofx inject-cofx]] (:require [re-frame.core :refer [dispatch trim-v reg-fx reg-cofx inject-cofx]]
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]] [status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
[status-im.data-store.contacts :as contacts] [status-im.data-store.contacts :as contacts]
[status-im.utils.crypt :refer [encrypt]] [status-im.utils.crypt :refer [encrypt]]
@ -22,15 +22,15 @@
;;;; COFX ;;;; COFX
(reg-cofx (reg-cofx
::get-all-contacts ::get-all-contacts
(fn [coeffects _] (fn [coeffects _]
(assoc coeffects :all-contacts (contacts/get-all)))) (assoc coeffects :all-contacts (contacts/get-all))))
(reg-cofx (reg-cofx
::get-default-contacts-and-groups ::get-default-contacts-and-groups
(fn [coeffects _] (fn [coeffects _]
(assoc coeffects :default-contacts js-res/default-contacts (assoc coeffects :default-contacts js-res/default-contacts
:default-groups js-res/default-contact-groups))) :default-groups js-res/default-contact-groups)))
;;;; FX ;;;; FX
@ -102,13 +102,13 @@
(reg-fx (reg-fx
::fetch-contacts-from-phone! ::fetch-contacts-from-phone!
(fn [_] (fn [on-contacts-event-creator]
(.getAll rn-dependencies/contacts (.getAll rn-dependencies/contacts
(fn [error contacts] (fn [error contacts]
(if error (if error
(log/debug :error-on-fetching-loading error) (log/debug :error-on-fetching-loading error)
(let [contacts' (normalize-phone-contacts contacts)] (let [contacts' (normalize-phone-contacts contacts)]
(dispatch [::get-contacts-identities contacts']))))))) (dispatch [::get-contacts-identities contacts' on-contacts-event-creator])))))))
(defn- get-contacts-by-hash [contacts] (defn- get-contacts-by-hash [contacts]
(->> contacts (->> contacts
@ -126,18 +126,17 @@
(map (fn [{:keys [phone-number-hash whisper-identity address]}] (map (fn [{:keys [phone-number-hash whisper-identity address]}]
(let [contact (contacts-by-hash phone-number-hash)] (let [contact (contacts-by-hash phone-number-hash)]
(assoc contact :whisper-identity whisper-identity (assoc contact :whisper-identity whisper-identity
:address address))) :address address)))
(js->clj contacts))) (js->clj contacts)))
(reg-fx (reg-fx
::request-stored-contacts ::request-stored-contacts
(fn [contacts] (fn [{:keys [contacts on-contacts-event-creator]}]
(let [contacts-by-hash (get-contacts-by-hash contacts) (let [contacts-by-hash (get-contacts-by-hash contacts)
data (or (keys contacts-by-hash) '())] data (or (keys contacts-by-hash) '())]
(http-post "get-contacts" {:phone-number-hashes data} (http-post "get-contacts" {:phone-number-hashes data}
(fn [{:keys [contacts]}] (fn [{:keys [contacts]}]
(let [contacts' (add-identity contacts-by-hash contacts)] (dispatch (on-contacts-event-creator (add-identity contacts-by-hash contacts))))))))
(dispatch [:add-contacts contacts'])))))))
(reg-fx (reg-fx
::request-contacts-by-address ::request-contacts-by-address
@ -159,16 +158,24 @@
(register-handler-fx (register-handler-fx
::get-contacts-identities ::get-contacts-identities
(fn [_ [_ contacts]] [trim-v]
{::request-stored-contacts contacts})) (fn [_ [contacts on-contacts-event-creator]]
{::request-stored-contacts {:contacts contacts
:on-contacts-event-creator on-contacts-event-creator}}))
(register-handler-fx
:sync-contacts
[trim-v]
(fn [_ [on-contacts-event-creator]]
{::fetch-contacts-from-phone! on-contacts-event-creator}))
(register-handler-fx (register-handler-fx
:watch-contact :watch-contact
(fn [{:keys [db]} [_ {:keys [public-key private-key] :as contact}]] (fn [{:keys [db]} [_ {:keys [public-key private-key] :as contact}]]
(when (and public-key private-key) (when (and public-key private-key)
{::watch-contact (merge {::watch-contact (merge
(select-keys db [:web3]) (select-keys db [:web3])
(select-keys contact [:whisper-identity :public-key :private-key]))}))) (select-keys contact [:whisper-identity :public-key :private-key]))})))
(register-handler-fx (register-handler-fx
:update-contact! :update-contact!
@ -177,11 +184,6 @@
{:db (update-in db [:contacts/contacts whisper-identity] merge contact) {:db (update-in db [:contacts/contacts whisper-identity] merge contact)
::save-contact contact}))) ::save-contact contact})))
(register-handler-fx
:sync-contacts
(fn [_ _]
{::fetch-contacts-from-phone! nil}))
(defn- update-pending-status [old-contacts {:keys [whisper-identity pending?] :as contact}] (defn- update-pending-status [old-contacts {:keys [whisper-identity pending?] :as contact}]
(let [{old-pending :pending? (let [{old-pending :pending?
:as old-contact} (get old-contacts whisper-identity) :as old-contact} (get old-contacts whisper-identity)
@ -209,7 +211,7 @@
:timestamp (random/timestamp) :timestamp (random/timestamp)
:contacts (mapv #(hash-map :identity %) contacts)})]]) :contacts (mapv #(hash-map :identity %) contacts)})]])
(defn- prepare-default-contacts-events [contacts default-contacts] (defn- prepare-default-contacts-events [contacts default-contacts]
[[:add-contacts [[:add-contacts
(for [[id {:keys [name photo-path public-key add-chat? global-command (for [[id {:keys [name photo-path public-key add-chat? global-command
dapp? dapp-url dapp-hash bot-url unremovable?]}] default-contacts dapp? dapp-url dapp-hash bot-url unremovable?]}] default-contacts
@ -257,11 +259,11 @@
(fn [{:keys [db default-contacts default-groups]} _] (fn [{:keys [db default-contacts default-groups]} _]
(let [{:contacts/keys [contacts] :group/keys [contact-groups]} db] (let [{:contacts/keys [contacts] :group/keys [contact-groups]} db]
{:dispatch-n (concat {:dispatch-n (concat
(prepare-default-groups-events contact-groups default-groups) (prepare-default-groups-events contact-groups default-groups)
(prepare-default-contacts-events contacts default-contacts) (prepare-default-contacts-events contacts default-contacts)
(prepare-add-chat-events contacts default-contacts) (prepare-add-chat-events contacts default-contacts)
(prepare-bot-commands-events contacts default-contacts) (prepare-bot-commands-events contacts default-contacts)
(prepare-add-contacts-to-groups-events contacts default-contacts))}))) (prepare-add-contacts-to-groups-events contacts default-contacts))})))
(register-handler-fx (register-handler-fx
:load-contacts :load-contacts
@ -278,30 +280,36 @@
(into {})) (into {}))
contacts (into {} contacts-list)] contacts (into {} contacts-list)]
{:db (assoc db :contacts/contacts contacts {:db (assoc db :contacts/contacts contacts
:global-commands global-commands) :global-commands global-commands)
:dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts)}))) :dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts)})))
(defn add-contacts
"Creates effects map for adding contacts"
[db new-contacts]
(let [{:contacts/keys [contacts]} db
identities (set (keys contacts))
new-contacts' (->> new-contacts
(map #(update-pending-status contacts %))
(remove #(identities (:whisper-identity %)))
(map #(vector (:whisper-identity %) %))
(into {}))
global-commands (->> new-contacts'
(keep (fn [[n {:keys [global-command]}]]
(when global-command
[(keyword n) (assoc global-command
:type :command
:bot n)])))
(into {}))]
{:db (-> db
(update :global-commands merge global-commands)
(update :contacts/contacts merge new-contacts'))
::save-contacts! (vals new-contacts')}))
(register-handler-fx (register-handler-fx
:add-contacts :add-contacts
(fn [{:keys [db]} [_ new-contacts]] [trim-v]
(let [{:contacts/keys [contacts]} db (fn [{:keys [db]} [new-contacts]]
identities (set (keys contacts)) (add-contacts db new-contacts)))
new-contacts' (->> new-contacts
(map #(update-pending-status contacts %))
(remove #(identities (:whisper-identity %)))
(map #(vector (:whisper-identity %) %))
(into {}))
global-commands (->> new-contacts'
(keep (fn [[n {:keys [global-command]}]]
(when global-command
[(keyword n) (assoc global-command
:type :command
:bot n)])))
(into {}))]
{:db (-> db
(update :global-commands merge global-commands)
(update :contacts/contacts merge new-contacts'))
::save-contacts! (vals new-contacts')})))
(register-handler-db (register-handler-db
:remove-contacts-click-handler :remove-contacts-click-handler
@ -347,7 +355,7 @@
(read-string contact-info) (read-string contact-info)
(get contacts chat-or-whisper-id)) (get contacts chat-or-whisper-id))
contact' (assoc contact :address (public-key->address chat-or-whisper-id) contact' (assoc contact :address (public-key->address chat-or-whisper-id)
:pending? false)] :pending? false)]
{:dispatch-n [[::add-new-contact contact'] {:dispatch-n [[::add-new-contact contact']
[:watch-contact contact'] [:watch-contact contact']
[:discoveries-send-portions chat-or-whisper-id]]}))) [:discoveries-send-portions chat-or-whisper-id]]})))
@ -403,8 +411,8 @@
:hide-contact :hide-contact
(fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]] (fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]]
{::stop-watching-contact (merge {::stop-watching-contact (merge
(select-keys db [:web3]) (select-keys db [:web3])
(select-keys contact [:whisper-identity])) (select-keys contact [:whisper-identity]))
:dispatch-n [[:update-contact! {:whisper-identity whisper-identity :dispatch-n [[:update-contact! {:whisper-identity whisper-identity
:pending? true}] :pending? true}]
[:account-update-keys]]})) [:account-update-keys]]}))
@ -433,10 +441,10 @@
:open-chat-with-contact :open-chat-with-contact
(fn [_ [_ {:keys [whisper-identity dapp?] :as contact}]] (fn [_ [_ {:keys [whisper-identity dapp?] :as contact}]]
{:dispatch-n (concat {:dispatch-n (concat
[[:navigate-to-clean :chat-list] [[:navigate-to-clean :chat-list]
[:start-chat whisper-identity {}]] [:start-chat whisper-identity {}]]
(when-not dapp? (when-not dapp?
[[::send-contact-request contact]]))})) [[::send-contact-request contact]]))}))
(register-handler-fx (register-handler-fx
:add-contact-handler :add-contact-handler

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.events (ns status-im.ui.screens.events
(:require status-im.bots.handlers (:require status-im.bots.events
status-im.chat.handlers status-im.chat.handlers
status-im.commands.handlers.jail status-im.commands.handlers.jail
status-im.commands.handlers.loading status-im.commands.handlers.loading
@ -17,7 +17,7 @@
status-im.ui.screens.profile.events status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events status-im.ui.screens.qr-scanner.events
status-im.ui.screens.wallet.events status-im.ui.screens.wallet.events
[re-frame.core :refer [dispatch reg-fx]] [re-frame.core :refer [dispatch reg-fx reg-cofx]]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.components.permissions :as permissions] [status-im.components.permissions :as permissions]
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]
@ -25,6 +25,9 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.js-dependencies :as dependencies] [status-im.js-dependencies :as dependencies]
[status-im.ui.screens.db :refer [app-db]] [status-im.ui.screens.db :refer [app-db]]
[status-im.utils.sms-listener :as sms-listener-util]
[status-im.utils.datetime :as time]
[status-im.utils.random :as random]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.crypt :as crypt] [status-im.utils.crypt :as crypt]
[status-im.utils.notifications :as notifications] [status-im.utils.notifications :as notifications]
@ -35,10 +38,87 @@
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
;;;; Helper fns
(defn- call-jail-function
[{:keys [chat-id function callback-events-creator] :as opts}]
(let [path [:functions function]
params (select-keys opts [:parameters :context])]
(status/call-jail
{:jail-id chat-id
:path path
:params params
:callback (fn [jail-response]
(doseq [event (if callback-events-creator
(callback-events-creator jail-response)
[[:received-bot-response
{:chat-id chat-id}
jail-response]])
:when event]
(dispatch event)))})))
;;;; COFX ;;;; COFX
(reg-cofx
:now
(fn [coeffects _]
(assoc coeffects :now (time/now-ms))))
(reg-cofx
:random-id
(fn [coeffects _]
(assoc coeffects :random-id (random/id))))
(reg-cofx
:random-id-seq
(fn [coeffects _]
(assoc coeffects :random-id-seq
((fn rand-id-seq []
(cons (random/id) (lazy-seq (rand-id-seq))))))))
;;;; FX ;;;; FX
(reg-fx
:call-jail
(fn [{:keys [callback-events-creator] :as opts}]
(status/call-jail
(-> opts
(dissoc :callback-events-creator)
(assoc :callback
(fn [jail-response]
(doseq [event (callback-events-creator jail-response)]
(dispatch event))))))))
(reg-fx
:call-jail-function
call-jail-function)
(reg-fx
:call-jail-function-n
(fn [opts-seq]
(doseq [opts opts-seq]
(call-jail-function opts))))
(reg-fx
:http-post
(fn [{:keys [action data success-event-creator failure-event-creator]}]
(utils/http-post action
data
#(dispatch (success-event-creator %))
#(dispatch (failure-event-creator %)))))
(reg-fx
:http-get
(fn [{:keys [url success-event-creator failure-event-creator]}]
(utils/http-get url
#(dispatch (success-event-creator %))
#(dispatch (failure-event-creator %)))))
(reg-fx
:remove-sms-listener
(fn [subscription]
(sms-listener-util/remove-sms-listener subscription)))
(reg-fx (reg-fx
::init-store ::init-store
(fn [] (fn []
@ -101,9 +181,9 @@
(i18n/label :testfairy-message))))) (i18n/label :testfairy-message)))))
(reg-fx (reg-fx
::get-fcm-token-fx ::get-fcm-token-fx
(fn [] (fn []
(notifications/get-fcm-token))) (notifications/get-fcm-token)))
;;;; Handlers ;;;; Handlers
@ -211,9 +291,9 @@
(.geoPermissionsGranted webview-bridge))) (.geoPermissionsGranted webview-bridge)))
(register-handler-fx (register-handler-fx
:get-fcm-token :get-fcm-token
(fn [_ _] (fn [_ _]
{::get-fcm-token-fx nil})) {::get-fcm-token-fx nil}))
(defn handle-jail-signal [{:keys[chat_id data]}] (defn handle-jail-signal [{:keys[chat_id data]}]
(let [{:keys [event data]} (types/json->clj data)] (let [{:keys [event data]} (types/json->clj data)]

View File

@ -95,4 +95,4 @@
(-> db (-> db
(assoc :prev-tab-view-id (:view-id db)) (assoc :prev-tab-view-id (:view-id db))
(assoc :prev-view-id (:view-id db)) (assoc :prev-view-id (:view-id db))
(navigate-to-clean view-id)))) (navigate-to-clean view-id))))

View File

@ -1,7 +1,7 @@
(ns status-im.ui.screens.profile.events (ns status-im.ui.screens.profile.events
(:require [clojure.spec.alpha :as spec] (:require [clojure.spec.alpha :as spec]
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame :refer [reg-fx]] [re-frame.core :as re-frame :refer [reg-fx trim-v]]
[status-im.components.react :refer [show-image-picker]] [status-im.components.react :refer [show-image-picker]]
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]
[status-im.ui.screens.profile.db :as db] [status-im.ui.screens.profile.db :as db]
@ -28,11 +28,13 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:profile/send-transaction :profile/send-transaction
(fn [_ [_ chat-id]] [trim-v]
{:dispatch-n [[:clear-seq-arguments] (fn [{:keys [db]} [chat-id contact-id]]
[:navigate-to :chat chat-id] {:dispatch-n [[:navigate-to :chat chat-id]
;; TODO https://github.com/status-im/status-react/issues/1621 [:select-chat-input-command {:name "send"}]
[:select-chat-input-command {:name "send"}]]})) [:set-contact-as-command-argument {:arg-index 0
:bot-db-key "recipient"
:contact (get-in db [:contacts/contacts contact-id])}]]}))
(handlers/register-handler-fx (handlers/register-handler-fx
:profile/send-message :profile/send-message

View File

@ -75,7 +75,7 @@
[action-button {:label (label :t/send-transaction) [action-button {:label (label :t/send-transaction)
:icon :icons/arrow-right :icon :icons/arrow-right
:icon-opts {:color :blue} :icon-opts {:color :blue}
:on-press #(dispatch [:profile/send-transaction chat-id])}]])]) :on-press #(dispatch [:profile/send-transaction chat-id whisper-identity])}]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value? accessibility-label]}] (defn profile-info-item [{:keys [label value options text-mode empty-value? accessibility-label]}]
[react/view styles/profile-setting-item [react/view styles/profile-setting-item

View File

@ -1,16 +1,17 @@
(ns status-im.utils.sms-listener (ns status-im.utils.sms-listener
(:require [status-im.utils.platform :refer [android?]] (:require [re-frame.core :as re-frame]
[status-im.utils.platform :refer [android?]]
[status-im.react-native.js-dependencies :as rn-dependencies])) [status-im.react-native.js-dependencies :as rn-dependencies]))
;; Only android is supported! ;; Only android is supported!
(defn add-sms-listener (defn add-sms-listener
"Message format: {:originatingAddress string, :body string}. Returns "Message format: {:originatingAddress string, :body string}. Returns
cancelable subscription." cancelable subscription."
[listen-fn] [listen-event-creator]
(when android? (when android?
(.addListener rn-dependencies/android-sms-listener (.addListener rn-dependencies/android-sms-listener
(fn [message] (fn [message]
(listen-fn (js->clj message :keywordize-keys true)))))) (re-frame/dispatch (listen-event-creator (js->clj message :keywordize-keys true)))))))
(defn remove-sms-listener [subscription] (defn remove-sms-listener [subscription]
(when android? (when android?

View File

@ -0,0 +1,48 @@
(ns status-im.test.chat.events
(:require [cljs.test :refer [deftest is testing]]
reagent.core
[re-frame.core :as rf]
[day8.re-frame.test :refer [run-test-sync]]
[status-im.constants :as const]
[status-im.chat.sign-up :as sign-up]
[status-im.chat.events :as chat-events]))
(def contact
{:address "c296367a939e0957500a25ca89b70bd64b03004e"
:whisper-identity "0x04f5722fba79eb36d73263417531007f43d13af76c6233573a8e3e60f667710611feba0785d751b50609bfc0b7cef35448875c5392c0a91948c95798a0ce600847"
:name "testuser"
:photo-path "contacts://testuser"
:dapp? false})
(deftest init-console-chat
(testing "initialising console if console is already added to chats, should not modify anything"
(let [db {:chats {const/console-chat-id sign-up/console-chat}}
fx (chat-events/init-console-chat db false)]
(is (= db (:db fx)))
(is (= #{:db} (-> fx keys set)))))
(testing "initialising console without existing account and console chat not initialisated"
(let [fresh-db {:chats {}
:accounts/current-account-id nil}
{:keys [db dispatch-n]} (chat-events/init-console-chat fresh-db false)]
(is (= (:current-chat-id db)
(:chat-id sign-up/console-chat)))
(is (= (:new-chat db)
sign-up/console-chat))
(is (= (:current-chat-id db)
const/console-chat-id))
(is (= dispatch-n
(concat [[:add-contacts [sign-up/console-contact]]]
sign-up/intro-events)))))
(testing "initialising console with existing account and console chat not initialisated"
(let [fresh-db {:chats {}
:accounts/current-account-id (:whisper-identity contact)}
{:keys [db dispatch-n]} (chat-events/init-console-chat fresh-db false)]
(is (= (:current-chat-id db)
(:chat-id sign-up/console-chat)))
(is (= (:new-chat db)
sign-up/console-chat))
(is (= (:current-chat-id db)
const/console-chat-id))
(is (= dispatch-n [[:add-contacts [sign-up/console-contact]]])))))

View File

@ -1,5 +1,6 @@
(ns status-im.test.runner (ns status-im.test.runner
(:require [doo.runner :refer-macros [doo-tests]] (:require [doo.runner :refer-macros [doo-tests]]
[status-im.test.chat.events]
[status-im.test.contacts.events] [status-im.test.contacts.events]
[status-im.test.accounts.events] [status-im.test.accounts.events]
[status-im.test.wallet.events] [status-im.test.wallet.events]
@ -24,17 +25,18 @@
(set! goog.DEBUG false) (set! goog.DEBUG false)
(doo-tests (doo-tests
'status-im.test.accounts.events 'status-im.test.chat.events
'status-im.test.contacts.events 'status-im.test.accounts.events
'status-im.test.profile.events 'status-im.test.contacts.events
'status-im.test.wallet.events 'status-im.test.profile.events
'status-im.test.chat.models.input 'status-im.test.wallet.events
'status-im.test.components.main-tabs 'status-im.test.chat.models.input
'status-im.test.handlers 'status-im.test.components.main-tabs
'status-im.test.utils.utils 'status-im.test.handlers
'status-im.test.utils.money 'status-im.test.utils.utils
'status-im.test.utils.clocks 'status-im.test.utils.money
'status-im.test.utils.erc20 'status-im.test.utils.clocks
'status-im.test.utils.random 'status-im.test.utils.erc20
'status-im.test.utils.gfycat.core 'status-im.test.utils.random
'status-im.test.utils.signing-phrase.core) 'status-im.test.utils.gfycat.core
'status-im.test.utils.signing-phrase.core)